Date: Sat, 12 Apr 2003 21:02:13 +0100 From: Ian Dowse <iedowse@maths.tcd.ie> To: Marko Zec <zec@tel.fer.hr> Cc: freebsd-stable@freebsd.org Subject: Re: PATCH: Forcible delaying of UFS (soft)updates Message-ID: <200304122102.aa53041@salmon.maths.tcd.ie> In-Reply-To: Your message of "Sat, 12 Apr 2003 03:41:17 %2B0200." <3E976EBD.C3E66EF8@tel.fer.hr>
next in thread | previous in thread | raw e-mail | index | archive | help
In message <3E976EBD.C3E66EF8@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();
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200304122102.aa53041>