Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932588Ab0LNVbY (ORCPT ); Tue, 14 Dec 2010 16:31:24 -0500 Received: from smtp-out.google.com ([216.239.44.51]:28363 "EHLO smtp-out.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932512Ab0LNVbU (ORCPT ); Tue, 14 Dec 2010 16:31:20 -0500 DomainKey-Signature: a=rsa-sha1; s=beta; d=google.com; c=nofws; q=dns; h=subject:to:from:cc:date:message-id:in-reply-to:references: user-agent:mime-version:content-type: content-transfer-encoding:x-system-of-record; b=luLDxjCyQ4TxzqBbk9xfEWB8Kuso0/VFQA6I7pdCGX6LKFB1xzdxJdluOOaKzYgex SF06eECAM8bQynr4JlM8Q== Subject: [PATCH v3 14/22] netpoll: Move target code into netpoll_targets.c To: simon.kagstrom@netinsight.net, davem@davemloft.net, nhorman@tuxdriver.com, Matt Mackall From: Mike Waychison Cc: adurbin@google.com, linux-kernel@vger.kernel.org, chavey@google.com, Greg KH , netdev@vger.kernel.org, =?utf-8?q?Am=C3=A9rico?= Wang , akpm@linux-foundation.org, linux-api@vger.kernel.org Date: Tue, 14 Dec 2010 13:30:10 -0800 Message-ID: <20101214213010.17022.54049.stgit@mike.mtv.corp.google.com> In-Reply-To: <20101214212846.17022.64836.stgit@mike.mtv.corp.google.com> References: <20101214212846.17022.64836.stgit@mike.mtv.corp.google.com> User-Agent: StGit/0.15 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit X-System-Of-Record: true Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 48682 Lines: 1739 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 Acked-by: Matt Mackall --- 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 #include #include -#include #include #include -#include #include +#include #include -#include MODULE_AUTHOR("Maintainer: Matt Mackall "); 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// - * | - * / - * | enabled - * | dev_name - * | local_port - * | remote_port - * | local_ip - * | remote_ip - * | local_mac - * | remote_mac - * | - * /... - */ - -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 + +#include +#include +#include + +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 + +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// + * | + * / + * | enabled + * | dev_name + * | local_port + * | remote_port + * | local_ip + * | remote_ip + * | local_mac + * | remote_mac + * | + * /... + */ + +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-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/