Merge "scsi: ufs: fix shutdown race condition"

This commit is contained in:
Linux Build Service Account 2016-12-09 19:59:29 -08:00 committed by Gerrit - the friendly Code Review server
commit 4a2acf1a09
2 changed files with 16 additions and 17 deletions

View file

@ -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);

View file

@ -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;