ide: fix races in handling of user-space SET XFER commands
* Make cmd->tf_flags field 'u16' and add IDE_TFLAG_SET_XFER taskfile flag. * Update ide_finish_cmd() to set xfer / re-read id if the new flag is set. * Convert set_xfer_rate() (write handler for /proc/ide/hd?/current_speed) and ide_cmd_ioctl() (HDIO_DRIVE_CMD ioctl handler) to use the new flag. * Remove no longer needed disable_irq_nosync() + enable_irq() from ide_config_drive_speed(). Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
fa56d4cb40
commit
665d66e8fa
|
@ -167,6 +167,8 @@ static int ide_cmd_ioctl(ide_drive_t *drive, unsigned long arg)
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
goto abort;
|
goto abort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.tf_flags |= IDE_TFLAG_SET_XFER;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ide_raw_taskfile(drive, &cmd, buf, args[3]);
|
err = ide_raw_taskfile(drive, &cmd, buf, args[3]);
|
||||||
|
@ -174,12 +176,6 @@ static int ide_cmd_ioctl(ide_drive_t *drive, unsigned long arg)
|
||||||
args[0] = tf->status;
|
args[0] = tf->status;
|
||||||
args[1] = tf->error;
|
args[1] = tf->error;
|
||||||
args[2] = tf->nsect;
|
args[2] = tf->nsect;
|
||||||
|
|
||||||
if (!err && xfer_rate) {
|
|
||||||
/* active-retuning-calls future */
|
|
||||||
ide_set_xfer_rate(drive, xfer_rate);
|
|
||||||
ide_driveid_update(drive);
|
|
||||||
}
|
|
||||||
abort:
|
abort:
|
||||||
if (copy_to_user((void __user *)arg, &args, 4))
|
if (copy_to_user((void __user *)arg, &args, 4))
|
||||||
err = -EFAULT;
|
err = -EFAULT;
|
||||||
|
|
|
@ -363,14 +363,6 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed)
|
||||||
* this point (lost interrupt).
|
* this point (lost interrupt).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
* FIXME: we race against the running IRQ here if
|
|
||||||
* this is called from non IRQ context. If we use
|
|
||||||
* disable_irq() we hang on the error path. Work
|
|
||||||
* is needed.
|
|
||||||
*/
|
|
||||||
disable_irq_nosync(hwif->irq);
|
|
||||||
|
|
||||||
udelay(1);
|
udelay(1);
|
||||||
tp_ops->dev_select(drive);
|
tp_ops->dev_select(drive);
|
||||||
SELECT_MASK(drive, 1);
|
SELECT_MASK(drive, 1);
|
||||||
|
@ -394,8 +386,6 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed)
|
||||||
|
|
||||||
SELECT_MASK(drive, 0);
|
SELECT_MASK(drive, 0);
|
||||||
|
|
||||||
enable_irq(hwif->irq);
|
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
(void) ide_dump_status(drive, "set_drive_speed_status", stat);
|
(void) ide_dump_status(drive, "set_drive_speed_status", stat);
|
||||||
return error;
|
return error;
|
||||||
|
|
|
@ -195,7 +195,6 @@ ide_devset_get(xfer_rate, current_speed);
|
||||||
static int set_xfer_rate (ide_drive_t *drive, int arg)
|
static int set_xfer_rate (ide_drive_t *drive, int arg)
|
||||||
{
|
{
|
||||||
struct ide_cmd cmd;
|
struct ide_cmd cmd;
|
||||||
int err;
|
|
||||||
|
|
||||||
if (arg < XFER_PIO_0 || arg > XFER_UDMA_6)
|
if (arg < XFER_PIO_0 || arg > XFER_UDMA_6)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -206,14 +205,9 @@ static int set_xfer_rate (ide_drive_t *drive, int arg)
|
||||||
cmd.tf.nsect = (u8)arg;
|
cmd.tf.nsect = (u8)arg;
|
||||||
cmd.valid.out.tf = IDE_VALID_FEATURE | IDE_VALID_NSECT;
|
cmd.valid.out.tf = IDE_VALID_FEATURE | IDE_VALID_NSECT;
|
||||||
cmd.valid.in.tf = IDE_VALID_NSECT;
|
cmd.valid.in.tf = IDE_VALID_NSECT;
|
||||||
|
cmd.tf_flags = IDE_TFLAG_SET_XFER;
|
||||||
|
|
||||||
err = ide_no_data_taskfile(drive, &cmd);
|
return ide_no_data_taskfile(drive, &cmd);
|
||||||
|
|
||||||
if (!err) {
|
|
||||||
ide_set_xfer_rate(drive, (u8) arg);
|
|
||||||
ide_driveid_update(drive);
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ide_devset_rw(current_speed, xfer_rate);
|
ide_devset_rw(current_speed, xfer_rate);
|
||||||
|
|
|
@ -324,10 +324,17 @@ static void ide_error_cmd(ide_drive_t *drive, struct ide_cmd *cmd)
|
||||||
void ide_finish_cmd(ide_drive_t *drive, struct ide_cmd *cmd, u8 stat)
|
void ide_finish_cmd(ide_drive_t *drive, struct ide_cmd *cmd, u8 stat)
|
||||||
{
|
{
|
||||||
struct request *rq = drive->hwif->rq;
|
struct request *rq = drive->hwif->rq;
|
||||||
u8 err = ide_read_error(drive);
|
u8 err = ide_read_error(drive), nsect = cmd->tf.nsect;
|
||||||
|
u8 set_xfer = !!(cmd->tf_flags & IDE_TFLAG_SET_XFER);
|
||||||
|
|
||||||
ide_complete_cmd(drive, cmd, stat, err);
|
ide_complete_cmd(drive, cmd, stat, err);
|
||||||
rq->errors = err;
|
rq->errors = err;
|
||||||
|
|
||||||
|
if (err == 0 && set_xfer) {
|
||||||
|
ide_set_xfer_rate(drive, nsect);
|
||||||
|
ide_driveid_update(drive);
|
||||||
|
}
|
||||||
|
|
||||||
ide_complete_rq(drive, err ? -EIO : 0, blk_rq_bytes(rq));
|
ide_complete_rq(drive, err ? -EIO : 0, blk_rq_bytes(rq));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -258,6 +258,7 @@ enum {
|
||||||
IDE_TFLAG_DYN = (1 << 5),
|
IDE_TFLAG_DYN = (1 << 5),
|
||||||
IDE_TFLAG_FS = (1 << 6),
|
IDE_TFLAG_FS = (1 << 6),
|
||||||
IDE_TFLAG_MULTI_PIO = (1 << 7),
|
IDE_TFLAG_MULTI_PIO = (1 << 7),
|
||||||
|
IDE_TFLAG_SET_XFER = (1 << 8),
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -294,7 +295,7 @@ struct ide_cmd {
|
||||||
} out, in;
|
} out, in;
|
||||||
} valid;
|
} valid;
|
||||||
|
|
||||||
u8 tf_flags;
|
u16 tf_flags;
|
||||||
u8 ftf_flags; /* for TASKFILE ioctl */
|
u8 ftf_flags; /* for TASKFILE ioctl */
|
||||||
int protocol;
|
int protocol;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue