Driver core patches for 3.18-rc1

Here's the driver core patches for 3.18-rc1.  Just a few small things,
 and the addition of a new interface to dump firmware "core dumps" to
 userspace through sysfs that the wireless and graphic drivers want to
 use.
 
 All of these have been in linux-next for a while.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iEYEABECAAYFAlQ0ZgwACgkQMUfUDdst+ymh9ACfZi5TG1AD8/EesCoKaoTd4yJZ
 QOcAnjISbF9IKL1ia1fESqFYyTO+XqrP
 =YdeX
 -----END PGP SIGNATURE-----

Merge tag 'driver-core-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core

Pull driver core update from Greg KH:
 "Here's the driver core patches for 3.18-rc1.  Just a few small things,
  and the addition of a new interface to dump firmware "core dumps" to
  userspace through sysfs that the wireless and graphic drivers want to
  use.

  All of these have been in linux-next for a while"

* tag 'driver-core-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core:
  dynamic_debug: change __dynamic_<foo>_dbg return types to void
  driver/base/node: remove unnecessary kfree of node struct from unregister_one_node
  devres: Improve devm_kasprintf()/kvasprintf() support
  Documentation: devres: Add missing devm_kstrdup() managed interface
  Documentation: devres: Add missing IRQ functions
  firmware_class: make sure fw requests contain a name
  driver core: Remove kerneldoc from local function
  attribute_container: fix coding style issues
  attribute_container: fix whitespace errors
  drivers/base: Fix length checks in create_syslog_header()/dev_vprintk_emit()
  device coredump: add new device coredump class
  Documentation/sysfs-rules.txt: Add device attribute error code documentation
This commit is contained in:
Linus Torvalds 2014-10-08 06:53:19 -04:00
commit bca51651fc
16 changed files with 411 additions and 54 deletions

View file

@ -281,7 +281,9 @@ IOMAP
IRQ
devm_free_irq()
devm_request_any_context_irq()
devm_request_irq()
devm_request_threaded_irq()
MDIO
devm_mdiobus_alloc()
@ -291,11 +293,14 @@ MDIO
MEM
devm_free_pages()
devm_get_free_pages()
devm_kasprintf()
devm_kcalloc()
devm_kfree()
devm_kmalloc()
devm_kmalloc_array()
devm_kmemdup()
devm_kstrdup()
devm_kvasprintf()
devm_kzalloc()
PCI

View file

@ -161,3 +161,24 @@ versions of the sysfs interface.
the device that matches the expected subsystem. Depending on a specific
position of a parent device or exposing relative paths using "../" to
access the chain of parents is a bug in the application.
- When reading and writing sysfs device attribute files, avoid dependency
on specific error codes wherever possible. This minimizes coupling to
the error handling implementation within the kernel.
In general, failures to read or write sysfs device attributes shall
propagate errors wherever possible. Common errors include, but are not
limited to:
-EIO: The read or store operation is not supported, typically returned by
the sysfs system itself if the read or store pointer is NULL.
-ENXIO: The read or store operation failed
Error codes will not be changed without good reason, and should a change
to error codes result in user-space breakage, it will be fixed, or the
the offending change will be reverted.
Userspace applications can, however, expect the format and contents of
the attribute files to remain consistent in the absence of a version
attribute change in the context of a given attribute.

View file

@ -2877,6 +2877,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
S: Maintained
F: drivers/usb/dwc3/
DEVICE COREDUMP (DEV_COREDUMP)
M: Johannes Berg <johannes@sipsolutions.net>
L: linux-kernel@vger.kernel.org
S: Maintained
F: drivers/base/devcoredump.c
F: include/linux/devcoredump.h
DEVICE FREQUENCY (DEVFREQ)
M: MyungJoo Ham <myungjoo.ham@samsung.com>
M: Kyungmin Park <kyungmin.park@samsung.com>

View file

@ -165,6 +165,27 @@ config FW_LOADER_USER_HELPER_FALLBACK
If you are unsure about this, say N here.
config WANT_DEV_COREDUMP
bool
help
Drivers should "select" this option if they desire to use the
device coredump mechanism.
config DISABLE_DEV_COREDUMP
bool "Disable device coredump" if EXPERT
help
Disable the device coredump mechanism despite drivers wanting to
use it; this allows for more sensitive systems or systems that
don't want to ever access the information to not have the code,
nor keep any data.
If unsure, say N.
config DEV_COREDUMP
bool
default y if WANT_DEV_COREDUMP
depends on !DISABLE_DEV_COREDUMP
config DEBUG_DRIVER
bool "Driver Core verbose debug messages"
depends on DEBUG_KERNEL

