aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2007-05-08 03:29:39 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-08 14:15:10 -0400
commit49a4ec188f9a96c9a5567956718213d38a456a19 (patch)
tree29ac9f610ed355b3e3f752206c03180054df9bd7
parenteb81d93046e7de51d47b8f1303d80e6f51ac9e33 (diff)
fix hotplug for legacy platform drivers
We've had various reports of some legacy "probe the hardware" style platform drivers having nasty problems with hotplug support. The core issue is that those legacy drivers don't fully conform to the driver model. They assume a role that should be the responsibility of infrastructure code: creating device nodes. The "modprobe" step in hotplugging relies on drivers to have split those roles into different modules. The lack of this split causes the problems. When a driver creates nodes for devices that don't exist (sending a hotplug event), then exits (aborting one modprobe) before the "modprobe $MODALIAS" step completes (by failing, since it's in the middle of a modprobe), the result can be an endless loop of modprobe invocations ... badness. This fix uses the newish per-device flag controlling issuance of "add" events. (A previous version of this patch used a per-device "driver can hotplug" flag, which only scrubbed $MODALIAS from the environment rather than suppressing the entire hotplug event.) It also shrinks that flag to one bit, saving a word in "struct device". So the net of this patch is removing some nasty failures with legacy drivers, while retaining hotplug capability for the majority of platform drivers. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Cc: Greg KH <gregkh@suse.de> Cc: Andres Salomon <dilinger@debian.org> Cc: Dominik Brodowski <linux@dominikbrodowski.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/base/platform.c18
-rw-r--r--drivers/pcmcia/pxa2xx_mainstone.c1
-rw-r--r--drivers/pcmcia/pxa2xx_sharpsl.c1
-rw-r--r--include/linux/device.h4
4 files changed, 22 insertions, 2 deletions
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 17b5ece8f82c..eb84d9d44645 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -160,6 +160,11 @@ static void platform_device_release(struct device *dev)
160 * 160 *
161 * Create a platform device object which can have other objects attached 161 * Create a platform device object which can have other objects attached
162 * to it, and which will have attached objects freed when it is released. 162 * to it, and which will have attached objects freed when it is released.
163 *
164 * This device will be marked as not supporting hotpluggable drivers; no
165 * device add/remove uevents will be generated. In the unusual case that
166 * the device isn't being dynamically allocated as a legacy "probe the
167 * hardware" driver, infrastructure code should reverse this marking.
163 */ 168 */
164struct platform_device *platform_device_alloc(const char *name, unsigned int id) 169struct platform_device *platform_device_alloc(const char *name, unsigned int id)
165{ 170{
@@ -172,6 +177,12 @@ struct platform_device *platform_device_alloc(const char *name, unsigned int id)
172 pa->pdev.id = id; 177 pa->pdev.id = id;
173 device_initialize(&pa->pdev.dev); 178 device_initialize(&pa->pdev.dev);
174 pa->pdev.dev.release = platform_device_release; 179 pa->pdev.dev.release = platform_device_release;
180
181 /* prevent hotplug "modprobe $(MODALIAS)" from causing trouble in
182 * legacy probe-the-hardware drivers, which don't properly split
183 * out device enumeration logic from drivers.
184 */
185 pa->pdev.dev.uevent_suppress = 1;
175 } 186 }
176 187
177 return pa ? &pa->pdev : NULL; 188 return pa ? &pa->pdev : NULL;
@@ -351,6 +362,13 @@ EXPORT_SYMBOL_GPL(platform_device_unregister);
351 * memory allocated for the device allows drivers using such devices 362 * memory allocated for the device allows drivers using such devices
352 * to be unloaded iwithout waiting for the last reference to the device 363 * to be unloaded iwithout waiting for the last reference to the device
353 * to be dropped. 364 * to be dropped.
365 *
366 * This interface is primarily intended for use with legacy drivers
367 * which probe hardware directly. Because such drivers create sysfs
368 * device nodes themselves, rather than letting system infrastructure
369 * handle such device enumeration tasks, they don't fully conform to
370 * the Linux driver model. In particular, when such drivers are built
371 * as modules, they can't be "hotplugged".
354 */ 372 */
355struct platform_device *platform_device_register_simple(char *name, unsigned int id, 373struct platform_device *platform_device_register_simple(char *name, unsigned int id,
356 struct resource *res, unsigned int num) 374 struct resource *res, unsigned int num)
diff --git a/drivers/pcmcia/pxa2xx_mainstone.c b/drivers/pcmcia/pxa2xx_mainstone.c
index fda06941e730..383107ba4bd3 100644
--- a/drivers/pcmcia/pxa2xx_mainstone.c
+++ b/drivers/pcmcia/pxa2xx_mainstone.c
@@ -175,6 +175,7 @@ static int __init mst_pcmcia_init(void)
175 if (!mst_pcmcia_device) 175 if (!mst_pcmcia_device)
176 return -ENOMEM; 176 return -ENOMEM;
177 177
178 mst_pcmcia_device->dev.uevent_suppress = 0;
178 mst_pcmcia_device->dev.platform_data = &mst_pcmcia_ops; 179 mst_pcmcia_device->dev.platform_data = &mst_pcmcia_ops;
179 180
180 ret = platform_device_add(mst_pcmcia_device); 181 ret = platform_device_add(mst_pcmcia_device);
diff --git a/drivers/pcmcia/pxa2xx_sharpsl.c b/drivers/pcmcia/pxa2xx_sharpsl.c
index b7b9e149c5b9..a2daa3f531b2 100644
--- a/drivers/pcmcia/pxa2xx_sharpsl.c
+++ b/drivers/pcmcia/pxa2xx_sharpsl.c
@@ -261,6 +261,7 @@ static int __init sharpsl_pcmcia_init(void)
261 if (!sharpsl_pcmcia_device) 261 if (!sharpsl_pcmcia_device)
262 return -ENOMEM; 262 return -ENOMEM;
263 263
264 sharpsl_pcmcia_device->dev.uevent_suppress = 0;
264 sharpsl_pcmcia_device->dev.platform_data = &sharpsl_pcmcia_ops; 265 sharpsl_pcmcia_device->dev.platform_data = &sharpsl_pcmcia_ops;
265 sharpsl_pcmcia_device->dev.parent = platform_scoop_config->devs[0].dev; 266 sharpsl_pcmcia_device->dev.parent = platform_scoop_config->devs[0].dev;
266 267
diff --git a/include/linux/device.h b/include/linux/device.h
index 6579068134d1..2e1a2988b7e1 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -412,12 +412,13 @@ struct device {
412 struct klist_node knode_parent; /* node in sibling list */ 412 struct klist_node knode_parent; /* node in sibling list */
413 struct klist_node knode_driver; 413 struct klist_node knode_driver;
414 struct klist_node knode_bus; 414 struct klist_node knode_bus;
415 struct device * parent; 415 struct device *parent;
416 416
417 struct kobject kobj; 417 struct kobject kobj;
418 char bus_id[BUS_ID_SIZE]; /* position on parent bus */ 418 char bus_id[BUS_ID_SIZE]; /* position on parent bus */
419 struct device_type *type; 419 struct device_type *type;
420 unsigned is_registered:1; 420 unsigned is_registered:1;
421 unsigned uevent_suppress:1;
421 struct device_attribute uevent_attr; 422 struct device_attribute uevent_attr;
422 struct device_attribute *devt_attr; 423 struct device_attribute *devt_attr;
423 424
@@ -458,7 +459,6 @@ struct device {
458 struct class *class; 459 struct class *class;
459 dev_t devt; /* dev_t, creates the sysfs "dev" */ 460 dev_t devt; /* dev_t, creates the sysfs "dev" */
460 struct attribute_group **groups; /* optional groups */ 461 struct attribute_group **groups; /* optional groups */
461 int uevent_suppress;
462 462
463 void (*release)(struct device * dev); 463 void (*release)(struct device * dev);
464}; 464};