Hi,
I've been trying for the last few days to get my D810 to suspend and
resume in linux.
I'm doing it from klaptop in kde using Fedora Core 3, but I've now
compiled my own linux-2.6.12-rc2-mm3 kernel since I've seen some ACPI
changes going in.
At 2.6.11 it would seem to suspend ok, but when doing the resume it
would come back and have I/O errors.. causing the computer to freeze for
a few seconds, then run for a second, then freeze again, etc.. the HDD
light would stay on solid, and at the tty1 I saw something like "ata1:
command 0xc8 timeout... I/O error..." So apparently something isn't
getting starting back up. Thinking it might be the HDD not spinning, I
powered off, but DID hear it spin down.
Running what I compiled, 2.6.12-rc2-mm3, the suspend happens a little
faster but the resume comes to a blank screen, then immediately reboots
without any messages that I can see.
I'm very interested in getting this to work and will do whatever someone
needs to gather information.
I may need to ask basic kernel info questions when asked to do something
as I haven't done much trouble shooting at this low a level before but
I'm game. From googling around this is a problem for many and I would
like to help resolve it.
If I need to take this message to another mailing list or another
individual working on ACPI stuff or something just let me know.
Any ideas?
Thanks,
Davy
Davy Durham <pubaddr2 <at> davyandbeth.com> writes:
>
> Hi,
> I've been trying for the last few days to get my D810 to suspend and
> resume in linux.
>
I have the same problem with a 2.6.11 kernel on an IBM T43, also with the new
Sonoma chipset.
Although a PATA HDD, it appears to be presented as a SATA device (/dev/sda in
Linux). It seems to be hanging on the first disk access following a resume, and
I'm seeing the same behaviour with both APM and ACPI.
Happy to provide any information/assistance that I can to help resolve this..
Regards,
Jon.
On Mon, May 02 2005, Jon Escombe wrote:
> Davy Durham <pubaddr2 <at> davyandbeth.com> writes:
>
> >
> > Hi,
> > I've been trying for the last few days to get my D810 to suspend and
> > resume in linux.
> >
>
> I have the same problem with a 2.6.11 kernel on an IBM T43, also with
> the new Sonoma chipset.
>
> Although a PATA HDD, it appears to be presented as a SATA device
> (/dev/sda in Linux). It seems to be hanging on the first disk access
> following a resume, and I'm seeing the same behaviour with both APM
> and ACPI.
>
> Happy to provide any information/assistance that I can to help resolve
> this..
SATA suspend is completely broken right now, so I'm not surprised. You
can shoe-horn this patch onto 2.6.12-rcX - it will reject for every
ordered_flush addition, but should be trivial to fix up. If you have
problems, let me know and I'll generate a proper diff for you.
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/ahci.c linux-2.6.11/drivers/scsi/ahci.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/ahci.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/ahci.c 2005-04-28 09:41:00.000000000 +0200
@@ -203,6 +203,8 @@ static Scsi_Host_Template ahci_sht = {
.dma_boundary = AHCI_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations ahci_ops = {
@@ -270,6 +272,8 @@ static struct pci_driver ahci_pci_driver
.id_table = ahci_pci_tbl,
.probe = ahci_init_one,
.remove = ahci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/ata_adma.c linux-2.6.11/drivers/scsi/ata_adma.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/ata_adma.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/ata_adma.c 2005-04-28 09:41:00.000000000 +0200
@@ -134,6 +134,8 @@ static Scsi_Host_Template adma_sht = {
.dma_boundary = ATA_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations adma_ops = {
@@ -183,6 +185,8 @@ static struct pci_driver adma_pci_driver
.id_table = adma_pci_tbl,
.probe = adma_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/ata_piix.c linux-2.6.11/drivers/scsi/ata_piix.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/ata_piix.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/ata_piix.c 2005-04-28 09:41:00.000000000 +0200
@@ -105,6 +105,8 @@ static struct pci_driver piix_pci_driver
.id_table = piix_pci_tbl,
.probe = piix_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
static Scsi_Host_Template piix_sht = {
@@ -124,6 +126,8 @@ static Scsi_Host_Template piix_sht = {
.dma_boundary = ATA_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations piix_pata_ops = {
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/libata-core.c linux-2.6.11/drivers/scsi/libata-core.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/libata-core.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/libata-core.c 2005-04-28 09:41:24.000000000 +0200
@@ -3338,6 +3338,104 @@ err_out:
ata_qc_complete(qc, ATA_ERR);
}
+/*
+ * Execute a 'simple' command, that only consists of the opcode 'cmd' itself,
+ * without filling any other registers
+ */
+static int ata_do_simple_cmd(struct ata_port *ap, struct ata_device *dev,
+ u8 cmd)
+{
+ DECLARE_COMPLETION(wait);
+ struct ata_queued_cmd *qc;
+ unsigned long flags;
+ int rc;
+
+ qc = ata_qc_new_init(ap, dev);
+ BUG_ON(qc == NULL);
+
+ qc->tf.command = cmd;
+ qc->tf.flags |= ATA_TFLAG_DEVICE;
+ qc->tf.protocol = ATA_PROT_NODATA;
+
+ qc->waiting = &wait;
+ qc->complete_fn = ata_qc_complete_noop;
+
+ spin_lock_irqsave(&ap->host_set->lock, flags);
+ rc = ata_qc_issue(qc);
+ spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+ if (!rc)
+ wait_for_completion(&wait);
+
+ return rc;
+}
+
+static int ata_flush_cache(struct ata_port *ap, struct ata_device *dev)
+{
+ u8 cmd;
+
+ if (!ata_try_flush_cache(dev))
+ return 0;
+
+ if (ata_id_has_flush_ext(dev->id))
+ cmd = ATA_CMD_FLUSH_EXT;
+ else
+ cmd = ATA_CMD_FLUSH;
+
+ return ata_do_simple_cmd(ap, dev, cmd);
+}
+
+static int ata_standby_drive(struct ata_port *ap, struct ata_device *dev)
+{
+ return ata_do_simple_cmd(ap, dev, ATA_CMD_STANDBYNOW1);
+}
+
+static int ata_start_drive(struct ata_port *ap, struct ata_device *dev)
+{
+ return ata_do_simple_cmd(ap, dev, ATA_CMD_IDLEIMMEDIATE);
+}
+
+/**
+ * ata_device_resume - wakeup a previously suspended devices
+ *
+ * Kick the drive back into action, by sending it an idle immediate
+ * command and making sure its transfer mode matches between drive
+ * and host.
+ *
+ */
+int ata_device_resume(struct ata_port *ap, struct ata_device *dev)
+{
+ if (ap->flags & ATA_FLAG_SUSPENDED) {
+ ap->flags &= ~ATA_FLAG_SUSPENDED;
+ ata_set_mode(ap);
+ }
+ if (!ata_dev_present(dev))
+ return 0;
+ if (dev->class == ATA_DEV_ATA)
+ ata_start_drive(ap, dev);
+
+ return 0;
+}
+
+/**
+ * ata_device_suspend - prepare a device for suspend
+ *
+ * Flush the cache on the drive, if appropriate, then issue a
+ * standbynow command.
+ *
+ */
+int ata_device_suspend(struct ata_port *ap, struct ata_device *dev)
+{
+ if (!ata_dev_present(dev))
+ return 0;
+ if (dev->class == ATA_DEV_ATA)
+ ata_flush_cache(ap, dev);
+
+ ata_standby_drive(ap, dev);
+ ap->flags |= ATA_FLAG_SUSPENDED;
+ return 0;
+}
+
int ata_port_start (struct ata_port *ap)
{
struct device *dev = ap->host_set->dev;
@@ -3977,6 +4075,23 @@ int pci_test_config_bits(struct pci_dev
return (tmp == bits->val) ? 1 : 0;
}
+
+int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+ return 0;
+}
+
+int ata_pci_device_resume(struct pci_dev *pdev)
+{
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ pci_enable_device(pdev);
+ pci_set_master(pdev);
+ return 0;
+}
#endif /* CONFIG_PCI */
@@ -4062,4 +4177,11 @@ EXPORT_SYMBOL_GPL(pci_test_config_bits);
EXPORT_SYMBOL_GPL(ata_pci_init_native_mode);
EXPORT_SYMBOL_GPL(ata_pci_init_one);
EXPORT_SYMBOL_GPL(ata_pci_remove_one);
+EXPORT_SYMBOL_GPL(ata_pci_device_suspend);
+EXPORT_SYMBOL_GPL(ata_pci_device_resume);
#endif /* CONFIG_PCI */
+
+EXPORT_SYMBOL_GPL(ata_device_suspend);
+EXPORT_SYMBOL_GPL(ata_device_resume);
+EXPORT_SYMBOL_GPL(ata_scsi_device_suspend);
+EXPORT_SYMBOL_GPL(ata_scsi_device_resume);
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/libata-scsi.c linux-2.6.11/drivers/scsi/libata-scsi.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/libata-scsi.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/libata-scsi.c 2005-04-28 09:41:00.000000000 +0200
@@ -637,6 +637,22 @@ int ata_scsi_error(struct Scsi_Host *hos
return 0;
}
+int ata_scsi_device_resume(struct scsi_device *sdev)
+{
+ struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0];
+ struct ata_device *dev = &ap->device[sdev->id];
+
+ return ata_device_resume(ap, dev);
+}
+
+int ata_scsi_device_suspend(struct scsi_device *sdev)
+{
+ struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0];
+ struct ata_device *dev = &ap->device[sdev->id];
+
+ return ata_device_suspend(ap, dev);
+}
+
/**
* ata_scsi_flush_xlat - Translate SCSI SYNCHRONIZE CACHE command
* @qc: Storage for translated ATA taskfile
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/pata_pdc2027x.c linux-2.6.11/drivers/scsi/pata_pdc2027x.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/pata_pdc2027x.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/pata_pdc2027x.c 2005-04-28 09:41:00.000000000 +0200
@@ -110,6 +110,8 @@ static struct pci_driver pdc2027x_pci_dr
.id_table = pdc2027x_pci_tbl,
.probe = pdc2027x_init_one,
.remove = __devexit_p(pdc2027x_remove_one),
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
static Scsi_Host_Template pdc2027x_sht = {
@@ -128,6 +130,8 @@ static Scsi_Host_Template pdc2027x_sht =
.dma_boundary = ATA_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations pdc2027x_pata100_ops = {
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/sata_nv.c linux-2.6.11/drivers/scsi/sata_nv.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/sata_nv.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/sata_nv.c 2005-04-28 09:41:00.000000000 +0200
@@ -186,6 +186,8 @@ static struct pci_driver nv_pci_driver =
.id_table = nv_pci_tbl,
.probe = nv_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
static Scsi_Host_Template nv_sht = {
@@ -205,6 +207,8 @@ static Scsi_Host_Template nv_sht = {
.dma_boundary = ATA_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations nv_ops = {
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/sata_promise.c linux-2.6.11/drivers/scsi/sata_promise.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/sata_promise.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/sata_promise.c 2005-04-28 09:41:00.000000000 +0200
@@ -105,6 +105,8 @@ static Scsi_Host_Template pdc_ata_sht =
.dma_boundary = ATA_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations pdc_ata_ops = {
@@ -194,6 +196,8 @@ static struct pci_driver pdc_ata_pci_dri
.id_table = pdc_ata_pci_tbl,
.probe = pdc_ata_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/sata_qstor.c linux-2.6.11/drivers/scsi/sata_qstor.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/sata_qstor.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/sata_qstor.c 2005-04-28 09:41:00.000000000 +0200
@@ -139,6 +139,8 @@ static Scsi_Host_Template qs_ata_sht = {
.dma_boundary = QS_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations qs_ata_ops = {
@@ -190,6 +192,8 @@ static struct pci_driver qs_ata_pci_driv
.id_table = qs_ata_pci_tbl,
.probe = qs_ata_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
static int qs_check_atapi_dma(struct ata_queued_cmd *qc)
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/sata_sil.c linux-2.6.11/drivers/scsi/sata_sil.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/sata_sil.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/sata_sil.c 2005-04-28 09:41:00.000000000 +0200
@@ -115,6 +115,8 @@ static struct pci_driver sil_pci_driver
.id_table = sil_pci_tbl,
.probe = sil_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
static Scsi_Host_Template sil_sht = {
@@ -134,6 +136,8 @@ static Scsi_Host_Template sil_sht = {
.dma_boundary = ATA_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations sil_ops = {
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/sata_sis.c linux-2.6.11/drivers/scsi/sata_sis.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/sata_sis.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/sata_sis.c 2005-04-28 09:41:00.000000000 +0200
@@ -71,6 +71,8 @@ static struct pci_driver sis_pci_driver
.id_table = sis_pci_tbl,
.probe = sis_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
static Scsi_Host_Template sis_sht = {
@@ -90,6 +92,8 @@ static Scsi_Host_Template sis_sht = {
.dma_boundary = ATA_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations sis_ops = {
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/sata_svw.c linux-2.6.11/drivers/scsi/sata_svw.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/sata_svw.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/sata_svw.c 2005-04-28 09:41:00.000000000 +0200
@@ -288,6 +288,8 @@ static Scsi_Host_Template k2_sata_sht =
.proc_info = k2_sata_proc_info,
#endif
.bios_param = ata_std_bios_param,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
@@ -457,6 +459,8 @@ static struct pci_driver k2_sata_pci_dri
.id_table = k2_sata_pci_tbl,
.probe = k2_sata_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/sata_sx4.c linux-2.6.11/drivers/scsi/sata_sx4.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/sata_sx4.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/sata_sx4.c 2005-04-28 09:41:00.000000000 +0200
@@ -188,6 +188,8 @@ static Scsi_Host_Template pdc_sata_sht =
.dma_boundary = ATA_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations pdc_20621_ops = {
@@ -234,6 +236,8 @@ static struct pci_driver pdc_sata_pci_dr
.id_table = pdc_sata_pci_tbl,
.probe = pdc_sata_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/sata_uli.c linux-2.6.11/drivers/scsi/sata_uli.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/sata_uli.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/sata_uli.c 2005-04-28 09:41:00.000000000 +0200
@@ -63,6 +63,8 @@ static struct pci_driver uli_pci_driver
.id_table = uli_pci_tbl,
.probe = uli_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
static Scsi_Host_Template uli_sht = {
@@ -82,6 +84,8 @@ static Scsi_Host_Template uli_sht = {
.dma_boundary = ATA_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations uli_ops = {
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/sata_via.c linux-2.6.11/drivers/scsi/sata_via.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/sata_via.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/sata_via.c 2005-04-28 09:41:00.000000000 +0200
@@ -83,6 +83,8 @@ static struct pci_driver svia_pci_driver
.id_table = svia_pci_tbl,
.probe = svia_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
static Scsi_Host_Template svia_sht = {
@@ -102,6 +104,8 @@ static Scsi_Host_Template svia_sht = {
.dma_boundary = ATA_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations svia_sata_ops = {
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/sata_vsc.c linux-2.6.11/drivers/scsi/sata_vsc.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/sata_vsc.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/sata_vsc.c 2005-04-28 09:41:00.000000000 +0200
@@ -204,6 +204,8 @@ static Scsi_Host_Template vsc_sata_sht =
.dma_boundary = ATA_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
@@ -380,6 +382,8 @@ static struct pci_driver vsc_sata_pci_dr
.id_table = vsc_sata_pci_tbl,
.probe = vsc_sata_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/scsi_lib.c linux-2.6.11/drivers/scsi/scsi_lib.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/scsi_lib.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/scsi_lib.c 2005-04-28 09:41:00.000000000 +0200
@@ -1843,8 +1843,9 @@ EXPORT_SYMBOL(scsi_device_quiesce);
void
scsi_device_resume(struct scsi_device *sdev)
{
- if(scsi_device_set_state(sdev, SDEV_RUNNING))
+ if (scsi_device_set_state(sdev, SDEV_RUNNING))
return;
+
scsi_run_queue(sdev->request_queue);
}
EXPORT_SYMBOL(scsi_device_resume);
diff -urp /opt/kernel/linux-2.6.11/drivers/scsi/scsi_sysfs.c linux-2.6.11/drivers/scsi/scsi_sysfs.c
--- /opt/kernel/linux-2.6.11/drivers/scsi/scsi_sysfs.c 2005-04-28 09:40:35.000000000 +0200
+++ linux-2.6.11/drivers/scsi/scsi_sysfs.c 2005-04-28 09:41:00.000000000 +0200
@@ -204,9 +204,40 @@ static int scsi_bus_match(struct device
return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0;
}
+static int scsi_bus_suspend(struct device * dev, pm_message_t state)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_host_template *sht = sdev->host->hostt;
+ int err;
+
+ err = scsi_device_quiesce(sdev);
+ if (err)
+ return err;
+
+ if (sht->suspend)
+ err = sht->suspend(sdev);
+
+ return err;
+}
+
+static int scsi_bus_resume(struct device * dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_host_template *sht = sdev->host->hostt;
+ int err = 0;
+
+ if (sht->resume)
+ err = sht->resume(sdev);
+
+ scsi_device_resume(sdev);
+ return err;
+}
+
struct bus_type scsi_bus_type = {
.name = "scsi",
.match = scsi_bus_match,
+ .suspend = scsi_bus_suspend,
+ .resume = scsi_bus_resume,
};
int scsi_sysfs_register(void)
diff -urp /opt/kernel/linux-2.6.11/include/linux/ata.h linux-2.6.11/include/linux/ata.h
--- /opt/kernel/linux-2.6.11/include/linux/ata.h 2005-04-28 09:40:36.000000000 +0200
+++ linux-2.6.11/include/linux/ata.h 2005-04-28 09:41:00.000000000 +0200
@@ -126,6 +126,8 @@ enum {
ATA_CMD_VERIFY = 0x40,
ATA_CMD_VERIFY_EXT = 0x42,
ATA_CMD_INIT_DEV_PARAMS = 0x91,
+ ATA_CMD_STANDBYNOW1 = 0xE0,
+ ATA_CMD_IDLEIMMEDIATE = 0xE1,
/* SETFEATURES stuff */
SETFEATURES_XFER = 0x03,
diff -urp /opt/kernel/linux-2.6.11/include/linux/libata.h linux-2.6.11/include/linux/libata.h
--- /opt/kernel/linux-2.6.11/include/linux/libata.h 2005-04-28 09:40:36.000000000 +0200
+++ linux-2.6.11/include/linux/libata.h 2005-04-28 09:41:45.000000000 +0200
@@ -114,6 +114,7 @@ enum {
ATA_FLAG_MMIO = (1 << 6), /* use MMIO, not PIO */
ATA_FLAG_SATA_RESET = (1 << 7), /* use COMRESET */
ATA_FLAG_PIO_DMA = (1 << 8), /* PIO cmds via DMA */
+ ATA_FLAG_SUSPENDED = (1 << 9), /* port is suspended */
ATA_QCFLAG_ACTIVE = (1 << 1), /* cmd not yet ack'd to scsi lyer */
ATA_QCFLAG_SG = (1 << 3), /* have s/g table? */
@@ -394,6 +395,8 @@ extern void ata_std_ports(struct ata_iop
extern int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
unsigned int n_ports);
extern void ata_pci_remove_one (struct pci_dev *pdev);
+extern int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state);
+extern int ata_pci_device_resume(struct pci_dev *pdev);
#endif /* CONFIG_PCI */
extern int ata_device_add(struct ata_probe_ent *ent);
extern int ata_scsi_detect(Scsi_Host_Template *sht);
@@ -402,6 +405,10 @@ extern int ata_scsi_queuecmd(struct scsi
extern int ata_scsi_error(struct Scsi_Host *host);
extern int ata_scsi_release(struct Scsi_Host *host);
extern unsigned int ata_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
+extern int ata_scsi_device_resume(struct scsi_device *);
+extern int ata_scsi_device_suspend(struct scsi_device *);
+extern int ata_device_resume(struct ata_port *, struct ata_device *);
+extern int ata_device_suspend(struct ata_port *, struct ata_device *);
/*
* Default driver ops implementations
*/
diff -urp /opt/kernel/linux-2.6.11/include/scsi/scsi_host.h linux-2.6.11/include/scsi/scsi_host.h
--- /opt/kernel/linux-2.6.11/include/scsi/scsi_host.h 2005-04-28 09:40:37.000000000 +0200
+++ linux-2.6.11/include/scsi/scsi_host.h 2005-04-28 09:41:00.000000000 +0200
@@ -269,6 +269,12 @@ struct scsi_host_template {
int (*proc_info)(struct Scsi_Host *, char *, char **, off_t, int, int);
/*
+ * suspend support
+ */
+ int (*resume)(struct scsi_device *);
+ int (*suspend)(struct scsi_device *);
+
+ /*
* Name of proc directory
*/
char *proc_name;
--
Jens Axboe
Jens Axboe <axboe <at> suse.de> writes:
> SATA suspend is completely broken right now, so I'm not surprised. You
> can shoe-horn this patch onto 2.6.12-rcX - it will reject for every
> ordered_flush addition, but should be trivial to fix up. If you have
> problems, let me know and I'll generate a proper diff for you.
>
Thanks, will give that a go.. Is this a fix that's likely to make it into 2.6.12?
Regards,
Jon.
On Mon, May 02 2005, Jon Escombe wrote:
> Jens Axboe <axboe <at> suse.de> writes:
>
> > SATA suspend is completely broken right now, so I'm not surprised. You
> > can shoe-horn this patch onto 2.6.12-rcX - it will reject for every
> > ordered_flush addition, but should be trivial to fix up. If you have
> > problems, let me know and I'll generate a proper diff for you.
> >
>
> Thanks, will give that a go.. Is this a fix that's likely to make it
> into 2.6.12?
(remember to cc folks, or your post may go unnoticed)
I don't know, depends on what Jeff/James think of this approach. There
are many different way to solve this problem. I let the scsi bus called
suspend/resume for the devices on that bus, and let the scsi host
adapter perform any device dependent actions. The pci helpers are less
debatable.
Jeff/James? Here's a patch that applies to current git.
Signed-off-by: Jens Axboe <[email protected]>
Index: drivers/scsi/ahci.c
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/drivers/scsi/ahci.c (mode:100644 sha1:da5bd33d982d7e521b6b0603adcb0911395b184d)
+++ uncommitted/drivers/scsi/ahci.c (mode:100644)
@@ -201,6 +201,8 @@
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
.ordered_flush = 1,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations ahci_ops = {
@@ -272,6 +274,8 @@
.id_table = ahci_pci_tbl,
.probe = ahci_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
Index: drivers/scsi/ata_piix.c
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/drivers/scsi/ata_piix.c (mode:100644 sha1:3867f91ef8c7bebe3d0edf0bd2e3b326a1d4061b)
+++ uncommitted/drivers/scsi/ata_piix.c (mode:100644)
@@ -104,6 +104,8 @@
.id_table = piix_pci_tbl,
.probe = piix_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
static Scsi_Host_Template piix_sht = {
@@ -124,6 +126,8 @@
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
.ordered_flush = 1,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations piix_pata_ops = {
Index: drivers/scsi/libata-core.c
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/drivers/scsi/libata-core.c (mode:100644 sha1:0b5d3a5b7edab5ce17889a0e698805ce346b4d1a)
+++ uncommitted/drivers/scsi/libata-core.c (mode:100644)
@@ -3299,6 +3299,104 @@
ata_qc_complete(qc, ATA_ERR);
}
+/*
+ * Execute a 'simple' command, that only consists of the opcode 'cmd' itself,
+ * without filling any other registers
+ */
+static int ata_do_simple_cmd(struct ata_port *ap, struct ata_device *dev,
+ u8 cmd)
+{
+ DECLARE_COMPLETION(wait);
+ struct ata_queued_cmd *qc;
+ unsigned long flags;
+ int rc;
+
+ qc = ata_qc_new_init(ap, dev);
+ BUG_ON(qc == NULL);
+
+ qc->tf.command = cmd;
+ qc->tf.flags |= ATA_TFLAG_DEVICE;
+ qc->tf.protocol = ATA_PROT_NODATA;
+
+ qc->waiting = &wait;
+ qc->complete_fn = ata_qc_complete_noop;
+
+ spin_lock_irqsave(&ap->host_set->lock, flags);
+ rc = ata_qc_issue(qc);
+ spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+ if (!rc)
+ wait_for_completion(&wait);
+
+ return rc;
+}
+
+static int ata_flush_cache(struct ata_port *ap, struct ata_device *dev)
+{
+ u8 cmd;
+
+ if (!ata_try_flush_cache(dev))
+ return 0;
+
+ if (ata_id_has_flush_ext(dev->id))
+ cmd = ATA_CMD_FLUSH_EXT;
+ else
+ cmd = ATA_CMD_FLUSH;
+
+ return ata_do_simple_cmd(ap, dev, cmd);
+}
+
+static int ata_standby_drive(struct ata_port *ap, struct ata_device *dev)
+{
+ return ata_do_simple_cmd(ap, dev, ATA_CMD_STANDBYNOW1);
+}
+
+static int ata_start_drive(struct ata_port *ap, struct ata_device *dev)
+{
+ return ata_do_simple_cmd(ap, dev, ATA_CMD_IDLEIMMEDIATE);
+}
+
+/**
+ * ata_device_resume - wakeup a previously suspended devices
+ *
+ * Kick the drive back into action, by sending it an idle immediate
+ * command and making sure its transfer mode matches between drive
+ * and host.
+ *
+ */
+int ata_device_resume(struct ata_port *ap, struct ata_device *dev)
+{
+ if (ap->flags & ATA_FLAG_SUSPENDED) {
+ ap->flags &= ~ATA_FLAG_SUSPENDED;
+ ata_set_mode(ap);
+ }
+ if (!ata_dev_present(dev))
+ return 0;
+ if (dev->class == ATA_DEV_ATA)
+ ata_start_drive(ap, dev);
+
+ return 0;
+}
+
+/**
+ * ata_device_suspend - prepare a device for suspend
+ *
+ * Flush the cache on the drive, if appropriate, then issue a
+ * standbynow command.
+ *
+ */
+int ata_device_suspend(struct ata_port *ap, struct ata_device *dev)
+{
+ if (!ata_dev_present(dev))
+ return 0;
+ if (dev->class == ATA_DEV_ATA)
+ ata_flush_cache(ap, dev);
+
+ ata_standby_drive(ap, dev);
+ ap->flags |= ATA_FLAG_SUSPENDED;
+ return 0;
+}
+
int ata_port_start (struct ata_port *ap)
{
struct device *dev = ap->host_set->dev;
@@ -3937,6 +4035,23 @@
return (tmp == bits->val) ? 1 : 0;
}
+
+int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+ return 0;
+}
+
+int ata_pci_device_resume(struct pci_dev *pdev)
+{
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ pci_enable_device(pdev);
+ pci_set_master(pdev);
+ return 0;
+}
#endif /* CONFIG_PCI */
@@ -4021,4 +4136,11 @@
EXPORT_SYMBOL_GPL(ata_pci_init_native_mode);
EXPORT_SYMBOL_GPL(ata_pci_init_one);
EXPORT_SYMBOL_GPL(ata_pci_remove_one);
+EXPORT_SYMBOL_GPL(ata_pci_device_suspend);
+EXPORT_SYMBOL_GPL(ata_pci_device_resume);
#endif /* CONFIG_PCI */
+
+EXPORT_SYMBOL_GPL(ata_device_suspend);
+EXPORT_SYMBOL_GPL(ata_device_resume);
+EXPORT_SYMBOL_GPL(ata_scsi_device_suspend);
+EXPORT_SYMBOL_GPL(ata_scsi_device_resume);
Index: drivers/scsi/libata-scsi.c
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/drivers/scsi/libata-scsi.c (mode:100644 sha1:4c96df060c3bad9af44ab4c4664d8cba38c63030)
+++ uncommitted/drivers/scsi/libata-scsi.c (mode:100644)
@@ -387,6 +387,22 @@
return 0;
}
+int ata_scsi_device_resume(struct scsi_device *sdev)
+{
+ struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0];
+ struct ata_device *dev = &ap->device[sdev->id];
+
+ return ata_device_resume(ap, dev);
+}
+
+int ata_scsi_device_suspend(struct scsi_device *sdev)
+{
+ struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0];
+ struct ata_device *dev = &ap->device[sdev->id];
+
+ return ata_device_suspend(ap, dev);
+}
+
/**
* ata_scsi_flush_xlat - Translate SCSI SYNCHRONIZE CACHE command
* @qc: Storage for translated ATA taskfile
Index: drivers/scsi/sata_nv.c
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/drivers/scsi/sata_nv.c (mode:100644 sha1:69009f853a4917f54a88aa0c2cf3e6fb0428c8f7)
+++ uncommitted/drivers/scsi/sata_nv.c (mode:100644)
@@ -187,6 +187,8 @@
.id_table = nv_pci_tbl,
.probe = nv_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
static Scsi_Host_Template nv_sht = {
@@ -207,6 +209,8 @@
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
.ordered_flush = 1,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations nv_ops = {
Index: drivers/scsi/sata_promise.c
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/drivers/scsi/sata_promise.c (mode:100644 sha1:19a13e3590f4eca5fab24675ffec12aaa56dd57f)
+++ uncommitted/drivers/scsi/sata_promise.c (mode:100644)
@@ -103,6 +103,8 @@
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
.ordered_flush = 1,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations pdc_ata_ops = {
@@ -178,6 +180,8 @@
.id_table = pdc_ata_pci_tbl,
.probe = pdc_ata_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
Index: drivers/scsi/sata_qstor.c
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/drivers/scsi/sata_qstor.c (mode:100644 sha1:dfd3621047171dc748ba9a11c82a418f89faaed6)
+++ uncommitted/drivers/scsi/sata_qstor.c (mode:100644)
@@ -140,6 +140,8 @@
.dma_boundary = QS_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations qs_ata_ops = {
@@ -191,6 +193,8 @@
.id_table = qs_ata_pci_tbl,
.probe = qs_ata_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
static int qs_check_atapi_dma(struct ata_queued_cmd *qc)
Index: drivers/scsi/sata_sil.c
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/drivers/scsi/sata_sil.c (mode:100644 sha1:f0489dc302a03054d6461ba54c9705f30190a8f4)
+++ uncommitted/drivers/scsi/sata_sil.c (mode:100644)
@@ -115,6 +115,8 @@
.id_table = sil_pci_tbl,
.probe = sil_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
static Scsi_Host_Template sil_sht = {
@@ -135,6 +137,8 @@
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
.ordered_flush = 1,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations sil_ops = {
Index: drivers/scsi/sata_sis.c
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/drivers/scsi/sata_sis.c (mode:100644 sha1:5105ddd08447807df3710492f70c31bf6f0d8767)
+++ uncommitted/drivers/scsi/sata_sis.c (mode:100644)
@@ -71,6 +71,8 @@
.id_table = sis_pci_tbl,
.probe = sis_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
static Scsi_Host_Template sis_sht = {
@@ -91,6 +93,8 @@
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
.ordered_flush = 1,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations sis_ops = {
Index: drivers/scsi/sata_svw.c
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/drivers/scsi/sata_svw.c (mode:100644 sha1:8d1a5d25c05364e41e7f98198b94335ed30d0046)
+++ uncommitted/drivers/scsi/sata_svw.c (mode:100644)
@@ -289,6 +289,8 @@
#endif
.bios_param = ata_std_bios_param,
.ordered_flush = 1,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
@@ -458,6 +460,8 @@
.id_table = k2_sata_pci_tbl,
.probe = k2_sata_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
Index: drivers/scsi/sata_sx4.c
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/drivers/scsi/sata_sx4.c (mode:100644 sha1:70118650c461e9d5d10ff0c3053ae5b49c6755a0)
+++ uncommitted/drivers/scsi/sata_sx4.c (mode:100644)
@@ -189,6 +189,8 @@
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
.ordered_flush = 1,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations pdc_20621_ops = {
@@ -235,6 +237,8 @@
.id_table = pdc_sata_pci_tbl,
.probe = pdc_sata_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
Index: drivers/scsi/sata_uli.c
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/drivers/scsi/sata_uli.c (mode:100644 sha1:0bff4f475f262c5c40254aecf85bd6a7137bd04e)
+++ uncommitted/drivers/scsi/sata_uli.c (mode:100644)
@@ -63,6 +63,8 @@
.id_table = uli_pci_tbl,
.probe = uli_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
static Scsi_Host_Template uli_sht = {
@@ -83,6 +85,8 @@
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
.ordered_flush = 1,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations uli_ops = {
Index: drivers/scsi/sata_via.c
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/drivers/scsi/sata_via.c (mode:100644 sha1:3a7830667277f35f71a83db5705b8cb2d376bafc)
+++ uncommitted/drivers/scsi/sata_via.c (mode:100644)
@@ -83,6 +83,8 @@
.id_table = svia_pci_tbl,
.probe = svia_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
static Scsi_Host_Template svia_sht = {
@@ -103,6 +105,8 @@
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
.ordered_flush = 1,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static struct ata_port_operations svia_sata_ops = {
Index: drivers/scsi/sata_vsc.c
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/drivers/scsi/sata_vsc.c (mode:100644 sha1:2c28f0ad73c204420aa4b419a60341a8b801d7fa)
+++ uncommitted/drivers/scsi/sata_vsc.c (mode:100644)
@@ -206,6 +206,8 @@
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
.ordered_flush = 1,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
@@ -382,6 +384,8 @@
.id_table = vsc_sata_pci_tbl,
.probe = vsc_sata_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
};
Index: drivers/scsi/scsi_lib.c
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/drivers/scsi/scsi_lib.c (mode:100644 sha1:d18da21c9c57e0ef590369ea815b7b1adba4042b)
+++ uncommitted/drivers/scsi/scsi_lib.c (mode:100644)
@@ -1855,8 +1855,9 @@
void
scsi_device_resume(struct scsi_device *sdev)
{
- if(scsi_device_set_state(sdev, SDEV_RUNNING))
+ if (scsi_device_set_state(sdev, SDEV_RUNNING))
return;
+
scsi_run_queue(sdev->request_queue);
}
EXPORT_SYMBOL(scsi_device_resume);
Index: drivers/scsi/scsi_sysfs.c
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/drivers/scsi/scsi_sysfs.c (mode:100644 sha1:e75ee4671ee3a0a6dcb608a73d6bd2f4007eff09)
+++ uncommitted/drivers/scsi/scsi_sysfs.c (mode:100644)
@@ -199,9 +199,40 @@
return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0;
}
+static int scsi_bus_suspend(struct device * dev, pm_message_t state)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_host_template *sht = sdev->host->hostt;
+ int err;
+
+ err = scsi_device_quiesce(sdev);
+ if (err)
+ return err;
+
+ if (sht->suspend)
+ err = sht->suspend(sdev);
+
+ return err;
+}
+
+static int scsi_bus_resume(struct device * dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_host_template *sht = sdev->host->hostt;
+ int err = 0;
+
+ if (sht->resume)
+ err = sht->resume(sdev);
+
+ scsi_device_resume(sdev);
+ return err;
+}
+
struct bus_type scsi_bus_type = {
.name = "scsi",
.match = scsi_bus_match,
+ .suspend = scsi_bus_suspend,
+ .resume = scsi_bus_resume,
};
int scsi_sysfs_register(void)
Index: include/linux/ata.h
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/include/linux/ata.h (mode:100644 sha1:f178894edd04f954a4f650661175b5fe009011c6)
+++ uncommitted/include/linux/ata.h (mode:100644)
@@ -125,6 +125,8 @@
ATA_CMD_PACKET = 0xA0,
ATA_CMD_VERIFY = 0x40,
ATA_CMD_VERIFY_EXT = 0x42,
+ ATA_CMD_STANDBYNOW1 = 0xE0,
+ ATA_CMD_IDLEIMMEDIATE = 0xE1,
/* SETFEATURES stuff */
SETFEATURES_XFER = 0x03,
Index: include/linux/libata.h
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/include/linux/libata.h (mode:100644 sha1:505160ab472b0198ec5393380ce7c21b2fad85d1)
+++ uncommitted/include/linux/libata.h (mode:100644)
@@ -113,6 +113,7 @@
ATA_FLAG_MMIO = (1 << 6), /* use MMIO, not PIO */
ATA_FLAG_SATA_RESET = (1 << 7), /* use COMRESET */
ATA_FLAG_PIO_DMA = (1 << 8), /* PIO cmds via DMA */
+ ATA_FLAG_SUSPENDED = (1 << 9), /* port is suspended */
ATA_QCFLAG_ACTIVE = (1 << 1), /* cmd not yet ack'd to scsi lyer */
ATA_QCFLAG_SG = (1 << 3), /* have s/g table? */
@@ -387,6 +388,8 @@
extern int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
unsigned int n_ports);
extern void ata_pci_remove_one (struct pci_dev *pdev);
+extern int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state);
+extern int ata_pci_device_resume(struct pci_dev *pdev);
#endif /* CONFIG_PCI */
extern int ata_device_add(struct ata_probe_ent *ent);
extern int ata_scsi_detect(Scsi_Host_Template *sht);
@@ -395,6 +398,10 @@
extern int ata_scsi_error(struct Scsi_Host *host);
extern int ata_scsi_release(struct Scsi_Host *host);
extern unsigned int ata_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
+extern int ata_scsi_device_resume(struct scsi_device *);
+extern int ata_scsi_device_suspend(struct scsi_device *);
+extern int ata_device_resume(struct ata_port *, struct ata_device *);
+extern int ata_device_suspend(struct ata_port *, struct ata_device *);
/*
* Default driver ops implementations
*/
Index: include/scsi/scsi_host.h
===================================================================
--- b023d524fb0a3b71aa0b957ce7c5540611497370/include/scsi/scsi_host.h (mode:100644 sha1:1cee1e100943dafe00c14d5329b73659062a4d1a)
+++ uncommitted/include/scsi/scsi_host.h (mode:100644)
@@ -270,6 +270,12 @@
int (*proc_info)(struct Scsi_Host *, char *, char **, off_t, int, int);
/*
+ * suspend support
+ */
+ int (*resume)(struct scsi_device *);
+ int (*suspend)(struct scsi_device *);
+
+ /*
* Name of proc directory
*/
char *proc_name;
--
Jens Axboe
On Tue, 2005-05-03 at 16:10 +0200, Jens Axboe wrote:
> I don't know, depends on what Jeff/James think of this approach. There
> are many different way to solve this problem. I let the scsi bus called
> suspend/resume for the devices on that bus, and let the scsi host
> adapter perform any device dependent actions. The pci helpers are less
> debatable.
>
> Jeff/James? Here's a patch that applies to current git.
The patch looks fine as far as it goes ... however, shouldn't we be
spinning *internal* suspended drives down as well like IDE does (i.e. at
least the sd ULD needs to be a party to the suspend)? Of course this is
a complete can of worms since we really have no idea which busses are
internal and which are external, although it might be something that
userland can determine.
James
P.S. I noticed the gratuitous coding style corrections ...
On May 8, 2005, at 5:53 AM, James Bottomley wrote:
> On Tue, 2005-05-03 at 16:10 +0200, Jens Axboe wrote:
>
>> I don't know, depends on what Jeff/James think of this approach.
>> There
>> are many different way to solve this problem. I let the scsi bus
>> called
>> suspend/resume for the devices on that bus, and let the scsi host
>> adapter perform any device dependent actions. The pci helpers are
>> less
>> debatable.
>>
>> Jeff/James? Here's a patch that applies to current git.
>>
>
> The patch looks fine as far as it goes ... however, shouldn't we be
> spinning *internal* suspended drives down as well like IDE does
> (i.e. at
> least the sd ULD needs to be a party to the suspend)? Of course
> this is
> a complete can of worms since we really have no idea which busses are
> internal and which are external, although it might be something that
> userland can determine.
I'm not sure I know what you mean by 'internal suspended drives' that
aren't spun down? For every device known on the sata "bus", we do the
standby routine.
There is room for improvement for software suspend, notably it is
extremely annoying that we cannot tell the difference between
'freeze' and 'suspend' currently, this adds overhead for suspend-to-
disk both in time spent and actual drive wear due to an excessive
spin down+up cycle.
> P.S. I noticed the gratuitous coding style corrections ...
Heh woops, I usually don't sneak those in with other changes. I think
this one got in because I actually had another change there that I
later reverted.
Jens
On Sun, 2005-05-08 at 18:21 +0200, Jens Axboe wrote:
> On May 8, 2005, at 5:53 AM, James Bottomley wrote:
> > The patch looks fine as far as it goes ... however, shouldn't we be
> > spinning *internal* suspended drives down as well like IDE does
> > (i.e. at
> > least the sd ULD needs to be a party to the suspend)? Of course
> > this is
> > a complete can of worms since we really have no idea which busses are
> > internal and which are external, although it might be something that
> > userland can determine.
>
> I'm not sure I know what you mean by 'internal suspended drives' that
> aren't spun down? For every device known on the sata "bus", we do the
> standby routine.
I mean that at the moment, the suspend code doesn't seem to spin down
any SCSI drives. However, if it did we'd need to distinguish between
drives that uniquely belong to us (i.e. drives that are internal to the
system) and drives that are external on a shared bus for which we'd
annoy other systems if we spun them down.
> There is room for improvement for software suspend, notably it is
> extremely annoying that we cannot tell the difference between
> 'freeze' and 'suspend' currently, this adds overhead for suspend-to-
> disk both in time spent and actual drive wear due to an excessive
> spin down+up cycle.
>
> > P.S. I noticed the gratuitous coding style corrections ...
>
> Heh woops, I usually don't sneak those in with other changes. I think
> this one got in because I actually had another change there that I
> later reverted.
;-)
James
Hi!
> > I don't know, depends on what Jeff/James think of this approach. There
> > are many different way to solve this problem. I let the scsi bus called
> > suspend/resume for the devices on that bus, and let the scsi host
> > adapter perform any device dependent actions. The pci helpers are less
> > debatable.
> >
> > Jeff/James? Here's a patch that applies to current git.
>
> The patch looks fine as far as it goes ... however, shouldn't we be
> spinning *internal* suspended drives down as well like IDE does (i.e. at
> least the sd ULD needs to be a party to the suspend)? Of course this is
In IDE we do that to reliably flush drive caches... If write caching
actually works on SCSI, we should not need that hacks.
Pavel
--
Boycott Kodak -- for their patent abuse against Java.
On Mon, 2005-05-09 at 12:13 +0200, Pavel Machek wrote:
> In IDE we do that to reliably flush drive caches... If write caching
> actually works on SCSI, we should not need that hacks.
Define "works". Actual write caching works fine with IDE (it doesn't
lose the data). On the other hand, turning the cache off or flushing it
can be problematic because not all IDE devices respond to these
commands.
So, what I think you're saying is that you don't want the internal
drives to spin down, but you do want to send a synchronize cache command
to those which have a writeback cache enabled?
James
On Po 09-05-05 09:07:21, James Bottomley wrote:
> On Mon, 2005-05-09 at 12:13 +0200, Pavel Machek wrote:
> > In IDE we do that to reliably flush drive caches... If write caching
> > actually works on SCSI, we should not need that hacks.
>
> Define "works". Actual write caching works fine with IDE (it doesn't
> lose the data). On the other hand, turning the cache off or flushing it
> can be problematic because not all IDE devices respond to these
> commands.
>
> So, what I think you're saying is that you don't want the internal
> drives to spin down, but you do want to send a synchronize cache command
> to those which have a writeback cache enabled?
Yes, something like that. I don't care if drivers are spinning or not,
but I need caches to be properly flushed and drive ready for system
powerdown.
Pavel
--
Boycott Kodak -- for their patent abuse against Java.