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 <[email protected]>
Acked-by: Matt Mackall <[email protected]>
---
 drivers/net/netconsole.c        |  823 ---------------------------------------
 include/linux/netpoll_targets.h |   76 ++++
 net/core/Makefile               |    1 
 net/core/netpoll_targets.c      |  749 +++++++++++++++++++++++++++++++++++
 4 files changed, 828 insertions(+), 821 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 1970be3..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 <[email protected]>");
 MODULE_DESCRIPTION("Console driver for network interfaces");
@@ -65,746 +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 disabled, or was disabled due
-        * to a netdev event, but we haven't had the chance to clean
-        * things up yet.
-        *
-        * We can't wait for the target to be cleaned up by its
-        * scheduled work however, as that work doesn't pin this module
-        * in place.
-        */
-       cancel_work_sync(&nt->cleanup_work);
-       if (nt->np_state == NETPOLL_ENABLED || nt->np_state == NETPOLL_CLEANING)
-               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;
@@ -844,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;
@@ -931,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..5fc84f8c
--- /dev/null
+++ b/net/core/netpoll_targets.c
@@ -0,0 +1,749 @@
+#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 disabled, or was disabled due
+        * to a netdev event, but we haven't had the chance to clean
+        * things up yet.
+        *
+        * We can't wait for the target to be cleaned up by its
+        * scheduled work however, as that work doesn't pin this module
+        * in place.
+        */
+       cancel_work_sync(&nt->cleanup_work);
+       if (nt->np_state == NETPOLL_ENABLED || nt->np_state == NETPOLL_CLEANING)
+               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-api" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to