diff options
author | Sujit Reddy Thumma <sthumma@codeaurora.org> | 2014-05-26 01:29:12 -0400 |
---|---|---|
committer | Christoph Hellwig <hch@lst.de> | 2014-05-28 06:25:13 -0400 |
commit | e293313262d3c780632f7888878c982fa0a9bf7e (patch) | |
tree | 48b9f96f64240af775f982e2330764319f38af14 /drivers/scsi/ufs/ufshcd.c | |
parent | bdbe5d2fe67031bf51777afbfa91585efcf9dbf5 (diff) |
scsi: ufs: Fix broken task management command implementation
Currently, sending Task Management (TM) command to the card might
be broken in some scenarios as listed below:
Problem: If there are more than 8 TM commands the implementation
returns error to the caller.
Fix: Wait for one of the slots to be emptied and send the command.
Problem: Sometimes it is necessary for the caller to know the TM service
response code to determine the task status.
Fix: Propogate the service response to the caller.
Problem: If the TM command times out no proper error recovery is
implemented.
Fix: Clear the command in the controller door-bell register, so that
further commands for the same slot don't fail.
Problem: While preparing the TM command descriptor, the task tag used
should be unique across SCSI/NOP/QUERY/TM commands and not the
task tag of the command which the TM command is trying to manage.
Fix: Use a unique task tag instead of task tag of SCSI command.
Problem: Since the TM command involves H/W communication, abruptly ending
the request on kill interrupt signal might cause h/w malfunction.
Fix: Wait for hardware completion interrupt with TASK_UNINTERRUPTIBLE
set.
Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
Reviewed-by: Yaniv Gardi <ygardi@codeaurora.org>
Tested-by: Dolev Raviv <draviv@codeaurora.org>
Acked-by: Vinayak Holikatti <vinholikatti@gmail.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'drivers/scsi/ufs/ufshcd.c')
-rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 169 |
1 files changed, 116 insertions, 53 deletions
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index d476cc34453c..c3acadc2cf0a 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c | |||
@@ -55,6 +55,9 @@ | |||
55 | /* Query request timeout */ | 55 | /* Query request timeout */ |
56 | #define QUERY_REQ_TIMEOUT 30 /* msec */ | 56 | #define QUERY_REQ_TIMEOUT 30 /* msec */ |
57 | 57 | ||
58 | /* Task management command timeout */ | ||
59 | #define TM_CMD_TIMEOUT 100 /* msecs */ | ||
60 | |||
58 | /* Expose the flag value from utp_upiu_query.value */ | 61 | /* Expose the flag value from utp_upiu_query.value */ |
59 | #define MASK_QUERY_UPIU_FLAG_LOC 0xFF | 62 | #define MASK_QUERY_UPIU_FLAG_LOC 0xFF |
60 | 63 | ||
@@ -182,13 +185,35 @@ ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp) | |||
182 | /** | 185 | /** |
183 | * ufshcd_get_tm_free_slot - get a free slot for task management request | 186 | * ufshcd_get_tm_free_slot - get a free slot for task management request |
184 | * @hba: per adapter instance | 187 | * @hba: per adapter instance |
188 | * @free_slot: pointer to variable with available slot value | ||
185 | * | 189 | * |
186 | * Returns maximum number of task management request slots in case of | 190 | * Get a free tag and lock it until ufshcd_put_tm_slot() is called. |
187 | * task management queue full or returns the free slot number | 191 | * Returns 0 if free slot is not available, else return 1 with tag value |
192 | * in @free_slot. | ||
188 | */ | 193 | */ |
189 | static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba) | 194 | static bool ufshcd_get_tm_free_slot(struct ufs_hba *hba, int *free_slot) |
190 | { | 195 | { |
191 | return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs); | 196 | int tag; |
197 | bool ret = false; | ||
198 | |||
199 | if (!free_slot) | ||
200 | goto out; | ||
201 | |||
202 | do { | ||
203 | tag = find_first_zero_bit(&hba->tm_slots_in_use, hba->nutmrs); | ||
204 | if (tag >= hba->nutmrs) | ||
205 | goto out; | ||
206 | } while (test_and_set_bit_lock(tag, &hba->tm_slots_in_use)); | ||
207 | |||
208 | *free_slot = tag; | ||
209 | ret = true; | ||
210 | out: | ||
211 | return ret; | ||
212 | } | ||
213 | |||
214 | static inline void ufshcd_put_tm_slot(struct ufs_hba *hba, int slot) | ||
215 | { | ||
216 | clear_bit_unlock(slot, &hba->tm_slots_in_use); | ||
192 | } | 217 | } |
193 | 218 | ||
194 | /** | 219 | /** |
@@ -1912,10 +1937,11 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev) | |||
1912 | * ufshcd_task_req_compl - handle task management request completion | 1937 | * ufshcd_task_req_compl - handle task management request completion |
1913 | * @hba: per adapter instance | 1938 | * @hba: per adapter instance |
1914 | * @index: index of the completed request | 1939 | * @index: index of the completed request |
1940 | * @resp: task management service response | ||
1915 | * | 1941 | * |
1916 | * Returns SUCCESS/FAILED | 1942 | * Returns non-zero value on error, zero on success |
1917 | */ | 1943 | */ |
1918 | static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) | 1944 | static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp) |
1919 | { | 1945 | { |
1920 | struct utp_task_req_desc *task_req_descp; | 1946 | struct utp_task_req_desc *task_req_descp; |
1921 | struct utp_upiu_task_rsp *task_rsp_upiup; | 1947 | struct utp_upiu_task_rsp *task_rsp_upiup; |
@@ -1936,19 +1962,15 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) | |||
1936 | task_req_descp[index].task_rsp_upiu; | 1962 | task_req_descp[index].task_rsp_upiu; |
1937 | task_result = be32_to_cpu(task_rsp_upiup->header.dword_1); | 1963 | task_result = be32_to_cpu(task_rsp_upiup->header.dword_1); |
1938 | task_result = ((task_result & MASK_TASK_RESPONSE) >> 8); | 1964 | task_result = ((task_result & MASK_TASK_RESPONSE) >> 8); |
1939 | 1965 | if (resp) | |
1940 | if (task_result != UPIU_TASK_MANAGEMENT_FUNC_COMPL && | 1966 | *resp = (u8)task_result; |
1941 | task_result != UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) | ||
1942 | task_result = FAILED; | ||
1943 | else | ||
1944 | task_result = SUCCESS; | ||
1945 | } else { | 1967 | } else { |
1946 | task_result = FAILED; | 1968 | dev_err(hba->dev, "%s: failed, ocs = 0x%x\n", |
1947 | dev_err(hba->dev, | 1969 | __func__, ocs_value); |
1948 | "trc: Invalid ocs = %x\n", ocs_value); | ||
1949 | } | 1970 | } |
1950 | spin_unlock_irqrestore(hba->host->host_lock, flags); | 1971 | spin_unlock_irqrestore(hba->host->host_lock, flags); |
1951 | return task_result; | 1972 | |
1973 | return ocs_value; | ||
1952 | } | 1974 | } |
1953 | 1975 | ||
1954 | /** | 1976 | /** |
@@ -2447,7 +2469,7 @@ static void ufshcd_tmc_handler(struct ufs_hba *hba) | |||
2447 | 2469 | ||
2448 | tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL); | 2470 | tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL); |
2449 | hba->tm_condition = tm_doorbell ^ hba->outstanding_tasks; | 2471 | hba->tm_condition = tm_doorbell ^ hba->outstanding_tasks; |
2450 | wake_up_interruptible(&hba->ufshcd_tm_wait_queue); | 2472 | wake_up(&hba->tm_wq); |
2451 | } | 2473 | } |
2452 | 2474 | ||
2453 | /** | 2475 | /** |
@@ -2497,38 +2519,58 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba) | |||
2497 | return retval; | 2519 | return retval; |
2498 | } | 2520 | } |
2499 | 2521 | ||
2522 | static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag) | ||
2523 | { | ||
2524 | int err = 0; | ||
2525 | u32 mask = 1 << tag; | ||
2526 | unsigned long flags; | ||
2527 | |||
2528 | if (!test_bit(tag, &hba->outstanding_tasks)) | ||
2529 | goto out; | ||
2530 | |||
2531 | spin_lock_irqsave(hba->host->host_lock, flags); | ||
2532 | ufshcd_writel(hba, ~(1 << tag), REG_UTP_TASK_REQ_LIST_CLEAR); | ||
2533 | spin_unlock_irqrestore(hba->host->host_lock, flags); | ||
2534 | |||
2535 | /* poll for max. 1 sec to clear door bell register by h/w */ | ||
2536 | err = ufshcd_wait_for_register(hba, | ||
2537 | REG_UTP_TASK_REQ_DOOR_BELL, | ||
2538 | mask, 0, 1000, 1000); | ||
2539 | out: | ||
2540 | return err; | ||
2541 | } | ||
2542 | |||
2500 | /** | 2543 | /** |
2501 | * ufshcd_issue_tm_cmd - issues task management commands to controller | 2544 | * ufshcd_issue_tm_cmd - issues task management commands to controller |
2502 | * @hba: per adapter instance | 2545 | * @hba: per adapter instance |
2503 | * @lrbp: pointer to local reference block | 2546 | * @lun_id: LUN ID to which TM command is sent |
2547 | * @task_id: task ID to which the TM command is applicable | ||
2548 | * @tm_function: task management function opcode | ||
2549 | * @tm_response: task management service response return value | ||
2504 | * | 2550 | * |
2505 | * Returns SUCCESS/FAILED | 2551 | * Returns non-zero value on error, zero on success. |
2506 | */ | 2552 | */ |
2507 | static int | 2553 | static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id, |
2508 | ufshcd_issue_tm_cmd(struct ufs_hba *hba, | 2554 | u8 tm_function, u8 *tm_response) |
2509 | struct ufshcd_lrb *lrbp, | ||
2510 | u8 tm_function) | ||
2511 | { | 2555 | { |
2512 | struct utp_task_req_desc *task_req_descp; | 2556 | struct utp_task_req_desc *task_req_descp; |
2513 | struct utp_upiu_task_req *task_req_upiup; | 2557 | struct utp_upiu_task_req *task_req_upiup; |
2514 | struct Scsi_Host *host; | 2558 | struct Scsi_Host *host; |
2515 | unsigned long flags; | 2559 | unsigned long flags; |
2516 | int free_slot = 0; | 2560 | int free_slot; |
2517 | int err; | 2561 | int err; |
2562 | int task_tag; | ||
2518 | 2563 | ||
2519 | host = hba->host; | 2564 | host = hba->host; |
2520 | 2565 | ||
2521 | spin_lock_irqsave(host->host_lock, flags); | 2566 | /* |
2522 | 2567 | * Get free slot, sleep if slots are unavailable. | |
2523 | /* If task management queue is full */ | 2568 | * Even though we use wait_event() which sleeps indefinitely, |
2524 | free_slot = ufshcd_get_tm_free_slot(hba); | 2569 | * the maximum wait time is bounded by %TM_CMD_TIMEOUT. |
2525 | if (free_slot >= hba->nutmrs) { | 2570 | */ |
2526 | spin_unlock_irqrestore(host->host_lock, flags); | 2571 | wait_event(hba->tm_tag_wq, ufshcd_get_tm_free_slot(hba, &free_slot)); |
2527 | dev_err(hba->dev, "Task management queue full\n"); | ||
2528 | err = FAILED; | ||
2529 | goto out; | ||
2530 | } | ||
2531 | 2572 | ||
2573 | spin_lock_irqsave(host->host_lock, flags); | ||
2532 | task_req_descp = hba->utmrdl_base_addr; | 2574 | task_req_descp = hba->utmrdl_base_addr; |
2533 | task_req_descp += free_slot; | 2575 | task_req_descp += free_slot; |
2534 | 2576 | ||
@@ -2540,14 +2582,15 @@ ufshcd_issue_tm_cmd(struct ufs_hba *hba, | |||
2540 | /* Configure task request UPIU */ | 2582 | /* Configure task request UPIU */ |
2541 | task_req_upiup = | 2583 | task_req_upiup = |
2542 | (struct utp_upiu_task_req *) task_req_descp->task_req_upiu; | 2584 | (struct utp_upiu_task_req *) task_req_descp->task_req_upiu; |
2585 | task_tag = hba->nutrs + free_slot; | ||
2543 | task_req_upiup->header.dword_0 = | 2586 | task_req_upiup->header.dword_0 = |
2544 | UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0, | 2587 | UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0, |
2545 | lrbp->lun, lrbp->task_tag); | 2588 | lun_id, task_tag); |
2546 | task_req_upiup->header.dword_1 = | 2589 | task_req_upiup->header.dword_1 = |
2547 | UPIU_HEADER_DWORD(0, tm_function, 0, 0); | 2590 | UPIU_HEADER_DWORD(0, tm_function, 0, 0); |
2548 | 2591 | ||
2549 | task_req_upiup->input_param1 = cpu_to_be32(lrbp->lun); | 2592 | task_req_upiup->input_param1 = cpu_to_be32(lun_id); |
2550 | task_req_upiup->input_param2 = cpu_to_be32(lrbp->task_tag); | 2593 | task_req_upiup->input_param2 = cpu_to_be32(task_id); |
2551 | 2594 | ||
2552 | /* send command to the controller */ | 2595 | /* send command to the controller */ |
2553 | __set_bit(free_slot, &hba->outstanding_tasks); | 2596 | __set_bit(free_slot, &hba->outstanding_tasks); |
@@ -2556,20 +2599,24 @@ ufshcd_issue_tm_cmd(struct ufs_hba *hba, | |||
2556 | spin_unlock_irqrestore(host->host_lock, flags); | 2599 | spin_unlock_irqrestore(host->host_lock, flags); |
2557 | 2600 | ||
2558 | /* wait until the task management command is completed */ | 2601 | /* wait until the task management command is completed */ |
2559 | err = | 2602 | err = wait_event_timeout(hba->tm_wq, |
2560 | wait_event_interruptible_timeout(hba->ufshcd_tm_wait_queue, | 2603 | test_bit(free_slot, &hba->tm_condition), |
2561 | (test_bit(free_slot, | 2604 | msecs_to_jiffies(TM_CMD_TIMEOUT)); |
2562 | &hba->tm_condition) != 0), | ||
2563 | 60 * HZ); | ||
2564 | if (!err) { | 2605 | if (!err) { |
2565 | dev_err(hba->dev, | 2606 | dev_err(hba->dev, "%s: task management cmd 0x%.2x timed-out\n", |
2566 | "Task management command timed-out\n"); | 2607 | __func__, tm_function); |
2567 | err = FAILED; | 2608 | if (ufshcd_clear_tm_cmd(hba, free_slot)) |
2568 | goto out; | 2609 | dev_WARN(hba->dev, "%s: unable clear tm cmd (slot %d) after timeout\n", |
2610 | __func__, free_slot); | ||
2611 | err = -ETIMEDOUT; | ||
2612 | } else { | ||
2613 | err = ufshcd_task_req_compl(hba, free_slot, tm_response); | ||
2569 | } | 2614 | } |
2615 | |||
2570 | clear_bit(free_slot, &hba->tm_condition); | 2616 | clear_bit(free_slot, &hba->tm_condition); |
2571 | err = ufshcd_task_req_compl(hba, free_slot); | 2617 | ufshcd_put_tm_slot(hba, free_slot); |
2572 | out: | 2618 | wake_up(&hba->tm_tag_wq); |
2619 | |||
2573 | return err; | 2620 | return err; |
2574 | } | 2621 | } |
2575 | 2622 | ||
@@ -2586,14 +2633,21 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd) | |||
2586 | unsigned int tag; | 2633 | unsigned int tag; |
2587 | u32 pos; | 2634 | u32 pos; |
2588 | int err; | 2635 | int err; |
2636 | u8 resp = 0xF; | ||
2637 | struct ufshcd_lrb *lrbp; | ||
2589 | 2638 | ||
2590 | host = cmd->device->host; | 2639 | host = cmd->device->host; |
2591 | hba = shost_priv(host); | 2640 | hba = shost_priv(host); |
2592 | tag = cmd->request->tag; | 2641 | tag = cmd->request->tag; |
2593 | 2642 | ||
2594 | err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag], UFS_LOGICAL_RESET); | 2643 | lrbp = &hba->lrb[tag]; |
2595 | if (err == FAILED) | 2644 | err = ufshcd_issue_tm_cmd(hba, lrbp->lun, 0, UFS_LOGICAL_RESET, &resp); |
2645 | if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) { | ||
2646 | err = FAILED; | ||
2596 | goto out; | 2647 | goto out; |
2648 | } else { | ||
2649 | err = SUCCESS; | ||
2650 | } | ||
2597 | 2651 | ||
2598 | for (pos = 0; pos < hba->nutrs; pos++) { | 2652 | for (pos = 0; pos < hba->nutrs; pos++) { |
2599 | if (test_bit(pos, &hba->outstanding_reqs) && | 2653 | if (test_bit(pos, &hba->outstanding_reqs) && |
@@ -2650,6 +2704,8 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) | |||
2650 | unsigned long flags; | 2704 | unsigned long flags; |
2651 | unsigned int tag; | 2705 | unsigned int tag; |
2652 | int err; | 2706 | int err; |
2707 | u8 resp = 0xF; | ||
2708 | struct ufshcd_lrb *lrbp; | ||
2653 | 2709 | ||
2654 | host = cmd->device->host; | 2710 | host = cmd->device->host; |
2655 | hba = shost_priv(host); | 2711 | hba = shost_priv(host); |
@@ -2665,9 +2721,15 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) | |||
2665 | } | 2721 | } |
2666 | spin_unlock_irqrestore(host->host_lock, flags); | 2722 | spin_unlock_irqrestore(host->host_lock, flags); |
2667 | 2723 | ||
2668 | err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag], UFS_ABORT_TASK); | 2724 | lrbp = &hba->lrb[tag]; |
2669 | if (err == FAILED) | 2725 | err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, |
2726 | UFS_ABORT_TASK, &resp); | ||
2727 | if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) { | ||
2728 | err = FAILED; | ||
2670 | goto out; | 2729 | goto out; |
2730 | } else { | ||
2731 | err = SUCCESS; | ||
2732 | } | ||
2671 | 2733 | ||
2672 | scsi_dma_unmap(cmd); | 2734 | scsi_dma_unmap(cmd); |
2673 | 2735 | ||
@@ -2890,7 +2952,8 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle, | |||
2890 | host->max_cmd_len = MAX_CDB_SIZE; | 2952 | host->max_cmd_len = MAX_CDB_SIZE; |
2891 | 2953 | ||
2892 | /* Initailize wait queue for task management */ | 2954 | /* Initailize wait queue for task management */ |
2893 | init_waitqueue_head(&hba->ufshcd_tm_wait_queue); | 2955 | init_waitqueue_head(&hba->tm_wq); |
2956 | init_waitqueue_head(&hba->tm_tag_wq); | ||
2894 | 2957 | ||
2895 | /* Initialize work queues */ | 2958 | /* Initialize work queues */ |
2896 | INIT_WORK(&hba->feh_workq, ufshcd_fatal_err_handler); | 2959 | INIT_WORK(&hba->feh_workq, ufshcd_fatal_err_handler); |