Commit 4abdc6ee7c47a1a6e12f95717e461baeebee5df7

Authored by Elias Oltmanns
Committed by Bartlomiej Zolnierkiewicz
1 parent 08243ba731

ide: Implement disk shock protection support (v4)

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 specified 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). Port resets are deferred whenever a device on that
port is in the parked state.

v3:
Elias Oltmanns <eo@nebensachen.de> wrote:
[...]
> >> 1. Make sure that no negative value is being passed to
> >>    jiffies_to_msecs() in ide_park_show().
> >> 2. Drop the superfluous variable hwif in ide_special_rq().
> >> 3. Skip initialisation of task and tf in ide_special_rq() if we are not
> >>    handling a (un)park request.
> >
> > Well, #3 should have been done differently because we donn't want to
> > check for REQ_(UN)?PARK_HEADS more often than is necessary.
>
> While preparing the backport to 2.6.27, it has just occurred to me that
> we need to clear the IDE_DFLAG_PARKED flag in ide_disk_pre_reset()
> because this flag must not be set after *any* sort of access to the
> device.

v4:
Fix a memory leak due to a missing blk_put_request() in
issue_park_cmd(). Additionally, we should plug the queue when enqueueing
the unpark request because there is no guarantee that the park timeout
has not expired by then. Even though the chance for that to happen is
very slim, the request might end up hanging in the queue until the next
I/O operation is queued up. While at it, clean up the code a little:
- make issue_park_cmd() a function of type void since nobody cares for
  the return value anyway;
- use blk_start_queueing() instead of __blk_run_queue() since we don't
  have to worry about recursion;
- remove a superfluous pointer deference in task_no_data_intr().

Signed-off-by: Elias Oltmanns <eo@nebensachen.de>
Cc: Jeff Garzik <jeff@garzik.org>,
Cc: Randy Dunlap <randy.dunlap@oracle.com>
Cc: Tejun Heo <htejun@gmail.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>

Showing 8 changed files with 206 additions and 5 deletions Side-by-side Diff

drivers/ide/Makefile
... ... @@ -5,7 +5,7 @@
5 5 EXTRA_CFLAGS += -Idrivers/ide
6 6  
7 7 ide-core-y += ide.o ide-ioctls.o ide-io.o ide-iops.o ide-lib.o ide-probe.o \
8   - ide-taskfile.o ide-pio-blacklist.o
  8 + ide-taskfile.o ide-park.o ide-pio-blacklist.o
