Switch to registering a new console for each target so that loglevel
based message filtering can be set individually for each target.

This adds a new loglevel= option to netconsole module parameter and also
add a configfs file to configure the loglevel.

The loglevel conf be adjusted at any time for synamic netconsole
consoles.

Signed-off-by: Bruno Prémont <bonb...@linux-vserver.org>
---
Note: only configuration via configfs has been runtime-tested.

 Documentation/networking/netconsole.txt |  11 ++-
 drivers/net/netconsole.c                | 114 +++++++++++++++++++++++---------
 2 files changed, 94 insertions(+), 31 deletions(-)

diff --git a/Documentation/networking/netconsole.txt 
b/Documentation/networking/netconsole.txt
index a5d574a..c1df516 100644
--- a/Documentation/networking/netconsole.txt
+++ b/Documentation/networking/netconsole.txt
@@ -24,7 +24,7 @@ Sender and receiver configuration:
 It takes a string configuration parameter "netconsole" in the
 following format:
 
- netconsole=[src-port]@[src-ip]/[<dev>],[tgt-port]@<tgt-ip>/[tgt-macaddr]
+ 
netconsole=[src-port]@[src-ip]/[<dev>],[tgt-port]@<tgt-ip>/[tgt-macaddr][,loglevel=level]
 
    where
         src-port      source for UDP packets (defaults to 6665)
@@ -33,6 +33,9 @@ following format:
         tgt-port      port for logging agent (6666)
         tgt-ip        IP address for logging agent
         tgt-macaddr   ethernet MAC address for logging agent (broadcast)
+        level         limit messages printed to those whose loglevel is
+                      smaller that value, range is 1 to 8
+                      If missing, loglevel limit as set via syslog(1) applies
 
 Examples:
 
@@ -114,11 +117,16 @@ The interface exposes these parameters of a netconsole 
target to userspace:
        remote_ip       Remote agent's IP address               (read-write)
        local_mac       Local interface's MAC address           (read-only)
        remote_mac      Remote agent's MAC address              (read-write)
+       loglevel        Console loglevel filter                 (read-write)
 
 The "enabled" attribute is also used to control whether the parameters of
 a target can be updated or not -- you can modify the parameters of only
 disabled targets (i.e. if "enabled" is 0).
 
+The "loglevel" parameter can be set at any time. When set to "0" it will
+read back as empty string and global loglevel filter applies. Otherwise
+specified loglevel filter applies.
+
 To update a target's parameters:
 
  cat enabled                           # check if enabled is 1
@@ -164,6 +172,7 @@ priority messages to the console. You can change this at 
runtime using:
 
  dmesg -n 8
 
+or by specifying netconsole loglevel parameter
 or by specifying "debug" on the kernel command line at boot, to send
 all kernel messages to the console. A specific value for this parameter
 can also be set using the "loglevel" kernel boot option. See the
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index ba2f5e7..a96cd8e 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -103,10 +103,12 @@ struct netconsole_target {
 #ifdef CONFIG_NETCONSOLE_DYNAMIC
        struct config_item      item;
 #endif
+       struct console          console;
        int                     enabled;
        struct mutex            mutex;
        struct netpoll          np;
 };
+static void write_msg(struct console *con, const char *msg, unsigned int len);
 
 #ifdef CONFIG_NETCONSOLE_DYNAMIC
 
@@ -171,6 +173,7 @@ static struct netconsole_target *alloc_param_target(char 
*target_config)
 {
        int err = -ENOMEM;
        struct netconsole_target *nt;
+       char *loglevel;
 
        /*
         * Allocate and initialize with defaults.
@@ -186,8 +189,26 @@ static struct netconsole_target *alloc_param_target(char 
*target_config)
        nt->np.remote_port = 6666;
        mutex_init(&nt->mutex);
        memset(nt->np.remote_mac, 0xff, ETH_ALEN);
+       snprintf(nt->console.name, sizeof(nt->console.name), "netcon");
+       /* Dump existing printks when we register */
+       nt->console.flags = CON_ENABLED | CON_PRINTBUFFER;
+       nt->console.write = write_msg;
 
        /* Parse parameters and setup netpoll */
+       loglevel = strstr(target_config, ",loglevel=");
+       if (loglevel) {
+               int level;
+               err = kstrtoint(loglevel+10, 10, &level);
+               if (err < 0)
+                       goto fail;
+               if (level < 1 || level > 8) {
+                       err = -EINVAL;
+                       goto fail;
+               }
+               nt->console.loglevel = level;
+               nt->console.flags |= CON_LOGLEVEL;
+               *loglevel = '\0';
+       }
        err = netpoll_parse_options(&nt->np, target_config);
        if (err)
                goto fail;
@@ -228,6 +249,7 @@ static void free_param_target(struct netconsole_target *nt)
  *                             |       remote_ip
  *                             |       local_mac
  *                             |       remote_mac
+ *                             |       loglevel
  *                             |
  *                             <target>/...
  */
@@ -301,6 +323,14 @@ static ssize_t show_remote_mac(struct netconsole_target 
*nt, char *buf)
        return snprintf(buf, PAGE_SIZE, "%pM\n", nt->np.remote_mac);
 }
 
