1.
下图是UFS Device协议架构图,UTP层上面对接UCS层,左上对接Device Manager(主要是Query Request), 右上对接Task Manager。
UTP层的作用:将应用层传下来的请求数据以UPIU的数据包下发给UIC层(Unipro和M-PHY), 将底层传上来的UPIU数据包解析之后将数据传给应用层
2. UFS System Model
UFS传输事务是由UFS协议信息单元(UPIU)的数据包组成,这些数据包在Unipro总线上的设备之间传输,传输事务请求-响应的操作在发起方设备和目标设备之间开始,发起方通过启动传输序列对目标设备和逻辑单元的请求,然后目标设备将响应一系列最终以响应交易结束的交易。
3. UPIU的通用标准格式
3.1 TASK MANAGEMENT REQUEST UPIU
注1:对于UFS Devices来说,每个UFS Devices有多个Lun, 每个Lun有一个Command Queue(命令队列), 命令队列深度一般为32,UFS Devices收到UFS Host的Command(一般来说一个Command Request就是一个Task)之后,会将这个命令放到命令队列里,队列最多存放32个Command Request. UFS Device一般一次执行1个Command Request, 执行完一个之后再执行下一个。
注2:TASK MANAGEMENT REQUEST UPIU管理UFS Device的Task,其实就是理解为管理UFS Host给UFS Device下发的Command Request.
注3:理解一下Task Management Function values
Abort Task:终止一个task(Command Request)
1.UFS Transfer Request Descriptors
ufshcd_queuecommand
ufshcd_comp_scsi_upiu
ret = ufshcd_prepare_req_desc_hdr(hba, lrbp, &upiu_flags, lrbp->cmd->sc_data_direction);
/**
* ufshcd_prepare_req_desc_hdr() - Fills the requests header
* descriptor according to request
* @hba: per adapter instance
* @lrbp: pointer to local reference block
* @upiu_flags: flags required in the header
* @cmd_dir: requests data direction
*/
static int ufshcd_prepare_req_desc_hdr(struct ufs_hba *hba,
struct ufshcd_lrb *lrbp, u32 *upiu_flags,
enum dma_data_direction cmd_dir)
{
struct utp_transfer_req_desc *req_desc = lrbp->utr_descriptor_ptr;
u32 data_direction;
u32 dword_0;
if (cmd_dir == DMA_FROM_DEVICE) {
data_direction = UTP_DEVICE_TO_HOST;
*upiu_flags = UPIU_CMD_FLAGS_READ;
} else if (cmd_dir == DMA_TO_DEVICE) {
data_direction = UTP_HOST_TO_DEVICE;
*upiu_flags = UPIU_CMD_FLAGS_WRITE;
} else {
data_direction = UTP_NO_DATA_TRANSFER;
*upiu_flags = UPIU_CMD_FLAGS_NONE;
}
set_customized_upiu_flags(lrbp, upiu_flags);
dword_0 = data_direction | (lrbp->command_type
<< UPIU_COMMAND_TYPE_OFFSET);
if (lrbp->intr_cmd)
dword_0 |= UTP_REQ_DESC_INT_CMD;
/* Transfer request descriptor header fields */
req_desc->header.dword_0 = cpu_to_le32(dword_0);
/* dword_1 is reserved, hence it is set to 0 */
req_desc->header.dword_1 = 0;
/*
* assigning invalid value for command status. Controller
* updates OCS on command completion, with the command
* status
*/
req_desc->header.dword_2 =
cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
/* dword_3 is reserved, hence it is set to 0 */
req_desc->header.dword_3 = 0;
req_desc->prd_table_length = 0;
if (ufshcd_is_crypto_supported(hba))
return ufshcd_prepare_crypto_utrd(hba, lrbp);
return 0;
}
对应于UFS Host Controller Spec:
DW0:
DW2:
其中DW1和DW3是Reseve的
/**
* ufshcd_memory_alloc - allocate memory for host memory space data structures
* @hba: per adapter instance
*
* 1. Allocate DMA memory for Command Descriptor array
* Each command descriptor consist of Command UPIU, Response UPIU and PRDT
* 2. Allocate DMA memory for UTP Transfer Request Descriptor List (UTRDL).
* 3. Allocate DMA memory for UTP Task Management Request Descriptor List
* (UTMRDL)
* 4. Allocate memory for local reference block(lrb).
*
* Returns 0 for success, non-zero in case of failure
*/
static int ufshcd_memory_alloc(struct ufs_hba *hba)
{
size_t utmrdl_size, utrdl_size, ucdl_size;
/* Allocate memory for UTP command descriptors */
ucdl_size = (sizeof(struct utp_transfer_cmd_desc) * hba->nutrs);
hba->ucdl_base_addr = dmam_alloc_coherent(hba->dev,
ucdl_size,
&hba->ucdl_dma_addr,
GFP_KERNEL);
/*
* UFSHCI requires UTP command descriptor to be 128 byte aligned.
* make sure hba->ucdl_dma_addr is aligned to PAGE_SIZE
* if hba->ucdl_dma_addr is aligned to PAGE_SIZE, then it will
* be aligned to 128 bytes as well
*/
if (!hba->ucdl_base_addr ||
WARN_ON(hba->ucdl_dma_addr & (PAGE_SIZE - 1))) {
dev_err(hba->dev,
"Command Descriptor Memory allocation failed\n");
goto out;
}
/*
* Allocate memory for UTP Transfer descriptors
* UFSHCI requires 1024 byte alignment of UTRD
*/
utrdl_size = (sizeof(struct utp_transfer_req_desc) * hba->nutrs);
hba->utrdl_base_addr = dmam_alloc_coherent(hba->dev,
utrdl_size,
&hba->utrdl_dma_addr,
GFP_KERNEL);
if (!hba->utrdl_base_addr ||
WARN_ON(hba->utrdl_dma_addr & (PAGE_SIZE - 1))) {
dev_err(hba->dev,
"Transfer Descriptor Memory allocation failed\n");
goto out;
}
/*
* Allocate memory for UTP Task Management descriptors
* UFSHCI requires 1024 byte alignment of UTMRD
*/
utmrdl_size = sizeof(struct utp_task_req_desc) * hba->nutmrs;
hba->utmrdl_base_addr = dmam_alloc_coherent(hba->dev,
utmrdl_size,
&hba->utmrdl_dma_addr,
GFP_KERNEL);
if (!hba->utmrdl_base_addr ||
WARN_ON(hba->utmrdl_dma_addr & (PAGE_SIZE - 1))) {
dev_err(hba->dev,
"Task Management Descriptor Memory allocation failed\n");
goto out;
}
/* Allocate memory for local reference block */
hba->lrb = devm_kcalloc(hba->dev,
hba->nutrs, sizeof(struct ufshcd_lrb),
GFP_KERNEL);
if (!hba->lrb) {
dev_err(hba->dev, "LRB Memory allocation failed\n");
goto out;
}
return 0;
out:
return -ENOMEM;
}
/**
* ufshcd_host_memory_configure - configure local reference block with
* memory offsets
* @hba: per adapter instance
*
* Configure Host memory space
* 1. Update Corresponding UTRD.UCDBA and UTRD.UCDBAU with UCD DMA
* address.
* 2. Update each UTRD with Response UPIU offset, Response UPIU length
* and PRDT offset.
* 3. Save the corresponding addresses of UTRD, UCD.CMD, UCD.RSP and UCD.PRDT
* into local reference block.
*/
static void ufshcd_host_memory_configure(struct ufs_hba *hba)
{
struct utp_transfer_cmd_desc *cmd_descp;
struct utp_transfer_req_desc *utrdlp;
dma_addr_t cmd_desc_dma_addr;
dma_addr_t cmd_desc_element_addr;
u16 response_offset;
u16 prdt_offset;
int cmd_desc_size;
int i;
utrdlp = hba->utrdl_base_addr;
cmd_descp = hba->ucdl_base_addr;
response_offset =
offsetof(struct utp_transfer_cmd_desc, response_upiu);
prdt_offset =
offsetof(struct utp_transfer_cmd_desc, prd_table);
cmd_desc_size = sizeof(struct utp_transfer_cmd_desc);
cmd_desc_dma_addr = hba->ucdl_dma_addr;
for (i = 0; i < hba->nutrs; i++) {
/* Configure UTRD with command descriptor base address */
cmd_desc_element_addr =
(cmd_desc_dma_addr + (cmd_desc_size * i));
utrdlp[i].command_desc_base_addr_lo =
cpu_to_le32(lower_32_bits(cmd_desc_element_addr));
utrdlp[i].command_desc_base_addr_hi =
cpu_to_le32(upper_32_bits(cmd_desc_element_addr));
/* Response upiu and prdt offset should be in double words */
if (hba->quirks & UFSHCD_QUIRK_PRDT_BYTE_GRAN) {
utrdlp[i].response_upiu_offset =
cpu_to_le16(response_offset);
utrdlp[i].prd_table_offset =
cpu_to_le16(prdt_offset);
utrdlp[i].response_upiu_length =
cpu_to_le16(ALIGNED_UPIU_SIZE);
} else {
utrdlp[i].response_upiu_offset =
cpu_to_le16((response_offset >> 2));
utrdlp[i].prd_table_offset =
cpu_to_le16((prdt_offset >> 2));
utrdlp[i].response_upiu_length =
cpu_to_le16(ALIGNED_UPIU_SIZE >> 2);
}
hba->lrb[i].utr_descriptor_ptr = (utrdlp + i);
hba->lrb[i].utrd_dma_addr = hba->utrdl_dma_addr +
(i * sizeof(struct utp_transfer_req_desc));
hba->lrb[i].ucd_req_ptr =
(struct utp_upiu_req *)(cmd_descp + i);
hba->lrb[i].ucd_req_dma_addr = cmd_desc_element_addr;
hba->lrb[i].ucd_rsp_ptr =
(struct utp_upiu_rsp *)cmd_descp[i].response_upiu;
hba->lrb[i].ucd_rsp_dma_addr = cmd_desc_element_addr +
response_offset;
hba->lrb[i].ucd_prdt_ptr =
(struct ufshcd_sg_entry *)cmd_descp[i].prd_table;
hba->lrb[i].ucd_prdt_dma_addr = cmd_desc_element_addr +
prdt_offset;
}
}
/**
* ufshcd_make_hba_operational - Make UFS controller operational
* @hba: per adapter instance
*
* To bring UFS host controller to operational state,
* 1. Enable required interrupts
* 2. Configure interrupt aggregation
* 3. Program UTRL and UTMRL base address
* 4. Configure run-stop-registers
*
* Returns 0 on success, non-zero value on failure
*/
static int ufshcd_make_hba_operational(struct ufs_hba *hba)
{
int err = 0;
u32 reg;
/* Enable required interrupts */
ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
/* Configure interrupt aggregation */
if (ufshcd_is_intr_aggr_allowed(hba))
ufshcd_config_intr_aggr(hba, hba->nutrs - 1, INT_AGGR_DEF_TO);
else
ufshcd_disable_intr_aggr(hba);
/* Configure UTRL and UTMRL base address registers */
ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr),
REG_UTP_TRANSFER_REQ_LIST_BASE_L);
ufshcd_writel(hba, upper_32_bits(hba->utrdl_dma_addr),
REG_UTP_TRANSFER_REQ_LIST_BASE_H);
ufshcd_writel(hba, lower_32_bits(hba->utmrdl_dma_addr),
REG_UTP_TASK_REQ_LIST_BASE_L);
ufshcd_writel(hba, upper_32_bits(hba->utmrdl_dma_addr),
REG_UTP_TASK_REQ_LIST_BASE_H);
/*
* Make sure base address and interrupt setup are updated before
* enabling the run/stop registers below.
*/
wmb();
/*
* UCRDY, UTMRLDY and UTRLRDY bits must be 1
*/
reg = ufshcd_readl(hba, REG_CONTROLLER_STATUS);
if (!(ufshcd_get_lists_status(reg))) {
ufshcd_enable_run_stop_reg(hba);
} else {
dev_err(hba->dev,
"Host controller not ready to process requests");
err = -EIO;
goto out;
}
out:
return err;
}
其中对应于Host Controller Register:
可以这么理解:UFS Host给UFS Devices传输的请求是以UTP Transfer Request Descriptor格式传输的,UTP Transfer Request Descriptor的地址由0x50 (– UTP Transfer Request List Base Address )和0x54(– UTP Transfer Request List Base Address Upper 32-bits )组成, 当控制器往doorbeer register寄存器写1,即敲doorbeer时,host controller就会去0x50+0x54组成的UTP Transfer Request List 地址处取相应的UTP Transfer Request Descriptor数据经过Unipro和M-PHY传输给UFS Devices.
注1:UFS Transfer Request Descriptors(UTRD)是UFS Host和UFS Devices之间传输的基本单元,UFS Transfer Request Descriptors(UTRD) 有地址DW4: UTP Command Descriptor Base Address (UCDBA)和DW5: UTP Command Descriptor Base Address Upper 32-bits(UCDBAU);
UTP Command Descriptor Base Address (UCDBA)和UTP Command Descriptor Base Address Upper 32-bits(UCDBAU)组成的地址为UCD(UTP Command Descriptor )的地址,其中UCD包括Request UPIU + Response UPIU + PRDT(存放读写Data的地址)
注2: UTP Task Management Request List 详情可以查看UFS Host Controller的介绍
ufshcd_prepare_utp_scsi_cmd_upiu(lrbp, upiu_flags);
/**
* ufshcd_prepare_utp_scsi_cmd_upiu() - fills the utp_transfer_req_desc,
* for scsi commands
* @lrbp: local reference block pointer
* @upiu_flags: flags
*/
static
void ufshcd_prepare_utp_scsi_cmd_upiu(struct ufshcd_lrb *lrbp, u32 upiu_flags)
{
struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
unsigned short cdb_len;
/* command descriptor fields */
ucd_req_ptr->header.dword_0 = UPIU_HEADER_DWORD(
UPIU_TRANSACTION_COMMAND, upiu_flags,
lrbp->lun, lrbp->task_tag);
ucd_req_ptr->header.dword_1 = UPIU_HEADER_DWORD(
UPIU_COMMAND_SET_TYPE_SCSI, 0, 0, 0);
/* Total EHS length and Data segment length will be zero */
ucd_req_ptr->header.dword_2 = 0;
ucd_req_ptr->sc.exp_data_transfer_len =
cpu_to_be32(lrbp->cmd->sdb.length);
cdb_len = min_t(unsigned short, lrbp->cmd->cmd_len, MAX_CDB_SIZE);
memcpy(ucd_req_ptr->sc.cdb, lrbp->cmd->cmnd, cdb_len);
if (cdb_len < MAX_CDB_SIZE)
memset(ucd_req_ptr->sc.cdb + cdb_len, 0,
(MAX_CDB_SIZE - cdb_len));
memset(lrbp->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp));
}
#define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
cpu_to_be32((byte3 << 24) | (byte2 << 16) |\
(byte1 << 8) | (byte0))
按照ufs devices协议command upiu的格式填充upiu数据包,准备下发给unipro层
2. UTP Task Management Request Descriptor:
static int ufshcd_memory_alloc(struct ufs_hba *hba)
{
hba->utmrdl_base_addr = dmam_alloc_coherent(hba->dev,
utmrdl_size,
&hba->utmrdl_dma_addr,
GFP_KERNEL);
if (!hba->utmrdl_base_addr ||
WARN_ON(hba->utmrdl_dma_addr & (PAGE_SIZE - 1))) {
dev_err(hba->dev,
"Task Management Descriptor Memory allocation failed\n");
goto out;
}
}
static int ufshcd_make_hba_operational(struct ufs_hba *hba)
{
ufshcd_writel(hba, lower_32_bits(hba->utmrdl_dma_addr),
REG_UTP_TASK_REQ_LIST_BASE_L);
ufshcd_writel(hba, upper_32_bits(hba->utmrdl_dma_addr),
REG_UTP_TASK_REQ_LIST_BASE_H);
}
下面是发送TASK MANAGEMENT REQUEST UPIU的地方:
(1)
err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, UFS_QUERY_TASK, &resp);
err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, UFS_ABORT_TASK, &resp);
/**
* ufshcd_abort - abort a specific command
* @cmd: SCSI command pointer
*
* Abort the pending command in device by sending UFS_ABORT_TASK task management
* command, and in host controller by clearing the door-bell register. There can
* be race between controller sending the command to the device while abort is
* issued. To avoid that, first issue UFS_QUERY_TASK to check if the command is
* really issued and then try to abort it.
*
* Returns SUCCESS/FAILED
*/
static int ufshcd_abort(struct scsi_cmnd *cmd)
{
struct Scsi_Host *host;
struct ufs_hba *hba;
unsigned long flags;
unsigned int tag;
int err = 0;
int poll_cnt;
u8 resp = 0xF;
struct ufshcd_lrb *lrbp;
u32 reg;
host = cmd->device->host;
hba = shost_priv(host);
tag = cmd->request->tag;
if (!ufshcd_valid_tag(hba, tag)) {
dev_err(hba->dev,
"%s: invalid command tag %d: cmd=0x%pK, cmd->request=0x%pK\n",
__func__, tag, cmd, cmd->request);
BUG_ON(1);
}
if (cmd->cmnd[0] == READ_10 || cmd->cmnd[0] == WRITE_10) {
unsigned long lba = (cmd->cmnd[2] << 24) |
(cmd->cmnd[3] << 16) |
(cmd->cmnd[4] << 8) |
(cmd->cmnd[5] << 0);
unsigned int sct = (cmd->cmnd[7] << 8) |
(cmd->cmnd[8] << 0);
sector_t lba_bi_sector = 0;
if (cmd->request->bio)
lba_bi_sector = cmd->request->bio->bi_iter.bi_sector;
dev_err(hba->dev, "%s: tag:%d, cmd:0x%x, "
"lba:0x%08lx(0x%llx), sct:0x%04x, retries %d\n",
__func__, tag, cmd->cmnd[0], lba,
(unsigned long long)lba_bi_sector, sct, cmd->allowed);
} else {
dev_err(hba->dev, "%s: tag:%d, cmd:0x%x, retries %d\n",
__func__, tag, cmd->cmnd[0], cmd->allowed);
}
lrbp = &hba->lrb[tag];
ufshcd_update_error_stats(hba, UFS_ERR_TASK_ABORT);
if (!ufshcd_valid_tag(hba, tag)) {
dev_err(hba->dev,
"%s: invalid command tag %d: cmd=0x%pK, cmd->request=0x%pK\n",
__func__, tag, cmd, cmd->request);
BUG_ON(1);
}
/*
* Task abort to the device W-LUN is illegal. When this command
* will fail, due to spec violation, scsi err handling next step
* will be to send LU reset which, again, is a spec violation.
* To avoid these unnecessary/illegal step we skip to the last error
* handling stage: reset and restore.
*/
if (lrbp->lun == UFS_UPIU_UFS_DEVICE_WLUN)
return ufshcd_eh_host_reset_handler(cmd);
#if defined(SEC_UFS_ERROR_COUNT)
SEC_ufs_utp_error_check(hba, cmd, false, 0);
#if defined(CONFIG_UFSFEATURE) && defined(CONFIG_UFSHPB)
if (hba->ufsf.hpb_dev_info.hpb_device &&
((cmd->cmnd[0] == READ_16) || (((cmd->cmnd[0] >> 4) & 0xF) == 0xF))) {
SEC_ufs_hpb_error_check(hba, cmd);
}
#endif
#endif
ufshcd_hold_all(hba);
reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
/* If command is already aborted/completed, return SUCCESS */
if (!(test_bit(tag, &hba->outstanding_reqs))) {
dev_err(hba->dev,
"%s: cmd at tag %d already completed, outstanding=0x%lx, doorbell=0x%x\n",
__func__, tag, hba->outstanding_reqs, reg);
goto out;
}
if (!(reg & (1 << tag))) {
dev_err(hba->dev,
"%s: cmd was completed, but without a notifying intr, tag = %d",
__func__, tag);
}
/* Print Transfer Request of aborted task */
dev_err(hba->dev, "%s: Device abort task at tag %d\n", __func__, tag);
/*
* Print detailed info about aborted request.
* As more than one request might get aborted at the same time,
* print full information only for the first aborted request in order
* to reduce repeated printouts. For other aborted requests only print
* basic details.
*/
scsi_print_command(cmd);
if (!hba->req_abort_count) {
ufshcd_print_fsm_state(hba);
ufshcd_print_host_regs(hba);
ufshcd_print_host_state(hba);
ufshcd_print_pwr_info(hba);
ufshcd_print_trs(hba, 1 << tag, true);
/* crash the system upon setting this debugfs. */
BUG_ON(hba->crash_on_err);
} else {
ufshcd_print_trs(hba, 1 << tag, false);
}
hba->req_abort_count++;
/* Skip task abort in case previous aborts failed and report failure */
if (lrbp->req_abort_skip) {
err = -EIO;
goto out;
}
for (poll_cnt = 100; poll_cnt; poll_cnt--) {
err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag,
UFS_QUERY_TASK, &resp);
if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) {
/* cmd pending in the device */
dev_err(hba->dev, "%s: cmd pending in the device. tag = %d\n",
__func__, tag);
break;
} else if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
/*
* cmd not pending in the device, check if it is
* in transition.
*/
dev_err(hba->dev, "%s: cmd at tag %d not pending in the device.\n",
__func__, tag);
reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
if (reg & (1 << tag)) {
/* sleep for max. 200us to stabilize */
usleep_range(100, 200);
continue;
}
/* command completed already */
dev_err(hba->dev, "%s: cmd at tag %d successfully cleared from DB.\n",
__func__, tag);
goto out;
} else {
dev_err(hba->dev,
"%s: no response from device. tag = %d, err %d\n",
__func__, tag, err);
if (!err)
err = resp; /* service response error */
#if defined(SEC_UFS_ERROR_COUNT)
SEC_ufs_utp_error_check(hba, NULL, true, UFS_QUERY_TASK);
#endif
goto out;
}
}
if (!poll_cnt) {
err = -EBUSY;
goto out;
}
err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag,
UFS_ABORT_TASK, &resp);
if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
if (!err) {
err = resp; /* service response error */
dev_err(hba->dev, "%s: issued. tag = %d, err %d\n",
__func__, tag, err);
}
#if defined(SEC_UFS_ERROR_COUNT)
SEC_ufs_utp_error_check(hba, NULL, true, UFS_ABORT_TASK);
#endif
goto out;
}
err = ufshcd_clear_cmd(hba, tag);
if (err) {
dev_err(hba->dev, "%s: Failed clearing cmd at tag %d, err %d\n",
__func__, tag, err);
goto out;
}
scsi_dma_unmap(cmd);
spin_lock_irqsave(host->host_lock, flags);
ufshcd_outstanding_req_clear(hba, tag);
hba->lrb[tag].cmd = NULL;
spin_unlock_irqrestore(host->host_lock, flags);
clear_bit_unlock(tag, &hba->lrb_in_use);
wake_up(&hba->dev_cmd.tag_wq);
out:
if (!err) {
err = SUCCESS;
if ((hba->dev_info.quirks & UFS_DEVICE_QUIRK_SUPPORT_QUERY_FATAL_MODE) &&
!hba->UFS_fatal_mode_done) {
unsigned long max_doorbells = (1UL << hba->nutrs) - 1;
if (hba->outstanding_reqs == max_doorbells)
__ufshcd_transfer_req_compl(hba,
(1UL << (hba->nutrs - 1)));
schedule_work(&hba->fatal_mode_work);
}
} else {
dev_err(hba->dev, "%s: failed with err %d\n", __func__, err);
ufshcd_set_req_abort_skip(hba, hba->outstanding_reqs);
err = FAILED;
}
/*
* This ufshcd_release_all() corresponds to the original scsi cmd that
* got aborted here (as we won't get any IRQ for it).
*/
ufshcd_release_all(hba);
return err;
}
注1:简单描述一下ufs abort的过程,先发送UFS_QUERY_TASK查询task的状态:
如果response为UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED,ufs host会发送UFS_ABORT_TASK去终止这个task.
如果response为UPIU_TASK_MANAGEMENT_FUNC_COMPL, ufs host会循环100次查询door bell的状态,如果查询次数小于100次的时候,发现task对应的door bell bit位为0时,此时ufs host不会发送UFS_ABORT_TASK,如果查询次数大于等于100次,表示ufs devices是busy的状态,,此时ufs host不会发送UFS_ABORT_TASK
如果response为其他值,说明ufs devices没有响应ufs host发送的task, 可能ufs devices出现某种错误,此时ufs host不会发送UFS_ABORT_TASK
(2) 在error handle处理的时候会发送TASK MANAGEMENT REQUEST UPIU去Reset 整个Lun的Task
err = ufshcd_issue_tm_cmd(hba, lrbp->lun, 0, UFS_LOGICAL_RESET, &resp);
/**
* ufshcd_eh_device_reset_handler - device reset handler registered to
* scsi layer.
* @cmd: SCSI command pointer
*
* Returns SUCCESS/FAILED
*/
static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd)
{
struct Scsi_Host *host;
struct ufs_hba *hba;
unsigned int tag;
u32 pos;
int err;
u8 resp = 0xF;
struct ufshcd_lrb *lrbp;
unsigned long flags;
host = cmd->device->host;
hba = shost_priv(host);
tag = cmd->request->tag;
lrbp = &hba->lrb[tag];
err = ufshcd_issue_tm_cmd(hba, lrbp->lun, 0, UFS_LOGICAL_RESET, &resp);
if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
if (!err)
err = resp;
goto out;
}
/* clear the commands that were pending for corresponding LUN */
for_each_set_bit(pos, &hba->outstanding_reqs, hba->nutrs) {
if (hba->lrb[pos].lun == lrbp->lun) {
err = ufshcd_clear_cmd(hba, pos);
if (err)
break;
}
}
spin_lock_irqsave(host->host_lock, flags);
ufshcd_transfer_req_compl(hba);
spin_unlock_irqrestore(host->host_lock, flags);
out:
hba->req_abort_count = 0;
if (!err) {
#if defined(CONFIG_UFSFEATURE)
ufsf_hpb_reset_lu(&hba->ufsf);
#endif
err = SUCCESS;
} else {
dev_err(hba->dev, "%s: failed with err %d\n", __func__, err);
err = FAILED;
}
return err;
}
四。Reference:
1. JESD223B (ufs host controller 2.0)
2. JESD220C-2_2 (ufs devices spec 2.2)
3. linux kernel ufs opensource (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/scsi/ufs?h=v5.11-rc4)