From be871b7e54711479d3b9d3617d49898770830db2 Mon Sep 17 00:00:00 2001 From: Michal Hocko Date: Tue, 12 Mar 2013 17:21:19 +0100 Subject: device: separate all subsys mutexes ca22e56d (driver-core: implement 'sysdev' functionality for regular devices and buses) has introduced bus_register macro with a static key to distinguish different subsys mutex classes. This however doesn't work for different subsys which use a common registering function. One example is subsys_system_register (and mce_device and cpu_device). In the end this leads to the following lockdep splat: [ 207.271924] ====================================================== [ 207.271932] [ INFO: possible circular locking dependency detected ] [ 207.271942] 3.9.0-rc1-0.7-default+ #34 Not tainted [ 207.271948] ------------------------------------------------------- [ 207.271957] bash/10493 is trying to acquire lock: [ 207.271963] (subsys mutex){+.+.+.}, at: [] bus_remove_device+0x37/0x1c0 [ 207.271987] [ 207.271987] but task is already holding lock: [ 207.271995] (cpu_hotplug.lock){+.+.+.}, at: [] cpu_hotplug_begin+0x2f/0x60 [ 207.272012] [ 207.272012] which lock already depends on the new lock. [ 207.272012] [ 207.272023] [ 207.272023] the existing dependency chain (in reverse order) is: [ 207.272033] [ 207.272033] -> #4 (cpu_hotplug.lock){+.+.+.}: [ 207.272044] [] lock_acquire+0xe9/0x120 [ 207.272056] [] mutex_lock_nested+0x37/0x360 [ 207.272069] [] get_online_cpus+0x29/0x40 [ 207.272082] [] drain_all_stock+0x30/0x150 [ 207.272094] [] mem_cgroup_reclaim+0xaa/0xe0 [ 207.272104] [] __mem_cgroup_try_charge+0x51e/0xcf0 [ 207.272114] [] mem_cgroup_charge_common+0x36/0x60 [ 207.272125] [] mem_cgroup_newpage_charge+0x2a/0x30 [ 207.272135] [] do_wp_page+0x231/0x830 [ 207.272147] [] handle_pte_fault+0x19e/0x8d0 [ 207.272157] [] handle_mm_fault+0x158/0x1e0 [ 207.272166] [] do_page_fault+0x2a3/0x4e0 [ 207.272178] [] page_fault+0x28/0x30 [ 207.272189] [ 207.272189] -> #3 (&mm->mmap_sem){++++++}: [ 207.272199] [] lock_acquire+0xe9/0x120 [ 207.272208] [] might_fault+0x6d/0x90 [ 207.272218] [] filldir64+0xb3/0x120 [ 207.272229] [] call_filldir+0x89/0x130 [ext3] [ 207.272248] [] ext3_readdir+0x6b7/0x7e0 [ext3] [ 207.272263] [] vfs_readdir+0xa9/0xc0 [ 207.272273] [] sys_getdents64+0x9b/0x110 [ 207.272284] [] system_call_fastpath+0x16/0x1b [ 207.272296] [ 207.272296] -> #2 (&type->i_mutex_dir_key#3){+.+.+.}: [ 207.272309] [] lock_acquire+0xe9/0x120 [ 207.272319] [] mutex_lock_nested+0x37/0x360 [ 207.272329] [] link_path_walk+0x6f4/0x9a0 [ 207.272339] [] path_openat+0xba/0x470 [ 207.272349] [] do_filp_open+0x48/0xa0 [ 207.272358] [] file_open_name+0xdc/0x110 [ 207.272369] [] filp_open+0x35/0x40 [ 207.272378] [] _request_firmware+0x52e/0xb20 [ 207.272389] [] request_firmware+0x16/0x20 [ 207.272399] [] request_microcode_fw+0x61/0xd0 [microcode] [ 207.272416] [] microcode_init_cpu+0x104/0x150 [microcode] [ 207.272431] [] mc_device_add+0x7c/0xb0 [microcode] [ 207.272444] [] subsys_interface_register+0xc9/0x100 [ 207.272457] [] 0xffffffffa04fc0f4 [ 207.272472] [] do_one_initcall+0x42/0x180 [ 207.272485] [] load_module+0x19df/0x1b70 [ 207.272499] [] sys_init_module+0xe6/0x130 [ 207.272511] [] system_call_fastpath+0x16/0x1b [ 207.272523] [ 207.272523] -> #1 (umhelper_sem){++++.+}: [ 207.272537] [] lock_acquire+0xe9/0x120 [ 207.272548] [] down_read+0x34/0x50 [ 207.272559] [] usermodehelper_read_trylock+0x4f/0x100 [ 207.272575] [] _request_firmware+0x59d/0xb20 [ 207.272587] [] request_firmware+0x16/0x20 [ 207.272599] [] request_microcode_fw+0x61/0xd0 [microcode] [ 207.272613] [] microcode_init_cpu+0x104/0x150 [microcode] [ 207.272627] [] mc_device_add+0x7c/0xb0 [microcode] [ 207.272641] [] subsys_interface_register+0xc9/0x100 [ 207.272654] [] 0xffffffffa04fc0f4 [ 207.272666] [] do_one_initcall+0x42/0x180 [ 207.272678] [] load_module+0x19df/0x1b70 [ 207.272690] [] sys_init_module+0xe6/0x130 [ 207.272702] [] system_call_fastpath+0x16/0x1b [ 207.272715] [ 207.272715] -> #0 (subsys mutex){+.+.+.}: [ 207.272729] [] __lock_acquire+0x13b2/0x15f0 [ 207.272740] [] lock_acquire+0xe9/0x120 [ 207.272751] [] mutex_lock_nested+0x37/0x360 [ 207.272763] [] bus_remove_device+0x37/0x1c0 [ 207.272775] [] device_del+0x134/0x1f0 [ 207.272786] [] device_unregister+0x22/0x60 [ 207.272798] [] mce_cpu_callback+0x15e/0x1ad [ 207.272812] [] notifier_call_chain+0x72/0x130 [ 207.272824] [] __raw_notifier_call_chain+0xe/0x10 [ 207.272839] [] _cpu_down+0x1d6/0x350 [ 207.272851] [] cpu_down+0x40/0x60 [ 207.272862] [] store_online+0x75/0xe0 [ 207.272874] [] dev_attr_store+0x20/0x30 [ 207.272886] [] sysfs_write_file+0xd9/0x150 [ 207.272900] [] vfs_write+0xcb/0x130 [ 207.272911] [] sys_write+0x64/0xa0 [ 207.272923] [] system_call_fastpath+0x16/0x1b [ 207.272936] [ 207.272936] other info that might help us debug this: [ 207.272936] [ 207.272952] Chain exists of: [ 207.272952] subsys mutex --> &mm->mmap_sem --> cpu_hotplug.lock [ 207.272952] [ 207.272973] Possible unsafe locking scenario: [ 207.272973] [ 207.272984] CPU0 CPU1 [ 207.272992] ---- ---- [ 207.273000] lock(cpu_hotplug.lock); [ 207.273009] lock(&mm->mmap_sem); [ 207.273020] lock(cpu_hotplug.lock); [ 207.273031] lock(subsys mutex); [ 207.273040] [ 207.273040] *** DEADLOCK *** [ 207.273040] [ 207.273055] 5 locks held by bash/10493: [ 207.273062] #0: (&buffer->mutex){+.+.+.}, at: [] sysfs_write_file+0x49/0x150 [ 207.273080] #1: (s_active#150){.+.+.+}, at: [] sysfs_write_file+0xc2/0x150 [ 207.273099] #2: (x86_cpu_hotplug_driver_mutex){+.+.+.}, at: [] cpu_hotplug_driver_lock+0x17/0x20 [ 207.273121] #3: (cpu_add_remove_lock){+.+.+.}, at: [] cpu_down+0x2c/0x60 [ 207.273140] #4: (cpu_hotplug.lock){+.+.+.}, at: [] cpu_hotplug_begin+0x2f/0x60 [ 207.273158] [ 207.273158] stack backtrace: [ 207.273170] Pid: 10493, comm: bash Not tainted 3.9.0-rc1-0.7-default+ #34 [ 207.273180] Call Trace: [ 207.273192] [] print_circular_bug+0x223/0x310 [ 207.273204] [] __lock_acquire+0x13b2/0x15f0 [ 207.273216] [] ? sysfs_hash_and_remove+0x60/0xc0 [ 207.273227] [] lock_acquire+0xe9/0x120 [ 207.273239] [] ? bus_remove_device+0x37/0x1c0 [ 207.273251] [] mutex_lock_nested+0x37/0x360 [ 207.273263] [] ? bus_remove_device+0x37/0x1c0 [ 207.273274] [] ? sysfs_hash_and_remove+0x60/0xc0 [ 207.273286] [] bus_remove_device+0x37/0x1c0 [ 207.273298] [] device_del+0x134/0x1f0 [ 207.273309] [] device_unregister+0x22/0x60 [ 207.273321] [] mce_cpu_callback+0x15e/0x1ad [ 207.273332] [] notifier_call_chain+0x72/0x130 [ 207.273344] [] __raw_notifier_call_chain+0xe/0x10 [ 207.273356] [] _cpu_down+0x1d6/0x350 [ 207.273368] [] ? cpu_hotplug_driver_lock+0x17/0x20 [ 207.273380] [] cpu_down+0x40/0x60 [ 207.273391] [] store_online+0x75/0xe0 [ 207.273402] [] dev_attr_store+0x20/0x30 [ 207.273413] [] sysfs_write_file+0xd9/0x150 [ 207.273425] [] vfs_write+0xcb/0x130 [ 207.273436] [] sys_write+0x64/0xa0 [ 207.273447] [] system_call_fastpath+0x16/0x1b Which reports a false possitive deadlock because it sees: 1) load_module -> subsys_interface_register -> mc_deveice_add (*) -> subsys->p->mutex -> link_path_walk -> lookup_slow -> i_mutex 2) sys_write -> _cpu_down -> cpu_hotplug_begin -> cpu_hotplug.lock -> mce_cpu_callback -> mce_device_remove(**) -> device_unregister -> bus_remove_device -> subsys mutex 3) vfs_readdir -> i_mutex -> filldir64 -> might_fault -> might_lock_read(mmap_sem) -> page_fault -> mmap_sem -> drain_all_stock -> cpu_hotplug.lock but 1) takes cpu_subsys subsys (*) but 2) takes mce_device subsys (**) so the deadlock is not possible AFAICS. The fix is quite simple. We can pull the key inside bus_type structure because they are defined per device so the pointer will be unique as well. bus_register doesn't need to be a macro anymore so change it to the inline. We could get rid of __bus_register as there is no other caller but maybe somebody will want to use a different key so keep it around for now. Reported-by: Li Zefan Signed-off-by: Michal Hocko Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 519865b..8a00dec 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -898,18 +898,18 @@ static ssize_t bus_uevent_store(struct bus_type *bus, static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store); /** - * __bus_register - register a driver-core subsystem + * bus_register - register a driver-core subsystem * @bus: bus to register - * @key: lockdep class key * * Once we have that, we register the bus with the kobject * infrastructure, then register the children subsystems it has: * the devices and drivers that belong to the subsystem. */ -int __bus_register(struct bus_type *bus, struct lock_class_key *key) +int bus_register(struct bus_type *bus) { int retval; struct subsys_private *priv; + struct lock_class_key *key = &bus->lock_key; priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); if (!priv) @@ -981,7 +981,7 @@ out: bus->p = NULL; return retval; } -EXPORT_SYMBOL_GPL(__bus_register); +EXPORT_SYMBOL_GPL(bus_register); /** * bus_unregister - remove a bus from the system diff --git a/include/linux/device.h b/include/linux/device.h index 9d6464e..4a7c4a8 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -111,17 +111,11 @@ struct bus_type { struct iommu_ops *iommu_ops; struct subsys_private *p; + struct lock_class_key lock_key; }; -/* This is a #define to keep the compiler from merging different - * instances of the __key variable */ -#define bus_register(subsys) \ -({ \ - static struct lock_class_key __key; \ - __bus_register(subsys, &__key); \ -}) -extern int __must_check __bus_register(struct bus_type *bus, - struct lock_class_key *key); +extern int __must_check bus_register(struct bus_type *bus); + extern void bus_unregister(struct bus_type *bus); extern int __must_check bus_rescan_devices(struct bus_type *bus); -- cgit v0.10.2 From 8f46baaa7ec6cd0851794020b31958e64679dd26 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 20 Feb 2013 10:31:42 +0200 Subject: base: core: WARN() about bogus permissions on device attributes Whenever a struct device_attribute is registered with mismatched permissions - read permission without a show routine or write permission without store routine - we will issue a big warning so we catch those early enough. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/base/core.c b/drivers/base/core.c index 56536f4b0..a7391a3 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -563,8 +563,15 @@ int device_create_file(struct device *dev, const struct device_attribute *attr) { int error = 0; - if (dev) + + if (dev) { + WARN(((attr->attr.mode & S_IWUGO) && !attr->store), + "Write permission without 'store'\n"); + WARN(((attr->attr.mode & S_IRUGO) && !attr->show), + "Read permission without 'show'\n"); error = sysfs_create_file(&dev->kobj, &attr->attr); + } + return error; } -- cgit v0.10.2 From 3db3c62584fbafee52a068035cc4c57e7b921acf Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Fri, 8 Mar 2013 16:07:27 +0100 Subject: sysfs: use atomic_inc_unless_negative in sysfs_get_active It seems that sysfs has an interesting way of doing the same thing. This removes the cpu_relax unfortunately, but if it's really needed, it would be better to add this to include/linux/atomic.h to benefit all atomic ops users. Signed-off-by: Maarten Lankhorst Acked-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 2fbdff6..7f968ed 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -165,21 +165,8 @@ struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) if (unlikely(!sd)) return NULL; - while (1) { - int v, t; - - v = atomic_read(&sd->s_active); - if (unlikely(v < 0)) - return NULL; - - t = atomic_cmpxchg(&sd->s_active, v, v + 1); - if (likely(t == v)) - break; - if (t < 0) - return NULL; - - cpu_relax(); - } + if (!atomic_inc_unless_negative(&sd->s_active)) + return NULL; if (likely(!ignore_lockdep(sd))) rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_); -- cgit v0.10.2 From 647c86d0a2d1131dd9412361958d8e0aae949523 Mon Sep 17 00:00:00 2001 From: Fabio Porcedda Date: Tue, 26 Mar 2013 10:35:15 +0100 Subject: driver core: warn that platform_driver_probe can not use deferred probing Add documentation that platform_driver_probe() is incompatible with deferred probing. Signed-off-by: Fabio Porcedda Cc: Arnd Bergmann Cc: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/base/platform.c b/drivers/base/platform.c index c0b8df3..ef2afb1 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -555,7 +555,8 @@ EXPORT_SYMBOL_GPL(platform_driver_unregister); /** * platform_driver_probe - register driver for non-hotpluggable device * @drv: platform driver structure - * @probe: the driver probe routine, probably from an __init section + * @probe: the driver probe routine, probably from an __init section, + * must not return -EPROBE_DEFER. * * Use this instead of platform_driver_register() when you know the device * is not hotpluggable and has already been registered, and you want to @@ -566,6 +567,9 @@ EXPORT_SYMBOL_GPL(platform_driver_unregister); * into system-on-chip processors, where the controller devices have been * configured as part of board setup. * + * This is incompatible with deferred probing so probe() must not + * return -EPROBE_DEFER. + * * Returns zero if the driver registered and bound to a device, else returns * a negative error code and with the driver not registered. */ -- cgit v0.10.2 From 0258e182e7a8db812817918f1417d31527a5f89b Mon Sep 17 00:00:00 2001 From: Fabio Porcedda Date: Tue, 26 Mar 2013 10:35:16 +0100 Subject: driver core: platform.c: fix checkpatch errors and warnings Signed-off-by: Fabio Porcedda Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/base/platform.c b/drivers/base/platform.c index ef2afb1..9eda842 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -46,8 +46,8 @@ EXPORT_SYMBOL_GPL(platform_bus); * manipulate any relevant information in the pdev_archdata they can do: * * platform_device_alloc() - * ... manipulate ... - * platform_device_add() + * ... manipulate ... + * platform_device_add() * * And if they don't care they can just call platform_device_register() and * everything will just work out. @@ -326,9 +326,7 @@ int platform_device_add(struct platform_device *pdev) } if (p && insert_resource(p, r)) { - printk(KERN_ERR - "%s: failed to claim resource %d\n", - dev_name(&pdev->dev), i); + dev_err(&pdev->dev, "failed to claim resource %d\n", i); ret = -EBUSY; goto failed; } @@ -686,7 +684,7 @@ static int platform_uevent(struct device *dev, struct kobj_uevent_env *env) int rc; /* Some devices have extra OF data and an OF-style MODALIAS */ - rc = of_device_uevent_modalias(dev,env); + rc = of_device_uevent_modalias(dev, env); if (rc != -ENODEV) return rc; @@ -1130,8 +1128,8 @@ static int __init early_platform_driver_probe_id(char *class_str, switch (match_id) { case EARLY_PLATFORM_ID_ERROR: - pr_warning("%s: unable to parse %s parameter\n", - class_str, epdrv->pdrv->driver.name); + pr_warn("%s: unable to parse %s parameter\n", + class_str, epdrv->pdrv->driver.name); /* fall-through */ case EARLY_PLATFORM_ID_UNSET: match = NULL; @@ -1162,8 +1160,8 @@ static int __init early_platform_driver_probe_id(char *class_str, } if (epdrv->pdrv->probe(match)) - pr_warning("%s: unable to probe %s early.\n", - class_str, match->name); + pr_warn("%s: unable to probe %s early.\n", + class_str, match->name); else n++; } -- cgit v0.10.2 From 6ae07f27ab202069bd567967a0099070eb7f77d5 Mon Sep 17 00:00:00 2001 From: Fabio Porcedda Date: Tue, 26 Mar 2013 10:35:17 +0100 Subject: driver core: platform_device.h: fix checkpatch errors and warnings Signed-off-by: Fabio Porcedda Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index c082c71..9abf1db 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -20,12 +20,12 @@ struct mfd_cell; struct platform_device { - const char * name; + const char *name; int id; bool id_auto; struct device dev; u32 num_resources; - struct resource * resource; + struct resource *resource; const struct platform_device_id *id_entry; @@ -47,9 +47,12 @@ extern struct bus_type platform_bus_type; extern struct device platform_bus; extern void arch_setup_pdev_archdata(struct platform_device *); -extern struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int); +extern struct resource *platform_get_resource(struct platform_device *, + unsigned int, unsigned int); extern int platform_get_irq(struct platform_device *, unsigned int); -extern struct resource *platform_get_resource_byname(struct platform_device *, unsigned int, const char *); +extern struct resource *platform_get_resource_byname(struct platform_device *, + unsigned int, + const char *); extern int platform_get_irq_byname(struct platform_device *, const char *); extern int platform_add_devices(struct platform_device **, int); @@ -161,7 +164,8 @@ extern struct platform_device *platform_device_alloc(const char *name, int id); extern int platform_device_add_resources(struct platform_device *pdev, const struct resource *res, unsigned int num); -extern int platform_device_add_data(struct platform_device *pdev, const void *data, size_t size); +extern int platform_device_add_data(struct platform_device *pdev, + const void *data, size_t size); extern int platform_device_add(struct platform_device *pdev); extern void platform_device_del(struct platform_device *pdev); extern void platform_device_put(struct platform_device *pdev); @@ -190,7 +194,8 @@ static inline void *platform_get_drvdata(const struct platform_device *pdev) return dev_get_drvdata(&pdev->dev); } -static inline void platform_set_drvdata(struct platform_device *pdev, void *data) +static inline void platform_set_drvdata(struct platform_device *pdev, + void *data) { dev_set_drvdata(&pdev->dev, data); } @@ -222,10 +227,10 @@ static void __exit __platform_driver##_exit(void) \ } \ module_exit(__platform_driver##_exit); -extern struct platform_device *platform_create_bundle(struct platform_driver *driver, - int (*probe)(struct platform_device *), - struct resource *res, unsigned int n_res, - const void *data, size_t size); +extern struct platform_device *platform_create_bundle( + struct platform_driver *driver, int (*probe)(struct platform_device *), + struct resource *res, unsigned int n_res, + const void *data, size_t size); /* early platform driver interface */ struct early_platform_driver { -- cgit v0.10.2 From eca4549f57a19f8881dcd7b9cef719b3452003c0 Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Thu, 28 Mar 2013 16:15:35 +0800 Subject: sysfs: Add crash_notes_size to export percpu note size For percpu notes, we are exporting only address and not size. So the userspace tool kexec-tools is putting an upper limit of 1024 and putting the value in p_memsz and p_filesz fields. So the patch add the new sysfile crash_notes_size to export the exact percpu note size and let the kexec-tools parse it intead of using 1024. The idea came from Vivek Goyal. And a later patch will be sent to kexec-tools to let it parse the size. Cc: "Eric W. Biederman" Cc: Vivek Goyal Signed-off-by: Zhang Yanfei Acked-by: Simon Horman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index fb10728..a55b590 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -132,6 +132,17 @@ static ssize_t show_crash_notes(struct device *dev, struct device_attribute *att return rc; } static DEVICE_ATTR(crash_notes, 0400, show_crash_notes, NULL); + +static ssize_t show_crash_notes_size(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t rc; + + rc = sprintf(buf, "%lu\n", sizeof(note_buf_t)); + return rc; +} +static DEVICE_ATTR(crash_notes_size, 0400, show_crash_notes_size, NULL); #endif /* @@ -259,6 +270,9 @@ int __cpuinit register_cpu(struct cpu *cpu, int num) #ifdef CONFIG_KEXEC if (!error) error = device_create_file(&cpu->dev, &dev_attr_crash_notes); + if (!error) + error = device_create_file(&cpu->dev, + &dev_attr_crash_notes_size); #endif return error; } -- cgit v0.10.2 From c4fd675f584a17bd8ceb4fcbf29af12be7ac2a57 Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Thu, 28 Mar 2013 16:16:45 +0800 Subject: Documentation: Add ABI entry for crash_notes and crash_notes_size Add an Documentation/ABI entry for /sys/devices/system/cpu/cpu0/crash_notes and /sys/devices/system/cpu/cpu0/crash_notes_size. Cc: "Eric W. Biederman" Cc: Vivek Goyal Signed-off-by: Zhang Yanfei Acked-by: Simon Horman Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu index 9c978dc..2447698 100644 --- a/Documentation/ABI/testing/sysfs-devices-system-cpu +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu @@ -173,3 +173,15 @@ Description: Processor frequency boosting control Boosting allows the CPU and the firmware to run at a frequency beyound it's nominal limit. More details can be found in Documentation/cpu-freq/boost.txt + + +What: /sys/devices/system/cpu/cpu#/crash_notes + /sys/devices/system/cpu/cpu#/crash_notes_size +Date: April 2013 +Contact: kexec@lists.infradead.org +Description: address and size of the percpu note. + + crash_notes: the physical address of the memory that holds the + note of cpu#. + + crash_notes_size: size of the note of cpu#. -- cgit v0.10.2 From 928c0c1571b004917a989c4d92b09ac129ad68b2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 29 Mar 2013 09:34:32 -0700 Subject: rtmutex-tester: fix mode of sysfs files This properly sets the modes of the sysfs files. Reported-by: Fengguang Wu Cc: Kay Sievers Cc: Felipe Balbi Cc: Clark Williams Cc: Ingo Molnar Signed-off-by: Greg Kroah-Hartman diff --git a/kernel/rtmutex-tester.c b/kernel/rtmutex-tester.c index 7890b10..55ad765 100644 --- a/kernel/rtmutex-tester.c +++ b/kernel/rtmutex-tester.c @@ -366,8 +366,8 @@ static ssize_t sysfs_test_status(struct device *dev, struct device_attribute *at return curr - buf; } -static DEVICE_ATTR(status, 0600, sysfs_test_status, NULL); -static DEVICE_ATTR(command, 0600, NULL, sysfs_test_command); +static DEVICE_ATTR(status, 0400, sysfs_test_status, NULL); +static DEVICE_ATTR(command, 0200, NULL, sysfs_test_command); static struct bus_type rttest_subsys = { .name = "rttest", -- cgit v0.10.2 From f7db5e7660b122142410dcf36ba903c73d473250 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Tue, 2 Apr 2013 10:12:26 +0800 Subject: sysfs: fix use after free in case of concurrent read/write and readdir The inode->i_mutex isn't hold when updating filp->f_pos in read()/write(), so the filp->f_pos might be read as 0 or 1 in readdir() when there is concurrent read()/write() on this same file, then may cause use after free in readdir(). The bug can be reproduced with Li Zefan's test code on the link: https://patchwork.kernel.org/patch/2160771/ This patch fixes the use after free under this situation. Cc: stable Reported-by: Li Zefan Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index c6f54ab..1bf016b 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -999,6 +999,7 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) enum kobj_ns_type type; const void *ns; ino_t ino; + loff_t off; type = sysfs_ns_type(parent_sd); ns = sysfs_info(dentry->d_sb)->ns[type]; @@ -1021,6 +1022,7 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) return 0; } mutex_lock(&sysfs_mutex); + off = filp->f_pos; for (pos = sysfs_dir_pos(ns, parent_sd, filp->f_pos, pos); pos; pos = sysfs_dir_next_pos(ns, parent_sd, filp->f_pos, pos)) { @@ -1032,19 +1034,24 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) len = strlen(name); ino = pos->s_ino; type = dt_type(pos); - filp->f_pos = pos->s_hash; + off = filp->f_pos = pos->s_hash; filp->private_data = sysfs_get(pos); mutex_unlock(&sysfs_mutex); - ret = filldir(dirent, name, len, filp->f_pos, ino, type); + ret = filldir(dirent, name, len, off, ino, type); mutex_lock(&sysfs_mutex); if (ret < 0) break; } mutex_unlock(&sysfs_mutex); - if ((filp->f_pos > 1) && !pos) { /* EOF */ - filp->f_pos = INT_MAX; + + /* don't reference last entry if its refcount is dropped */ + if (!pos) { filp->private_data = NULL; + + /* EOF and not changed as 0 or 1 in read/write path */ + if (off == filp->f_pos && off > 1) + filp->f_pos = INT_MAX; } return 0; } -- cgit v0.10.2 From bcfb87fb75fa3a9b96c8a73d19166897d167fe3f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 3 Apr 2013 15:18:24 +0000 Subject: sysfs: fix crash_notes_size build warning commit eca4549f57 "sysfs: Add crash_notes_size to export percpu note size" adds a printk that outputs a size_t value as %lu when it should be %zu, resulting in this warning. drivers/base/cpu.c: In function 'show_crash_notes_size': drivers/base/cpu.c:142:2: warning: format '%lu' expects argument of type 'long unsigned int', but argument 3 has type 'unsigned int' [-Wformat=] Reported-by: kbuild test robot Signed-off-by: Arnd Bergmann Cc: Zhang Yanfei Cc: Simon Horman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index a55b590..d8c7f3e 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -139,7 +139,7 @@ static ssize_t show_crash_notes_size(struct device *dev, { ssize_t rc; - rc = sprintf(buf, "%lu\n", sizeof(note_buf_t)); + rc = sprintf(buf, "%zu\n", sizeof(note_buf_t)); return rc; } static DEVICE_ATTR(crash_notes_size, 0400, show_crash_notes_size, NULL); -- cgit v0.10.2 From bb2b0051d7b0772ea9d0b4be900c2d965093f5d7 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Thu, 4 Apr 2013 22:22:37 +0800 Subject: sysfs: check if one entry has been removed before freeing It might be a kernel disaster if one sysfs entry is freed but still referenced by sysfs tree. Recently Dave and Sasha reported one use-after-free problem on sysfs entry, and the problem has been troubleshooted with help of debug message added in this patch. Given sysfs_get_dirent/sysfs_put are exported APIs, even inside sysfs they are called in many contexts(kobject/attribe add/delete, inode init/drop, dentry lookup/release, readdir, ...), it is healthful to check the removed flag before freeing one entry and dump message if it is freeing without being removed first. Cc: Dave Jones Cc: Sasha Levin Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 1bf016b..e8e0e71 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -268,6 +268,10 @@ void release_sysfs_dirent(struct sysfs_dirent * sd) */ parent_sd = sd->s_parent; + WARN(!(sd->s_flags & SYSFS_FLAG_REMOVED), + "sysfs: free using entry: %s/%s\n", + parent_sd ? parent_sd->s_name : "", sd->s_name); + if (sysfs_type(sd) == SYSFS_KOBJ_LINK) sysfs_put(sd->s_symlink.target_sd); if (sysfs_type(sd) & SYSFS_COPY_NAME) @@ -386,7 +390,7 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) sd->s_name = name; sd->s_mode = mode; - sd->s_flags = type; + sd->s_flags = type | SYSFS_FLAG_REMOVED; return sd; @@ -466,6 +470,9 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME; } + /* Mark the entry added into directory tree */ + sd->s_flags &= ~SYSFS_FLAG_REMOVED; + return 0; } -- cgit v0.10.2 From 3c2670e6515cf584810f417db9b00992c8b2d75a Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Sat, 6 Apr 2013 09:56:00 -0700 Subject: driver core: add uid and gid to devtmpfs Some drivers want to tell userspace what uid and gid should be used for their device nodes, so allow that information to percolate through the driver core to userspace in order to make this happen. This means that some systems (i.e. Android and friends) will not need to even run a udev-like daemon for their device node manager and can just rely in devtmpfs fully, reducing their footprint even more. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman diff --git a/block/genhd.c b/block/genhd.c index 3c001fba..dfcec43 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1111,7 +1111,8 @@ struct class block_class = { .name = "block", }; -static char *block_devnode(struct device *dev, umode_t *mode) +static char *block_devnode(struct device *dev, umode_t *mode, + uid_t *uid, gid_t *gid) { struct gendisk *disk = dev_to_disk(dev); diff --git a/drivers/base/core.c b/drivers/base/core.c index a7391a3..8a428b5 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -283,15 +283,21 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, const char *tmp; const char *name; umode_t mode = 0; + uid_t uid = 0; + gid_t gid = 0; add_uevent_var(env, "MAJOR=%u", MAJOR(dev->devt)); add_uevent_var(env, "MINOR=%u", MINOR(dev->devt)); - name = device_get_devnode(dev, &mode, &tmp); + name = device_get_devnode(dev, &mode, &uid, &gid, &tmp); if (name) { add_uevent_var(env, "DEVNAME=%s", name); - kfree(tmp); if (mode) add_uevent_var(env, "DEVMODE=%#o", mode & 0777); + if (uid) + add_uevent_var(env, "DEVUID=%u", uid); + if (gid) + add_uevent_var(env, "DEVGID=%u", gid); + kfree(tmp); } } @@ -1281,6 +1287,8 @@ static struct device *next_device(struct klist_iter *i) * device_get_devnode - path of device node file * @dev: device * @mode: returned file access mode + * @uid: returned file owner + * @gid: returned file group * @tmp: possibly allocated string * * Return the relative path of a possible device node. @@ -1289,7 +1297,8 @@ static struct device *next_device(struct klist_iter *i) * freed by the caller. */ const char *device_get_devnode(struct device *dev, - umode_t *mode, const char **tmp) + umode_t *mode, uid_t *uid, gid_t *gid, + const char **tmp) { char *s; @@ -1297,7 +1306,7 @@ const char *device_get_devnode(struct device *dev, /* the device type may provide a specific name */ if (dev->type && dev->type->devnode) - *tmp = dev->type->devnode(dev, mode); + *tmp = dev->type->devnode(dev, mode, uid, gid); if (*tmp) return *tmp; diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 01fc5b0..fda5256 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -41,6 +41,8 @@ static struct req { int err; const char *name; umode_t mode; /* 0 => delete */ + uid_t uid; + gid_t gid; struct device *dev; } *requests; @@ -85,7 +87,9 @@ int devtmpfs_create_node(struct device *dev) return 0; req.mode = 0; - req.name = device_get_devnode(dev, &req.mode, &tmp); + req.uid = 0; + req.gid = 0; + req.name = device_get_devnode(dev, &req.mode, &req.uid, &req.gid, &tmp); if (!req.name) return -ENOMEM; @@ -121,7 +125,7 @@ int devtmpfs_delete_node(struct device *dev) if (!thread) return 0; - req.name = device_get_devnode(dev, NULL, &tmp); + req.name = device_get_devnode(dev, NULL, NULL, NULL, &tmp); if (!req.name) return -ENOMEM; @@ -187,7 +191,8 @@ static int create_path(const char *nodepath) return err; } -static int handle_create(const char *nodename, umode_t mode, struct device *dev) +static int handle_create(const char *nodename, umode_t mode, uid_t uid, + gid_t gid, struct device *dev) { struct dentry *dentry; struct path path; @@ -201,14 +206,14 @@ static int handle_create(const char *nodename, umode_t mode, struct device *dev) if (IS_ERR(dentry)) return PTR_ERR(dentry); - err = vfs_mknod(path.dentry->d_inode, - dentry, mode, dev->devt); + err = vfs_mknod(path.dentry->d_inode, dentry, mode, dev->devt); if (!err) { struct iattr newattrs; - /* fixup possibly umasked mode */ newattrs.ia_mode = mode; - newattrs.ia_valid = ATTR_MODE; + newattrs.ia_uid = uid; + newattrs.ia_gid = gid; + newattrs.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID; mutex_lock(&dentry->d_inode->i_mutex); notify_change(dentry, &newattrs); mutex_unlock(&dentry->d_inode->i_mutex); @@ -358,10 +363,11 @@ int devtmpfs_mount(const char *mntdir) static DECLARE_COMPLETION(setup_done); -static int handle(const char *name, umode_t mode, struct device *dev) +static int handle(const char *name, umode_t mode, uid_t uid, gid_t gid, + struct device *dev) { if (mode) - return handle_create(name, mode, dev); + return handle_create(name, mode, uid, gid, dev); else return handle_remove(name, dev); } @@ -387,7 +393,8 @@ static int devtmpfsd(void *p) spin_unlock(&req_lock); while (req) { struct req *next = req->next; - req->err = handle(req->name, req->mode, req->dev); + req->err = handle(req->name, req->mode, + req->uid, req->gid, req->dev); complete(&req->done); req = next; } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index f81b925..1700283 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -317,7 +317,8 @@ static const struct dev_pm_ops usb_device_pm_ops = { #endif /* CONFIG_PM */ -static char *usb_devnode(struct device *dev, umode_t *mode) +static char *usb_devnode(struct device *dev, + umode_t *mode, uid_t *uid, gid_t *gid) { struct usb_device *usb_dev; diff --git a/include/linux/device.h b/include/linux/device.h index 4a7c4a8..851b85c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -25,6 +25,7 @@ #include #include #include +#include #include struct device; @@ -465,7 +466,8 @@ struct device_type { const char *name; const struct attribute_group **groups; int (*uevent)(struct device *dev, struct kobj_uevent_env *env); - char *(*devnode)(struct device *dev, umode_t *mode); + char *(*devnode)(struct device *dev, umode_t *mode, + uid_t *uid, gid_t *gid); void (*release)(struct device *dev); const struct dev_pm_ops *pm; @@ -843,7 +845,8 @@ extern int device_rename(struct device *dev, const char *new_name); extern int device_move(struct device *dev, struct device *new_parent, enum dpm_order dpm_order); extern const char *device_get_devnode(struct device *dev, - umode_t *mode, const char **tmp); + umode_t *mode, uid_t *uid, gid_t *gid, + const char **tmp); extern void *dev_get_drvdata(const struct device *dev); extern int dev_set_drvdata(struct device *dev, void *data); -- cgit v0.10.2 From c3a30420902de10bcc4c8474766cb5f97f4c4dfc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 10 Apr 2013 09:00:15 -0700 Subject: devtmpfs: add base.h include This fixes a sparse warning, and is a good idea given that the devtmpfs_init() prototype is in this file. Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index fda5256..aadf647 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -24,6 +24,7 @@ #include #include #include +#include "base.h" static struct task_struct *thread; -- cgit v0.10.2 From d81c8d19da8fb6514c75d5c19334f4236856c561 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Thu, 11 Apr 2013 00:12:56 +0800 Subject: driver core: devtmpfs: fix compile failure with CONFIG_UIDGID_STRICT_TYPE_CHECKS If CONFIG_UIDGID_STRICT_TYPE_CHECKS is enalbed, the below compile failure will be triggered: drivers/base/devtmpfs.c: In function 'handle_create': drivers/base/devtmpfs.c:214:19: error: incompatible types when assigning to type 'kuid_t' from type 'uid_t' drivers/base/devtmpfs.c:215:19: error: incompatible types when assigning to type 'kgid_t' from type 'gid_t' make[2]: *** [drivers/base/devtmpfs.o] Error 1 This patch fixes the compile failure. Signed-off-by: Ming Lei Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index aadf647..abd4eee 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -212,8 +212,8 @@ static int handle_create(const char *nodename, umode_t mode, uid_t uid, struct iattr newattrs; newattrs.ia_mode = mode; - newattrs.ia_uid = uid; - newattrs.ia_gid = gid; + newattrs.ia_uid = KUIDT_INIT(uid); + newattrs.ia_gid = KGIDT_INIT(gid); newattrs.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID; mutex_lock(&dentry->d_inode->i_mutex); notify_change(dentry, &newattrs); -- cgit v0.10.2 From 4e4098a3e08783cfd75f9fcdab276dc1d46931da Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 11 Apr 2013 11:43:29 -0700 Subject: driver core: handle user namespaces properly with the uid/gid devtmpfs change Now that devtmpfs is caring about uid/gid, we need to use the correct internal types so users who have USER_NS enabled will have things work properly for them. Thanks to Eric for pointing this out, and the patch review. Reported-by: Eric W. Biederman Cc: Kay Sievers Cc: Ming Lei Signed-off-by: Greg Kroah-Hartman diff --git a/block/genhd.c b/block/genhd.c index dfcec43..20625ee 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1112,7 +1112,7 @@ struct class block_class = { }; static char *block_devnode(struct device *dev, umode_t *mode, - uid_t *uid, gid_t *gid) + kuid_t *uid, kgid_t *gid) { struct gendisk *disk = dev_to_disk(dev); diff --git a/drivers/base/core.c b/drivers/base/core.c index 8a428b5..f88d9e2 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -283,8 +283,8 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, const char *tmp; const char *name; umode_t mode = 0; - uid_t uid = 0; - gid_t gid = 0; + kuid_t uid = GLOBAL_ROOT_UID; + kgid_t gid = GLOBAL_ROOT_GID; add_uevent_var(env, "MAJOR=%u", MAJOR(dev->devt)); add_uevent_var(env, "MINOR=%u", MINOR(dev->devt)); @@ -293,10 +293,10 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, add_uevent_var(env, "DEVNAME=%s", name); if (mode) add_uevent_var(env, "DEVMODE=%#o", mode & 0777); - if (uid) - add_uevent_var(env, "DEVUID=%u", uid); - if (gid) - add_uevent_var(env, "DEVGID=%u", gid); + if (!uid_eq(uid, GLOBAL_ROOT_UID)) + add_uevent_var(env, "DEVUID=%u", from_kuid(&init_user_ns, uid)); + if (!gid_eq(gid, GLOBAL_ROOT_GID)) + add_uevent_var(env, "DEVGID=%u", from_kgid(&init_user_ns, gid)); kfree(tmp); } } @@ -1297,7 +1297,7 @@ static struct device *next_device(struct klist_iter *i) * freed by the caller. */ const char *device_get_devnode(struct device *dev, - umode_t *mode, uid_t *uid, gid_t *gid, + umode_t *mode, kuid_t *uid, kgid_t *gid, const char **tmp) { char *s; diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index abd4eee..7413d06 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -42,8 +42,8 @@ static struct req { int err; const char *name; umode_t mode; /* 0 => delete */ - uid_t uid; - gid_t gid; + kuid_t uid; + kgid_t gid; struct device *dev; } *requests; @@ -88,8 +88,8 @@ int devtmpfs_create_node(struct device *dev) return 0; req.mode = 0; - req.uid = 0; - req.gid = 0; + req.uid = GLOBAL_ROOT_UID; + req.gid = GLOBAL_ROOT_GID; req.name = device_get_devnode(dev, &req.mode, &req.uid, &req.gid, &tmp); if (!req.name) return -ENOMEM; @@ -192,8 +192,8 @@ static int create_path(const char *nodepath) return err; } -static int handle_create(const char *nodename, umode_t mode, uid_t uid, - gid_t gid, struct device *dev) +static int handle_create(const char *nodename, umode_t mode, kuid_t uid, + kgid_t gid, struct device *dev) { struct dentry *dentry; struct path path; @@ -212,8 +212,8 @@ static int handle_create(const char *nodename, umode_t mode, uid_t uid, struct iattr newattrs; newattrs.ia_mode = mode; - newattrs.ia_uid = KUIDT_INIT(uid); - newattrs.ia_gid = KGIDT_INIT(gid); + newattrs.ia_uid = uid; + newattrs.ia_gid = gid; newattrs.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID; mutex_lock(&dentry->d_inode->i_mutex); notify_change(dentry, &newattrs); @@ -364,7 +364,7 @@ int devtmpfs_mount(const char *mntdir) static DECLARE_COMPLETION(setup_done); -static int handle(const char *name, umode_t mode, uid_t uid, gid_t gid, +static int handle(const char *name, umode_t mode, kuid_t uid, kgid_t gid, struct device *dev) { if (mode) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 1700283..e092b41 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -318,7 +318,7 @@ static const struct dev_pm_ops usb_device_pm_ops = { static char *usb_devnode(struct device *dev, - umode_t *mode, uid_t *uid, gid_t *gid) + umode_t *mode, kuid_t *uid, kgid_t *gid) { struct usb_device *usb_dev; diff --git a/include/linux/device.h b/include/linux/device.h index 851b85c..88615cc 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -467,7 +467,7 @@ struct device_type { const struct attribute_group **groups; int (*uevent)(struct device *dev, struct kobj_uevent_env *env); char *(*devnode)(struct device *dev, umode_t *mode, - uid_t *uid, gid_t *gid); + kuid_t *uid, kgid_t *gid); void (*release)(struct device *dev); const struct dev_pm_ops *pm; @@ -845,7 +845,7 @@ extern int device_rename(struct device *dev, const char *new_name); extern int device_move(struct device *dev, struct device *new_parent, enum dpm_order dpm_order); extern const char *device_get_devnode(struct device *dev, - umode_t *mode, uid_t *uid, gid_t *gid, + umode_t *mode, kuid_t *uid, kgid_t *gid, const char **tmp); extern void *dev_get_drvdata(const struct device *dev); extern int dev_set_drvdata(struct device *dev, void *data); -- cgit v0.10.2 From fa180eb448fa263cf18dd930143b515d27d70d7b Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 10 Apr 2013 17:00:48 +0200 Subject: PM / Runtime: Idle devices asynchronously after probe|release Putting devices into idle|suspend in a synchronous manner means we are waiting for each device to become idle|suspended before the probe|release is fully done. This patch switch to use the asynchronous runtime PM API:s instead and thus improves the parallelism since we can move on and handle the next device in queue in an earlier phase. Signed-off-by: Ulf Hansson Acked-by: Kevin Hilman Acked-by: Rafael J. Wysocki Cc: Alan Stern Acked-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/base/dd.c b/drivers/base/dd.c index bb5645e..35fa368 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -380,7 +380,7 @@ int driver_probe_device(struct device_driver *drv, struct device *dev) pm_runtime_barrier(dev); ret = really_probe(dev, drv); - pm_runtime_idle(dev); + pm_request_idle(dev); return ret; } @@ -428,7 +428,7 @@ int device_attach(struct device *dev) } } else { ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); - pm_runtime_idle(dev); + pm_request_idle(dev); } out_unlock: device_unlock(dev); @@ -499,7 +499,7 @@ static void __device_release_driver(struct device *dev) BUS_NOTIFY_UNBIND_DRIVER, dev); - pm_runtime_put_sync(dev); + pm_runtime_put(dev); if (dev->bus && dev->bus->remove) dev->bus->remove(dev); -- cgit v0.10.2