lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20101108203258.22479.3417.stgit@crlf.mtv.corp.google.com>
Date:	Mon, 08 Nov 2010 12:32:59 -0800
From:	Mike Waychison <mikew@...gle.com>
To:	simon.kagstrom@...insight.net, davem@...emloft.net,
	nhorman@...driver.com, Matt Mackall <mpm@...enic.com>
Cc:	adurbin@...gle.com, linux-kernel@...r.kernel.org,
	chavey@...gle.com, Greg KH <greg@...ah.com>,
	Américo Wang <xiyou.wangcong@...il.com>,
	akpm@...ux-foundation.org, linux-api@...r.kernel.org
Subject: [PATCH v2 14/23] netpoll: Move target code into netpoll_targets.c

Move all the target handling code out of netconsole.c and into a new
file, net/core/netpoll_targets.c.

In doing so, we have to remove the __exit and __init attributes on the
constructors/desctructors, as well as export the needed symbols to
modules.  Reference counting also becomes a no-op when dynamic targets
are disabled (!CONFIG_NETPOLL_TARGETS_DYNAMIC).

>>From netpoll_targets.c's perspective, MAX_PARAM_LENGTH isn't available,
so we ensure that the string being passed in is zero-terminated and do
an open strlen() instead of strnlen().

Signed-off-by: Mike Waychison <mikew@...gle.com>
---
 drivers/net/netconsole.c        |  820 ---------------------------------------
 include/linux/netpoll_targets.h |   76 ++++
 net/core/Makefile               |    1 
 net/core/netpoll_targets.c      |  746 +++++++++++++++++++++++++++++++++++
 4 files changed, 825 insertions(+), 818 deletions(-)
 create mode 100644 include/linux/netpoll_targets.h
 create mode 100644 net/core/netpoll_targets.c

diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 0d77fc8..64b0e4f 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -37,13 +37,11 @@
 #include <linux/mm.h>
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/slab.h>
 #include <linux/console.h>
 #include <linux/moduleparam.h>
-#include <linux/string.h>
 #include <linux/netpoll.h>
+#include <linux/netpoll_targets.h>
 #include <linux/inet.h>
-#include <linux/configfs.h>
 
 MODULE_AUTHOR("Maintainer: Matt Mackall <mpm@...enic.com>");
 MODULE_DESCRIPTION("Console driver for network interfaces");
@@ -65,743 +63,8 @@ static int __init option_setup(char *opt)
 __setup("netconsole=", option_setup);
 #endif	/* MODULE */
 
-struct netpoll_targets {
-	struct list_head list;
-	spinlock_t lock;
-	u16 default_local_port, default_remote_port;
-#ifdef	CONFIG_NETPOLL_TARGETS_DYNAMIC
-	struct configfs_subsystem configfs_subsys;
-#endif
-	struct notifier_block netdev_notifier;
-	char *subsys_name;
-};
-#define DEFINE_NETPOLL_TARGETS(x) struct netpoll_targets x = \
-	{ .list = LIST_HEAD_INIT(x.list), \
-	  .lock = __SPIN_LOCK_UNLOCKED(x.lock) }
 static DEFINE_NETPOLL_TARGETS(targets);
 
