/* SPDX-License-Identifier: GPL-2.0 */ /* * Universal Flash Storage Host Performance Booster * * Copyright (C) 2017-2021 Samsung Electronics Co., Ltd. * * Authors: * Yongmyung Lee * Jinyoung Choi */ #ifndef _UFSHPB_H_ #define _UFSHPB_H_ /* hpb response UPIU macro */ #define HPB_RSP_NONE 0x0 #define HPB_RSP_REQ_REGION_UPDATE 0x1 #define HPB_RSP_DEV_RESET 0x2 #define MAX_ACTIVE_NUM 2 #define MAX_INACTIVE_NUM 2 #define DEV_DATA_SEG_LEN 0x14 #define DEV_SENSE_SEG_LEN 0x12 #define DEV_DES_TYPE 0x80 #define DEV_ADDITIONAL_LEN 0x10 /* hpb map & entries macro */ #define HPB_RGN_SIZE_UNIT 512 #define HPB_ENTRY_BLOCK_SIZE 4096 #define HPB_ENTRY_SIZE 0x8 #define PINNED_NOT_SET U32_MAX /* hpb support chunk size */ #define HPB_LEGACY_CHUNK_HIGH 1 #define HPB_MULTI_CHUNK_HIGH 255 /* hpb vender defined opcode */ #define UFSHPB_READ 0xF8 #define UFSHPB_READ_BUFFER 0xF9 #define UFSHPB_READ_BUFFER_ID 0x01 #define UFSHPB_WRITE_BUFFER 0xFA #define UFSHPB_WRITE_BUFFER_INACT_SINGLE_ID 0x01 #define UFSHPB_WRITE_BUFFER_PREFETCH_ID 0x02 #define UFSHPB_WRITE_BUFFER_INACT_ALL_ID 0x03 #define HPB_WRITE_BUFFER_CMD_LENGTH 10 #define MAX_HPB_READ_ID 0x7F #define HPB_READ_BUFFER_CMD_LENGTH 10 #define LU_ENABLED_HPB_FUNC 0x02 #define HPB_RESET_REQ_RETRIES 10 #define HPB_MAP_REQ_RETRIES 5 #define HPB_REQUEUE_TIME_MS 0 #define HPB_SUPPORT_VERSION 0x200 #define HPB_SUPPORT_LEGACY_VERSION 0x100 enum UFSHPB_MODE { HPB_HOST_CONTROL, HPB_DEVICE_CONTROL, }; enum UFSHPB_STATE { HPB_INIT, HPB_PRESENT, HPB_SUSPEND, HPB_FAILED, HPB_RESET, }; enum HPB_RGN_STATE { HPB_RGN_INACTIVE, HPB_RGN_ACTIVE, /* pinned regions are always active */ HPB_RGN_PINNED, }; enum HPB_SRGN_STATE { HPB_SRGN_UNUSED, HPB_SRGN_INVALID, HPB_SRGN_VALID, HPB_SRGN_ISSUED, }; /** * struct ufshpb_lu_info - UFSHPB logical unit related info * @num_blocks: the number of logical block * @pinned_start: the start region number of pinned region * @num_pinned: the number of pinned regions * @max_active_rgns: maximum number of active regions */ struct ufshpb_lu_info { int num_blocks; int pinned_start; int num_pinned; 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; /* subregion reads - for host mode */ unsigned int reads; /* below information is used by rsp_list */ struct list_head list_act_srgn; }; struct ufshpb_region { struct ufshpb_lu *hpb; struct ufshpb_subregion *srgn_tbl; 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; unsigned long rgn_flags; #define RGN_FLAG_DIRTY 0 #define RGN_FLAG_UPDATE 1 /* region reads - for host mode */ spinlock_t rgn_lock; unsigned int reads; /* region "cold" timer - for host mode */ ktime_t read_timeout; unsigned int read_timeout_expiries; struct list_head list_expired_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 - HPB related request structure (write/read buffer) * @req: block layer request structure * @bio: bio for this request * @hpb: ufshpb_lu structure that related to * @list_req: ufshpb_req mempool list * @sense: store its sense data * @mctx: L2P map information * @rgn_idx: target region index * @srgn_idx: target sub-region index * @lun: target logical unit number * @m_page: L2P map information data for pre-request * @len: length of host-side cached L2P map in m_page * @lpn: start LPN of L2P map in m_page */ struct ufshpb_req { struct request *req; struct bio *bio; struct ufshpb_lu *hpb; struct list_head list_req; union { struct { struct ufshpb_map_ctx *mctx; unsigned int rgn_idx; unsigned int srgn_idx; unsigned int lun; } rb; struct { struct page *m_page; unsigned int len; unsigned long lpn; } wb; }; }; 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; }; /** * ufshpb_params - ufs hpb parameters * @requeue_timeout_ms - requeue threshold of wb command (0x2) * @activation_thld - min reads [IOs] to activate/update a region * @normalization_factor - shift right the region's reads * @eviction_thld_enter - min reads [IOs] for the entering region in eviction * @eviction_thld_exit - max reads [IOs] for the exiting region in eviction * @read_timeout_ms - timeout [ms] from the last read IO to the region * @read_timeout_expiries - amount of allowable timeout expireis * @timeout_polling_interval_ms - frequency in which timeouts are checked * @inflight_map_req - number of inflight map requests */ struct ufshpb_params { unsigned int requeue_timeout_ms; unsigned int activation_thld; unsigned int normalization_factor; unsigned int eviction_thld_enter; unsigned int eviction_thld_exit; unsigned int read_timeout_ms; unsigned int read_timeout_expiries; unsigned int timeout_polling_interval_ms; unsigned int inflight_map_req; }; struct ufshpb_stats { u64 hit_cnt; u64 miss_cnt; u64 rcmd_noti_cnt; u64 rcmd_active_cnt; u64 rcmd_inactive_cnt; u64 map_req_cnt; u64 pre_req_cnt; u64 umap_req_cnt; }; 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 */ /* pre request information */ struct ufshpb_req *pre_req; int num_inflight_pre_req; int throttle_pre_req; int num_inflight_map_req; /* hold param_lock */ spinlock_t param_lock; struct list_head lh_pre_req_free; int pre_req_max_tr_len; /* cached L2P map management worker */ struct work_struct map_work; /* for selecting victim */ struct victim_select_info lru_info; struct work_struct ufshpb_normalization_work; struct delayed_work ufshpb_read_to_work; unsigned long work_data_bits; #define TIMEOUT_WORK_RUNNING 0 /* pinned region information */ u32 lu_pinned_start; u32 lu_pinned_end; /* HPB related configuration */ u32 rgns_per_lu; u32 srgns_per_lu; u32 last_srgn_entries; int srgns_per_rgn; u32 srgn_mem_size; u32 entries_per_rgn_mask; u32 entries_per_rgn_shift; u32 entries_per_srgn; u32 entries_per_srgn_mask; u32 entries_per_srgn_shift; u32 pages_per_srgn; bool is_hcm; struct ufshpb_stats stats; struct ufshpb_params params; struct kmem_cache *map_req_cache; struct kmem_cache *m_page_cache; struct list_head list_hpb_lu; }; struct ufs_hba; struct ufshcd_lrb; #ifndef CONFIG_SCSI_UFS_HPB static int ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) { return 0; } 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_toggle_state(struct ufs_hba *hba, enum UFSHPB_STATE src, enum UFSHPB_STATE dest) {} 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) {} static bool ufshpb_is_legacy(struct ufs_hba *hba) { return false; } #else int ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb *lrbp); 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_toggle_state(struct ufs_hba *hba, enum UFSHPB_STATE src, enum UFSHPB_STATE dest); 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); bool ufshpb_is_legacy(struct ufs_hba *hba); extern struct attribute_group ufs_sysfs_hpb_stat_group; extern struct attribute_group ufs_sysfs_hpb_param_group; #endif #endif /* End of Header */