From d6bbd448c783a92e425f928e32ec470a9ebe2cb2 Mon Sep 17 00:00:00 2001 From: Andrey Markovytch Date: Sun, 29 Jan 2017 09:36:34 +0200 Subject: ufs: add additional sync between ice work queue and pending request Fixes the issue where the job could have been scheduled with request that was about to be released thus causing crash with stale pointer. Change-Id: I4ed1f08ed810303738c05d08f27a8ea21ba1e4f7 Signed-off-by: Andrey Markovytch --- drivers/scsi/ufs/ufs-qcom-ice.c | 46 ++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/ufs/ufs-qcom-ice.c b/drivers/scsi/ufs/ufs-qcom-ice.c index 85f82b2251c1..814d1dcfe90e 100644 --- a/drivers/scsi/ufs/ufs-qcom-ice.c +++ b/drivers/scsi/ufs/ufs-qcom-ice.c @@ -173,10 +173,19 @@ static void ufs_qcom_ice_cfg_work(struct work_struct *work) struct ice_data_setting ice_set; struct ufs_qcom_host *qcom_host = container_of(work, struct ufs_qcom_host, ice_cfg_work); + struct request *req_pending = NULL; - if (!qcom_host->ice.vops->config_start || !qcom_host->req_pending) + if (!qcom_host->ice.vops->config_start) return; + spin_lock_irqsave(&qcom_host->ice_work_lock, flags); + req_pending = qcom_host->req_pending; + if (!req_pending) { + spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags); + return; + } + spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags); + /* * config_start is called again as previous attempt returned -EAGAIN, * this call shall now take care of the necessary key setup. @@ -263,6 +272,10 @@ int ufs_qcom_ice_req_setup(struct ufs_qcom_host *qcom_host, if (qcom_host->ice.vops->config_start) { memset(&ice_set, 0, sizeof(ice_set)); + + spin_lock_irqsave( + &qcom_host->ice_work_lock, flags); + err = qcom_host->ice.vops->config_start(qcom_host->ice.pdev, cmd->request, &ice_set, true); if (err) { @@ -281,13 +294,11 @@ int ufs_qcom_ice_req_setup(struct ufs_qcom_host *qcom_host, "%s: scheduling task for ice setup\n", __func__); - spin_lock_irqsave( - &qcom_host->ice_work_lock, flags); - if (!qcom_host->req_pending) { ufshcd_scsi_block_requests( qcom_host->hba); qcom_host->req_pending = cmd->request; + if (!schedule_work( &qcom_host->ice_cfg_work)) { qcom_host->req_pending = NULL; @@ -302,9 +313,6 @@ int ufs_qcom_ice_req_setup(struct ufs_qcom_host *qcom_host, } } - spin_unlock_irqrestore( - &qcom_host->ice_work_lock, flags); - } else { if (err != -EBUSY) dev_err(qcom_host->hba->dev, @@ -312,9 +320,14 @@ int ufs_qcom_ice_req_setup(struct ufs_qcom_host *qcom_host, __func__, err); } + spin_unlock_irqrestore(&qcom_host->ice_work_lock, + flags); + return err; } + spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags); + if (ufs_qcom_is_data_cmd(cmd_op, true)) *enable = !ice_set.encr_bypass; else if (ufs_qcom_is_data_cmd(cmd_op, false)) @@ -380,8 +393,13 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host, return -EINVAL; } - memset(&ice_set, 0, sizeof(ice_set)); + if (qcom_host->ice.vops->config_start) { + memset(&ice_set, 0, sizeof(ice_set)); + + spin_lock_irqsave( + &qcom_host->ice_work_lock, flags); + err = qcom_host->ice.vops->config_start(qcom_host->ice.pdev, req, &ice_set, true); if (err) { @@ -401,9 +419,6 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host, "%s: scheduling task for ice setup\n", __func__); - spin_lock_irqsave( - &qcom_host->ice_work_lock, flags); - if (!qcom_host->req_pending) { ufshcd_scsi_block_requests( qcom_host->hba); @@ -422,9 +437,6 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host, } } - spin_unlock_irqrestore( - &qcom_host->ice_work_lock, flags); - } else { if (err != -EBUSY) dev_err(qcom_host->hba->dev, @@ -432,8 +444,14 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host, __func__, err); } + spin_unlock_irqrestore( + &qcom_host->ice_work_lock, flags); + return err; } + + spin_unlock_irqrestore( + &qcom_host->ice_work_lock, flags); } cmd_op = cmd->cmnd[0]; -- cgit v1.2.3