-#define NETPOLL_DISABLED	0
-#define NETPOLL_SETTINGUP	1
-#define NETPOLL_ENABLED		2
-#define NETPOLL_CLEANING	3
-
-/**
- * struct netpoll_target - Represents a configured netpoll target.
- * @list:	Links this target into the netpoll_targets.list.
- * @item:	Links us into the configfs subsystem hierarchy.
- * @np_state:	Enabled / Disabled / SettingUp / Cleaning
- *		Visible from userspace (read-write) as "enabled".
- *		We maintain a state machine here of the valid states.  Either a
- *		target is enabled or disabled, but it may also be in a
- *		transitional state whereby nobody is allowed to act on the
- *		target other than whoever owns the transition.
- *
- *		Also, other parameters of a target may be modified at
- *		runtime only when it is disabled (np_state == NETPOLL_ENABLED).
- * @np:		The netpoll structure for this target.
- *		Contains the other userspace visible parameters:
- *		dev_name	(read-write)
- *		local_port	(read-write)
- *		remote_port	(read-write)
- *		local_ip	(read-write)
- *		remote_ip	(read-write)
- *		local_mac	(read-only)
- *		remote_mac	(read-write)
- */
-struct netpoll_target {
-	struct netpoll_targets *nts;
-	struct list_head	list;
-#ifdef	CONFIG_NETPOLL_TARGETS_DYNAMIC
-	struct config_item	item;
-#endif
-	int			np_state;
-	struct netpoll		np;
-	struct work_struct	cleanup_work;
-};
-
-static void netpoll_target_get(struct netpoll_target *nt);
-static void netpoll_target_put(struct netpoll_target *nt);
-
-static void deferred_netpoll_cleanup(struct work_struct *work)
-{
-	struct netpoll_target *nt;
-	struct netpoll_targets *nts;
-	unsigned long flags;
-
-	nt = container_of(work, struct netpoll_target, cleanup_work);
-	nts = nt->nts;
-
-	netpoll_cleanup(&nt->np);
-
-	spin_lock_irqsave(&nts->lock, flags);
-	BUG_ON(nt->np_state != NETPOLL_CLEANING);
-	nt->np_state = NETPOLL_DISABLED;
-	spin_unlock_irqrestore(&nts->lock, flags);
-
-	netpoll_target_put(nt);
-}
-
-/* Allocate new target (from boot/module param) and setup netpoll for it */
-static struct netpoll_target *alloc_param_target(struct netpoll_targets *nts,
-						 char *target_config)
-{
-	int err = -ENOMEM;
-	struct netpoll_target *nt;
-
-	/*
-	 * Allocate and initialize with defaults.
-	 * Note that these targets get their config_item fields zeroed-out.
-	 */
-	nt = kzalloc(sizeof(*nt), GFP_KERNEL);
-	if (!nt) {
-		printk(KERN_ERR "%s: failed to allocate memory\n",
-		       nts->subsys_name);
-		goto fail;
-	}
-
-	nt->nts = nts;
-	nt->np.name = nts->subsys_name;
-	strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ);
-	nt->np.local_port = nts->default_local_port;
-	nt->np.remote_port = nts->default_remote_port;
-	memset(nt->np.remote_mac, 0xff, ETH_ALEN);
-	INIT_WORK(&nt->cleanup_work, deferred_netpoll_cleanup);
-
-	/* Parse parameters and setup netpoll */
-	err = netpoll_parse_options(&nt->np, target_config);
-	if (err)
-		goto fail;
-
-	err = netpoll_setup(&nt->np);
-	if (err)
-		goto fail;
-
-	nt->np_state = NETPOLL_ENABLED;
-
-	return nt;
-
-fail:
-	kfree(nt);
-	return ERR_PTR(err);
-}
-
-/* Cleanup netpoll for given target (from boot/module param) and free it */
-static void free_param_target(struct netpoll_target *nt)
-{
-	cancel_work_sync(&nt->cleanup_work);
-	if (nt->np_state == NETPOLL_CLEANING || nt->np_state == NETPOLL_ENABLED)
-		netpoll_cleanup(&nt->np);
-	kfree(nt);
-}
-
-#ifdef	CONFIG_NETPOLL_TARGETS_DYNAMIC
-
-/*
- * Our subsystem hierarchy is:
- *
- * /sys/kernel/config/<subsys_name>/
- *				|
- *				<target>/
- *				|	enabled
- *				|	dev_name
- *				|	local_port
- *				|	remote_port
- *				|	local_ip
- *				|	remote_ip
- *				|	local_mac
- *				|	remote_mac
- *				|
- *				<target>/...
- */
-
-struct netpoll_target_attr {
-	struct configfs_attribute	attr;
-	ssize_t				(*show)(struct netpoll_target *nt,
-						char *buf);
-	ssize_t				(*store)(struct netpoll_target *nt,
-						 const char *buf,
-						 size_t count);
-};
-
-static struct netpoll_target *to_target(struct config_item *item)
-{
-	return item ?
-		container_of(item, struct netpoll_target, item) :
-		NULL;
-}
-
-/*
- * Wrapper over simple_strtol (base 10) with sanity and range checking.
- * We return (signed) long only because we may want to return errors.
- * Do not use this to convert numbers that are allowed to be negative.
- */
-static long strtol10_check_range(struct netpoll_targets *nts,
-				 const char *cp, long min, long max)
-{
-	long ret;
-	char *p = (char *) cp;
-
-	WARN_ON(min < 0);
-	WARN_ON(max < min);
-
-	ret = simple_strtol(p, &p, 10);
-
-	if (*p && (*p != '\n')) {
-		printk(KERN_ERR "%s: invalid input\n", nts->subsys_name);
-		return -EINVAL;
-	}
-	if ((ret < min) || (ret > max)) {
-		printk(KERN_ERR "%s: input %ld must be between %ld and %ld\n",
-		       nts->subsys_name, ret, min, max);
-		return -EINVAL;
-	}
-
-	return ret;
-}
-
-/*
- * Attribute operations for netpoll_target.
- */
-
-static ssize_t show_enabled(struct netpoll_target *nt, char *buf)
-{
-	return snprintf(buf, PAGE_SIZE, "%d\n",
-			nt->np_state == NETPOLL_ENABLED);
-}
-
-static ssize_t show_dev_name(struct netpoll_target *nt, char *buf)
-{
-	return snprintf(buf, PAGE_SIZE, "%s\n", nt->np.dev_name);
-}
-
-static ssize_t show_local_port(struct netpoll_target *nt, char *buf)
-{
-	return snprintf(buf, PAGE_SIZE, "%d\n", nt->np.local_port);
-}
-
-static ssize_t show_remote_port(struct netpoll_target *nt, char *buf)
-{
-	return snprintf(buf, PAGE_SIZE, "%d\n", nt->np.remote_port);
-}
-
-static ssize_t show_local_ip(struct netpoll_target *nt, char *buf)
-{
-	return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.local_ip);
-}
-
-static ssize_t show_remote_ip(struct netpoll_target *nt, char *buf)
-{
-	return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.remote_ip);
-}
-
-static ssize_t show_local_mac(struct netpoll_target *nt, char *buf)
-{
-	struct net_device *dev = nt->np.dev;
-	static const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-
-	return snprintf(buf, PAGE_SIZE, "%pM\n", dev ? dev->dev_addr : bcast);
-}
-
-static ssize_t show_remote_mac(struct netpoll_target *nt, char *buf)
-{
-	return snprintf(buf, PAGE_SIZE, "%pM\n", nt->np.remote_mac);
-}
-
-/*
- * This one is special -- targets created through the configfs interface
- * are not enabled (and the corresponding netpoll activated) by default.
- * The user is expected to set the desired parameters first (which
- * would enable him to dynamically add new netpoll targets for new
- * network interfaces as and when they come up).
- */
-static ssize_t store_enabled(struct netpoll_target *nt,
-			     const char *buf,
-			     size_t count)
-{
-	struct netpoll_targets *nts = nt->nts;
-	unsigned long flags;
-	int err;
-	long enabled;
-
-	enabled = strtol10_check_range(nts, buf, 0, 1);
-	if (enabled < 0)
-		return enabled;
-
-	if (enabled) {	/* 1 */
-		spin_lock_irqsave(&nts->lock, flags);
-		if (nt->np_state != NETPOLL_DISABLED)
-			goto busy;
-		else {
-			nt->np_state = NETPOLL_SETTINGUP;
-			/*
-			 * Nominally, we would grab an extra reference on the
-			 * config_item here for dynamic targets while we let go
-			 * of the lock, but this isn't required in this case
-			 * because there is a reference implicitly held by the
-			 * caller of the store operation.
-			 */
-			spin_unlock_irqrestore(&nts->lock, flags);
-		}
-
-		/*
-		 * Skip netpoll_parse_options() -- all the attributes are
-		 * already configured via configfs. Just print them out.
-		 */
-		netpoll_print_options(&nt->np);
-
-		err = netpoll_setup(&nt->np);
-		spin_lock_irqsave(&nts->lock, flags);
-		if (err)
-			nt->np_state = NETPOLL_DISABLED;
-		else
-			nt->np_state = NETPOLL_ENABLED;
-		spin_unlock_irqrestore(&nts->lock, flags);
-		if (err)
-			return err;
-
-		printk(KERN_INFO "%s: network logging started\n",
-		       nts->subsys_name);
-	} else {	/* 0 */
-		spin_lock_irqsave(&nts->lock, flags);
-		if (nt->np_state == NETPOLL_ENABLED)
-			nt->np_state = NETPOLL_CLEANING;
-		else if (nt->np_state != NETPOLL_DISABLED)
-			goto busy;
-		spin_unlock_irqrestore(&nts->lock, flags);
-
-		netpoll_cleanup(&nt->np);
-
-		spin_lock_irqsave(&nts->lock, flags);
-		nt->np_state = NETPOLL_DISABLED;
-		spin_unlock_irqrestore(&nts->lock, flags);
-	}
-
-	return strnlen(buf, count);
-busy:
-	spin_unlock_irqrestore(&nts->lock, flags);
-	return -EBUSY;
-}
-
-static ssize_t store_dev_name(struct netpoll_target *nt,
-			      const char *buf,
-			      size_t count)
-{
-	size_t len;
-
-	strlcpy(nt->np.dev_name, buf, IFNAMSIZ);
-
-	/* Get rid of possible trailing newline from echo(1) */
-	len = strnlen(nt->np.dev_name, IFNAMSIZ);
-	if (nt->np.dev_name[len - 1] == '\n')
-		nt->np.dev_name[len - 1] = '\0';
-
-	return strnlen(buf, count);
-}
-
-static ssize_t store_local_port(struct netpoll_target *nt,
-				const char *buf,
-				size_t count)
-{
-	long local_port;
-#define __U16_MAX	((__u16) ~0U)
-
-	local_port = strtol10_check_range(nt->nts, buf, 0, __U16_MAX);
-	if (local_port < 0)
-		return local_port;
-
-	nt->np.local_port = local_port;
-
-	return strnlen(buf, count);
-}
-
-static ssize_t store_remote_port(struct netpoll_target *nt,
-				 const char *buf,
-				 size_t count)
-{
-	long remote_port;
-#define __U16_MAX	((__u16) ~0U)
-
-	remote_port = strtol10_check_range(nt->nts, buf, 0, __U16_MAX);
-	if (remote_port < 0)
-		return remote_port;
-
-	nt->np.remote_port = remote_port;
-
-	return strnlen(buf, count);
-}
-
-static ssize_t store_local_ip(struct netpoll_target *nt,
-			      const char *buf,
-			      size_t count)
-{
-	nt->np.local_ip = in_aton(buf);
-
-	return strnlen(buf, count);
-}
-
-static ssize_t store_remote_ip(struct netpoll_target *nt,
-			       const char *buf,
-			       size_t count)
-{
-	nt->np.remote_ip = in_aton(buf);
-
-	return strnlen(buf, count);
-}
-
-static ssize_t store_remote_mac(struct netpoll_target *nt,
-				const char *buf,
-				size_t count)
-{
-	u8 remote_mac[ETH_ALEN];
-	char *p = (char *) buf;
-	int i;
-
-	for (i = 0; i < ETH_ALEN - 1; i++) {
-		remote_mac[i] = simple_strtoul(p, &p, 16);
-		if (*p != ':')
-			goto invalid;
-		p++;
-	}
-	remote_mac[ETH_ALEN - 1] = simple_strtoul(p, &p, 16);
-	if (*p && (*p != '\n'))
-		goto invalid;
-
-	memcpy(nt->np.remote_mac, remote_mac, ETH_ALEN);
-
-	return strnlen(buf, count);
-
-invalid:
-	printk(KERN_ERR "%s: invalid input\n", nt->nts->subsys_name);
-	return -EINVAL;
-}
-
-/*
- * Attribute definitions for netpoll_target.
- */
-
-#define __NETPOLL_TARGET_ATTR_RO(_name, _prefix_...)			\
-static struct netpoll_target_attr netpoll_target_##_name =	\
-	__CONFIGFS_ATTR(_name, S_IRUGO, show_##_prefix_##_name, NULL)
-
-#define __NETPOLL_TARGET_ATTR_RW(_name, _prefix_...)			\
-static struct netpoll_target_attr netpoll_target_##_name =	\
-	__CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR,			\
-			show_##_prefix_##_name, store_##_prefix_##_name)
-
-#define NETPOLL_WRAP_ATTR_STORE(_name)					\
-static ssize_t store_locked_##_name(struct netpoll_target *nt,		\
-				    const char *buf,			\
-				    size_t count)			\
-{									\
-	struct netpoll_targets *nts = nt->nts;				\
-	unsigned long flags;						\
-	ssize_t ret;							\
-	spin_lock_irqsave(&nts->lock, flags);				\
-	if (nt->np_state != NETPOLL_DISABLED) {				\
-		printk(KERN_ERR "%s: target (%s) is enabled, "		\
-				"disable to update parameters\n",	\
-				nts->subsys_name,			\
-				config_item_name(&nt->item));		\
-		spin_unlock_irqrestore(&nts->lock, flags);		\
-		return -EBUSY;						\
-	}								\
-	ret = store_##_name(nt, buf, count);				\
-	spin_unlock_irqrestore(&nts->lock, flags);			\
-	return ret;							\
-}
-
-#define NETPOLL_WRAP_ATTR_SHOW(_name)					\
-static ssize_t show_locked_##_name(struct netpoll_target *nt, char *buf) \
-{									\
-	struct netpoll_targets *nts = nt->nts;				\
-	unsigned long flags;						\
-	ssize_t ret;							\
-	spin_lock_irqsave(&nts->lock, flags);				\
-	ret = show_##_name(nt, buf);					\
-	spin_unlock_irqrestore(&nts->lock, flags);			\
-	return ret;							\
-}
-
-#define NETPOLL_TARGET_ATTR_RW(_name)				\
-		NETPOLL_WRAP_ATTR_STORE(_name)			\
-		NETPOLL_WRAP_ATTR_SHOW(_name)			\
-		__NETPOLL_TARGET_ATTR_RW(_name, locked_)
-
-#define NETPOLL_TARGET_ATTR_RO(_name)				\
-		NETPOLL_WRAP_ATTR_SHOW(_name)			\
-		__NETPOLL_TARGET_ATTR_RO(_name, locked_)
-
-__NETPOLL_TARGET_ATTR_RW(enabled);
-NETPOLL_TARGET_ATTR_RW(dev_name);
-NETPOLL_TARGET_ATTR_RW(local_port);
-NETPOLL_TARGET_ATTR_RW(remote_port);
-NETPOLL_TARGET_ATTR_RW(local_ip);
-NETPOLL_TARGET_ATTR_RW(remote_ip);
-NETPOLL_TARGET_ATTR_RO(local_mac);
-NETPOLL_TARGET_ATTR_RW(remote_mac);
-
-static struct configfs_attribute *netpoll_target_attrs[] = {
-	&netpoll_target_enabled.attr,
-	&netpoll_target_dev_name.attr,
-	&netpoll_target_local_port.attr,
-	&netpoll_target_remote_port.attr,
-	&netpoll_target_local_ip.attr,
-	&netpoll_target_remote_ip.attr,
-	&netpoll_target_local_mac.attr,
-	&netpoll_target_remote_mac.attr,
-	NULL,
-};
-
-/*
- * Item operations and type for netpoll_target.
- */
-
-static void netpoll_target_release(struct config_item *item)
-{
-	kfree(to_target(item));
-}
-
-static ssize_t netpoll_target_attr_show(struct config_item *item,
-					struct configfs_attribute *attr,
-					char *buf)
-{
-	ssize_t ret = -EINVAL;
-	struct netpoll_target *nt = to_target(item);
-	struct netpoll_target_attr *na =
-		container_of(attr, struct netpoll_target_attr, attr);
-
-	if (na->show)
-		ret = na->show(nt, buf);
-
-	return ret;
-}
-
-static ssize_t netpoll_target_attr_store(struct config_item *item,
-					 struct configfs_attribute *attr,
-					 const char *buf,
-					 size_t count)
-{
-	ssize_t ret = -EINVAL;
-	struct netpoll_target *nt = to_target(item);
-	struct netpoll_target_attr *na =
-		container_of(attr, struct netpoll_target_attr, attr);
-
-	if (na->store)
-		ret = na->store(nt, buf, count);
-
-	return ret;
-}
-
-static struct configfs_item_operations netpoll_target_item_ops = {
-	.release		= netpoll_target_release,
-	.show_attribute		= netpoll_target_attr_show,
-	.store_attribute	= netpoll_target_attr_store,
-};
-
-static struct config_item_type netpoll_target_type = {
-	.ct_attrs		= netpoll_target_attrs,
-	.ct_item_ops		= &netpoll_target_item_ops,
-	.ct_owner		= THIS_MODULE,
-};
-
-static struct netpoll_targets *group_to_targets(struct config_group *group)
-{
-	struct configfs_subsystem *subsys;
-	subsys = container_of(group, struct configfs_subsystem, su_group);
-	return container_of(subsys, struct netpoll_targets, configfs_subsys);
-}
-
-/*
- * Group operations and type for netpoll_target_subsys.
- */
-
-static struct config_item *make_netpoll_target(struct config_group *group,
-					       const char *name)
-{
-	struct netpoll_targets *nts = group_to_targets(group);
-	struct netpoll_target *nt;
-	unsigned long flags;
-
-	/*
-	 * Allocate and initialize with defaults.
-	 * Target is disabled at creation (enabled == 0).
-	 */
-	nt = kzalloc(sizeof(*nt), GFP_KERNEL);
-	if (!nt) {
-		printk(KERN_ERR "%s: failed to allocate memory\n",
-		       nts->subsys_name);
-		return ERR_PTR(-ENOMEM);
-	}
-
-	nt->nts = nts;
-	nt->np.name = nts->subsys_name;
-	strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ);
-	nt->np.local_port = nts->default_local_port;
-	nt->np.remote_port = nts->default_remote_port;
-	memset(nt->np.remote_mac, 0xff, ETH_ALEN);
-	INIT_WORK(&nt->cleanup_work, deferred_netpoll_cleanup);
-
-	/* Initialize the config_item member */
-	config_item_init_type_name(&nt->item, name, &netpoll_target_type);
-
-	/* Adding, but it is disabled */
-	spin_lock_irqsave(&nts->lock, flags);
-	list_add(&nt->list, &nts->list);
-	spin_unlock_irqrestore(&nts->lock, flags);
-
-	return &nt->item;
-}
-
-static void drop_netpoll_target(struct config_group *group,
-				struct config_item *item)
-{
-	struct netpoll_targets *nts = group_to_targets(group);
-	struct netpoll_target *nt = to_target(item);
-	unsigned long flags;
-
-	spin_lock_irqsave(&nts->lock, flags);
-	list_del(&nt->list);
-	spin_unlock_irqrestore(&nts->lock, flags);
-
-	/*
-	 * The target may have never been enabled, or was manually disabled
-	 * before being removed so netpoll may have already been cleaned up.
-	 *
-	 * If it queued for cleanup already, that is fine, as that path holds a
-	 * reference to the config_item.
-	 */
-	if (nt->np_state == NETPOLL_ENABLED)
-		netpoll_cleanup(&nt->np);
-
-	netpoll_target_put(nt);
-}
-
-static struct configfs_group_operations netpoll_subsys_group_ops = {
-	.make_item	= make_netpoll_target,
-	.drop_item	= drop_netpoll_target,
-};
-
-static struct config_item_type netpoll_subsys_type = {
-	.ct_group_ops	= &netpoll_subsys_group_ops,
-	.ct_owner	= THIS_MODULE,
-};
-
-static int __init dynamic_netpoll_targets_init(const char *subsys_name,
-					       struct netpoll_targets *nts)
-{
-	struct configfs_subsystem *subsys = &nts->configfs_subsys;
-
-	config_group_init(&subsys->su_group);
-	mutex_init(&subsys->su_mutex);
-	strncpy((char *)&subsys->su_group.cg_item.ci_namebuf, subsys_name,
-		CONFIGFS_ITEM_NAME_LEN);
-	subsys->su_group.cg_item.ci_type = &netpoll_subsys_type;
-	return configfs_register_subsystem(subsys);
-}
-
-static void __exit dynamic_netpoll_targets_exit(struct netpoll_targets *nts)
-{
-	configfs_unregister_subsystem(&nts->configfs_subsys);
-}
-
-/*
- * Targets that were created by parsing the boot/module option string
- * do not exist in the configfs hierarchy (and have NULL names) and will
- * never go away, so make these a no-op for them.
- */
-static void netpoll_target_get(struct netpoll_target *nt)
-{
-	if (config_item_name(&nt->item))
-		config_item_get(&nt->item);
-}
-
-static void netpoll_target_put(struct netpoll_target *nt)
-{
-	if (config_item_name(&nt->item))
-		config_item_put(&nt->item);
-}
-
-#else	/* !CONFIG_NETPOLL_TARGETS_DYNAMIC */
-
-static int __init dynamic_netpoll_targets_init(const char *subsys_name,
-					       struct netpoll_targets *nts)
-{
-	return 0;
-}
-
-static void __exit dynamic_netpoll_targets_exit(struct netpoll_targets *nts)
-{
-}
-
-/*
- * No danger of targets going away from under us when dynamic
- * reconfigurability is off.
- */
-static void netpoll_target_get(struct netpoll_target *nt)
-{
-}
-
-static void netpoll_target_put(struct netpoll_target *nt)
-{
-}
-
-#endif	/* CONFIG_NETPOLL_TARGETS_DYNAMIC */
-
-/*
- * Call netpoll_cleanup on this target asynchronously.
- * nts->lock is required.
- */
-static void defer_netpoll_cleanup(struct netpoll_target *nt)
-{
-	if (nt->np_state != NETPOLL_ENABLED)
-		return;
-	netpoll_target_get(nt);
-	nt->np_state = NETPOLL_CLEANING;
-	schedule_work(&nt->cleanup_work);
-}
-
-/* Handle network interface device notifications */
-static int netpoll_targets_netdev_event(struct notifier_block *this,
-					unsigned long event,
-					void *ptr)
-{
-	struct netpoll_targets *nts = container_of(this, struct netpoll_targets,
-						   netdev_notifier);
-	unsigned long flags;
-	struct netpoll_target *nt;
-	struct net_device *dev = ptr;
-
-	if (!(event == NETDEV_CHANGENAME || event == NETDEV_UNREGISTER ||
-	      event == NETDEV_BONDING_DESLAVE))
-		goto done;
-
-	spin_lock_irqsave(&nts->lock, flags);
-	list_for_each_entry(nt, &nts->list, list) {
-		if (nt->np_state == NETPOLL_ENABLED && nt->np.dev == dev) {
-			switch (event) {
-			case NETDEV_CHANGENAME:
-				strlcpy(nt->np.dev_name, dev->name, IFNAMSIZ);
-				break;
-			case NETDEV_BONDING_DESLAVE:
-			case NETDEV_UNREGISTER:
-				/*
-				 * We can't cleanup netpoll in atomic context.
-				 * Kick it off as deferred work.
-				 */
-				defer_netpoll_cleanup(nt);
-			}
-		}
-	}
-	spin_unlock_irqrestore(&nts->lock, flags);
-	if (event == NETDEV_UNREGISTER || event == NETDEV_BONDING_DESLAVE)
-		printk(KERN_INFO "%s: network logging stopped, "
-			"interface %s %s\n", nts->subsys_name, dev->name,
-			event == NETDEV_UNREGISTER ? "unregistered" : "released slaves");
-
-done:
-	return NOTIFY_DONE;
-}
-
 static void write_msg(struct console *con, const char *msg, unsigned int len)
 {
 	int frag, left;
@@ -841,86 +104,6 @@ static struct console netconsole = {
 	.write	= write_msg,
 };
 
-static int __init register_netpoll_targets(const char *subsys_name,
-					   struct netpoll_targets *nts,
-					   char *static_targets)
-{
-	int err;
-	struct netpoll_target *nt, *tmp;
-	char *target_config;
-	char *input = static_targets;
-	unsigned long flags;
-
-	if (strnlen(input, MAX_PARAM_LENGTH)) {
-		while ((target_config = strsep(&input, ";"))) {
-			nt = alloc_param_target(nts, target_config);
-			if (IS_ERR(nt)) {
-				err = PTR_ERR(nt);
-				goto fail;
-			}
-
-			spin_lock_irqsave(&nts->lock, flags);
-			list_add(&nt->list, &nts->list);
-			spin_unlock_irqrestore(&nts->lock, flags);
-		}
-	}
-
-	nts->netdev_notifier.notifier_call = netpoll_targets_netdev_event;
-	err = register_netdevice_notifier(&nts->netdev_notifier);
-	if (err)
-		goto fail;
-
-	nts->subsys_name = kstrdup(subsys_name, GFP_KERNEL);
-	err = -ENOMEM;
-	if (!nts->subsys_name)
-		goto undonotifier;
-
-	err = dynamic_netpoll_targets_init(subsys_name, nts);
-	if (err)
-		goto free_subsys_name;
-
-	return 0;
-
-free_subsys_name:
-	kfree(nts->subsys_name);
-undonotifier:
-	unregister_netdevice_notifier(&nts->netdev_notifier);
-fail:
-	/*
-	 * Remove all targets and destroy them (only targets created
-	 * from the boot/module option exist here). Skipping the list
-	 * lock is safe here, and netpoll_cleanup() will sleep.
-	 */
-	list_for_each_entry_safe(nt, tmp, &nts->list, list) {
-		list_del(&nt->list);
-		free_param_target(nt);
-	}
-
-	return err;
-}
-
-static void __exit unregister_netpoll_targets(struct netpoll_targets *nts)
-{
-	struct netpoll_target *nt, *tmp;
-
-	dynamic_netpoll_targets_exit(nts);
-	unregister_netdevice_notifier(&nts->netdev_notifier);
-
-	/*
-	 * Targets created via configfs pin references on our module
-	 * and would first be rmdir(2)'ed from userspace. We reach
-	 * here only when they are already destroyed, and only those
-	 * created from the boot/module option are left, so remove and
-	 * destroy them. Skipping the list lock is safe here, and
-	 * netpoll_cleanup() will sleep.
-	 */
-	list_for_each_entry_safe(nt, tmp, &nts->list, list) {
-		list_del(&nt->list);
-		free_param_target(nt);
-	}
-	kfree(nts->subsys_name);
-}
-
 static int __init init_netconsole(void)
 {
 	int err;
@@ -928,6 +111,7 @@ static int __init init_netconsole(void)
 	targets.default_local_port = 6665;
 	targets.default_remote_port = 6666;
 
+	config[MAX_PARAM_LENGTH - 1] = '\0';
 	err = register_netpoll_targets("netconsole", &targets, config);
 	if (err)
 		return err;
diff --git a/include/linux/netpoll_targets.h b/include/linux/netpoll_targets.h
new file mode 100644
index 0000000..d08dde0
--- /dev/null
+++ b/include/linux/netpoll_targets.h
@@ -0,0 +1,76 @@
+#ifndef _LINUX_NETPOLL_TARGETS_H
+#define _LINUX_NETPOLL_TARGETS_H
+
+#include <linux/netpoll.h>
+
+#include <linux/configfs.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+struct netpoll_targets {
+	struct list_head list;
+	spinlock_t lock;
+	u16 default_local_port, default_remote_port;
+#ifdef	CONFIG_NETPOLL_TARGETS_DYNAMIC
+	struct configfs_subsystem configfs_subsys;
+#endif
+	struct notifier_block netdev_notifier;
+	char *subsys_name;
+};
+#define DEFINE_NETPOLL_TARGETS(x) struct netpoll_targets x = \
+	{ .list = LIST_HEAD_INIT(x.list), \
+	  .lock = __SPIN_LOCK_UNLOCKED(x.lock) }
+
+#define NETPOLL_DISABLED	0
+#define NETPOLL_SETTINGUP	1
+#define NETPOLL_ENABLED		2
+#define NETPOLL_CLEANING	3
+
+/**
+ * struct netpoll_target - Represents a configured netpoll target.
+ * @list:	Links this target into the netpoll_targets.list.
+ * @item:	Links us into the configfs subsystem hierarchy.
+ * @np_state:	Enabled / Disabled / SettingUp / Cleaning
+ *		Visible from userspace (read-write) as "enabled".
+ *		We maintain a state machine here of the valid states.  Either a
+ *		target is enabled or disabled, but it may also be in a
+ *		transitional state whereby nobody is allowed to act on the
+ *		target other than whoever owns the transition.
+ *
+ *		Also, other parameters of a target may be modified at
+ *		runtime only when it is disabled (np_state == NETPOLL_ENABLED).
+ * @np:		The netpoll structure for this target.
+ *		Contains the other userspace visible parameters:
+ *		dev_name	(read-write)
+ *		local_port	(read-write)
+ *		remote_port	(read-write)
+ *		local_ip	(read-write)
+ *		remote_ip	(read-write)
+ *		local_mac	(read-only)
+ *		remote_mac	(read-write)
+ */
+struct netpoll_target {
+	struct netpoll_targets *nts;
+	struct list_head	list;
+#ifdef	CONFIG_NETPOLL_TARGETS_DYNAMIC
+	struct config_item	item;
+#endif
+	int			np_state;
+	struct netpoll		np;
+	struct work_struct	cleanup_work;
+};
+
+#ifdef	CONFIG_NETPOLL_TARGETS_DYNAMIC
+void netpoll_target_get(struct netpoll_target *nt);
+void netpoll_target_put(struct netpoll_target *nt);
+#else
+static void netpoll_target_get(struct netpoll_target *nt) {}
+static void netpoll_target_put(struct netpoll_target *nt) {}
+#endif
+
+int register_netpoll_targets(const char *subsys_name,
+			     struct netpoll_targets *nts,
+			     char *static_targets);
+void unregister_netpoll_targets(struct netpoll_targets *nts);
+
+#endif /* _LINUX_NETPOLL_TARGETS_H */
diff --git a/net/core/Makefile b/net/core/Makefile
index 8a04dd2..262217c 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_XFRM) += flow.o
 obj-y += net-sysfs.o
 obj-$(CONFIG_NET_PKTGEN) += pktgen.o
 obj-$(CONFIG_NETPOLL) += netpoll.o
