scsi: add SG_SCSI_RESET_NO_ESCALATE flag to SG_SCSI_RESET ioctl
authorDouglas Gilbert <dgilbert@interlog.com>
Sat, 18 Oct 2014 20:11:21 +0000 (22:11 +0200)
committerChristoph Hellwig <hch@lst.de>
Wed, 12 Nov 2014 10:15:54 +0000 (11:15 +0100)
Further to a January 2013 thread titled: "[PATCH] SG_SCSI_RESET ioctl
should only perform requested operation" by Jeremy Linton a patch (v3)
is presented that expands the existing ioctl to include "no_escalate"
versions to the existing resets. This requires no changes to SCSI low
level drivers (LLDs); it adds several more finely tuned reset options
to the user space. For example:

   /* This call remains the same, with the same escalating semantics
    * if the device (LU) reset fail. That is: on failure to try a
    * target reset and if that fails, try a bus reset, and if that fails
    * try a host (i.e. LLD) reset. */
   val = SG_SCSI_RESET_DEVICE;
   res = ioctl(<sg_or_block_fd>, SG_SCSI_RESET, &val);

   /* What follows is a new option introduced by this patch series. Only
    * a device reset is attempted. If that fails then an appropriate
    * error code is provided. N.B. There is no reset escalation. */
   val = SG_SCSI_RESET_DEVICE | SG_SCSI_RESET_NO_ESCALATE;
   res = ioctl(<sg_or_block_fd>, SG_SCSI_RESET, &val);

Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
Reviewed-by: Jeremy Linton <jlinton@tributary.com>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Christoph Hellwig <hch@lst.de>
drivers/scsi/scsi_error.c
drivers/scsi/scsi_ioctl.c
drivers/scsi/sg.c
include/scsi/scsi_eh.h
include/scsi/sg.h

index bc5ff6ff9c796bfa9101ba7d73b4e834af9d0b33..0ed666112b4f5add196279dc48909453899d5dc4 100644 (file)
@@ -2366,8 +2366,18 @@ scsi_reset_provider(struct scsi_device *dev, int flag)
                        break;
                /* FALLTHROUGH */
        case SCSI_TRY_RESET_HOST:
+       case SCSI_TRY_RESET_HOST | SCSI_TRY_RESET_NO_ESCALATE:
                rtn = scsi_try_host_reset(scmd);
                break;
+       case SCSI_TRY_RESET_DEVICE | SCSI_TRY_RESET_NO_ESCALATE:
+               rtn = scsi_try_bus_device_reset(scmd);
+               break;
+       case SCSI_TRY_RESET_TARGET | SCSI_TRY_RESET_NO_ESCALATE:
+               rtn = scsi_try_target_reset(scmd);
+               break;
+       case SCSI_TRY_RESET_BUS | SCSI_TRY_RESET_NO_ESCALATE:
+               rtn = scsi_try_bus_reset(scmd);
+               break;
        default:
                rtn = FAILED;
        }
index 1aaaf43c6803b80d3b9b09afccf547ce12cd1d7e..12fe676d13433e68a9afab7515877a1dfc37596e 100644 (file)
@@ -285,13 +285,14 @@ EXPORT_SYMBOL(scsi_ioctl);
  * scsi_nonblockable_ioctl() - Handle SG_SCSI_RESET
  * @sdev: scsi device receiving ioctl
  * @cmd: Must be SC_SCSI_RESET
