Merge branch 'for-linus-4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/mason...
[firefly-linux-kernel-4.4.55.git] / drivers / scsi / cxlflash / superpipe.c
index 34acb587d73020823d7ce8017ed16bb17b0aeddb..cac2e6a50efd83ab001bdb84cf56fb2d8d1d35b2 100644 (file)
@@ -162,10 +162,7 @@ struct ctx_info *get_context(struct cxlflash_cfg *cfg, u64 rctxid,
 
        if (likely(ctxid < MAX_CONTEXT)) {
                while (true) {
-                       rc = mutex_lock_interruptible(&cfg->ctx_tbl_list_mutex);
-                       if (rc)
-                               goto out;
-
+                       mutex_lock(&cfg->ctx_tbl_list_mutex);
                        ctxi = cfg->ctx_tbl[ctxid];
                        if (ctxi)
                                if ((file && (ctxi->file != file)) ||
@@ -283,6 +280,24 @@ out:
  * @sdev:      SCSI device associated with LUN.
  * @lli:       LUN destined for capacity request.
  *
+ * The READ_CAP16 can take quite a while to complete. Should an EEH occur while
+ * in scsi_execute(), the EEH handler will attempt to recover. As part of the
+ * recovery, the handler drains all currently running ioctls, waiting until they
+ * have completed before proceeding with a reset. As this routine is used on the
+ * ioctl path, this can create a condition where the EEH handler becomes stuck,
+ * infinitely waiting for this ioctl thread. To avoid this behavior, temporarily
+ * unmark this thread as an ioctl thread by releasing the ioctl read semaphore.
+ * This will allow the EEH handler to proceed with a recovery while this thread
+ * is still running. Once the scsi_execute() returns, reacquire the ioctl read
+ * semaphore and check the adapter state in case it changed while inside of
+ * scsi_execute(). The state check will wait if the adapter is still being
+ * recovered or return a failure if the recovery failed. In the event that the
+ * adapter reset failed, simply return the failure as the ioctl would be unable
+ * to continue.
+ *
+ * Note that the above puts a requirement on this routine to only be called on
+ * an ioctl thread.
+ *
  * Return: 0 on success, -errno on failure
  */
 static int read_cap16(struct scsi_device *sdev, struct llun_info *lli)
@@ -314,8 +329,18 @@ retry:
        dev_dbg(dev, "%s: %ssending cmd(0x%x)\n", __func__,
                retry_cnt ? "re" : "", scsi_cmd[0]);
 
+       /* Drop the ioctl read semahpore across lengthy call */
+       up_read(&cfg->ioctl_rwsem);
        result = scsi_execute(sdev, scsi_cmd, DMA_FROM_DEVICE, cmd_buf,
                              CMD_BUFSIZE, sense_buf, to, CMD_RETRIES, 0, NULL);
+       down_read(&cfg->ioctl_rwsem);
+       rc = check_state(cfg);
+       if (rc) {
+               dev_err(dev, "%s: Failed state! result=0x08%X\n",
+                       __func__, result);
+               rc = -ENODEV;
+               goto out;
+       }
 
        if (driver_byte(result) == DRIVER_SENSE) {
                result &= ~(0xFF<<24); /* DRIVER_SENSE is not an error */
@@ -1221,7 +1246,7 @@ static const struct file_operations null_fops = {
  *
  * Return: 0 on success, -errno on failure
  */
-static int check_state(struct cxlflash_cfg *cfg)
+int check_state(struct cxlflash_cfg *cfg)
 {
        struct device *dev = &cfg->dev->dev;
        int rc = 0;