summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2014-10-31 11:13:07 -0600
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-11-07 11:17:27 -0800
commit0372ffb35d00288802265586a29c117911d02fb8 (patch)
treee701ab8f25cdd801ecaf3f1c303c18df83f78e49
parent0cd75047de7f54d6fb4aba0ec5818f8194815a5a (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.c8
1 files changed, 5 insertions, 3 deletions
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 83e910a5756..876bae5ade3 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,
const char *buf, size_t count)
{
struct device *dev;
+ int err = -EINVAL;
dev = bus_find_device_by_name(bus, NULL, buf);
if (!dev)
return -ENODEV;
- if (bus_rescan_devices_helper(dev, NULL) != 0)
- return -EINVAL;
- return count;
+ if (bus_rescan_devices_helper(dev, NULL) == 0)
+ err = count;
+ put_device(dev);
+ return err;
}
static struct device *next_device(struct klist_iter *i)