View file

@ -21,6 +21,7 @@ obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
obj-$(CONFIG_REGMAP) += regmap/
obj-$(CONFIG_SOC_BUS) += soc.o
obj-$(CONFIG_PINCTRL) += pinctrl.o
obj-$(CONFIG_DEV_COREDUMP) += devcoredump.o
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG

View file

@ -74,9 +74,9 @@ int
attribute_container_register(struct attribute_container *cont)
{
INIT_LIST_HEAD(&cont->node);
klist_init(&cont->containers,internal_container_klist_get,
klist_init(&cont->containers, internal_container_klist_get,
internal_container_klist_put);
mutex_lock(&attribute_container_mutex);
list_add_tail(&cont->node, &attribute_container_list);
mutex_unlock(&attribute_container_mutex);
@ -104,14 +104,14 @@ attribute_container_unregister(struct attribute_container *cont)
spin_unlock(&cont->containers.k_lock);
mutex_unlock(&attribute_container_mutex);
return retval;
}
EXPORT_SYMBOL_GPL(attribute_container_unregister);
/* private function used as class release */
static void attribute_container_release(struct device *classdev)
{
struct internal_container *ic
struct internal_container *ic
= container_of(classdev, struct internal_container, classdev);
struct device *dev = classdev->parent;
@ -184,8 +184,8 @@ attribute_container_add_device(struct device *dev,
struct klist_node *n = klist_next(iter); \
n ? container_of(n, typeof(*pos), member) : \
({ klist_iter_exit(iter) ; NULL; }); \
}) ) != NULL; )
})) != NULL;)
/**
* attribute_container_remove_device - make device eligible for removal.
@ -247,7 +247,7 @@ attribute_container_remove_device(struct device *dev,
* container, then use attribute_container_trigger() instead.
*/
void
attribute_container_device_trigger(struct device *dev,
attribute_container_device_trigger(struct device *dev,
int (*fn)(struct attribute_container *,
struct device *,
struct device *))

View file

@ -2007,6 +2007,8 @@ create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen)
return 0;
pos += snprintf(hdr + pos, hdrlen - pos, "SUBSYSTEM=%s", subsys);
if (pos >= hdrlen)
goto overflow;
/*
* Add device identifier DEVICE=:
@ -2038,7 +2040,14 @@ create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen)
"DEVICE=+%s:%s", subsys, dev_name(dev));
}
if (pos >= hdrlen)
goto overflow;
return pos;
overflow:
dev_WARN(dev, "device/subsystem name too long");
return 0;
}
int dev_vprintk_emit(int level, const struct device *dev,

View file

@ -54,7 +54,7 @@ static LIST_HEAD(deferred_probe_active_list);
static struct workqueue_struct *deferred_wq;
static atomic_t deferred_trigger_count = ATOMIC_INIT(0);
/**
/*
* deferred_probe_work_func() - Retry probing devices in the active list.
*/
static void deferred_probe_work_func(struct work_struct *work)

265
drivers/base/devcoredump.c Normal file
View file

