Commit 49a4ec188f9a96c9a5567956718213d38a456a19

Authored by David Brownell
Committed by Linus Torvalds
1 parent eb81d93046

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>

Showing 4 changed files with 22 additions and 2 deletions Side-by-side Diff

drivers/base/platform.c
... ... @@ -160,6 +160,11 @@
160 160 *
161 161 * Create a platform device object which can have other objects attached
162 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 */
164 169 struct platform_device *platform_device_alloc(const char *name, unsigned int id)
165 170 {
... ... @@ -172,6 +177,12 @@
172 177 pa->pdev.id = id;
173 178 device_initialize(&pa->pdev.dev);
174 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 188 return pa ? &pa->pdev : NULL;
... ... @@ -351,6 +362,13 @@
351 362 * memory allocated for the device allows drivers using such devices
352 363 * to be unloaded iwithout waiting for the last reference to the device
353 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 */
355 373 struct platform_device *platform_device_register_simple(char *name, unsigned int id,
356 374 struct resource *res, unsigned int num)
drivers/pcmcia/pxa2xx_mainstone.c
... ... @@ -175,6 +175,7 @@
175 175 if (!mst_pcmcia_device)
176 176 return -ENOMEM;
177 177  
  178 + mst_pcmcia_device->dev.uevent_suppress = 0;
178 179 mst_pcmcia_device->dev.platform_data = &mst_pcmcia_ops;
179 180  
180 181 ret = platform_device_add(mst_pcmcia_device);
drivers/pcmcia/pxa2xx_sharpsl.c
... ... @@ -261,6 +261,7 @@
261 261 if (!sharpsl_pcmcia_device)
262 262 return -ENOMEM;
263 263  
  264 + sharpsl_pcmcia_device->dev.uevent_suppress = 0;
264 265 sharpsl_pcmcia_device->dev.platform_data = &sharpsl_pcmcia_ops;
265 266 sharpsl_pcmcia_device->dev.parent = platform_scoop_config->devs[0].dev;
266 267  
include/linux/device.h
... ... @@ -412,12 +412,13 @@
412 412 struct klist_node knode_parent; /* node in sibling list */
413 413 struct klist_node knode_driver;
414 414 struct klist_node knode_bus;
415   - struct device * parent;
  415 + struct device *parent;
416 416  
417 417 struct kobject kobj;
418 418 char bus_id[BUS_ID_SIZE]; /* position on parent bus */
419 419 struct device_type *type;
420 420 unsigned is_registered:1;
  421 + unsigned uevent_suppress:1;
421 422 struct device_attribute uevent_attr;
422 423 struct device_attribute *devt_attr;
423 424  
... ... @@ -458,7 +459,6 @@
458 459 struct class *class;
459 460 dev_t devt; /* dev_t, creates the sysfs "dev" */
460 461 struct attribute_group **groups; /* optional groups */
461   - int uevent_suppress;
462 462  
463 463 void (*release)(struct device * dev);
464 464 };