The patch titled driver core: Introduce device_find_child() has been added to the -mm tree. Its filename is driver-core-introduce-device_find_child.patch See http://www.zip.com.au/~akpm/linux/patches/stuff/added-to-mm.txt to find out what to do about this ------------------------------------------------------ Subject: driver core: Introduce device_find_child() From: Cornelia Huck <cornelia.huck@xxxxxxxxxx> For quite a while, we've been trying to make block devices on s390 channel attached devices (aka ccw devices) more resilent against outages of storage servers. The main problem is that the kernel doesn't take well to having a mounted device removed (and that might happen if, for instance, there is an intermittant outage at the switch connecting the storage server and we get machine checks indicating the dasd devices are no longer there). To work around this, we introduced a "disconnected" status for ccw devices which are in use ("online") but currently not there (or without a path). The dasd driver will plug its queue when it is notified that the device is gone, and unplug it when notified that the device is there again. We have a rather complex logic in the s390 common I/O layer (see drivers/s390/cio/) in order to get disconnected devices operational again without introducing lockups with other non-functional devices coming or going or allocating memory (our swap device may just be disconnected...) Currently, our logic works well with most real-world scenarios. Except one: Re-attaching devices under z/VM. ccw devices are addressed via io subchannels residing on the css bus: css0 -> 0.0.0000 -> 0.0.0815 -> 0.0.0001 -> 0.0.4711 ... (There also may be non-io subchannels with no attached ccw device, although we currently don't support them). On LPAR, the subchannel<->device relationship is defined in the IOCDS (which requires manual intervention to change), but z/VM will assign the first free subchannel to a device when it is attached (and attach is done by specifying the device number of the ccw device (like 0815 and 4711 in the example above), the subchannel number is completely uninteresting to z/VM). This may lead to the following: - z/VM recognizes it can no longer access devices 0815 and 4711 and detaches them from the guest. - Linux gets machine checks indicating devices 0815 and 4711 are gone. Since they are currently in use, they are put into "disconnected" state. - Access to devices 0815 and 4711 is reestablished. However, z/VM won't automatically reattach them. - The operator manually reattaches 0815 and 4711. However, since he has no way of knowing in which order they were originally attached, he first attaches 4711 and then 0815. The layout z/VM presents to Linux is now the following: subchannel 0 -> device 4711 subchannel 1 -> device 0815 So we've now got different devices than before on the same subchannel. Currently, we have to deregister the old (disconnected) device and register the new one, which leads to all the problems we wanted to avoid with the "disconnected" state in the first place. (There are more ways you can mix up your devices, like "DEFINE xxxx AS yyyy" under z/VM (which changes the device number from xxxx to yyyy), but I consider those "shoot yourself in the foot" unlike the above, which is a real world recovery scenario.) [Still reading? Here comes the fun part.] What we really want to do is to switch the devices around, i. e. move ccw device 0.0.4711 to subchannel 0.0.0000 and ccw device 0.0.0815 to subchannel 0.0.0001. That is what the following patchset implements: - Ability to move devices to a new parent in the device tree, keeping established relationships intact. - Using this to move ccw devices appearing on a different subchannel to their new parent. There also is a "pseudo subchannel" per channel subsystem which servers as a temporary parent for ccw devices which have been displaced by a new ccw device on their old subchannel. This patchset works well for me when detaching several used ccw devices and reattaching them in random order. It should make us survive storage server outages in nearly all cases (except when we need to move devices and we have no chance of getting some memory). This patch: Introduce device_find_child() to match device_for_each_child(). Signed-off-by: Cornelia Huck <cornelia.huck@xxxxxxxxxx> Cc: Martin Schwidefsky <schwidefsky@xxxxxxxxxx> Cc: Greg KH <greg@xxxxxxxxx> Signed-off-by: Andrew Morton <akpm@xxxxxxxx> --- drivers/base/core.c | 33 +++++++++++++++++++++++++++++++++ include/linux/device.h | 2 ++ 2 files changed, 35 insertions(+) diff -puN drivers/base/core.c~driver-core-introduce-device_find_child drivers/base/core.c --- a/drivers/base/core.c~driver-core-introduce-device_find_child +++ a/drivers/base/core.c @@ -750,12 +750,45 @@ int device_for_each_child(struct device return error; } +/** + * device_find_child - device iterator for locating a particular device. + * @parent: parent struct device + * @data: Data to pass to match function + * @match: Callback function to check device + * + * This is similar to the device_for_each_child() function above, but it + * returns a reference to a device that is 'found' for later use, as + * determined by the @match callback. + * + * The callback should return 0 if the device doesn't match and non-zero + * if it does. If the callback returns non-zero and a reference to the + * current device can be obtained, this function will return to the caller + * and not iterate over any more devices. + */ +struct device * device_find_child(struct device *parent, void *data, + int (*match)(struct device *, void *)) +{ + struct klist_iter i; + struct device *child; + + if (!parent) + return NULL; + + klist_iter_init(&parent->klist_children, &i); + while ((child = next_device(&i))) + if (match(child, data) && get_device(child)) + break; + klist_iter_exit(&i); + return child; +} + int __init devices_init(void) { return subsystem_register(&devices_subsys); } EXPORT_SYMBOL_GPL(device_for_each_child); +EXPORT_SYMBOL_GPL(device_find_child); EXPORT_SYMBOL_GPL(device_initialize); EXPORT_SYMBOL_GPL(device_add); diff -puN include/linux/device.h~driver-core-introduce-device_find_child include/linux/device.h --- a/include/linux/device.h~driver-core-introduce-device_find_child +++ a/include/linux/device.h @@ -420,6 +420,8 @@ extern int __must_check device_add(struc extern void device_del(struct device * dev); extern int device_for_each_child(struct device *, void *, int (*fn)(struct device *, void *)); +extern struct device *device_find_child(struct device *, void *data, + int (*match)(struct device *, void *)); extern int device_rename(struct device *dev, char *new_name); /* _ Patches currently in -mm which might be from cornelia.huck@xxxxxxxxxx are driver-core-introduce-device_find_child.patch driver-core-introduce-device_move-move-a-device.patch git-s390.patch tty-switch-to-ktermios-sclp-fix.patch s390-preparatory-cleanup-in-common-i-o-layer.patch s390-make-the-per-subchannel-lock-dynamic.patch s390-dynamic-subchannel-mapping-of-ccw-devices.patch - To unsubscribe from this list: send the line "unsubscribe mm-commits" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html