Skip to content

Commit 52cdbdd

Browse files
grygoriySgregkh
authored andcommitted
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 <[email protected]> Signed-off-by: Grygorii Strashko <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 82b2c3c commit 52cdbdd

File tree

3 files changed

+58
-0
lines changed

3 files changed

+58
-0
lines changed

drivers/base/base.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ extern int devres_release_all(struct device *dev);
134134

135135
/* /sys/devices directory */
136136
extern struct kset *devices_kset;
137+
extern void devices_kset_move_last(struct device *dev);
137138

138139
#if defined(CONFIG_MODULES) && defined(CONFIG_SYSFS)
139140
extern void module_add_driver(struct module *mod, struct device_driver *drv);

drivers/base/core.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,52 @@ static DEVICE_ATTR_RO(dev);
533533
/* /sys/devices/ */
534534
struct kset *devices_kset;
535535

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+
536582
/**
537583
* device_create_file - create sysfs attribute file for device.
538584
* @dev: device.
@@ -1923,12 +1969,15 @@ int device_move(struct device *dev, struct device *new_parent,
19231969
break;
19241970
case DPM_ORDER_DEV_AFTER_PARENT:
19251971
device_pm_move_after(dev, new_parent);
1972+
devices_kset_move_after(dev, new_parent);
19261973
break;
19271974
case DPM_ORDER_PARENT_BEFORE_DEV:
19281975
device_pm_move_before(new_parent, dev);
1976+
devices_kset_move_before(new_parent, dev);
19291977
break;
19301978
case DPM_ORDER_DEV_LAST:
19311979
device_pm_move_last(dev);
1980+
devices_kset_move_last(dev);
19321981
break;
19331982
}
19341983

drivers/base/dd.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,14 @@ static int really_probe(struct device *dev, struct device_driver *drv)
304304
goto probe_failed;
305305
}
306306

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+
307315
if (dev->bus->probe) {
308316
ret = dev->bus->probe(dev);
309317
if (ret)

0 commit comments

Comments
 (0)