Merge "scsi: ufs: fix shutdown race condition"
This commit is contained in:
commit
4a2acf1a09
2 changed files with 16 additions and 17 deletions
|
@ -2697,10 +2697,9 @@ static inline u16 ufshcd_upiu_wlun_to_scsi_wlun(u8 upiu_wlun_id)
|
||||||
* Lock is predominantly held by shutdown context thus, ensuring
|
* Lock is predominantly held by shutdown context thus, ensuring
|
||||||
* that no requests from any other context may sneak through.
|
* that no requests from any other context may sneak through.
|
||||||
*/
|
*/
|
||||||
static void ufshcd_get_write_lock(struct ufs_hba *hba)
|
static inline void ufshcd_get_write_lock(struct ufs_hba *hba)
|
||||||
{
|
{
|
||||||
down_write(&hba->lock);
|
down_write(&hba->lock);
|
||||||
hba->issuing_task = current;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2710,18 +2709,19 @@ static void ufshcd_get_write_lock(struct ufs_hba *hba)
|
||||||
*
|
*
|
||||||
* Returns 1 if acquired, < 0 on contention
|
* Returns 1 if acquired, < 0 on contention
|
||||||
*
|
*
|
||||||
* After shutdown's initiated, allow requests only from shutdown
|
* After shutdown's initiated, allow requests only directed to the
|
||||||
* context. The sync between scaling & issue is maintained
|
* well known device lun. The sync between scaling & issue is maintained
|
||||||
* as is and this restructuring syncs shutdown with these too.
|
* as is and this restructuring syncs shutdown with these too.
|
||||||
*/
|
*/
|
||||||
static int ufshcd_get_read_lock(struct ufs_hba *hba)
|
static int ufshcd_get_read_lock(struct ufs_hba *hba, u64 lun)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
err = down_read_trylock(&hba->lock);
|
err = down_read_trylock(&hba->lock);
|
||||||
if (err > 0)
|
if (err > 0)
|
||||||
goto out;
|
goto out;
|
||||||
if (hba->issuing_task == current)
|
/* let requests for well known device lun to go through */
|
||||||
|
if (ufshcd_scsi_to_upiu_lun(lun) == UFS_UPIU_UFS_DEVICE_WLUN)
|
||||||
return 0;
|
return 0;
|
||||||
else if (!ufshcd_is_shutdown_ongoing(hba))
|
else if (!ufshcd_is_shutdown_ongoing(hba))
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
@ -2729,7 +2729,6 @@ static int ufshcd_get_read_lock(struct ufs_hba *hba)
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
hba->issuing_task = current;
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2742,10 +2741,7 @@ out:
|
||||||
*/
|
*/
|
||||||
static inline void ufshcd_put_read_lock(struct ufs_hba *hba)
|
static inline void ufshcd_put_read_lock(struct ufs_hba *hba)
|
||||||
{
|
{
|
||||||
if (!ufshcd_is_shutdown_ongoing(hba)) {
|
up_read(&hba->lock);
|
||||||
hba->issuing_task = NULL;
|
|
||||||
up_read(&hba->lock);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2762,6 +2758,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int tag;
|
int tag;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
bool has_read_lock = false;
|
||||||
|
|
||||||
hba = shost_priv(host);
|
hba = shost_priv(host);
|
||||||
|
|
||||||
|
@ -2773,7 +2770,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
|
||||||
BUG();
|
BUG();
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ufshcd_get_read_lock(hba);
|
err = ufshcd_get_read_lock(hba, cmd->device->lun);
|
||||||
if (unlikely(err < 0)) {
|
if (unlikely(err < 0)) {
|
||||||
if (err == -EPERM) {
|
if (err == -EPERM) {
|
||||||
set_host_byte(cmd, DID_ERROR);
|
set_host_byte(cmd, DID_ERROR);
|
||||||
|
@ -2782,6 +2779,8 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
|
||||||
}
|
}
|
||||||
if (err == -EAGAIN)
|
if (err == -EAGAIN)
|
||||||
return SCSI_MLQUEUE_HOST_BUSY;
|
return SCSI_MLQUEUE_HOST_BUSY;
|
||||||
|
} else if (err == 1) {
|
||||||
|
has_read_lock = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(hba->host->host_lock, flags);
|
spin_lock_irqsave(hba->host->host_lock, flags);
|
||||||
|
@ -2922,7 +2921,8 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
|
||||||
out_unlock:
|
out_unlock:
|
||||||
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||||
out:
|
out:
|
||||||
ufshcd_put_read_lock(hba);
|
if (has_read_lock)
|
||||||
|
ufshcd_put_read_lock(hba);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8808,13 +8808,13 @@ int ufshcd_shutdown(struct ufs_hba *hba)
|
||||||
pm_runtime_get_sync(hba->dev);
|
pm_runtime_get_sync(hba->dev);
|
||||||
ufshcd_hold_all(hba);
|
ufshcd_hold_all(hba);
|
||||||
/**
|
/**
|
||||||
* (1) Acquire the lock to stop any more requests
|
* (1) Set state to shutting down
|
||||||
* (2) Set state to shutting down
|
* (2) Acquire the lock to stop any more requests
|
||||||
* (3) Suspend clock scaling
|
* (3) Suspend clock scaling
|
||||||
* (4) Wait for all issued requests to complete
|
* (4) Wait for all issued requests to complete
|
||||||
*/
|
*/
|
||||||
ufshcd_get_write_lock(hba);
|
|
||||||
ufshcd_mark_shutdown_ongoing(hba);
|
ufshcd_mark_shutdown_ongoing(hba);
|
||||||
|
ufshcd_get_write_lock(hba);
|
||||||
ufshcd_scsi_block_requests(hba);
|
ufshcd_scsi_block_requests(hba);
|
||||||
ufshcd_suspend_clkscaling(hba);
|
ufshcd_suspend_clkscaling(hba);
|
||||||
ret = ufshcd_wait_for_doorbell_clr(hba, U64_MAX);
|
ret = ufshcd_wait_for_doorbell_clr(hba, U64_MAX);
|
||||||
|
|
|
@ -897,7 +897,6 @@ struct ufs_hba {
|
||||||
|
|
||||||
/* sync b/w diff contexts */
|
/* sync b/w diff contexts */
|
||||||
struct rw_semaphore lock;
|
struct rw_semaphore lock;
|
||||||
struct task_struct *issuing_task;
|
|
||||||
unsigned long shutdown_in_prog;
|
unsigned long shutdown_in_prog;
|
||||||
|
|
||||||
struct reset_control *core_reset;
|
struct reset_control *core_reset;
|
||||||
|
|
Loading…
Add table
Reference in a new issue