Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 114225
b: refs/heads/master
c: 4abdc6e
h: refs/heads/master
i:
  114223: 77fac19
v: v3
  • Loading branch information
Elias Oltmanns authored and Bartlomiej Zolnierkiewicz committed Oct 13, 2008
1 parent f5ed93e commit 496f877
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 6 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 08243ba731ee08ff42cf1589379c81567690218f
refs/heads/master: 4abdc6ee7c47a1a6e12f95717e461baeebee5df7
2 changes: 1 addition & 1 deletion trunk/drivers/ide/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
EXTRA_CFLAGS += -Idrivers/ide

ide-core-y += ide.o ide-ioctls.o ide-io.o ide-iops.o ide-lib.o ide-probe.o \
ide-taskfile.o ide-pio-blacklist.o
ide-taskfile.o ide-park.o ide-pio-blacklist.o

# core IDE code
ide-core-$(CONFIG_IDE_TIMINGS) += ide-timings.o
Expand Down
29 changes: 27 additions & 2 deletions trunk/drivers/ide/ide-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,32 @@ EXPORT_SYMBOL_GPL(ide_devset_execute);

static ide_startstop_t ide_special_rq(ide_drive_t *drive, struct request *rq)
{
switch (rq->cmd[0]) {
u8 cmd = rq->cmd[0];

if (cmd == REQ_PARK_HEADS || cmd == REQ_UNPARK_HEADS) {
ide_task_t task;
struct ide_taskfile *tf = &task.tf;

memset(&task, 0, sizeof(task));
if (cmd == REQ_PARK_HEADS) {
drive->sleep = *(unsigned long *)rq->special;
drive->dev_flags |= IDE_DFLAG_SLEEPING;
tf->command = ATA_CMD_IDLEIMMEDIATE;
tf->feature = 0x44;
tf->lbal = 0x4c;
tf->lbam = 0x4e;
tf->lbah = 0x55;
task.tf_flags |= IDE_TFLAG_CUSTOM_HANDLER;
} else /* cmd == REQ_UNPARK_HEADS */
tf->command = ATA_CMD_CHK_POWER;

task.tf_flags |= IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
task.rq = rq;
drive->hwif->data_phase = task.data_phase = TASKFILE_NO_DATA;
return do_rw_taskfile(drive, &task);
}

switch (cmd) {
case REQ_DEVSET_EXEC:
{
int err, (*setfunc)(ide_drive_t *, int) = rq->special;
Expand Down Expand Up @@ -1008,7 +1033,7 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
}
hwgroup->hwif = hwif;
hwgroup->drive = drive;
drive->dev_flags &= ~IDE_DFLAG_SLEEPING;
drive->dev_flags &= ~(IDE_DFLAG_SLEEPING | IDE_DFLAG_PARKED);
drive->service_start = jiffies;

if (blk_queue_plugged(drive->queue)) {
Expand Down
29 changes: 28 additions & 1 deletion trunk/drivers/ide/ide-iops.c
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,7 @@ static void ide_disk_pre_reset(ide_drive_t *drive)
drive->special.b.recalibrate = legacy;

drive->mult_count = 0;
drive->dev_flags &= ~IDE_DFLAG_PARKED;

if ((drive->dev_flags & IDE_DFLAG_KEEP_SETTINGS) == 0 &&
(drive->dev_flags & IDE_DFLAG_USING_DMA) == 0)
Expand Down Expand Up @@ -1079,12 +1080,13 @@ static void pre_reset(ide_drive_t *drive)
static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
{
unsigned int unit;
unsigned long flags;
unsigned long flags, timeout;
ide_hwif_t *hwif;
ide_hwgroup_t *hwgroup;
struct ide_io_ports *io_ports;
const struct ide_tp_ops *tp_ops;
const struct ide_port_ops *port_ops;
DEFINE_WAIT(wait);

spin_lock_irqsave(&ide_lock, flags);
hwif = HWIF(drive);
Expand All @@ -1111,6 +1113,31 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
return ide_started;
}

/* We must not disturb devices in the IDE_DFLAG_PARKED state. */
do {
unsigned long now;

prepare_to_wait(&ide_park_wq, &wait, TASK_UNINTERRUPTIBLE);
timeout = jiffies;
for (unit = 0; unit < MAX_DRIVES; unit++) {
ide_drive_t *tdrive = &hwif->drives[unit];

if (tdrive->dev_flags & IDE_DFLAG_PRESENT &&
tdrive->dev_flags & IDE_DFLAG_PARKED &&
time_after(tdrive->sleep, timeout))
timeout = tdrive->sleep;
}

now = jiffies;
if (time_before_eq(timeout, now))
break;

spin_unlock_irqrestore(&ide_lock, flags);
timeout = schedule_timeout_uninterruptible(timeout - now);
spin_lock_irqsave(&ide_lock, flags);
} while (timeout);
finish_wait(&ide_park_wq, &wait);

/*
* First, reset any device state data we were maintaining
* for any of the drives on this interface.
Expand Down
121 changes: 121 additions & 0 deletions trunk/drivers/ide/ide-park.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#include <linux/kernel.h>
#include <linux/ide.h>
#include <linux/jiffies.h>
#include <linux/blkdev.h>

DECLARE_WAIT_QUEUE_HEAD(ide_park_wq);

static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout)
{
struct request_queue *q = drive->queue;
struct request *rq;
int rc;

timeout += jiffies;
spin_lock_irq(&ide_lock);
if (drive->dev_flags & IDE_DFLAG_PARKED) {
ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
int reset_timer;

reset_timer = time_before(timeout, drive->sleep);
drive->sleep = timeout;
wake_up_all(&ide_park_wq);
if (reset_timer && hwgroup->sleeping &&
del_timer(&hwgroup->timer)) {
hwgroup->sleeping = 0;
hwgroup->busy = 0;
blk_start_queueing(q);
}
spin_unlock_irq(&ide_lock);
return;
}
spin_unlock_irq(&ide_lock);

rq = blk_get_request(q, READ, __GFP_WAIT);
rq->cmd[0] = REQ_PARK_HEADS;
rq->cmd_len = 1;
rq->cmd_type = REQ_TYPE_SPECIAL;
rq->special = &timeout;
rc = blk_execute_rq(q, NULL, rq, 1);
blk_put_request(rq);
if (rc)
goto out;

/*
* Make sure that *some* command is sent to the drive after the
* timeout has expired, so power management will be reenabled.
*/
rq = blk_get_request(q, READ, GFP_NOWAIT);
if (unlikely(!rq))
goto out;

rq->cmd[0] = REQ_UNPARK_HEADS;
rq->cmd_len = 1;
rq->cmd_type = REQ_TYPE_SPECIAL;
elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 1);

out:
return;
}

ssize_t ide_park_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
ide_drive_t *drive = to_ide_device(dev);
unsigned long now;
unsigned int msecs;

if (drive->dev_flags & IDE_DFLAG_NO_UNLOAD)
return -EOPNOTSUPP;

spin_lock_irq(&ide_lock);
now = jiffies;
if (drive->dev_flags & IDE_DFLAG_PARKED &&
time_after(drive->sleep, now))
msecs = jiffies_to_msecs(drive->sleep - now);
else
msecs = 0;
spin_unlock_irq(&ide_lock);

return snprintf(buf, 20, "%u\n", msecs);
}

ssize_t ide_park_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t len)
{
#define MAX_PARK_TIMEOUT 30000
ide_drive_t *drive = to_ide_device(dev);
long int input;
int rc;

rc = strict_strtol(buf, 10, &input);
if (rc || input < -2)
return -EINVAL;
if (input > MAX_PARK_TIMEOUT) {
input = MAX_PARK_TIMEOUT;
rc = -EOVERFLOW;
}

mutex_lock(&ide_setting_mtx);
if (input >= 0) {
if (drive->dev_flags & IDE_DFLAG_NO_UNLOAD)
rc = -EOPNOTSUPP;
else if (input || drive->dev_flags & IDE_DFLAG_PARKED)
issue_park_cmd(drive, msecs_to_jiffies(input));
} else {
if (drive->media == ide_disk)
switch (input) {
case -1:
drive->dev_flags &= ~IDE_DFLAG_NO_UNLOAD;
break;
case -2:
drive->dev_flags |= IDE_DFLAG_NO_UNLOAD;
break;
}
else
rc = -EOPNOTSUPP;
}
mutex_unlock(&ide_setting_mtx);

return rc ? rc : len;
}
5 changes: 5 additions & 0 deletions trunk/drivers/ide/ide-probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd)
drive->ready_stat = 0;
if (ata_id_cdb_intr(id))
drive->atapi_flags |= IDE_AFLAG_DRQ_INTERRUPT;
/* we don't do head unloading on ATAPI devices */
drive->dev_flags |= IDE_DFLAG_NO_UNLOAD;
return;
}

