Add a "spindown" facility to ata-disks: If no requests have been received

for a configurable number of seconds, spin the disk down.  Spin it back
up on the next request.

Notice that the timeout is only armed by a request, so to spin down a
disk you may have to do:

	atacontrol spindown ad10 5
	dd if=/dev/ad10 of=/dev/null count=1

To disable spindown, set timeout to zero:

	atacontrol spindown ad10 0

In order to debug any trouble caused, this code is somewhat noisy on the
console.

Enabling spindown on a disk containing / or /var/log/messages is not
going to do anything sensible.

Spinning a disk up and down all the time will wear it out, use sensibly.

Approved by:	sos
This commit is contained in:
Poul-Henning Kamp 2008-03-17 10:33:23 +00:00
parent 272870cf7b
commit 72d945abcc
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=177298
7 changed files with 97 additions and 2 deletions

View File

@ -70,6 +70,10 @@
.Ic cap
.Ar device
.Nm
.Ic spindown
.Ar device
.Op Ar seconds
.Nm
.Ic list
.Sh DESCRIPTION
The
@ -190,6 +194,10 @@ The device name and manufacture/version strings are shown.
.It Ic cap
Show detailed info about the device on
.Ar device .
.It Ic spindown
Set or report timeout after which the
.Ar device
will be spun down.
.It Ic info
Show info about the attached devices on the
.Ar channel .

View File

@ -102,6 +102,7 @@ usage(void)
" atacontrol status array\n"
" atacontrol mode device [mode]\n"
" atacontrol cap device\n"
" atacontrol spindown device [seconds]\n"
);
exit(EX_USAGE);
}
@ -285,6 +286,26 @@ info_print(int fd, int channel, int prchan)
printf(" no device present\n");
}
static void
ata_spindown(int fd, const char *dev, const char *arg)
{
int tmo;
if (arg != NULL) {
tmo = strtoul(arg, NULL, 0);
if (ioctl(fd, IOCATASSPINDOWN, &tmo) < 0)
err(1, "ioctl(IOCATASSPINDOWN)");
} else {
if (ioctl(fd, IOCATAGSPINDOWN, &tmo) < 0)
err(1, "ioctl(IOCATAGSPINDOWN)");
if (tmo == 0)
printf("%s: idle spin down disabled\n", dev);
else
printf("%s: spin down after %d seconds idle\n",
dev, tmo);
}
}
static int
open_dev(const char *arg, int mode)
{
@ -356,6 +377,12 @@ main(int argc, char **argv)
exit(EX_OK);
}
if (!strcmp(argv[1], "spindown") && (argc == 3 || argc == 4)) {
fd = open_dev(argv[2], O_RDONLY);
ata_spindown(fd, argv[2], argv[3]);
exit(EX_OK);
}
if ((fd = open("/dev/ata", O_RDWR)) < 0)
err(1, "control device not found");

View File

@ -514,6 +514,12 @@ ata_device_ioctl(device_t dev, u_long cmd, caddr_t data)
case IOCATAGMODE:
*mode = atadev->mode;
return 0;
case IOCATASSPINDOWN:
atadev->spindown = *mode;
return 0;
case IOCATAGSPINDOWN:
*mode = atadev->spindown;
return 0;
default:
return ENOTTY;
}

View File

@ -403,6 +403,9 @@ struct ata_device {
struct ata_params param; /* ata param structure */
int mode; /* current transfermode */
u_int32_t max_iosize; /* max IO size */
int spindown; /* idle spindown timeout */
struct callout spindown_timer;
int spindown_state;
int flags;
#define ATA_D_USE_CHS 0x0001
#define ATA_D_MEDIA_CHANGED 0x0002

View File

@ -171,6 +171,8 @@ ad_attach(device_t dev)
device_add_child(dev, "subdisk", device_get_unit(dev));
ad_firmware_geom_adjust(dev, adp->disk);
bus_generic_attach(dev);
callout_init(&atadev->spindown_timer, 1);
return 0;
}
@ -178,6 +180,7 @@ static int
ad_detach(device_t dev)
{
struct ad_softc *adp = device_get_ivars(dev);
struct ata_device *atadev = device_get_softc(dev);
device_t *children;
int nchildren, i;
@ -185,6 +188,9 @@ ad_detach(device_t dev)
if (!device_get_ivars(dev))
return ENXIO;
/* destroy the power timeout */
callout_drain(&atadev->spindown_timer);
/* detach & delete all children */
if (!device_get_children(dev, &children, &nchildren)) {
for (i = 0; i < nchildren; i++)
@ -229,6 +235,38 @@ ad_reinit(device_t dev)
return 0;
}
static void
ad_power_callback(struct ata_request *request)
{
device_printf(request->dev, "drive spun down.\n");
ata_free_request(request);
}
static void
ad_spindown(void *priv)
{
device_t dev = priv;
struct ata_device *atadev = device_get_softc(dev);
struct ata_request *request;
if(atadev->spindown == 0)
return;
device_printf(dev, "Idle, spin down\n");
atadev->spindown_state = 1;
if (!(request = ata_alloc_request())) {
device_printf(dev, "FAILURE - out of memory in ad_spindown\n");
return;
}
request->flags = ATA_R_CONTROL;
request->dev = dev;
request->timeout = 5;
request->retries = 1;
request->callback = ad_power_callback;
request->u.ata.command = ATA_STANDBY_IMMEDIATE;
ata_queue_request(request);
}
static void
ad_strategy(struct bio *bp)
{
@ -236,6 +274,10 @@ ad_strategy(struct bio *bp)
struct ata_device *atadev = device_get_softc(dev);
struct ata_request *request;
if (atadev->spindown != 0)
callout_reset(&atadev->spindown_timer, hz * atadev->spindown,
ad_spindown, dev);
if (!(request = ata_alloc_request())) {
device_printf(dev, "FAILURE - out of memory in start\n");
biofinish(bp, NULL, ENOMEM);
@ -246,7 +288,13 @@ ad_strategy(struct bio *bp)
request->dev = dev;
request->bio = bp;
request->callback = ad_done;
request->timeout = 5;
if (atadev->spindown_state) {
device_printf(dev, "request while spun down, starting.\n");
atadev->spindown_state = 0;
request->timeout = 31;
} else {
request->timeout = 5;
}
request->retries = 2;
request->data = bp->bio_data;
request->bytecount = bp->bio_bcount;

View File

@ -34,7 +34,7 @@ struct ad_softc {
u_int32_t transfersize; /* size of each transfer */
int num_tags; /* number of tags supported */
int flags; /* drive flags */
#define AD_F_LABELLING 0x0001
#define AD_F_LABELLING 0x0001
#define AD_F_CHS_USED 0x0002
#define AD_F_32B_ENABLED 0x0004
#define AD_F_TAG_ENABLED 0x0008

View File

@ -433,6 +433,9 @@ struct ata_ioc_request {
#define IOCATAGMODE _IOR('a', 102, int)
#define IOCATASMODE _IOW('a', 103, int)
#define IOCATAGSPINDOWN _IOR('a', 104, int)
#define IOCATASSPINDOWN _IOW('a', 105, int)
struct ata_ioc_raid_config {
int lun;