@ -0,0 +1,265 @@
/*
* This file is provided under the GPLv2 license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* Author: Johannes Berg <johannes@sipsolutions.net>
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/devcoredump.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/workqueue.h>
/* if data isn't read by userspace after 5 minutes then delete it */
#define DEVCD_TIMEOUT (HZ * 60 * 5)
struct devcd_entry {
struct device devcd_dev;
const void *data;
size_t datalen;
struct module *owner;
ssize_t (*read)(char *buffer, loff_t offset, size_t count,
const void *data, size_t datalen);
void (*free)(const void *data);
struct delayed_work del_wk;
struct device *failing_dev;
};
static struct devcd_entry *dev_to_devcd(struct device *dev)
{
return container_of(dev, struct devcd_entry, devcd_dev);
}
static void devcd_dev_release(struct device *dev)
{
struct devcd_entry *devcd = dev_to_devcd(dev);
devcd->free(devcd->data);
module_put(devcd->owner);
/*
* this seems racy, but I don't see a notifier or such on
* a struct device to know when it goes away?
*/
if (devcd->failing_dev->kobj.sd)
sysfs_delete_link(&devcd->failing_dev->kobj, &dev->kobj,
"devcoredump");
put_device(devcd->failing_dev);
kfree(devcd);
}
static void devcd_del(struct work_struct *wk)
{
struct devcd_entry *devcd;
devcd = container_of(wk, struct devcd_entry, del_wk.work);
device_del(&devcd->devcd_dev);
put_device(&devcd->devcd_dev);
}
static ssize_t devcd_data_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buffer, loff_t offset, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
struct devcd_entry *devcd = dev_to_devcd(dev);
return devcd->read(buffer, offset, count, devcd->data, devcd->datalen);
}
static ssize_t devcd_data_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buffer, loff_t offset, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
struct devcd_entry *devcd = dev_to_devcd(dev);
mod_delayed_work(system_wq, &devcd->del_wk, 0);
return count;
}
static struct bin_attribute devcd_attr_data = {
.attr = { .name = "data", .mode = S_IRUSR | S_IWUSR, },
.size = 0,
.read = devcd_data_read,
.write = devcd_data_write,
};
static struct bin_attribute *devcd_dev_bin_attrs[] = {
&devcd_attr_data, NULL,
};
static const struct attribute_group devcd_dev_group = {
.bin_attrs = devcd_dev_bin_attrs,
};
static const struct attribute_group *devcd_dev_groups[] = {
&devcd_dev_group, NULL,
};
static struct class devcd_class = {
.name = "devcoredump",
.owner = THIS_MODULE,
.dev_release = devcd_dev_release,
.dev_groups = devcd_dev_groups,
};
static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count,
const void *data, size_t datalen)
{
if (offset > datalen)
return -EINVAL;
if (offset + count > datalen)
count = datalen - offset;
if (count)
memcpy(buffer, ((u8 *)data) + offset, count);
return count;
}
/**
* dev_coredumpv - create device coredump with vmalloc data
* @dev: the struct device for the crashed device
* @data: vmalloc data containing the device coredump
* @datalen: length of the data
* @gfp: allocation flags
*
* This function takes ownership of the vmalloc'ed data and will free
* it when it is no longer used. See dev_coredumpm() for more information.
*/
void dev_coredumpv(struct device *dev, const void *data, size_t datalen,
gfp_t gfp)
{
dev_coredumpm(dev, NULL, data, datalen, gfp, devcd_readv, vfree);
}
EXPORT_SYMBOL_GPL(dev_coredumpv);
static int devcd_match_failing(struct device *dev, const void *failing)
{
struct devcd_entry *devcd = dev_to_devcd(dev);
return devcd->failing_dev == failing;
}
/**
* dev_coredumpm - create device coredump with read/free methods
* @dev: the struct device for the crashed device
* @owner: the module that contains the read/free functions, use %THIS_MODULE
* @data: data cookie for the @read/@free functions
* @datalen: length of the data
* @gfp: allocation flags
* @read: function to read from the given buffer
* @free: function to free the given buffer
*
* Creates a new device coredump for the given device. If a previous one hasn't
* been read yet, the new coredump is discarded. The data lifetime is determined
* by the device coredump framework and when it is no longer needed the @free
* function will be called to free the data.
*/
void dev_coredumpm(struct device *dev, struct module *owner,
const void *data, size_t datalen, gfp_t gfp,
ssize_t (*read)(char *buffer, loff_t offset, size_t count,
const void *data, size_t datalen),
void (*free)(const void *data))
{
static atomic_t devcd_count = ATOMIC_INIT(0);
struct devcd_entry *devcd;
struct device *existing;
existing = class_find_device(&devcd_class, NULL, dev,
devcd_match_failing);
if (existing) {
put_device(existing);
goto free;
}
if (!try_module_get(owner))
goto free;
devcd = kzalloc(sizeof(*devcd), gfp);
if (!devcd)
goto put_module;
devcd->owner = owner;
devcd->data = data;
devcd->datalen = datalen;
devcd->read = read;
devcd->free = free;
devcd->failing_dev = get_device(dev);
device_initialize(&devcd->devcd_dev);
dev_set_name(&devcd->devcd_dev, "devcd%d",
atomic_inc_return(&devcd_count));
devcd->devcd_dev.class = &devcd_class;
if (device_add(&devcd->devcd_dev))
goto put_device;
if (sysfs_create_link(&devcd->devcd_dev.kobj, &dev->kobj,
"failing_device"))
/* nothing - symlink will be missing */;
if (sysfs_create_link(&dev->kobj, &devcd->devcd_dev.kobj,
"devcoredump"))
/* nothing - symlink will be missing */;
INIT_DELAYED_WORK(&devcd->del_wk, devcd_del);
schedule_delayed_work(&devcd->del_wk, DEVCD_TIMEOUT);
return;
put_device:
put_device(&devcd->devcd_dev);
put_module:
module_put(owner);
free:
free(data);
}
EXPORT_SYMBOL_GPL(dev_coredumpm);
static int __init devcoredump_init(void)
{
return class_register(&devcd_class);
}
__initcall(devcoredump_init);
static int devcd_free(struct device *dev, void *data)
{
struct devcd_entry *devcd = dev_to_devcd(dev);
flush_delayed_work(&devcd->del_wk);
return 0;
}
static void __exit devcoredump_exit(void)
{
class_for_each_device(&devcd_class, NULL, NULL, devcd_free);
class_unregister(&devcd_class);
}
__exitcall(devcoredump_exit);

