diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/Kconfig | 2 | ||||
-rw-r--r-- | drivers/base/base.h | 2 | ||||
-rw-r--r-- | drivers/base/bus.c | 54 | ||||
-rw-r--r-- | drivers/base/class.c | 136 | ||||
-rw-r--r-- | drivers/base/core.c | 36 | ||||
-rw-r--r-- | drivers/base/dd.c | 3 | ||||
-rw-r--r-- | drivers/base/firmware_class.c | 24 | ||||
-rw-r--r-- | drivers/base/platform.c | 80 | ||||
-rw-r--r-- | drivers/base/power/main.c | 9 |
9 files changed, 264 insertions, 82 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 6318f6b57360..d8e8c49c0cbd 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -54,7 +54,7 @@ config FIRMWARE_IN_KERNEL such firmware, and do not wish to use an initrd. This single option controls the inclusion of firmware for - every driver which usees request_firmare() and ships its + every driver which uses request_firmare() and ships its firmware in the kernel source tree, to avoid a proliferation of 'Include firmware for xxx device' options. diff --git a/drivers/base/base.h b/drivers/base/base.h index 31dc0cd84afa..0a5f055dffba 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -54,7 +54,7 @@ struct driver_private { */ struct class_private { struct kset class_subsys; - struct list_head class_devices; + struct klist class_devices; struct list_head class_interfaces; struct kset class_dirs; struct mutex class_mutex; diff --git a/drivers/base/bus.c b/drivers/base/bus.c index ef522ae55480..5aee1c0169ea 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -333,9 +333,7 @@ static int match_name(struct device *dev, void *data) { const char *name = data; - if (strcmp(name, dev->bus_id) == 0) - return 1; - return 0; + return sysfs_streq(name, dev->bus_id); } /** @@ -982,6 +980,56 @@ struct klist *bus_get_device_klist(struct bus_type *bus) } EXPORT_SYMBOL_GPL(bus_get_device_klist); +/* + * Yes, this forcably breaks the klist abstraction temporarily. It + * just wants to sort the klist, not change reference counts and + * take/drop locks rapidly in the process. It does all this while + * holding the lock for the list, so objects can't otherwise be + * added/removed while we're swizzling. + */ +static void device_insertion_sort_klist(struct device *a, struct list_head *list, + int (*compare)(const struct device *a, + const struct device *b)) +{ + struct list_head *pos; + struct klist_node *n; + struct device *b; + + list_for_each(pos, list) { + n = container_of(pos, struct klist_node, n_node); + b = container_of(n, struct device, knode_bus); + if (compare(a, b) <= 0) { + list_move_tail(&a->knode_bus.n_node, + &b->knode_bus.n_node); + return; + } + } + list_move_tail(&a->knode_bus.n_node, list); +} + +void bus_sort_breadthfirst(struct bus_type *bus, + int (*compare)(const struct device *a, + const struct device *b)) +{ + LIST_HEAD(sorted_devices); + struct list_head *pos, *tmp; + struct klist_node *n; + struct device *dev; + struct klist *device_klist; + + device_klist = bus_get_device_klist(bus); + + spin_lock(&device_klist->k_lock); + list_for_each_safe(pos, tmp, &device_klist->k_list) { + n = container_of(pos, struct klist_node, n_node); + dev = container_of(n, struct device, knode_bus); + device_insertion_sort_klist(dev, &sorted_devices, compare); + } + list_splice(&sorted_devices, &device_klist->k_list); + spin_unlock(&device_klist->k_lock); +} +EXPORT_SYMBOL_GPL(bus_sort_breadthfirst); + int __init buses_init(void) { bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL); diff --git a/drivers/base/class.c b/drivers/base/class.c index cc5e28c8885c..eb85e4312301 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -135,6 +135,20 @@ static void remove_class_attrs(struct class *cls) } } +static void klist_class_dev_get(struct klist_node *n) +{ + struct device *dev = container_of(n, struct device, knode_class); + + get_device(dev); +} + +static void klist_class_dev_put(struct klist_node *n) +{ + struct device *dev = container_of(n, struct device, knode_class); + + put_device(dev); +} + int __class_register(struct class *cls, struct lock_class_key *key) { struct class_private *cp; @@ -145,7 +159,7 @@ int __class_register(struct class *cls, struct lock_class_key *key) cp = kzalloc(sizeof(*cp), GFP_KERNEL); if (!cp) return -ENOMEM; - INIT_LIST_HEAD(&cp->class_devices); + klist_init(&cp->class_devices, klist_class_dev_get, klist_class_dev_put); INIT_LIST_HEAD(&cp->class_interfaces); kset_init(&cp->class_dirs); __mutex_init(&cp->class_mutex, "struct class mutex", key); @@ -269,6 +283,71 @@ char *make_class_name(const char *name, struct kobject *kobj) #endif /** + * class_dev_iter_init - initialize class device iterator + * @iter: class iterator to initialize + * @class: the class we wanna iterate over + * @start: the device to start iterating from, if any + * @type: device_type of the devices to iterate over, NULL for all + * + * Initialize class iterator @iter such that it iterates over devices + * of @class. If @start is set, the list iteration will start there, + * otherwise if it is NULL, the iteration starts at the beginning of + * the list. + */ +void class_dev_iter_init(struct class_dev_iter *iter, struct class *class, + struct device *start, const struct device_type *type) +{ + struct klist_node *start_knode = NULL; + + if (start) + start_knode = &start->knode_class; + klist_iter_init_node(&class->p->class_devices, &iter->ki, start_knode); + iter->type = type; +} +EXPORT_SYMBOL_GPL(class_dev_iter_init); + +/** + * class_dev_iter_next - iterate to the next device + * @iter: class iterator to proceed + * + * Proceed @iter to the next device and return it. Returns NULL if + * iteration is complete. + * + * The returned device is referenced and won't be released till + * iterator is proceed to the next device or exited. The caller is + * free to do whatever it wants to do with the device including + * calling back into class code. + */ +struct device *class_dev_iter_next(struct class_dev_iter *iter) +{ + struct klist_node *knode; + struct device *dev; + + while (1) { + knode = klist_next(&iter->ki); + if (!knode) + return NULL; + dev = container_of(knode, struct device, knode_class); + if (!iter->type || iter->type == dev->type) + return dev; + } +} +EXPORT_SYMBOL_GPL(class_dev_iter_next); + +/** + * class_dev_iter_exit - finish iteration + * @iter: class iterator to finish + * + * Finish an iteration. Always call this function after iteration is + * complete whether the iteration ran till the end or not. + */ +void class_dev_iter_exit(struct class_dev_iter *iter) +{ + klist_iter_exit(&iter->ki); +} +EXPORT_SYMBOL_GPL(class_dev_iter_exit); + +/** * class_for_each_device - device iterator * @class: the class we're iterating * @start: the device to start with in the list, if any. @@ -283,13 +362,13 @@ char *make_class_name(const char *name, struct kobject *kobj) * We check the return of @fn each time. If it returns anything * other than 0, we break out and return that value. * - * Note, we hold class->class_mutex in this function, so it can not be - * re-acquired in @fn, otherwise it will self-deadlocking. For - * example, calls to add or remove class members would be verboten. + * @fn is allowed to do anything including calling back into class + * code. There's no locking restriction. */ int class_for_each_device(struct class *class, struct device *start, void *data, int (*fn)(struct device *, void *)) { + struct class_dev_iter iter; struct device *dev; int error = 0; @@ -301,20 +380,13 @@ int class_for_each_device(struct class *class, struct device *start, return -EINVAL; } - mutex_lock(&class->p->class_mutex); - list_for_each_entry(dev, &class->p->class_devices, node) { - if (start) { - if (start == dev) - start = NULL; - continue; - } - dev = get_device(dev); + class_dev_iter_init(&iter, class, start, NULL); + while ((dev = class_dev_iter_next(&iter))) { error = fn(dev, data); - put_device(dev); if (error) break; } - mutex_unlock(&class->p->class_mutex); + class_dev_iter_exit(&iter); return error; } @@ -337,16 +409,15 @@ EXPORT_SYMBOL_GPL(class_for_each_device); * * Note, you will need to drop the reference with put_device() after use. * - * We hold class->class_mutex in this function, so it can not be - * re-acquired in @match, otherwise it will self-deadlocking. For - * example, calls to add or remove class members would be verboten. + * @fn is allowed to do anything including calling back into class + * code. There's no locking restriction. */ struct device *class_find_device(struct class *class, struct device *start, void *data, int (*match)(struct device *, void *)) { + struct class_dev_iter iter; struct device *dev; - int found = 0; if (!class) return NULL; @@ -356,29 +427,23 @@ struct device *class_find_device(struct class *class, struct device *start, return NULL; } - mutex_lock(&class->p->class_mutex); - list_for_each_entry(dev, &class->p->class_devices, node) { - if (start) { - if (start == dev) - start = NULL; - continue; - } - dev = get_device(dev); + class_dev_iter_init(&iter, class, start, NULL); + while ((dev = class_dev_iter_next(&iter))) { if (match(dev, data)) { - found = 1; + get_device(dev); break; - } else - put_device(dev); + } } - mutex_unlock(&class->p->class_mutex); + class_dev_iter_exit(&iter); - return found ? dev : NULL; + return dev; } EXPORT_SYMBOL_GPL(class_find_device); int class_interface_register(struct class_interface *class_intf) { struct class *parent; + struct class_dev_iter iter; struct device *dev; if (!class_intf || !class_intf->class) @@ -391,8 +456,10 @@ int class_interface_register(struct class_interface *class_intf) mutex_lock(&parent->p->class_mutex); list_add_tail(&class_intf->node, &parent->p->class_interfaces); if (class_intf->add_dev) { - list_for_each_entry(dev, &parent->p->class_devices, node) + class_dev_iter_init(&iter, parent, NULL, NULL); + while ((dev = class_dev_iter_next(&iter))) class_intf->add_dev(dev, class_intf); + class_dev_iter_exit(&iter); } mutex_unlock(&parent->p->class_mutex); @@ -402,6 +469,7 @@ int class_interface_register(struct class_interface *class_intf) void class_interface_unregister(struct class_interface *class_intf) { struct class *parent = class_intf->class; + struct class_dev_iter iter; struct device *dev; if (!parent) @@ -410,8 +478,10 @@ void class_interface_unregister(struct class_interface *class_intf) mutex_lock(&parent->p->class_mutex); list_del_init(&class_intf->node); if (class_intf->remove_dev) { - list_for_each_entry(dev, &parent->p->class_devices, node) + class_dev_iter_init(&iter, parent, NULL, NULL); + while ((dev = class_dev_iter_next(&iter))) class_intf->remove_dev(dev, class_intf); + class_dev_iter_exit(&iter); } mutex_unlock(&parent->p->class_mutex); diff --git a/drivers/base/core.c b/drivers/base/core.c index d021c98605b3..8c2cc2648f5a 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -523,11 +523,16 @@ static void klist_children_put(struct klist_node *n) * device_initialize - init device structure. * @dev: device. * - * This prepares the device for use by other layers, - * including adding it to the device hierarchy. + * This prepares the device for use by other layers by initializing + * its fields. * It is the first half of device_register(), if called by - * that, though it can also be called separately, so one - * may use @dev's fields (e.g. the refcount). + * that function, though it can also be called separately, so one + * may use @dev's fields. In particular, get_device()/put_device() + * may be used for reference counting of @dev after calling this + * function. + * + * NOTE: Use put_device() to give up your reference instead of freeing + * @dev directly once you have called this function. */ void device_initialize(struct device *dev) { @@ -536,7 +541,6 @@ void device_initialize(struct device *dev) klist_init(&dev->klist_children, klist_children_get, klist_children_put); INIT_LIST_HEAD(&dev->dma_pools); - INIT_LIST_HEAD(&dev->node); init_MUTEX(&dev->sem); spin_lock_init(&dev->devres_lock); INIT_LIST_HEAD(&dev->devres_head); @@ -836,9 +840,13 @@ static void device_remove_sys_dev_entry(struct device *dev) * This is part 2 of device_register(), though may be called * separately _iff_ device_initialize() has been called separately. * - * This adds it to the kobject hierarchy via kobject_add(), adds it + * This adds @dev to the kobject hierarchy via kobject_add(), adds it * to the global and sibling lists for the device, then * adds it to the other relevant subsystems of the driver model. + * + * NOTE: _Never_ directly free @dev after calling this function, even + * if it returned an error! Always use put_device() to give up your + * reference instead. */ int device_add(struct device *dev) { @@ -916,7 +924,8 @@ int device_add(struct device *dev) if (dev->class) { mutex_lock(&dev->class->p->class_mutex); /* tie the class to the device */ - list_add_tail(&dev->node, &dev->class->p->class_devices); + klist_add_tail(&dev->knode_class, + &dev->class->p->class_devices); /* notify any interfaces that the device is here */ list_for_each_entry(class_intf, @@ -965,6 +974,10 @@ done: * I.e. you should only call the two helpers separately if * have a clearly defined need to use and refcount the device * before it is added to the hierarchy. + * + * NOTE: _Never_ directly free @dev after calling this function, even + * if it returned an error! Always use put_device() to give up the + * reference initialized in this function instead. */ int device_register(struct device *dev) { @@ -1032,7 +1045,7 @@ void device_del(struct device *dev) if (class_intf->remove_dev) class_intf->remove_dev(dev, class_intf); /* remove the device from the class list */ - list_del_init(&dev->node); + klist_del(&dev->knode_class); mutex_unlock(&dev->class->p->class_mutex); } device_remove_file(dev, &uevent_attr); @@ -1243,7 +1256,7 @@ struct device *device_create_vargs(struct class *class, struct device *parent, return dev; error: - kfree(dev); + put_device(dev); return ERR_PTR(retval); } EXPORT_SYMBOL_GPL(device_create_vargs); @@ -1314,6 +1327,11 @@ EXPORT_SYMBOL_GPL(device_destroy); * device_rename - renames a device * @dev: the pointer to the struct device to be renamed * @new_name: the new name of the device + * + * It is the responsibility of the caller to provide mutual + * exclusion between two different calls of device_rename + * on the same device to ensure that new_name is valid and + * won't conflict with other devices. */ int device_rename(struct device *dev, char *new_name) { diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 3ac443b2ac08..20febc00a525 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -257,6 +257,9 @@ static int __driver_attach(struct device *dev, void *data) * is an error. */ + if (drv->bus->match && !drv->bus->match(dev, drv)) + return 0; + if (dev->parent) /* Needed for USB */ down(&dev->parent->sem); down(&dev->sem); diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index c9c92b00fd55..b7e571031ecd 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -164,8 +164,7 @@ static ssize_t firmware_loading_store(struct device *dev, } /* fallthrough */ default: - printk(KERN_ERR "%s: unexpected value (%d)\n", __func__, - loading); + dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading); /* fallthrough */ case -1: fw_load_abort(fw_priv); @@ -309,7 +308,7 @@ static int fw_register_device(struct device **dev_p, const char *fw_name, *dev_p = NULL; if (!fw_priv || !f_dev) { - printk(KERN_ERR "%s: kmalloc failed\n", __func__); + dev_err(device, "%s: kmalloc failed\n", __func__); retval = -ENOMEM; goto error_kfree; } @@ -329,8 +328,7 @@ static int fw_register_device(struct device **dev_p, const char *fw_name, f_dev->uevent_suppress = 1; retval = device_register(f_dev); if (retval) { - printk(KERN_ERR "%s: device_register failed\n", - __func__); + dev_err(device, "%s: device_register failed\n", __func__); goto error_kfree; } *dev_p = f_dev; @@ -363,15 +361,13 @@ static int fw_setup_device(struct firmware *fw, struct device **dev_p, fw_priv->fw = fw; retval = sysfs_create_bin_file(&f_dev->kobj, &fw_priv->attr_data); if (retval) { - printk(KERN_ERR "%s: sysfs_create_bin_file failed\n", - __func__); + dev_err(device, "%s: sysfs_create_bin_file failed\n", __func__); goto error_unreg; } retval = device_create_file(f_dev, &dev_attr_loading); if (retval) { - printk(KERN_ERR "%s: device_create_file failed\n", - __func__); + dev_err(device, "%s: device_create_file failed\n", __func__); goto error_unreg; } @@ -401,8 +397,8 @@ _request_firmware(const struct firmware **firmware_p, const char *name, *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); if (!firmware) { - printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n", - __func__); + dev_err(device, "%s: kmalloc(struct firmware) failed\n", + __func__); retval = -ENOMEM; goto out; } @@ -411,15 +407,15 @@ _request_firmware(const struct firmware **firmware_p, const char *name, builtin++) { if (strcmp(name, builtin->name)) continue; - printk(KERN_INFO "firmware: using built-in firmware %s\n", - name); + dev_info(device, "firmware: using built-in firmware %s\n", + name); firmware->size = builtin->size; firmware->data = builtin->data; return 0; } if (uevent) - printk(KERN_INFO "firmware: requesting %s\n", name); + dev_info(device, "firmware: requesting %s\n", name); retval = fw_setup_device(firmware, &f_dev, name, device, uevent); if (retval) diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 3f940393d6c7..dfcbfe504867 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -42,10 +42,8 @@ struct resource *platform_get_resource(struct platform_device *dev, for (i = 0; i < dev->num_resources; i++) { struct resource *r = &dev->resource[i]; - if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM| - IORESOURCE_IRQ|IORESOURCE_DMA)) == type) - if (num-- == 0) - return r; + if (type == resource_type(r) && num-- == 0) + return r; } return NULL; } @@ -78,10 +76,8 @@ struct resource *platform_get_resource_byname(struct platform_device *dev, for (i = 0; i < dev->num_resources; i++) { struct resource *r = &dev->resource[i]; - if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM| - IORESOURCE_IRQ|IORESOURCE_DMA)) == type) - if (!strcmp(r->name, name)) - return r; + if (type == resource_type(r) && !strcmp(r->name, name)) + return r; } return NULL; } @@ -259,9 +255,9 @@ int platform_device_add(struct platform_device *pdev) p = r->parent; if (!p) { - if (r->flags & IORESOURCE_MEM) + if (resource_type(r) == IORESOURCE_MEM) p = &iomem_resource; - else if (r->flags & IORESOURCE_IO) + else if (resource_type(r) == IORESOURCE_IO) p = &ioport_resource; } @@ -282,9 +278,14 @@ int platform_device_add(struct platform_device *pdev) return ret; failed: - while (--i >= 0) - if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO)) - release_resource(&pdev->resource[i]); + while (--i >= 0) { + struct resource *r = &pdev->resource[i]; + unsigned long type = resource_type(r); + + if (type == IORESOURCE_MEM || type == IORESOURCE_IO) + release_resource(r); + } + return ret; } EXPORT_SYMBOL_GPL(platform_device_add); @@ -306,7 +307,9 @@ void platform_device_del(struct platform_device *pdev) for (i = 0; i < pdev->num_resources; i++) { struct resource *r = &pdev->resource[i]; - if (r->flags & (IORESOURCE_MEM|IORESOURCE_IO)) + unsigned long type = resource_type(r); + + if (type == IORESOURCE_MEM || type == IORESOURCE_IO) release_resource(r); } } @@ -391,6 +394,53 @@ error: } EXPORT_SYMBOL_GPL(platform_device_register_simple); +/** + * platform_device_register_data + * @parent: parent device for the device we're adding + * @name: base name of the device we're adding + * @id: instance id + * @data: platform specific data for this platform device + * @size: size of platform specific data + * + * This function creates a simple platform device that requires minimal + * resource and memory management. Canned release function freeing memory + * allocated for the device allows drivers using such devices to be + * unloaded without waiting for the last reference to the device to be + * dropped. + */ +struct platform_device *platform_device_register_data( + struct device *parent, + const char *name, int id, + const void *data, size_t size) +{ + struct platform_device *pdev; + int retval; + + pdev = platform_device_alloc(name, id); + if (!pdev) { + retval = -ENOMEM; + goto error; + } + + pdev->dev.parent = parent; + + if (size) { + retval = platform_device_add_data(pdev, data, size); + if (retval) + goto error; + } + + retval = platform_device_add(pdev); + if (retval) + goto error; + + return pdev; + +error: + platform_device_put(pdev); + return ERR_PTR(retval); +} + static int platform_drv_probe(struct device *_dev) { struct platform_driver *drv = to_platform_driver(_dev->driver); @@ -862,7 +912,7 @@ static int platform_pm_restore_noirq(struct device *dev) #endif /* !CONFIG_HIBERNATION */ -struct pm_ext_ops platform_pm_ops = { +static struct pm_ext_ops platform_pm_ops = { .base = { .prepare = platform_pm_prepare, .complete = platform_pm_complete, diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 273a944d4040..692c20ba5144 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -83,7 +83,7 @@ void device_pm_add(struct device *dev) * transition is in progress in order to avoid leaving them * unhandled down the road */ - WARN_ON(true); + dev_WARN(dev, "Parentless device registered during a PM transaction\n"); } list_add_tail(&dev->power.entry, &dpm_list); @@ -778,10 +778,7 @@ EXPORT_SYMBOL_GPL(device_suspend); void __suspend_report_result(const char *function, void *fn, int ret) { - if (ret) { - printk(KERN_ERR "%s(): ", function); - print_fn_descriptor_symbol("%s returns ", fn); - printk("%d\n", ret); - } + if (ret) + printk(KERN_ERR "%s(): %pF returns %d\n", function, fn, ret); } EXPORT_SYMBOL_GPL(__suspend_report_result); |