Expand All @@ -223,6 +225,9 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd)

drive->media = ide_disk;

if (!ata_id_has_unload(drive->id))
drive->dev_flags |= IDE_DFLAG_NO_UNLOAD;

printk(KERN_CONT "%s DISK drive\n", is_cfa ? "CFA" : "ATA");

return;
Expand Down
11 changes: 10 additions & 1 deletion trunk/drivers/ide/ide-taskfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,16 @@ static ide_startstop_t task_no_data_intr(ide_drive_t *drive)

if (!custom)
ide_end_drive_cmd(drive, stat, ide_read_error(drive));
else if (tf->command == ATA_CMD_SET_MULTI)
else if (tf->command == ATA_CMD_IDLEIMMEDIATE) {
hwif->tp_ops->tf_read(drive, task);
if (tf->lbal != 0xc4) {
printk(KERN_ERR "%s: head unload failed!\n",
drive->name);
ide_tf_dump(drive->name, tf);
} else
drive->dev_flags |= IDE_DFLAG_PARKED;
ide_end_drive_cmd(drive, stat, ide_read_error(drive));
} else if (tf->command == ATA_CMD_SET_MULTI)
drive->mult_count = drive->mult_req;

return ide_stopped;
Expand Down
1 change: 1 addition & 0 deletions trunk/drivers/ide/ide.c
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,7 @@ static struct device_attribute ide_dev_attrs[] = {
__ATTR_RO(model),
__ATTR_RO(firmware),
__ATTR(serial, 0400, serial_show, NULL),
__ATTR(unload_heads, 0644, ide_park_show, ide_park_store),
__ATTR_NULL
};