View file

@ -817,13 +817,13 @@ char *devm_kstrdup(struct device *dev, const char *s, gfp_t gfp)
EXPORT_SYMBOL_GPL(devm_kstrdup);
/**
* devm_kvasprintf - Allocate resource managed space
* for the formatted string.
* devm_kvasprintf - Allocate resource managed space and format a string
* into that.
* @dev: Device to allocate memory for
* @gfp: the GFP mask used in the devm_kmalloc() call when
* allocating memory
* @fmt: the formatted string to duplicate
* @ap: the list of tokens to be placed in the formatted string
* @fmt: The printf()-style format string
* @ap: Arguments for the format string
* RETURNS:
* Pointer to allocated string on success, NULL on failure.
*/
@ -849,12 +849,13 @@ char *devm_kvasprintf(struct device *dev, gfp_t gfp, const char *fmt,
EXPORT_SYMBOL(devm_kvasprintf);
/**
* devm_kasprintf - Allocate resource managed space
* and copy an existing formatted string into that
* devm_kasprintf - Allocate resource managed space and format a string
* into that.
* @dev: Device to allocate memory for
* @gfp: the GFP mask used in the devm_kmalloc() call when
* allocating memory
* @fmt: the string to duplicate
* @fmt: The printf()-style format string
* @...: Arguments for the format string
* RETURNS:
* Pointer to allocated string on success, NULL on failure.
*/

View file

@ -1105,6 +1105,9 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
if (!firmware_p)
return -EINVAL;
if (!name || name[0] == '\0')
return -EINVAL;
ret = _request_firmware_prepare(&fw, name, device);
if (ret <= 0) /* error or already assigned */
goto out;

View file

@ -603,7 +603,6 @@ void unregister_one_node(int nid)
return;
unregister_node(node_devices[nid]);
kfree(node_devices[nid]);
node_devices[nid] = NULL;
}

View file

@ -0,0 +1,35 @@
#ifndef __DEVCOREDUMP_H
#define __DEVCOREDUMP_H
#include <linux/device.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#ifdef CONFIG_DEV_COREDUMP
void dev_coredumpv(struct device *dev, const void *data, size_t datalen,
gfp_t gfp);
void dev_coredumpm(struct device *dev, struct module *owner,
const void *data, size_t datalen, gfp_t gfp,
ssize_t (*read)(char *buffer, loff_t offset, size_t count,
const void *data, size_t datalen),
void (*free)(const void *data));
#else
static inline void dev_coredumpv(struct device *dev, const void *data,
size_t datalen, gfp_t gfp)
{
vfree(data);
}
static inline void
dev_coredumpm(struct device *dev, struct module *owner,
const void *data, size_t datalen, gfp_t gfp,
ssize_t (*read)(char *buffer, loff_t offset, size_t count,
const void *data, size_t datalen),
void (*free)(const void *data))
{
free(data);
}
#endif /* CONFIG_DEV_COREDUMP */
#endif /* __DEVCOREDUMP_H */

View file

