diff options
author | Douglas Gilbert <dgilbert@interlog.com> | 2014-10-18 16:11:21 -0400 |
---|---|---|
committer | Christoph Hellwig <hch@lst.de> | 2014-11-12 05:15:54 -0500 |
commit | 26cf591e6dfc0d07495b7bcf20a557b316811f00 (patch) | |
tree | aaebbfe8c9764f981f2fa540b6ae20c79dce55af | |
parent | 678e27573237a0b065defdf99e5070c9b0c403c3 (diff) |
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: 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>
-rw-r--r-- | drivers/scsi/scsi_error.c | 10 | ||||
-rw-r--r-- | drivers/scsi/scsi_ioctl.c | 20 | ||||
-rw-r--r-- | drivers/scsi/sg.c | 17 | ||||
-rw-r--r-- | include/scsi/scsi_eh.h | 5 | ||||
-rw-r--r-- | include/scsi/sg.h | 5 |
5 files changed, 43 insertions, 14 deletions
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index bc5ff6ff9c79..0ed666112b4f 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c | |||
@@ -2366,8 +2366,18 @@ scsi_reset_provider(struct scsi_device *dev, int flag) | |||
2366 | break; | 2366 | break; |
2367 | /* FALLTHROUGH */ | 2367 | /* FALLTHROUGH */ |
2368 | case SCSI_TRY_RESET_HOST: | 2368 | case SCSI_TRY_RESET_HOST: |
2369 | case SCSI_TRY_RESET_HOST | SCSI_TRY_RESET_NO_ESCALATE: | ||
2369 | rtn = scsi_try_host_reset(scmd); | 2370 | rtn = scsi_try_host_reset(scmd); |
2370 | break; | 2371 | break; |
2372 | case SCSI_TRY_RESET_DEVICE | SCSI_TRY_RESET_NO_ESCALATE: | ||
2373 | rtn = scsi_try_bus_device_reset(scmd); | ||
2374 | break; | ||
2375 | case SCSI_TRY_RESET_TARGET | SCSI_TRY_RESET_NO_ESCALATE: | ||
2376 | rtn = scsi_try_target_reset(scmd); | ||
2377 | break; | ||
2378 | case SCSI_TRY_RESET_BUS | SCSI_TRY_RESET_NO_ESCALATE: | ||
2379 | rtn = scsi_try_bus_reset(scmd); | ||
2380 | break; | ||
2371 | default: | 2381 | default: |
2372 | rtn = FAILED; | 2382 | rtn = FAILED; |
2373 | } | 2383 | } |
diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index 1aaaf43c6803..12fe676d1343 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c | |||
@@ -285,13 +285,14 @@ EXPORT_SYMBOL(scsi_ioctl); | |||
285 | * scsi_nonblockable_ioctl() - Handle SG_SCSI_RESET | 285 | * scsi_nonblockable_ioctl() - Handle SG_SCSI_RESET |
286 | * @sdev: scsi device receiving ioctl | 286 | * @sdev: scsi device receiving ioctl |
287 | * @cmd: Must be SC_SCSI_RESET | 287 | * @cmd: Must be SC_SCSI_RESET |
288 | * @arg: pointer to int containing SG_SCSI_RESET_{DEVICE,BUS,HOST} | 288 | * @arg: pointer to int containing SG_SCSI_RESET_{DEVICE,TARGET,BUS,HOST} |
289 | * possibly OR-ed with SG_SCSI_RESET_NO_ESCALATE | ||
289 | * @ndelay: file mode O_NDELAY flag | 290 | * @ndelay: file mode O_NDELAY flag |
290 | */ | 291 | */ |
291 | int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd, | 292 | int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd, |
292 | void __user *arg, int ndelay) | 293 | void __user *arg, int ndelay) |
293 | { | 294 | { |
294 | int val, result; | 295 | int val, val2, result; |
295 | 296 | ||
296 | /* The first set of iocts may be executed even if we're doing | 297 | /* The first set of iocts may be executed even if we're doing |
297 | * error processing, as long as the device was opened | 298 | * error processing, as long as the device was opened |
@@ -307,27 +308,32 @@ int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd, | |||
307 | result = get_user(val, (int __user *)arg); | 308 | result = get_user(val, (int __user *)arg); |
308 | if (result) | 309 | if (result) |
309 | return result; | 310 | return result; |
311 | if (val & SG_SCSI_RESET_NO_ESCALATE) { | ||
312 | val &= ~SG_SCSI_RESET_NO_ESCALATE; | ||
313 | val2 = SCSI_TRY_RESET_NO_ESCALATE; | ||
314 | } else | ||
315 | val2 = 0; | ||
310 | if (val == SG_SCSI_RESET_NOTHING) | 316 | if (val == SG_SCSI_RESET_NOTHING) |
311 | return 0; | 317 | return 0; |
312 | switch (val) { | 318 | switch (val) { |
313 | case SG_SCSI_RESET_DEVICE: | 319 | case SG_SCSI_RESET_DEVICE: |
314 | val = SCSI_TRY_RESET_DEVICE; | 320 | val2 |= SCSI_TRY_RESET_DEVICE; |
315 | break; | 321 | break; |
316 | case SG_SCSI_RESET_TARGET: | 322 | case SG_SCSI_RESET_TARGET: |
317 | val = SCSI_TRY_RESET_TARGET; | 323 | val2 |= SCSI_TRY_RESET_TARGET; |
318 | break; | 324 | break; |
319 | case SG_SCSI_RESET_BUS: | 325 | case SG_SCSI_RESET_BUS: |
320 | val = SCSI_TRY_RESET_BUS; | 326 | val2 |= SCSI_TRY_RESET_BUS; |
321 | break; | 327 | break; |
322 | case SG_SCSI_RESET_HOST: | 328 | case SG_SCSI_RESET_HOST: |
323 | val = SCSI_TRY_RESET_HOST; | 329 | val2 |= SCSI_TRY_RESET_HOST; |
324 | break; | 330 | break; |
325 | default: | 331 | default: |
326 | return -EINVAL; | 332 | return -EINVAL; |
327 | } | 333 | } |
328 | if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) | 334 | if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) |
329 | return -EACCES; | 335 | return -EACCES; |
330 | return (scsi_reset_provider(sdev, val) == | 336 | return (scsi_reset_provider(sdev, val2) == |
331 | SUCCESS) ? 0 : -EIO; | 337 | SUCCESS) ? 0 : -EIO; |
332 | } | 338 | } |
333 | return -ENODEV; | 339 | return -ENODEV; |
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 60354449d9ed..fe44c14f551e 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c | |||
@@ -847,7 +847,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) | |||
847 | { | 847 | { |
848 | void __user *p = (void __user *)arg; | 848 | void __user *p = (void __user *)arg; |
849 | int __user *ip = p; | 849 | int __user *ip = p; |
850 | int result, val, read_only; | 850 | int result, val, val2, read_only; |
851 | Sg_device *sdp; | 851 | Sg_device *sdp; |
852 | Sg_fd *sfp; | 852 | Sg_fd *sfp; |
853 | Sg_request *srp; | 853 | Sg_request *srp; |
@@ -1082,27 +1082,32 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) | |||
1082 | result = get_user(val, ip); | 1082 | result = get_user(val, ip); |
1083 | if (result) | 1083 | if (result) |
1084 | return result; | 1084 | return result; |
1085 | if (val & SG_SCSI_RESET_NO_ESCALATE) { | ||
1086 | val &= ~SG_SCSI_RESET_NO_ESCALATE; | ||
1087 | val2 = SCSI_TRY_RESET_NO_ESCALATE; | ||
1088 | } else | ||
1089 | val2 = 0; | ||
1085 | if (SG_SCSI_RESET_NOTHING == val) | 1090 | if (SG_SCSI_RESET_NOTHING == val) |
1086 | return 0; | 1091 | return 0; |
1087 | switch (val) { | 1092 | switch (val) { |
1088 | case SG_SCSI_RESET_DEVICE: | 1093 | case SG_SCSI_RESET_DEVICE: |
1089 | val = SCSI_TRY_RESET_DEVICE; | 1094 | val2 |= SCSI_TRY_RESET_DEVICE; |
1090 | break; | 1095 | break; |
1091 | case SG_SCSI_RESET_TARGET: | 1096 | case SG_SCSI_RESET_TARGET: |
1092 | val = SCSI_TRY_RESET_TARGET; | 1097 | val2 |= SCSI_TRY_RESET_TARGET; |
1093 | break; | 1098 | break; |
1094 | case SG_SCSI_RESET_BUS: | 1099 | case SG_SCSI_RESET_BUS: |
1095 | val = SCSI_TRY_RESET_BUS; | 1100 | val2 |= SCSI_TRY_RESET_BUS; |
1096 | break; | 1101 | break; |
1097 | case SG_SCSI_RESET_HOST: | 1102 | case SG_SCSI_RESET_HOST: |
1098 | val = SCSI_TRY_RESET_HOST; | 1103 | val2 |= SCSI_TRY_RESET_HOST; |
1099 | break; | 1104 | break; |
1100 | default: | 1105 | default: |
1101 | return -EINVAL; | 1106 | return -EINVAL; |
1102 | } | 1107 | } |
1103 | if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) | 1108 | if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) |
1104 | return -EACCES; | 1109 | return -EACCES; |
1105 | return (scsi_reset_provider(sdp->device, val) == | 1110 | return (scsi_reset_provider(sdp->device, val2) == |
1106 | SUCCESS) ? 0 : -EIO; | 1111 | SUCCESS) ? 0 : -EIO; |
1107 | case SCSI_IOCTL_SEND_COMMAND: | 1112 | case SCSI_IOCTL_SEND_COMMAND: |
1108 | if (atomic_read(&sdp->detaching)) | 1113 | if (atomic_read(&sdp->detaching)) |
diff --git a/include/scsi/scsi_eh.h b/include/scsi/scsi_eh.h index 06a8790893ef..49af14ad5288 100644 --- a/include/scsi/scsi_eh.h +++ b/include/scsi/scsi_eh.h | |||
@@ -62,11 +62,16 @@ extern void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq); | |||
62 | 62 | ||
63 | /* | 63 | /* |
64 | * Reset request from external source | 64 | * Reset request from external source |
65 | * Note: if SCSI_TRY_RESET_DEVICE fails then it will escalate to | ||
66 | * SCSI_TRY_RESET_TARGET which if it fails will escalate to | ||
67 | * SCSI_TRY_RESET_BUS which if it fails will escalate to SCSI_TRY_RESET_HOST. | ||
68 | * To prevent escalation OR with SCSI_TRY_RESET_NO_ESCALATE. | ||
65 | */ | 69 | */ |
66 | #define SCSI_TRY_RESET_DEVICE 1 | 70 | #define SCSI_TRY_RESET_DEVICE 1 |
67 | #define SCSI_TRY_RESET_BUS 2 | 71 | #define SCSI_TRY_RESET_BUS 2 |
68 | #define SCSI_TRY_RESET_HOST 3 | 72 | #define SCSI_TRY_RESET_HOST 3 |
69 | #define SCSI_TRY_RESET_TARGET 4 | 73 | #define SCSI_TRY_RESET_TARGET 4 |
74 | #define SCSI_TRY_RESET_NO_ESCALATE 0x100 /* OR-ed to prior defines */ | ||
70 | 75 | ||
71 | extern int scsi_reset_provider(struct scsi_device *, int); | 76 | extern int scsi_reset_provider(struct scsi_device *, int); |
72 | 77 | ||
diff --git a/include/scsi/sg.h b/include/scsi/sg.h index 750e5db7c6bf..3afec7032448 100644 --- a/include/scsi/sg.h +++ b/include/scsi/sg.h | |||
@@ -164,12 +164,15 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */ | |||
164 | 164 | ||
165 | /* Returns -EBUSY if occupied. 3rd argument pointer to int (see next) */ | 165 | /* Returns -EBUSY if occupied. 3rd argument pointer to int (see next) */ |
166 | #define SG_SCSI_RESET 0x2284 | 166 | #define SG_SCSI_RESET 0x2284 |
167 | /* Associated values that can be given to SG_SCSI_RESET follow */ | 167 | /* Associated values that can be given to SG_SCSI_RESET follow. |
168 | * SG_SCSI_RESET_NO_ESCALATE may be OR-ed to the _DEVICE, _TARGET, _BUS | ||
169 | * or _HOST reset value so only that action is attempted. */ | ||
168 | #define SG_SCSI_RESET_NOTHING 0 | 170 | #define SG_SCSI_RESET_NOTHING 0 |
169 | #define SG_SCSI_RESET_DEVICE 1 | 171 | #define SG_SCSI_RESET_DEVICE 1 |
170 | #define SG_SCSI_RESET_BUS 2 | 172 | #define SG_SCSI_RESET_BUS 2 |
171 | #define SG_SCSI_RESET_HOST 3 | 173 | #define SG_SCSI_RESET_HOST 3 |
172 | #define SG_SCSI_RESET_TARGET 4 | 174 | #define SG_SCSI_RESET_TARGET 4 |
175 | #define SG_SCSI_RESET_NO_ESCALATE 0x100 | ||
173 | 176 | ||
174 | /* synchronous SCSI command ioctl, (only in version 3 interface) */ | 177 | /* synchronous SCSI command ioctl, (only in version 3 interface) */ |
175 | #define SG_IO 0x2285 /* similar effect as write() followed by read() */ | 178 | #define SG_IO 0x2285 /* similar effect as write() followed by read() */ |