summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/net/netconsole.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drivers/net/netconsole.c')
-rw-r--r--kernel/drivers/net/netconsole.c414
1 files changed, 270 insertions, 144 deletions
diff --git a/kernel/drivers/net/netconsole.c b/kernel/drivers/net/netconsole.c
index 15731d1db..06ee63951 100644
--- a/kernel/drivers/net/netconsole.c
+++ b/kernel/drivers/net/netconsole.c
@@ -79,6 +79,12 @@ 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.
@@ -104,14 +110,15 @@ struct netconsole_target {
#ifdef CONFIG_NETCONSOLE_DYNAMIC
struct config_item item;
#endif
- int enabled;
- struct mutex mutex;
+ 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)
{
@@ -185,9 +192,13 @@ static struct netconsole_target *alloc_param_target(char *target_config)
strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ);
nt->np.local_port = 6665;
nt->np.remote_port = 6666;
- mutex_init(&nt->mutex);
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)
@@ -197,7 +208,7 @@ static struct netconsole_target *alloc_param_target(char *target_config)
if (err)
goto fail;
- nt->enabled = 1;
+ nt->enabled = true;
return nt;
@@ -233,15 +244,6 @@ static void free_param_target(struct netconsole_target *nt)
* <target>/...
*/
-struct netconsole_target_attr {
- struct configfs_attribute attr;
- ssize_t (*show)(struct netconsole_target *nt,
- char *buf);
- ssize_t (*store)(struct netconsole_target *nt,
- const char *buf,
- size_t count);
-};
-
static struct netconsole_target *to_target(struct config_item *item)
{
return item ?
@@ -253,53 +255,62 @@ static struct netconsole_target *to_target(struct config_item *item)
* Attribute operations for netconsole_target.
*/
-static ssize_t show_enabled(struct netconsole_target *nt, char *buf)
+static ssize_t enabled_show(struct config_item *item, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%d\n", nt->enabled);
+ return snprintf(buf, PAGE_SIZE, "%d\n", to_target(item)->enabled);
}
-static ssize_t show_dev_name(struct netconsole_target *nt, char *buf)
+static ssize_t extended_show(struct config_item *item, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%s\n", nt->np.dev_name);
+ return snprintf(buf, PAGE_SIZE, "%d\n", to_target(item)->extended);
}
-static ssize_t show_local_port(struct netconsole_target *nt, char *buf)
+static ssize_t dev_name_show(struct config_item *item, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%d\n", nt->np.local_port);
+ return snprintf(buf, PAGE_SIZE, "%s\n", to_target(item)->np.dev_name);
}
-static ssize_t show_remote_port(struct netconsole_target *nt, char *buf)
+static ssize_t local_port_show(struct config_item *item, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%d\n", nt->np.remote_port);
+ return snprintf(buf, PAGE_SIZE, "%d\n", to_target(item)->np.local_port);
}
-static ssize_t show_local_ip(struct netconsole_target *nt, char *buf)
+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);
+}
+
+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);
}
-static ssize_t show_remote_ip(struct netconsole_target *nt, char *buf)
+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);
}
-static ssize_t show_local_mac(struct netconsole_target *nt, char *buf)
+static ssize_t local_mac_show(struct config_item *item, char *buf)
{
- struct net_device *dev = nt->np.dev;
+ 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);
}
-static ssize_t show_remote_mac(struct netconsole_target *nt, char *buf)
+static ssize_t remote_mac_show(struct config_item *item, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%pM\n", nt->np.remote_mac);
+ return snprintf(buf, PAGE_SIZE, "%pM\n", to_target(item)->np.remote_mac);
}
/*
@@ -309,26 +320,34 @@ static ssize_t show_remote_mac(struct netconsole_target *nt, char *buf)
* 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 netconsole_target *nt,
- const char *buf,
- size_t count)
+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)
- return err;
+ goto out_unlock;
+
+ err = -EINVAL;
if (enabled < 0 || enabled > 1)
- return -EINVAL;
- if (enabled == nt->enabled) {
+ goto out_unlock;
+ if ((bool)enabled == nt->enabled) {
pr_info("network logging has already %s\n",
nt->enabled ? "started" : "stopped");
- return -EINVAL;
+ goto out_unlock;
}
- if (enabled) { /* 1 */
+ 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.
@@ -337,34 +356,72 @@ static ssize_t store_enabled(struct netconsole_target *nt,
err = netpoll_setup(&nt->np);
if (err)
- return err;
+ goto out_unlock;
pr_info("netconsole: network logging started\n");
- } else { /* 0 */
+ } 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 == 1
+ * nt->np.dev == NULL and nt->enabled == true
*/
spin_lock_irqsave(&target_list_lock, flags);
- nt->enabled = 0;
+ 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;
}
-static ssize_t store_dev_name(struct netconsole_target *nt,
- const char *buf,
- size_t count)
+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;
+}
+
+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;
}
@@ -375,53 +432,66 @@ static ssize_t store_dev_name(struct netconsole_target *nt,
if (nt->np.dev_name[len - 1] == '\n')
nt->np.dev_name[len - 1] = '\0';
+ mutex_unlock(&dynamic_netconsole_mutex);
return strnlen(buf, count);
}
-static ssize_t store_local_port(struct netconsole_target *nt,
- const char *buf,
- size_t count)
+static ssize_t local_port_store(struct config_item *item, const char *buf,
+ size_t count)
{
- int rv;
+ 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));
- return -EINVAL;
+ goto out_unlock;
}
rv = kstrtou16(buf, 10, &nt->np.local_port);
if (rv < 0)
- return rv;
+ goto out_unlock;
+ mutex_unlock(&dynamic_netconsole_mutex);
return strnlen(buf, count);
+out_unlock:
+ mutex_unlock(&dynamic_netconsole_mutex);
+ return rv;
}
-static ssize_t store_remote_port(struct netconsole_target *nt,
- const char *buf,
- size_t count)
+static ssize_t remote_port_store(struct config_item *item,
+ const char *buf, size_t count)
{
- int rv;
+ 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));
- return -EINVAL;
+ goto out_unlock;
}
rv = kstrtou16(buf, 10, &nt->np.remote_port);
if (rv < 0)
- return rv;
+ goto out_unlock;
+ mutex_unlock(&dynamic_netconsole_mutex);
return strnlen(buf, count);
+out_unlock:
+ mutex_unlock(&dynamic_netconsole_mutex);
+ return rv;
}
-static ssize_t store_local_ip(struct netconsole_target *nt,
- const char *buf,
- size_t count)
+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));
- return -EINVAL;
+ goto out_unlock;
}
if (strnchr(buf, count, ':')) {
@@ -429,29 +499,35 @@ static ssize_t store_local_ip(struct netconsole_target *nt,
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);
- return -EINVAL;
+ goto out_unlock;
}
nt->np.ipv6 = true;
} else
- return -EINVAL;
+ goto out_unlock;
} else {
if (!nt->np.ipv6) {
nt->np.local_ip.ip = in_aton(buf);
} else
- return -EINVAL;
+ goto out_unlock;
}
+ mutex_unlock(&dynamic_netconsole_mutex);
return strnlen(buf, count);
+out_unlock:
+ mutex_unlock(&dynamic_netconsole_mutex);
+ return -EINVAL;
}
-static ssize_t store_remote_ip(struct netconsole_target *nt,
- const char *buf,
- size_t count)
+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));
- return -EINVAL;
+ goto out_unlock;
}
if (strnchr(buf, count, ':')) {
@@ -459,72 +535,71 @@ static ssize_t store_remote_ip(struct netconsole_target *nt,
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);
- return -EINVAL;
+ goto out_unlock;
}
nt->np.ipv6 = true;
} else
- return -EINVAL;
+ goto out_unlock;
} else {
if (!nt->np.ipv6) {
nt->np.remote_ip.ip = in_aton(buf);
} else
- return -EINVAL;
+ goto out_unlock;
}
+ mutex_unlock(&dynamic_netconsole_mutex);
return strnlen(buf, count);
+out_unlock:
+ mutex_unlock(&dynamic_netconsole_mutex);
+ return -EINVAL;
}
-static ssize_t store_remote_mac(struct netconsole_target *nt,
- const char *buf,
- size_t count)
+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));
- return -EINVAL;
+ goto out_unlock;
}
if (!mac_pton(buf, remote_mac))
- return -EINVAL;
+ goto out_unlock;
if (buf[3 * ETH_ALEN - 1] && buf[3 * ETH_ALEN - 1] != '\n')
- return -EINVAL;
+ 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;
}
-/*
- * Attribute definitions for netconsole_target.
- */
-
-#define NETCONSOLE_TARGET_ATTR_RO(_name) \
-static struct netconsole_target_attr netconsole_target_##_name = \
- __CONFIGFS_ATTR(_name, S_IRUGO, show_##_name, NULL)
-
-#define NETCONSOLE_TARGET_ATTR_RW(_name) \
-static struct netconsole_target_attr netconsole_target_##_name = \
- __CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR, show_##_name, store_##_name)
-
-NETCONSOLE_TARGET_ATTR_RW(enabled);
-NETCONSOLE_TARGET_ATTR_RW(dev_name);
-NETCONSOLE_TARGET_ATTR_RW(local_port);
-NETCONSOLE_TARGET_ATTR_RW(remote_port);
-NETCONSOLE_TARGET_ATTR_RW(local_ip);
-NETCONSOLE_TARGET_ATTR_RW(remote_ip);
-NETCONSOLE_TARGET_ATTR_RO(local_mac);
-NETCONSOLE_TARGET_ATTR_RW(remote_mac);
+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[] = {
- &netconsole_target_enabled.attr,
- &netconsole_target_dev_name.attr,
- &netconsole_target_local_port.attr,
- &netconsole_target_remote_port.attr,
- &netconsole_target_local_ip.attr,
- &netconsole_target_remote_ip.attr,
- &netconsole_target_local_mac.attr,
- &netconsole_target_remote_mac.attr,
+ &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,
};
@@ -537,43 +612,8 @@ static void netconsole_target_release(struct config_item *item)
kfree(to_target(item));
}
-static ssize_t netconsole_target_attr_show(struct config_item *item,
- struct configfs_attribute *attr,
- char *buf)
-{
- ssize_t ret = -EINVAL;
- struct netconsole_target *nt = to_target(item);
- struct netconsole_target_attr *na =
- container_of(attr, struct netconsole_target_attr, attr);
-
- if (na->show)
- ret = na->show(nt, buf);
-
- return ret;
-}
-
-static ssize_t netconsole_target_attr_store(struct config_item *item,
- struct configfs_attribute *attr,
- const char *buf,
- size_t count)
-{
- ssize_t ret = -EINVAL;
- struct netconsole_target *nt = to_target(item);
- struct netconsole_target_attr *na =
- container_of(attr, struct netconsole_target_attr, attr);
-
- mutex_lock(&nt->mutex);
- if (na->store)
- ret = na->store(nt, buf, count);
- mutex_unlock(&nt->mutex);
-
- return ret;
-}
-
static struct configfs_item_operations netconsole_target_item_ops = {
.release = netconsole_target_release,
- .show_attribute = netconsole_target_attr_show,
- .store_attribute = netconsole_target_attr_store,
};
static struct config_item_type netconsole_target_type = {
@@ -594,7 +634,7 @@ static struct config_item *make_netconsole_target(struct config_group *group,
/*
* Allocate and initialize with defaults.
- * Target is disabled at creation (enabled == 0).
+ * Target is disabled at creation (!enabled).
*/
nt = kzalloc(sizeof(*nt), GFP_KERNEL);
if (!nt)
@@ -604,7 +644,6 @@ static struct config_item *make_netconsole_target(struct config_group *group,
strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ);
nt->np.local_port = 6665;
nt->np.remote_port = 6666;
- mutex_init(&nt->mutex);
eth_broadcast_addr(nt->np.remote_mac);
/* Initialize the config_item member */
@@ -695,7 +734,7 @@ restart:
spin_lock_irqsave(&target_list_lock, flags);
dev_put(nt->np.dev);
nt->np.dev = NULL;
- nt->enabled = 0;
+ nt->enabled = false;
stopped = true;
netconsole_target_put(nt);
goto restart;
@@ -729,6 +768,82 @@ 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;
+ }
+}
+
+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);
+}
+
static void write_msg(struct console *con, const char *msg, unsigned int len)
{
int frag, left;
@@ -744,8 +859,7 @@ static void write_msg(struct console *con, const char *msg, unsigned int len)
spin_lock_irqsave(&target_list_lock, flags);
list_for_each_entry(nt, &target_list, list) {
- netconsole_target_get(nt);
- if (nt->enabled && netif_running(nt->np.dev)) {
+ 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
@@ -760,11 +874,16 @@ static void write_msg(struct console *con, const char *msg, unsigned int len)
left -= frag;
}
}
- netconsole_target_put(nt);
}
spin_unlock_irqrestore(&target_list_lock, flags);
}
+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,
@@ -787,7 +906,11 @@ static int __init init_netconsole(void)
goto fail;
}
/* Dump existing printks when we register */
- netconsole.flags |= CON_PRINTBUFFER;
+ 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);
@@ -803,6 +926,8 @@ static int __init init_netconsole(void)
if (err)
goto undonotifier;
+ if (netconsole_ext.flags & CON_ENABLED)
+ register_console(&netconsole_ext);
register_console(&netconsole);
pr_info("network logging started\n");
@@ -831,6 +956,7 @@ 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);