9 9  
10 10 # core IDE code
11 11 ide-core-$(CONFIG_IDE_TIMINGS) += ide-timings.o
drivers/ide/ide-io.c
... ... @@ -672,7 +672,32 @@
672 672  
673 673 static ide_startstop_t ide_special_rq(ide_drive_t *drive, struct request *rq)
674 674 {
675   - switch (rq->cmd[0]) {
  675 + u8 cmd = rq->cmd[0];
  676 +
  677 + if (cmd == REQ_PARK_HEADS || cmd == REQ_UNPARK_HEADS) {
  678 + ide_task_t task;
  679 + struct ide_taskfile *tf = &task.tf;
  680 +
  681 + memset(&task, 0, sizeof(task));
  682 + if (cmd == REQ_PARK_HEADS) {
  683 + drive->sleep = *(unsigned long *)rq->special;
  684 + drive->dev_flags |= IDE_DFLAG_SLEEPING;
  685 + tf->command = ATA_CMD_IDLEIMMEDIATE;
  686 + tf->feature = 0x44;
  687 + tf->lbal = 0x4c;
  688 + tf->lbam = 0x4e;
  689 + tf->lbah = 0x55;
  690 + task.tf_flags |= IDE_TFLAG_CUSTOM_HANDLER;
  691 + } else /* cmd == REQ_UNPARK_HEADS */
  692 + tf->command = ATA_CMD_CHK_POWER;
  693 +
  694 + task.tf_flags |= IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
  695 + task.rq = rq;
  696 + drive->hwif->data_phase = task.data_phase = TASKFILE_NO_DATA;
  697 + return do_rw_taskfile(drive, &task);
  698 + }
  699 +
  700 + switch (cmd) {
676 701 case REQ_DEVSET_EXEC:
677 702 {
678 703 int err, (*setfunc)(ide_drive_t *, int) = rq->special;
... ... @@ -1008,7 +1033,7 @@
1008 1033 }
1009 1034 hwgroup->hwif = hwif;
1010 1035 hwgroup->drive = drive;
1011   - drive->dev_flags &= ~IDE_DFLAG_SLEEPING;
  1036 + drive->dev_flags &= ~(IDE_DFLAG_SLEEPING | IDE_DFLAG_PARKED);
1012 1037 drive->service_start = jiffies;
1013 1038  
1014 1039 if (blk_queue_plugged(drive->queue)) {
drivers/ide/ide-iops.c
... ... @@ -1020,6 +1020,7 @@
1020 1020 drive->special.b.recalibrate = legacy;
1021 1021  
1022 1022 drive->mult_count = 0;
  1023 + drive->dev_flags &= ~IDE_DFLAG_PARKED;
1023 1024  
1024 1025 if ((drive->dev_flags & IDE_DFLAG_KEEP_SETTINGS) == 0 &&
1025 1026 (drive->dev_flags & IDE_DFLAG_USING_DMA) == 0)
1026 1027  
... ... @@ -1079,12 +1080,13 @@
1079 1080 static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
1080 1081 {
1081 1082 unsigned int unit;
1082   - unsigned long flags;
  1083 + unsigned long flags, timeout;
1083 1084 ide_hwif_t *hwif;
1084 1085 ide_hwgroup_t *hwgroup;
1085 1086 struct ide_io_ports *io_ports;
1086 1087 const struct ide_tp_ops *tp_ops;
1087 1088 const struct ide_port_ops *port_ops;
  1089 + DEFINE_WAIT(wait);
1088 1090  
1089 1091 spin_lock_irqsave(&ide_lock, flags);
1090 1092 hwif = HWIF(drive);
... ... @@ -1110,6 +1112,31 @@
1110 1112 spin_unlock_irqrestore(&ide_lock, flags);
1111 1113 return ide_started;
1112 1114 }
  1115 +
  1116 + /* We must not disturb devices in the IDE_DFLAG_PARKED state. */
  1117 + do {
  1118 + unsigned long now;
  1119 +
  1120 + prepare_to_wait(&ide_park_wq, &wait, TASK_UNINTERRUPTIBLE);
  1121 + timeout = jiffies;
  1122 + for (unit = 0; unit < MAX_DRIVES; unit++) {
  1123 + ide_drive_t *tdrive = &hwif->drives[unit];
  1124 +
  1125 + if (tdrive->dev_flags & IDE_DFLAG_PRESENT &&
  1126 + tdrive->dev_flags & IDE_DFLAG_PARKED &&
  1127 + time_after(tdrive->sleep, timeout))
  1128 + timeout = tdrive->sleep;
  1129 + }
  1130 +
  1131 + now = jiffies;
  1132 + if (time_before_eq(timeout, now))
  1133 + break;
  1134 +
  1135 + spin_unlock_irqrestore(&ide_lock, flags);
  1136 + timeout = schedule_timeout_uninterruptible(timeout - now);
  1137 + spin_lock_irqsave(&ide_lock, flags);
  1138 + } while (timeout);
  1139 + finish_wait(&ide_park_wq, &wait);
1113 1140  
1114 1141 /*
1115 1142 * First, reset any device state data we were maintaining
drivers/ide/ide-park.c
  1 +#include <linux/kernel.h>
  2 +#include <linux/ide.h>
  3 +#include <linux/jiffies.h>
  4 +#include <linux/blkdev.h>
  5 +
  6 +DECLARE_WAIT_QUEUE_HEAD(ide_park_wq);
  7 +
  8 +static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout)
  9 +{
  10 + struct request_queue *q = drive->queue;
  11 + struct request *rq;
  12 + int rc;
  13 +
  14 + timeout += jiffies;
  15 + spin_lock_irq(&ide_lock);
  16 + if (drive->dev_flags & IDE_DFLAG_PARKED) {
  17 + ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
  18 + int reset_timer;
  19 +
  20 + reset_timer = time_before(timeout, drive->sleep);
  21 + drive->sleep = timeout;
  22 + wake_up_all(&ide_park_wq);
  23 + if (reset_timer && hwgroup->sleeping &&
  24 + del_timer(&hwgroup->timer)) {
  25 + hwgroup->sleeping = 0;
  26 + hwgroup->busy = 0;
  27 + blk_start_queueing(q);
  28 + }
  29 + spin_unlock_irq(&ide_lock);
  30 + return;
  31 + }
  32 + spin_unlock_irq(&ide_lock);
  33 +
  34 + rq = blk_get_request(q, READ, __GFP_WAIT);
  35 + rq->cmd[0] = REQ_PARK_HEADS;
  36 + rq->cmd_len = 1;
  37 + rq->cmd_type = REQ_TYPE_SPECIAL;
  38 + rq->special = &timeout;
  39 + rc = blk_execute_rq(q, NULL, rq, 1);
  40 + blk_put_request(rq);
  41 + if (rc)
  42 + goto out;
  43 +
  44 + /*
  45 + * Make sure that *some* command is sent to the drive after the
  46 + * timeout has expired, so power management will be reenabled.
  47 + */
  48 + rq = blk_get_request(q, READ, GFP_NOWAIT);
  49 + if (unlikely(!rq))
  50 + goto out;
  51 +
  52 + rq->cmd[0] = REQ_UNPARK_HEADS;
  53 + rq->cmd_len = 1;
  54 + rq->cmd_type = REQ_TYPE_SPECIAL;
  55 + elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 1);
  56 +
  57 +out:
  58 + return;
  59 +}
  60 +
  61 +ssize_t ide_park_show(struct device *dev, struct device_attribute *attr,
  62 + char *buf)
  63 +{
  64 + ide_drive_t *drive = to_ide_device(dev);
  65 + unsigned long now;
  66 + unsigned int msecs;
  67 +
  68 + if (drive->dev_flags & IDE_DFLAG_NO_UNLOAD)
  69 + return -EOPNOTSUPP;
  70 +
  71 + spin_lock_irq(&ide_lock);
  72 + now = jiffies;
  73 + if (drive->dev_flags & IDE_DFLAG_PARKED &&
  74 + time_after(drive->sleep, now))
  75 + msecs = jiffies_to_msecs(drive->sleep - now);
  76 + else
  77 + msecs = 0;
  78 + spin_unlock_irq(&ide_lock);
  79 +
  80 + return snprintf(buf, 20, "%u\n", msecs);
  81 +}
  82 +
  83 +ssize_t ide_park_store(struct device *dev, struct device_attribute *attr,
  84 + const char *buf, size_t len)
  85 +{
  86 +#define MAX_PARK_TIMEOUT 30000
  87 + ide_drive_t *drive = to_ide_device(dev);
  88 + long int input;
  89 + int rc;
  90 +
  91 + rc = strict_strtol(buf, 10, &input);
  92 + if (rc || input < -2)
  93 + return -EINVAL;
  94 + if (input > MAX_PARK_TIMEOUT) {
  95 + input = MAX_PARK_TIMEOUT;
  96 + rc = -EOVERFLOW;
  97 + }
  98 +
  99 + mutex_lock(&ide_setting_mtx);
  100 + if (input >= 0) {
  101 + if (drive->dev_flags & IDE_DFLAG_NO_UNLOAD)
  102 + rc = -EOPNOTSUPP;
  103 + else if (input || drive->dev_flags & IDE_DFLAG_PARKED)
  104 + issue_park_cmd(drive, msecs_to_jiffies(input));
  105 + } else {
  106 + if (drive->media == ide_disk)
  107 + switch (input) {
  108 + case -1:
  109 + drive->dev_flags &= ~IDE_DFLAG_NO_UNLOAD;
  110 + break;
  111 + case -2:
  112 + drive->dev_flags |= IDE_DFLAG_NO_UNLOAD;
  113 + break;
  114 + }
  115 + else
  116 + rc = -EOPNOTSUPP;
  117 + }
  118 + mutex_unlock(&ide_setting_mtx);
  119 +
  120 + return rc ? rc : len;
  121 +}
drivers/ide/ide-probe.c
... ... @@ -208,6 +208,8 @@
208 208 drive->ready_stat = 0;
209 209 if (ata_id_cdb_intr(id))
210 210 drive->atapi_flags |= IDE_AFLAG_DRQ_INTERRUPT;
  211 + /* we don't do head unloading on ATAPI devices */
  212 + drive->dev_flags |= IDE_DFLAG_NO_UNLOAD;
211 213 return;
212 214 }
213 215  
... ... @@ -222,6 +224,9 @@
222 224 drive->dev_flags |= IDE_DFLAG_REMOVABLE;
223 225  
224 226 drive->media = ide_disk;
  227 +
  228 + if (!ata_id_has_unload(drive->id))
  229 + drive->dev_flags |= IDE_DFLAG_NO_UNLOAD;
225 230  
226 231 printk(KERN_CONT "%s DISK drive\n", is_cfa ? "CFA" : "ATA");
227 232  
drivers/ide/ide-taskfile.c
... ... @@ -152,7 +152,16 @@
152 152  
153 153 if (!custom)
154 154 ide_end_drive_cmd(drive, stat, ide_read_error(drive));
155   - else if (tf->command == ATA_CMD_SET_MULTI)
  155 + else if (tf->command == ATA_CMD_IDLEIMMEDIATE) {
  156 + hwif->tp_ops->tf_read(drive, task);
  157 + if (tf->lbal != 0xc4) {
  158 + printk(KERN_ERR "%s: head unload failed!\n",
  159 + drive->name);
  160 + ide_tf_dump(drive->name, tf);
  161 + } else
  162 + drive->dev_flags |= IDE_DFLAG_PARKED;
  163 + ide_end_drive_cmd(drive, stat, ide_read_error(drive));
  164 + } else if (tf->command == ATA_CMD_SET_MULTI)
156 165 drive->mult_count = drive->mult_req;
157 166  
158 167 return ide_stopped;
... ... @@ -587,6 +587,7 @@
587 587 __ATTR_RO(model),
588 588 __ATTR_RO(firmware),
589 589 __ATTR(serial, 0400, serial_show, NULL),
  590 + __ATTR(unload_heads, 0644, ide_park_show, ide_park_store),
590 591 __ATTR_NULL
591 592 };
592 593  
... ... @@ -156,6 +156,8 @@
156 156 */
157 157 #define REQ_DRIVE_RESET 0x20
158 158 #define REQ_DEVSET_EXEC 0x21
  159 +#define REQ_PARK_HEADS 0x22
  160 +#define REQ_UNPARK_HEADS 0x23
