Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 1888

kernel-2.6.18-238.el5.src.rpm

From: mchristi@redhat.com <mchristi@redhat.com>
Date: Thu, 28 Aug 2008 13:43:32 -0500
Subject: [misc] driver core: port bus notifiers
Message-id: 1219949016-15055-2-git-send-email-mchristi@redhat.com
O-Subject: [RHEL 5.3 PATCH 1/5] driver core: port bus notifiers
Bugzilla: 438761
RH-Acked-by: Doug Ledford <dledford@redhat.com>

From: Mike Christie <mchristi@redhat.com>

RHEL 5.2 doesn't have support for bus notifications, which is needed
for SCSI Hardware Handlers. This ports them to with kabi workarounds.
Patch checked with check-kabi patch.

Upstream commit:
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=116af378201ef793424cd10508ccf18b06d8a021
The bus_type_nb related code is a workaround for KABI
where for upstream the bus_notifier field was added to the bus_type:

struct bus_type {
        struct klist            klist_devices;
        struct klist            klist_drivers;

	struct blocking_notifier_head bus_notifier;

and we could not add it.

diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 420edd1..2643bf8 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -671,6 +671,78 @@ static void klist_drivers_put(struct klist_node *n)
 	put_driver(drv);
 }
 
+#define NUM_BUCKETS 64
+#define MASK_BUCKETS (NUM_BUCKETS - 1)
+static struct list_head _bus_buckets[NUM_BUCKETS];
+struct bus_type_nb {
+	struct list_head list;
+	struct bus_type *bus;
+	struct blocking_notifier_head notifier;
+};
+
+static void init_buckets(struct list_head *buckets)
+{
+	unsigned int i;
+
+	for (i = 0; i < NUM_BUCKETS; i++)
+		INIT_LIST_HEAD(buckets + i);
+}
+
+static unsigned int hash_str(const void *bus)
+{
+	const unsigned int hash_mult = 2654435387U;
+	unsigned int h = 0;
+	char *str = (char *) &bus;
+	int i;
+
+	for (i = 0; i < sizeof(void *); i++)
+		h = (h + (unsigned int) str[i]) * hash_mult;
+
+	return h & MASK_BUCKETS;
+}
+
+static struct bus_type_nb *__get_cell(const void *bus)
+{
+	struct bus_type_nb *cell;
+	unsigned int h = hash_str(bus);
+
+	list_for_each_entry(cell, _bus_buckets + h, list)
+		if (cell->bus == bus)
+			return cell;
+	return NULL;
+}
+
+static struct blocking_notifier_head *alloc_save_notifier_for_bus(struct bus_type *bus)
+{
+	struct bus_type_nb *cell = __get_cell(bus);
+
+	if (cell) {
+		printk(KERN_ERR "bus %p trying to reallocate notifier head\n", bus);
+		return NULL;
+	}
+	cell = kzalloc(sizeof(*cell), GFP_KERNEL);
+	if (!cell)
+		return NULL;
+	cell->bus = bus;
+	list_add(&cell->list, _bus_buckets + hash_str(bus));
+	return &cell->notifier;
+}
+
+static void free_notifier_for_bus(struct bus_type *bus)
+{
+	struct bus_type_nb *cell = __get_cell(bus);
+	if (cell) {
+		list_del(&cell->list);
+		kfree(cell);
+	}
+}
+
+struct blocking_notifier_head *get_notifier_for_bus(struct bus_type *bus)
+{
+	struct bus_type_nb *cell = __get_cell(bus);
+	return cell ? &cell->notifier : NULL;
+}
+
 /**
  *	bus_register - register a bus with the system.
  *	@bus:	bus.
@@ -681,7 +753,14 @@ static void klist_drivers_put(struct klist_node *n)
  */
 int bus_register(struct bus_type * bus)
 {
-	int retval;
+	int retval = -ENOMEM;
+	struct blocking_notifier_head *notifier_head;
+
+	notifier_head = alloc_save_notifier_for_bus(bus);
+	if (!notifier_head)
+		goto out;
+
+	BLOCKING_INIT_NOTIFIER_HEAD(notifier_head);
 
 	retval = kobject_set_name(&bus->subsys.kset.kobj, "%s", bus->name);
 	if (retval)
@@ -720,6 +799,27 @@ out:
 	return retval;
 }
 
