Commit 26cf591e authored by Douglas Gilbert's avatar Douglas Gilbert Committed by Christoph Hellwig

scsi: add SG_SCSI_RESET_NO_ESCALATE flag to SG_SCSI_RESET ioctl

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: default avatarDouglas Gilbert <dgilbert@interlog.com>
Reviewed-by: default avatarJeremy Linton <jlinton@tributary.com>
Reviewed-by: default avatarHannes Reinecke <hare@suse.de>
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
parent 678e2757
...@@ -2366,8 +2366,18 @@ scsi_reset_provider(struct scsi_device *dev, int flag) ...@@ -2366,8 +2366,18 @@ scsi_reset_provider(struct scsi_device *dev, int flag)
break; break;
/* FALLTHROUGH */ /* FALLTHROUGH */
case SCSI_TRY_RESET_HOST: case SCSI_TRY_RESET_HOST:
case SCSI_TRY_RESET_HOST | SCSI_TRY_RESET_NO_ESCALATE:
rtn = scsi_try_host_reset(scmd); rtn = scsi_try_host_reset(scmd);
break; 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: default:
rtn = FAILED; rtn = FAILED;
} }
......
...@@ -285,13 +285,14 @@ EXPORT_SYMBOL(scsi_ioctl); ...@@ -285,13 +285,14 @@ EXPORT_SYMBOL(scsi_ioctl);
* scsi_nonblockable_ioctl() - Handle SG_SCSI_RESET * scsi_nonblockable_ioctl() - Handle SG_SCSI_RESET
* @sdev: scsi device receiving ioctl * @sdev: scsi device receiving ioctl
* @cmd: Must be SC_SCSI_RESET * @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 * @ndelay: file mode O_NDELAY flag
*/ */
int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd, int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd,
void __user *arg, int ndelay) 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 /* The first set of iocts may be executed even if we're doing
* error processing, as long as the device was opened * error processing, as long as the device was opened
...@@ -307,27 +308,32 @@ int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd, ...@@ -307,27 +308,32 @@ int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd,
result = get_user(val, (int __user *)arg); result = get_user(val, (int __user *)arg);
if (result) if (result)
return 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) if (val == SG_SCSI_RESET_NOTHING)
return 0; return 0;
switch (val) { switch (val) {
case SG_SCSI_RESET_DEVICE: case SG_SCSI_RESET_DEVICE:
val = SCSI_TRY_RESET_DEVICE; val2 |= SCSI_TRY_RESET_DEVICE;
break; break;
case SG_SCSI_RESET_TARGET: case SG_SCSI_RESET_TARGET:
val = SCSI_TRY_RESET_TARGET; val2 |= SCSI_TRY_RESET_TARGET;
break; break;
case SG_SCSI_RESET_BUS: case SG_SCSI_RESET_BUS:
val = SCSI_TRY_RESET_BUS; val2 |= SCSI_TRY_RESET_BUS;
break; break;
case SG_SCSI_RESET_HOST: case SG_SCSI_RESET_HOST:
val = SCSI_TRY_RESET_HOST; val2 |= SCSI_TRY_RESET_HOST;
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
return -EACCES; return -EACCES;
return (scsi_reset_provider(sdev, val) == return (scsi_reset_provider(sdev, val2) ==
SUCCESS) ? 0 : -EIO; SUCCESS) ? 0 : -EIO;
} }
return -ENODEV; return -ENODEV;
......
...@@ -847,7 +847,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) ...@@ -847,7 +847,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
{ {
void __user *p = (void __user *)arg; void __user *p = (void __user *)arg;
int __user *ip = p; int __user *ip = p;
int result, val, read_only; int result, val, val2, read_only;
Sg_device *sdp; Sg_device *sdp;
Sg_fd *sfp; Sg_fd *sfp;
Sg_request *srp; Sg_request *srp;
...@@ -1082,27 +1082,32 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) ...@@ -1082,27 +1082,32 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
result = get_user(val, ip); result = get_user(val, ip);
if (result) if (result)
return 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) if (SG_SCSI_RESET_NOTHING == val)
return 0; return 0;
switch (val) { switch (val) {
case SG_SCSI_RESET_DEVICE: case SG_SCSI_RESET_DEVICE:
val = SCSI_TRY_RESET_DEVICE; val2 |= SCSI_TRY_RESET_DEVICE;
break; break;
case SG_SCSI_RESET_TARGET: case SG_SCSI_RESET_TARGET:
val = SCSI_TRY_RESET_TARGET; val2 |= SCSI_TRY_RESET_TARGET;
break; break;
case SG_SCSI_RESET_BUS: case SG_SCSI_RESET_BUS:
val = SCSI_TRY_RESET_BUS; val2 |= SCSI_TRY_RESET_BUS;
break; break;
case SG_SCSI_RESET_HOST: case SG_SCSI_RESET_HOST:
val = SCSI_TRY_RESET_HOST; val2 |= SCSI_TRY_RESET_HOST;
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
return -EACCES; return -EACCES;
return (scsi_reset_provider(sdp->device, val) == return (scsi_reset_provider(sdp->device, val2) ==
SUCCESS) ? 0 : -EIO; SUCCESS) ? 0 : -EIO;
case SCSI_IOCTL_SEND_COMMAND: case SCSI_IOCTL_SEND_COMMAND:
if (atomic_read(&sdp->detaching)) if (atomic_read(&sdp->detaching))
......
...@@ -62,11 +62,16 @@ extern void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq); ...@@ -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 * 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_DEVICE 1
#define SCSI_TRY_RESET_BUS 2 #define SCSI_TRY_RESET_BUS 2
#define SCSI_TRY_RESET_HOST 3 #define SCSI_TRY_RESET_HOST 3
#define SCSI_TRY_RESET_TARGET 4 #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); extern int scsi_reset_provider(struct scsi_device *, int);
......
...@@ -164,12 +164,15 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */ ...@@ -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) */ /* Returns -EBUSY if occupied. 3rd argument pointer to int (see next) */
#define SG_SCSI_RESET 0x2284 #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_NOTHING 0
#define SG_SCSI_RESET_DEVICE 1 #define SG_SCSI_RESET_DEVICE 1
#define SG_SCSI_RESET_BUS 2 #define SG_SCSI_RESET_BUS 2
#define SG_SCSI_RESET_HOST 3 #define SG_SCSI_RESET_HOST 3
#define SG_SCSI_RESET_TARGET 4 #define SG_SCSI_RESET_TARGET 4
#define SG_SCSI_RESET_NO_ESCALATE 0x100
/* synchronous SCSI command ioctl, (only in version 3 interface) */ /* synchronous SCSI command ioctl, (only in version 3 interface) */
#define SG_IO 0x2285 /* similar effect as write() followed by read() */ #define SG_IO 0x2285 /* similar effect as write() followed by read() */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment