From 864062457a2e444227bd368ca5f2a2b740de604f Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Wed, 14 Mar 2007 03:25:56 +0100 Subject: driver core: fix namespace issue with devices assigned to classes - uses a kset in "struct class" to keep track of all directories belonging to this class - merges with the /sys/devices/virtual logic. - removes the namespace-dir if the last member of that class leaves the directory. There may be locking or refcounting fixes left, I stopped when it seemed to work with network and sound modules. :) From: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- include/linux/device.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include/linux/device.h') diff --git a/include/linux/device.h b/include/linux/device.h index 5cf30e95c8b6..de0e73eae6bc 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -181,10 +181,9 @@ struct class { struct list_head children; struct list_head devices; struct list_head interfaces; + struct kset class_dirs; struct semaphore sem; /* locks both the children and interfaces lists */ - struct kobject *virtual_dir; - struct class_attribute * class_attrs; struct class_device_attribute * class_dev_attrs; struct device_attribute * dev_attrs; -- cgit v1.2.3 From b8c5cec23d5c33b767a1cddebd4f8813a9563e3c Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Fri, 16 Feb 2007 17:33:36 +0100 Subject: Driver core: udev triggered device-<>driver binding We get two per-bus sysfs files: ls-l /sys/subsystem/usb drwxr-xr-x 2 root root 0 2007-02-16 16:42 devices drwxr-xr-x 7 root root 0 2007-02-16 14:55 drivers -rw-r--r-- 1 root root 4096 2007-02-16 16:42 drivers_autoprobe --w------- 1 root root 4096 2007-02-16 16:42 drivers_probe The flag "drivers_autoprobe" controls the behavior of the bus to bind devices by default, or just initialize the device and leave it alone. The command "drivers_probe" accepts a bus_id and the bus tries to bind a driver to this device. Systems who want to control the driver binding with udev, switch off the bus initiated probing: echo 0 > /sys/subsystem/usb/drivers_autoprobe echo 0 > /sys/subsystem/pcmcia/drivers_autoprobe ... and initiate the probing with udev rules like: ACTION=="add", SUBSYSTEM=="usb", ATTR{subsystem/drivers_probe}="$kernel" ACTION=="add", SUBSYSTEM=="pcmcia", ATTR{subsystem/drivers_probe}="$kernel" ... Custom driver binding can happen in earlier rules by something like: ACTION=="add", SUBSYSTEM=="usb", \ ATTRS{idVendor}=="1234", ATTRS{idProduct}=="5678" \ ATTR{subsystem/drivers//bind}="$kernel" This is intended to solve the modprobe.conf mess with "install-rules", custom bind/unbind-scripts and all the weird things people invented over the years. It should also provide the functionality "libusual" was supposed to do. With udev, one can just write a udev rule to drive all USB-disks at the third port of USB-hub by the "ub" driver, and everything else by usb-storage. One can also instruct udev to bind different wireless drivers to identical cards - just selected by the pcmcia slot-number, and whatever ... To use the mentioned rules, it needs udev version 106, to be able to write ATTR{}="$kernel" to sysfs files. Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++---- include/linux/device.h | 34 +++++++++++--------- 2 files changed, 97 insertions(+), 21 deletions(-) (limited to 'include/linux/device.h') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 253868e03c70..9df2e6dff519 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -27,6 +27,9 @@ #define to_driver(obj) container_of(obj, struct device_driver, kobj) +static int __must_check bus_rescan_devices_helper(struct device *dev, + void *data); + static ssize_t drv_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) { @@ -133,7 +136,6 @@ static decl_subsys(bus, &ktype_bus, NULL); #ifdef CONFIG_HOTPLUG - /* Manually detach a device from its associated driver. */ static int driver_helper(struct device *dev, void *data) { @@ -199,6 +201,33 @@ static ssize_t driver_bind(struct device_driver *drv, } static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind); +static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf) +{ + return sprintf(buf, "%d\n", bus->drivers_autoprobe); +} + +static ssize_t store_drivers_autoprobe(struct bus_type *bus, + const char *buf, size_t count) +{ + if (buf[0] == '0') + bus->drivers_autoprobe = 0; + else + bus->drivers_autoprobe = 1; + return count; +} + +static ssize_t store_drivers_probe(struct bus_type *bus, + const char *buf, size_t count) +{ + struct device *dev; + + dev = bus_find_device(bus, NULL, (void *)buf, driver_helper); + if (!dev) + return -ENODEV; + if (bus_rescan_devices_helper(dev, NULL) != 0) + return -EINVAL; + return count; +} #endif static struct device * next_device(struct klist_iter * i) @@ -425,7 +454,8 @@ int bus_attach_device(struct device * dev) if (bus) { dev->is_registered = 1; - ret = device_attach(dev); + if (bus->drivers_autoprobe) + ret = device_attach(dev); if (ret >= 0) { klist_add_tail(&dev->knode_bus, &bus->klist_devices); ret = 0; @@ -515,9 +545,41 @@ static void remove_bind_files(struct device_driver *drv) driver_remove_file(drv, &driver_attr_bind); driver_remove_file(drv, &driver_attr_unbind); } + +static int add_probe_files(struct bus_type *bus) +{ + int retval; + + bus->drivers_probe_attr.attr.name = "drivers_probe"; + bus->drivers_probe_attr.attr.mode = S_IWUSR; + bus->drivers_probe_attr.attr.owner = bus->owner; + bus->drivers_probe_attr.store = store_drivers_probe; + retval = bus_create_file(bus, &bus->drivers_probe_attr); + if (retval) + goto out; + + bus->drivers_autoprobe_attr.attr.name = "drivers_autoprobe"; + bus->drivers_autoprobe_attr.attr.mode = S_IWUSR | S_IRUGO; + bus->drivers_autoprobe_attr.attr.owner = bus->owner; + bus->drivers_autoprobe_attr.show = show_drivers_autoprobe; + bus->drivers_autoprobe_attr.store = store_drivers_autoprobe; + retval = bus_create_file(bus, &bus->drivers_autoprobe_attr); + if (retval) + bus_remove_file(bus, &bus->drivers_probe_attr); +out: + return retval; +} + +static void remove_probe_files(struct bus_type *bus) +{ + bus_remove_file(bus, &bus->drivers_autoprobe_attr); + bus_remove_file(bus, &bus->drivers_probe_attr); +} #else static inline int add_bind_files(struct device_driver *drv) { return 0; } static inline void remove_bind_files(struct device_driver *drv) {} +static inline int add_probe_files(struct bus_type *bus) { return 0; } +static inline void remove_probe_files(struct bus_type *bus) {} #endif /** @@ -541,9 +603,11 @@ int bus_add_driver(struct device_driver *drv) if ((error = kobject_register(&drv->kobj))) goto out_put_bus; - error = driver_attach(drv); - if (error) - goto out_unregister; + if (drv->bus->drivers_autoprobe) { + error = driver_attach(drv); + if (error) + goto out_unregister; + } klist_add_tail(&drv->knode_bus, &bus->klist_drivers); module_add_driver(drv->owner, drv); @@ -762,6 +826,12 @@ int bus_register(struct bus_type * bus) klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put); klist_init(&bus->klist_drivers, NULL, NULL); + + bus->drivers_autoprobe = 1; + retval = add_probe_files(bus); + if (retval) + goto bus_probe_files_fail; + retval = bus_add_attrs(bus); if (retval) goto bus_attrs_fail; @@ -770,6 +840,8 @@ int bus_register(struct bus_type * bus) return 0; bus_attrs_fail: + remove_probe_files(bus); +bus_probe_files_fail: kset_unregister(&bus->drivers); bus_drivers_fail: kset_unregister(&bus->devices); @@ -779,7 +851,6 @@ out: return retval; } - /** * bus_unregister - remove a bus from the system * @bus: bus. @@ -791,6 +862,7 @@ void bus_unregister(struct bus_type * bus) { pr_debug("bus %s: unregistering\n", bus->name); bus_remove_attrs(bus); + remove_probe_files(bus); kset_unregister(&bus->drivers); kset_unregister(&bus->devices); subsystem_unregister(&bus->subsys); diff --git a/include/linux/device.h b/include/linux/device.h index de0e73eae6bc..9d54fe13eb2e 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -34,9 +34,24 @@ struct device; struct device_driver; struct class; struct class_device; +struct bus_type; + +struct bus_attribute { + struct attribute attr; + ssize_t (*show)(struct bus_type *, char * buf); + ssize_t (*store)(struct bus_type *, const char * buf, size_t count); +}; + +#define BUS_ATTR(_name,_mode,_show,_store) \ +struct bus_attribute bus_attr_##_name = __ATTR(_name,_mode,_show,_store) + +extern int __must_check bus_create_file(struct bus_type *, + struct bus_attribute *); +extern void bus_remove_file(struct bus_type *, struct bus_attribute *); struct bus_type { const char * name; + struct module * owner; struct subsystem subsys; struct kset drivers; @@ -49,6 +64,8 @@ struct bus_type { struct bus_attribute * bus_attrs; struct device_attribute * dev_attrs; struct driver_attribute * drv_attrs; + struct bus_attribute drivers_autoprobe_attr; + struct bus_attribute drivers_probe_attr; int (*match)(struct device * dev, struct device_driver * drv); int (*uevent)(struct device *dev, char **envp, @@ -61,6 +78,8 @@ struct bus_type { int (*suspend_late)(struct device * dev, pm_message_t state); int (*resume_early)(struct device * dev); int (*resume)(struct device * dev); + + unsigned int drivers_autoprobe:1; }; extern int __must_check bus_register(struct bus_type * bus); @@ -102,21 +121,6 @@ extern int bus_unregister_notifier(struct bus_type *bus, #define BUS_NOTIFY_UNBIND_DRIVER 0x00000004 /* driver about to be unbound */ -/* sysfs interface for exporting bus attributes */ - -struct bus_attribute { - struct attribute attr; - ssize_t (*show)(struct bus_type *, char * buf); - ssize_t (*store)(struct bus_type *, const char * buf, size_t count); -}; - -#define BUS_ATTR(_name,_mode,_show,_store) \ -struct bus_attribute bus_attr_##_name = __ATTR(_name,_mode,_show,_store) - -extern int __must_check bus_create_file(struct bus_type *, - struct bus_attribute *); -extern void bus_remove_file(struct bus_type *, struct bus_attribute *); - struct device_driver { const char * name; struct bus_type * bus; -- cgit v1.2.3 From 621a1672f7377e08a942f205d6742d8af1292aab Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 10 Mar 2007 01:37:34 -0500 Subject: driver core: Use attribute groups in struct device_type Driver core: use attribute groups in struct device_type Attribute groups are more flexible than attribute lists (an attribute list can be represented by anonymous group) so switch struct device_type to use them. Also rework attribute creation for devices so that they all cleaned up properly in case of errors. Signed-off-by: Dmitry Torokhov Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 115 +++++++++++++++++++++++++++++-------------------- include/linux/device.h | 2 +- 2 files changed, 70 insertions(+), 47 deletions(-) (limited to 'include/linux/device.h') diff --git a/drivers/base/core.c b/drivers/base/core.c index 9ea12d9b48a6..bb2cc37a4d43 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -246,64 +246,95 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr, return count; } -static int device_add_groups(struct device *dev) +static int device_add_attributes(struct device *dev, + struct device_attribute *attrs) +{ + int error = 0; + int i; + + if (attrs) { + for (i = 0; attr_name(attrs[i]); i++) { + error = device_create_file(dev, &attrs[i]); + if (error) + break; + } + if (error) + while (--i >= 0) + device_remove_file(dev, &attrs[i]); + } + return error; +} + +static void device_remove_attributes(struct device *dev, + struct device_attribute *attrs) { int i; + + if (attrs) + for (i = 0; attr_name(attrs[i]); i++) + device_remove_file(dev, &attrs[i]); +} + +static int device_add_groups(struct device *dev, + struct attribute_group **groups) +{ int error = 0; + int i; - if (dev->groups) { - for (i = 0; dev->groups[i]; i++) { - error = sysfs_create_group(&dev->kobj, dev->groups[i]); + if (groups) { + for (i = 0; groups[i]; i++) { + error = sysfs_create_group(&dev->kobj, groups[i]); if (error) { while (--i >= 0) - sysfs_remove_group(&dev->kobj, dev->groups[i]); - goto out; + sysfs_remove_group(&dev->kobj, groups[i]); + break; } } } -out: return error; } -static void device_remove_groups(struct device *dev) +static void device_remove_groups(struct device *dev, + struct attribute_group **groups) { int i; - if (dev->groups) { - for (i = 0; dev->groups[i]; i++) { - sysfs_remove_group(&dev->kobj, dev->groups[i]); - } - } + + if (groups) + for (i = 0; groups[i]; i++) + sysfs_remove_group(&dev->kobj, groups[i]); } static int device_add_attrs(struct device *dev) { struct class *class = dev->class; struct device_type *type = dev->type; - int error = 0; - int i; + int error; - if (class && class->dev_attrs) { - for (i = 0; attr_name(class->dev_attrs[i]); i++) { - error = device_create_file(dev, &class->dev_attrs[i]); - if (error) - break; - } + if (class) { + error = device_add_attributes(dev, class->dev_attrs); if (error) - while (--i >= 0) - device_remove_file(dev, &class->dev_attrs[i]); + return error; } - if (type && type->attrs) { - for (i = 0; attr_name(type->attrs[i]); i++) { - error = device_create_file(dev, &type->attrs[i]); - if (error) - break; - } + if (type) { + error = device_add_groups(dev, type->groups); if (error) - while (--i >= 0) - device_remove_file(dev, &type->attrs[i]); + goto err_remove_class_attrs; } + error = device_add_groups(dev, dev->groups); + if (error) + goto err_remove_type_groups; + + return 0; + + err_remove_type_groups: + if (type) + device_remove_groups(dev, type->groups); + err_remove_class_attrs: + if (class) + device_remove_attributes(dev, class->dev_attrs); + return error; } @@ -311,17 +342,14 @@ static void device_remove_attrs(struct device *dev) { struct class *class = dev->class; struct device_type *type = dev->type; - int i; - if (class && class->dev_attrs) { - for (i = 0; attr_name(class->dev_attrs[i]); i++) - device_remove_file(dev, &class->dev_attrs[i]); - } + device_remove_groups(dev, dev->groups); - if (type && type->attrs) { - for (i = 0; attr_name(type->attrs[i]); i++) - device_remove_file(dev, &type->attrs[i]); - } + if (type) + device_remove_groups(dev, type->groups); + + if (class) + device_remove_attributes(dev, class->dev_attrs); } @@ -638,8 +666,6 @@ int device_add(struct device *dev) if ((error = device_add_attrs(dev))) goto AttrsError; - if ((error = device_add_groups(dev))) - goto GroupError; if ((error = device_pm_add(dev))) goto PMError; if ((error = bus_add_device(dev))) @@ -663,7 +689,7 @@ int device_add(struct device *dev) up(&dev->class->sem); } Done: - kfree(class_name); + kfree(class_name); put_device(dev); return error; AttachError: @@ -674,8 +700,6 @@ int device_add(struct device *dev) if (dev->bus) blocking_notifier_call_chain(&dev->bus->bus_notifier, BUS_NOTIFY_DEL_DEVICE, dev); - device_remove_groups(dev); - GroupError: device_remove_attrs(dev); AttrsError: if (dev->devt_attr) { @@ -838,7 +862,6 @@ void device_del(struct device * dev) } } device_remove_file(dev, &dev->uevent_attr); - device_remove_groups(dev); device_remove_attrs(dev); bus_remove_device(dev); diff --git a/include/linux/device.h b/include/linux/device.h index 9d54fe13eb2e..3b64fdecd041 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -332,7 +332,7 @@ extern struct class_device *class_device_create(struct class *cls, extern void class_device_destroy(struct class *cls, dev_t devt); struct device_type { - struct device_attribute *attrs; + struct attribute_group **groups; int (*uevent)(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size); void (*release)(struct device *dev); -- cgit v1.2.3 From 414264f959cf46f49f974b3510400e12ac3624a6 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Mon, 12 Mar 2007 21:08:57 +0100 Subject: Driver core: add name to device_type If "name" of a device_type is specified, the uevent will contain the device_type name in the DEVTYPE variable. This helps userspace to distingiush between different types of devices, belonging to the same subsystem. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 5 +++++ include/linux/device.h | 10 ++++++++++ 2 files changed, 15 insertions(+) (limited to 'include/linux/device.h') diff --git a/drivers/base/core.c b/drivers/base/core.c index bb2cc37a4d43..bffb69e4bde2 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -157,6 +157,11 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp, "MINOR=%u", MINOR(dev->devt)); } + if (dev->type && dev->type->name) + add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "DEVTYPE=%s", dev->type->name); + if (dev->driver) add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, diff --git a/include/linux/device.h b/include/linux/device.h index 3b64fdecd041..7f63d4de5c4d 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -331,7 +331,17 @@ extern struct class_device *class_device_create(struct class *cls, __attribute__((format(printf,5,6))); extern void class_device_destroy(struct class *cls, dev_t devt); +/* + * The type of device, "struct device" is embedded in. A class + * or bus can contain devices of different types + * like "partitions" and "disks", "mouse" and "event". + * This identifies the device type and carries type-specific + * information, equivalent to the kobj_type of a kobject. + * If "name" is specified, the uevent will contain it in + * the DEVTYPE variable. + */ struct device_type { + const char *name; struct attribute_group **groups; int (*uevent)(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size); -- cgit v1.2.3 From 21c7f30b1d3f8a3de3128478daca3ce203fc8733 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 5 Feb 2007 16:15:25 -0800 Subject: driver core: per-subsystem multithreaded probing Make multithreaded probing work per subsystem instead of per driver. It doesn't make much sense to probe the same device for multiple drivers in parallel (after all, only one driver can bind to the device). Instead, create a probing thread for each device that probes the drivers one after another. Also make the decision to use multi-threaded probe per bus instead of per device and adapt the pci code. Signed-off-by: Cornelia Huck Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 62 ++++++++++++++++++++++++------------------------ drivers/pci/pci-driver.c | 6 +---- include/linux/device.h | 3 +-- include/linux/pci.h | 2 -- 4 files changed, 33 insertions(+), 40 deletions(-) (limited to 'include/linux/device.h') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 6a48824e43ff..616b4bbacf1b 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -94,19 +94,11 @@ int device_bind_driver(struct device *dev) return ret; } -struct stupid_thread_structure { - struct device_driver *drv; - struct device *dev; -}; - static atomic_t probe_count = ATOMIC_INIT(0); static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue); -static int really_probe(void *void_data) +static int really_probe(struct device *dev, struct device_driver *drv) { - struct stupid_thread_structure *data = void_data; - struct device_driver *drv = data->drv; - struct device *dev = data->dev; int ret = 0; atomic_inc(&probe_count); @@ -154,7 +146,6 @@ probe_failed: */ ret = 0; done: - kfree(data); atomic_dec(&probe_count); wake_up(&probe_waitqueue); return ret; @@ -186,16 +177,14 @@ int driver_probe_done(void) * format of the ID structures, nor what is to be considered a match and * what is not. * - * This function returns 1 if a match is found, an error if one occurs - * (that is not -ENODEV or -ENXIO), and 0 otherwise. + * This function returns 1 if a match is found, -ENODEV if the device is + * not registered, and 0 otherwise. * * This function must be called with @dev->sem held. When called for a * USB interface, @dev->parent->sem must be held as well. */ int driver_probe_device(struct device_driver * drv, struct device * dev) { - struct stupid_thread_structure *data; - struct task_struct *probe_task; int ret = 0; if (!device_is_registered(dev)) @@ -206,19 +195,7 @@ int driver_probe_device(struct device_driver * drv, struct device * dev) pr_debug("%s: Matched Device %s with Driver %s\n", drv->bus->name, dev->bus_id, drv->name); - data = kmalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - data->drv = drv; - data->dev = dev; - - if (drv->multithread_probe) { - probe_task = kthread_run(really_probe, data, - "probe-%s", dev->bus_id); - if (IS_ERR(probe_task)) - ret = really_probe(data); - } else - ret = really_probe(data); + ret = really_probe(dev, drv); done: return ret; @@ -230,30 +207,53 @@ static int __device_attach(struct device_driver * drv, void * data) return driver_probe_device(drv, dev); } +static int device_probe_drivers(void *data) +{ + struct device *dev = data; + int ret = 0; + + if (dev->bus) { + down(&dev->sem); + ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); + up(&dev->sem); + } + return ret; +} + /** * device_attach - try to attach device to a driver. * @dev: device. * * Walk the list of drivers that the bus has and call * driver_probe_device() for each pair. If a compatible - * pair is found, break out and return. + * pair is found, break out and return. If the bus specifies + * multithreaded probing, walking the list of drivers is done + * on a probing thread. * * Returns 1 if the device was bound to a driver; - * 0 if no matching device was found; error code otherwise. + * 0 if no matching device was found or multithreaded probing is done; + * error code otherwise. * * When called for a USB interface, @dev->parent->sem must be held. */ int device_attach(struct device * dev) { int ret = 0; + struct task_struct *probe_task = ERR_PTR(-ENOMEM); down(&dev->sem); if (dev->driver) { ret = device_bind_driver(dev); if (ret == 0) ret = 1; - } else - ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); + } else { + if (dev->bus->multithread_probe) + probe_task = kthread_run(device_probe_drivers, dev, + "probe-%s", dev->bus_id); + if(IS_ERR(probe_task)) + ret = bus_for_each_drv(dev->bus, NULL, dev, + __device_attach); + } up(&dev->sem); return ret; } diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index a3c1755b2f28..39e80fcef4b3 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -434,11 +434,6 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner, drv->driver.mod_name = mod_name; drv->driver.kobj.ktype = &pci_driver_kobj_type; - if (pci_multithread_probe) - drv->driver.multithread_probe = pci_multithread_probe; - else - drv->driver.multithread_probe = drv->multithread_probe; - spin_lock_init(&drv->dynids.lock); INIT_LIST_HEAD(&drv->dynids.list); @@ -574,6 +569,7 @@ struct bus_type pci_bus_type = { static int __init pci_driver_init(void) { + pci_bus_type.multithread_probe = pci_multithread_probe; return bus_register(&pci_bus_type); } diff --git a/include/linux/device.h b/include/linux/device.h index 7f63d4de5c4d..eb1fff0b1d2a 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -80,6 +80,7 @@ struct bus_type { int (*resume)(struct device * dev); unsigned int drivers_autoprobe:1; + unsigned int multithread_probe:1; }; extern int __must_check bus_register(struct bus_type * bus); @@ -139,8 +140,6 @@ struct device_driver { void (*shutdown) (struct device * dev); int (*suspend) (struct device * dev, pm_message_t state); int (*resume) (struct device * dev); - - unsigned int multithread_probe:1; }; diff --git a/include/linux/pci.h b/include/linux/pci.h index 481ea0663f19..a3ad76221c6f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -361,8 +361,6 @@ struct pci_driver { struct pci_error_handlers *err_handler; struct device_driver driver; struct pci_dynids dynids; - - int multithread_probe; }; #define to_pci_driver(drv) container_of(drv,struct pci_driver, driver) -- cgit v1.2.3 From 74e9f5fa1570f956c96dd5d3f1053daedbbf01a0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 9 Apr 2002 12:14:34 -0700 Subject: Driver core: remove unneeded completion from driver release path The completion in the driver release path is due to ancient history in the _very_ early 2.5 days when we were not tracking the module reference count of attributes. It is not needed at all and can be removed. Note, we now have an empty release function for the driver structure. This is due to the fact that drivers are statically allocated in the system at this point in time, something which I want to change in the future. But remember, drivers are really code, which is reference counted by the module, unlike devices, which are data and _must_ be reference counted properly in order to work correctly. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 15 +++++++++++++-- drivers/base/driver.c | 20 -------------------- include/linux/device.h | 1 - 3 files changed, 13 insertions(+), 23 deletions(-) (limited to 'include/linux/device.h') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 20b6dc8706fa..1a5a350eca15 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -63,8 +63,19 @@ static struct sysfs_ops driver_sysfs_ops = { static void driver_release(struct kobject * kobj) { - struct device_driver * drv = to_driver(kobj); - complete(&drv->unloaded); + /* + * Yes this is an empty release function, it is this way because struct + * device is always a static object, not a dynamic one. Yes, this is + * not nice and bad, but remember, drivers are code, reference counted + * by the module count, not a device, which is really data. And yes, + * in the future I do want to have all drivers be created dynamically, + * and am working toward that goal, but it will take a bit longer... + * + * But do not let this example give _anyone_ the idea that they can + * create a release function without any code in it at all, to do that + * is almost always wrong. If you have any questions about this, + * please send an email to + */ } static struct kobj_type ktype_driver = { diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 082bfded3854..eb11475293ed 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -149,10 +149,6 @@ void put_driver(struct device_driver * drv) * We pass off most of the work to the bus_add_driver() call, * since most of the things we have to do deal with the bus * structures. - * - * The one interesting aspect is that we setup @drv->unloaded - * as a completion that gets complete when the driver reference - * count reaches 0. */ int driver_register(struct device_driver * drv) { @@ -162,35 +158,19 @@ int driver_register(struct device_driver * drv) printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name); } klist_init(&drv->klist_devices, NULL, NULL); - init_completion(&drv->unloaded); return bus_add_driver(drv); } - /** * driver_unregister - remove driver from system. * @drv: driver. * * Again, we pass off most of the work to the bus-level call. - * - * Though, once that is done, we wait until @drv->unloaded is completed. - * This will block until the driver refcount reaches 0, and it is - * released. Only modular drivers will call this function, and we - * have to guarantee that it won't complete, letting the driver - * unload until all references are gone. */ void driver_unregister(struct device_driver * drv) { bus_remove_driver(drv); - /* - * If the driver is a module, we are probably in - * the module unload path, and we want to wait - * for everything to unload before we can actually - * finish the unload. - */ - if (drv->owner) - wait_for_completion(&drv->unloaded); } /** diff --git a/include/linux/device.h b/include/linux/device.h index eb1fff0b1d2a..c9dc458e8e50 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -126,7 +126,6 @@ struct device_driver { const char * name; struct bus_type * bus; - struct completion unloaded; struct kobject kobj; struct klist klist_devices; struct klist_node knode_bus; -- cgit v1.2.3 From f89cbc399ecd924c4bd879344e662aace2274b4f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 3 Apr 2007 01:08:40 -0400 Subject: Driver core: add suspend() and resume() to struct device_type Driver core: add suspend() and resume() to struct device_type In cases when there are devices of different types in the same class we can't use class's implementation of suspend and resume methods and we need to add them to struct device_type instead. Also fix error handling in resume code (we should not try to call class's resume method iof bus's resume method for the device failed. Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/resume.c | 13 ++++++++++++- drivers/base/power/suspend.c | 12 ++++++++++++ include/linux/device.h | 2 ++ 3 files changed, 26 insertions(+), 1 deletion(-) (limited to 'include/linux/device.h') diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c index 020be36705a6..a2c64188d713 100644 --- a/drivers/base/power/resume.c +++ b/drivers/base/power/resume.c @@ -26,7 +26,9 @@ int resume_device(struct device * dev) TRACE_DEVICE(dev); TRACE_RESUME(0); + down(&dev->sem); + if (dev->power.pm_parent && dev->power.pm_parent->power.power_state.event) { dev_err(dev, "PM: resume from %d, parent %s still %d\n", @@ -34,15 +36,24 @@ int resume_device(struct device * dev) dev->power.pm_parent->bus_id, dev->power.pm_parent->power.power_state.event); } + if (dev->bus && dev->bus->resume) { dev_dbg(dev,"resuming\n"); error = dev->bus->resume(dev); } - if (dev->class && dev->class->resume) { + + if (!error && dev->type && dev->type->resume) { + dev_dbg(dev,"resuming\n"); + error = dev->type->resume(dev); + } + + if (!error && dev->class && dev->class->resume) { dev_dbg(dev,"class resume\n"); error = dev->class->resume(dev); } + up(&dev->sem); + TRACE_RESUME(error); return error; } diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index ece136bf97e3..42d2b86ba765 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -78,6 +78,18 @@ int suspend_device(struct device * dev, pm_message_t state) suspend_report_result(dev->class->suspend, error); } + if (!error && dev->type && dev->type->suspend && !dev->power.power_state.event) { + dev_dbg(dev, "%s%s\n", + suspend_verb(state.event), + ((state.event == PM_EVENT_SUSPEND) + && device_may_wakeup(dev)) + ? ", may wakeup" + : "" + ); + error = dev->type->suspend(dev, state); + suspend_report_result(dev->type->suspend, error); + } + if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) { dev_dbg(dev, "%s%s\n", suspend_verb(state.event), diff --git a/include/linux/device.h b/include/linux/device.h index c9dc458e8e50..af603a137690 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -344,6 +344,8 @@ struct device_type { int (*uevent)(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size); void (*release)(struct device *dev); + int (*suspend)(struct device * dev, pm_message_t state); + int (*resume)(struct device * dev); }; /* interface for exporting device attributes */ -- cgit v1.2.3 From 523ded71de0c5e66973335bf99a80edfda9f401b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 26 Apr 2007 00:12:04 -0700 Subject: device_schedule_callback() needs a module reference This patch (as896b) fixes an oversight in the design of device_schedule_callback(). It is necessary to acquire a reference to the module owning the callback routine, to prevent the module from being unloaded before the callback can run. Signed-off-by: Alan Stern Cc: Satyam Sharma Cc: Neil Brown Cc: Cornelia Huck Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 16 ++++++++++------ fs/sysfs/file.c | 14 +++++++++++--- include/linux/device.h | 8 ++++++-- include/linux/sysfs.h | 4 ++-- 4 files changed, 29 insertions(+), 13 deletions(-) (limited to 'include/linux/device.h') diff --git a/drivers/base/core.c b/drivers/base/core.c index f69305c7269d..8aa090da1cd7 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -480,9 +480,10 @@ void device_remove_bin_file(struct device *dev, struct bin_attribute *attr) EXPORT_SYMBOL_GPL(device_remove_bin_file); /** - * device_schedule_callback - helper to schedule a callback for a device + * device_schedule_callback_owner - helper to schedule a callback for a device * @dev: device. * @func: callback function to invoke later. + * @owner: module owning the callback routine * * Attribute methods must not unregister themselves or their parent device * (which would amount to the same thing). Attempts to do so will deadlock, @@ -493,20 +494,23 @@ EXPORT_SYMBOL_GPL(device_remove_bin_file); * argument in the workqueue's process context. @dev will be pinned until * @func returns. * + * This routine is usually called via the inline device_schedule_callback(), + * which automatically sets @owner to THIS_MODULE. + * * Returns 0 if the request was submitted, -ENOMEM if storage could not - * be allocated. + * be allocated, -ENODEV if a reference to @owner isn't available. * * NOTE: This routine won't work if CONFIG_SYSFS isn't set! It uses an * underlying sysfs routine (since it is intended for use by attribute * methods), and if sysfs isn't available you'll get nothing but -ENOSYS. */ -int device_schedule_callback(struct device *dev, - void (*func)(struct device *)) +int device_schedule_callback_owner(struct device *dev, + void (*func)(struct device *), struct module *owner) { return sysfs_schedule_callback(&dev->kobj, - (void (*)(void *)) func, dev); + (void (*)(void *)) func, dev, owner); } -EXPORT_SYMBOL_GPL(device_schedule_callback); +EXPORT_SYMBOL_GPL(device_schedule_callback_owner); static void klist_children_get(struct klist_node *n) { diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index fc4633378dc0..db0413a411d6 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -633,6 +633,7 @@ struct sysfs_schedule_callback_struct { struct kobject *kobj; void (*func)(void *); void *data; + struct module *owner; struct work_struct work; }; @@ -643,6 +644,7 @@ static void sysfs_schedule_callback_work(struct work_struct *work) (ss->func)(ss->data); kobject_put(ss->kobj); + module_put(ss->owner); kfree(ss); } @@ -651,6 +653,7 @@ static void sysfs_schedule_callback_work(struct work_struct *work) * @kobj: object we're acting for. * @func: callback function to invoke later. * @data: argument to pass to @func. + * @owner: module owning the callback code * * sysfs attribute methods must not unregister themselves or their parent * kobject (which would amount to the same thing). Attempts to do so will @@ -663,20 +666,25 @@ static void sysfs_schedule_callback_work(struct work_struct *work) * until @func returns. * * Returns 0 if the request was submitted, -ENOMEM if storage could not - * be allocated. + * be allocated, -ENODEV if a reference to @owner isn't available. */ int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), - void *data) + void *data, struct module *owner) { struct sysfs_schedule_callback_struct *ss; + if (!try_module_get(owner)) + return -ENODEV; ss = kmalloc(sizeof(*ss), GFP_KERNEL); - if (!ss) + if (!ss) { + module_put(owner); return -ENOMEM; + } kobject_get(kobj); ss->kobj = kobj; ss->func = func; ss->data = data; + ss->owner = owner; INIT_WORK(&ss->work, sysfs_schedule_callback_work); schedule_work(&ss->work); return 0; diff --git a/include/linux/device.h b/include/linux/device.h index af603a137690..8511d14071b3 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -367,8 +367,12 @@ extern int __must_check device_create_bin_file(struct device *dev, struct bin_attribute *attr); extern void device_remove_bin_file(struct device *dev, struct bin_attribute *attr); -extern int device_schedule_callback(struct device *dev, - void (*func)(struct device *)); +extern int device_schedule_callback_owner(struct device *dev, + void (*func)(struct device *), struct module *owner); + +/* This is a macro to avoid include problems with THIS_MODULE */ +#define device_schedule_callback(dev, func) \ + device_schedule_callback_owner(dev, func, THIS_MODULE) /* device resource management */ typedef void (*dr_release_t)(struct device *dev, void *res); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index fea9a6b3fb7b..7d5d1ec95c2e 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -80,7 +80,7 @@ struct sysfs_ops { #ifdef CONFIG_SYSFS extern int sysfs_schedule_callback(struct kobject *kobj, - void (*func)(void *), void *data); + void (*func)(void *), void *data, struct module *owner); extern int __must_check sysfs_create_dir(struct kobject *, struct dentry *); @@ -137,7 +137,7 @@ extern int __must_check sysfs_init(void); #else /* CONFIG_SYSFS */ static inline int sysfs_schedule_callback(struct kobject *kobj, - void (*func)(void *), void *data) + void (*func)(void *), void *data, struct module *owner) { return -ENOSYS; } -- cgit v1.2.3 From 404d5b185b4eb56d6fa2f7bd27833f8df1c38ce4 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 26 Apr 2007 00:12:10 -0700 Subject: dev_dbg: check dev_dbg() arguments Duplicate what Zach Brown did for pr_debug in commit 8b2a1fd1b394c60eaa2587716102dd5e9b4e5990 [akpm@linux-foundation.org: fix a couple of things which broke] Signed-off-by: Dan Williams Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 4 +--- drivers/usb/host/ohci-hcd.c | 6 ------ include/linux/device.h | 6 +++++- 3 files changed, 6 insertions(+), 10 deletions(-) (limited to 'include/linux/device.h') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b89a98e61323..7a6028599d62 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -119,8 +119,7 @@ MODULE_PARM_DESC(use_both_schemes, "first one fails"); -#ifdef DEBUG -static inline char *portspeed (int portstatus) +static inline char *portspeed(int portstatus) { if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED)) return "480 Mb/s"; @@ -129,7 +128,6 @@ static inline char *portspeed (int portstatus) else return "12 Mb/s"; } -#endif /* Note that hdev or one of its children must be locked! */ static inline struct usb_hub *hdev_to_hub(struct usb_device *hdev) diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index f0d29eda3c6d..e8bbe8bc2598 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -486,9 +486,6 @@ static int ohci_run (struct ohci_hcd *ohci) * or if bus glue did the same (e.g. for PCI add-in cards with * PCI PM support). */ - ohci_dbg (ohci, "resetting from state '%s', control = 0x%x\n", - hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), - ohci_readl (ohci, &ohci->regs->control)); if ((ohci->hc_control & OHCI_CTRL_RWC) != 0 && !device_may_wakeup(hcd->self.controller)) device_init_wakeup(hcd->self.controller, 1); @@ -744,9 +741,6 @@ static void ohci_stop (struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); - ohci_dbg (ohci, "stop %s controller (state 0x%02x)\n", - hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), - hcd->state); ohci_dump (ohci, 1); flush_scheduled_work(); diff --git a/include/linux/device.h b/include/linux/device.h index 8511d14071b3..a0cd2ced31a9 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -571,7 +571,11 @@ extern const char *dev_driver_string(struct device *dev); #define dev_dbg(dev, format, arg...) \ dev_printk(KERN_DEBUG , dev , format , ## arg) #else -#define dev_dbg(dev, format, arg...) do { (void)(dev); } while (0) +static inline int __attribute__ ((format (printf, 2, 3))) +dev_dbg(struct device * dev, const char * fmt, ...) +{ + return 0; +} #endif #define dev_err(dev, format, arg...) \ -- cgit v1.2.3