From: Michael Hanselmann <linux-kernel@hansmi.ch>
To: Jeff Garzik <jgarzik@pobox.com>
Cc: linux-ide@vger.kernel.org, B.Zolnierkiewicz@elka.pw.edu.pl,
axboe@suse.de, linux-kernel@killerfox.forkbomb.ch
Subject: Re: Parking hard disk head from drivers
Date: Sat, 1 Jul 2006 12:01:16 +0200 [thread overview]
Message-ID: <20060701100116.GA1752@hansmi.ch> (raw)
In-Reply-To: <449AF804.4080205@pobox.com>
On Thu, Jun 22, 2006 at 04:05:24PM -0400, Jeff Garzik wrote:
> If I had to guess, I would say use a notifier...
Thanks, that way I finally came up with something.
Below you find the HD parking part of my driver. I'll be really thankful
for any comments, suggestions and (constructive) critics on it. It's not
like I fully understood what the IDE layer does, but I looked at the
power management code and implemented something like it.
It does what it should on my PowerBook, but maybe it's totally wrong.
---
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17.orig/block/hdpark.c linux-2.6.17/block/hdpark.c
--- linux-2.6.17.orig/block/hdpark.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.17/block/hdpark.c 2006-07-01 01:23:39.000000000 +0200
@@ -0,0 +1,60 @@
+/*
+ * Generic code for hard disk head parking
+ *
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/hdpark.h>
+#include <asm/atomic.h>
+
+static BLOCKING_NOTIFIER_HEAD(hdpark_notifier_list);
+static atomic_t hdpark_nested = ATOMIC_INIT(0);
+
+int register_hdpark_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&hdpark_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(register_hdpark_notifier);
+
+int unregister_hdpark_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&hdpark_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_hdpark_notifier);
+
+/* Parks the head of all registered hard disks */
+int hdpark_start()
+{
+ if (atomic_inc_return(&hdpark_nested) == 1) {
+ int err = blocking_notifier_call_chain(&hdpark_notifier_list,
+ HDPARK_ACTION_START, NULL);
+ if (err == NOTIFY_BAD)
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hdpark_start);
+
+/* Reactivates the registered hard disks */
+int hdpark_end()
+{
+ if (atomic_dec_and_test(&hdpark_nested)) {
+ int err = blocking_notifier_call_chain(&hdpark_notifier_list,
+ HDPARK_ACTION_END, NULL);
+ if (err == NOTIFY_BAD)
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hdpark_end);
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17.orig/block/Kconfig linux-2.6.17/block/Kconfig
--- linux-2.6.17.orig/block/Kconfig 2006-06-19 21:47:12.000000000 +0200
+++ linux-2.6.17/block/Kconfig 2006-06-27 00:19:53.000000000 +0200
@@ -33,4 +33,7 @@ config LSF
If unsure, say Y.
+config HDPARK
+ bool ""
+
source block/Kconfig.iosched
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17.orig/block/Makefile linux-2.6.17/block/Makefile
--- linux-2.6.17.orig/block/Makefile 2006-06-19 21:47:12.000000000 +0200
+++ linux-2.6.17/block/Makefile 2006-06-27 00:18:29.000000000 +0200
@@ -10,3 +10,4 @@ obj-$(CONFIG_IOSCHED_DEADLINE) += deadli
obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o
obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o
+obj-$(CONFIG_HDPARK) += hdpark.o
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17.orig/drivers/ide/ide-disk.c linux-2.6.17/drivers/ide/ide-disk.c
--- linux-2.6.17.orig/drivers/ide/ide-disk.c 2006-06-19 21:47:08.000000000 +0200
+++ linux-2.6.17/drivers/ide/ide-disk.c 2006-07-01 00:56:37.000000000 +0200
@@ -62,6 +62,7 @@
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/leds.h>
+#include <linux/hdpark.h>
#define _IDE_DISK
@@ -108,6 +109,85 @@ static void ide_disk_put(struct ide_disk
mutex_unlock(&idedisk_ref_mutex);
}
+#ifdef CONFIG_HDPARK
+/* This function is called when something should be done in regard to head
+ * parking.
+ */
+static int ide_disk_park_callback(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ ide_drive_t *drive = nb->private_data;
+ ide_task_t args;
+ unsigned long flags;
+
+ switch (action) {
+ case HDPARK_ACTION_START:
+ memset(&args, 0, sizeof(ide_task_t));
+
+ /* Command to put HD into idle mode */
+ args.tfRegister[IDE_COMMAND_OFFSET] = WIN_IDLEIMMEDIATE;
+ args.command_type = IDE_DRIVE_TASK_NO_DATA;
+ args.handler = &task_no_data_intr;
+
+ /* Ignore return value. If the HD fails to park that's bad, but
+ * we can't do anything about it.
+ */
+ ide_raw_taskfile(drive, &args, NULL);
+
+ /* At least block the queue */
+ spin_lock_irqsave(&ide_lock, flags);
+ drive->blocked = 1;
+ blk_stop_queue(drive->queue);
+ spin_unlock_irqrestore(&ide_lock, flags);
+
+ break;
+
+ case HDPARK_ACTION_END:
+ /* Unlock the queue */
+ spin_lock_irqsave(&ide_lock, flags);
+ drive->blocked = 0;
+ blk_start_queue(drive->queue);
+ spin_unlock_irqrestore(&ide_lock, flags);
+
+ break;
+ }
+
+ /* We don't care wether the park command actually succeeded */
+ return NOTIFY_DONE;
+}
+
+/* Registers a drive on the global hd parking notifier list */
+static void ide_disk_park_init(ide_drive_t *drive)
+{
+ struct notifier_block *nb;
+
+ nb = kzalloc(sizeof(struct notifier_block), GFP_KERNEL);
+ if (likely(nb)) {
+ nb->notifier_call = ide_disk_park_callback;
+ nb->private_data = drive;
+
+ register_hdpark_notifier(nb);
+ } else
+ printk(KERN_ERR "ide-disk: hdpark notifier registration failed.\n");
+
+ drive->park_notifier = nb;
+}
+
+/* Unregisters a drive from the global hd parking notifier list */
+static void ide_disk_park_exit(ide_drive_t *drive)
+{
+ if (likely(drive->park_notifier)) {
+ unregister_hdpark_notifier(drive->park_notifier);
+
+ kfree(drive->park_notifier);
+ drive->park_notifier = NULL;
+ }
+}
+#else
+static inline void ide_disk_park_init(ide_drive_t *drive) {}
+static inline void ide_disk_park_exit(ide_drive_t *drive) {}
+#endif
+
/*
* lba_capacity_is_ok() performs a sanity check on the claimed "lba_capacity"
* value for this drive (from its reported identification information).
@@ -1002,6 +1082,8 @@ static void ide_disk_remove(ide_drive_t
struct ide_disk_obj *idkp = drive->driver_data;
struct gendisk *g = idkp->disk;
+ ide_disk_park_exit(drive);
+
ide_unregister_subdriver(drive, idkp->driver);
del_gendisk(g);
@@ -1228,6 +1310,9 @@ static int ide_disk_probe(ide_drive_t *d
set_capacity(g, idedisk_capacity(drive));
g->fops = &idedisk_ops;
add_disk(g);
+
+ ide_disk_park_init(drive);
+
return 0;
out_free_idkp:
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17.orig/include/linux/hdpark.h linux-2.6.17/include/linux/hdpark.h
--- linux-2.6.17.orig/include/linux/hdpark.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.17/include/linux/hdpark.h 2006-06-30 23:07:22.000000000 +0200
@@ -0,0 +1,22 @@
+/*
+ * Header for block/hdpark.c -- generic code for hard disk head parking
+ *
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
+ */
+
+#ifndef _LINUX_HDPARK_H
+#define _LINUX_HDPAKR_H
+
+#include <linux/notifier.h>
+
+enum hdpark_action {
+ HDPARK_ACTION_START = 1,
+ HDPARK_ACTION_END = 2,
+};
+
+extern int register_hdpark_notifier(struct notifier_block *nb);
+extern int unregister_hdpark_notifier(struct notifier_block *nb);
+extern int hdpark_start(void);
+extern int hdpark_end(void);
+
+#endif
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17.orig/include/linux/ide.h linux-2.6.17/include/linux/ide.h
--- linux-2.6.17.orig/include/linux/ide.h 2006-06-19 21:47:13.000000000 +0200
+++ linux-2.6.17/include/linux/ide.h 2006-06-27 00:32:08.000000000 +0200
@@ -639,6 +639,10 @@ typedef struct ide_drive_s {
struct list_head list;
struct device gendev;
struct completion gendev_rel_comp; /* to deal with device release() */
+
+#ifdef CONFIG_HDPARK
+ void *park_notifier;
+#endif
} ide_drive_t;
#define to_ide_device(dev)container_of(dev, ide_drive_t, gendev)
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17.orig/include/linux/notifier.h linux-2.6.17/include/linux/notifier.h
--- linux-2.6.17.orig/include/linux/notifier.h 2006-06-19 21:47:13.000000000 +0200
+++ linux-2.6.17/include/linux/notifier.h 2006-06-27 00:38:10.000000000 +0200
@@ -36,6 +36,7 @@ struct notifier_block {
int (*notifier_call)(struct notifier_block *, unsigned long, void *);
struct notifier_block *next;
int priority;
+ void *private_data;
};
struct atomic_notifier_head {
prev parent reply other threads:[~2006-07-01 10:01 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-06-22 19:59 Parking hard disk head from drivers Michael Hanselmann
2006-06-22 20:05 ` Jeff Garzik
2006-07-01 10:01 ` Michael Hanselmann [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20060701100116.GA1752@hansmi.ch \
--to=linux-kernel@hansmi.ch \
--cc=B.Zolnierkiewicz@elka.pw.edu.pl \
--cc=axboe@suse.de \
--cc=jgarzik@pobox.com \
--cc=linux-ide@vger.kernel.org \
--cc=linux-kernel@killerfox.forkbomb.ch \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).