PATCH: Forcible delaying of UFS (soft)updates

Ian Dowse iedowse at maths.tcd.ie
Sat Apr 12 13:02:19 PDT 2003


In message <3E976EBD.C3E66EF8 at tel.fer.hr>, Marko Zec writes:
>Here's a patch against 4.8-RELEASE kernel that allows disk writes on
>softupdates-enabled filesystems to be delayed for (theoretically)
>arbitrarily long periods of time. The motivation for such updating
>policy is surprisingly not purely suicidal - it can allow disks on
>laptops to spin down immediately after I/O operations and stay idle for
>longer periods of time, thus saving considerable amount of battery
>power.

Looks interesting. A while ago I was reading the spec of some IBM
ATA hard disk, and discovered that there is a "delayed write" feature
built into most ATA disks that is extremely useful for keeping a
laptop disk spun down.

When the feature is enabled, the disk behaves normally until it
spins down due to the standard ATA spindown timeout. Then it enters
the delayed write mode, and all further writes to the disk go just
to the disk cache and the disk is not spun up. Finally, when for
any reason the disk needs to be spun up (cache is full, or a read
of an uncached sector occurs), the cache is flushed as soon as the
disk spins up. Assuming this is was happens (it's mostly based on
observation rather than documentation), you get a much smaller
window where the disk is potentially inconsistent, and automatic
triggering of the writes only when they are necessary.

Below is simple script I use to turn on the feature when running
on battery power (using ACPI), and the -CURRENT patches that allow
the spindown delay and delayed write features to be controlled with
atacontrol (I mailed the patches to Soren a while ago).

Ian


#!/bin/sh

oacline=""
while :; do
	sleep 5

	acline=`sysctl -n hw.acpi.acline`
	if [ "X$acline" = "X$oacline" ]; then
		continue
	fi
	oacline="$acline";

	case "$acline" in
	1)
		atacontrol standby 0 0 300
		atacontrol delayed_write 0 0 0
		;;
	0)
		atacontrol standby 0 0 20
		atacontrol delayed_write 0 0 1
		;;
	esac
	
done


Index: sys/sys/ata.h
===================================================================
RCS file: /dump/FreeBSD-CVS/src/sys/sys/ata.h,v
retrieving revision 1.17
diff -u -r1.17 ata.h
--- sys/sys/ata.h	22 Mar 2003 12:18:20 -0000	1.17
+++ sys/sys/ata.h	28 Mar 2003 02:42:27 -0000
@@ -370,6 +370,7 @@
 #define ATARAIDSTATUS		11
 #define ATAENCSTAT		12
 #define ATAGMAXCHANNEL		13
