scsi: cxlflash: Schedule asynchronous reset of the host
authorUma Krishnan <ukrishn@linux.vnet.ibm.com>
Thu, 22 Jun 2017 02:14:17 +0000 (21:14 -0500)
committerMartin K. Petersen <martin.petersen@oracle.com>
Mon, 26 Jun 2017 19:01:08 +0000 (15:01 -0400)
A context reset failure indicates the AFU is in a bad state. At present,
when such a situation occurs, no further action is taken. This leaves the
adapter in an unusable state with no recoverable actions.

To avoid this situation, context reset failures will be escalated to a host
reset operation. This will be done asynchronously to allow the acting
thread to return to the user with a failure.

Signed-off-by: Uma Krishnan <ukrishn@linux.vnet.ibm.com>
Acked-by: Matthew R. Ochs <mrochs@linux.vnet.ibm.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/cxlflash/common.h
drivers/scsi/cxlflash/main.c

index 75decf67174373de978fc64f34a83787004eb449..e9b61087b72bbeb398676c915feb6a792d3069e7 100644 (file)
@@ -15,6 +15,7 @@
 #ifndef _CXLFLASH_COMMON_H
 #define _CXLFLASH_COMMON_H
 
+#include <linux/async.h>
 #include <linux/irq_poll.h>
 #include <linux/list.h>
 #include <linux/rwsem.h>
@@ -144,6 +145,7 @@ struct cxlflash_cfg {
        bool tmf_active;
        wait_queue_head_t reset_waitq;
        enum cxlflash_state state;
+       async_cookie_t async_reset_cookie;
 };
 
 struct afu_cmd {
index b8dc379227f0b1a672563c62468480b9bad4db71..20c2c5e111b44364f98a15c53293bf0b9fdb6bb1 100644 (file)
@@ -585,6 +585,20 @@ static void free_mem(struct cxlflash_cfg *cfg)
        }
 }
 
+/**
+ * cxlflash_reset_sync() - synchronizing point for asynchronous resets
+ * @cfg:       Internal structure associated with the host.
+ */
+static void cxlflash_reset_sync(struct cxlflash_cfg *cfg)
+{
+       if (cfg->async_reset_cookie == 0)
+               return;
+
+       /* Wait until all async calls prior to this cookie have completed */
+       async_synchronize_cookie(cfg->async_reset_cookie + 1);
+       cfg->async_reset_cookie = 0;
+}
+
 /**
  * stop_afu() - stops the AFU command timers and unmaps the MMIO space
  * @cfg:       Internal structure associated with the host.
@@ -601,6 +615,8 @@ static void stop_afu(struct cxlflash_cfg *cfg)
        int i;
 
        cancel_work_sync(&cfg->work_q);
+       if (!current_is_async())
+               cxlflash_reset_sync(cfg);
 
        if (likely(afu)) {
                while (atomic_read(&afu->cmds_active))
@@ -2004,6 +2020,91 @@ err1:
        goto out;
 }
 
+/**
+ * afu_reset() - resets the AFU
+ * @cfg:       Internal structure associated with the host.
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int afu_reset(struct cxlflash_cfg *cfg)
+{
+       struct device *dev = &cfg->dev->dev;
+       int rc = 0;
+
+       /* Stop the context before the reset. Since the context is
+        * no longer available restart it after the reset is complete
+        */
+       term_afu(cfg);
+
+       rc = init_afu(cfg);
+
+       dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
+       return rc;
+}
+
+/**
+ * drain_ioctls() - wait until all currently executing ioctls have completed
+ * @cfg:       Internal structure associated with the host.
+ *
+ * Obtain write access to read/write semaphore that wraps ioctl
+ * handling to 'drain' ioctls currently executing.
+ */
+static void drain_ioctls(struct cxlflash_cfg *cfg)
+{
+       down_write(&cfg->ioctl_rwsem);
+       up_write(&cfg->ioctl_rwsem);
+}
+
+/**
+ * cxlflash_async_reset_host() - asynchronous host reset handler
+ * @data:      Private data provided while scheduling reset.
+ * @cookie:    Cookie that can be used for checkpointing.
+ */
+static void cxlflash_async_reset_host(void *data, async_cookie_t cookie)
+{
+       struct cxlflash_cfg *cfg = data;
+       struct device *dev = &cfg->dev->dev;
+       int rc = 0;
+
+       if (cfg->state != STATE_RESET) {
+               dev_dbg(dev, "%s: Not performing a reset, state=%d\n",
+                       __func__, cfg->state);
+               goto out;
+       }
+
+       drain_ioctls(cfg);
+       cxlflash_mark_contexts_error(cfg);
+       rc = afu_reset(cfg);
+       if (rc)
+               cfg->state = STATE_FAILTERM;
+       else
+               cfg->state = STATE_NORMAL;
+       wake_up_all(&cfg->reset_waitq);
+
+out:
+       scsi_unblock_requests(cfg->host);
+}
+
+/**
+ * cxlflash_schedule_async_reset() - schedule an asynchronous host reset
+ * @cfg:       Internal structure associated with the host.
+ */
+static void cxlflash_schedule_async_reset(struct cxlflash_cfg *cfg)
+{
+       struct device *dev = &cfg->dev->dev;
+
+       if (cfg->state != STATE_NORMAL) {
+               dev_dbg(dev, "%s: Not performing reset state=%d\n",
+                       __func__, cfg->state);
+               return;
+       }
+
+       cfg->state = STATE_RESET;
+       scsi_block_requests(cfg->host);
+       cfg->async_reset_cookie = async_schedule(cxlflash_async_reset_host,
+                                                cfg);
+}
+
 /**
  * cxlflash_afu_sync() - builds and sends an AFU sync command
  * @afu:       AFU associated with the host.
@@ -2085,6 +2186,7 @@ retry:
                rc = afu->context_reset(hwq);
                if (!rc && ++nretry < 2)
                        goto retry;
+               cxlflash_schedule_async_reset(cfg);
        }
 
 out:
@@ -2095,41 +2197,6 @@ out:
        return rc;
 }
 
-/**
- * afu_reset() - resets the AFU
- * @cfg:       Internal structure associated with the host.
- *
- * Return: 0 on success, -errno on failure
- */
-static int afu_reset(struct cxlflash_cfg *cfg)
-{
-       struct device *dev = &cfg->dev->dev;
-       int rc = 0;
-
-       /* Stop the context before the reset. Since the context is
-        * no longer available restart it after the reset is complete
-        */
-       term_afu(cfg);
-
-       rc = init_afu(cfg);
-
-       dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
-       return rc;
-}
-
-/**
- * drain_ioctls() - wait until all currently executing ioctls have completed
- * @cfg:       Internal structure associated with the host.
- *
- * Obtain write access to read/write semaphore that wraps ioctl
- * handling to 'drain' ioctls currently executing.
- */
-static void drain_ioctls(struct cxlflash_cfg *cfg)
-{
-       down_write(&cfg->ioctl_rwsem);
-       up_write(&cfg->ioctl_rwsem);
-}
-
 /**
  * cxlflash_eh_device_reset_handler() - reset a single LUN
  * @scp:       SCSI command to send.