linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Parking hard disk head from drivers
@ 2006-06-22 19:59 Michael Hanselmann
  2006-06-22 20:05 ` Jeff Garzik
  0 siblings, 1 reply; 3+ messages in thread
From: Michael Hanselmann @ 2006-06-22 19:59 UTC (permalink / raw)
  To: linux-ide; +Cc: B.Zolnierkiewicz, axboe, jgarzik, linux-kernel

Hello

I'm working on a driver for the Apple Motion Sensor found in PowerBooks
from 2005 and MacBooks. The chip doing the real work gives an interrupt
when it detects a "freefall" or "shock". When that happens, Mac OS X
parks the internal hard disk's head to minimize the possible damage.

What would be the best and accepted way to implement the parking in the
Linux kernel? IMHO this has to be done in kernel space, because it has
to be instant.

My idea was to extend the genhd layer by a function to receive the
gendisk structure by the HD's name (foo("hda"), foo("sda")). This might
work, because the internal HD is always the first one. Another idea was
to do it in the ide-disk (PowerBooks, later iBooks) and libata code
(MacBooks), depending on which type is available.

Thanks,
Michael

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Parking hard disk head from drivers
  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
  0 siblings, 1 reply; 3+ messages in thread
From: Jeff Garzik @ 2006-06-22 20:05 UTC (permalink / raw)
  To: Michael Hanselmann; +Cc: linux-ide, B.Zolnierkiewicz, axboe, linux-kernel

Michael Hanselmann wrote:
> Hello
> 
> I'm working on a driver for the Apple Motion Sensor found in PowerBooks
> from 2005 and MacBooks. The chip doing the real work gives an interrupt
> when it detects a "freefall" or "shock". When that happens, Mac OS X
> parks the internal hard disk's head to minimize the possible damage.
> 
> What would be the best and accepted way to implement the parking in the
> Linux kernel? IMHO this has to be done in kernel space, because it has
> to be instant.
> 
> My idea was to extend the genhd layer by a function to receive the
> gendisk structure by the HD's name (foo("hda"), foo("sda")). This might
> work, because the internal HD is always the first one. Another idea was
> to do it in the ide-disk (PowerBooks, later iBooks) and libata code
> (MacBooks), depending on which type is available.

If I had to guess, I would say use a notifier...

	Jeff




^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Parking hard disk head from drivers
  2006-06-22 20:05 ` Jeff Garzik
@ 2006-07-01 10:01   ` Michael Hanselmann
  0 siblings, 0 replies; 3+ messages in thread
From: Michael Hanselmann @ 2006-07-01 10:01 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: linux-ide, B.Zolnierkiewicz, axboe, linux-kernel

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 {

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2006-07-01 10:01 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 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).