+#define ATACMD			14
 
     union {
 	struct {
@@ -409,6 +410,20 @@
 	    int			v05;
 	    int			v12;
 	} enclosure;
+	struct {
+	    int			flags;		/* info about the request */
+#define ATA_CMD_CTRL			0x00
+#define ATA_CMD_READ			0x01
+#define ATA_CMD_WRITE			0x02
+
+	    u_int8_t		command;	/* command code */
+	    u_int64_t		lba;		/* lba address */
+	    u_int16_t		count;		/* sector count */
+	    u_int8_t		feature;	/* feature modifier bits */
+
+	    caddr_t		databuf;	/* I/O data buffer */
+	    int			datalen;	/* length of data buffer */
+	} ata;
 	struct {
 	    char		ccb[16];
 	    caddr_t		data;
Index: sys/dev/ata/ata-all.c
===================================================================
RCS file: /dump/FreeBSD-CVS/src/sys/dev/ata/ata-all.c,v
retrieving revision 1.175
diff -u -r1.175 ata-all.c
--- sys/dev/ata/ata-all.c	30 Mar 2003 09:27:59 -0000	1.175
+++ sys/dev/ata/ata-all.c	1 Apr 2003 12:27:07 -0000
@@ -355,6 +355,28 @@
 		      sizeof(struct ata_params));
 	    return 0;
 
+	case ATACMD: {
+	    struct ata_device *atadev;
+
+	    if (!device || !(ch = device_get_softc(device)))
+		return ENXIO;
+	    if (!(atadev = &ch->device[iocmd->device]) ||
+		!(ch->devices & (iocmd->device == MASTER ?
+				 ATA_ATA_MASTER : ATA_ATA_SLAVE)))
+		return ENXIO;
+	    if (iocmd->u.ata.flags != ATA_CMD_CTRL)
+		return EOPNOTSUPP;
+
+	    error = 0;
+	    ATA_SLEEPLOCK_CH(ch, ATA_CONTROL);
+	    if (ata_command(atadev, iocmd->u.ata.command, iocmd->u.ata.lba,
+			    iocmd->u.ata.count, iocmd->u.ata.feature,
+			    ATA_WAIT_INTR) != 0)
+		error = EIO;
+	    ATA_UNLOCK_CH(ch);
+	    return error;
+	}
+
 	case ATAENCSTAT: {
 	    struct ata_device *atadev;
 
Index: sbin/atacontrol/atacontrol.8
===================================================================
RCS file: /dump/FreeBSD-CVS/src/sbin/atacontrol/atacontrol.8,v
retrieving revision 1.22
diff -u -r1.22 atacontrol.8
--- sbin/atacontrol/atacontrol.8	23 Dec 2002 15:30:40 -0000	1.22
+++ sbin/atacontrol/atacontrol.8	31 Jan 2003 00:57:52 -0000
@@ -25,7 +25,7 @@
 .\"
 .\" $FreeBSD: src/sbin/atacontrol/atacontrol.8,v 1.22 2002/12/23 15:30:40 ru Exp $
 .\"
-.Dd May 17, 2001
+.Dd August 18, 2002
 .Dt ATACONTROL 8
 .Os
 .Sh NAME
@@ -72,6 +72,21 @@
 .Ar channel device
 .Nm
 .Ic list
+.Nm
+.Ic idle
+.Ar channel device
+.Op seconds
+.Nm
+.Ic standby
+.Ar channel device
+.Op seconds
+.Nm
+.Ic sleep
+.Ar channel device
+.Nm
+.Ic delayed_write
+.Ar channel device
+.Op 0 | 1
 .Sh DESCRIPTION
 The
 .Nm
@@ -208,6 +223,27 @@
 Fan RPM speed, enclosure temperature, 5V and 12V levels are shown.
 .It Ic list
 Show info about all attached devices on all active controllers.
+.It Ic idle
+Set the idle timeout on the specified device.
+If no timeout is given, put the device into the idle state immediately.
+.It Ic standby
+Set the standby timeout on the specified device.
+If no timeout is given, put the device into the standby state immediately.
+.It Ic sleep
+Put the device into sleep mode.
+Since this effectively powers down the device, settings configured by
+the driver are lost, so this should not be used on an active drive.
+Use
+.Nm
+.Ic reinit
+to reinitialize the device for later use.
+.It Ic delayed_write
+Enable or disable the delayed write feature on the specified device.
+When delayed writes are enabled on devices that support this feature,
+writes that occur while the disk is spun down are stored in the
+drive cache only.
+Once the cache becomes full or the disk is spun up (e.g. for a read
+operation), the cached writes are immediately flushed to the disk.
 .El
 .Sh EXAMPLES
 To see the devices' current access modes, use the command line:
Index: sbin/atacontrol/atacontrol.c
===================================================================
RCS file: /dump/FreeBSD-CVS/src/sbin/atacontrol/atacontrol.c,v
retrieving revision 1.20
diff -u -r1.20 atacontrol.c
--- sbin/atacontrol/atacontrol.c	22 Mar 2003 12:18:20 -0000	1.20
+++ sbin/atacontrol/atacontrol.c	1 Apr 2003 13:26:51 -0000
@@ -249,7 +249,7 @@
 main(int argc, char **argv)
 {
 	struct ata_cmd iocmd;
-	int fd, maxunit, unit;
+	int enable, fd, idle, maxunit, unit;
 
 	if ((fd = open("/dev/ata", O_RDWR)) < 0)
 		err(1, "control device not found");
@@ -427,6 +427,43 @@
 				mode2str(iocmd.u.mode.mode[0]), 
 				mode2str(iocmd.u.mode.mode[1]));
 		}
+	}
+	else if ((!strcmp(argv[1], "idle") || !strcmp(argv[1], "standby") ||
+	    !strcmp(argv[1], "sleep")) && argc == 4) {
+		iocmd.cmd = ATACMD;
+		iocmd.device = atoi(argv[3]);
+		iocmd.u.ata.flags = ATA_CMD_CTRL;
+		iocmd.u.ata.command = !strcmp(argv[1], "idle") ? 0xe1 :
+		    !strcmp(argv[1], "standby") ? 0xe0 : 0xe6;
+		if (ioctl(fd, IOCATA, &iocmd) < 0)
+			err(1, "ioctl(ATACMD)");
+	}
+	else if ((!strcmp(argv[1], "idle") || !strcmp(argv[1], "standby")) &&
+	    argc == 5) {
+		idle = atoi(argv[4]);
+		if (idle > 19800)
+			errx(1, "Maximum idle time is 19800 seconds");
+		if (idle <= 240*5)
+			iocmd.u.ata.count = (idle + 4) / 5;
+		else
+			iocmd.u.ata.count = idle / (30*60) + 240;
+
+		iocmd.cmd = ATACMD;
+		iocmd.device = atoi(argv[3]);
+		iocmd.u.ata.flags = ATA_CMD_CTRL;
+		iocmd.u.ata.command = !strcmp(argv[1], "idle") ? 0xe3 : 0xe2;
+		if (ioctl(fd, IOCATA, &iocmd) < 0)
+			err(1, "ioctl(ATACMD)");
+	}
+	else if (!strcmp(argv[1], "delayed_write") && argc == 5) {
+		enable = atoi(argv[4]);
+		iocmd.cmd = ATACMD;
+		iocmd.device = atoi(argv[3]);
+		iocmd.u.ata.feature = enable ? 0x07 : 0x87;
+		iocmd.u.ata.flags = ATA_CMD_CTRL;
+		iocmd.u.ata.command = 0xfa;
+		if (ioctl(fd, IOCATA, &iocmd) < 0)
+			err(1, "ioctl(ATACMD)");
 	}
 	else
 	    	usage();


More information about the freebsd-fs mailing list