scsi: ufs: ufshpb: L2P map management for HPB read

Implement L2P map management in HPB.

The HPB divides logical addresses into several regions. A region consists
of several sub-regions. The sub-region is a basic unit where L2P mapping is
managed. The driver loads L2P mapping data of each sub-region. The loaded
sub-region is called active-state. The HPB driver unloads L2P mapping data
as region unit. The unloaded region is called inactive-state.

Sub-region/region candidates to be loaded and unloaded are delivered from
the UFS device. The UFS device delivers the recommended active sub-region
and inactivate region to the driver using sense data. The HPB module
performs L2P mapping management on the host through the delivered
information.

A pinned region is a preset region on the UFS device that is always
in activate-state.

The data structures for map data requests and L2P mappings use the mempool
API, minimizing allocation overhead while avoiding static allocation.

The mininum size of the memory pool used in the HPB is implemented
as a module parameter so that it can be configurable by the user.

To guarantee a minimum memory pool size of 4MB: ufshpb_host_map_kbytes=4096.

The map_work manages active/inactive via 2 "to-do" lists:

 - hpb->lh_inact_rgn: regions to be inactivated
 - hpb->lh_act_srgn: subregions to be activated

These lists are maintained on I/O completion.

[mkp: switch to REQ_OP_DRV_*]

Link: https://lore.kernel.org/r/20210712085859epcms2p36e420f19564f6cd0c4a45d54949619eb@epcms2p3
Tested-by: Bean Huo <beanhuo@micron.com>
Tested-by: Can Guo <cang@codeaurora.org>
Tested-by: Stanley Chu <stanley.chu@mediatek.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Can Guo <cang@codeaurora.org>
Reviewed-by: Bean Huo <beanhuo@micron.com>
Reviewed-by: Stanley Chu <stanley.chu@mediatek.com>
Acked-by: Avri Altman <Avri.Altman@wdc.com>
Signed-off-by: Daejun Park <daejun7.park@samsung.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Daejun Park 2021-07-12 17:58:59 +09:00 committed by Martin K. Petersen
parent f02bc9754a
commit 4b5f49079c
4 changed files with 1178 additions and 15 deletions

View File

@ -478,6 +478,41 @@ struct utp_cmd_rsp {
u8 sense_data[UFS_SENSE_SIZE];
};
struct ufshpb_active_field {
__be16 active_rgn;
__be16 active_srgn;
};
#define HPB_ACT_FIELD_SIZE 4
/**
* struct utp_hpb_rsp - Response UPIU structure
* @residual_transfer_count: Residual transfer count DW-3
* @reserved1: Reserved double words DW-4 to DW-7
* @sense_data_len: Sense data length DW-8 U16
* @desc_type: Descriptor type of sense data
* @additional_len: Additional length of sense data
* @hpb_op: HPB operation type
* @lun: LUN of response UPIU
* @active_rgn_cnt: Active region count
* @inactive_rgn_cnt: Inactive region count
* @hpb_active_field: Recommended to read HPB region and subregion
* @hpb_inactive_field: To be inactivated HPB region and subregion
*/
struct utp_hpb_rsp {
__be32 residual_transfer_count;
__be32 reserved1[4];
__be16 sense_data_len;
u8 desc_type;
u8 additional_len;
u8 hpb_op;
u8 lun;
u8 active_rgn_cnt;
u8 inactive_rgn_cnt;
struct ufshpb_active_field hpb_active_field[2];
__be16 hpb_inactive_field[2];
};
#define UTP_HPB_RSP_SIZE 40
/**
* struct utp_upiu_rsp - general upiu response structure
* @header: UPIU header structure DW-0 to DW-2
@ -488,6 +523,7 @@ struct utp_upiu_rsp {
struct utp_upiu_header header;
union {
struct utp_cmd_rsp sr;
struct utp_hpb_rsp hr;
struct utp_upiu_query qr;
};
};

View File

@ -5148,6 +5148,9 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
ufshcd_is_exception_event(lrbp->ucd_rsp_ptr))
/* Flushed in suspend */
schedule_work(&hba->eeh_work);
if (scsi_status == SAM_STAT_GOOD)
ufshpb_rsp_upiu(hba, lrbp);
break;
case UPIU_TRANSACTION_REJECT_UPIU:
/* TODO: handle Reject UPIU Response */
@ -9390,6 +9393,7 @@ void ufshcd_remove(struct ufs_hba *hba)
if (hba->sdev_ufs_device)
ufshcd_rpm_get_sync(hba);
ufs_bsg_remove(hba);
ufshpb_remove(hba);
ufs_sysfs_remove_nodes(hba->dev);
blk_cleanup_queue(hba->tmf_queue);
blk_mq_free_tag_set(&hba->tmf_tag_set);

File diff suppressed because it is too large Load Diff

View File

