From 60a96a59569bab85571d0089682109bd3324e896 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Sun, 8 Jul 2007 22:29:26 +0200 Subject: Driver core: accept all valid action-strings in uevent-trigger This allows the uevent file to handle any type of uevent action to be triggered by userspace instead of just the "add" uevent. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/base/core.c b/drivers/base/core.c index 0455aa78fa13..91a0367d583e 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -24,6 +24,8 @@ #include "base.h" #include "power/power.h" +extern const char *kobject_actions[]; + int (*platform_notify)(struct device * dev) = NULL; int (*platform_notify_remove)(struct device * dev) = NULL; @@ -303,10 +305,25 @@ out: static ssize_t store_uevent(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - if (memcmp(buf, "add", 3) != 0) - dev_err(dev, "uevent: unsupported action-string; this will " - "be ignored in a future kernel version"); + size_t len = count; + enum kobject_action action; + + if (len && buf[len-1] == '\n') + len--; + + for (action = 0; action < KOBJ_MAX; action++) { + if (strncmp(kobject_actions[action], buf, len) != 0) + continue; + if (kobject_actions[action][len] != '\0') + continue; + kobject_uevent(&dev->kobj, action); + goto out; + } + + dev_err(dev, "uevent: unsupported action-string; this will " + "be ignored in a future kernel version\n"); kobject_uevent(&dev->kobj, KOBJ_ADD); +out: return count; } -- cgit v1.2.3-59-g8ed1b From 471d0558045fe35f8c5f291c1ee63815eb9c2dcd Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 12 Jul 2007 16:55:07 -0400 Subject: PM: Remove deprecated sysfs files This patch (as932) removes the deprecated sysfs .../power/state attribute files. Signed-off-by: Alan Stern Acked-by: Pavel Machek Signed-off-by: Greg Kroah-Hartman --- Documentation/feature-removal-schedule.txt | 3 +- drivers/base/power/sysfs.c | 66 ------------------------------ kernel/power/Kconfig | 12 ------ 3 files changed, 1 insertion(+), 80 deletions(-) (limited to 'drivers') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index d05e6243b4df..9cf9d8346289 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -26,8 +26,7 @@ Who: Hans Verkuil and --------------------------- -What: /sys/devices/.../power/state - dev->power.power_state +What: dev->power.power_state dpm_runtime_{suspend,resume)() When: July 2007 Why: Broken design for runtime control over driver power states, confusing diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 2d47517dbe32..f2ed179cd695 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -7,69 +7,6 @@ #include "power.h" -#ifdef CONFIG_PM_SYSFS_DEPRECATED - -/** - * state - Control current power state of device - * - * show() returns the current power state of the device. '0' indicates - * the device is on. Other values (2) indicate the device is in some low - * power state. - * - * store() sets the current power state, which is an integer valued - * 0, 2, or 3. Devices with bus.suspend_late(), or bus.resume_early() - * methods fail this operation; those methods couldn't be called. - * Otherwise, - * - * - If the recorded dev->power.power_state.event matches the - * target value, nothing is done. - * - If the recorded event code is nonzero, the device is reactivated - * by calling bus.resume() and/or class.resume(). - * - If the target value is nonzero, the device is suspended by - * calling class.suspend() and/or bus.suspend() with event code - * PM_EVENT_SUSPEND. - * - * This mechanism is DEPRECATED and should only be used for testing. - */ - -static ssize_t state_show(struct device * dev, struct device_attribute *attr, char * buf) -{ - if (dev->power.power_state.event) - return sprintf(buf, "2\n"); - else - return sprintf(buf, "0\n"); -} - -static ssize_t state_store(struct device * dev, struct device_attribute *attr, const char * buf, size_t n) -{ - pm_message_t state; - int error = -EINVAL; - - /* disallow incomplete suspend sequences */ - if (dev->bus && (dev->bus->suspend_late || dev->bus->resume_early)) - return error; - - state.event = PM_EVENT_SUSPEND; - /* Older apps expected to write "3" here - confused with PCI D3 */ - if ((n == 1) && !strcmp(buf, "3")) - error = dpm_runtime_suspend(dev, state); - - if ((n == 1) && !strcmp(buf, "2")) - error = dpm_runtime_suspend(dev, state); - - if ((n == 1) && !strcmp(buf, "0")) { - dpm_runtime_resume(dev); - error = 0; - } - - return error ? error : n; -} - -static DEVICE_ATTR(state, 0644, state_show, state_store); - - -#endif /* CONFIG_PM_SYSFS_DEPRECATED */ - /* * wakeup - Report/change current wakeup option for device * @@ -143,9 +80,6 @@ static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); static struct attribute * power_attrs[] = { -#ifdef CONFIG_PM_SYSFS_DEPRECATED - &dev_attr_state.attr, -#endif &dev_attr_wakeup.attr, NULL, }; diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 495b7d4dd330..73328476761c 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -65,18 +65,6 @@ config PM_TRACE CAUTION: this option will cause your machine's real-time clock to be set to an invalid time after a resume. -config PM_SYSFS_DEPRECATED - bool "Driver model /sys/devices/.../power/state files (DEPRECATED)" - depends on PM && SYSFS - default n - help - The driver model started out with a sysfs file intended to provide - a userspace hook for device power management. This feature has never - worked very well, except for limited testing purposes, and so it will - be removed. It's not clear that a generic mechanism could really - handle the wide variability of device power states; any replacements - are likely to be bus or driver specific. - config SOFTWARE_SUSPEND bool "Software Suspend (Hibernation)" depends on PM && SWAP && (((X86 || PPC64_SWSUSP) && (!SMP || SUSPEND_SMP)) || ((FRV || PPC32) && !SMP)) -- cgit v1.2.3-59-g8ed1b From 3f8df781fc5f9ee5253a54ba669e1c8872844b86 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 12 Jul 2007 16:57:22 -0400 Subject: PM: remove deprecated dpm_runtime_* routines This patch (as933) removes the deprecated dpm_runtime_suspend() and dpm_runtime_resume() routines from the PM core. The only user of those routines is the PCMCIA ds driver; local replacements are added. Signed-off-by: Alan Stern CC: Dominik Brodowski Signed-off-by: Greg Kroah-Hartman --- Documentation/feature-removal-schedule.txt | 1 - drivers/base/power/Makefile | 2 +- drivers/base/power/power.h | 5 -- drivers/base/power/runtime.c | 85 ------------------------------ drivers/pcmcia/ds.c | 40 +++++++++++--- include/linux/pm.h | 11 ---- 6 files changed, 35 insertions(+), 109 deletions(-) delete mode 100644 drivers/base/power/runtime.c (limited to 'drivers') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 9cf9d8346289..1b5c70758a1a 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -27,7 +27,6 @@ Who: Hans Verkuil and --------------------------- What: dev->power.power_state - dpm_runtime_{suspend,resume)() When: July 2007 Why: Broken design for runtime control over driver power states, confusing driver-internal runtime power management with: mechanisms to support diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index 91f230939c1e..fff178007208 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -1,5 +1,5 @@ obj-y := shutdown.o -obj-$(CONFIG_PM) += main.o suspend.o resume.o runtime.o sysfs.o +obj-$(CONFIG_PM) += main.o suspend.o resume.o sysfs.o obj-$(CONFIG_PM_TRACE) += trace.o ifeq ($(CONFIG_DEBUG_DRIVER),y) diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 2760f25b3ac5..591a0dd5deee 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -62,11 +62,6 @@ extern int resume_device(struct device *); */ extern int suspend_device(struct device *, pm_message_t); - -/* - * runtime.c - */ - #else /* CONFIG_PM */ diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c deleted file mode 100644 index df6174d85866..000000000000 --- a/drivers/base/power/runtime.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * drivers/base/power/runtime.c - Handling dynamic device power management. - * - * Copyright (c) 2003 Patrick Mochel - * Copyright (c) 2003 Open Source Development Lab - * - */ - -#include -#include "power.h" - - -static void runtime_resume(struct device * dev) -{ - dev_dbg(dev, "resuming\n"); - if (!dev->power.power_state.event) - return; - if (!resume_device(dev)) - dev->power.power_state = PMSG_ON; -} - - -/** - * dpm_runtime_resume - Power one device back on. - * @dev: Device. - * - * Bring one device back to the on state by first powering it - * on, then restoring state. We only operate on devices that aren't - * already on. - * FIXME: We need to handle devices that are in an unknown state. - */ - -void dpm_runtime_resume(struct device * dev) -{ - mutex_lock(&dpm_mtx); - runtime_resume(dev); - mutex_unlock(&dpm_mtx); -} -EXPORT_SYMBOL(dpm_runtime_resume); - - -/** - * dpm_runtime_suspend - Put one device in low-power state. - * @dev: Device. - * @state: State to enter. - */ - -int dpm_runtime_suspend(struct device * dev, pm_message_t state) -{ - int error = 0; - - mutex_lock(&dpm_mtx); - if (dev->power.power_state.event == state.event) - goto Done; - - if (dev->power.power_state.event) - runtime_resume(dev); - - if (!(error = suspend_device(dev, state))) - dev->power.power_state = state; - Done: - mutex_unlock(&dpm_mtx); - return error; -} -EXPORT_SYMBOL(dpm_runtime_suspend); - - -#if 0 -/** - * dpm_set_power_state - Update power_state field. - * @dev: Device. - * @state: Power state device is in. - * - * This is an update mechanism for drivers to notify the core - * what power state a device is in. Device probing code may not - * always be able to tell, but we need accurate information to - * work reliably. - */ -void dpm_set_power_state(struct device * dev, pm_message_t state) -{ - mutex_lock(&dpm_mtx); - dev->power.power_state = state; - mutex_unlock(&dpm_mtx); -} -#endif /* 0 */ diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 143c6efc478a..a99607142fc8 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -1127,6 +1127,34 @@ static int pcmcia_bus_uevent(struct device *dev, char **envp, int num_envp, #endif +/************************ runtime PM support ***************************/ + +static int pcmcia_dev_suspend(struct device *dev, pm_message_t state); +static int pcmcia_dev_resume(struct device *dev); + +static int runtime_suspend(struct device *dev) +{ + int rc; + + down(&dev->sem); + rc = pcmcia_dev_suspend(dev, PMSG_SUSPEND); + up(&dev->sem); + if (!rc) + dev->power.power_state.event = PM_EVENT_SUSPEND; + return rc; +} + +static void runtime_resume(struct device *dev) +{ + int rc; + + down(&dev->sem); + rc = pcmcia_dev_resume(dev); + up(&dev->sem); + if (!rc) + dev->power.power_state.event = PM_EVENT_ON; +} + /************************ per-device sysfs output ***************************/ #define pcmcia_device_attr(field, test, format) \ @@ -1173,9 +1201,9 @@ static ssize_t pcmcia_store_pm_state(struct device *dev, struct device_attribute return -EINVAL; if ((!p_dev->suspended) && !strncmp(buf, "off", 3)) - ret = dpm_runtime_suspend(dev, PMSG_SUSPEND); + ret = runtime_suspend(dev); else if (p_dev->suspended && !strncmp(buf, "on", 2)) - dpm_runtime_resume(dev); + runtime_resume(dev); return ret ? ret : count; } @@ -1312,10 +1340,10 @@ static int pcmcia_bus_suspend_callback(struct device *dev, void * _data) struct pcmcia_socket *skt = _data; struct pcmcia_device *p_dev = to_pcmcia_dev(dev); - if (p_dev->socket != skt) + if (p_dev->socket != skt || p_dev->suspended) return 0; - return dpm_runtime_suspend(dev, PMSG_SUSPEND); + return runtime_suspend(dev); } static int pcmcia_bus_resume_callback(struct device *dev, void * _data) @@ -1323,10 +1351,10 @@ static int pcmcia_bus_resume_callback(struct device *dev, void * _data) struct pcmcia_socket *skt = _data; struct pcmcia_device *p_dev = to_pcmcia_dev(dev); - if (p_dev->socket != skt) + if (p_dev->socket != skt || !p_dev->suspended) return 0; - dpm_runtime_resume(dev); + runtime_resume(dev); return 0; } diff --git a/include/linux/pm.h b/include/linux/pm.h index 273781c82e4d..2735b7cadd20 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -284,8 +284,6 @@ extern int device_prepare_suspend(pm_message_t state); #define device_may_wakeup(dev) \ (device_can_wakeup(dev) && (dev)->power.should_wakeup) -extern int dpm_runtime_suspend(struct device *, pm_message_t); -extern void dpm_runtime_resume(struct device *); extern void __suspend_report_result(const char *function, void *fn, int ret); #define suspend_report_result(fn, ret) \ @@ -317,15 +315,6 @@ static inline int device_suspend(pm_message_t state) #define device_set_wakeup_enable(dev,val) do{}while(0) #define device_may_wakeup(dev) (0) -static inline int dpm_runtime_suspend(struct device * dev, pm_message_t state) -{ - return 0; -} - -static inline void dpm_runtime_resume(struct device * dev) -{ -} - #define suspend_report_result(fn, ret) do { } while (0) static inline int call_platform_enable_wakeup(struct device *dev, int is_on) -- cgit v1.2.3-59-g8ed1b From aebdc3b450a3febf7d7d00cd2235509055ec7082 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 12 Jul 2007 22:08:22 -0700 Subject: dev_vdbg(), available with -DVERBOSE_DEBUG This defines a dev_vdbg() call, which is enabled with -DVERBOSE_DEBUG. When enabled, dev_vdbg() acts just like dev_dbg(). When disabled, it is a NOP ... just like dev_dbg() without -DDEBUG. The specific code was moved out of a USB patch, but lots of drivers have similar support. That is, code can now be written to use an additional level of debug output, selected at compile time. Many driver authors have found this idiom to be very useful. A typical usage model is for "normal" debug messages to focus on fault paths and not be very "chatty", so that those messages can be left on during normal operation without much of a performance or syslog load. On the other hand "verbose" messages would be noisy enough that they wouldn't normally be enabled; they might even affect timings enough to change system or driver behavior. Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 7 ------- include/linux/device.h | 10 ++++++++++ 2 files changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 73c49362cd47..654857493a82 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -29,13 +29,6 @@ #include "hcd.h" #include "usb.h" -#define VERBOSE_DEBUG 0 - -#if VERBOSE_DEBUG -#define dev_vdbg dev_dbg -#else -#define dev_vdbg(dev, fmt, args...) do { } while (0) -#endif #ifdef CONFIG_HOTPLUG diff --git a/include/linux/device.h b/include/linux/device.h index be2debed70d2..d9f0a57f5a2f 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -572,6 +572,16 @@ dev_dbg(struct device * dev, const char * fmt, ...) } #endif +#ifdef VERBOSE_DEBUG +#define dev_vdbg dev_dbg +#else +static inline int __attribute__ ((format (printf, 2, 3))) +dev_vdbg(struct device * dev, const char * fmt, ...) +{ + return 0; +} +#endif + #define dev_err(dev, format, arg...) \ dev_printk(KERN_ERR , dev , format , ## arg) #define dev_info(dev, format, arg...) \ -- cgit v1.2.3-59-g8ed1b From 2ee97caf0a6602f749ddbfdb1449e383e1212707 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Wed, 18 Jul 2007 01:43:47 -0700 Subject: Driver core: check return code of sysfs_create_link() Check for return value of sysfs_create_link() in device_add() and device_rename(). Add helper functions device_add_class_symlinks() and device_remove_class_symlinks() to make the code easier to read. [akpm@linux-foundation.org: fix unused var warnings] Signed-off-by: Cornelia Huck Acked-by: Jeff Garzik Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 145 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 105 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/base/core.c b/drivers/base/core.c index 91a0367d583e..3599ab2506d2 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -660,6 +660,82 @@ static int setup_parent(struct device *dev, struct device *parent) return 0; } +static int device_add_class_symlinks(struct device *dev) +{ + int error; + + if (!dev->class) + return 0; + error = sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj, + "subsystem"); + if (error) + goto out; + /* + * If this is not a "fake" compatible device, then create the + * symlink from the class to the device. + */ + if (dev->kobj.parent != &dev->class->subsys.kobj) { + error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, + dev->bus_id); + if (error) + goto out_subsys; + } + /* only bus-device parents get a "device"-link */ + if (dev->parent && dev->parent->bus) { + error = sysfs_create_link(&dev->kobj, &dev->parent->kobj, + "device"); + if (error) + goto out_busid; +#ifdef CONFIG_SYSFS_DEPRECATED + { + char * class_name = make_class_name(dev->class->name, + &dev->kobj); + if (class_name) + error = sysfs_create_link(&dev->parent->kobj, + &dev->kobj, class_name); + kfree(class_name); + if (error) + goto out_device; + } +#endif + } + return 0; + +#ifdef CONFIG_SYSFS_DEPRECATED +out_device: + if (dev->parent) + sysfs_remove_link(&dev->kobj, "device"); +#endif +out_busid: + if (dev->kobj.parent != &dev->class->subsys.kobj) + sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); +out_subsys: + sysfs_remove_link(&dev->kobj, "subsystem"); +out: + return error; +} + +static void device_remove_class_symlinks(struct device *dev) +{ + if (!dev->class) + return; + if (dev->parent) { +#ifdef CONFIG_SYSFS_DEPRECATED + char *class_name; + + class_name = make_class_name(dev->class->name, &dev->kobj); + if (class_name) { + sysfs_remove_link(&dev->parent->kobj, class_name); + kfree(class_name); + } +#endif + sysfs_remove_link(&dev->kobj, "device"); + } + if (dev->kobj.parent != &dev->class->subsys.kobj) + sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); + sysfs_remove_link(&dev->kobj, "subsystem"); +} + /** * device_add - add device to device hierarchy. * @dev: device. @@ -674,7 +750,6 @@ static int setup_parent(struct device *dev, struct device *parent) int device_add(struct device *dev) { struct device *parent = NULL; - char *class_name = NULL; struct class_interface *class_intf; int error = -EINVAL; @@ -714,27 +789,9 @@ int device_add(struct device *dev) goto ueventattrError; } - if (dev->class) { - sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj, - "subsystem"); - /* If this is not a "fake" compatible device, then create the - * symlink from the class to the device. */ - if (dev->kobj.parent != &dev->class->subsys.kobj) - sysfs_create_link(&dev->class->subsys.kobj, - &dev->kobj, dev->bus_id); - if (parent) { - sysfs_create_link(&dev->kobj, &dev->parent->kobj, - "device"); -#ifdef CONFIG_SYSFS_DEPRECATED - class_name = make_class_name(dev->class->name, - &dev->kobj); - if (class_name) - sysfs_create_link(&dev->parent->kobj, - &dev->kobj, class_name); -#endif - } - } - + error = device_add_class_symlinks(dev); + if (error) + goto SymlinkError; error = device_add_attrs(dev); if (error) goto AttrsError; @@ -761,7 +818,6 @@ int device_add(struct device *dev) up(&dev->class->sem); } Done: - kfree(class_name); put_device(dev); return error; BusError: @@ -772,6 +828,8 @@ int device_add(struct device *dev) BUS_NOTIFY_DEL_DEVICE, dev); device_remove_attrs(dev); AttrsError: + device_remove_class_symlinks(dev); + SymlinkError: if (MAJOR(dev->devt)) device_remove_file(dev, &devt_attr); @@ -1156,7 +1214,7 @@ int device_rename(struct device *dev, char *new_name) { char *old_class_name = NULL; char *new_class_name = NULL; - char *old_symlink_name = NULL; + char *old_device_name = NULL; int error; dev = get_device(dev); @@ -1170,42 +1228,49 @@ int device_rename(struct device *dev, char *new_name) old_class_name = make_class_name(dev->class->name, &dev->kobj); #endif - if (dev->class) { - old_symlink_name = kmalloc(BUS_ID_SIZE, GFP_KERNEL); - if (!old_symlink_name) { - error = -ENOMEM; - goto out_free_old_class; - } - strlcpy(old_symlink_name, dev->bus_id, BUS_ID_SIZE); + old_device_name = kmalloc(BUS_ID_SIZE, GFP_KERNEL); + if (!old_device_name) { + error = -ENOMEM; + goto out; } - + strlcpy(old_device_name, dev->bus_id, BUS_ID_SIZE); strlcpy(dev->bus_id, new_name, BUS_ID_SIZE); error = kobject_rename(&dev->kobj, new_name); + if (error) { + strlcpy(dev->bus_id, old_device_name, BUS_ID_SIZE); + goto out; + } #ifdef CONFIG_SYSFS_DEPRECATED if (old_class_name) { new_class_name = make_class_name(dev->class->name, &dev->kobj); if (new_class_name) { - sysfs_create_link(&dev->parent->kobj, &dev->kobj, - new_class_name); + error = sysfs_create_link(&dev->parent->kobj, + &dev->kobj, new_class_name); + if (error) + goto out; sysfs_remove_link(&dev->parent->kobj, old_class_name); } } #endif if (dev->class) { - sysfs_remove_link(&dev->class->subsys.kobj, - old_symlink_name); - sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, - dev->bus_id); + sysfs_remove_link(&dev->class->subsys.kobj, old_device_name); + error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, + dev->bus_id); + if (error) { + /* Uh... how to unravel this if restoring can fail? */ + dev_err(dev, "%s: sysfs_create_symlink failed (%d)\n", + __FUNCTION__, error); + } } +out: put_device(dev); kfree(new_class_name); - kfree(old_symlink_name); - out_free_old_class: kfree(old_class_name); + kfree(old_device_name); return error; } -- cgit v1.2.3-59-g8ed1b