Commit 0ed32242 authored by James Bottomley's avatar James Bottomley Committed by James Bottomley

scsi: Add reset ioctl capability to ULDs

Currently, the only way to issue a SCSI reset of any
type is to use the sg device.  By adding extra ioctls
to scsi_ioctl.c we enable this for all ULDs.

The slight complication is that scsi_ioctl() is usually
only called when the device has been checked not to be
undergoing eh recovery.  Resets may be issued in
this scenario if the user opens the device O_NONBLOCK.
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 89f88233
......@@ -20,6 +20,7 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_ioctl.h>
#include <scsi/scsi_request.h>
#include <scsi/sg.h>
#include "scsi_logging.h"
......@@ -456,3 +457,51 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
}
return -EINVAL;
}
/*
* the scsi_nonblock_ioctl() function is designed for ioctls which may
* be executed even if the device is in recovery.
*/
int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd,
void __user *arg, struct file *filp)
{
int val, result;
/* The first set of iocts may be executed even if we're doing
* error processing, as long as the device was opened
* non-blocking */
if (filp && filp->f_flags & O_NONBLOCK) {
if (test_bit(SHOST_RECOVERY,
&sdev->host->shost_state))
return -ENODEV;
} else if (!scsi_block_when_processing_errors(sdev))
return -ENODEV;
switch (cmd) {
case SG_SCSI_RESET:
result = get_user(val, (int __user *)arg);
if (result)
return result;
if (val == SG_SCSI_RESET_NOTHING)
return 0;
switch (val) {
case SG_SCSI_RESET_DEVICE:
val = SCSI_TRY_RESET_DEVICE;
break;
case SG_SCSI_RESET_BUS:
val = SCSI_TRY_RESET_BUS;
break;
case SG_SCSI_RESET_HOST:
val = 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) ==
SUCCESS) ? 0 : -EIO;
}
return -ENODEV;
}
EXPORT_SYMBOL(scsi_nonblockable_ioctl);
......@@ -574,8 +574,9 @@ static int sd_ioctl(struct inode * inode, struct file * filp,
* may try and take the device offline, in which case all further
* access to the device is prohibited.
*/
if (!scsi_block_when_processing_errors(sdp))
return -ENODEV;
error = scsi_nonblockable_ioctl(sdp, cmd, p, filp);
if (!scsi_block_when_processing_errors(sdp) || !error)
return error;
if (cmd == HDIO_GETGEO) {
if (!arg)
......
......@@ -549,5 +549,17 @@ int sr_dev_ioctl(struct cdrom_device_info *cdi,
unsigned int cmd, unsigned long arg)
{
Scsi_CD *cd = cdi->handle;
int ret;
ret = scsi_nonblockable_ioctl(cd->device, cmd,
(void __user *)arg, NULL);
/*
* ENODEV means that we didn't recognise the ioctl, or that we
* cannot execute it in the current device state. In either
* case fall through to scsi_ioctl, which will return ENDOEV again
* if it doesn't recognise the ioctl
*/
if (ret != -ENODEV)
return ret;
return scsi_ioctl(cd->device, cmd, (void __user *)arg);
}
......@@ -3128,10 +3128,10 @@ static int st_ioctl(struct inode *inode, struct file *file,
* may try and take the device offline, in which case all further
* access to the device is prohibited.
*/
if (!scsi_block_when_processing_errors(STp->device)) {
retval = (-ENXIO);
retval = scsi_nonblockable_ioctl(STp->device, cmd_in, p, file);
if (!scsi_block_when_processing_errors(STp->device) || !retval)
goto out;
}
cmd_type = _IOC_TYPE(cmd_in);
cmd_nr = _IOC_NR(cmd_in);
......
......@@ -43,6 +43,8 @@ typedef struct scsi_fctargaddress {
extern int scsi_ioctl(struct scsi_device *, int, void __user *);
extern int scsi_ioctl_send_command(struct scsi_device *,
struct scsi_ioctl_command __user *);
extern int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd,
void __user *arg, struct file *filp);
#endif /* __KERNEL__ */
#endif /* _SCSI_IOCTL_H */
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