diff options
author | Alex Williamson <alex.williamson@redhat.com> | 2014-10-31 13:13:07 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-11-07 14:17:27 -0500 |
commit | 0372ffb35d00288802265586a29c117911d02fb8 (patch) | |
tree | e701ab8f25cdd801ecaf3f1c303c18df83f78e49 | |
parent | 0cd75047de7f54d6fb4aba0ec5818f8194815a5a (diff) |
driver core: Fix unbalanced device reference in drivers_probe
bus_find_device_by_name() acquires a device reference which is never
released. This results in an object leak, which on older kernels
results in failure to release all resources of PCI devices. libvirt
uses drivers_probe to re-attach devices to the host after assignment
and is therefore a common trigger for this leak.
Example:
# cd /sys/bus/pci/
# dmesg -C
# echo 1 > devices/0000\:01\:00.0/sriov_numvfs
# echo 0 > devices/0000\:01\:00.0/sriov_numvfs
# dmesg | grep 01:10
pci 0000:01:10.0: [8086:10ca] type 00 class 0x020000
kobject: '0000:01:10.0' (ffff8801d79cd0a8): kobject_add_internal: parent: '0000:00:01.0', set: 'devices'
kobject: '0000:01:10.0' (ffff8801d79cd0a8): kobject_uevent_env
kobject: '0000:01:10.0' (ffff8801d79cd0a8): fill_kobj_path: path = '/devices/pci0000:00/0000:00:01.0/0000:01:10.0'
kobject: '0000:01:10.0' (ffff8801d79cd0a8): kobject_uevent_env
kobject: '0000:01:10.0' (ffff8801d79cd0a8): fill_kobj_path: path = '/devices/pci0000:00/0000:00:01.0/0000:01:10.0'
kobject: '0000:01:10.0' (ffff8801d79cd0a8): kobject_uevent_env
kobject: '0000:01:10.0' (ffff8801d79cd0a8): fill_kobj_path: path = '/devices/pci0000:00/0000:00:01.0/0000:01:10.0'
kobject: '0000:01:10.0' (ffff8801d79cd0a8): kobject_cleanup, parent (null)
kobject: '0000:01:10.0' (ffff8801d79cd0a8): calling ktype release
kobject: '0000:01:10.0': free name
[kobject freed as expected]
# dmesg -C
# echo 1 > devices/0000\:01\:00.0/sriov_numvfs
# echo 0000:01:10.0 > drivers_probe
# echo 0 > devices/0000\:01\:00.0/sriov_numvfs
# dmesg | grep 01:10
pci 0000:01:10.0: [8086:10ca] type 00 class 0x020000
kobject: '0000:01:10.0' (ffff8801d79ce0a8): kobject_add_internal: parent: '0000:00:01.0', set: 'devices'
kobject: '0000:01:10.0' (ffff8801d79ce0a8): kobject_uevent_env
kobject: '0000:01:10.0' (ffff8801d79ce0a8): fill_kobj_path: path = '/devices/pci0000:00/0000:00:01.0/0000:01:10.0'
kobject: '0000:01:10.0' (ffff8801d79ce0a8): kobject_uevent_env
kobject: '0000:01:10.0' (ffff8801d79ce0a8): fill_kobj_path: path = '/devices/pci0000:00/0000:00:01.0/0000:01:10.0'
kobject: '0000:01:10.0' (ffff8801d79ce0a8): kobject_uevent_env
kobject: '0000:01:10.0' (ffff8801d79ce0a8): fill_kobj_path: path = '/devices/pci0000:00/0000:00:01.0/0000:01:10.0'
[no free]
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/base/bus.c | 8 |
1 files changed, 5 insertions, 3 deletions
diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 83e910a57563..876bae5ade33 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c | |||
@@ -254,13 +254,15 @@ static ssize_t store_drivers_probe(struct bus_type *bus, | |||
254 | const char *buf, size_t count) | 254 | const char *buf, size_t count) |
255 | { | 255 | { |
256 | struct device *dev; | 256 | struct device *dev; |
257 | int err = -EINVAL; | ||
257 | 258 | ||
258 | dev = bus_find_device_by_name(bus, NULL, buf); | 259 | dev = bus_find_device_by_name(bus, NULL, buf); |
259 | if (!dev) | 260 | if (!dev) |
260 | return -ENODEV; | 261 | return -ENODEV; |
261 | if (bus_rescan_devices_helper(dev, NULL) != 0) | 262 | if (bus_rescan_devices_helper(dev, NULL) == 0) |
262 | return -EINVAL; | 263 | err = count; |
263 | return count; | 264 | put_device(dev); |
265 | return err; | ||
264 | } | 266 | } |
265 | 267 | ||
266 | static struct device *next_device(struct klist_iter *i) | 268 | static struct device *next_device(struct klist_iter *i) |