Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

driver core: correct device's shutdown order

Now device's shutdown sequence is performed in reverse order of their
registration in devices_kset list and this sequence corresponds to the
reverse device's creation order. So, devices_kset data tracks
"parent<-child" device's dependencies only.

Unfortunately, that's not enough and causes problems in case of
implementing board's specific shutdown procedures. For example [1]:
"DRA7XX_evm uses PCF8575 and one of the PCF output lines feeds to
MMC/SD and this line should be driven high in order for the MMC/SD to
be detected. This line is modelled as regulator and the hsmmc driver
takes care of enabling and disabling it. In the case of 'reboot',
during shutdown path as part of it's cleanup process the hsmmc driver
disables this regulator. This makes MMC boot not functional."

To handle this issue the .shutdown() callback could be implemented
for PCF8575 device where corresponding GPIO pins will be configured to
states, required for correct warm/cold reset. This can be achieved
only when all .shutdown() callbacks have been called already for all
PCF8575's consumers. But devices_kset is not filled correctly now:

devices_kset: Device61 4e000000.dmm
devices_kset: Device62 48070000.i2c
devices_kset: Device63 48072000.i2c
devices_kset: Device64 48060000.i2c
devices_kset: Device65 4809c000.mmc
...
devices_kset: Device102 fixedregulator-sd
...
devices_kset: Device181 0-0020 // PCF8575
devices_kset: Device182 gpiochip496
devices_kset: Device183 0-0021 // PCF8575
devices_kset: Device184 gpiochip480

As can be seen from above .shutdown() callback for PCF8575 will be called
before its consumers, which, in turn means, that any changes of PCF8575
GPIO's pins will be or unsafe or overwritten later by GPIO's consumers.
The problem can be solved if devices_kset list will be filled not only
according device creation order, but also according device's probing
order to track "supplier<-consumer" dependencies also.

Hence, as a fix, lets add devices_kset_move_last(),
devices_kset_move_before(), devices_kset_move_after() and call them
from device_move() and also add call of devices_kset_move_last() in
really_probe(). After this change all entries in devices_kset will
be sorted according to device's creation ("parent<-child") and
probing ("supplier<-consumer") order.

devices_kset after:
devices_kset: Device121 48070000.i2c
devices_kset: Device122 i2c-0
...
devices_kset: Device147 regulator.24
devices_kset: Device148 0-0020
devices_kset: Device149 gpiochip496
devices_kset: Device150 0-0021
devices_kset: Device151 gpiochip480
devices_kset: Device152 0-0019
...
devices_kset: Device372 fixedregulator-sd
devices_kset: Device373 regulator.29
devices_kset: Device374 4809c000.mmc
devices_kset: Device375 mmc0

[1] http://www.spinics.net/lists/linux-mmc/msg29825.html

Cc: Sekhar Nori <nsekhar@ti.com>
Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Grygorii Strashko and committed by
Greg Kroah-Hartman
52cdbdd4 82b2c3c5

+58
+1
drivers/base/base.h
··· 134 134 135 135 /* /sys/devices directory */ 136 136 extern struct kset *devices_kset; 137 + extern void devices_kset_move_last(struct device *dev); 137 138 138 139 #if defined(CONFIG_MODULES) && defined(CONFIG_SYSFS) 139 140 extern void module_add_driver(struct module *mod, struct device_driver *drv);
+49
drivers/base/core.c
··· 534 534 struct kset *devices_kset; 535 535 536 536 /** 537 + * devices_kset_move_before - Move device in the devices_kset's list. 538 + * @deva: Device to move. 539 + * @devb: Device @deva should come before. 540 + */ 541 + static void devices_kset_move_before(struct device *deva, struct device *devb) 542 + { 543 + if (!devices_kset) 544 + return; 545 + pr_debug("devices_kset: Moving %s before %s\n", 546 + dev_name(deva), dev_name(devb)); 547 + spin_lock(&devices_kset->list_lock); 548 + list_move_tail(&deva->kobj.entry, &devb->kobj.entry); 549 + spin_unlock(&devices_kset->list_lock); 550 + } 551 + 552 + /** 553 + * devices_kset_move_after - Move device in the devices_kset's list. 554 + * @deva: Device to move 555 + * @devb: Device @deva should come after. 556 + */ 557 + static void devices_kset_move_after(struct device *deva, struct device *devb) 558 + { 559 + if (!devices_kset) 560 + return; 561 + pr_debug("devices_kset: Moving %s after %s\n", 562 + dev_name(deva), dev_name(devb)); 563 + spin_lock(&devices_kset->list_lock); 564 + list_move(&deva->kobj.entry, &devb->kobj.entry); 565 + spin_unlock(&devices_kset->list_lock); 566 + } 567 + 568 + /** 569 + * devices_kset_move_last - move the device to the end of devices_kset's list. 570 + * @dev: device to move 571 + */ 572 + void devices_kset_move_last(struct device *dev) 573 + { 574 + if (!devices_kset) 575 + return; 576 + pr_debug("devices_kset: Moving %s to end of list\n", dev_name(dev)); 577 + spin_lock(&devices_kset->list_lock); 578 + list_move_tail(&dev->kobj.entry, &devices_kset->list); 579 + spin_unlock(&devices_kset->list_lock); 580 + } 581 + 582 + /** 537 583 * device_create_file - create sysfs attribute file for device. 538 584 * @dev: device. 539 585 * @attr: device attribute descriptor. ··· 1969 1923 break; 1970 1924 case DPM_ORDER_DEV_AFTER_PARENT: 1971 1925 device_pm_move_after(dev, new_parent); 1926 + devices_kset_move_after(dev, new_parent); 1972 1927 break; 1973 1928 case DPM_ORDER_PARENT_BEFORE_DEV: 1974 1929 device_pm_move_before(new_parent, dev); 1930 + devices_kset_move_before(new_parent, dev); 1975 1931 break; 1976 1932 case DPM_ORDER_DEV_LAST: 1977 1933 device_pm_move_last(dev); 1934 + devices_kset_move_last(dev); 1978 1935 break; 1979 1936 } 1980 1937
+8
drivers/base/dd.c
··· 304 304 goto probe_failed; 305 305 } 306 306 307 + /* 308 + * Ensure devices are listed in devices_kset in correct order 309 + * It's important to move Dev to the end of devices_kset before 310 + * calling .probe, because it could be recursive and parent Dev 311 + * should always go first 312 + */ 313 + devices_kset_move_last(dev); 314 + 307 315 if (dev->bus->probe) { 308 316 ret = dev->bus->probe(dev); 309 317 if (ret)