From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

This patch seem to have been lost, so here it is again.  It fixes an Ooops
on unregistering hwifs due to the device model now having mandatory
release() functions.  It also close the possible race we had on release if
the entry was in use (by or /sys typically) by using a semaphore waiting
for the release() to be called after doing an unregister.

It also include the fix for the gendev parent that wasn't merged neither.



 drivers/ide/ide-probe.c |   18 ++++++++++++++++++
 drivers/ide/ide.c       |    5 +++++
 include/linux/ide.h     |    3 +++
 3 files changed, 26 insertions(+)

diff -puN drivers/ide/ide.c~hwifs-oops-unregister-fix drivers/ide/ide.c
--- 25/drivers/ide/ide.c~hwifs-oops-unregister-fix	2003-08-24 13:09:54.000000000 -0700
+++ 25-akpm/drivers/ide/ide.c	2003-08-24 13:09:54.000000000 -0700
@@ -255,6 +255,8 @@ static void init_hwif_data (unsigned int
 	hwif->mwdma_mask = 0x80;	/* disable all mwdma */
 	hwif->swdma_mask = 0x80;	/* disable all swdma */
 
+	sema_init(&hwif->gendev_rel_sem, 0);
+
 	default_hwif_iops(hwif);
 	default_hwif_transport(hwif);
 	for (unit = 0; unit < MAX_DRIVES; ++unit) {
@@ -277,6 +279,7 @@ static void init_hwif_data (unsigned int
 		drive->driver			= &idedefault_driver;
 		drive->vdma			= 0;
 		INIT_LIST_HEAD(&drive->list);
+		sema_init(&drive->gendev_rel_sem, 0);
 	}
 }
 
@@ -749,6 +752,7 @@ void ide_unregister (unsigned int index)
 		spin_unlock_irq(&ide_lock);
 		blk_cleanup_queue(drive->queue);
 		device_unregister(&drive->gendev);
+		down(&drive->gendev_rel_sem);
 		spin_lock_irq(&ide_lock);
 		drive->queue = NULL;
 	}
@@ -778,6 +782,7 @@ void ide_unregister (unsigned int index)
 	/* More messed up locking ... */
 	spin_unlock_irq(&ide_lock);
 	device_unregister(&hwif->gendev);
+	down(&hwif->gendev_rel_sem);
 
 	/*
 	 * Remove us from the kernel's knowledge
diff -puN drivers/ide/ide-probe.c~hwifs-oops-unregister-fix drivers/ide/ide-probe.c
--- 25/drivers/ide/ide-probe.c~hwifs-oops-unregister-fix	2003-08-24 13:09:54.000000000 -0700
+++ 25-akpm/drivers/ide/ide-probe.c	2003-08-24 13:09:54.000000000 -0700
@@ -644,15 +644,25 @@ static inline u8 probe_for_drive (ide_dr
 	return drive->present;
 }
 
+static void hwif_release_dev (struct device *dev)
+{
+	ide_hwif_t *hwif = container_of(dev, ide_hwif_t, gendev);
+
+	up(&hwif->gendev_rel_sem);
+}
+
 static void hwif_register (ide_hwif_t *hwif)
 {
 	/* register with global device tree */
 	strlcpy(hwif->gendev.bus_id,hwif->name,BUS_ID_SIZE);
 	hwif->gendev.driver_data = hwif;
+	if (hwif->gendev.parent == NULL) {
 	if (hwif->pci_dev)
 		hwif->gendev.parent = &hwif->pci_dev->dev;
 	else
 		hwif->gendev.parent = NULL; /* Would like to do = &device_legacy */
+	}
+	hwif->gendev.release = hwif_release_dev;
 	device_register(&hwif->gendev);
 }
 
@@ -1201,6 +1211,13 @@ Enomem:
 	return -ENOMEM;
 }
 
+static void drive_release_dev (struct device *dev)
+{
+	ide_drive_t *drive = container_of(dev, ide_drive_t, gendev);
+
+	up(&drive->gendev_rel_sem);
+}
+
 /*
  * init_gendisk() (as opposed to ide_geninit) is called for each major device,
  * after probing for drives, to allocate partition tables and other data
@@ -1219,6 +1236,7 @@ static void init_gendisk (ide_hwif_t *hw
 		drive->gendev.parent = &hwif->gendev;
 		drive->gendev.bus = &ide_bus_type;
 		drive->gendev.driver_data = drive;
+		drive->gendev.release = drive_release_dev;
 		if (drive->present) {
 			device_register(&drive->gendev);
 			sprintf(drive->devfs_name, "ide/host%d/bus%d/target%d/lun%d",
diff -puN include/linux/ide.h~hwifs-oops-unregister-fix include/linux/ide.h
--- 25/include/linux/ide.h~hwifs-oops-unregister-fix	2003-08-24 13:09:54.000000000 -0700
+++ 25-akpm/include/linux/ide.h	2003-08-24 13:09:54.000000000 -0700
@@ -22,6 +22,7 @@
 #include <asm/system.h>
 #include <asm/hdreg.h>
 #include <asm/io.h>
+#include <asm/semaphore.h>
 
 #define DEBUG_PM
 
@@ -774,6 +775,7 @@ typedef struct ide_drive_s {
 	int		crc_count;	/* crc counter to reduce drive speed */
 	struct list_head list;
 	struct device	gendev;
+	struct semaphore gendev_rel_sem;	/* to deal with device release() */
 	struct gendisk *disk;
 } ide_drive_t;
 
@@ -1040,6 +1042,7 @@ typedef struct hwif_s {
 	unsigned	auto_poll  : 1; /* supports nop auto-poll */
 
 	struct device	gendev;
+	struct semaphore gendev_rel_sem; /* To deal with device release() */
 
 	void		*hwif_data;	/* extra hwif data */
 

_