linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Derek Basehore <dbasehore@chromium.org>
To: JBottomley@parallels.com
Cc: jgarzik@pobox.com, linux-ide@vger.kernel.org,
	linux-kernel@vger.kernel.org,
	Derek Basehore <dbasehore@chromium.org>
Subject: [PATCH 1/2] don't wait on disk to start on resume
Date: Thu, 20 Dec 2012 20:35:39 -0800	[thread overview]
Message-ID: <1356064540-17414-1-git-send-email-dbasehore@chromium.org> (raw)

We no longer wait for the disk to spin up in sd_resume. It now enters the
request to spinup the disk into the elevator and returns.

A function is scheduled under the scsi_sd_probe_domain to wait for the command
to spinup the disk to complete. This function then checks for errors and cleans
up after the sd resume function.

This allows system resume to complete much faster on systems with an HDD since
spinning up the disk is a significant portion of resume time.

Signed-off-by: Derek Basehore <dbasehore@chromium.org>
---
 drivers/scsi/sd.c |  106 +++++++++++++++++++++++++++++++++++++++++++++++++---
 drivers/scsi/sd.h |    9 ++++
 2 files changed, 108 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 7992635..2fe051f 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -3045,6 +3045,7 @@ static void sd_shutdown(struct device *dev)
 	if (!sdkp)
 		return;         /* this can happen */
 
+	async_synchronize_full_domain(&scsi_sd_probe_domain);
 	if (pm_runtime_suspended(dev))
 		goto exit;
 
@@ -3070,6 +3071,7 @@ static int sd_suspend(struct device *dev)
 	if (!sdkp)
 		return 0;	/* this can happen */
 
+	async_synchronize_full_domain(&scsi_sd_probe_domain);
 	if (sdkp->WCE) {
 		sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n");
 		ret = sd_sync_cache(sdkp);
@@ -3087,20 +3089,110 @@ done:
 	return ret;
 }
 
+/*
+ * Callback function for when the request that starts the disk completes. Passed
+ * into blk_execute_rq_nowait in sd_resume.
+ */
+static void sd_end_start_rq(struct request *rq, int error)
+{
+	struct completion *waiting = rq->end_io_data;
+
+	rq->end_io_data = NULL;
+	/*
+	 * Set the end_io_data to NULL before calling complete. This (with
+	 * sd_resume async) ensures that it is set to NULL before we call
+	 * blk_put_request.
+	 */
+	complete(waiting);
+}
+
+/*
+ * Asynchronous part of sd_resume. This is separate from sd_end_start_rq since
+ * we need to block on resume for suspend and shutdown. We already do this by
+ * synchronizing on the scsi_sd_probe_domain, so use that.
+ */
+static void sd_resume_async(void *data, async_cookie_t cookie)
+{
+	struct scsi_sense_hdr sshdr;
+	struct resume_async_struct *resume = data;
+	struct scsi_disk *sdkp = resume->sdkp;
+	struct request *req = resume->req;
+
+	wait_for_completion(&resume->wait);
+
+	if (req->errors) {
+		scsi_normalize_sense(resume->sense,
+				     SCSI_SENSE_BUFFERSIZE,
+				     &sshdr);
+		sd_printk(KERN_WARNING, sdkp, "START_STOP FAILED\n");
+		sd_print_result(sdkp, req->errors);
+		if (driver_byte(req->errors) & DRIVER_SENSE)
+			sd_print_sense_hdr(sdkp, &sshdr);
+	}
+	kfree(resume);
+	scsi_disk_put(sdkp);
+	blk_put_request(req);
+}
+
+/*
+ * This does not wait for the disk to spin up. This function enters the request
+ * to spinup the disk and schedules a function, sd_resume_async, that waits for
+ * that request to complete.
+ */
 static int sd_resume(struct device *dev)
 {
 	struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev);
-	int ret = 0;
+	struct scsi_device *sdp = sdkp->device;
+	struct resume_async_struct *resume = NULL;
+	struct request *req;
+	unsigned char cmd[6] = { START_STOP };	/* START_VALID */
 