+int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb)
+{
+	struct blocking_notifier_head *notifier_head;
+
+	notifier_head = get_notifier_for_bus(bus);
+	if (!notifier_head)
+		return 0;
+	return blocking_notifier_chain_register(notifier_head, nb);
+}
+EXPORT_SYMBOL_GPL(bus_register_notifier);
+
+int bus_unregister_notifier(struct bus_type *bus, struct notifier_block *nb)
+{
+	struct blocking_notifier_head *notifier_head;
+
+	notifier_head = get_notifier_for_bus(bus);
+	if (!notifier_head)
+		return 0;
+	return blocking_notifier_chain_unregister(notifier_head, nb);
+}
+EXPORT_SYMBOL_GPL(bus_unregister_notifier);
 
 /**
  *	bus_unregister - remove a bus from the system
@@ -731,6 +831,7 @@ out:
 void bus_unregister(struct bus_type * bus)
 {
 	pr_debug("bus %s: unregistering\n", bus->name);
+	free_notifier_for_bus(bus);
 	bus_remove_attrs(bus);
 	kset_unregister(&bus->drivers);
 	kset_unregister(&bus->devices);
@@ -739,10 +840,10 @@ void bus_unregister(struct bus_type * bus)
 
 int __init buses_init(void)
 {
+	init_buckets(_bus_buckets);
 	return subsystem_register(&bus_subsys);
 }
 
-
 EXPORT_SYMBOL_GPL(bus_for_each_dev);
 EXPORT_SYMBOL_GPL(bus_find_device);
 EXPORT_SYMBOL_GPL(bus_for_each_drv);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index fe17f00..923ebef 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -309,6 +309,16 @@ int device_add(struct device *dev)
 	if ((error = kobject_add(&dev->kobj)))
 		goto Error;
 
+	/* notify clients of device entry (new way) */
+	if (dev->bus) {
+		struct blocking_notifier_head *notifier_head;
+
+		notifier_head = get_notifier_for_bus(dev->bus);
+		if (notifier_head)
+			blocking_notifier_call_chain(notifier_head,
+						BUS_NOTIFY_ADD_DEVICE, dev);
+	}
+
 	dev->uevent_attr.attr.name = "uevent";
 	dev->uevent_attr.attr.mode = S_IWUSR;
 	if (dev->driver)
@@ -368,6 +378,7 @@ int device_add(struct device *dev)
 	/* notify platform of device entry */
 	if (platform_notify)
 		platform_notify(dev);
+
  Done:
  	kfree(class_name);
 	put_device(dev);
@@ -487,6 +498,15 @@ void device_del(struct device * dev)
 	if (platform_notify_remove)
 		platform_notify_remove(dev);
 
+	if (dev->bus) {
+		struct blocking_notifier_head *notifier_head;
+
+		notifier_head = get_notifier_for_bus(dev->bus);
+		if (notifier_head)
+			blocking_notifier_call_chain(notifier_head,
+						BUS_NOTIFY_DEL_DEVICE, dev);
+	}
+
 	device_pm_remove(dev);
 	kobject_uevent(&dev->kobj, KOBJ_REMOVE);
 	kobject_del(&dev->kobj);
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index e9c2e4c..bce7b65 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -45,6 +45,15 @@ void device_bind_driver(struct device * dev)
 
 	pr_debug("bound device '%s' to driver '%s'\n",
 		 dev->bus_id, dev->driver->name);
+	if (dev->bus) {
+		struct blocking_notifier_head *notifier_head;
+
+		notifier_head = get_notifier_for_bus(dev->bus);
+		if (notifier_head)
+			blocking_notifier_call_chain(notifier_head,
+						BUS_NOTIFY_BOUND_DRIVER, dev);
+	}
+
 	klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices);
 	sysfs_create_link(&dev->driver->kobj, &dev->kobj,
 			  kobject_name(&dev->kobj));
@@ -212,6 +221,14 @@ static void __device_release_driver(struct device * dev)
 		sysfs_remove_link(&dev->kobj, "driver");
 		klist_remove(&dev->knode_driver);
 
+		if (dev->bus) {
+			struct blocking_notifier_head *notifier_head;
+
+			notifier_head = get_notifier_for_bus(dev->bus);
+			if (notifier_head)
+				blocking_notifier_call_chain(notifier_head,
+						BUS_NOTIFY_UNBIND_DRIVER, dev);
+		}
 		if (dev->bus && dev->bus->remove)
 			dev->bus->remove(dev);
 		else if (drv->remove)
diff --git a/include/linux/device.h b/include/linux/device.h
index 2a00e3a..d561172 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -71,6 +71,29 @@ int bus_for_each_drv(struct bus_type * bus, struct device_driver * start,
 		     void * data, int (*fn)(struct device_driver *, void *));
 
 
+/*
+ * Bus notifiers: Get notified of addition/removal of devices
+ * and binding/unbinding of drivers to devices.
+ * In the long run, it should be a replacement for the platform
+ * notify hooks.
+ */
+struct notifier_block;
+
+extern int bus_register_notifier(struct bus_type *bus,
+					struct notifier_block *nb);
+extern int bus_unregister_notifier(struct bus_type *bus,
+					struct notifier_block *nb);
+extern struct blocking_notifier_head *get_notifier_for_bus(struct bus_type *bus);
+
+/* All 4 notifers below get called with the target struct device *
+ * as an argument. Note that those functions are likely to be called
+ * with the device semaphore held in the core, so be careful.
+ */
+#define BUS_NOTIFY_ADD_DEVICE		0x00000001 /* device added */
+#define BUS_NOTIFY_DEL_DEVICE		0x00000002 /* device removed */
+#define BUS_NOTIFY_BOUND_DRIVER		0x00000003 /* driver bound to device */
+#define BUS_NOTIFY_UNBIND_DRIVER	0x00000004 /* about to be unbound */
+
 /* driverfs interface for exporting bus attributes */
 
 struct bus_attribute {