RE: [PATCH V5 1/4] scsi: ufs: Fix broken task management command implementation

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

 



Reviewed-by: Yaniv Gardi <ygardi@xxxxxxxxxxxxxx>

QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation


= > -----Original Message-----
= > From: linux-scsi-owner@xxxxxxxxxxxxxxx [mailto:linux-scsi-
= > owner@xxxxxxxxxxxxxxx] On Behalf Of Dolev Raviv
= > Sent: Monday, August 12, 2013 4:01 PM
= > To: Sujit Reddy Thumma
= > Cc: Vinayak Holikatti; Santosh Y; James E.J. Bottomley; linux-
= > scsi@xxxxxxxxxxxxxxx; Sujit Reddy Thumma; linux-arm-
= > msm@xxxxxxxxxxxxxxx
= > Subject: Re: [PATCH V5 1/4] scsi: ufs: Fix broken task management
= > command implementation
= > 
= > Tested-by: Dolev Raviv <draviv@xxxxxxxxxxxxxx>
= > 
= > > 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 |  173
= > > ++++++++++++++++++++++++++++++---------------
= > >  drivers/scsi/ufs/ufshcd.h |    8 ++-
= > >  2 files changed, 122 insertions(+), 59 deletions(-)
= > >
= > > diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
= > > index b36ca9a..d7f3746 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
= > >
= > > @@ -183,13 +186,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)
= > >  {
= > > -	return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs);
= > > +	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)
= > > +{
= > > +	clear_bit_unlock(slot, &hba->tm_slots_in_use);
= > >  }
= > >
= > >  /**
= > > @@ -1700,10 +1725,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; @@ -1724,19 +1750,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;
= > >  }
= > >
= > >  /**
= > > @@ -2237,7 +2259,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);
= > >  }
= > >
= > >  /**
= > > @@ -2287,38 +2309,58 @@ 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 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 */
= > > +	err = ufshcd_wait_for_register(hba,
= > > +			REG_UTP_TASK_REQ_DOOR_BELL,
= > > +			mask, 0, 1000, 1000);
= > > +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;
= > >
= > > @@ -2330,18 +2372,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;
= > >  	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); @@ -2350,20
= > +2389,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;
= > >  }
= > >
= > > @@ -2380,14 +2423,21 @@ static int ufshcd_device_reset(struct
= > > scsi_cmnd
= > > *cmd)
= > >  	unsigned int tag;
= > >  	u32 pos;
= > >  	int err;
= > > +	u8 resp = 0xF;
= > > +	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, 0,
= > UFS_LOGICAL_RESET, &resp);
= > > +	if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
= > > +		err = FAILED;
= > >  		goto out;
= > > +	} else {
= > > +		err = SUCCESS;
= > > +	}
= > >
= > >  	for (pos = 0; pos < hba->nutrs; pos++) {
= > >  		if (test_bit(pos, &hba->outstanding_reqs) && @@ -2444,6
= > +2494,8 @@
= > > static int ufshcd_abort(struct scsi_cmnd *cmd)
= > >  	unsigned long flags;
= > >  	unsigned int tag;
= > >  	int err;
= > > +	u8 resp = 0xF;
= > > +	struct ufshcd_lrb *lrbp;
= > >
= > >  	host = cmd->device->host;
= > >  	hba = shost_priv(host);
= > > @@ -2459,9 +2511,15 @@ static int ufshcd_abort(struct scsi_cmnd
= > *cmd)
= > >  	}
= > >  	spin_unlock_irqrestore(host->host_lock, flags);
= > >
= > > -	err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag],
= > UFS_ABORT_TASK);
= > > -	if (err == FAILED)
= > > +	lrbp = &hba->lrb[tag];
= > > +	err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag,
= > > +			UFS_ABORT_TASK, &resp);
= > > +	if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
= > > +		err = FAILED;
= > >  		goto out;
= > > +	} else {
= > > +		err = SUCCESS;
= > > +	}
= > >
= > >  	scsi_dma_unmap(cmd);
= > >
= > > @@ -2682,7 +2740,8 @@ int ufshcd_init(struct device *dev, struct
= > > ufs_hba **hba_handle,
= > >  	host->max_cmd_len = MAX_CDB_SIZE;
= > >
= > >  	/* Initailize wait queue for task management */
= > > -	init_waitqueue_head(&hba->ufshcd_tm_wait_queue);
= > > +	init_waitqueue_head(&hba->tm_wq);
= > > +	init_waitqueue_head(&hba->tm_tag_wq);
= > >
= > >  	/* Initialize work queues */
= > >  	INIT_WORK(&hba->feh_workq, ufshcd_fatal_err_handler); diff --
= > git
= > > a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index
= > > 59c9c48..fe7c947 100644
= > > --- a/drivers/scsi/ufs/ufshcd.h
= > > +++ b/drivers/scsi/ufs/ufshcd.h
= > > @@ -174,8 +174,10 @@ struct ufs_dev_cmd {
= > >   * @irq: Irq number of the controller
= > >   * @active_uic_cmd: handle of active UIC command
= > >   * @uic_cmd_mutex: mutex for uic command
= > > - * @ufshcd_tm_wait_queue: wait queue for task management
= > > + * @tm_wq: wait queue for task management
= > > + * @tm_tag_wq: wait queue for free task management slots
= > >   * @tm_condition: condition variable for task management
= > > + * @tm_slots_in_use: bit map of task management request slots in use
= > >   * @ufshcd_state: UFSHCD states
= > >   * @intr_mask: Interrupt Mask Bits
= > >   * @ee_ctrl_mask: Exception event control mask @@ -216,8 +218,10
= > @@
= > > struct ufs_hba {
= > >  	struct uic_command *active_uic_cmd;
= > >  	struct mutex uic_cmd_mutex;
= > >
= > > -	wait_queue_head_t ufshcd_tm_wait_queue;
= > > +	wait_queue_head_t tm_wq;
= > > +	wait_queue_head_t tm_tag_wq;
= > >  	unsigned long tm_condition;
= > > +	unsigned long tm_slots_in_use;
= > >
= > >  	u32 ufshcd_state;
= > >  	u32 intr_mask;
= > > --
= > > QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a
= > > member of Code Aurora Forum, hosted by The Linux Foundation.
= > >
= > > --
= > > To unsubscribe from this list: send the line "unsubscribe linux-scsi"
= > > in the body of a message to majordomo@xxxxxxxxxxxxxxx More
= > majordomo
= > > info at  http://vger.kernel.org/majordomo-info.html
= > >
= > 
= > 
= > --
= > QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a
= > member of Code Aurora Forum, hosted by The Linux Foundation
= > 
= > --
= > To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the
= > body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info
= > at  http://vger.kernel.org/majordomo-info.html

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



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux