From: Randy Dunlap <randy_d_dunlap@linux.intel.com>
To: linux-scsi@vger.kernel.org
Cc: linux-ide@vger.kernel.org, axboe@suse.de, jgarzik@pobox.com, hch@lst.de
Subject: [PATCH/RFC] SCSI mid-layer suspend/resume
Date: Mon, 24 Oct 2005 16:05:31 -0700 [thread overview]
Message-ID: <20051024160531.7d879a7c.randy_d_dunlap@linux.intel.com> (raw)
RFC.
Although Jens's SATA suspend/resume patch works, it seems that the
SCSI maintainer(s) want something different, e.g., Nathan Bryant's
2004 patch[1] with some changes:
Jeff's comments[2]:
- Use START STOP UNIT on suspend & resume (done);
- Issue hardware or software reset on suspend (not done).
Christoph's comments[3][4]:
- some functions should be renamed (done, maybe :);
- some (same) functions should be in scsi_sysfs.c (done);
- drop the CONFIG_PM ifdefs (done);
- way to avoid spinning down (or generically: suspending) external
disks, such as a sysfs-controllable flag (done at device level);
TBD:
James Smart's comment about limiting number of drive spin-ups[5];
Stefan Richter's comment about which driver decides to support or
implement suspend/resume[6];
Mark Lord: where is the GFP_KERNEL usage that you were concerned
about on resume? [7]
BTW, what does this comment in scsi_sysfs.c refer to when I am
adding an attribute to it?
/* Default template for device attributes. May NOT be modified */
static struct device_attribute *scsi_sysfs_sdev_attrs[] = {
---
[1] http://lwn.net/Articles/97453/
[2] http://marc.theaimsgroup.com/?l=linux-scsi&m=112797130926411&w=2
[3] http://marc.theaimsgroup.com/?l=linux-scsi&m=112797930421645&w=2
[4] http://marc.theaimsgroup.com/?l=linux-scsi&m=112798153124878&w=2
[5] http://marc.theaimsgroup.com/?l=linux-scsi&m=112800404305403&w=2
[6] http://marc.theaimsgroup.com/?l=linux-scsi&m=112800966123767&w=2
[7] http://marc.theaimsgroup.com/?l=linux-scsi&m=112819108407925&w=2
---
From: Randy Dunlap <randy_d_dunlap@linux.intel.com>
Update Nathan Bryant's SCSI midlayer suspend/resume patch per
email suggestions from Jeff Garzik and Christoph Hellwig.
"suspend" is a device-level flag that controls whether a
device should participate in suspend operations. Then it's
up to each device driver to determine what that means for
a device.
Signed-off-by: Randy Dunlap <randy_d_dunlap@linux.intel.com>
---
drivers/scsi/scsi_priv.h | 2
drivers/scsi/scsi_sysfs.c | 63 +++++++++++++++++++++++++-
drivers/scsi/sd.c | 82 ++++++++++++++++++++++++++++++++++-
include/scsi/scsi_device.h | 1
include/scsi/scsi_driver.h | 2
5 files changed, 146 insertions(+), 4 deletions(-)
diff -Naurp -X /home/rddunlap/doc/dontdiff linux-2614-rc4-pv/drivers/scsi/scsi_priv.h linux-2614-rc4-scsi/drivers/scsi/scsi_priv.h
--- linux-2614-rc4-pv/drivers/scsi/scsi_priv.h 2005-10-14 09:40:15.000000000 -0700
+++ linux-2614-rc4-scsi/drivers/scsi/scsi_priv.h 2005-10-14 09:38:33.000000000 -0700
@@ -55,6 +55,8 @@ static inline void scsi_log_send(struct
static inline void scsi_log_completion(struct scsi_cmnd *cmd, int disposition)
{ };
#endif
+extern int scsi_device_suspend(struct device *dev, pm_message_t state);
+extern int scsi_device_wakeup(struct device *dev);
/* scsi_devinfo.c */
extern int scsi_get_device_flags(struct scsi_device *sdev,
diff -Naurp -X /home/rddunlap/doc/dontdiff linux-2614-rc4-pv/drivers/scsi/scsi_sysfs.c linux-2614-rc4-scsi/drivers/scsi/scsi_sysfs.c
--- linux-2614-rc4-pv/drivers/scsi/scsi_sysfs.c 2005-10-14 09:40:15.000000000 -0700
+++ linux-2614-rc4-scsi/drivers/scsi/scsi_sysfs.c 2005-10-19 16:59:10.000000000 -0700
@@ -14,6 +14,7 @@
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
+#include <scsi/scsi_driver.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport.h>
@@ -264,8 +265,10 @@ static int scsi_bus_match(struct device
}
struct bus_type scsi_bus_type = {
- .name = "scsi",
- .match = scsi_bus_match,
+ .name = "scsi",
+ .match = scsi_bus_match,
+ .suspend = scsi_device_suspend,
+ .resume = scsi_device_wakeup,
};
int scsi_sysfs_register(void)
@@ -401,6 +404,28 @@ sdev_store_timeout (struct device *dev,
static DEVICE_ATTR(timeout, S_IRUGO | S_IWUSR, sdev_show_timeout, sdev_store_timeout);
static ssize_t
+sdev_show_suspend(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+
+ return snprintf (buf, 5, "%d\n", sdev->suspend);
+}
+
+static ssize_t
+sdev_store_suspend(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ unsigned char suspend;
+
+ sscanf(buf, "%hhd\n", &suspend);
+ sdev->suspend = !!suspend;
+ return count;
+}
+static DEVICE_ATTR(suspend, S_IRUGO | S_IWUSR,
+ sdev_show_suspend, sdev_store_suspend);
+
+static ssize_t
store_rescan_field (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
scsi_rescan_device(dev);
@@ -506,6 +531,7 @@ static struct device_attribute *scsi_sys
&dev_attr_delete,
&dev_attr_state,
&dev_attr_timeout,
+ &dev_attr_suspend,
&dev_attr_iocounterbits,
&dev_attr_iorequest_cnt,
&dev_attr_iodone_cnt,
@@ -584,7 +610,6 @@ static struct device_attribute *attr_cha
return attr;
}
-
static struct device_attribute *attr_overridden(
struct device_attribute **attrs,
struct device_attribute *attr)
@@ -770,6 +795,38 @@ void scsi_remove_target(struct device *d
}
EXPORT_SYMBOL(scsi_remove_target);
+int scsi_device_suspend(struct device *dev, pm_message_t state)
+{
+ int err;
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_driver *drv = to_scsi_driver(dev->driver);
+
+ err = scsi_device_quiesce(sdev);
+ if (err)
+ return err;
+
+ if (drv->suspend)
+ return drv->suspend(dev, state);
+
+ return 0;
+}
+
+int scsi_device_wakeup(struct device *dev)
+{
+ int err;
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_driver *drv = to_scsi_driver(dev->driver);
+
+ if (drv->resume) {
+ err = drv->resume(dev);
+ if (err)
+ return err;
+ }
+
+ scsi_device_resume(sdev);
+ return 0;
+}
+
int scsi_register_driver(struct device_driver *drv)
{
drv->bus = &scsi_bus_type;
diff -Naurp -X /home/rddunlap/doc/dontdiff linux-2614-rc4-pv/drivers/scsi/sd.c linux-2614-rc4-scsi/drivers/scsi/sd.c
--- linux-2614-rc4-pv/drivers/scsi/sd.c 2005-10-14 09:40:15.000000000 -0700
+++ linux-2614-rc4-scsi/drivers/scsi/sd.c 2005-10-14 09:38:33.000000000 -0700
@@ -117,6 +117,8 @@ static void sd_rw_intr(struct scsi_cmnd
static int sd_probe(struct device *);
static int sd_remove(struct device *);
+static int sd_suspend(struct device *, pm_message_t);
+static int sd_resume(struct device *);
static void sd_shutdown(struct device *dev);
static void sd_rescan(struct device *);
static int sd_init_command(struct scsi_cmnd *);
@@ -136,6 +138,8 @@ static struct scsi_driver sd_template =
},
.rescan = sd_rescan,
.init_command = sd_init_command,
+ .suspend = sd_suspend,
+ .resume = sd_resume,
.issue_flush = sd_issue_flush,
.prepare_flush = sd_prepare_flush,
.end_flush = sd_end_flush,
@@ -1691,7 +1695,83 @@ static void sd_shutdown(struct device *d
printk(KERN_NOTICE "Synchronizing SCSI cache for disk %s: \n",
sdkp->disk->disk_name);
sd_sync_cache(sdp);
-}
+}
+
+static int sd_suspend(struct device *dev, pm_message_t state)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_disk *sdkp = dev_get_drvdata(dev);
+ int retries, res;
+ struct scsi_sense_hdr sshdr;
+ unsigned char cmd[10] = { 0 };
+
+ sd_shutdown(dev);
+
+ /* stop the drive */
+ for (retries = 3; retries > 0; --retries) {
+ cmd[0] = START_STOP;
+ cmd[4] = 0;
+ res = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, &sshdr,
+ SD_TIMEOUT, SD_MAX_RETRIES);
+ if (res == 0)
+ break;
+ }
+
+ if (res) {
+ printk(KERN_WARNING "STOP UNIT FAILED\n"
+ " status = %x, message = %02x, "
+ "host = %d, driver = %02x\n ",
+ status_byte(res), msg_byte(res),
+ host_byte(res), driver_byte(res));
+ if (driver_byte(res) & DRIVER_SENSE)
+ scsi_print_sense_hdr("sd", &sshdr);
+ else
+ printk("%s: sense not available.\n",
+ sdkp->disk->disk_name);
+ /* TBD: is this a suspend error? */
+ }
+
+ /* TBD: do hardware or software reset on suspend */
+
+ return 0;
+}
+
+static int sd_resume(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_disk *sdkp = dev_get_drvdata(dev);
+ int retries, res;
+ struct scsi_sense_hdr sshdr;
+ unsigned char cmd[10] = { 0 };
+
+ sd_rescan(dev);
+
+ /* start the drive */
+ for (retries = 3; retries > 0; --retries) {
+ cmd[0] = START_STOP;
+ cmd[4] = 1;
+ res = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, &sshdr,
+ SD_TIMEOUT, SD_MAX_RETRIES);
+ if (res == 0)
+ break;
+ }
+
+ if (res) {
+ printk(KERN_WARNING "START UNIT FAILED\n"
+ " status = %x, message = %02x, "
+ "host = %d, driver = %02x\n ",
+ status_byte(res), msg_byte(res),
+ host_byte(res), driver_byte(res));
+ if (driver_byte(res) & DRIVER_SENSE)
+ scsi_print_sense_hdr("sd", &sshdr);
+ else
+ printk("%s: sense not available.\n",
+ sdkp->disk->disk_name);
+ /* TBD: is this a resume error? */
+ }
+
+ return 0;
+}
/**
* init_sd - entry point for this driver (both when built in or when
diff -Naurp -X /home/rddunlap/doc/dontdiff linux-2614-rc4-pv/include/scsi/scsi_device.h linux-2614-rc4-scsi/include/scsi/scsi_device.h
--- linux-2614-rc4-pv/include/scsi/scsi_device.h 2005-10-14 09:40:16.000000000 -0700
+++ linux-2614-rc4-scsi/include/scsi/scsi_device.h 2005-10-19 16:24:57.000000000 -0700
@@ -83,6 +83,7 @@ struct scsi_device {
char * model; /* ... after scan; point to static string */
char * rev; /* ... "nullnullnullnull" before scan */
unsigned char current_tag; /* current tag */
+ unsigned char suspend; /* ok to participate in suspend/resume */
struct scsi_target *sdev_target; /* used only for single_lun */
unsigned int sdev_bflags; /* black/white flags as also found in
diff -Naurp -X /home/rddunlap/doc/dontdiff linux-2614-rc4-pv/include/scsi/scsi_driver.h linux-2614-rc4-scsi/include/scsi/scsi_driver.h
--- linux-2614-rc4-pv/include/scsi/scsi_driver.h 2005-08-28 16:41:01.000000000 -0700
+++ linux-2614-rc4-scsi/include/scsi/scsi_driver.h 2005-10-14 09:38:33.000000000 -0700
@@ -13,6 +13,8 @@ struct scsi_driver {
int (*init_command)(struct scsi_cmnd *);
void (*rescan)(struct device *);
+ int (*suspend)(struct device *dev, pm_message_t state);
+ int (*resume)(struct device *dev);
int (*issue_flush)(struct device *, sector_t *);
int (*prepare_flush)(struct request_queue *, struct request *);
void (*end_flush)(struct request_queue *, struct request *);
next reply other threads:[~2005-10-24 23:03 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-10-24 23:05 Randy Dunlap [this message]
2005-10-24 23:29 ` [PATCH/RFC] SCSI mid-layer suspend/resume Jeff Garzik
2005-10-25 21:25 ` Randy Dunlap
2005-10-25 21:37 ` Jeff Garzik
2005-10-27 3:35 ` Mark Lord
2005-10-27 9:17 ` Erik Slagter
2005-10-27 12:51 ` Mark Lord
2005-10-27 12:56 ` Mark Lord
2005-10-27 15:07 ` Erik Slagter
2005-10-26 4:26 ` Stefan Richter
2005-10-25 2:55 ` Mark Lord
2005-10-25 3:08 ` Mark Lord
2005-10-25 21:22 ` Randy Dunlap
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20051024160531.7d879a7c.randy_d_dunlap@linux.intel.com \
--to=randy_d_dunlap@linux.intel.com \
--cc=axboe@suse.de \
--cc=hch@lst.de \
--cc=jgarzik@pobox.com \
--cc=linux-ide@vger.kernel.org \
--cc=linux-scsi@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).