mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-15 15:06:42 +00:00
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:
parent
272870cf7b
commit
72d945abcc
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=177298
@ -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 .
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user