159 161  
160 162 /*
161 163 * Check for an interrupt and acknowledge the interrupt status
... ... @@ -573,6 +575,10 @@
573 575 /* retrying in PIO */
574 576 IDE_DFLAG_DMA_PIO_RETRY = (1 << 25),
575 577 IDE_DFLAG_LBA = (1 << 26),
  578 + /* don't unload heads */
  579 + IDE_DFLAG_NO_UNLOAD = (1 << 27),
  580 + /* heads unloaded, please don't reset port */
  581 + IDE_DFLAG_PARKED = (1 << 28)
576 582 };
577 583  
578 584 struct ide_drive_s {
... ... @@ -1206,6 +1212,13 @@
1206 1212 int ide_check_atapi_device(ide_drive_t *, const char *);
1207 1213  
1208 1214 void ide_init_pc(struct ide_atapi_pc *);
  1215 +
  1216 +/* Disk head parking */
  1217 +extern wait_queue_head_t ide_park_wq;
  1218 +ssize_t ide_park_show(struct device *dev, struct device_attribute *attr,
  1219 + char *buf);
  1220 +ssize_t ide_park_store(struct device *dev, struct device_attribute *attr,
  1221 + const char *buf, size_t len);
1209 1222  
1210 1223 /*
1211 1224 * Special requests for ide-tape block device strategy routine.