-	if (!sdkp->device->manage_start_stop)
-		goto done;
+	if (!sdkp->device->manage_start_stop) {
+		scsi_disk_put(sdkp);
+		return 0;
+	}
 
 	sd_printk(KERN_NOTICE, sdkp, "Starting disk\n");
-	ret = sd_start_stop_device(sdkp, 1);
 
-done:
-	scsi_disk_put(sdkp);
-	return ret;
+	cmd[4] |= 1;	/* START */
+
+	if (sdp->start_stop_pwr_cond)
+		cmd[4] |= 1 << 4;	/* Active */
+
+	if (!scsi_device_online(sdp))
+		return -ENODEV;
+
+	resume = kzalloc(sizeof(struct resume_async_struct), GFP_NOIO);
+	if (!resume)
+		return DRIVER_ERROR << 24;
+
+	req = blk_get_request(sdp->request_queue, 0, __GFP_WAIT);
+	if (!req)
+		return DRIVER_ERROR << 24;
+
+	resume->req = req;
+	resume->sdkp = sdkp;
+	init_completion(&resume->wait);
+
+	req->cmd_len = COMMAND_SIZE(cmd[0]);
+	memcpy(req->cmd, cmd, req->cmd_len);
+	req->sense = resume->sense;
+	req->sense_len = 0;
+	req->retries = SD_MAX_RETRIES;
+	req->timeout = SD_TIMEOUT;
+	req->cmd_type = REQ_TYPE_BLOCK_PC;
+	req->cmd_flags |= REQ_QUIET | REQ_PREEMPT;
+	req->end_io_data = &resume->wait;
+
+	async_schedule_domain(sd_resume_async, resume, &scsi_sd_probe_domain);
+	/*
+	 * don't wait here for the disk to spin up, since we can resume faster
+	 * if we don't. Cleanup and checking for errors is done the the
+	 * previously scheduled sd_resume_async function
+	 */
+	blk_execute_rq_nowait(req->q, NULL, req, 1, sd_end_start_rq);
+
+	return 0;
 }
 
 /**
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
index 74a1e4c..b09f255 100644
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -1,6 +1,8 @@
 #ifndef _SCSI_DISK_H
 #define _SCSI_DISK_H
 
+#include <scsi/scsi_cmnd.h>
+
 /*
  * More than enough for everybody ;)  The huge number of majors
  * is a leftover from 16bit dev_t days, we don't really need that
@@ -87,6 +89,13 @@ struct scsi_disk {
 };
 #define to_scsi_disk(obj) container_of(obj,struct scsi_disk,dev)
 
+struct resume_async_struct {
+	struct scsi_disk *sdkp;
+	struct request *req;
+	struct completion wait;
+	char sense[SCSI_SENSE_BUFFERSIZE];
+};
+
 static inline struct scsi_disk *scsi_disk(struct gendisk *disk)
 {
 	return container_of(disk->private_data, struct scsi_disk, driver);
-- 
1.7.7.3

             reply	other threads:[~2012-12-21  4:35 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-12-21  4:35 Derek Basehore [this message]
2012-12-21  4:35 ` [PATCH 2/2] ata: don't wait on resume Derek Basehore
2012-12-22 14:32 ` [PATCH 1/2] don't wait on disk to start " Sergei Shtylyov
2013-01-31 22:00   ` dbasehore .
2013-01-31 22:27     ` James Bottomley
2013-01-31 22:32       ` dbasehore .
2012-12-23 11:49 ` James Bottomley
2013-01-31 22:02   ` dbasehore .
2013-01-31 22:26     ` James Bottomley
2013-02-01 11:51 ` Aaron Lu
2013-02-01 15:28   ` Alan Stern
2013-02-02 10:45     ` Aaron Lu
2013-02-02 15:09       ` Alan Stern
2013-02-03  6:23         ` Aaron Lu
2013-02-04  0:07           ` dbasehore .
2013-02-04 14:27             ` Aaron Lu
2013-02-04  8:04           ` Aaron Lu

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=1356064540-17414-1-git-send-email-dbasehore@chromium.org \
    --to=dbasehore@chromium.org \
    --cc=JBottomley@parallels.com \
    --cc=jgarzik@pobox.com \
    --cc=linux-ide@vger.kernel.org \
    --cc=linux-kernel@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).