ide: ensure atapi sense request aren't preempted

There's an issue with how sense requests are handled in IDE. If ide-cd
encounters an error, it queues a sense request. With how IDE request
handling is done, this is the next request we need to handle. But it's
impossible to guarantee this, as another request could come in between
the sense being queued, and ->queue_rq() being run and handling it. If
that request ALSO fails, then we attempt to doubly queue the single
sense request we have.

Since we only support one active request at the time, defer request
processing when a sense request is queued.

Fixes: 600335205b "ide: convert to blk-mq"
Reported-by: He Zhe <zhe.he@windriver.com>
Tested-by: He Zhe <zhe.he@windriver.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Jens Axboe 2019-01-30 08:41:40 -07:00
parent 85bd6e61f3
commit 9a6d548800
5 changed files with 59 additions and 38 deletions

View File

@ -235,21 +235,28 @@ EXPORT_SYMBOL_GPL(ide_prep_sense);
int ide_queue_sense_rq(ide_drive_t *drive, void *special)
{
struct request *sense_rq = drive->sense_rq;
ide_hwif_t *hwif = drive->hwif;
struct request *sense_rq;
unsigned long flags;
spin_lock_irqsave(&hwif->lock, flags);
/* deferred failure from ide_prep_sense() */
if (!drive->sense_rq_armed) {
printk(KERN_WARNING PFX "%s: error queuing a sense request\n",
drive->name);
spin_unlock_irqrestore(&hwif->lock, flags);
return -ENOMEM;
}
sense_rq = drive->sense_rq;
ide_req(sense_rq)->special = special;
drive->sense_rq_armed = false;
drive->hwif->rq = NULL;
ide_insert_request_head(drive, sense_rq);
spin_unlock_irqrestore(&hwif->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ide_queue_sense_rq);

View File

@ -68,8 +68,10 @@ int ide_end_rq(ide_drive_t *drive, struct request *rq, blk_status_t error,
}
if (!blk_update_request(rq, error, nr_bytes)) {
if (rq == drive->sense_rq)
if (rq == drive->sense_rq) {
drive->sense_rq = NULL;
drive->sense_rq_active = false;
}
__blk_mq_end_request(rq, error);
return 0;
@ -451,16 +453,11 @@ void ide_requeue_and_plug(ide_drive_t *drive, struct request *rq)
blk_mq_delay_run_hw_queue(q->queue_hw_ctx[0], 3);
}
/*
* Issue a new request to a device.
*/
blk_status_t ide_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd)
blk_status_t ide_issue_rq(ide_drive_t *drive, struct request *rq,
bool local_requeue)
{
ide_drive_t *drive = hctx->queue->queuedata;
ide_hwif_t *hwif = drive->hwif;
ide_hwif_t *hwif = drive->hwif;
struct ide_host *host = hwif->host;
struct request *rq = bd->rq;
ide_startstop_t startstop;
if (!blk_rq_is_passthrough(rq) && !(rq->rq_flags & RQF_DONTPREP)) {
@ -474,8 +471,6 @@ blk_status_t ide_queue_rq(struct blk_mq_hw_ctx *hctx,
if (ide_lock_host(host, hwif))
return BLK_STS_DEV_RESOURCE;
blk_mq_start_request(rq);
spin_lock_irq(&hwif->lock);
if (!ide_lock_port(hwif)) {
@ -510,18 +505,6 @@ repeat:
hwif->cur_dev = drive;
drive->dev_flags &= ~(IDE_DFLAG_SLEEPING | IDE_DFLAG_PARKED);
/*
* we know that the queue isn't empty, but this can happen
* if ->prep_rq() decides to kill a request
*/
if (!rq) {
rq = bd->rq;
if (!rq) {
ide_unlock_port(hwif);
goto out;
}
}
/*
* Sanity: don't accept a request that isn't a PM request
* if we are currently power managed. This is very important as
@ -560,9 +543,12 @@ repeat:
}
} else {
plug_device:
if (local_requeue)
list_add(&rq->queuelist, &drive->rq_list);
spin_unlock_irq(&hwif->lock);
ide_unlock_host(host);
ide_requeue_and_plug(drive, rq);
if (!local_requeue)
ide_requeue_and_plug(drive, rq);
return BLK_STS_OK;
}
@ -573,6 +559,26 @@ out:
return BLK_STS_OK;
}
/*
* Issue a new request to a device.
*/
blk_status_t ide_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd)
{
ide_drive_t *drive = hctx->queue->queuedata;
ide_hwif_t *hwif = drive->hwif;
spin_lock_irq(&hwif->lock);
if (drive->sense_rq_active) {
spin_unlock_irq(&hwif->lock);
return BLK_STS_DEV_RESOURCE;
}
spin_unlock_irq(&hwif->lock);
blk_mq_start_request(bd->rq);
return ide_issue_rq(drive, bd->rq, false);
}
static int drive_is_ready(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
@ -893,13 +899,8 @@ EXPORT_SYMBOL_GPL(ide_pad_transfer);
void ide_insert_request_head(ide_drive_t *drive, struct request *rq)
{
ide_hwif_t *hwif = drive->hwif;
unsigned long flags;
spin_lock_irqsave(&hwif->lock, flags);
drive->sense_rq_active = true;
list_add_tail(&rq->queuelist, &drive->rq_list);
spin_unlock_irqrestore(&hwif->lock, flags);
kblockd_schedule_work(&drive->rq_work);
}
EXPORT_SYMBOL_GPL(ide_insert_request_head);

View File

@ -54,7 +54,9 @@ static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout)
scsi_req(rq)->cmd[0] = REQ_UNPARK_HEADS;
scsi_req(rq)->cmd_len = 1;
ide_req(rq)->type = ATA_PRIV_MISC;
spin_lock_irq(&hwif->lock);
ide_insert_request_head(drive, rq);
spin_unlock_irq(&hwif->lock);
out:
return;

View File

@ -1159,18 +1159,27 @@ static void drive_rq_insert_work(struct work_struct *work)
ide_drive_t *drive = container_of(work, ide_drive_t, rq_work);
ide_hwif_t *hwif = drive->hwif;
struct request *rq;
blk_status_t ret;
LIST_HEAD(list);
blk_mq_quiesce_queue(drive->queue);
ret = BLK_STS_OK;
spin_lock_irq(&hwif->lock);
if (!list_empty(&drive->rq_list))
list_splice_init(&drive->rq_list, &list);
while (!list_empty(&drive->rq_list)) {
rq = list_first_entry(&drive->rq_list, struct request, queuelist);
list_del_init(&rq->queuelist);
spin_unlock_irq(&hwif->lock);
ret = ide_issue_rq(drive, rq, true);
spin_lock_irq(&hwif->lock);
}
spin_unlock_irq(&hwif->lock);
while (!list_empty(&list)) {
rq = list_first_entry(&list, struct request, queuelist);
list_del_init(&rq->queuelist);
blk_execute_rq_nowait(drive->queue, rq->rq_disk, rq, true, NULL);
}
blk_mq_unquiesce_queue(drive->queue);
if (ret != BLK_STS_OK)
kblockd_schedule_work(&drive->rq_work);
}
static const u8 ide_hwif_to_major[] =

View File

@ -615,6 +615,7 @@ struct ide_drive_s {
/* current sense rq and buffer */
bool sense_rq_armed;
bool sense_rq_active;
struct request *sense_rq;
struct request_sense sense_data;
@ -1219,6 +1220,7 @@ extern void ide_stall_queue(ide_drive_t *drive, unsigned long timeout);
extern void ide_timer_expiry(struct timer_list *t);
extern irqreturn_t ide_intr(int irq, void *dev_id);
extern blk_status_t ide_queue_rq(struct blk_mq_hw_ctx *, const struct blk_mq_queue_data *);
extern blk_status_t ide_issue_rq(ide_drive_t *, struct request *, bool);
extern void ide_requeue_and_plug(ide_drive_t *drive, struct request *rq);
void ide_init_disk(struct gendisk *, ide_drive_t *);