- * @arg: pointer to int containing SG_SCSI_RESET_{DEVICE,BUS,HOST}
+ * @arg: pointer to int containing SG_SCSI_RESET_{DEVICE,TARGET,BUS,HOST}
+ *       possibly OR-ed with SG_SCSI_RESET_NO_ESCALATE
  * @ndelay: file mode O_NDELAY flag
  */
 int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd,
                            void __user *arg, int ndelay)
 {
-       int val, result;
+       int val, val2, result;
 
        /* The first set of iocts may be executed even if we're doing
         * error processing, as long as the device was opened
@@ -307,27 +308,32 @@ int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd,
                result = get_user(val, (int __user *)arg);
                if (result)
                        return result;
+               if (val & SG_SCSI_RESET_NO_ESCALATE) {
+                       val &= ~SG_SCSI_RESET_NO_ESCALATE;
+                       val2 = SCSI_TRY_RESET_NO_ESCALATE;
+               } else
+                       val2 = 0;
                if (val == SG_SCSI_RESET_NOTHING)
                        return 0;
                switch (val) {
                case SG_SCSI_RESET_DEVICE:
-                       val = SCSI_TRY_RESET_DEVICE;
+                       val2 |= SCSI_TRY_RESET_DEVICE;
                        break;
                case SG_SCSI_RESET_TARGET:
-                       val = SCSI_TRY_RESET_TARGET;
+                       val2 |= SCSI_TRY_RESET_TARGET;
                        break;
                case SG_SCSI_RESET_BUS:
-                       val = SCSI_TRY_RESET_BUS;
+                       val2 |= SCSI_TRY_RESET_BUS;
                        break;
                case SG_SCSI_RESET_HOST:
-                       val = SCSI_TRY_RESET_HOST;
+                       val2 |= SCSI_TRY_RESET_HOST;
                        break;
                default:
                        return -EINVAL;
                }
                if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
                        return -EACCES;
-               return (scsi_reset_provider(sdev, val) ==
+               return (scsi_reset_provider(sdev, val2) ==
                        SUCCESS) ? 0 : -EIO;
        }
        return -ENODEV;
index 60354449d9ed1cc16f7ca43fdef340c935d2b1c8..fe44c14f551e6044f73514d04d6557dbdc1c595a 100644 (file)
@@ -847,7 +847,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
 {
        void __user *p = (void __user *)arg;
        int __user *ip = p;
-       int result, val, read_only;
+       int result, val, val2, read_only;
        Sg_device *sdp;
        Sg_fd *sfp;
        Sg_request *srp;
@@ -1082,27 +1082,32 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
                result = get_user(val, ip);
                if (result)
                        return result;
+               if (val & SG_SCSI_RESET_NO_ESCALATE) {
+                       val &= ~SG_SCSI_RESET_NO_ESCALATE;
+                       val2 = SCSI_TRY_RESET_NO_ESCALATE;
+               } else
+                       val2 = 0;
                if (SG_SCSI_RESET_NOTHING == val)
                        return 0;
                switch (val) {
                case SG_SCSI_RESET_DEVICE:
-                       val = SCSI_TRY_RESET_DEVICE;
+                       val2 |= SCSI_TRY_RESET_DEVICE;
                        break;
                case SG_SCSI_RESET_TARGET:
-                       val = SCSI_TRY_RESET_TARGET;
+                       val2 |= SCSI_TRY_RESET_TARGET;
                        break;
                case SG_SCSI_RESET_BUS:
-                       val = SCSI_TRY_RESET_BUS;
+                       val2 |= SCSI_TRY_RESET_BUS;
                        break;
                case SG_SCSI_RESET_HOST:
-                       val = SCSI_TRY_RESET_HOST;
+                       val2 |= SCSI_TRY_RESET_HOST;
                        break;
                default:
                        return -EINVAL;
                }
                if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
                        return -EACCES;
-               return (scsi_reset_provider(sdp->device, val) ==
+               return (scsi_reset_provider(sdp->device, val2) ==
                        SUCCESS) ? 0 : -EIO;
        case SCSI_IOCTL_SEND_COMMAND:
                if (atomic_read(&sdp->detaching))
index 06a8790893ef60e15cdd100550134e698e6a5e2e..49af14ad5288c6dcc916db2d8f697f9fc54401fb 100644 (file)
@@ -62,11 +62,16 @@ extern void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq);
 
 /*
  * Reset request from external source
+ * Note: if SCSI_TRY_RESET_DEVICE fails then it will escalate to
+ * SCSI_TRY_RESET_TARGET which if it fails will escalate to
+ * SCSI_TRY_RESET_BUS which if it fails will escalate to SCSI_TRY_RESET_HOST.
+ * To prevent escalation OR with SCSI_TRY_RESET_NO_ESCALATE.
  */
 #define SCSI_TRY_RESET_DEVICE  1
 #define SCSI_TRY_RESET_BUS     2
 #define SCSI_TRY_RESET_HOST    3
 #define SCSI_TRY_RESET_TARGET  4
+#define SCSI_TRY_RESET_NO_ESCALATE     0x100   /* OR-ed to prior defines */
 
 extern int scsi_reset_provider(struct scsi_device *, int);
 
index 750e5db7c6bff10b04e7d5ee8853d244cb7fc610..3afec7032448c8927d2cd134111606ee0206ca3d 100644 (file)
@@ -164,12 +164,15 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */
 
 /* Returns -EBUSY if occupied. 3rd argument pointer to int (see next) */
 #define SG_SCSI_RESET 0x2284
-/* Associated values that can be given to SG_SCSI_RESET follow */
+/* Associated values that can be given to SG_SCSI_RESET follow.
+ * SG_SCSI_RESET_NO_ESCALATE may be OR-ed to the _DEVICE, _TARGET, _BUS
+ * or _HOST reset value so only that action is attempted. */
 #define                SG_SCSI_RESET_NOTHING   0
 #define                SG_SCSI_RESET_DEVICE    1
 #define                SG_SCSI_RESET_BUS       2
 #define                SG_SCSI_RESET_HOST      3
 #define                SG_SCSI_RESET_TARGET    4
+#define                SG_SCSI_RESET_NO_ESCALATE       0x100
 
 /* synchronous SCSI command ioctl, (only in version 3 interface) */
 #define SG_IO 0x2285   /* similar effect as write() followed by read() */