Release 4.11 drivers/net/netconsole.c
/*
* linux/drivers/net/netconsole.c
*
* Copyright (C) 2001 Ingo Molnar <mingo@redhat.com>
*
* This file contains the implementation of an IRQ-safe, crash-safe
* kernel console implementation that outputs kernel messages to the
* network.
*
* Modification history:
*
* 2001-09-17 started by Ingo Molnar.
* 2003-08-11 2.6 port by Matt Mackall
* simplified options
* generic card hooks
* works non-modular
* 2003-09-07 rewritten with netpoll api
*/
/****************************************************************
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
****************************************************************/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#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/kernel.h>
#include <linux/string.h>
#include <linux/netpoll.h>
#include <linux/inet.h>
#include <linux/configfs.h>
#include <linux/etherdevice.h>
MODULE_AUTHOR("Maintainer: Matt Mackall <mpm@selenic.com>");
MODULE_DESCRIPTION("Console driver for network interfaces");
MODULE_LICENSE("GPL");
#define MAX_PARAM_LENGTH 256
#define MAX_PRINT_CHUNK 1000
static char config[MAX_PARAM_LENGTH];
module_param_string(netconsole, config, MAX_PARAM_LENGTH, 0);
MODULE_PARM_DESC(netconsole, " netconsole=[src-port]@[src-ip]/[dev],[tgt-port]@<tgt-ip>/[tgt-macaddr]");
static bool oops_only = false;
module_param(oops_only, bool, 0600);
MODULE_PARM_DESC(oops_only, "Only log oops messages");
#ifndef MODULE
static int __init option_setup(char *opt)
{
strlcpy(config, opt, MAX_PARAM_LENGTH);
return 1;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 23 | 100.00% | 1 | 100.00% |
Total | 23 | 100.00% | 1 | 100.00% |
__setup("netconsole=", option_setup);
#endif /* MODULE */
/* Linked list of all configured targets */
static LIST_HEAD(target_list);
/* This needs to be a spinlock because write_msg() cannot sleep */
static DEFINE_SPINLOCK(target_list_lock);
/*
* Console driver for extended netconsoles. Registered on the first use to
* avoid unnecessarily enabling ext message formatting.
*/
static struct console netconsole_ext;
/**
* struct netconsole_target - Represents a configured netconsole target.
* @list: Links this target into the target_list.
* @item: Links us into the configfs subsystem hierarchy.
* @enabled: On / off knob to enable / disable target.
* Visible from userspace (read-write).
* We maintain a strict 1:1 correspondence between this and
* whether the corresponding netpoll is active or inactive.
* Also, other parameters of a target may be modified at
* runtime only when it is disabled (enabled == 0).
* @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 netconsole_target {
struct list_head list;
#ifdef CONFIG_NETCONSOLE_DYNAMIC
struct config_item item;
#endif
bool enabled;
bool extended;
struct netpoll np;
};
#ifdef CONFIG_NETCONSOLE_DYNAMIC
static struct configfs_subsystem netconsole_subsys;
static DEFINE_MUTEX(dynamic_netconsole_mutex);
static int __init dynamic_netconsole_init(void)
{
config_group_init(&netconsole_subsys.su_group);
mutex_init(&netconsole_subsys.su_mutex);
return configfs_register_subsystem(&netconsole_subsys);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 32 | 100.00% | 1 | 100.00% |
Total | 32 | 100.00% | 1 | 100.00% |
static void __exit dynamic_netconsole_exit(void)
{
configfs_unregister_subsystem(&netconsole_subsys);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 15 | 100.00% | 1 | 100.00% |
Total | 15 | 100.00% | 1 | 100.00% |
/*
* 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 netconsole_target_get(struct netconsole_target *nt)
{
if (config_item_name(&nt->item))
config_item_get(&nt->item);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 29 | 100.00% | 1 | 100.00% |
Total | 29 | 100.00% | 1 | 100.00% |
static void netconsole_target_put(struct netconsole_target *nt)
{
if (config_item_name(&nt->item))
config_item_put(&nt->item);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 29 | 100.00% | 1 | 100.00% |
Total | 29 | 100.00% | 1 | 100.00% |
#else /* !CONFIG_NETCONSOLE_DYNAMIC */
static int __init dynamic_netconsole_init(void)
{
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 12 | 100.00% | 1 | 100.00% |
Total | 12 | 100.00% | 1 | 100.00% |
static void __exit dynamic_netconsole_exit(void)
{
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 8 | 100.00% | 1 | 100.00% |
Total | 8 | 100.00% | 1 | 100.00% |
/*
* No danger of targets going away from under us when dynamic
* reconfigurability is off.
*/
static void netconsole_target_get(struct netconsole_target *nt)
{
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 10 | 100.00% | 1 | 100.00% |
Total | 10 | 100.00% | 1 | 100.00% |
static void netconsole_target_put(struct netconsole_target *nt)
{
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 10 | 100.00% | 1 | 100.00% |
Total | 10 | 100.00% | 1 | 100.00% |
#endif /* CONFIG_NETCONSOLE_DYNAMIC */
/* Allocate new target (from boot/module param) and setup netpoll for it */
static struct netconsole_target *alloc_param_target(char *target_config)
{
int err = -ENOMEM;
struct netconsole_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)
goto fail;
nt->np.name = "netconsole";
strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ);
nt->np.local_port = 6665;
nt->np.remote_port = 6666;
eth_broadcast_addr(nt->np.remote_mac);
if (*target_config == '+') {
nt->extended = true;
target_config++;
}
/* 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->enabled = true;
return nt;
fail:
kfree(nt);
return ERR_PTR(err);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 148 | 88.10% | 1 | 25.00% |
Tejun Heo | 19 | 11.31% | 2 | 50.00% |
Joe Perches | 1 | 0.60% | 1 | 25.00% |
Total | 168 | 100.00% | 4 | 100.00% |
/* Cleanup netpoll for given target (from boot/module param) and free it */
static void free_param_target(struct netconsole_target *nt)
{
netpoll_cleanup(&nt->np);
kfree(nt);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 24 | 100.00% | 1 | 100.00% |
Total | 24 | 100.00% | 1 | 100.00% |
#ifdef CONFIG_NETCONSOLE_DYNAMIC
/*
* Our subsystem hierarchy is:
*
* /sys/kernel/config/netconsole/
* |
* <target>/
* | enabled
* | dev_name
* | local_port
* | remote_port
* | local_ip
* | remote_ip
* | local_mac
* | remote_mac
* |
* <target>/...
*/
static struct netconsole_target *to_target(struct config_item *item)
{
return item ?
container_of(item, struct netconsole_target, item) :
NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 28 | 100.00% | 1 | 100.00% |
Total | 28 | 100.00% | 1 | 100.00% |
/*
* Attribute operations for netconsole_target.
*/
static ssize_t enabled_show(struct config_item *item, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", to_target(item)->enabled);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 25 | 78.12% | 1 | 50.00% |
Christoph Hellwig | 7 | 21.88% | 1 | 50.00% |
Total | 32 | 100.00% | 2 | 100.00% |
static ssize_t extended_show(struct config_item *item, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", to_target(item)->extended);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Tejun Heo | 25 | 78.12% | 1 | 50.00% |
Christoph Hellwig | 7 | 21.88% | 1 | 50.00% |
Total | 32 | 100.00% | 2 | 100.00% |
static ssize_t dev_name_show(struct config_item *item, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", to_target(item)->np.dev_name);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 27 | 79.41% | 1 | 50.00% |
Christoph Hellwig | 7 | 20.59% | 1 | 50.00% |
Total | 34 | 100.00% | 2 | 100.00% |
static ssize_t local_port_show(struct config_item *item, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", to_target(item)->np.local_port);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 27 | 79.41% | 1 | 50.00% |
Christoph Hellwig | 7 | 20.59% | 1 | 50.00% |
Total | 34 | 100.00% | 2 | 100.00% |
static ssize_t remote_port_show(struct config_item *item, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", to_target(item)->np.remote_port);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 27 | 79.41% | 1 | 50.00% |
Christoph Hellwig | 7 | 20.59% | 1 | 50.00% |
Total | 34 | 100.00% | 2 | 100.00% |
static ssize_t local_ip_show(struct config_item *item, char *buf)
{
struct netconsole_target *nt = to_target(item);
if (nt->np.ipv6)
return snprintf(buf, PAGE_SIZE, "%pI6c\n", &nt->np.local_ip.in6);
else
return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.local_ip);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Américo Wang | 28 | 40.00% | 2 | 40.00% |
Satyam Sharma | 27 | 38.57% | 1 | 20.00% |
Christoph Hellwig | 13 | 18.57% | 1 | 20.00% |
Harvey Harrison | 2 | 2.86% | 1 | 20.00% |
Total | 70 | 100.00% | 5 | 100.00% |
static ssize_t remote_ip_show(struct config_item *item, char *buf)
{
struct netconsole_target *nt = to_target(item);
if (nt->np.ipv6)
return snprintf(buf, PAGE_SIZE, "%pI6c\n", &nt->np.remote_ip.in6);
else
return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.remote_ip);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Américo Wang | 28 | 40.00% | 2 | 40.00% |
Satyam Sharma | 27 | 38.57% | 1 | 20.00% |
Christoph Hellwig | 13 | 18.57% | 1 | 20.00% |
Harvey Harrison | 2 | 2.86% | 1 | 20.00% |
Total | 70 | 100.00% | 5 | 100.00% |
static ssize_t local_mac_show(struct config_item *item, char *buf)
{
struct net_device *dev = to_target(item)->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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 24 | 34.78% | 1 | 20.00% |
Satyam Sharma | 23 | 33.33% | 1 | 20.00% |
Stephen Hemminger | 12 | 17.39% | 1 | 20.00% |
Christoph Hellwig | 7 | 10.14% | 1 | 20.00% |
Keiichi Kii | 3 | 4.35% | 1 | 20.00% |
Total | 69 | 100.00% | 5 | 100.00% |
static ssize_t remote_mac_show(struct config_item *item, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%pM\n", to_target(item)->np.remote_mac);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 25 | 73.53% | 1 | 25.00% |
Christoph Hellwig | 7 | 20.59% | 1 | 25.00% |
Joe Perches | 1 | 2.94% | 1 | 25.00% |
Johannes Berg | 1 | 2.94% | 1 | 25.00% |
Total | 34 | 100.00% | 4 | 100.00% |
/*
* 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 enabled_store(struct config_item *item,
const char *buf, size_t count)
{
struct netconsole_target *nt = to_target(item);
unsigned long flags;
int enabled;
int err;
mutex_lock(&dynamic_netconsole_mutex);
err = kstrtoint(buf, 10, &enabled);
if (err < 0)
goto out_unlock;
err = -EINVAL;
if (enabled < 0 || enabled > 1)
goto out_unlock;
if ((bool)enabled == nt->enabled) {
pr_info("network logging has already %s\n",
nt->enabled ? "started" : "stopped");
goto out_unlock;
}
if (enabled) { /* true */
if (nt->extended && !(netconsole_ext.flags & CON_ENABLED)) {
netconsole_ext.flags |= CON_ENABLED;
register_console(&netconsole_ext);
}
/*
* 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);
if (err)
goto out_unlock;
pr_info("netconsole: network logging started\n");
} else { /* false */
/* We need to disable the netconsole before cleaning it up
* otherwise we might end up in write_msg() with
* nt->np.dev == NULL and nt->enabled == true
*/
spin_lock_irqsave(&target_list_lock, flags);
nt->enabled = false;
spin_unlock_irqrestore(&target_list_lock, flags);
netpoll_cleanup(&nt->np);
}
nt->enabled = enabled;
mutex_unlock(&dynamic_netconsole_mutex);
return strnlen(buf, count);
out_unlock:
mutex_unlock(&dynamic_netconsole_mutex);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 93 | 37.80% | 1 | 11.11% |
Christoph Hellwig | 52 | 21.14% | 1 | 11.11% |
Tejun Heo | 36 | 14.63% | 2 | 22.22% |
Gao Feng | 21 | 8.54% | 1 | 11.11% |
Alexey Dobriyan | 19 | 7.72% | 1 | 11.11% |
Nikolay Aleksandrov | 16 | 6.50% | 1 | 11.11% |
Dan Aloni | 6 | 2.44% | 1 | 11.11% |
Joe Perches | 3 | 1.22% | 1 | 11.11% |
Total | 246 | 100.00% | 9 | 100.00% |
static ssize_t extended_store(struct config_item *item, const char *buf,
size_t count)
{
struct netconsole_target *nt = to_target(item);
int extended;
int err;
mutex_lock(&dynamic_netconsole_mutex);
if (nt->enabled) {
pr_err("target (%s) is enabled, disable to update parameters\n",
config_item_name(&nt->item));
err = -EINVAL;
goto out_unlock;
}
err = kstrtoint(buf, 10, &extended);
if (err < 0)
goto out_unlock;
if (extended < 0 || extended > 1) {
err = -EINVAL;
goto out_unlock;
}
nt->extended = extended;
mutex_unlock(&dynamic_netconsole_mutex);
return strnlen(buf, count);
out_unlock:
mutex_unlock(&dynamic_netconsole_mutex);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Tejun Heo | 89 | 62.68% | 1 | 50.00% |
Christoph Hellwig | 53 | 37.32% | 1 | 50.00% |
Total | 142 | 100.00% | 2 | 100.00% |
static ssize_t dev_name_store(struct config_item *item, const char *buf,
size_t count)
{
struct netconsole_target *nt = to_target(item);
size_t len;
mutex_lock(&dynamic_netconsole_mutex);
if (nt->enabled) {
pr_err("target (%s) is enabled, disable to update parameters\n",
config_item_name(&nt->item));
mutex_unlock(&dynamic_netconsole_mutex);
return -EINVAL;
}
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';
mutex_unlock(&dynamic_netconsole_mutex);
return strnlen(buf, count);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 99 | 71.74% | 1 | 33.33% |
Christoph Hellwig | 37 | 26.81% | 1 | 33.33% |
Joe Perches | 2 | 1.45% | 1 | 33.33% |
Total | 138 | 100.00% | 3 | 100.00% |
static ssize_t local_port_store(struct config_item *item, const char *buf,
size_t count)
{
struct netconsole_target *nt = to_target(item);
int rv = -EINVAL;
mutex_lock(&dynamic_netconsole_mutex);
if (nt->enabled) {
pr_err("target (%s) is enabled, disable to update parameters\n",
config_item_name(&nt->item));
goto out_unlock;
}
rv = kstrtou16(buf, 10, &nt->np.local_port);
if (rv < 0)
goto out_unlock;
mutex_unlock(&dynamic_netconsole_mutex);
return strnlen(buf, count);
out_unlock:
mutex_unlock(&dynamic_netconsole_mutex);
return rv;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 56 | 48.70% | 1 | 25.00% |
Christoph Hellwig | 45 | 39.13% | 1 | 25.00% |
Alexey Dobriyan | 12 | 10.43% | 1 | 25.00% |
Joe Perches | 2 | 1.74% | 1 | 25.00% |
Total | 115 | 100.00% | 4 | 100.00% |
static ssize_t remote_port_store(struct config_item *item,
const char *buf, size_t count)
{
struct netconsole_target *nt = to_target(item);
int rv = -EINVAL;
mutex_lock(&dynamic_netconsole_mutex);
if (nt->enabled) {
pr_err("target (%s) is enabled, disable to update parameters\n",
config_item_name(&nt->item));
goto out_unlock;
}
rv = kstrtou16(buf, 10, &nt->np.remote_port);
if (rv < 0)
goto out_unlock;
mutex_unlock(&dynamic_netconsole_mutex);
return strnlen(buf, count);
out_unlock:
mutex_unlock(&dynamic_netconsole_mutex);
return rv;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 56 | 48.70% | 1 | 25.00% |
Christoph Hellwig | 45 | 39.13% | 1 | 25.00% |
Alexey Dobriyan | 12 | 10.43% | 1 | 25.00% |
Joe Perches | 2 | 1.74% | 1 | 25.00% |
Total | 115 | 100.00% | 4 | 100.00% |
static ssize_t local_ip_store(struct config_item *item, const char *buf,
size_t count)
{
struct netconsole_target *nt = to_target(item);
mutex_lock(&dynamic_netconsole_mutex);
if (nt->enabled) {
pr_err("target (%s) is enabled, disable to update parameters\n",
config_item_name(&nt->item));
goto out_unlock;
}
if (strnchr(buf, count, ':')) {
const char *end;
if (in6_pton(buf, count, nt->np.local_ip.in6.s6_addr, -1, &end) > 0) {
if (*end && *end != '\n') {
pr_err("invalid IPv6 address at: <%c>\n", *end);
goto out_unlock;
}
nt->np.ipv6 = true;
} else
goto out_unlock;
} else {
if (!nt->np.ipv6) {
nt->np.local_ip.ip = in_aton(buf);
} else
goto out_unlock;
}
mutex_unlock(&dynamic_netconsole_mutex);
return strnlen(buf, count);
out_unlock:
mutex_unlock(&dynamic_netconsole_mutex);
return -EINVAL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Américo Wang | 91 | 45.96% | 2 | 40.00% |
Satyam Sharma | 54 | 27.27% | 1 | 20.00% |
Christoph Hellwig | 49 | 24.75% | 1 | 20.00% |
Joe Perches | 4 | 2.02% | 1 | 20.00% |
Total | 198 | 100.00% | 5 | 100.00% |
static ssize_t remote_ip_store(struct config_item *item, const char *buf,
size_t count)
{
struct netconsole_target *nt = to_target(item);
mutex_lock(&dynamic_netconsole_mutex);
if (nt->enabled) {
pr_err("target (%s) is enabled, disable to update parameters\n",
config_item_name(&nt->item));
goto out_unlock;
}
if (strnchr(buf, count, ':')) {
const char *end;
if (in6_pton(buf, count, nt->np.remote_ip.in6.s6_addr, -1, &end) > 0) {
if (*end && *end != '\n') {
pr_err("invalid IPv6 address at: <%c>\n", *end);
goto out_unlock;
}
nt->np.ipv6 = true;
} else
goto out_unlock;
} else {
if (!nt->np.ipv6) {
nt->np.remote_ip.ip = in_aton(buf);
} else
goto out_unlock;
}
mutex_unlock(&dynamic_netconsole_mutex);
return strnlen(buf, count);
out_unlock:
mutex_unlock(&dynamic_netconsole_mutex);
return -EINVAL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Américo Wang | 91 | 45.96% | 2 | 40.00% |
Satyam Sharma | 54 | 27.27% | 1 | 20.00% |
Christoph Hellwig | 49 | 24.75% | 1 | 20.00% |
Joe Perches | 4 | 2.02% | 1 | 20.00% |
Total | 198 | 100.00% | 5 | 100.00% |
static ssize_t remote_mac_store(struct config_item *item, const char *buf,
size_t count)
{
struct netconsole_target *nt = to_target(item);
u8 remote_mac[ETH_ALEN];
mutex_lock(&dynamic_netconsole_mutex);
if (nt->enabled) {
pr_err("target (%s) is enabled, disable to update parameters\n",
config_item_name(&nt->item));
goto out_unlock;
}
if (!mac_pton(buf, remote_mac))
goto out_unlock;
if (buf[3 * ETH_ALEN - 1] && buf[3 * ETH_ALEN - 1] != '\n')
goto out_unlock;
memcpy(nt->np.remote_mac, remote_mac, ETH_ALEN);
mutex_unlock(&dynamic_netconsole_mutex);
return strnlen(buf, count);
out_unlock:
mutex_unlock(&dynamic_netconsole_mutex);
return -EINVAL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 77 | 54.23% | 1 | 25.00% |
Christoph Hellwig | 46 | 32.39% | 1 | 25.00% |
Alexey Dobriyan | 17 | 11.97% | 1 | 25.00% |
Joe Perches | 2 | 1.41% | 1 | 25.00% |
Total | 142 | 100.00% | 4 | 100.00% |
CONFIGFS_ATTR(, enabled);
CONFIGFS_ATTR(, extended);
CONFIGFS_ATTR(, dev_name);
CONFIGFS_ATTR(, local_port);
CONFIGFS_ATTR(, remote_port);
CONFIGFS_ATTR(, local_ip);
CONFIGFS_ATTR(, remote_ip);
CONFIGFS_ATTR_RO(, local_mac);
CONFIGFS_ATTR(, remote_mac);
static struct configfs_attribute *netconsole_target_attrs[] = {
&attr_enabled,
&attr_extended,
&attr_dev_name,
&attr_local_port,
&attr_remote_port,
&attr_local_ip,
&attr_remote_ip,
&attr_local_mac,
&attr_remote_mac,
NULL,
};
/*
* Item operations and type for netconsole_target.
*/
static void netconsole_target_release(struct config_item *item)
{
kfree(to_target(item));
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 19 | 100.00% | 1 | 100.00% |
Total | 19 | 100.00% | 1 | 100.00% |
static struct configfs_item_operations netconsole_target_item_ops = {
.release = netconsole_target_release,
};
static struct config_item_type netconsole_target_type = {
.ct_attrs = netconsole_target_attrs,
.ct_item_ops = &netconsole_target_item_ops,
.ct_owner = THIS_MODULE,
};
/*
* Group operations and type for netconsole_subsys.
*/
static struct config_item *make_netconsole_target(struct config_group *group,
const char *name)
{
unsigned long flags;
struct netconsole_target *nt;
/*
* Allocate and initialize with defaults.
* Target is disabled at creation (!enabled).
*/
nt = kzalloc(sizeof(*nt), GFP_KERNEL);
if (!nt)
return ERR_PTR(-ENOMEM);
nt->np.name = "netconsole";
strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ);
nt->np.local_port = 6665;
nt->np.remote_port = 6666;
eth_broadcast_addr(nt->np.remote_mac);
/* Initialize the config_item member */
config_item_init_type_name(&nt->item, name, &netconsole_target_type);
/* Adding, but it is disabled */
spin_lock_irqsave(&target_list_lock, flags);
list_add(&nt->list, &target_list);
spin_unlock_irqrestore(&target_list_lock, flags);
return &nt->item;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 119 | 80.95% | 3 | 37.50% |
Matt Mackall | 17 | 11.56% | 1 | 12.50% |
Joel Becker | 9 | 6.12% | 2 | 25.00% |
Joe Perches | 1 | 0.68% | 1 | 12.50% |
Tejun Heo | 1 | 0.68% | 1 | 12.50% |
Total | 147 | 100.00% | 8 | 100.00% |
static void drop_netconsole_target(struct config_group *group,
struct config_item *item)
{
unsigned long flags;
struct netconsole_target *nt = to_target(item);
spin_lock_irqsave(&target_list_lock, flags);
list_del(&nt->list);
spin_unlock_irqrestore(&target_list_lock, flags);
/*
* The target may have never been enabled, or was manually disabled
* before being removed so netpoll may have already been cleaned up.
*/
if (nt->enabled)
netpoll_cleanup(&nt->np);
config_item_put(&nt->item);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 75 | 97.40% | 2 | 66.67% |
Matt Mackall | 2 | 2.60% | 1 | 33.33% |
Total | 77 | 100.00% | 3 | 100.00% |
static struct configfs_group_operations netconsole_subsys_group_ops = {
.make_item = make_netconsole_target,
.drop_item = drop_netconsole_target,
};
static struct config_item_type netconsole_subsys_type = {
.ct_group_ops = &netconsole_subsys_group_ops,
.ct_owner = THIS_MODULE,
};
/* The netconsole configfs subsystem */
static struct configfs_subsystem netconsole_subsys = {
.su_group = {
.cg_item = {
.ci_namebuf = "netconsole",
.ci_type = &netconsole_subsys_type,
},
},
};
#endif /* CONFIG_NETCONSOLE_DYNAMIC */
/* Handle network interface device notifications */
static int netconsole_netdev_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
unsigned long flags;
struct netconsole_target *nt;
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
bool stopped = false;
if (!(event == NETDEV_CHANGENAME || event == NETDEV_UNREGISTER ||
event == NETDEV_RELEASE || event == NETDEV_JOIN))
goto done;
spin_lock_irqsave(&target_list_lock, flags);
restart:
list_for_each_entry(nt, &target_list, list) {
netconsole_target_get(nt);
if (nt->np.dev == dev) {
switch (event) {
case NETDEV_CHANGENAME:
strlcpy(nt->np.dev_name, dev->name, IFNAMSIZ);
break;
case NETDEV_RELEASE:
case NETDEV_JOIN:
case NETDEV_UNREGISTER:
/* rtnl_lock already held
* we might sleep in __netpoll_cleanup()
*/
spin_unlock_irqrestore(&target_list_lock, flags);
__netpoll_cleanup(&nt->np);
spin_lock_irqsave(&target_list_lock, flags);
dev_put(nt->np.dev);
nt->np.dev = NULL;
nt->enabled = false;
stopped = true;
netconsole_target_put(nt);
goto restart;
}
}
netconsole_target_put(nt);
}
spin_unlock_irqrestore(&target_list_lock, flags);
if (stopped) {
const char *msg = "had an event";
switch (event) {
case NETDEV_UNREGISTER:
msg = "unregistered";
break;
case NETDEV_RELEASE:
msg = "released slaves";
break;
case NETDEV_JOIN:
msg = "is joining a master device";
break;
}
pr_info("network logging stopped on interface %s as it %s\n",
dev->name, msg);
}
done:
return NOTIFY_DONE;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 125 | 46.30% | 3 | 21.43% |
Américo Wang | 40 | 14.81% | 3 | 21.43% |
Joe Perches | 27 | 10.00% | 1 | 7.14% |
Veaceslav Falico | 26 | 9.63% | 1 | 7.14% |
Bruno Prémont | 19 | 7.04% | 1 | 7.14% |
Neil Horman | 18 | 6.67% | 1 | 7.14% |
Wagner Ferenc | 10 | 3.70% | 1 | 7.14% |
Jiri Pirko | 3 | 1.11% | 1 | 7.14% |
Tejun Heo | 1 | 0.37% | 1 | 7.14% |
Nikolay Aleksandrov | 1 | 0.37% | 1 | 7.14% |
Total | 270 | 100.00% | 14 | 100.00% |
static struct notifier_block netconsole_netdev_notifier = {
.notifier_call = netconsole_netdev_event,
};
/**
* send_ext_msg_udp - send extended log message to target
* @nt: target to send message to
* @msg: extended log message to send
* @msg_len: length of message
*
* Transfer extended log @msg to @nt. If @msg is longer than
* MAX_PRINT_CHUNK, it'll be split and transmitted in multiple chunks with
* ncfrag header field added to identify them.
*/
static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg,
int msg_len)
{
static char buf[MAX_PRINT_CHUNK]; /* protected by target_list_lock */
const char *header, *body;
int offset = 0;
int header_len, body_len;
if (msg_len <= MAX_PRINT_CHUNK) {
netpoll_send_udp(&nt->np, msg, msg_len);
return;
}
/* need to insert extra header fields, detect header and body */
header = msg;
body = memchr(msg, ';', msg_len);
if (WARN_ON_ONCE(!body))
return;
header_len = body - header;
body_len = msg_len - header_len - 1;
body++;
/*
* Transfer multiple chunks with the following extra header.
* "ncfrag=<byte-offset>/<total-bytes>"
*/
memcpy(buf, header, header_len);
while (offset < body_len) {
int this_header = header_len;
int this_chunk;
this_header += scnprintf(buf + this_header,
sizeof(buf) - this_header,
",ncfrag=%d/%d;", offset, body_len);
this_chunk = min(body_len - offset,
MAX_PRINT_CHUNK - this_header);
if (WARN_ON_ONCE(this_chunk <= 0))
return;
memcpy(buf + this_header, body + offset, this_chunk);
netpoll_send_udp(&nt->np, buf, this_header + this_chunk);
offset += this_chunk;
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Tejun Heo | 210 | 100.00% | 1 | 100.00% |
Total | 210 | 100.00% | 1 | 100.00% |
static void write_ext_msg(struct console *con, const char *msg,
unsigned int len)
{
struct netconsole_target *nt;
unsigned long flags;
if ((oops_only && !oops_in_progress) || list_empty(&target_list))
return;
spin_lock_irqsave(&target_list_lock, flags);
list_for_each_entry(nt, &target_list, list)
if (nt->extended && nt->enabled && netif_running(nt->np.dev))
send_ext_msg_udp(nt, msg, len);
spin_unlock_irqrestore(&target_list_lock, flags);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Tejun Heo | 97 | 100.00% | 1 | 100.00% |
Total | 97 | 100.00% | 1 | 100.00% |
static void write_msg(struct console *con, const char *msg, unsigned int len)
{
int frag, left;
unsigned long flags;
struct netconsole_target *nt;
const char *tmp;
if (oops_only && !oops_in_progress)
return;
/* Avoid taking lock and disabling interrupts unnecessarily */
if (list_empty(&target_list))
return;
spin_lock_irqsave(&target_list_lock, flags);
list_for_each_entry(nt, &target_list, list) {
if (!nt->extended && nt->enabled && netif_running(nt->np.dev)) {
/*
* We nest this inside the for-each-target loop above
* so that we're able to get as much logging out to
* at least one target if we die inside here, instead
* of unnecessarily keeping all targets in lock-step.
*/
tmp = msg;
for (left = len; left;) {
frag = min(left, MAX_PRINT_CHUNK);
netpoll_send_udp(&nt->np, tmp, frag);
tmp += frag;
left -= frag;
}
}
}
spin_unlock_irqrestore(&target_list_lock, flags);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Matt Mackall | 70 | 46.67% | 1 | 14.29% |
Satyam Sharma | 67 | 44.67% | 4 | 57.14% |
Américo Wang | 8 | 5.33% | 1 | 14.29% |
Tejun Heo | 5 | 3.33% | 1 | 14.29% |
Total | 150 | 100.00% | 7 | 100.00% |
static struct console netconsole_ext = {
.name = "netcon_ext",
.flags = CON_EXTENDED, /* starts disabled, registered on first use */
.write = write_ext_msg,
};
static struct console netconsole = {
.name = "netcon",
.flags = CON_ENABLED,
.write = write_msg,
};
static int __init init_netconsole(void)
{
int err;
struct netconsole_target *nt, *tmp;
unsigned long flags;
char *target_config;
char *input = config;
if (strnlen(input, MAX_PARAM_LENGTH)) {
while ((target_config = strsep(&input, ";"))) {
nt = alloc_param_target(target_config);
if (IS_ERR(nt)) {
err = PTR_ERR(nt);
goto fail;
}
/* Dump existing printks when we register */
if (nt->extended)
netconsole_ext.flags |= CON_PRINTBUFFER |
CON_ENABLED;
else
netconsole.flags |= CON_PRINTBUFFER;
spin_lock_irqsave(&target_list_lock, flags);
list_add(&nt->list, &target_list);
spin_unlock_irqrestore(&target_list_lock, flags);
}
}
err = register_netdevice_notifier(&netconsole_netdev_notifier);
if (err)
goto fail;
err = dynamic_netconsole_init();
if (err)
goto undonotifier;
if (netconsole_ext.flags & CON_ENABLED)
register_console(&netconsole_ext);
register_console(&netconsole);
pr_info("network logging started\n");
return err;
undonotifier:
unregister_netdevice_notifier(&netconsole_netdev_notifier);
fail:
pr_err("cleaning up\n");
/*
* 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, &target_list, list) {
list_del(&nt->list);
free_param_target(nt);
}
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 163 | 69.36% | 6 | 50.00% |
Tejun Heo | 29 | 12.34% | 1 | 8.33% |
Matt Mackall | 27 | 11.49% | 2 | 16.67% |
Michael Ellerman | 7 | 2.98% | 1 | 8.33% |
Stephen Hemminger | 5 | 2.13% | 1 | 8.33% |
Joe Perches | 4 | 1.70% | 1 | 8.33% |
Total | 235 | 100.00% | 12 | 100.00% |
static void __exit cleanup_netconsole(void)
{
struct netconsole_target *nt, *tmp;
unregister_console(&netconsole_ext);
unregister_console(&netconsole);
dynamic_netconsole_exit();
unregister_netdevice_notifier(&netconsole_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, &target_list, list) {
list_del(&nt->list);
free_param_target(nt);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 40 | 62.50% | 5 | 71.43% |
Matt Mackall | 18 | 28.12% | 1 | 14.29% |
Tejun Heo | 6 | 9.38% | 1 | 14.29% |
Total | 64 | 100.00% | 7 | 100.00% |
/*
* Use late_initcall to ensure netconsole is
* initialized after network device driver if built-in.
*
* late_initcall() and module_init() are identical if built as module.
*/
late_initcall(init_netconsole);
module_exit(cleanup_netconsole);
Overall Contributors
Person | Tokens | Prop | Commits | CommitProp |
Satyam Sharma | 1933 | 50.55% | 7 | 15.22% |
Tejun Heo | 566 | 14.80% | 4 | 8.70% |
Christoph Hellwig | 478 | 12.50% | 1 | 2.17% |
Américo Wang | 308 | 8.05% | 6 | 13.04% |
Matt Mackall | 220 | 5.75% | 2 | 4.35% |
Joe Perches | 63 | 1.65% | 3 | 6.52% |
Alexey Dobriyan | 60 | 1.57% | 2 | 4.35% |
Veaceslav Falico | 26 | 0.68% | 1 | 2.17% |
Johannes Berg | 25 | 0.65% | 1 | 2.17% |
Gao Feng | 21 | 0.55% | 1 | 2.17% |
Bruno Prémont | 19 | 0.50% | 1 | 2.17% |
Neil Horman | 18 | 0.47% | 1 | 2.17% |
Stephen Hemminger | 17 | 0.44% | 2 | 4.35% |
Nikolay Aleksandrov | 17 | 0.44% | 2 | 4.35% |
Wagner Ferenc | 10 | 0.26% | 1 | 2.17% |
Joel Becker | 9 | 0.24% | 2 | 4.35% |
Michael Ellerman | 7 | 0.18% | 1 | 2.17% |
Dan Aloni | 6 | 0.16% | 1 | 2.17% |
Randy Dunlap | 5 | 0.13% | 1 | 2.17% |
Harvey Harrison | 4 | 0.10% | 1 | 2.17% |
Jiri Pirko | 3 | 0.08% | 1 | 2.17% |
Keiichi Kii | 3 | 0.08% | 1 | 2.17% |
Andy Shevchenko | 3 | 0.08% | 1 | 2.17% |
Lin Ming | 2 | 0.05% | 1 | 2.17% |
Niels de Vos | 1 | 0.03% | 1 | 2.17% |
Total | 3824 | 100.00% | 46 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.