@ -607,8 +607,8 @@ extern int devres_release_group(struct device *dev, void *id);
extern void *devm_kmalloc(struct device *dev, size_t size, gfp_t gfp);
extern char *devm_kvasprintf(struct device *dev, gfp_t gfp, const char *fmt,
va_list ap);
extern char *devm_kasprintf(struct device *dev, gfp_t gfp,
const char *fmt, ...);
extern __printf(3, 4)
char *devm_kasprintf(struct device *dev, gfp_t gfp, const char *fmt, ...);
static inline void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp)
{
return devm_kmalloc(dev, size, gfp | __GFP_ZERO);

View file

@ -42,7 +42,7 @@ int ddebug_add_module(struct _ddebug *tab, unsigned int n,
#if defined(CONFIG_DYNAMIC_DEBUG)
extern int ddebug_remove_module(const char *mod_name);
extern __printf(2, 3)
int __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...);
void __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...);
extern int ddebug_dyndbg_module_param_cb(char *param, char *val,
const char *modname);
@ -50,15 +50,15 @@ extern int ddebug_dyndbg_module_param_cb(char *param, char *val,
struct device;
extern __printf(3, 4)
int __dynamic_dev_dbg(struct _ddebug *descriptor, const struct device *dev,
const char *fmt, ...);
void __dynamic_dev_dbg(struct _ddebug *descriptor, const struct device *dev,
const char *fmt, ...);
struct net_device;
extern __printf(3, 4)
int __dynamic_netdev_dbg(struct _ddebug *descriptor,
const struct net_device *dev,
const char *fmt, ...);
void __dynamic_netdev_dbg(struct _ddebug *descriptor,
const struct net_device *dev,
const char *fmt, ...);
#define DEFINE_DYNAMIC_DEBUG_METADATA(name, fmt) \
static struct _ddebug __aligned(8) \

View file

@ -537,10 +537,9 @@ static char *dynamic_emit_prefix(const struct _ddebug *desc, char *buf)
return buf;
}
int __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...)
void __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...)
{
va_list args;
int res;
struct va_format vaf;
char buf[PREFIX_SIZE];
@ -552,21 +551,17 @@ int __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...)
vaf.fmt = fmt;
vaf.va = &args;
res = printk(KERN_DEBUG "%s%pV",
dynamic_emit_prefix(descriptor, buf), &vaf);
printk(KERN_DEBUG "%s%pV", dynamic_emit_prefix(descriptor, buf), &vaf);
va_end(args);
return res;
}
EXPORT_SYMBOL(__dynamic_pr_debug);
int __dynamic_dev_dbg(struct _ddebug *descriptor,
void __dynamic_dev_dbg(struct _ddebug *descriptor,
const struct device *dev, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
int res;
BUG_ON(!descriptor);
BUG_ON(!fmt);
@ -577,30 +572,27 @@ int __dynamic_dev_dbg(struct _ddebug *descriptor,
vaf.va = &args;
if (!dev) {
res = printk(KERN_DEBUG "(NULL device *): %pV", &vaf);
printk(KERN_DEBUG "(NULL device *): %pV", &vaf);
} else {
char buf[PREFIX_SIZE];
res = dev_printk_emit(7, dev, "%s%s %s: %pV",
dynamic_emit_prefix(descriptor, buf),
dev_driver_string(dev), dev_name(dev),
&vaf);
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);
return res;
}
EXPORT_SYMBOL(__dynamic_dev_dbg);
#ifdef CONFIG_NET
int __dynamic_netdev_dbg(struct _ddebug *descriptor,
const struct net_device *dev, const char *fmt, ...)
void __dynamic_netdev_dbg(struct _ddebug *descriptor,
const struct net_device *dev, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
int res;
BUG_ON(!descriptor);
BUG_ON(!fmt);
@ -613,23 +605,21 @@ int __dynamic_netdev_dbg(struct _ddebug *descriptor,
if (dev && dev->dev.parent) {
char buf[PREFIX_SIZE];
res = dev_printk_emit(7, dev->dev.parent,
"%s%s %s %s%s: %pV",
dynamic_emit_prefix(descriptor, buf),
dev_driver_string(dev->dev.parent),
dev_name(dev->dev.parent),
netdev_name(dev), netdev_reg_state(dev),
&vaf);
dev_printk_emit(7, dev->dev.parent,
"%s%s %s %s%s: %pV",
dynamic_emit_prefix(descriptor, buf),
dev_driver_string(dev->dev.parent),
dev_name(dev->dev.parent),
netdev_name(dev), netdev_reg_state(dev),
&vaf);
} else if (dev) {
res = printk(KERN_DEBUG "%s%s: %pV", netdev_name(dev),
netdev_reg_state(dev), &vaf);
printk(KERN_DEBUG "%s%s: %pV", netdev_name(dev),
netdev_reg_state(dev), &vaf);
} else {
res = printk(KERN_DEBUG "(NULL net_device): %pV", &vaf);
printk(KERN_DEBUG "(NULL net_device): %pV", &vaf);
}
va_end(args);
return res;
}
EXPORT_SYMBOL(__dynamic_netdev_dbg);