+obj-$(CONFIG_NETPOLL_TARGETS) += netpoll_targets.o
 obj-$(CONFIG_NET_DMA) += user_dma.o
 obj-$(CONFIG_FIB_RULES) += fib_rules.o
 obj-$(CONFIG_TRACEPOINTS) += net-traces.o
diff --git a/net/core/netpoll_targets.c b/net/core/netpoll_targets.c
new file mode 100644
index 0000000..6817389
--- /dev/null
+++ b/net/core/netpoll_targets.c
@@ -0,0 +1,746 @@
+#include <linux/netpoll_targets.h>
+
+static void deferred_netpoll_cleanup(struct work_struct *work)
+{
+	struct netpoll_target *nt;
+	struct netpoll_targets *nts;
+	unsigned long flags;
+
+	nt = container_of(work, struct netpoll_target, cleanup_work);
+	nts = nt->nts;
+
+	netpoll_cleanup(&nt->np);
+
+	spin_lock_irqsave(&nts->lock, flags);
+	BUG_ON(nt->np_state != NETPOLL_CLEANING);
+	nt->np_state = NETPOLL_DISABLED;
+	spin_unlock_irqrestore(&nts->lock, flags);
+
+	netpoll_target_put(nt);
+}
+
+/* Allocate new target (from boot/module param) and setup netpoll for it */
+static struct netpoll_target *alloc_param_target(struct netpoll_targets *nts,
+						 char *target_config)
+{
+	int err = -ENOMEM;
+	struct netpoll_target *nt;
+
+	/*
+	 * Allocate and initialize with defaults.
+	 * Note that these targets get their config_item fields zeroed-out.
+	 */
+	nt = kzalloc(sizeof(*nt), GFP_KERNEL);
+	if (!nt) {
+		printk(KERN_ERR "%s: failed to allocate memory\n",
+		       nts->subsys_name);
+		goto fail;
+	}
+
+	nt->nts = nts;
+	nt->np.name = nts->subsys_name;
+	strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ);
+	nt->np.local_port = nts->default_local_port;
+	nt->np.remote_port = nts->default_remote_port;
+	memset(nt->np.remote_mac, 0xff, ETH_ALEN);
+	INIT_WORK(&nt->cleanup_work, deferred_netpoll_cleanup);
+
+	/* Parse parameters and setup netpoll */
+	err = netpoll_parse_options(&nt->np, target_config);
+	if (err)
+		goto fail;
+
+	err = netpoll_setup(&nt->np);
+	if (err)
+		goto fail;
+
+	nt->np_state = NETPOLL_ENABLED;
+
+	return nt;
+
+fail:
+	kfree(nt);
+	return ERR_PTR(err);
+}
+
+/* Cleanup netpoll for given target (from boot/module param) and free it */
+static void free_param_target(struct netpoll_target *nt)
+{
+	cancel_work_sync(&nt->cleanup_work);
+	if (nt->np_state == NETPOLL_CLEANING || nt->np_state == NETPOLL_ENABLED)
+		netpoll_cleanup(&nt->np);
+	kfree(nt);
+}
+
+#ifdef	CONFIG_NETPOLL_TARGETS_DYNAMIC
+
+/*
+ * Our subsystem hierarchy is:
+ *
+ * /sys/kernel/config/<subsys_name>/
+ *				|
+ *				<target>/
+ *				|	enabled
+ *				|	dev_name
+ *				|	local_port
+ *				|	remote_port
+ *				|	local_ip
+ *				|	remote_ip
+ *				|	local_mac
+ *				|	remote_mac
+ *				|
+ *				<target>/...
+ */
+
+struct netpoll_target_attr {
+	struct configfs_attribute	attr;
+	ssize_t				(*show)(struct netpoll_target *nt,
+						char *buf);
+	ssize_t				(*store)(struct netpoll_target *nt,
+						 const char *buf,
+						 size_t count);
+};
+
+static struct netpoll_target *to_target(struct config_item *item)
+{
+	return item ?
+		container_of(item, struct netpoll_target, item) :
+		NULL;
+}
+
+/*
+ * Wrapper over simple_strtol (base 10) with sanity and range checking.
+ * We return (signed) long only because we may want to return errors.
+ * Do not use this to convert numbers that are allowed to be negative.
+ */
+static long strtol10_check_range(struct netpoll_targets *nts,
+				 const char *cp, long min, long max)
+{
+	long ret;
+	char *p = (char *) cp;
+
+	WARN_ON(min < 0);
+	WARN_ON(max < min);
+
+	ret = simple_strtol(p, &p, 10);
+
+	if (*p && (*p != '\n')) {
+		printk(KERN_ERR "%s: invalid input\n", nts->subsys_name);
+		return -EINVAL;
+	}
+	if ((ret < min) || (ret > max)) {
+		printk(KERN_ERR "%s: input %ld must be between %ld and %ld\n",
+		       nts->subsys_name, ret, min, max);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+/*
+ * Attribute operations for netpoll_target.
+ */
+
+static ssize_t show_enabled(struct netpoll_target *nt, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			nt->np_state == NETPOLL_ENABLED);
+}
+
+static ssize_t show_dev_name(struct netpoll_target *nt, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", nt->np.dev_name);
+}
+
+static ssize_t show_local_port(struct netpoll_target *nt, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", nt->np.local_port);
+}
+
+static ssize_t show_remote_port(struct netpoll_target *nt, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", nt->np.remote_port);
+}
+
+static ssize_t show_local_ip(struct netpoll_target *nt, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.local_ip);
+}
+
+static ssize_t show_remote_ip(struct netpoll_target *nt, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.remote_ip);
+}
+
+static ssize_t show_local_mac(struct netpoll_target *nt, char *buf)
+{
+	struct net_device *dev = nt->np.dev;
+	static const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+	return snprintf(buf, PAGE_SIZE, "%pM\n", dev ? dev->dev_addr : bcast);
+}
+
+static ssize_t show_remote_mac(struct netpoll_target *nt, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%pM\n", nt->np.remote_mac);
+}
+
+/*
+ * This one is special -- targets created through the configfs interface
+ * are not enabled (and the corresponding netpoll activated) by default.
+ * The user is expected to set the desired parameters first (which
+ * would enable him to dynamically add new netpoll targets for new
+ * network interfaces as and when they come up).
+ */
+static ssize_t store_enabled(struct netpoll_target *nt,
+			     const char *buf,
+			     size_t count)
+{
+	struct netpoll_targets *nts = nt->nts;
+	unsigned long flags;
+	int err;
+	long enabled;
+
+	enabled = strtol10_check_range(nts, buf, 0, 1);
+	if (enabled < 0)
+		return enabled;
+
+	if (enabled) {	/* 1 */
+		spin_lock_irqsave(&nts->lock, flags);
+		if (nt->np_state != NETPOLL_DISABLED)
+			goto busy;
+		else {
+			nt->np_state = NETPOLL_SETTINGUP;
+			/*
+			 * Nominally, we would grab an extra reference on the
+			 * config_item here for dynamic targets while we let go
+			 * of the lock, but this isn't required in this case
+			 * because there is a reference implicitly held by the
+			 * caller of the store operation.
+			 */
+			spin_unlock_irqrestore(&nts->lock, flags);
+		}
+
+		/*
+		 * Skip netpoll_parse_options() -- all the attributes are
+		 * already configured via configfs. Just print them out.
+		 */
+		netpoll_print_options(&nt->np);
+
+		err = netpoll_setup(&nt->np);
+		spin_lock_irqsave(&nts->lock, flags);
+		if (err)
+			nt->np_state = NETPOLL_DISABLED;
+		else
+			nt->np_state = NETPOLL_ENABLED;
+		spin_unlock_irqrestore(&nts->lock, flags);
+		if (err)
+			return err;
+
+		printk(KERN_INFO "%s: network logging started\n",
+		       nts->subsys_name);
+	} else {	/* 0 */
+		spin_lock_irqsave(&nts->lock, flags);
+		if (nt->np_state == NETPOLL_ENABLED)
+			nt->np_state = NETPOLL_CLEANING;
+		else if (nt->np_state != NETPOLL_DISABLED)
+			goto busy;
+		spin_unlock_irqrestore(&nts->lock, flags);
+
+		netpoll_cleanup(&nt->np);
+
+		spin_lock_irqsave(&nts->lock, flags);
+		nt->np_state = NETPOLL_DISABLED;
+		spin_unlock_irqrestore(&nts->lock, flags);
+	}
+
+	return strnlen(buf, count);
+busy:
+	spin_unlock_irqrestore(&nts->lock, flags);
+	return -EBUSY;
+}
+
+static ssize_t store_dev_name(struct netpoll_target *nt,
+			      const char *buf,
+			      size_t count)
+{
+	size_t len;
+
+	strlcpy(nt->np.dev_name, buf, IFNAMSIZ);
+
+	/* Get rid of possible trailing newline from echo(1) */
+	len = strnlen(nt->np.dev_name, IFNAMSIZ);
+	if (nt->np.dev_name[len - 1] == '\n')
+		nt->np.dev_name[len - 1] = '\0';
+
+	return strnlen(buf, count);
+}
+
+static ssize_t store_local_port(struct netpoll_target *nt,
+				const char *buf,
+				size_t count)
+{
+	long local_port;
+#define __U16_MAX	((__u16) ~0U)
+
+	local_port = strtol10_check_range(nt->nts, buf, 0, __U16_MAX);
+	if (local_port < 0)
+		return local_port;
+
+	nt->np.local_port = local_port;
+
+	return strnlen(buf, count);
+}
+
+static ssize_t store_remote_port(struct netpoll_target *nt,
+				 const char *buf,
+				 size_t count)
+{
+	long remote_port;
+#define __U16_MAX	((__u16) ~0U)
+
+	remote_port = strtol10_check_range(nt->nts, buf, 0, __U16_MAX);
+	if (remote_port < 0)
+		return remote_port;
+
+	nt->np.remote_port = remote_port;
+
+	return strnlen(buf, count);
+}
+
+static ssize_t store_local_ip(struct netpoll_target *nt,
+			      const char *buf,
+			      size_t count)
+{
+	nt->np.local_ip = in_aton(buf);
+
+	return strnlen(buf, count);
+}
+
+static ssize_t store_remote_ip(struct netpoll_target *nt,
+			       const char *buf,
+			       size_t count)
+{
+	nt->np.remote_ip = in_aton(buf);
+
+	return strnlen(buf, count);
+}
+
+static ssize_t store_remote_mac(struct netpoll_target *nt,
+				const char *buf,
+				size_t count)
+{
+	u8 remote_mac[ETH_ALEN];
+	char *p = (char *) buf;
+	int i;
+
+	for (i = 0; i < ETH_ALEN - 1; i++) {
+		remote_mac[i] = simple_strtoul(p, &p, 16);
+		if (*p != ':')
+			goto invalid;
+		p++;
+	}
+	remote_mac[ETH_ALEN - 1] = simple_strtoul(p, &p, 16);
+	if (*p && (*p != '\n'))
+		goto invalid;
+
+	memcpy(nt->np.remote_mac, remote_mac, ETH_ALEN);
+
+	return strnlen(buf, count);
+
+invalid:
+	printk(KERN_ERR "%s: invalid input\n", nt->nts->subsys_name);
+	return -EINVAL;
+}
+
+/*
+ * Attribute definitions for netpoll_target.
+ */
+
+#define __NETPOLL_TARGET_ATTR_RO(_name, _prefix_...)			\
+static struct netpoll_target_attr netpoll_target_##_name =	\
+	__CONFIGFS_ATTR(_name, S_IRUGO, show_##_prefix_##_name, NULL)
+
+#define __NETPOLL_TARGET_ATTR_RW(_name, _prefix_...)			\
+static struct netpoll_target_attr netpoll_target_##_name =	\
+	__CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR,			\
+			show_##_prefix_##_name, store_##_prefix_##_name)
+
+#define NETPOLL_WRAP_ATTR_STORE(_name)					\
+static ssize_t store_locked_##_name(struct netpoll_target *nt,		\
+				    const char *buf,			\
+				    size_t count)			\
+{									\
+	struct netpoll_targets *nts = nt->nts;				\
+	unsigned long flags;						\
+	ssize_t ret;							\
+	spin_lock_irqsave(&nts->lock, flags);				\
+	if (nt->np_state != NETPOLL_DISABLED) {				\
+		printk(KERN_ERR "%s: target (%s) is enabled, "		\
+				"disable to update parameters\n",	\
+				nts->subsys_name,			\
+				config_item_name(&nt->item));		\
+		spin_unlock_irqrestore(&nts->lock, flags);		\
+		return -EBUSY;						\
+	}								\
+	ret = store_##_name(nt, buf, count);				\
+	spin_unlock_irqrestore(&nts->lock, flags);			\
+	return ret;							\
+}
+
+#define NETPOLL_WRAP_ATTR_SHOW(_name)					\
+static ssize_t show_locked_##_name(struct netpoll_target *nt, char *buf) \
+{									\
+	struct netpoll_targets *nts = nt->nts;				\
+	unsigned long flags;						\
+	ssize_t ret;							\
+	spin_lock_irqsave(&nts->lock, flags);				\
+	ret = show_##_name(nt, buf);					\
+	spin_unlock_irqrestore(&nts->lock, flags);			\
+	return ret;							\
+}
+
+#define NETPOLL_TARGET_ATTR_RW(_name)				\
+		NETPOLL_WRAP_ATTR_STORE(_name)			\
+		NETPOLL_WRAP_ATTR_SHOW(_name)			\
+		__NETPOLL_TARGET_ATTR_RW(_name, locked_)
+
+#define NETPOLL_TARGET_ATTR_RO(_name)				\
+		NETPOLL_WRAP_ATTR_SHOW(_name)			\
+		__NETPOLL_TARGET_ATTR_RO(_name, locked_)
+
+__NETPOLL_TARGET_ATTR_RW(enabled);
+NETPOLL_TARGET_ATTR_RW(dev_name);
+NETPOLL_TARGET_ATTR_RW(local_port);
+NETPOLL_TARGET_ATTR_RW(remote_port);
+NETPOLL_TARGET_ATTR_RW(local_ip);
+NETPOLL_TARGET_ATTR_RW(remote_ip);
+NETPOLL_TARGET_ATTR_RO(local_mac);
+NETPOLL_TARGET_ATTR_RW(remote_mac);
+
+static struct configfs_attribute *netpoll_target_attrs[] = {
+	&netpoll_target_enabled.attr,
+	&netpoll_target_dev_name.attr,
+	&netpoll_target_local_port.attr,
+	&netpoll_target_remote_port.attr,
+	&netpoll_target_local_ip.attr,
+	&netpoll_target_remote_ip.attr,
+	&netpoll_target_local_mac.attr,
+	&netpoll_target_remote_mac.attr,
+	NULL,
+};
+
+/*
+ * Item operations and type for netpoll_target.
+ */
+
+static void netpoll_target_release(struct config_item *item)
+{
+	kfree(to_target(item));
+}
+
+static ssize_t netpoll_target_attr_show(struct config_item *item,
+					struct configfs_attribute *attr,
+					char *buf)
+{
+	ssize_t ret = -EINVAL;
+	struct netpoll_target *nt = to_target(item);
+	struct netpoll_target_attr *na =
+		container_of(attr, struct netpoll_target_attr, attr);
+
+	if (na->show)
+		ret = na->show(nt, buf);
+
+	return ret;
+}
+
+static ssize_t netpoll_target_attr_store(struct config_item *item,
+					 struct configfs_attribute *attr,
+					 const char *buf,
+					 size_t count)
+{
+	ssize_t ret = -EINVAL;
+	struct netpoll_target *nt = to_target(item);
+	struct netpoll_target_attr *na =
+		container_of(attr, struct netpoll_target_attr, attr);
+
+	if (na->store)
+		ret = na->store(nt, buf, count);
+
+	return ret;
+}
+
+static struct configfs_item_operations netpoll_target_item_ops = {
+	.release		= netpoll_target_release,
+	.show_attribute		= netpoll_target_attr_show,
+	.store_attribute	= netpoll_target_attr_store,
+};
+
+static struct config_item_type netpoll_target_type = {
+	.ct_attrs		= netpoll_target_attrs,
+	.ct_item_ops		= &netpoll_target_item_ops,
+	.ct_owner		= THIS_MODULE,
+};
+
+static struct netpoll_targets *group_to_targets(struct config_group *group)
+{
+	struct configfs_subsystem *subsys;
+	subsys = container_of(group, struct configfs_subsystem, su_group);
+	return container_of(subsys, struct netpoll_targets, configfs_subsys);
+}
+
+/*
+ * Group operations and type for netpoll_target_subsys.
+ */
+
+static struct config_item *make_netpoll_target(struct config_group *group,
+					       const char *name)
+{
+	struct netpoll_targets *nts = group_to_targets(group);
+	struct netpoll_target *nt;
+	unsigned long flags;
+
+	/*
+	 * Allocate and initialize with defaults.
+	 * Target is disabled at creation (enabled == 0).
+	 */
+	nt = kzalloc(sizeof(*nt), GFP_KERNEL);
+	if (!nt) {
+		printk(KERN_ERR "%s: failed to allocate memory\n",
+		       nts->subsys_name);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	nt->nts = nts;
+	nt->np.name = nts->subsys_name;
+	strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ);
+	nt->np.local_port = nts->default_local_port;
+	nt->np.remote_port = nts->default_remote_port;
+	memset(nt->np.remote_mac, 0xff, ETH_ALEN);
+	INIT_WORK(&nt->cleanup_work, deferred_netpoll_cleanup);
+
+	/* Initialize the config_item member */
+	config_item_init_type_name(&nt->item, name, &netpoll_target_type);
+
+	/* Adding, but it is disabled */
+	spin_lock_irqsave(&nts->lock, flags);
+	list_add(&nt->list, &nts->list);
+	spin_unlock_irqrestore(&nts->lock, flags);
+
+	return &nt->item;
+}
+
+static void drop_netpoll_target(struct config_group *group,
+				struct config_item *item)
+{
+	struct netpoll_targets *nts = group_to_targets(group);
+	struct netpoll_target *nt = to_target(item);
+	unsigned long flags;
+
+	spin_lock_irqsave(&nts->lock, flags);
+	list_del(&nt->list);
+	spin_unlock_irqrestore(&nts->lock, flags);
+
+	/*
+	 * The target may have never been enabled, or was manually disabled
+	 * before being removed so netpoll may have already been cleaned up.
+	 *
+	 * If it queued for cleanup already, that is fine, as that path holds a
+	 * reference to the config_item.
+	 */
+	if (nt->np_state == NETPOLL_ENABLED)
+		netpoll_cleanup(&nt->np);
+
+	netpoll_target_put(nt);
+}
+
+static struct configfs_group_operations netpoll_subsys_group_ops = {
+	.make_item	= make_netpoll_target,
+	.drop_item	= drop_netpoll_target,
+};
+
+static struct config_item_type netpoll_subsys_type = {
+	.ct_group_ops	= &netpoll_subsys_group_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+static int dynamic_netpoll_targets_init(const char *subsys_name,
+					struct netpoll_targets *nts)
+{
+	struct configfs_subsystem *subsys = &nts->configfs_subsys;
+
+	config_group_init(&subsys->su_group);
+	mutex_init(&subsys->su_mutex);
+	strncpy((char *)&subsys->su_group.cg_item.ci_namebuf, subsys_name,
+		CONFIGFS_ITEM_NAME_LEN);
+	subsys->su_group.cg_item.ci_type = &netpoll_subsys_type;
+	return configfs_register_subsystem(subsys);
+}
+
+static void dynamic_netpoll_targets_exit(struct netpoll_targets *nts)
+{
+	configfs_unregister_subsystem(&nts->configfs_subsys);
+}
+
+/*
+ * Targets that were created by parsing the boot/module option string
+ * do not exist in the configfs hierarchy (and have NULL names) and will
+ * never go away, so make these a no-op for them.
+ */
+void netpoll_target_get(struct netpoll_target *nt)
+{
+	if (config_item_name(&nt->item))
+		config_item_get(&nt->item);
+}
+EXPORT_SYMBOL_GPL(netpoll_target_get);
+
+void netpoll_target_put(struct netpoll_target *nt)
+{
+	if (config_item_name(&nt->item))
+		config_item_put(&nt->item);
+}
+EXPORT_SYMBOL_GPL(netpoll_target_put);
+
+#else	/* CONFIG_NETPOLL_TARGETS_DYNAMIC */
+static int dynamic_netpoll_targets_init(const char *subsys_name,
+					struct netpoll_targets *nts) {}
+static int dynamic_netpoll_targets_exit(struct netpoll_targets *nts) {}
+#endif	/* CONFIG_NETPOLL_TARGETS_DYNAMIC */
+
+/*
+ * Call netpoll_cleanup on this target asynchronously.
+ * nts->lock is required.
+ */
+static void defer_netpoll_cleanup(struct netpoll_target *nt)
+{
+	if (nt->np_state != NETPOLL_ENABLED)
+		return;
+	netpoll_target_get(nt);
+	nt->np_state = NETPOLL_CLEANING;
+	schedule_work(&nt->cleanup_work);
+}
+
+/* Handle network interface device notifications */
+static int netpoll_targets_netdev_event(struct notifier_block *this,
+					unsigned long event,
+					void *ptr)
+{
+	struct netpoll_targets *nts = container_of(this, struct netpoll_targets,
+						   netdev_notifier);
+	unsigned long flags;
+	struct netpoll_target *nt;
+	struct net_device *dev = ptr;
+
+	if (!(event == NETDEV_CHANGENAME || event == NETDEV_UNREGISTER ||
+	      event == NETDEV_BONDING_DESLAVE))
+		goto done;
+
+	spin_lock_irqsave(&nts->lock, flags);
+	list_for_each_entry(nt, &nts->list, list) {
+		if (nt->np_state == NETPOLL_ENABLED && nt->np.dev == dev) {
+			switch (event) {
+			case NETDEV_CHANGENAME:
+				strlcpy(nt->np.dev_name, dev->name, IFNAMSIZ);
+				break;
+			case NETDEV_BONDING_DESLAVE:
+			case NETDEV_UNREGISTER:
+				/*
+				 * We can't cleanup netpoll in atomic context.
+				 * Kick it off as deferred work.
+				 */
+				defer_netpoll_cleanup(nt);
+			}
+		}
+	}
+	spin_unlock_irqrestore(&nts->lock, flags);
+	if (event == NETDEV_UNREGISTER || event == NETDEV_BONDING_DESLAVE)
+		printk(KERN_INFO "%s: network logging stopped, "
+			"interface %s %s\n", nts->subsys_name, dev->name,
+			event == NETDEV_UNREGISTER ? "unregistered" : "released slaves");
+
+done:
+	return NOTIFY_DONE;
+}
+
+int register_netpoll_targets(const char *subsys_name,
+			     struct netpoll_targets *nts,
+			     char *static_targets)
+{
+	int err;
+	struct netpoll_target *nt, *tmp;
+	char *target_config;
+	char *input = static_targets;
+	unsigned long flags;
+
+	if (strlen(input)) {
+		while ((target_config = strsep(&input, ";"))) {
+			nt = alloc_param_target(nts, target_config);
+			if (IS_ERR(nt)) {
+				err = PTR_ERR(nt);
+				goto fail;
+			}
+
+			spin_lock_irqsave(&nts->lock, flags);
+			list_add(&nt->list, &nts->list);
+			spin_unlock_irqrestore(&nts->lock, flags);
+		}
+	}
+
+	nts->netdev_notifier.notifier_call = netpoll_targets_netdev_event;
+	err = register_netdevice_notifier(&nts->netdev_notifier);
+	if (err)
+		goto fail;
+
+	nts->subsys_name = kstrdup(subsys_name, GFP_KERNEL);
+	err = -ENOMEM;
+	if (!nts->subsys_name)
+		goto undonotifier;
+
+	err = dynamic_netpoll_targets_init(subsys_name, nts);
+	if (err)
+		goto free_subsys_name;
+
+	return 0;
+
+free_subsys_name:
+	kfree(nts->subsys_name);
+undonotifier:
+	unregister_netdevice_notifier(&nts->netdev_notifier);
+fail:
+	/*
+	 * Remove all targets and destroy them (only targets created
+	 * from the boot/module option exist here). Skipping the list
+	 * lock is safe here, and netpoll_cleanup() will sleep.
+	 */
+	list_for_each_entry_safe(nt, tmp, &nts->list, list) {
+		list_del(&nt->list);
+		free_param_target(nt);
+	}
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(register_netpoll_targets);
+
+void unregister_netpoll_targets(struct netpoll_targets *nts)
+{
+	struct netpoll_target *nt, *tmp;
+
+	dynamic_netpoll_targets_exit(nts);
+	unregister_netdevice_notifier(&nts->netdev_notifier);
+
+	/*
+	 * Targets created via configfs pin references on our module
+	 * and would first be rmdir(2)'ed from userspace. We reach
+	 * here only when they are already destroyed, and only those
+	 * created from the boot/module option are left, so remove and
+	 * destroy them. Skipping the list lock is safe here, and
+	 * netpoll_cleanup() will sleep.
+	 */
+	list_for_each_entry_safe(nt, tmp, &nts->list, list) {
+		list_del(&nt->list);
+		free_param_target(nt);
+	}
+	kfree(nts->subsys_name);
+}
+EXPORT_SYMBOL_GPL(unregister_netpoll_targets);
+

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