+static ssize_t show_loglevel(struct netconsole_target *nt, char *buf)
+{
+       if (nt->console.flags & CON_LOGLEVEL)
+               return snprintf(buf, PAGE_SIZE, "%u\n", nt->console.loglevel);
+       else
+               return snprintf(buf, PAGE_SIZE, "\n");
+}
+
 /*
  * This one is special -- targets created through the configfs interface
  * are not enabled (and the corresponding netpoll activated) by default.
@@ -338,6 +368,8 @@ static ssize_t store_enabled(struct netconsole_target *nt,
                if (err)
                        return err;
 
+               nt->enabled = 1;
+               register_console(&nt->console);
                pr_info("netconsole: network logging started\n");
        } else {        /* 0 */
                /* We need to disable the netconsole before cleaning it up
@@ -347,11 +379,11 @@ static ssize_t store_enabled(struct netconsole_target *nt,
                spin_lock_irqsave(&target_list_lock, flags);
                nt->enabled = 0;
                spin_unlock_irqrestore(&target_list_lock, flags);
+               unregister_console(&nt->console);
                netpoll_cleanup(&nt->np);
+               nt->console.flags = nt->console.flags & ~CON_PRINTBUFFER;
        }
 
-       nt->enabled = enabled;
-
        return strnlen(buf, count);
 }
 
@@ -494,6 +526,27 @@ static ssize_t store_remote_mac(struct netconsole_target 
*nt,
        return strnlen(buf, count);
 }
 
+static ssize_t store_loglevel(struct netconsole_target *nt,
+                                const char *buf,
+                                size_t count)
+{
+       int rv, level;
+
+       rv = kstrtoint(buf, 10, &level);
+       if (rv < 0)
+               return rv;
+       if (level < 0 || level > 8)
+               return -EINVAL;
+
+       if (level > 0) {
+               nt->console.loglevel = level;
+               nt->console.flags |= CON_LOGLEVEL;
+       } else
+               nt->console.flags &= ~CON_LOGLEVEL;
+
+       return strnlen(buf, count);
+}
+
 /*
  * Attribute definitions for netconsole_target.
  */
@@ -514,6 +567,7 @@ NETCONSOLE_TARGET_ATTR_RW(local_ip);
 NETCONSOLE_TARGET_ATTR_RW(remote_ip);
 NETCONSOLE_TARGET_ATTR_RO(local_mac);
 NETCONSOLE_TARGET_ATTR_RW(remote_mac);
+NETCONSOLE_TARGET_ATTR_RW(loglevel);
 
 static struct configfs_attribute *netconsole_target_attrs[] = {
        &netconsole_target_enabled.attr,
@@ -524,6 +578,7 @@ static struct configfs_attribute *netconsole_target_attrs[] 
= {
        &netconsole_target_remote_ip.attr,
        &netconsole_target_local_mac.attr,
        &netconsole_target_remote_mac.attr,
+       &netconsole_target_loglevel.attr,
        NULL,
 };
 
@@ -605,6 +660,10 @@ static struct config_item *make_netconsole_target(struct 
config_group *group,
        nt->np.remote_port = 6666;
        mutex_init(&nt->mutex);
        memset(nt->np.remote_mac, 0xff, ETH_ALEN);
+       snprintf(nt->console.name, sizeof(nt->console.name), "netcon-%s", name);
+       nt->console.flags = CON_ENABLED;
+       nt->console.write = write_msg;
+       nt->console.loglevel = 0;
 
        /* Initialize the config_item member */
        config_item_init_type_name(&nt->item, name, &netconsole_target_type);
@@ -626,6 +685,7 @@ static void drop_netconsole_target(struct config_group 
*group,
        spin_lock_irqsave(&target_list_lock, flags);
        list_del(&nt->list);
        spin_unlock_irqrestore(&target_list_lock, flags);
+       unregister_console(&nt->console);
 
        /*
         * The target may have never been enabled, or was manually disabled
@@ -732,7 +792,7 @@ static void write_msg(struct console *con, const char *msg, 
unsigned int len)
 {
        int frag, left;
        unsigned long flags;
-       struct netconsole_target *nt;
+       struct netconsole_target *nt = container_of(con, struct 
netconsole_target, console);
        const char *tmp;
 
        if (oops_only && !oops_in_progress)
@@ -742,34 +802,26 @@ static void write_msg(struct console *con, const char 
*msg, unsigned int len)
                return;
 
        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)) {
-                       /*
-                        * 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;
-                       }
+       netconsole_target_get(nt);
+       if (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;
                }
-               netconsole_target_put(nt);
        }
+       netconsole_target_put(nt);
        spin_unlock_irqrestore(&target_list_lock, flags);
 }
 
-static struct console netconsole = {
-       .name   = "netcon",
-       .flags  = CON_ENABLED,
-       .write  = write_msg,
-};
-
 static int __init init_netconsole(void)
 {
        int err;
@@ -785,8 +837,6 @@ static int __init init_netconsole(void)
                                err = PTR_ERR(nt);
                                goto fail;
                        }
-                       /* Dump existing printks when we register */
-                       netconsole.flags |= CON_PRINTBUFFER;
 
                        spin_lock_irqsave(&target_list_lock, flags);
                        list_add(&nt->list, &target_list);
@@ -802,7 +852,9 @@ static int __init init_netconsole(void)
        if (err)
                goto undonotifier;
 
-       register_console(&netconsole);
+       list_for_each_entry_safe(nt, tmp, &target_list, list) {
+               register_console(&nt->console);
+       }
        pr_info("network logging started\n");
 
        return err;
@@ -830,7 +882,9 @@ static void __exit cleanup_netconsole(void)
 {
        struct netconsole_target *nt, *tmp;
 
-       unregister_console(&netconsole);
+       list_for_each_entry_safe(nt, tmp, &target_list, list) {
+               unregister_console(&nt->console);
+       }
        dynamic_netconsole_exit();
        unregister_netdevice_notifier(&netconsole_netdev_notifier);
 
-- 
2.0.4

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

Reply via email to