libata: fix shutdown warning message printing
Unlocking ap->lock and ssleeping don't work because SCSI commands can be issued from completion path without context. Reimplement delayed completion by allowing translation functions to override qc->scsidone(), storing the original completion function to scmd->scsi_done() and overriding qc->scsidone() with a function which schedules delayed invocation of scmd->scsi_done(). This isn't pretty at all but all the ugly parts are thankfully contained in the stop translation path where the compat feature is implemented. Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
This commit is contained in:
parent
3cadbcc098
commit
da071b42f7
|
@ -893,6 +893,23 @@ int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth)
|
||||||
return queue_depth;
|
return queue_depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* XXX: for ata_spindown_compat */
|
||||||
|
static void ata_delayed_done_timerfn(unsigned long arg)
|
||||||
|
{
|
||||||
|
struct scsi_cmnd *scmd = (void *)arg;
|
||||||
|
|
||||||
|
scmd->scsi_done(scmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* XXX: for ata_spindown_compat */
|
||||||
|
static void ata_delayed_done(struct scsi_cmnd *scmd)
|
||||||
|
{
|
||||||
|
static struct timer_list timer;
|
||||||
|
|
||||||
|
setup_timer(&timer, ata_delayed_done_timerfn, (unsigned long)scmd);
|
||||||
|
mod_timer(&timer, jiffies + 5 * HZ);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ata_scsi_start_stop_xlat - Translate SCSI START STOP UNIT command
|
* ata_scsi_start_stop_xlat - Translate SCSI START STOP UNIT command
|
||||||
* @qc: Storage for translated ATA taskfile
|
* @qc: Storage for translated ATA taskfile
|
||||||
|
@ -952,19 +969,21 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc)
|
||||||
if (ata_spindown_compat &&
|
if (ata_spindown_compat &&
|
||||||
(system_state == SYSTEM_HALT ||
|
(system_state == SYSTEM_HALT ||
|
||||||
system_state == SYSTEM_POWER_OFF)) {
|
system_state == SYSTEM_POWER_OFF)) {
|
||||||
static int warned = 0;
|
static unsigned long warned = 0;
|
||||||
|
|
||||||
if (!warned) {
|
if (!test_and_set_bit(0, &warned)) {
|
||||||
spin_unlock_irq(qc->ap->lock);
|
|
||||||
ata_dev_printk(qc->dev, KERN_WARNING,
|
ata_dev_printk(qc->dev, KERN_WARNING,
|
||||||
"DISK MIGHT NOT BE SPUN DOWN PROPERLY. "
|
"DISK MIGHT NOT BE SPUN DOWN PROPERLY. "
|
||||||
"UPDATE SHUTDOWN UTILITY\n");
|
"UPDATE SHUTDOWN UTILITY\n");
|
||||||
ata_dev_printk(qc->dev, KERN_WARNING,
|
ata_dev_printk(qc->dev, KERN_WARNING,
|
||||||
"For more info, visit "
|
"For more info, visit "
|
||||||
"http://linux-ata.org/shutdown.html\n");
|
"http://linux-ata.org/shutdown.html\n");
|
||||||
warned = 1;
|
|
||||||
ssleep(5);
|
/* ->scsi_done is not used, use it for
|
||||||
spin_lock_irq(qc->ap->lock);
|
* delayed completion.
|
||||||
|
*/
|
||||||
|
scmd->scsi_done = qc->scsidone;
|
||||||
|
qc->scsidone = ata_delayed_done;
|
||||||
}
|
}
|
||||||
scmd->result = SAM_STAT_GOOD;
|
scmd->result = SAM_STAT_GOOD;
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1488,14 +1507,14 @@ static int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd,
|
||||||
|
|
||||||
early_finish:
|
early_finish:
|
||||||
ata_qc_free(qc);
|
ata_qc_free(qc);
|
||||||
done(cmd);
|
qc->scsidone(cmd);
|
||||||
DPRINTK("EXIT - early finish (good or error)\n");
|
DPRINTK("EXIT - early finish (good or error)\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_did:
|
err_did:
|
||||||
ata_qc_free(qc);
|
ata_qc_free(qc);
|
||||||
cmd->result = (DID_ERROR << 16);
|
cmd->result = (DID_ERROR << 16);
|
||||||
done(cmd);
|
qc->scsidone(cmd);
|
||||||
err_mem:
|
err_mem:
|
||||||
DPRINTK("EXIT - internal\n");
|
DPRINTK("EXIT - internal\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue