summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/cxlflash/superpipe.c
diff options
context:
space:
mode:
authorMatthew R. Ochs <mrochs@linux.vnet.ibm.com>2015-10-21 22:11:52 +0200
committerJames Bottomley <JBottomley@Odin.com>2015-10-30 09:02:40 +0100
commit0a27ae514740b4d64b586043d6b837ad5d0c40f8 (patch)
treee5a30419013e20bc2ea3ebb8f17f16dfe36639be /drivers/scsi/cxlflash/superpipe.c
parentcxlflash: Fix context encode mask width (diff)
downloadlinux-0a27ae514740b4d64b586043d6b837ad5d0c40f8.tar.xz
linux-0a27ae514740b4d64b586043d6b837ad5d0c40f8.zip
cxlflash: Fix to avoid CXL services during EEH
During an EEH freeze event, certain CXL services should not be called until after the hardware reset has taken place. Doing so can result in unnecessary failures and possibly cause other ill effects by triggering hardware accesses. This translates to a requirement to quiesce all threads that may potentially use CXL runtime service during this window. In particular, multiple ioctls make use of the CXL services when acting on contexts on behalf of the user. Thus, it is essential to 'drain' running ioctls _before_ proceeding with handling the EEH freeze event. Create the ability to drain ioctls by wrapping the ioctl handler call in a read semaphore and then implementing a small routine that obtains the write semaphore, effectively creating a wait point for all currently executing ioctls. Signed-off-by: Matthew R. Ochs <mrochs@linux.vnet.ibm.com> Signed-off-by: Manoj N. Kumar <manoj@linux.vnet.ibm.com> Reviewed-by: Brian King <brking@linux.vnet.ibm.com> Reviewed-by: Daniel Axtens <dja@axtens.net> Reviewed-by: Tomas Henzl <thenzl@redhat.com> Signed-off-by: James Bottomley <JBottomley@Odin.com>
Diffstat (limited to 'drivers/scsi/cxlflash/superpipe.c')
-rw-r--r--drivers/scsi/cxlflash/superpipe.c98
1 files changed, 60 insertions, 38 deletions
diff --git a/drivers/scsi/cxlflash/superpipe.c b/drivers/scsi/cxlflash/superpipe.c
index c75f3ef6a7fa..6e8fc1125e53 100644
--- a/drivers/scsi/cxlflash/superpipe.c
+++ b/drivers/scsi/cxlflash/superpipe.c
@@ -1214,6 +1214,46 @@ static const struct file_operations null_fops = {
};
/**
+ * check_state() - checks and responds to the current adapter state
+ * @cfg: Internal structure associated with the host.
+ *
+ * This routine can block and should only be used on process context.
+ * It assumes that the caller is an ioctl thread and holding the ioctl
+ * read semaphore. This is temporarily let up across the wait to allow
+ * for draining actively running ioctls. Also note that when waking up
+ * from waiting in reset, the state is unknown and must be checked again
+ * before proceeding.
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int check_state(struct cxlflash_cfg *cfg)
+{
+ struct device *dev = &cfg->dev->dev;
+ int rc = 0;
+
+retry:
+ switch (cfg->state) {
+ case STATE_LIMBO:
+ dev_dbg(dev, "%s: Limbo state, going to wait...\n", __func__);
+ up_read(&cfg->ioctl_rwsem);
+ rc = wait_event_interruptible(cfg->limbo_waitq,
+ cfg->state != STATE_LIMBO);
+ down_read(&cfg->ioctl_rwsem);
+ if (unlikely(rc))
+ break;
+ goto retry;
+ case STATE_FAILTERM:
+ dev_dbg(dev, "%s: Failed/Terminating!\n", __func__);
+ rc = -ENODEV;
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+/**
* cxlflash_disk_attach() - attach a LUN to a context
* @sdev: SCSI device associated with LUN.
* @attach: Attach ioctl data structure.
@@ -1523,41 +1563,6 @@ err1:
}
/**
- * check_state() - checks and responds to the current adapter state
- * @cfg: Internal structure associated with the host.
- *
- * This routine can block and should only be used on process context.
- * Note that when waking up from waiting in limbo, the state is unknown
- * and must be checked again before proceeding.
- *
- * Return: 0 on success, -errno on failure
- */
-static int check_state(struct cxlflash_cfg *cfg)
-{
- struct device *dev = &cfg->dev->dev;
- int rc = 0;
-
-retry:
- switch (cfg->state) {
- case STATE_LIMBO:
- dev_dbg(dev, "%s: Limbo, going to wait...\n", __func__);
- rc = wait_event_interruptible(cfg->limbo_waitq,
- cfg->state != STATE_LIMBO);
- if (unlikely(rc))
- break;
- goto retry;
- case STATE_FAILTERM:
- dev_dbg(dev, "%s: Failed/Terminating!\n", __func__);
- rc = -ENODEV;
- break;
- default:
- break;
- }
-
- return rc;
-}
-
-/**
* cxlflash_afu_recover() - initiates AFU recovery
* @sdev: SCSI device associated with LUN.
* @recover: Recover ioctl data structure.
@@ -1646,9 +1651,14 @@ retry_recover:
/* Test if in error state */
reg = readq_be(&afu->ctrl_map->mbox_r);
if (reg == -1) {
- dev_dbg(dev, "%s: MMIO read fail! Wait for recovery...\n",
- __func__);
- mutex_unlock(&ctxi->mutex);
+ dev_dbg(dev, "%s: MMIO fail, wait for recovery.\n", __func__);
+
+ /*
+ * Before checking the state, put back the context obtained with
+ * get_context() as it is no longer needed and sleep for a short
+ * period of time (see prolog notes).
+ */
+ put_context(ctxi);
ctxi = NULL;
ssleep(1);
rc = check_state(cfg);
@@ -1967,6 +1977,14 @@ out:
* @cmd: IOCTL command.
* @arg: Userspace ioctl data structure.
*
+ * A read/write semaphore is used to implement a 'drain' of currently
+ * running ioctls. The read semaphore is taken at the beginning of each
+ * ioctl thread and released upon concluding execution. Additionally the
+ * semaphore should be released and then reacquired in any ioctl execution
+ * path which will wait for an event to occur that is outside the scope of
+ * the ioctl (i.e. an adapter reset). To drain the ioctls currently running,
+ * a thread simply needs to acquire the write semaphore.
+ *
* Return: 0 on success, -errno on failure
*/
int cxlflash_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
@@ -2001,6 +2019,9 @@ int cxlflash_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
{sizeof(struct dk_cxlflash_clone), (sioctl)cxlflash_disk_clone},
};
+ /* Hold read semaphore so we can drain if needed */
+ down_read(&cfg->ioctl_rwsem);
+
/* Restrict command set to physical support only for internal LUN */
if (afu->internal_lun)
switch (cmd) {
@@ -2082,6 +2103,7 @@ int cxlflash_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
/* fall through to exit */
cxlflash_ioctl_exit:
+ up_read(&cfg->ioctl_rwsem);
if (unlikely(rc && known_ioctl))
dev_err(dev, "%s: ioctl %s (%08X) on dev(%d/%d/%d/%llu) "
"returned rc %d\n", __func__,