scsi: split scsi_nonblockable_ioctl
The calling conventions for this function are bad as it could return -ENODEV both for a device not currently online and a not recognized ioctl. Add a new scsi_ioctl_block_when_processing_errors function that wraps scsi_block_when_processing_errors with the a special case for the SG_SCSI_RESET ioctl command, and handle the SG_SCSI_RESET case itself in scsi_ioctl. All callers of scsi_ioctl now must call the above helper to check for the EH state, so that the ioctl handler itself doesn't have to. Reported-by: Robert Elliott <Elliott@hp.com> Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com> Reviewed-by: Hannes Reinecke <hare@suse.de>
This commit is contained in:
parent
176aa9d6ee
commit
906d15fbd2
@ -616,6 +616,11 @@ static long ch_ioctl(struct file *file,
|
|||||||
int retval;
|
int retval;
|
||||||
void __user *argp = (void __user *)arg;
|
void __user *argp = (void __user *)arg;
|
||||||
|
|
||||||
|
retval = scsi_ioctl_block_when_processing_errors(ch->device, cmd,
|
||||||
|
file->f_flags & O_NDELAY);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case CHIOGPARAMS:
|
case CHIOGPARAMS:
|
||||||
{
|
{
|
||||||
|
@ -4969,10 +4969,10 @@ static long osst_ioctl(struct file * file,
|
|||||||
* may try and take the device offline, in which case all further
|
* may try and take the device offline, in which case all further
|
||||||
* access to the device is prohibited.
|
* access to the device is prohibited.
|
||||||
*/
|
*/
|
||||||
if( !scsi_block_when_processing_errors(STp->device) ) {
|
retval = scsi_ioctl_block_when_processing_errors(STp->device, cmd_in,
|
||||||
retval = (-ENXIO);
|
file->f_flags & O_NDELAY);
|
||||||
|
if (retval)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
cmd_type = _IOC_TYPE(cmd_in);
|
cmd_type = _IOC_TYPE(cmd_in);
|
||||||
cmd_nr = _IOC_NR(cmd_in);
|
cmd_nr = _IOC_NR(cmd_in);
|
||||||
|
@ -200,19 +200,6 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
|
|||||||
{
|
{
|
||||||
char scsi_cmd[MAX_COMMAND_SIZE];
|
char scsi_cmd[MAX_COMMAND_SIZE];
|
||||||
|
|
||||||
/* No idea how this happens.... */
|
|
||||||
if (!sdev)
|
|
||||||
return -ENXIO;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we are in the middle of error recovery, don't let anyone
|
|
||||||
* else try and use this device. Also, if error recovery fails, it
|
|
||||||
* may try and take the device offline, in which case all further
|
|
||||||
* access to the device is prohibited.
|
|
||||||
*/
|
|
||||||
if (!scsi_block_when_processing_errors(sdev))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
/* Check for deprecated ioctls ... all the ioctls which don't
|
/* Check for deprecated ioctls ... all the ioctls which don't
|
||||||
* follow the new unique numbering scheme are deprecated */
|
* follow the new unique numbering scheme are deprecated */
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
@ -273,6 +260,8 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
|
|||||||
START_STOP_TIMEOUT, NORMAL_RETRIES);
|
START_STOP_TIMEOUT, NORMAL_RETRIES);
|
||||||
case SCSI_IOCTL_GET_PCI:
|
case SCSI_IOCTL_GET_PCI:
|
||||||
return scsi_ioctl_get_pci(sdev, arg);
|
return scsi_ioctl_get_pci(sdev, arg);
|
||||||
|
case SG_SCSI_RESET:
|
||||||
|
return scsi_ioctl_reset(sdev, arg);
|
||||||
default:
|
default:
|
||||||
if (sdev->host->hostt->ioctl)
|
if (sdev->host->hostt->ioctl)
|
||||||
return sdev->host->hostt->ioctl(sdev, cmd, arg);
|
return sdev->host->hostt->ioctl(sdev, cmd, arg);
|
||||||
@ -281,30 +270,20 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(scsi_ioctl);
|
EXPORT_SYMBOL(scsi_ioctl);
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* scsi_nonblockable_ioctl() - Handle SG_SCSI_RESET
|
* We can process a reset even when a device isn't fully operable.
|
||||||
* @sdev: scsi device receiving ioctl
|
|
||||||
* @cmd: Must be SC_SCSI_RESET
|
|
||||||
* @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,
|
int scsi_ioctl_block_when_processing_errors(struct scsi_device *sdev, int cmd,
|
||||||
void __user *arg, int ndelay)
|
bool ndelay)
|
||||||
{
|
{
|
||||||
/* The first set of iocts may be executed even if we're doing
|
if (cmd == SG_SCSI_RESET && ndelay) {
|
||||||
* error processing, as long as the device was opened
|
|
||||||
* non-blocking */
|
|
||||||
if (ndelay) {
|
|
||||||
if (scsi_host_in_recovery(sdev->host))
|
if (scsi_host_in_recovery(sdev->host))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
} else if (!scsi_block_when_processing_errors(sdev))
|
} else {
|
||||||
return -ENODEV;
|
if (!scsi_block_when_processing_errors(sdev))
|
||||||
|
return -ENODEV;
|
||||||
switch (cmd) {
|
|
||||||
case SG_SCSI_RESET:
|
|
||||||
return scsi_ioctl_reset(sdev, arg);
|
|
||||||
}
|
}
|
||||||
return -ENODEV;
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(scsi_nonblockable_ioctl);
|
EXPORT_SYMBOL_GPL(scsi_ioctl_block_when_processing_errors);
|
||||||
|
@ -1334,9 +1334,9 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode,
|
|||||||
* may try and take the device offline, in which case all further
|
* may try and take the device offline, in which case all further
|
||||||
* access to the device is prohibited.
|
* access to the device is prohibited.
|
||||||
*/
|
*/
|
||||||
error = scsi_nonblockable_ioctl(sdp, cmd, p,
|
error = scsi_ioctl_block_when_processing_errors(sdp, cmd,
|
||||||
(mode & FMODE_NDELAY) != 0);
|
(mode & FMODE_NDELAY) != 0);
|
||||||
if (!scsi_block_when_processing_errors(sdp) || !error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1071,16 +1071,6 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
|
|||||||
if (atomic_read(&sdp->detaching))
|
if (atomic_read(&sdp->detaching))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
return put_user(sdp->device->host->hostt->emulated, ip);
|
return put_user(sdp->device->host->hostt->emulated, ip);
|
||||||
case SG_SCSI_RESET:
|
|
||||||
if (atomic_read(&sdp->detaching))
|
|
||||||
return -ENODEV;
|
|
||||||
if (filp->f_flags & O_NONBLOCK) {
|
|
||||||
if (scsi_host_in_recovery(sdp->device->host))
|
|
||||||
return -EBUSY;
|
|
||||||
} else if (!scsi_block_when_processing_errors(sdp->device))
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
return scsi_ioctl_reset(sdp->device, ip);
|
|
||||||
case SCSI_IOCTL_SEND_COMMAND:
|
case SCSI_IOCTL_SEND_COMMAND:
|
||||||
if (atomic_read(&sdp->detaching))
|
if (atomic_read(&sdp->detaching))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
@ -1100,13 +1090,6 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
|
|||||||
return result;
|
return result;
|
||||||
sdp->sgdebug = (char) val;
|
sdp->sgdebug = (char) val;
|
||||||
return 0;
|
return 0;
|
||||||
case SCSI_IOCTL_GET_IDLUN:
|
|
||||||
case SCSI_IOCTL_GET_BUS_NUMBER:
|
|
||||||
case SCSI_IOCTL_PROBE_HOST:
|
|
||||||
case SG_GET_TRANSFORM:
|
|
||||||
if (atomic_read(&sdp->detaching))
|
|
||||||
return -ENODEV;
|
|
||||||
return scsi_ioctl(sdp->device, cmd_in, p);
|
|
||||||
case BLKSECTGET:
|
case BLKSECTGET:
|
||||||
return put_user(max_sectors_bytes(sdp->device->request_queue),
|
return put_user(max_sectors_bytes(sdp->device->request_queue),
|
||||||
ip);
|
ip);
|
||||||
@ -1122,11 +1105,25 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
|
|||||||
return blk_trace_startstop(sdp->device->request_queue, 0);
|
return blk_trace_startstop(sdp->device->request_queue, 0);
|
||||||
case BLKTRACETEARDOWN:
|
case BLKTRACETEARDOWN:
|
||||||
return blk_trace_remove(sdp->device->request_queue);
|
return blk_trace_remove(sdp->device->request_queue);
|
||||||
|
case SCSI_IOCTL_GET_IDLUN:
|
||||||
|
case SCSI_IOCTL_GET_BUS_NUMBER:
|
||||||
|
case SCSI_IOCTL_PROBE_HOST:
|
||||||
|
case SG_GET_TRANSFORM:
|
||||||
|
case SG_SCSI_RESET:
|
||||||
|
if (atomic_read(&sdp->detaching))
|
||||||
|
return -ENODEV;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (read_only)
|
if (read_only)
|
||||||
return -EPERM; /* don't know so take safe approach */
|
return -EPERM; /* don't know so take safe approach */
|
||||||
return scsi_ioctl(sdp->device, cmd_in, p);
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result = scsi_ioctl_block_when_processing_errors(sdp->device,
|
||||||
|
cmd_in, filp->f_flags & O_NDELAY);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
return scsi_ioctl(sdp->device, cmd_in, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_COMPAT
|
#ifdef CONFIG_COMPAT
|
||||||
|
@ -549,6 +549,11 @@ static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
|
|||||||
|
|
||||||
mutex_lock(&sr_mutex);
|
mutex_lock(&sr_mutex);
|
||||||
|
|
||||||
|
ret = scsi_ioctl_block_when_processing_errors(sdev, cmd,
|
||||||
|
(mode & FMODE_NDELAY) != 0);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send SCSI addressing ioctls directly to mid level, send other
|
* Send SCSI addressing ioctls directly to mid level, send other
|
||||||
* ioctls to cdrom/block level.
|
* ioctls to cdrom/block level.
|
||||||
@ -564,16 +569,6 @@ static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
|
|||||||
if (ret != -ENOSYS)
|
if (ret != -ENOSYS)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/*
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
ret = scsi_nonblockable_ioctl(sdev, cmd, argp,
|
|
||||||
(mode & FMODE_NDELAY) != 0);
|
|
||||||
if (ret != -ENODEV)
|
|
||||||
goto out;
|
|
||||||
ret = scsi_ioctl(sdev, cmd, argp);
|
ret = scsi_ioctl(sdev, cmd, argp);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -3376,11 +3376,10 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
|
|||||||
* may try and take the device offline, in which case all further
|
* may try and take the device offline, in which case all further
|
||||||
* access to the device is prohibited.
|
* access to the device is prohibited.
|
||||||
*/
|
*/
|
||||||
retval = scsi_nonblockable_ioctl(STp->device, cmd_in, p,
|
retval = scsi_ioctl_block_when_processing_errors(STp->device, cmd_in,
|
||||||
file->f_flags & O_NDELAY);
|
file->f_flags & O_NDELAY);
|
||||||
if (!scsi_block_when_processing_errors(STp->device) || retval != -ENODEV)
|
if (retval)
|
||||||
goto out;
|
goto out;
|
||||||
retval = 0;
|
|
||||||
|
|
||||||
cmd_type = _IOC_TYPE(cmd_in);
|
cmd_type = _IOC_TYPE(cmd_in);
|
||||||
cmd_nr = _IOC_NR(cmd_in);
|
cmd_nr = _IOC_NR(cmd_in);
|
||||||
|
@ -40,9 +40,9 @@ typedef struct scsi_fctargaddress {
|
|||||||
unsigned char host_wwn[8]; // include NULL term.
|
unsigned char host_wwn[8]; // include NULL term.
|
||||||
} Scsi_FCTargAddress;
|
} Scsi_FCTargAddress;
|
||||||
|
|
||||||
|
int scsi_ioctl_block_when_processing_errors(struct scsi_device *sdev,
|
||||||
|
int cmd, bool ndelay);
|
||||||
extern int scsi_ioctl(struct scsi_device *, int, void __user *);
|
extern int scsi_ioctl(struct scsi_device *, int, void __user *);
|
||||||
extern int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd,
|
|
||||||
void __user *arg, int ndelay);
|
|
||||||
|
|
||||||
#endif /* __KERNEL__ */
|
#endif /* __KERNEL__ */
|
||||||
#endif /* _SCSI_IOCTL_H */
|
#endif /* _SCSI_IOCTL_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user