Hi all, yet again, I'm trying to get this feature into the Linux kernel. This time, I have tried to arrange the code in such a way that hardly anything not strictly belonging to the libata or ide subsystem is touched at all. Since former attempts to design this in a more generic way involving the block and scsi layer have proven very difficult and since this is all about an ATA specific feature anyway, I think the best solution (for now at least) is to stick to those two subsystems as far as possible. Nevertheless, I'd welcome James' opinion on the third patch in the series with regard to the use of vendor specific cdbs. If it is acceptable in principle, the question remains whether we sould reserve part of the range 0xf800 - 0xffff of vendor specific service actions for the exclusive use by LLDDs (in much the way I did it here), or whether all these codes should be unique in the kernel and defined in scsi.h. Also, we have to agree upon a suitable way to deal with devices that actually do support the UNLOAD FEATURE but don't report that capability in their ID as specified in ATA-7. Perhaps we even want to fall back to STANDBY IMMEDIATE on devices that definitely don't support IDLE IMMEDIATE with UNLOAD FEATURE; this has been the case for the original hdaps / disk-protect patch but I'm not quite sure whether anybody actually relies on this feature and whether it would be advisable in the first place. Regarding devices that support head unloading but don't report it properly, there seem to be the following options: a) Provide an attribute in sysfs for the user to indicate that the device really does support the UNLOAD FEATURE. b) Issue the command (for the first time) anyway and check whether the device responds as specified in ATA-7. Make a note of the result and act accordingly from there on. The second option looks appealing because no user intervention is required and it is known to work at least for some devices. On the other hand, it may be dangerous on ...
This is just a trivial fix of a potential memory leak when ata_init()
encounters an error.
Signed-off-by: Elias Oltmanns <eo@nebensachen.de>
---
drivers/ata/libata-core.c | 14 +++++++++-----
1 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 9bef1a8..0a2f921 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -6088,16 +6088,20 @@ static int __init ata_init(void)
ata_wq = create_workqueue("ata");
if (!ata_wq)
- return -ENOMEM;
+ goto free_force_tbl;
ata_aux_wq = create_singlethread_workqueue("ata_aux");
- if (!ata_aux_wq) {
- destroy_workqueue(ata_wq);
- return -ENOMEM;
- }
+ if (!ata_aux_wq)
+ goto free_wq;
printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n");
return 0;
+
+free_wq:
+ destroy_workqueue(ata_wq);
+free_force_tbl:
+ kfree(ata_force_tbl);
+ return -ENOMEM;
}
static void __exit ata_exit(void)
--
Add a function to check an ATA device's id for head unload support as
specified in ATA-7.
Signed-off-by: Elias Oltmanns <eo@nebensachen.de>
---
include/linux/ata.h | 15 +++++++++++++++
1 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/include/linux/ata.h b/include/linux/ata.h
index cf4ef6d..c92ac10 100644
--- a/include/linux/ata.h
+++ b/include/linux/ata.h
@@ -530,6 +530,21 @@ static inline bool ata_id_has_dipm(const u16 *id)
}
+static inline int ata_id_has_unload(const u16 *id)
+{
+ /*
+ * ATA-7 specifies two places to indicate unload feature support.
+ * Since I don't really understand the difference, I'll just check
+ * both and only return zero if none of them indicates otherwise.
+ */
+ if ((id[ATA_ID_CFSSE] & 0xC000) == 0x4000
+ && id[ATA_ID_CFSSE] & (1 << 13))
+ return id[ATA_ID_CFSSE] & (1 << 13);
+ if ((id[ATA_ID_CSF_DEFAULT] & 0xC000) == 0x4000)
+ return id[ATA_ID_CSF_DEFAULT] & (1 << 13);
+ return 0;
+}
+
static inline int ata_id_has_fua(const u16 *id)
{
if ((id[ATA_ID_CFSSE] & 0xC000) != 0x4000)
--
On Sat, 26 Jul 2008 08:24:35 +0200 If a feature is new in ATA 7 you should also check the ATA version is >=7 in the tests. Just in case some prehistoric device has it down for a different purpose. Alan --
On user request (through sysfs), the IDLE IMMEDIATE command with UNLOAD
FEATURE as specified in ATA-7 is issued to the device and processing of
the request queue is stopped thereafter until the speified timeout
expires or user space asks to resume normal operation. This is supposed
to prevent the heads of a hard drive from accidentally crashing onto the
platter when a heavy shock is anticipated (like a falling laptop
expected to hit the floor). This patch simply stops processing the
request queue. In particular, it does not yet, for instance, defer an
SRST issued in order to recover from an error on the other device on the
interface.
Signed-off-by: Elias Oltmanns <eo@nebensachen.de>
---
drivers/ata/ahci.c | 1
drivers/ata/ata_piix.c | 6 +
drivers/ata/libata-core.c | 6 +
drivers/ata/libata-scsi.c | 343 +++++++++++++++++++++++++++++++++++++++++++--
drivers/ata/libata.h | 11 +
include/linux/libata.h | 6 +
6 files changed, 360 insertions(+), 13 deletions(-)
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index dc7596f..cbf86fa 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -316,6 +316,7 @@ static struct device_attribute *ahci_shost_attrs[] = {
static struct device_attribute *ahci_sdev_attrs[] = {
&dev_attr_sw_activity,
+ &dev_attr_unload_heads,
NULL
};
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index a90ae03..e4cdd86 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -289,8 +289,14 @@ static struct pci_driver piix_pci_driver = {
#endif
};
+static struct device_attribute *piix_sdev_attrs[] = {
+ &dev_attr_unload_heads,
+ NULL
+};
+
static struct scsi_host_template piix_sht = {
ATA_BMDMA_SHT(DRV_NAME),
+ .sdev_attrs = piix_sdev_attrs,
};
static struct ata_port_operations piix_pata_ops = {
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 0a2f921..07737ec 100644
--- a/drivers/ata/libata-core.c
+++ ...For libata, the easiest way to achieve the above would be adding a per-dev EH action, say, ATA_EH_UNLOAD and schedule EH w/ the action OR'd to eh_info->action. The EH_UNLOAD handler can then issue the command wait for the specified number of seconds and continue. This will be pretty simple to implement as command exclusion and stuff are all automatically handled by EH framework. However, SATA or not, there simply isn't a way to abort commands in ATA. Issuing random command while other commands are in progress simply is state machine violation and there will be many interesting results including complete system lockup (ATA controller dying while holding the PCI bus). The only reliable way to abort in-flight commands are by issuing hardreset. However, ATA reset protocol is not designed for quick recovery. The machine is gonna hit the ground hard way before the reset protocol is complete. The only way to solve this nicely is either to build the accelerometer into the drive and let the drive itself protect itself or implement a sideband signal to tell it to duck for cover. For SATA, this sideband signal can be another OOB sequence. If it's ever implemented this way, it will be in SControl, I guess. Well, short of that, all we can do is to wait for the currently in-flight commands to drain and hope that it happens before the machine hits the ground. Also, that the harddrive is not going through one of the longish EH recovery sequences when it starts to fall. :-( -- tejun --
Actually you cau can issue idle immediate on older ATA devices. I am not clear if that was stuck back into the current accelerometer friendly drives or not. Would need to check with IBLenovo --
Was that something intentional or was it a happy accident? There can be bus ownership problem on PATA and on SATA this is much more state logic on both sides of the cable and I think things like that would be more difficult to work accidentally. Thanks. -- tejun --
IDLE IMMEDIATE is part of the "historic" specification rather than modern ATA standards. Older drives support it happily newer ones tended to get We'd have to query the drive vendors to see what they expected. --
We'll have to query the controller vendors too and I think it's highly likely to cause problems on many controllers. -- tejun --
In ide_atapi_error() IDLE IMMEDIATE is issued even if busy bit is still set. This made me hope that we could do something similar wrt disk head unloading. However, since I haven't found anything about this in the specs and considering Tejun's comments, I'm now wondering whether the code in ide_atapi_error() isn't a little imprudent. Also, as long as nothing definite is said about it in the specs, we can only issue IDLE IMMEDIATE in parallel to other commands on devices we know to support it, right? Regards, Elias --
I'm rather afraid this approach is impractical or unfavourable at the very least. Depending on the configured thresholds, a head unload request might well be issued unintentionally, e.g. by accidentally knocking against the table. It is quite alright for the HD to stop I/O for a moment but if the secondary device on the interface happens to be a CD writer, it will be very annoying to have CD writing operations fail due to minor percussions. Also, if there are two devices on the same port that support the UNLOAD FEATURE and you issue a head unload request to both of them in close succession, the IDLE IMMEDIATE to the second device will be blocked until the timeout for the first has expired. Generally, blocking SRST and the likes on a port seems acceptable, but stopping all I/O on a port just because a head unload request has been Yes, I suspected as much. Thanks for the confirmation. Yes, it's a bit unsatisfactory but it's better than nothing. Regards, Elias --
Unload can be implemented as port-wide operation so that it issues IDLE IMMEDIATE to all drives on the port but given that this is mostly for laptop, this discussion is a bit peripheral. -- tejun --
To be quite honest, I don't know very much about the way CD writing works. I just assumed that delaying queue processing for a CD writer for several seconds would have very much the same effect as the input buffer of cdrecord getting empty prematurely. Do you mean to say that CD writing (or any other time expensive operation I haven't thought of) We can't rule out that the HD is connected as master and CDRW as slave to the same controller in a PATA setup. I'm not familiar with the SATA configurations in modern laptops though. Regards, Elias --
Any modern cd/dvd writer can happily recover from buffer underruns. I think the physical shock itself has better chance of screwing up the recording. The only thing to make sure is that no command is issued to On most, they occupy different channels and even when they reside on the same channel, it just doesn't really matter these days. And even on those cases, it would be better to use EH as that will make the IDLE IMMEDIATE command always win the bus as soon as possible while IDLE IMMEDIATE queued at the head of the drive queue could lose to an ATAPI command. Thanks. -- tejun --
A system lockup may be an acceptable compromise if that saves the
hardware. Maybe the kernel should explicitely panic unless the
controller/drive is known to be able to recover.
Gabor
--
---------------------------------------------------------
MTA SZTAKI Computer and Automation Research Institute
Hungarian Academy of Sciences
---------------------------------------------------------
--
On Mon, 4 Aug 2008 16:28:32 +0200 We've already been told that the accelerometer will now and then randomly trigger due to other shock patterns like a bump. I don't want my laptop to panic randomly on train journeys thank you. Alan --
Such lockups usually would occur before the intervening command is successfully issued. HSM violation occurs when the driver asks the controller to send another command while it's already processing another command. Heh... panicking on accelerometer would be fun tho. We're gonna get ourselves really flamewars on just about every linux news site. -- tejun --
On user request (through sysfs), the IDLE IMMEDIATE command with UNLOAD
FEATURE as specified in ATA-7 is issued to the device and processing of
the request queue is stopped thereafter until the speified timeout
expires or user space asks to resume normal operation. This is supposed
to prevent the heads of a hard drive from accidentally crashing onto the
platter when a heavy shock is anticipated (like a falling laptop
expected to hit the floor). This patch simply stops processing the
request queue. In particular, it does not yet, for instance, defer an
SRST issued in order to recover from an error on the other device on the
interface.
Signed-off-by: Elias Oltmanns <eo@nebensachen.de>
---
drivers/ide/ide-io.c | 26 ++++++
drivers/ide/ide.c | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/ide.h | 5 +
3 files changed, 254 insertions(+), 0 deletions(-)
diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c
index ce9ecd1..888ed28 100644
--- a/drivers/ide/ide-io.c
+++ b/drivers/ide/ide-io.c
@@ -717,7 +717,28 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive,
static ide_startstop_t ide_special_rq(ide_drive_t *drive, struct request *rq)
{
+ ide_hwif_t *hwif = drive->hwif;
+ ide_task_t task;
+ struct ide_taskfile *tf = &task.tf;
+
+ memset(&task, 0, sizeof(task));
switch (rq->cmd[0]) {
+ case REQ_PARK_HEADS:
+ if (unlikely(!timer_pending(&drive->park_timer))) {
+ ide_end_request(drive, 0, 0);
+ return ide_stopped;
+ }
+ drive->sleep = drive->park_timer.expires;
+ drive->sleeping = 1;
+ tf->command = WIN_IDLEIMMEDIATE;
+ tf->feature = 0x44;
+ tf->lbal = 0x4c;
+ tf->lbam = 0x4e;
+ tf->lbah = 0x55;
+ break;
+ case REQ_UNPARK_HEADS:
+ tf->command = WIN_CHECKPOWERMODE1;
+ break;
case REQ_DRIVE_RESET:
return ide_do_reset(drive);
default:
@@ -725,6 +746,11 @@ static ide_startstop_t ide_special_rq(ide_drive_t *drive, struct request *rq)
ide_end_request(drive, 0, 0);
return ...Any reason this does not use normal driver model suspend/resume callbacks? Pavel -- (english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html --
Yes, two in fact. Firstly, it would appear that after freeze_processes() has completed, timers don't fire anymore. That's why we have to call the unpark hook manually. The ATA suspend callback would be the place to do that but the scsi ULD's suspend callback issues a cache sync and a disk start_stop command before and waits indefinitely for completion. Secondly and more importantly, using pm_notifiers is the right thing to do as long as user processes control when to park and unpark the disks. This way we can hold back from suspending until user space gives the all clear. Regards, Elias --
Put some information (and pointers to more) into the kernel's doc tree, describing briefly how to set up disk shock protection under GNU/Linux. Signed-off-by: Elias Oltmanns <eo@nebensachen.de> --- Documentation/laptops/disk-shock-protection.txt | 66 +++++++++++++++++++++++ 1 files changed, 66 insertions(+), 0 deletions(-) create mode 100644 Documentation/laptops/disk-shock-protection.txt diff --git a/Documentation/laptops/disk-shock-protection.txt b/Documentation/laptops/disk-shock-protection.txt new file mode 100644 index 0000000..ac26040 --- /dev/null +++ b/Documentation/laptops/disk-shock-protection.txt @@ -0,0 +1,66 @@ +Hard disk shock protection +========================== + +Author: Elias Oltmanns <eo@nebensachen.de> +Last modified: 2008-07-24 + + +Intro +----- + +ATA/ATAPI-7 specifies the IDLE IMMEDIATE command with UNLOAD FEATURE. +Issuing this command should cause the drive to switch to idle mode and +unload disk heads. This feature is being used in modern laptops in +conjunction with accelerometers and appropriate software to implement +a shock protection facility. The idea is to stop all I/O operations on +the internal hard drive and park its heads on the ramp when critical +situations are anticipated. The desire to have such a feature +available on GNU/Linux systems has been the original motivation to +implement a generic disk head parking interface in the Linux kernel. + + +The interface +------------- + +The interface works as follows: Writing an integer value to +/sys/block/*/device/unload_heads will take the heads of the respective +drive off the platter and block all I/O operations for the specified +number of seconds. When the timeout expires and no further disk head +park request has been issued in the meantime, normal operation will be +resumed. + +FIXME: +Once we've addressed the problem with pre-ATA-7 drives supporting the +UNLOAD FEATURE but not announcing it properly, we'll document ...
Hello. A typo in "system". :-) MBR, Sergei --