@ -40,6 +40,7 @@
#define LU_ENABLED_HPB_FUNC 0x02
#define HPB_RESET_REQ_RETRIES 10
#define HPB_MAP_REQ_RETRIES 5
#define HPB_SUPPORT_VERSION 0x100
@ -84,11 +85,19 @@ struct ufshpb_lu_info {
int max_active_rgns;
};
struct ufshpb_map_ctx {
struct page **m_page;
unsigned long *ppn_dirty;
};
struct ufshpb_subregion {
struct ufshpb_map_ctx *mctx;
enum HPB_SRGN_STATE srgn_state;
int rgn_idx;
int srgn_idx;
bool is_last;
/* below information is used by rsp_list */
struct list_head list_act_srgn;
};
struct ufshpb_region {
@ -96,6 +105,43 @@ struct ufshpb_region {
enum HPB_RGN_STATE rgn_state;
int rgn_idx;
int srgn_cnt;
/* below information is used by rsp_list */
struct list_head list_inact_rgn;
/* below information is used by lru */
struct list_head list_lru_rgn;
};
#define for_each_sub_region(rgn, i, srgn) \
for ((i) = 0; \
((i) < (rgn)->srgn_cnt) && ((srgn) = &(rgn)->srgn_tbl[i]); \
(i)++)
/**
* struct ufshpb_req - UFSHPB READ BUFFER (for caching map) request structure
* @req: block layer request for READ BUFFER
* @bio: bio for holding map page
* @hpb: ufshpb_lu structure that related to the L2P map
* @mctx: L2P map information
* @rgn_idx: target region index
* @srgn_idx: target sub-region index
* @lun: target logical unit number
*/
struct ufshpb_req {
struct request *req;
struct bio *bio;
struct ufshpb_lu *hpb;
struct ufshpb_map_ctx *mctx;
unsigned int rgn_idx;
unsigned int srgn_idx;
};
struct victim_select_info {
struct list_head lh_lru_rgn; /* LRU list of regions */
int max_lru_active_cnt; /* supported hpb #region - pinned #region */
atomic_t active_cnt;
};
struct ufshpb_stats {
@ -110,10 +156,22 @@ struct ufshpb_stats {
struct ufshpb_lu {
int lun;
struct scsi_device *sdev_ufs_lu;
spinlock_t rgn_state_lock; /* for protect rgn/srgn state */
struct ufshpb_region *rgn_tbl;
atomic_t hpb_state;
spinlock_t rsp_list_lock;
struct list_head lh_act_srgn; /* hold rsp_list_lock */
struct list_head lh_inact_rgn; /* hold rsp_list_lock */
/* cached L2P map management worker */
struct work_struct map_work;
/* for selecting victim */
struct victim_select_info lru_info;
/* pinned region information */
u32 lu_pinned_start;
u32 lu_pinned_end;
@ -133,6 +191,9 @@ struct ufshpb_lu {
struct ufshpb_stats stats;
struct kmem_cache *map_req_cache;
struct kmem_cache *m_page_cache;
struct list_head list_hpb_lu;
};
@ -140,6 +201,7 @@ struct ufs_hba;
struct ufshcd_lrb;
#ifndef CONFIG_SCSI_UFS_HPB
static void ufshpb_rsp_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) {}
static void ufshpb_resume(struct ufs_hba *hba) {}
static void ufshpb_suspend(struct ufs_hba *hba) {}
static void ufshpb_reset(struct ufs_hba *hba) {}
@ -147,10 +209,12 @@ static void ufshpb_reset_host(struct ufs_hba *hba) {}
static void ufshpb_init(struct ufs_hba *hba) {}
static void ufshpb_init_hpb_lu(struct ufs_hba *hba, struct scsi_device *sdev) {}
static void ufshpb_destroy_lu(struct ufs_hba *hba, struct scsi_device *sdev) {}
static void ufshpb_remove(struct ufs_hba *hba) {}
static bool ufshpb_is_allowed(struct ufs_hba *hba) { return false; }
static void ufshpb_get_geo_info(struct ufs_hba *hba, u8 *geo_buf) {}
static void ufshpb_get_dev_info(struct ufs_hba *hba, u8 *desc_buf) {}
#else
void ufshpb_rsp_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp);
void ufshpb_resume(struct ufs_hba *hba);
void ufshpb_suspend(struct ufs_hba *hba);
void ufshpb_reset(struct ufs_hba *hba);
@ -158,6 +222,7 @@ void ufshpb_reset_host(struct ufs_hba *hba);
void ufshpb_init(struct ufs_hba *hba);
void ufshpb_init_hpb_lu(struct ufs_hba *hba, struct scsi_device *sdev);
void ufshpb_destroy_lu(struct ufs_hba *hba, struct scsi_device *sdev);
void ufshpb_remove(struct ufs_hba *hba);
bool ufshpb_is_allowed(struct ufs_hba *hba);
void ufshpb_get_geo_info(struct ufs_hba *hba, u8 *geo_buf);
void ufshpb_get_dev_info(struct ufs_hba *hba, u8 *desc_buf);