[PATCH] sd: implement stop_on_shutdown

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



sd doesn't stop (unload head) on shutdown.  This behavior is necessary
for multi initiator cases.  Unloading head by powering off stresses
the drive and sometimes produces distinct clunking noise which
apparently disturbs users considering multiple reports on different
distributions.  halt(8) usually puts the drives to sleep prior to
shutdown but the implementation is fragile and it doesn't work with
sleep-to-disk.

This patch implements sd attribute stop_on_shutdown.  If set to 1, sd
stops the drive on non-restarting shutdown.  stop_on_shutdown is
initialized from sd parameter stop_on_shutdown_default which defaults
to 0.  So, this patch does not change the default behavior.

Signed-off-by: Tejun Heo <htejun@xxxxxxxxx>
Acked-by: Henrique de Moraes Holschuh <hmh@xxxxxxxxxx>

diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 978bfc1..f21e5fe 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -90,6 +90,12 @@ MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK13_MAJOR);
 MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK14_MAJOR);
 MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK15_MAJOR);
 
+static int sd_stop_on_shutdown_dfl = 0;
+module_param_named(stop_on_shutdown_default, sd_stop_on_shutdown_dfl,
+		   bool, 0644);
+MODULE_PARM_DESC(stop_on_shutdown_default, "Default setting for stopping "
+		 "disk on shutdown (0=disable, 1=enable)");
+
 /*
  * This is limited by the naming scheme enforced in sd_probe,
  * add another character to it if you really need more disks.
@@ -126,6 +132,7 @@ struct scsi_disk {
 	unsigned	WCE : 1;	/* state of disk WCE bit */
 	unsigned	RCD : 1;	/* state of disk RCD bit, unused */
 	unsigned	DPOFUA : 1;	/* state of disk DPOFUA bit */
+	unsigned	stop_on_shutdown : 1; /* stop on device shutdown */
 };
 #define to_scsi_disk(obj) container_of(obj,struct scsi_disk,cdev)
 
@@ -207,6 +214,19 @@ static ssize_t sd_store_cache_type(struct class_device *cdev, const char *buf,
 	return count;
 }
 
+static ssize_t sd_store_stop_on_shutdown(struct class_device *cdev,
+					 const char *buf, size_t count)
+{
+	struct scsi_disk *sdkp = to_scsi_disk(cdev);
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	sdkp->stop_on_shutdown = simple_strtoul(buf, NULL, 10);
+
+	return count;
+}
+
 static ssize_t sd_store_allow_restart(struct class_device *cdev, const char *buf,
 				      size_t count)
 {
@@ -239,6 +259,13 @@ static ssize_t sd_show_fua(struct class_device *cdev, char *buf)
 	return snprintf(buf, 20, "%u\n", sdkp->DPOFUA);
 }
 
+static ssize_t sd_show_stop_on_shutdown(struct class_device *cdev, char *buf)
+{
+	struct scsi_disk *sdkp = to_scsi_disk(cdev);
+
+	return snprintf(buf, 20, "%u\n", sdkp->stop_on_shutdown);
+}
+
 static ssize_t sd_show_allow_restart(struct class_device *cdev, char *buf)
 {
 	struct scsi_disk *sdkp = to_scsi_disk(cdev);
@@ -252,6 +279,8 @@ static struct class_device_attribute sd_disk_attrs[] = {
 	__ATTR(FUA, S_IRUGO, sd_show_fua, NULL),
 	__ATTR(allow_restart, S_IRUGO|S_IWUSR, sd_show_allow_restart,
 	       sd_store_allow_restart),
+	__ATTR(stop_on_shutdown, S_IRUGO|S_IWUSR, sd_show_stop_on_shutdown,
+	       sd_store_stop_on_shutdown),
 	__ATTR_NULL,
 };
 
@@ -1662,6 +1691,7 @@ static int sd_probe(struct device *dev)
 	sdkp->disk = gd;
 	sdkp->index = index;
 	sdkp->openers = 0;
+	sdkp->stop_on_shutdown = sd_stop_on_shutdown_dfl;
 
 	if (!sdp->timeout) {
 		if (sdp->type != TYPE_MOD)
@@ -1766,6 +1796,29 @@ static void scsi_disk_release(struct class_device *cdev)
 	kfree(sdkp);
 }
 
+static int sd_stop_device(struct scsi_device *sdp)
+{
+	unsigned char cmd[6] = { START_STOP }; /* START_VALID and START == 0 */
+	struct scsi_sense_hdr sshdr;
+	int res;
+
+	if (!scsi_device_online(sdp))
+		return -ENODEV;
+
+	res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
+			       SD_TIMEOUT, SD_MAX_RETRIES);
+	if (res) {
+		printk(KERN_WARNING "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);
+	}
+
+	return res;
+}
+
 /*
  * Send a SYNCHRONIZE CACHE instruction down to the device through
  * the normal SCSI command structure.  Wait for the command to
@@ -1784,6 +1837,13 @@ static void sd_shutdown(struct device *dev)
 				sdkp->disk->disk_name);
 		sd_sync_cache(sdp);
 	}
+
+	if (system_state != SYSTEM_RESTART && sdkp->stop_on_shutdown) {
+ 		printk(KERN_NOTICE "Stopping disk %s: \n",
+		       sdkp->disk->disk_name);
+		sd_stop_device(sdp);
+	}
+
 	scsi_disk_put(sdkp);
 }
 
-
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Filesystems]     [Linux SCSI]     [Linux RAID]     [Git]     [Kernel Newbies]     [Linux Newbie]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Samba]     [Device Mapper]

  Powered by Linux