Re: [PATCH V3 1/4] scsi: ufs: Fix broken task management command implementation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 7/23/2013 1:54 PM, Seungwon Jeon wrote:
> On Sat, July 20, 2013, Sujit Reddy Thumma wrote:
>> On 7/19/2013 7:26 PM, Seungwon Jeon wrote:
>>> On Tue, July 09, 2013 Sujit Reddy Thumma wrote:
>>>> 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@xxxxxxxxxxxxxx>
>>>> ---
>>>>    drivers/scsi/ufs/ufshcd.c |  177 ++++++++++++++++++++++++++++++---------------
>>>>    drivers/scsi/ufs/ufshcd.h |    8 ++-
>>>>    2 files changed, 126 insertions(+), 59 deletions(-)
>>>>
>>>> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
>>>> index af7d01d..a176421 100644
>>>> --- a/drivers/scsi/ufs/ufshcd.c
>>>> +++ b/drivers/scsi/ufs/ufshcd.c
>>>> @@ -53,6 +53,9 @@
>>>>    /* Query request timeout */
>>>>    #define QUERY_REQ_TIMEOUT 30 /* msec */
>>>>
>>>> +/* Task management command timeout */
>>>> +#define TM_CMD_TIMEOUT	100 /* msecs */
>>>> +
>>>>    /* Expose the flag value from utp_upiu_query.value */
>>>>    #define MASK_QUERY_UPIU_FLAG_LOC 0xFF
>>>>
>>>> @@ -190,13 +193,35 @@ ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp)
>>>>    /**
>>>>     * ufshcd_get_tm_free_slot - get a free slot for task management request
>>>>     * @hba: per adapter instance
>>>> + * @free_slot: pointer to variable with available slot value
>>>>     *
>>>> - * Returns maximum number of task management request slots in case of
>>>> - * task management queue full or returns the free slot number
>>>> + * Get a free tag and lock it until ufshcd_put_tm_slot() is called.
>>>> + * Returns 0 if free slot is not available, else return 1 with tag value
>>>> + * in @free_slot.
>>>>     */
>>>> -static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba)
>>>> +static bool ufshcd_get_tm_free_slot(struct ufs_hba *hba, int *free_slot)
>>>> +{
>>>> +	int tag;
>>>> +	bool ret = false;
>>>> +
>>>> +	if (!free_slot)
>>>> +		goto out;
>>>> +
>>>> +	do {
>>>> +		tag = find_first_zero_bit(&hba->tm_slots_in_use, hba->nutmrs);
>>>> +		if (tag >= hba->nutmrs)
>>>> +			goto out;
>>>> +	} while (test_and_set_bit_lock(tag, &hba->tm_slots_in_use));
>>>> +
>>>> +	*free_slot = tag;
>>>> +	ret = true;
>>>> +out:
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static inline void ufshcd_put_tm_slot(struct ufs_hba *hba, int slot)
>>>>    {
>>>> -	return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs);
>>>> +	clear_bit_unlock(slot, &hba->tm_slots_in_use);
>>>>    }
>>>>
>>>>    /**
>>>> @@ -1778,10 +1803,11 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev)
>>>>     * ufshcd_task_req_compl - handle task management request completion
>>>>     * @hba: per adapter instance
>>>>     * @index: index of the completed request
>>>> + * @resp: task management service response
>>>>     *
>>>> - * Returns SUCCESS/FAILED
>>>> + * Returns non-zero value on error, zero on success
>>>>     */
>>>> -static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index)
>>>> +static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp)
>>>>    {
>>>>    	struct utp_task_req_desc *task_req_descp;
>>>>    	struct utp_upiu_task_rsp *task_rsp_upiup;
>>>> @@ -1802,19 +1828,15 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index)
>>>>    				task_req_descp[index].task_rsp_upiu;
>>>>    		task_result = be32_to_cpu(task_rsp_upiup->header.dword_1);
>>>>    		task_result = ((task_result & MASK_TASK_RESPONSE) >> 8);
>>>> -
>>>> -		if (task_result != UPIU_TASK_MANAGEMENT_FUNC_COMPL &&
>>>> -		    task_result != UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED)
>>>> -			task_result = FAILED;
>>>> -		else
>>>> -			task_result = SUCCESS;
>>>> +		if (resp)
>>>> +			*resp = (u8)task_result;
>>>>    	} else {
>>>> -		task_result = FAILED;
>>>> -		dev_err(hba->dev,
>>>> -			"trc: Invalid ocs = %x\n", ocs_value);
>>>> +		dev_err(hba->dev, "%s: failed, ocs = 0x%x\n",
>>>> +				__func__, ocs_value);
>>>>    	}
>>>>    	spin_unlock_irqrestore(hba->host->host_lock, flags);
>>>> -	return task_result;
>>>> +
>>>> +	return ocs_value;
>>>>    }
>>>>
>>>>    /**
>>>> @@ -2298,7 +2320,7 @@ static void ufshcd_tmc_handler(struct ufs_hba *hba)
>>>>
>>>>    	tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
>>>>    	hba->tm_condition = tm_doorbell ^ hba->outstanding_tasks;
>>>> -	wake_up_interruptible(&hba->ufshcd_tm_wait_queue);
>>>> +	wake_up(&hba->tm_wq);
>>>>    }
>>>>
>>>>    /**
>>>> @@ -2348,38 +2370,61 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba)
>>>>    	return retval;
>>>>    }
>>>>
>>>> +static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag)
>>>> +{
>>>> +	int err = 0;
>>>> +	u32 reg;
>>>> +	u32 mask = 1 << tag;
>>>> +	unsigned long flags;
>>>> +
>>>> +	if (!test_bit(tag, &hba->outstanding_reqs))
>>>> +		goto out;
>>>> +
>>>> +	spin_lock_irqsave(hba->host->host_lock, flags);
>>>> +	ufshcd_writel(hba, ~(1 << tag), REG_UTP_TASK_REQ_LIST_CLEAR);
>>>> +	spin_unlock_irqrestore(hba->host->host_lock, flags);
>>>> +
>>>> +	/* poll for max. 1 sec to clear door bell register by h/w */
>>>> +	reg = ufshcd_wait_for_register(hba,
>>>> +			REG_UTP_TASK_REQ_DOOR_BELL,
>>>> +			mask, 0, 1000, 1000);
>>>> +	if ((reg & mask) == mask)
>>>> +		err = -ETIMEDOUT;
>>>> +out:
>>>> +	return err;
>>>> +}
>>>> +
>>>>    /**
>>>>     * ufshcd_issue_tm_cmd - issues task management commands to controller
>>>>     * @hba: per adapter instance
>>>> - * @lrbp: pointer to local reference block
>>>> + * @lun_id: LUN ID to which TM command is sent
>>>> + * @task_id: task ID to which the TM command is applicable
>>>> + * @tm_function: task management function opcode
>>>> + * @tm_response: task management service response return value
>>>>     *
>>>> - * Returns SUCCESS/FAILED
>>>> + * Returns non-zero value on error, zero on success.
>>>>     */
>>>> -static int
>>>> -ufshcd_issue_tm_cmd(struct ufs_hba *hba,
>>>> -		    struct ufshcd_lrb *lrbp,
>>>> -		    u8 tm_function)
>>>> +static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id,
>>>> +		u8 tm_function, u8 *tm_response)
>>>>    {
>>>>    	struct utp_task_req_desc *task_req_descp;
>>>>    	struct utp_upiu_task_req *task_req_upiup;
>>>>    	struct Scsi_Host *host;
>>>>    	unsigned long flags;
>>>> -	int free_slot = 0;
>>>> +	int free_slot;
>>>>    	int err;
>>>> +	int task_tag;
>>>>
>>>>    	host = hba->host;
>>>>
>>>> -	spin_lock_irqsave(host->host_lock, flags);
>>>> -
>>>> -	/* If task management queue is full */
>>>> -	free_slot = ufshcd_get_tm_free_slot(hba);
>>>> -	if (free_slot >= hba->nutmrs) {
>>>> -		spin_unlock_irqrestore(host->host_lock, flags);
>>>> -		dev_err(hba->dev, "Task management queue full\n");
>>>> -		err = FAILED;
>>>> -		goto out;
>>>> -	}
>>>> +	/*
>>>> +	 * Get free slot, sleep if slots are unavailable.
>>>> +	 * Even though we use wait_event() which sleeps indefinitely,
>>>> +	 * the maximum wait time is bounded by %TM_CMD_TIMEOUT.
>>>> +	 */
>>>> +	wait_event(hba->tm_tag_wq, ufshcd_get_tm_free_slot(hba, &free_slot));
>>>>
>>>> +	spin_lock_irqsave(host->host_lock, flags);
>>>>    	task_req_descp = hba->utmrdl_base_addr;
>>>>    	task_req_descp += free_slot;
>>>>
>>>> @@ -2391,18 +2436,15 @@ ufshcd_issue_tm_cmd(struct ufs_hba *hba,
>>>>    	/* Configure task request UPIU */
>>>>    	task_req_upiup =
>>>>    		(struct utp_upiu_task_req *) task_req_descp->task_req_upiu;
>>>> +	task_tag = hba->nutrs + free_slot;
>>> Possible, did you intend 'hba->nutmrs', not 'hba->nutrs'?
>>> I think it's safer with hba->nutmrs if we can't sure that NUTRS is larger than NUTMRS.
>>
>> It should be hba->nutrs and not hba->nutmrs.
>>
>> The equation is -
>> 0 <= free_slot < hba->nutmrs
>> 0 <= transfer_req_task_id < hba->nutrs
>> hba->nutrs <= tm_req_task_id < hba->nutmrs + hba_nutrs
>>
>> Whatever be the values of NUTRS/NUTMRS the above gives a unique
>> task_id.
> Yes.
> 
>>
>>
>>>
>>>>    	task_req_upiup->header.dword_0 =
>>>>    		UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
>>>> -					      lrbp->lun, lrbp->task_tag);
>>>> +				lun_id, task_tag);
>>>>    	task_req_upiup->header.dword_1 =
>>>>    		UPIU_HEADER_DWORD(0, tm_function, 0, 0);
>>>>
>>>> -	task_req_upiup->input_param1 = lrbp->lun;
>>>> -	task_req_upiup->input_param1 =
>>>> -		cpu_to_be32(task_req_upiup->input_param1);
>>>> -	task_req_upiup->input_param2 = lrbp->task_tag;
>>>> -	task_req_upiup->input_param2 =
>>>> -		cpu_to_be32(task_req_upiup->input_param2);
>>>> +	task_req_upiup->input_param1 = cpu_to_be32(lun_id);
>>>> +	task_req_upiup->input_param2 = cpu_to_be32(task_id);
>>>>
>>>>    	/* send command to the controller */
>>>>    	__set_bit(free_slot, &hba->outstanding_tasks);
>>>> @@ -2411,20 +2453,24 @@ ufshcd_issue_tm_cmd(struct ufs_hba *hba,
>>>>    	spin_unlock_irqrestore(host->host_lock, flags);
>>>>
>>>>    	/* wait until the task management command is completed */
>>>> -	err =
>>>> -	wait_event_interruptible_timeout(hba->ufshcd_tm_wait_queue,
>>>> -					 (test_bit(free_slot,
>>>> -					 &hba->tm_condition) != 0),
>>>> -					 60 * HZ);
>>>> +	err = wait_event_timeout(hba->tm_wq,
>>>> +			test_bit(free_slot, &hba->tm_condition),
>>>> +			msecs_to_jiffies(TM_CMD_TIMEOUT));
>>>>    	if (!err) {
>>>> -		dev_err(hba->dev,
>>>> -			"Task management command timed-out\n");
>>>> -		err = FAILED;
>>>> -		goto out;
>>>> +		dev_err(hba->dev, "%s: task management cmd 0x%.2x timed-out\n",
>>>> +				__func__, tm_function);
>>>> +		if (ufshcd_clear_tm_cmd(hba, free_slot))
>>>> +			dev_WARN(hba->dev, "%s: unable clear tm cmd (slot %d) after timeout\n",
>>>> +					__func__, free_slot);
>>>> +		err = -ETIMEDOUT;
>>>> +	} else {
>>>> +		err = ufshcd_task_req_compl(hba, free_slot, tm_response);
>>>>    	}
>>>> +
>>>>    	clear_bit(free_slot, &hba->tm_condition);
>>>> -	err = ufshcd_task_req_compl(hba, free_slot);
>>>> -out:
>>>> +	ufshcd_put_tm_slot(hba, free_slot);
>>>> +	wake_up(&hba->tm_tag_wq);
>>>> +
>>>>    	return err;
>>>>    }
>>>>
>>>> @@ -2441,14 +2487,22 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd)
>>>>    	unsigned int tag;
>>>>    	u32 pos;
>>>>    	int err;
>>>> +	u8 resp;
>>>> +	struct ufshcd_lrb *lrbp;
>>>>
>>>>    	host = cmd->device->host;
>>>>    	hba = shost_priv(host);
>>>>    	tag = cmd->request->tag;
>>>>
>>>> -	err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag], UFS_LOGICAL_RESET);
>>>> -	if (err == FAILED)
>>>> +	lrbp = &hba->lrb[tag];
>>>> +	err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag,
>>> Argument 2nd, 3rd can be replaced by lrbp.
>>> Then, we can reduce the number of argument.
>>>
>>
>> TM issue command doesn't need to know about lrbp, It just need
>> LUN ID and task ID. This helps when we are not dealing with lrbp's
>> and just want to issue some other TM command.
>> I believe an extra argument is not so costly on the systems which
>> demand high performance UFS devices.
> Yes, you're right. only need LUN ID and task ID.
> It might be trivial. But 'lrbp' should be referred for getting these.
> 

Whatever way the caller gets lun_id/task_id is not of a concern for
ufshcd_issue_tm_cmd().

I prefer this way even though lrbp is anyway needed to determine IDs.

--
Regards,
Sujit
--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [Linux for Sparc]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux