From a525a3ddeaca69f405d98442ab3c0746e53168dc Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 25 Jul 2012 01:42:29 +0800 Subject: driver core: free devres in device_release device_del can happen anytime, so once it happens, the devres of the device will be freed inside device_del, but drivers can't know it has been deleted and may still add resources into the device, so memory leak is caused. This patch moves the devres_release_all to fix the problem. Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index f338037a4f3d..c8fe4a563866 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -184,6 +184,17 @@ static void device_release(struct kobject *kobj) struct device *dev = kobj_to_dev(kobj); struct device_private *p = dev->p; + /* + * Some platform devices are driven without driver attached + * and managed resources may have been acquired. Make sure + * all resources are released. + * + * Drivers still can add resources into device after device + * is deleted but alive, so release devres here to avoid + * possible memory leak. + */ + devres_release_all(dev); + if (dev->release) dev->release(dev); else if (dev->type && dev->type->release) @@ -1196,13 +1207,6 @@ void device_del(struct device *dev) bus_remove_device(dev); driver_deferred_probe_del(dev); - /* - * Some platform devices are driven without driver attached - * and managed resources may have been acquired. Make sure - * all resources are released. - */ - devres_release_all(dev); - /* Notify the platform of the removal, in case they * need to do anything... */ -- cgit v1.3-14-g43fede From 798efc60e4276825df34af0e91ecbe0781237834 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 12 Sep 2012 20:11:29 -0700 Subject: dev_dbg/dynamic_debug: Update to use printk_emit, optimize stack commit c4e00daaa9 ("driver-core: extend dev_printk() to pass structured data") changed __dev_printk and broke dynamic-debug's ability to control the dynamic prefix of dev_dbg(dev,..). commit af7f2158fd ("drivers-core: make structured logging play nice with dynamic-debug") made a minimal correction. The current dynamic debug code uses up to 3 recursion levels via %pV. This can consume quite a bit of stack. Directly call printk_emit to reduce the recursion depth. These changes include: dev_dbg: o Create and use function create_syslog_header to format the syslog header for printk_emit uses. o Call create_syslog_header and neaten __dev_printk o Make __dev_printk static not global o Remove include header declaration of __dev_printk o Remove now unused EXPORT_SYMBOL() of __dev_printk o Whitespace neatening dynamic_dev_dbg: o Remove KERN_DEBUG from dynamic_emit_prefix o Call create_syslog_header and printk_emit o Whitespace neatening Signed-off-by: Joe Perches Acked-by: David S. Miller Tested-by: Jim Cromie Acked-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 64 +++++++++++++++++++++++++++----------------------- include/linux/device.h | 8 +++---- lib/dynamic_debug.c | 39 ++++++++++++++++++++++-------- 3 files changed, 67 insertions(+), 44 deletions(-) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index 91478bd35418..d325b9dab3ff 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1865,26 +1865,19 @@ void device_shutdown(void) */ #ifdef CONFIG_PRINTK -int __dev_printk(const char *level, const struct device *dev, - struct va_format *vaf) +int create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen) { - char dict[128]; - const char *level_extra = ""; - size_t dictlen = 0; const char *subsys; - - if (!dev) - return printk("%s(NULL device *): %pV", level, vaf); + size_t pos = 0; if (dev->class) subsys = dev->class->name; else if (dev->bus) subsys = dev->bus->name; else - goto skip; + return 0; - dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen, - "SUBSYSTEM=%s", subsys); + pos += snprintf(hdr + pos, hdrlen - pos, "SUBSYSTEM=%s", subsys); /* * Add device identifier DEVICE=: @@ -1900,32 +1893,41 @@ int __dev_printk(const char *level, const struct device *dev, c = 'b'; else c = 'c'; - dictlen++; - dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen, - "DEVICE=%c%u:%u", - c, MAJOR(dev->devt), MINOR(dev->devt)); + pos++; + pos += snprintf(hdr + pos, hdrlen - pos, + "DEVICE=%c%u:%u", + c, MAJOR(dev->devt), MINOR(dev->devt)); } else if (strcmp(subsys, "net") == 0) { struct net_device *net = to_net_dev(dev); - dictlen++; - dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen, - "DEVICE=n%u", net->ifindex); + pos++; + pos += snprintf(hdr + pos, hdrlen - pos, + "DEVICE=n%u", net->ifindex); } else { - dictlen++; - dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen, - "DEVICE=+%s:%s", subsys, dev_name(dev)); + pos++; + pos += snprintf(hdr + pos, hdrlen - pos, + "DEVICE=+%s:%s", subsys, dev_name(dev)); } -skip: - if (level[2]) - level_extra = &level[2]; /* skip past KERN_SOH "L" */ - return printk_emit(0, level[1] - '0', - dictlen ? dict : NULL, dictlen, - "%s %s: %s%pV", - dev_driver_string(dev), dev_name(dev), - level_extra, vaf); + return pos; +} +EXPORT_SYMBOL(create_syslog_header); + +static int __dev_printk(const char *level, const struct device *dev, + struct va_format *vaf) +{ + char hdr[128]; + size_t hdrlen; + + if (!dev) + return printk("%s(NULL device *): %pV", level, vaf); + + hdrlen = create_syslog_header(dev, hdr, sizeof(hdr)); + + return printk_emit(0, level[1] - '0', hdrlen ? hdr : NULL, hdrlen, + "%s %s: %pV", + dev_driver_string(dev), dev_name(dev), vaf); } -EXPORT_SYMBOL(__dev_printk); int dev_printk(const char *level, const struct device *dev, const char *fmt, ...) @@ -1940,6 +1942,7 @@ int dev_printk(const char *level, const struct device *dev, vaf.va = &args; r = __dev_printk(level, dev, &vaf); + va_end(args); return r; @@ -1959,6 +1962,7 @@ int func(const struct device *dev, const char *fmt, ...) \ vaf.va = &args; \ \ r = __dev_printk(kern_level, dev, &vaf); \ + \ va_end(args); \ \ return r; \ diff --git a/include/linux/device.h b/include/linux/device.h index 378a27c1d1af..a1443b48ed4b 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -895,12 +895,12 @@ extern const char *dev_driver_string(const struct device *dev); #ifdef CONFIG_PRINTK -extern int __dev_printk(const char *level, const struct device *dev, - struct va_format *vaf); +extern int create_syslog_header(const struct device *dev, + char *hdr, size_t hdrlen); + extern __printf(3, 4) int dev_printk(const char *level, const struct device *dev, - const char *fmt, ...) - ; + const char *fmt, ...); extern __printf(2, 3) int dev_emerg(const struct device *dev, const char *fmt, ...); extern __printf(2, 3) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 7ca29a0a3019..29ff2e4cfb75 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -521,25 +521,25 @@ static char *dynamic_emit_prefix(const struct _ddebug *desc, char *buf) int pos_after_tid; int pos = 0; - pos += snprintf(buf + pos, remaining(pos), "%s", KERN_DEBUG); + *buf = '\0'; + if (desc->flags & _DPRINTK_FLAGS_INCL_TID) { if (in_interrupt()) - pos += snprintf(buf + pos, remaining(pos), "%s ", - ""); + pos += snprintf(buf + pos, remaining(pos), " "); else pos += snprintf(buf + pos, remaining(pos), "[%d] ", - task_pid_vnr(current)); + task_pid_vnr(current)); } pos_after_tid = pos; if (desc->flags & _DPRINTK_FLAGS_INCL_MODNAME) pos += snprintf(buf + pos, remaining(pos), "%s:", - desc->modname); + desc->modname); if (desc->flags & _DPRINTK_FLAGS_INCL_FUNCNAME) pos += snprintf(buf + pos, remaining(pos), "%s:", - desc->function); + desc->function); if (desc->flags & _DPRINTK_FLAGS_INCL_LINENO) pos += snprintf(buf + pos, remaining(pos), "%d:", - desc->lineno); + desc->lineno); if (pos - pos_after_tid) pos += snprintf(buf + pos, remaining(pos), " "); if (pos >= PREFIX_SIZE) @@ -559,9 +559,13 @@ int __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...) BUG_ON(!fmt); va_start(args, fmt); + vaf.fmt = fmt; vaf.va = &args; - res = printk("%s%pV", dynamic_emit_prefix(descriptor, buf), &vaf); + + res = printk(KERN_DEBUG "%s%pV", + dynamic_emit_prefix(descriptor, buf), &vaf); + va_end(args); return res; @@ -574,15 +578,30 @@ int __dynamic_dev_dbg(struct _ddebug *descriptor, struct va_format vaf; va_list args; int res; - char buf[PREFIX_SIZE]; BUG_ON(!descriptor); BUG_ON(!fmt); va_start(args, fmt); + vaf.fmt = fmt; vaf.va = &args; - res = __dev_printk(dynamic_emit_prefix(descriptor, buf), dev, &vaf); + + if (!dev) { + res = printk(KERN_DEBUG "(NULL device *): %pV", &vaf); + } else { + char buf[PREFIX_SIZE]; + char dict[128]; + size_t dictlen; + + dictlen = create_syslog_header(dev, dict, sizeof(dict)); + + res = printk_emit(0, 7, dictlen ? dict : NULL, dictlen, + "%s%s %s: %pV", + dynamic_emit_prefix(descriptor, buf), + dev_driver_string(dev), dev_name(dev), &vaf); + } + va_end(args); return res; -- cgit v1.3-14-g43fede From 05e4e5b87aab74f994acf9dd4bed4a8f1367cd09 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 12 Sep 2012 20:13:37 -0700 Subject: dev: Add dev_vprintk_emit and dev_printk_emit Add utility functions to consolidate the use of create_syslog_header and vprintk_emit. This allows conversion of logging functions that call create_syslog_header and then call vprintk_emit or printk_emit to the dev_ equivalents. Signed-off-by: Joe Perches Acked-by: David S. Miller Tested-by: Jim Cromie Acked-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 27 +++++++++++++++++++++++++++ include/linux/device.h | 11 +++++++++++ 2 files changed, 38 insertions(+) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index d325b9dab3ff..dff94c8fb350 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1913,6 +1913,33 @@ int create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen) } EXPORT_SYMBOL(create_syslog_header); +int dev_vprintk_emit(int level, const struct device *dev, + const char *fmt, va_list args) +{ + char hdr[128]; + size_t hdrlen; + + hdrlen = create_syslog_header(dev, hdr, sizeof(hdr)); + + return vprintk_emit(0, level, hdrlen ? hdr : NULL, hdrlen, fmt, args); +} +EXPORT_SYMBOL(dev_vprintk_emit); + +int dev_printk_emit(int level, const struct device *dev, const char *fmt, ...) +{ + va_list args; + int r; + + va_start(args, fmt); + + r = dev_vprintk_emit(level, dev, fmt, args); + + va_end(args); + + return r; +} +EXPORT_SYMBOL(dev_printk_emit); + static int __dev_printk(const char *level, const struct device *dev, struct va_format *vaf) { diff --git a/include/linux/device.h b/include/linux/device.h index a1443b48ed4b..0d9ba0c09791 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -897,6 +897,10 @@ extern const char *dev_driver_string(const struct device *dev); extern int create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen); +extern int dev_vprintk_emit(int level, const struct device *dev, + const char *fmt, va_list args); +extern __printf(3, 4) +int dev_printk_emit(int level, const struct device *dev, const char *fmt, ...); extern __printf(3, 4) int dev_printk(const char *level, const struct device *dev, @@ -918,6 +922,13 @@ int _dev_info(const struct device *dev, const char *fmt, ...); #else +static int dev_vprintk_emit(int level, const struct device *dev, + const char *fmt, va_list args) +{ return 0; } +static inline __printf(3, 4) +int dev_printk_emit(int level, const struct device *dev, const char *fmt, ...) +{ return 0; } + static inline int __dev_printk(const char *level, const struct device *dev, struct va_format *vaf) { return 0; } -- cgit v1.3-14-g43fede From 666f355f3805d68b6ed5f7013806f1f65abfbf03 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 12 Sep 2012 20:14:11 -0700 Subject: device and dynamic_debug: Use dev_vprintk_emit and dev_printk_emit Convert direct calls of vprintk_emit and printk_emit to the dev_ equivalents. Make create_syslog_header static. Signed-off-by: Joe Perches Acked-by: David S. Miller Tested-by: Jim Cromie Acked-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 14 +++++--------- include/linux/device.h | 2 -- lib/dynamic_debug.c | 31 +++++++++++-------------------- net/core/dev.c | 16 ++++++---------- 4 files changed, 22 insertions(+), 41 deletions(-) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index dff94c8fb350..abea76c36a4b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1865,7 +1865,8 @@ void device_shutdown(void) */ #ifdef CONFIG_PRINTK -int create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen) +static int +create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen) { const char *subsys; size_t pos = 0; @@ -1943,17 +1944,12 @@ EXPORT_SYMBOL(dev_printk_emit); static int __dev_printk(const char *level, const struct device *dev, struct va_format *vaf) { - char hdr[128]; - size_t hdrlen; - if (!dev) return printk("%s(NULL device *): %pV", level, vaf); - hdrlen = create_syslog_header(dev, hdr, sizeof(hdr)); - - return printk_emit(0, level[1] - '0', hdrlen ? hdr : NULL, hdrlen, - "%s %s: %pV", - dev_driver_string(dev), dev_name(dev), vaf); + return dev_printk_emit(level[1] - '0', dev, + "%s %s: %pV", + dev_driver_string(dev), dev_name(dev), vaf); } int dev_printk(const char *level, const struct device *dev, diff --git a/include/linux/device.h b/include/linux/device.h index 0d9ba0c09791..6d37e59db571 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -895,8 +895,6 @@ extern const char *dev_driver_string(const struct device *dev); #ifdef CONFIG_PRINTK -extern int create_syslog_header(const struct device *dev, - char *hdr, size_t hdrlen); extern int dev_vprintk_emit(int level, const struct device *dev, const char *fmt, va_list args); extern __printf(3, 4) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 6b3ebabacfa8..e7f7d993357a 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -591,15 +591,11 @@ int __dynamic_dev_dbg(struct _ddebug *descriptor, res = printk(KERN_DEBUG "(NULL device *): %pV", &vaf); } else { char buf[PREFIX_SIZE]; - char dict[128]; - size_t dictlen; - dictlen = create_syslog_header(dev, dict, sizeof(dict)); - - res = printk_emit(0, 7, dictlen ? dict : NULL, dictlen, - "%s%s %s: %pV", - dynamic_emit_prefix(descriptor, buf), - dev_driver_string(dev), dev_name(dev), &vaf); + res = dev_printk_emit(7, dev, "%s%s %s: %pV", + dynamic_emit_prefix(descriptor, buf), + dev_driver_string(dev), dev_name(dev), + &vaf); } va_end(args); @@ -627,18 +623,13 @@ int __dynamic_netdev_dbg(struct _ddebug *descriptor, if (dev && dev->dev.parent) { char buf[PREFIX_SIZE]; - char dict[128]; - size_t dictlen; - - dictlen = create_syslog_header(dev->dev.parent, - dict, sizeof(dict)); - - res = printk_emit(0, 7, dictlen ? dict : NULL, dictlen, - "%s%s %s %s: %pV", - dynamic_emit_prefix(descriptor, buf), - dev_driver_string(dev->dev.parent), - dev_name(dev->dev.parent), - netdev_name(dev), &vaf); + + res = dev_printk_emit(7, dev->dev.parent, + "%s%s %s %s: %pV", + dynamic_emit_prefix(descriptor, buf), + dev_driver_string(dev->dev.parent), + dev_name(dev->dev.parent), + netdev_name(dev), &vaf); } else if (dev) { res = printk(KERN_DEBUG "%s: %pV", netdev_name(dev), &vaf); } else { diff --git a/net/core/dev.c b/net/core/dev.c index cb9d43be07e7..76c0fe66fdc0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6429,16 +6429,12 @@ static int __netdev_printk(const char *level, const struct net_device *dev, int r; if (dev && dev->dev.parent) { - char dict[128]; - size_t dictlen = create_syslog_header(dev->dev.parent, - dict, sizeof(dict)); - - r = printk_emit(0, level[1] - '0', - dictlen ? dict : NULL, dictlen, - "%s %s %s: %pV", - dev_driver_string(dev->dev.parent), - dev_name(dev->dev.parent), - netdev_name(dev), vaf); + r = dev_printk_emit(level[1] - '0', + dev->dev.parent, + "%s %s %s: %pV", + dev_driver_string(dev->dev.parent), + dev_name(dev->dev.parent), + netdev_name(dev), vaf); } else if (dev) { r = printk("%s%s: %pV", level, netdev_name(dev), vaf); } else { -- cgit v1.3-14-g43fede