Expand Down
13 changes: 13 additions & 0 deletions trunk/include/linux/ide.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ enum {
*/
#define REQ_DRIVE_RESET 0x20
#define REQ_DEVSET_EXEC 0x21
#define REQ_PARK_HEADS 0x22
#define REQ_UNPARK_HEADS 0x23

/*
* Check for an interrupt and acknowledge the interrupt status
Expand Down Expand Up @@ -573,6 +575,10 @@ enum {
/* retrying in PIO */
IDE_DFLAG_DMA_PIO_RETRY = (1 << 25),
IDE_DFLAG_LBA = (1 << 26),
/* don't unload heads */
IDE_DFLAG_NO_UNLOAD = (1 << 27),
/* heads unloaded, please don't reset port */
IDE_DFLAG_PARKED = (1 << 28)
};

struct ide_drive_s {
Expand Down Expand Up @@ -1207,6 +1213,13 @@ int ide_check_atapi_device(ide_drive_t *, const char *);

void ide_init_pc(struct ide_atapi_pc *);

/* Disk head parking */
extern wait_queue_head_t ide_park_wq;
ssize_t ide_park_show(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t ide_park_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t len);

/*
* Special requests for ide-tape block device strategy routine.
*
Expand Down

0 comments on commit 496f877

Please sign in to comment.