Skip to content

Commit d9d32e5

Browse files
committed
Merge tag 'ata-7.0-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/libata/linux
Pull ata fixes from Niklas Cassel: - The newly introduced feature that issues a deferred (non-NCQ) command from a workqueue, forgot to consider the case where the deferred QC times out. Fix the code to take timeouts into consideration, which avoids a use after free (Damien) - The newly introduced feature that issues a deferred (non-NCQ) command from a workqueue, when unloading the module, calls cancel_work_sync(), a function that can sleep, while holding a spin lock. Move the function call outside the lock (Damien) * tag 'ata-7.0-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/libata/linux: ata: libata-core: fix cancellation of a port deferred qc work ata: libata-eh: correctly handle deferred qc timeouts
2 parents 0e335a7 + 55db009 commit d9d32e5

2 files changed

Lines changed: 22 additions & 8 deletions

File tree

drivers/ata/libata-core.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6269,10 +6269,6 @@ static void ata_port_detach(struct ata_port *ap)
62696269
}
62706270
}
62716271

6272-
/* Make sure the deferred qc work finished. */
6273-
cancel_work_sync(&ap->deferred_qc_work);
6274-
WARN_ON(ap->deferred_qc);
6275-
62766272
/* Tell EH to disable all devices */
62776273
ap->pflags |= ATA_PFLAG_UNLOADING;
62786274
ata_port_schedule_eh(ap);
@@ -6283,9 +6279,11 @@ static void ata_port_detach(struct ata_port *ap)
62836279
/* wait till EH commits suicide */
62846280
ata_port_wait_eh(ap);
62856281

6286-
/* it better be dead now */
6282+
/* It better be dead now and not have any remaining deferred qc. */
62876283
WARN_ON(!(ap->pflags & ATA_PFLAG_UNLOADED));
6284+
WARN_ON(ap->deferred_qc);
62886285

6286+
cancel_work_sync(&ap->deferred_qc_work);
62896287
cancel_delayed_work_sync(&ap->hotplug_task);
62906288
cancel_delayed_work_sync(&ap->scsi_rescan_task);
62916289

drivers/ata/libata-eh.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -640,12 +640,28 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap,
640640
set_host_byte(scmd, DID_OK);
641641

642642
ata_qc_for_each_raw(ap, qc, i) {
643-
if (qc->flags & ATA_QCFLAG_ACTIVE &&
644-
qc->scsicmd == scmd)
643+
if (qc->scsicmd != scmd)
644+
continue;
645+
if ((qc->flags & ATA_QCFLAG_ACTIVE) ||
646+
qc == ap->deferred_qc)
645647
break;
646648
}
647649

648-
if (i < ATA_MAX_QUEUE) {
650+
if (qc == ap->deferred_qc) {
651+
/*
652+
* This is a deferred command that timed out while
653+
* waiting for the command queue to drain. Since the qc
654+
* is not active yet (deferred_qc is still set, so the
655+
* deferred qc work has not issued the command yet),
656+
* simply signal the timeout by finishing the SCSI
657+
* command and clear the deferred qc to prevent the
658+
* deferred qc work from issuing this qc.
659+
*/
660+
WARN_ON_ONCE(qc->flags & ATA_QCFLAG_ACTIVE);
661+
ap->deferred_qc = NULL;
662+
set_host_byte(scmd, DID_TIME_OUT);
663+
scsi_eh_finish_cmd(scmd, &ap->eh_done_q);
664+
} else if (i < ATA_MAX_QUEUE) {
649665
/* the scmd has an associated qc */
650666
if (!(qc->flags & ATA_QCFLAG_EH)) {
651667
/* which hasn't failed yet, timeout */

0 commit comments

Comments
 (0)