11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * linux/drivers/net/netconsole.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 2001 Ingo Molnar <mingo@redhat.com> 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This file contains the implementation of an IRQ-safe, crash-safe 71da177e4SLinus Torvalds * kernel console implementation that outputs kernel messages to the 81da177e4SLinus Torvalds * network. 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * Modification history: 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * 2001-09-17 started by Ingo Molnar. 131da177e4SLinus Torvalds * 2003-08-11 2.6 port by Matt Mackall 141da177e4SLinus Torvalds * simplified options 151da177e4SLinus Torvalds * generic card hooks 161da177e4SLinus Torvalds * works non-modular 171da177e4SLinus Torvalds * 2003-09-07 rewritten with netpoll api 181da177e4SLinus Torvalds */ 191da177e4SLinus Torvalds 201da177e4SLinus Torvalds /**************************************************************** 211da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 221da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 231da177e4SLinus Torvalds * the Free Software Foundation; either version 2, or (at your option) 241da177e4SLinus Torvalds * any later version. 251da177e4SLinus Torvalds * 261da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 271da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 281da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 291da177e4SLinus Torvalds * GNU General Public License for more details. 301da177e4SLinus Torvalds * 311da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 321da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 331da177e4SLinus Torvalds * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 341da177e4SLinus Torvalds * 351da177e4SLinus Torvalds ****************************************************************/ 361da177e4SLinus Torvalds 3722ded577SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 3822ded577SJoe Perches 391da177e4SLinus Torvalds #include <linux/mm.h> 401da177e4SLinus Torvalds #include <linux/init.h> 411da177e4SLinus Torvalds #include <linux/module.h> 425a0e3ad6STejun Heo #include <linux/slab.h> 431da177e4SLinus Torvalds #include <linux/console.h> 441da177e4SLinus Torvalds #include <linux/moduleparam.h> 454cd5773aSAndy Shevchenko #include <linux/kernel.h> 461da177e4SLinus Torvalds #include <linux/string.h> 471da177e4SLinus Torvalds #include <linux/netpoll.h> 480bcc1816SSatyam Sharma #include <linux/inet.h> 490bcc1816SSatyam Sharma #include <linux/configfs.h> 501667c942SJoe Perches #include <linux/etherdevice.h> 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds MODULE_AUTHOR("Maintainer: Matt Mackall <mpm@selenic.com>"); 531da177e4SLinus Torvalds MODULE_DESCRIPTION("Console driver for network interfaces"); 541da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 551da177e4SLinus Torvalds 56d39badf0SSatyam Sharma #define MAX_PARAM_LENGTH 256 57d39badf0SSatyam Sharma #define MAX_PRINT_CHUNK 1000 58d39badf0SSatyam Sharma 59d39badf0SSatyam Sharma static char config[MAX_PARAM_LENGTH]; 60d39badf0SSatyam Sharma module_param_string(netconsole, config, MAX_PARAM_LENGTH, 0); 6161a2d07dSNiels de Vos MODULE_PARM_DESC(netconsole, " netconsole=[src-port]@[src-ip]/[dev],[tgt-port]@<tgt-ip>/[tgt-macaddr]"); 621da177e4SLinus Torvalds 63c1a60851SAmerigo Wang static bool oops_only = false; 64c1a60851SAmerigo Wang module_param(oops_only, bool, 0600); 65c1a60851SAmerigo Wang MODULE_PARM_DESC(oops_only, "Only log oops messages"); 66c1a60851SAmerigo Wang 67d2b60881SSatyam Sharma #ifndef MODULE 68d2b60881SSatyam Sharma static int __init option_setup(char *opt) 69d2b60881SSatyam Sharma { 70d2b60881SSatyam Sharma strlcpy(config, opt, MAX_PARAM_LENGTH); 71d2b60881SSatyam Sharma return 1; 72d2b60881SSatyam Sharma } 73d2b60881SSatyam Sharma __setup("netconsole=", option_setup); 74d2b60881SSatyam Sharma #endif /* MODULE */ 75d2b60881SSatyam Sharma 76b5427c27SSatyam Sharma /* Linked list of all configured targets */ 77b5427c27SSatyam Sharma static LIST_HEAD(target_list); 78b5427c27SSatyam Sharma 79b5427c27SSatyam Sharma /* This needs to be a spinlock because write_msg() cannot sleep */ 80b5427c27SSatyam Sharma static DEFINE_SPINLOCK(target_list_lock); 81b5427c27SSatyam Sharma 82e2f15f9aSTejun Heo /* 83e2f15f9aSTejun Heo * Console driver for extended netconsoles. Registered on the first use to 84e2f15f9aSTejun Heo * avoid unnecessarily enabling ext message formatting. 85e2f15f9aSTejun Heo */ 86e2f15f9aSTejun Heo static struct console netconsole_ext; 87e2f15f9aSTejun Heo 88df180e36SSatyam Sharma /** 89df180e36SSatyam Sharma * struct netconsole_target - Represents a configured netconsole target. 90b5427c27SSatyam Sharma * @list: Links this target into the target_list. 910bcc1816SSatyam Sharma * @item: Links us into the configfs subsystem hierarchy. 920bcc1816SSatyam Sharma * @enabled: On / off knob to enable / disable target. 930bcc1816SSatyam Sharma * Visible from userspace (read-write). 940bcc1816SSatyam Sharma * We maintain a strict 1:1 correspondence between this and 950bcc1816SSatyam Sharma * whether the corresponding netpoll is active or inactive. 960bcc1816SSatyam Sharma * Also, other parameters of a target may be modified at 970bcc1816SSatyam Sharma * runtime only when it is disabled (enabled == 0). 98df180e36SSatyam Sharma * @np: The netpoll structure for this target. 990bcc1816SSatyam Sharma * Contains the other userspace visible parameters: 1000bcc1816SSatyam Sharma * dev_name (read-write) 1010bcc1816SSatyam Sharma * local_port (read-write) 1020bcc1816SSatyam Sharma * remote_port (read-write) 1030bcc1816SSatyam Sharma * local_ip (read-write) 1040bcc1816SSatyam Sharma * remote_ip (read-write) 1050bcc1816SSatyam Sharma * local_mac (read-only) 1060bcc1816SSatyam Sharma * remote_mac (read-write) 107df180e36SSatyam Sharma */ 108df180e36SSatyam Sharma struct netconsole_target { 109b5427c27SSatyam Sharma struct list_head list; 1100bcc1816SSatyam Sharma #ifdef CONFIG_NETCONSOLE_DYNAMIC 1110bcc1816SSatyam Sharma struct config_item item; 1120bcc1816SSatyam Sharma #endif 113698cf1c6STejun Heo bool enabled; 114e2f15f9aSTejun Heo bool extended; 115df180e36SSatyam Sharma struct netpoll np; 116df180e36SSatyam Sharma }; 117df180e36SSatyam Sharma 1180bcc1816SSatyam Sharma #ifdef CONFIG_NETCONSOLE_DYNAMIC 1190bcc1816SSatyam Sharma 1200bcc1816SSatyam Sharma static struct configfs_subsystem netconsole_subsys; 121369e5a88STejun Heo static DEFINE_MUTEX(dynamic_netconsole_mutex); 1220bcc1816SSatyam Sharma 1230bcc1816SSatyam Sharma static int __init dynamic_netconsole_init(void) 1240bcc1816SSatyam Sharma { 1250bcc1816SSatyam Sharma config_group_init(&netconsole_subsys.su_group); 1260bcc1816SSatyam Sharma mutex_init(&netconsole_subsys.su_mutex); 1270bcc1816SSatyam Sharma return configfs_register_subsystem(&netconsole_subsys); 1280bcc1816SSatyam Sharma } 1290bcc1816SSatyam Sharma 1300bcc1816SSatyam Sharma static void __exit dynamic_netconsole_exit(void) 1310bcc1816SSatyam Sharma { 1320bcc1816SSatyam Sharma configfs_unregister_subsystem(&netconsole_subsys); 1330bcc1816SSatyam Sharma } 1340bcc1816SSatyam Sharma 1350bcc1816SSatyam Sharma /* 1360bcc1816SSatyam Sharma * Targets that were created by parsing the boot/module option string 1370bcc1816SSatyam Sharma * do not exist in the configfs hierarchy (and have NULL names) and will 1380bcc1816SSatyam Sharma * never go away, so make these a no-op for them. 1390bcc1816SSatyam Sharma */ 1400bcc1816SSatyam Sharma static void netconsole_target_get(struct netconsole_target *nt) 1410bcc1816SSatyam Sharma { 1420bcc1816SSatyam Sharma if (config_item_name(&nt->item)) 1430bcc1816SSatyam Sharma config_item_get(&nt->item); 1440bcc1816SSatyam Sharma } 1450bcc1816SSatyam Sharma 1460bcc1816SSatyam Sharma static void netconsole_target_put(struct netconsole_target *nt) 1470bcc1816SSatyam Sharma { 1480bcc1816SSatyam Sharma if (config_item_name(&nt->item)) 1490bcc1816SSatyam Sharma config_item_put(&nt->item); 1500bcc1816SSatyam Sharma } 1510bcc1816SSatyam Sharma 1520bcc1816SSatyam Sharma #else /* !CONFIG_NETCONSOLE_DYNAMIC */ 1530bcc1816SSatyam Sharma 1540bcc1816SSatyam Sharma static int __init dynamic_netconsole_init(void) 1550bcc1816SSatyam Sharma { 1560bcc1816SSatyam Sharma return 0; 1570bcc1816SSatyam Sharma } 1580bcc1816SSatyam Sharma 1590bcc1816SSatyam Sharma static void __exit dynamic_netconsole_exit(void) 1600bcc1816SSatyam Sharma { 1610bcc1816SSatyam Sharma } 1620bcc1816SSatyam Sharma 1630bcc1816SSatyam Sharma /* 1640bcc1816SSatyam Sharma * No danger of targets going away from under us when dynamic 1650bcc1816SSatyam Sharma * reconfigurability is off. 1660bcc1816SSatyam Sharma */ 1670bcc1816SSatyam Sharma static void netconsole_target_get(struct netconsole_target *nt) 1680bcc1816SSatyam Sharma { 1690bcc1816SSatyam Sharma } 1700bcc1816SSatyam Sharma 1710bcc1816SSatyam Sharma static void netconsole_target_put(struct netconsole_target *nt) 1720bcc1816SSatyam Sharma { 1730bcc1816SSatyam Sharma } 1740bcc1816SSatyam Sharma 1750bcc1816SSatyam Sharma #endif /* CONFIG_NETCONSOLE_DYNAMIC */ 1760bcc1816SSatyam Sharma 1770bcc1816SSatyam Sharma /* Allocate new target (from boot/module param) and setup netpoll for it */ 1780bcc1816SSatyam Sharma static struct netconsole_target *alloc_param_target(char *target_config) 179b5427c27SSatyam Sharma { 180b5427c27SSatyam Sharma int err = -ENOMEM; 181b5427c27SSatyam Sharma struct netconsole_target *nt; 182b5427c27SSatyam Sharma 1830bcc1816SSatyam Sharma /* 1840bcc1816SSatyam Sharma * Allocate and initialize with defaults. 1850bcc1816SSatyam Sharma * Note that these targets get their config_item fields zeroed-out. 1860bcc1816SSatyam Sharma */ 187b5427c27SSatyam Sharma nt = kzalloc(sizeof(*nt), GFP_KERNEL); 188e404decbSJoe Perches if (!nt) 189b5427c27SSatyam Sharma goto fail; 190b5427c27SSatyam Sharma 191b5427c27SSatyam Sharma nt->np.name = "netconsole"; 192b5427c27SSatyam Sharma strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ); 193b5427c27SSatyam Sharma nt->np.local_port = 6665; 194b5427c27SSatyam Sharma nt->np.remote_port = 6666; 1951667c942SJoe Perches eth_broadcast_addr(nt->np.remote_mac); 196b5427c27SSatyam Sharma 197e2f15f9aSTejun Heo if (*target_config == '+') { 198e2f15f9aSTejun Heo nt->extended = true; 199e2f15f9aSTejun Heo target_config++; 200e2f15f9aSTejun Heo } 201e2f15f9aSTejun Heo 202b5427c27SSatyam Sharma /* Parse parameters and setup netpoll */ 203b5427c27SSatyam Sharma err = netpoll_parse_options(&nt->np, target_config); 204b5427c27SSatyam Sharma if (err) 205b5427c27SSatyam Sharma goto fail; 206b5427c27SSatyam Sharma 207b5427c27SSatyam Sharma err = netpoll_setup(&nt->np); 208b5427c27SSatyam Sharma if (err) 209b5427c27SSatyam Sharma goto fail; 210b5427c27SSatyam Sharma 211698cf1c6STejun Heo nt->enabled = true; 2120bcc1816SSatyam Sharma 213b5427c27SSatyam Sharma return nt; 214b5427c27SSatyam Sharma 215b5427c27SSatyam Sharma fail: 216b5427c27SSatyam Sharma kfree(nt); 217b5427c27SSatyam Sharma return ERR_PTR(err); 218b5427c27SSatyam Sharma } 219b5427c27SSatyam Sharma 2200bcc1816SSatyam Sharma /* Cleanup netpoll for given target (from boot/module param) and free it */ 2210bcc1816SSatyam Sharma static void free_param_target(struct netconsole_target *nt) 222b5427c27SSatyam Sharma { 223b5427c27SSatyam Sharma netpoll_cleanup(&nt->np); 224b5427c27SSatyam Sharma kfree(nt); 225b5427c27SSatyam Sharma } 2261da177e4SLinus Torvalds 2270bcc1816SSatyam Sharma #ifdef CONFIG_NETCONSOLE_DYNAMIC 2280bcc1816SSatyam Sharma 2290bcc1816SSatyam Sharma /* 2300bcc1816SSatyam Sharma * Our subsystem hierarchy is: 2310bcc1816SSatyam Sharma * 2320bcc1816SSatyam Sharma * /sys/kernel/config/netconsole/ 2330bcc1816SSatyam Sharma * | 2340bcc1816SSatyam Sharma * <target>/ 2350bcc1816SSatyam Sharma * | enabled 2360bcc1816SSatyam Sharma * | dev_name 2370bcc1816SSatyam Sharma * | local_port 2380bcc1816SSatyam Sharma * | remote_port 2390bcc1816SSatyam Sharma * | local_ip 2400bcc1816SSatyam Sharma * | remote_ip 2410bcc1816SSatyam Sharma * | local_mac 2420bcc1816SSatyam Sharma * | remote_mac 2430bcc1816SSatyam Sharma * | 2440bcc1816SSatyam Sharma * <target>/... 2450bcc1816SSatyam Sharma */ 2460bcc1816SSatyam Sharma 2470bcc1816SSatyam Sharma static struct netconsole_target *to_target(struct config_item *item) 2480bcc1816SSatyam Sharma { 2490bcc1816SSatyam Sharma return item ? 2500bcc1816SSatyam Sharma container_of(item, struct netconsole_target, item) : 2510bcc1816SSatyam Sharma NULL; 2520bcc1816SSatyam Sharma } 2530bcc1816SSatyam Sharma 2540bcc1816SSatyam Sharma /* 2550bcc1816SSatyam Sharma * Attribute operations for netconsole_target. 2560bcc1816SSatyam Sharma */ 2570bcc1816SSatyam Sharma 258ea9ed9cfSChristoph Hellwig static ssize_t enabled_show(struct config_item *item, char *buf) 2590bcc1816SSatyam Sharma { 260ea9ed9cfSChristoph Hellwig return snprintf(buf, PAGE_SIZE, "%d\n", to_target(item)->enabled); 2610bcc1816SSatyam Sharma } 2620bcc1816SSatyam Sharma 263ea9ed9cfSChristoph Hellwig static ssize_t extended_show(struct config_item *item, char *buf) 264e2f15f9aSTejun Heo { 265ea9ed9cfSChristoph Hellwig return snprintf(buf, PAGE_SIZE, "%d\n", to_target(item)->extended); 266e2f15f9aSTejun Heo } 267e2f15f9aSTejun Heo 268ea9ed9cfSChristoph Hellwig static ssize_t dev_name_show(struct config_item *item, char *buf) 2690bcc1816SSatyam Sharma { 270ea9ed9cfSChristoph Hellwig return snprintf(buf, PAGE_SIZE, "%s\n", to_target(item)->np.dev_name); 2710bcc1816SSatyam Sharma } 2720bcc1816SSatyam Sharma 273ea9ed9cfSChristoph Hellwig static ssize_t local_port_show(struct config_item *item, char *buf) 2740bcc1816SSatyam Sharma { 275ea9ed9cfSChristoph Hellwig return snprintf(buf, PAGE_SIZE, "%d\n", to_target(item)->np.local_port); 2760bcc1816SSatyam Sharma } 2770bcc1816SSatyam Sharma 278ea9ed9cfSChristoph Hellwig static ssize_t remote_port_show(struct config_item *item, char *buf) 2790bcc1816SSatyam Sharma { 280ea9ed9cfSChristoph Hellwig return snprintf(buf, PAGE_SIZE, "%d\n", to_target(item)->np.remote_port); 2810bcc1816SSatyam Sharma } 2820bcc1816SSatyam Sharma 283ea9ed9cfSChristoph Hellwig static ssize_t local_ip_show(struct config_item *item, char *buf) 2840bcc1816SSatyam Sharma { 285ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item); 286ea9ed9cfSChristoph Hellwig 287b3d936f3SCong Wang if (nt->np.ipv6) 288b3d936f3SCong Wang return snprintf(buf, PAGE_SIZE, "%pI6c\n", &nt->np.local_ip.in6); 289b3d936f3SCong Wang else 290e7557af5SHarvey Harrison return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.local_ip); 2910bcc1816SSatyam Sharma } 2920bcc1816SSatyam Sharma 293ea9ed9cfSChristoph Hellwig static ssize_t remote_ip_show(struct config_item *item, char *buf) 2940bcc1816SSatyam Sharma { 295ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item); 296ea9ed9cfSChristoph Hellwig 297b3d936f3SCong Wang if (nt->np.ipv6) 298b3d936f3SCong Wang return snprintf(buf, PAGE_SIZE, "%pI6c\n", &nt->np.remote_ip.in6); 299b3d936f3SCong Wang else 300e7557af5SHarvey Harrison return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.remote_ip); 3010bcc1816SSatyam Sharma } 3020bcc1816SSatyam Sharma 303ea9ed9cfSChristoph Hellwig static ssize_t local_mac_show(struct config_item *item, char *buf) 3040bcc1816SSatyam Sharma { 305ea9ed9cfSChristoph Hellwig struct net_device *dev = to_target(item)->np.dev; 306e174961cSJohannes Berg static const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 30709538641SStephen Hemminger 308e174961cSJohannes Berg return snprintf(buf, PAGE_SIZE, "%pM\n", dev ? dev->dev_addr : bcast); 3090bcc1816SSatyam Sharma } 3100bcc1816SSatyam Sharma 311ea9ed9cfSChristoph Hellwig static ssize_t remote_mac_show(struct config_item *item, char *buf) 3120bcc1816SSatyam Sharma { 313ea9ed9cfSChristoph Hellwig return snprintf(buf, PAGE_SIZE, "%pM\n", to_target(item)->np.remote_mac); 3140bcc1816SSatyam Sharma } 3150bcc1816SSatyam Sharma 3160bcc1816SSatyam Sharma /* 3170bcc1816SSatyam Sharma * This one is special -- targets created through the configfs interface 3180bcc1816SSatyam Sharma * are not enabled (and the corresponding netpoll activated) by default. 3190bcc1816SSatyam Sharma * The user is expected to set the desired parameters first (which 3200bcc1816SSatyam Sharma * would enable him to dynamically add new netpoll targets for new 3210bcc1816SSatyam Sharma * network interfaces as and when they come up). 3220bcc1816SSatyam Sharma */ 323ea9ed9cfSChristoph Hellwig static ssize_t enabled_store(struct config_item *item, 324ea9ed9cfSChristoph Hellwig const char *buf, size_t count) 3250bcc1816SSatyam Sharma { 326ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item); 32745e526e8SNikolay Aleksandrov unsigned long flags; 32899f823f9SAlexey Dobriyan int enabled; 3290bcc1816SSatyam Sharma int err; 3300bcc1816SSatyam Sharma 331ea9ed9cfSChristoph Hellwig mutex_lock(&dynamic_netconsole_mutex); 33299f823f9SAlexey Dobriyan err = kstrtoint(buf, 10, &enabled); 33399f823f9SAlexey Dobriyan if (err < 0) 334ea9ed9cfSChristoph Hellwig goto out_unlock; 335ea9ed9cfSChristoph Hellwig 336ea9ed9cfSChristoph Hellwig err = -EINVAL; 33799f823f9SAlexey Dobriyan if (enabled < 0 || enabled > 1) 338ea9ed9cfSChristoph Hellwig goto out_unlock; 339698cf1c6STejun Heo if ((bool)enabled == nt->enabled) { 34022ded577SJoe Perches pr_info("network logging has already %s\n", 341d5123480SGao feng nt->enabled ? "started" : "stopped"); 342ea9ed9cfSChristoph Hellwig goto out_unlock; 343d5123480SGao feng } 3440bcc1816SSatyam Sharma 345698cf1c6STejun Heo if (enabled) { /* true */ 346e2f15f9aSTejun Heo if (nt->extended && !(netconsole_ext.flags & CON_ENABLED)) { 347e2f15f9aSTejun Heo netconsole_ext.flags |= CON_ENABLED; 348e2f15f9aSTejun Heo register_console(&netconsole_ext); 349e2f15f9aSTejun Heo } 350e2f15f9aSTejun Heo 3510bcc1816SSatyam Sharma /* 3520bcc1816SSatyam Sharma * Skip netpoll_parse_options() -- all the attributes are 3530bcc1816SSatyam Sharma * already configured via configfs. Just print them out. 3540bcc1816SSatyam Sharma */ 3550bcc1816SSatyam Sharma netpoll_print_options(&nt->np); 3560bcc1816SSatyam Sharma 3570bcc1816SSatyam Sharma err = netpoll_setup(&nt->np); 358c7c6effdSNikolay Aleksandrov if (err) 359ea9ed9cfSChristoph Hellwig goto out_unlock; 3600bcc1816SSatyam Sharma 3614a6a97e2SJoe Perches pr_info("network logging started\n"); 362698cf1c6STejun Heo } else { /* false */ 36345e526e8SNikolay Aleksandrov /* We need to disable the netconsole before cleaning it up 36445e526e8SNikolay Aleksandrov * otherwise we might end up in write_msg() with 365698cf1c6STejun Heo * nt->np.dev == NULL and nt->enabled == true 36645e526e8SNikolay Aleksandrov */ 36745e526e8SNikolay Aleksandrov spin_lock_irqsave(&target_list_lock, flags); 368698cf1c6STejun Heo nt->enabled = false; 36945e526e8SNikolay Aleksandrov spin_unlock_irqrestore(&target_list_lock, flags); 3700bcc1816SSatyam Sharma netpoll_cleanup(&nt->np); 3710bcc1816SSatyam Sharma } 3720bcc1816SSatyam Sharma 3730bcc1816SSatyam Sharma nt->enabled = enabled; 3740bcc1816SSatyam Sharma 375ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex); 3760bcc1816SSatyam Sharma return strnlen(buf, count); 377ea9ed9cfSChristoph Hellwig out_unlock: 378ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex); 379ea9ed9cfSChristoph Hellwig return err; 3800bcc1816SSatyam Sharma } 3810bcc1816SSatyam Sharma 382ea9ed9cfSChristoph Hellwig static ssize_t extended_store(struct config_item *item, const char *buf, 383e2f15f9aSTejun Heo size_t count) 384e2f15f9aSTejun Heo { 385ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item); 386e2f15f9aSTejun Heo int extended; 387e2f15f9aSTejun Heo int err; 388e2f15f9aSTejun Heo 389ea9ed9cfSChristoph Hellwig mutex_lock(&dynamic_netconsole_mutex); 390e2f15f9aSTejun Heo if (nt->enabled) { 391e2f15f9aSTejun Heo pr_err("target (%s) is enabled, disable to update parameters\n", 392e2f15f9aSTejun Heo config_item_name(&nt->item)); 393ea9ed9cfSChristoph Hellwig err = -EINVAL; 394ea9ed9cfSChristoph Hellwig goto out_unlock; 395e2f15f9aSTejun Heo } 396e2f15f9aSTejun Heo 397e2f15f9aSTejun Heo err = kstrtoint(buf, 10, &extended); 398e2f15f9aSTejun Heo if (err < 0) 399ea9ed9cfSChristoph Hellwig goto out_unlock; 400ea9ed9cfSChristoph Hellwig if (extended < 0 || extended > 1) { 401ea9ed9cfSChristoph Hellwig err = -EINVAL; 402ea9ed9cfSChristoph Hellwig goto out_unlock; 403ea9ed9cfSChristoph Hellwig } 404e2f15f9aSTejun Heo 405e2f15f9aSTejun Heo nt->extended = extended; 406e2f15f9aSTejun Heo 407ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex); 408e2f15f9aSTejun Heo return strnlen(buf, count); 409ea9ed9cfSChristoph Hellwig out_unlock: 410ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex); 411ea9ed9cfSChristoph Hellwig return err; 412e2f15f9aSTejun Heo } 413e2f15f9aSTejun Heo 414ea9ed9cfSChristoph Hellwig static ssize_t dev_name_store(struct config_item *item, const char *buf, 4150bcc1816SSatyam Sharma size_t count) 4160bcc1816SSatyam Sharma { 417ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item); 4180bcc1816SSatyam Sharma size_t len; 4190bcc1816SSatyam Sharma 420ea9ed9cfSChristoph Hellwig mutex_lock(&dynamic_netconsole_mutex); 4210bcc1816SSatyam Sharma if (nt->enabled) { 42222ded577SJoe Perches pr_err("target (%s) is enabled, disable to update parameters\n", 4230bcc1816SSatyam Sharma config_item_name(&nt->item)); 424ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex); 4250bcc1816SSatyam Sharma return -EINVAL; 4260bcc1816SSatyam Sharma } 4270bcc1816SSatyam Sharma 4280bcc1816SSatyam Sharma strlcpy(nt->np.dev_name, buf, IFNAMSIZ); 4290bcc1816SSatyam Sharma 4300bcc1816SSatyam Sharma /* Get rid of possible trailing newline from echo(1) */ 4310bcc1816SSatyam Sharma len = strnlen(nt->np.dev_name, IFNAMSIZ); 4320bcc1816SSatyam Sharma if (nt->np.dev_name[len - 1] == '\n') 4330bcc1816SSatyam Sharma nt->np.dev_name[len - 1] = '\0'; 4340bcc1816SSatyam Sharma 435ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex); 4360bcc1816SSatyam Sharma return strnlen(buf, count); 4370bcc1816SSatyam Sharma } 4380bcc1816SSatyam Sharma 439ea9ed9cfSChristoph Hellwig static ssize_t local_port_store(struct config_item *item, const char *buf, 4400bcc1816SSatyam Sharma size_t count) 4410bcc1816SSatyam Sharma { 442ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item); 443ea9ed9cfSChristoph Hellwig int rv = -EINVAL; 4440bcc1816SSatyam Sharma 445ea9ed9cfSChristoph Hellwig mutex_lock(&dynamic_netconsole_mutex); 4460bcc1816SSatyam Sharma if (nt->enabled) { 44722ded577SJoe Perches pr_err("target (%s) is enabled, disable to update parameters\n", 4480bcc1816SSatyam Sharma config_item_name(&nt->item)); 449ea9ed9cfSChristoph Hellwig goto out_unlock; 4500bcc1816SSatyam Sharma } 4510bcc1816SSatyam Sharma 45299f823f9SAlexey Dobriyan rv = kstrtou16(buf, 10, &nt->np.local_port); 45399f823f9SAlexey Dobriyan if (rv < 0) 454ea9ed9cfSChristoph Hellwig goto out_unlock; 455ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex); 4560bcc1816SSatyam Sharma return strnlen(buf, count); 457ea9ed9cfSChristoph Hellwig out_unlock: 458ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex); 459ea9ed9cfSChristoph Hellwig return rv; 4600bcc1816SSatyam Sharma } 4610bcc1816SSatyam Sharma 462ea9ed9cfSChristoph Hellwig static ssize_t remote_port_store(struct config_item *item, 463ea9ed9cfSChristoph Hellwig const char *buf, size_t count) 4640bcc1816SSatyam Sharma { 465ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item); 466ea9ed9cfSChristoph Hellwig int rv = -EINVAL; 4670bcc1816SSatyam Sharma 468ea9ed9cfSChristoph Hellwig mutex_lock(&dynamic_netconsole_mutex); 4690bcc1816SSatyam Sharma if (nt->enabled) { 47022ded577SJoe Perches pr_err("target (%s) is enabled, disable to update parameters\n", 4710bcc1816SSatyam Sharma config_item_name(&nt->item)); 472ea9ed9cfSChristoph Hellwig goto out_unlock; 4730bcc1816SSatyam Sharma } 4740bcc1816SSatyam Sharma 47599f823f9SAlexey Dobriyan rv = kstrtou16(buf, 10, &nt->np.remote_port); 47699f823f9SAlexey Dobriyan if (rv < 0) 477ea9ed9cfSChristoph Hellwig goto out_unlock; 478ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex); 4790bcc1816SSatyam Sharma return strnlen(buf, count); 480ea9ed9cfSChristoph Hellwig out_unlock: 481ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex); 482ea9ed9cfSChristoph Hellwig return rv; 4830bcc1816SSatyam Sharma } 4840bcc1816SSatyam Sharma 485ea9ed9cfSChristoph Hellwig static ssize_t local_ip_store(struct config_item *item, const char *buf, 4860bcc1816SSatyam Sharma size_t count) 4870bcc1816SSatyam Sharma { 488ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item); 489ea9ed9cfSChristoph Hellwig 490ea9ed9cfSChristoph Hellwig mutex_lock(&dynamic_netconsole_mutex); 4910bcc1816SSatyam Sharma if (nt->enabled) { 49222ded577SJoe Perches pr_err("target (%s) is enabled, disable to update parameters\n", 4930bcc1816SSatyam Sharma config_item_name(&nt->item)); 494ea9ed9cfSChristoph Hellwig goto out_unlock; 4950bcc1816SSatyam Sharma } 4960bcc1816SSatyam Sharma 497b3d936f3SCong Wang if (strnchr(buf, count, ':')) { 498b3d936f3SCong Wang const char *end; 499b3d936f3SCong Wang if (in6_pton(buf, count, nt->np.local_ip.in6.s6_addr, -1, &end) > 0) { 500b3d936f3SCong Wang if (*end && *end != '\n') { 50122ded577SJoe Perches pr_err("invalid IPv6 address at: <%c>\n", *end); 502ea9ed9cfSChristoph Hellwig goto out_unlock; 503b3d936f3SCong Wang } 504b3d936f3SCong Wang nt->np.ipv6 = true; 505b3d936f3SCong Wang } else 506ea9ed9cfSChristoph Hellwig goto out_unlock; 507b3d936f3SCong Wang } else { 508b3d936f3SCong Wang if (!nt->np.ipv6) { 509b7394d24SCong Wang nt->np.local_ip.ip = in_aton(buf); 510b3d936f3SCong Wang } else 511ea9ed9cfSChristoph Hellwig goto out_unlock; 512ea9ed9cfSChristoph Hellwig } 513ea9ed9cfSChristoph Hellwig 514ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex); 515ea9ed9cfSChristoph Hellwig return strnlen(buf, count); 516ea9ed9cfSChristoph Hellwig out_unlock: 517ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex); 518b3d936f3SCong Wang return -EINVAL; 519b3d936f3SCong Wang } 5200bcc1816SSatyam Sharma 521ea9ed9cfSChristoph Hellwig static ssize_t remote_ip_store(struct config_item *item, const char *buf, 5220bcc1816SSatyam Sharma size_t count) 5230bcc1816SSatyam Sharma { 524ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item); 525ea9ed9cfSChristoph Hellwig 526ea9ed9cfSChristoph Hellwig mutex_lock(&dynamic_netconsole_mutex); 5270bcc1816SSatyam Sharma if (nt->enabled) { 52822ded577SJoe Perches pr_err("target (%s) is enabled, disable to update parameters\n", 5290bcc1816SSatyam Sharma config_item_name(&nt->item)); 530ea9ed9cfSChristoph Hellwig goto out_unlock; 5310bcc1816SSatyam Sharma } 5320bcc1816SSatyam Sharma 533b3d936f3SCong Wang if (strnchr(buf, count, ':')) { 534b3d936f3SCong Wang const char *end; 535b3d936f3SCong Wang if (in6_pton(buf, count, nt->np.remote_ip.in6.s6_addr, -1, &end) > 0) { 536b3d936f3SCong Wang if (*end && *end != '\n') { 53722ded577SJoe Perches pr_err("invalid IPv6 address at: <%c>\n", *end); 538ea9ed9cfSChristoph Hellwig goto out_unlock; 539b3d936f3SCong Wang } 540b3d936f3SCong Wang nt->np.ipv6 = true; 541b3d936f3SCong Wang } else 542ea9ed9cfSChristoph Hellwig goto out_unlock; 543b3d936f3SCong Wang } else { 544b3d936f3SCong Wang if (!nt->np.ipv6) { 545b7394d24SCong Wang nt->np.remote_ip.ip = in_aton(buf); 546b3d936f3SCong Wang } else 547ea9ed9cfSChristoph Hellwig goto out_unlock; 548ea9ed9cfSChristoph Hellwig } 549ea9ed9cfSChristoph Hellwig 550ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex); 551ea9ed9cfSChristoph Hellwig return strnlen(buf, count); 552ea9ed9cfSChristoph Hellwig out_unlock: 553ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex); 554b3d936f3SCong Wang return -EINVAL; 555b3d936f3SCong Wang } 5560bcc1816SSatyam Sharma 557ea9ed9cfSChristoph Hellwig static ssize_t remote_mac_store(struct config_item *item, const char *buf, 5580bcc1816SSatyam Sharma size_t count) 5590bcc1816SSatyam Sharma { 560ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item); 5610bcc1816SSatyam Sharma u8 remote_mac[ETH_ALEN]; 5620bcc1816SSatyam Sharma 563ea9ed9cfSChristoph Hellwig mutex_lock(&dynamic_netconsole_mutex); 5640bcc1816SSatyam Sharma if (nt->enabled) { 56522ded577SJoe Perches pr_err("target (%s) is enabled, disable to update parameters\n", 5660bcc1816SSatyam Sharma config_item_name(&nt->item)); 567ea9ed9cfSChristoph Hellwig goto out_unlock; 5680bcc1816SSatyam Sharma } 5690bcc1816SSatyam Sharma 5704940fc88SAlexey Dobriyan if (!mac_pton(buf, remote_mac)) 571ea9ed9cfSChristoph Hellwig goto out_unlock; 5724940fc88SAlexey Dobriyan if (buf[3 * ETH_ALEN - 1] && buf[3 * ETH_ALEN - 1] != '\n') 573ea9ed9cfSChristoph Hellwig goto out_unlock; 5740bcc1816SSatyam Sharma memcpy(nt->np.remote_mac, remote_mac, ETH_ALEN); 5750bcc1816SSatyam Sharma 576ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex); 5770bcc1816SSatyam Sharma return strnlen(buf, count); 578ea9ed9cfSChristoph Hellwig out_unlock: 579ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex); 580ea9ed9cfSChristoph Hellwig return -EINVAL; 5810bcc1816SSatyam Sharma } 5820bcc1816SSatyam Sharma 583ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR(, enabled); 584ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR(, extended); 585ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR(, dev_name); 586ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR(, local_port); 587ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR(, remote_port); 588ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR(, local_ip); 589ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR(, remote_ip); 590ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR_RO(, local_mac); 591ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR(, remote_mac); 5920bcc1816SSatyam Sharma 5930bcc1816SSatyam Sharma static struct configfs_attribute *netconsole_target_attrs[] = { 594ea9ed9cfSChristoph Hellwig &attr_enabled, 595ea9ed9cfSChristoph Hellwig &attr_extended, 596ea9ed9cfSChristoph Hellwig &attr_dev_name, 597ea9ed9cfSChristoph Hellwig &attr_local_port, 598ea9ed9cfSChristoph Hellwig &attr_remote_port, 599ea9ed9cfSChristoph Hellwig &attr_local_ip, 600ea9ed9cfSChristoph Hellwig &attr_remote_ip, 601ea9ed9cfSChristoph Hellwig &attr_local_mac, 602ea9ed9cfSChristoph Hellwig &attr_remote_mac, 6030bcc1816SSatyam Sharma NULL, 6040bcc1816SSatyam Sharma }; 6050bcc1816SSatyam Sharma 6060bcc1816SSatyam Sharma /* 6070bcc1816SSatyam Sharma * Item operations and type for netconsole_target. 6080bcc1816SSatyam Sharma */ 6090bcc1816SSatyam Sharma 6100bcc1816SSatyam Sharma static void netconsole_target_release(struct config_item *item) 6110bcc1816SSatyam Sharma { 6120bcc1816SSatyam Sharma kfree(to_target(item)); 6130bcc1816SSatyam Sharma } 6140bcc1816SSatyam Sharma 6150bcc1816SSatyam Sharma static struct configfs_item_operations netconsole_target_item_ops = { 6160bcc1816SSatyam Sharma .release = netconsole_target_release, 6170bcc1816SSatyam Sharma }; 6180bcc1816SSatyam Sharma 6190d4a4406SBhumika Goyal static const struct config_item_type netconsole_target_type = { 6200bcc1816SSatyam Sharma .ct_attrs = netconsole_target_attrs, 6210bcc1816SSatyam Sharma .ct_item_ops = &netconsole_target_item_ops, 6220bcc1816SSatyam Sharma .ct_owner = THIS_MODULE, 6230bcc1816SSatyam Sharma }; 6240bcc1816SSatyam Sharma 6250bcc1816SSatyam Sharma /* 6260bcc1816SSatyam Sharma * Group operations and type for netconsole_subsys. 6270bcc1816SSatyam Sharma */ 6280bcc1816SSatyam Sharma 629f89ab861SJoel Becker static struct config_item *make_netconsole_target(struct config_group *group, 630f89ab861SJoel Becker const char *name) 6310bcc1816SSatyam Sharma { 6320bcc1816SSatyam Sharma unsigned long flags; 6330bcc1816SSatyam Sharma struct netconsole_target *nt; 6340bcc1816SSatyam Sharma 6350bcc1816SSatyam Sharma /* 6360bcc1816SSatyam Sharma * Allocate and initialize with defaults. 637698cf1c6STejun Heo * Target is disabled at creation (!enabled). 6380bcc1816SSatyam Sharma */ 6390bcc1816SSatyam Sharma nt = kzalloc(sizeof(*nt), GFP_KERNEL); 640e404decbSJoe Perches if (!nt) 641a6795e9eSJoel Becker return ERR_PTR(-ENOMEM); 6420bcc1816SSatyam Sharma 6430bcc1816SSatyam Sharma nt->np.name = "netconsole"; 6440bcc1816SSatyam Sharma strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ); 6450bcc1816SSatyam Sharma nt->np.local_port = 6665; 6460bcc1816SSatyam Sharma nt->np.remote_port = 6666; 6471667c942SJoe Perches eth_broadcast_addr(nt->np.remote_mac); 6480bcc1816SSatyam Sharma 6490bcc1816SSatyam Sharma /* Initialize the config_item member */ 6500bcc1816SSatyam Sharma config_item_init_type_name(&nt->item, name, &netconsole_target_type); 6510bcc1816SSatyam Sharma 6520bcc1816SSatyam Sharma /* Adding, but it is disabled */ 6530bcc1816SSatyam Sharma spin_lock_irqsave(&target_list_lock, flags); 6540bcc1816SSatyam Sharma list_add(&nt->list, &target_list); 6550bcc1816SSatyam Sharma spin_unlock_irqrestore(&target_list_lock, flags); 6560bcc1816SSatyam Sharma 657f89ab861SJoel Becker return &nt->item; 6580bcc1816SSatyam Sharma } 6590bcc1816SSatyam Sharma 6600bcc1816SSatyam Sharma static void drop_netconsole_target(struct config_group *group, 6610bcc1816SSatyam Sharma struct config_item *item) 6620bcc1816SSatyam Sharma { 6630bcc1816SSatyam Sharma unsigned long flags; 6640bcc1816SSatyam Sharma struct netconsole_target *nt = to_target(item); 6650bcc1816SSatyam Sharma 6660bcc1816SSatyam Sharma spin_lock_irqsave(&target_list_lock, flags); 6670bcc1816SSatyam Sharma list_del(&nt->list); 6680bcc1816SSatyam Sharma spin_unlock_irqrestore(&target_list_lock, flags); 6690bcc1816SSatyam Sharma 6700bcc1816SSatyam Sharma /* 6710bcc1816SSatyam Sharma * The target may have never been enabled, or was manually disabled 6720bcc1816SSatyam Sharma * before being removed so netpoll may have already been cleaned up. 6730bcc1816SSatyam Sharma */ 6740bcc1816SSatyam Sharma if (nt->enabled) 6750bcc1816SSatyam Sharma netpoll_cleanup(&nt->np); 6760bcc1816SSatyam Sharma 6770bcc1816SSatyam Sharma config_item_put(&nt->item); 6780bcc1816SSatyam Sharma } 6790bcc1816SSatyam Sharma 6800bcc1816SSatyam Sharma static struct configfs_group_operations netconsole_subsys_group_ops = { 6810bcc1816SSatyam Sharma .make_item = make_netconsole_target, 6820bcc1816SSatyam Sharma .drop_item = drop_netconsole_target, 6830bcc1816SSatyam Sharma }; 6840bcc1816SSatyam Sharma 6850d4a4406SBhumika Goyal static const struct config_item_type netconsole_subsys_type = { 6860bcc1816SSatyam Sharma .ct_group_ops = &netconsole_subsys_group_ops, 6870bcc1816SSatyam Sharma .ct_owner = THIS_MODULE, 6880bcc1816SSatyam Sharma }; 6890bcc1816SSatyam Sharma 6900bcc1816SSatyam Sharma /* The netconsole configfs subsystem */ 6910bcc1816SSatyam Sharma static struct configfs_subsystem netconsole_subsys = { 6920bcc1816SSatyam Sharma .su_group = { 6930bcc1816SSatyam Sharma .cg_item = { 6940bcc1816SSatyam Sharma .ci_namebuf = "netconsole", 6950bcc1816SSatyam Sharma .ci_type = &netconsole_subsys_type, 6960bcc1816SSatyam Sharma }, 6970bcc1816SSatyam Sharma }, 6980bcc1816SSatyam Sharma }; 6990bcc1816SSatyam Sharma 7000bcc1816SSatyam Sharma #endif /* CONFIG_NETCONSOLE_DYNAMIC */ 7010bcc1816SSatyam Sharma 70217951f34SSatyam Sharma /* Handle network interface device notifications */ 70317951f34SSatyam Sharma static int netconsole_netdev_event(struct notifier_block *this, 704351638e7SJiri Pirko unsigned long event, void *ptr) 70517951f34SSatyam Sharma { 706b5427c27SSatyam Sharma unsigned long flags; 707b5427c27SSatyam Sharma struct netconsole_target *nt; 708351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 709141dfba3SFerenc Wagner bool stopped = false; 71017951f34SSatyam Sharma 7110e34e931SWANG Cong if (!(event == NETDEV_CHANGENAME || event == NETDEV_UNREGISTER || 712daf9209bSAmerigo Wang event == NETDEV_RELEASE || event == NETDEV_JOIN)) 713b5427c27SSatyam Sharma goto done; 714b5427c27SSatyam Sharma 715b5427c27SSatyam Sharma spin_lock_irqsave(&target_list_lock, flags); 7163f315befSVeaceslav Falico restart: 717b5427c27SSatyam Sharma list_for_each_entry(nt, &target_list, list) { 7180bcc1816SSatyam Sharma netconsole_target_get(nt); 71917951f34SSatyam Sharma if (nt->np.dev == dev) { 72017951f34SSatyam Sharma switch (event) { 72117951f34SSatyam Sharma case NETDEV_CHANGENAME: 72217951f34SSatyam Sharma strlcpy(nt->np.dev_name, dev->name, IFNAMSIZ); 72317951f34SSatyam Sharma break; 724daf9209bSAmerigo Wang case NETDEV_RELEASE: 7258d8fc29dSAmerigo Wang case NETDEV_JOIN: 7262382b15bSBruno Prémont case NETDEV_UNREGISTER: 727c71380ffSNikolay Aleksandrov /* rtnl_lock already held 7283f315befSVeaceslav Falico * we might sleep in __netpoll_cleanup() 7293b410a31SNeil Horman */ 7303f315befSVeaceslav Falico spin_unlock_irqrestore(&target_list_lock, flags); 7317a163bfbSDan Aloni 7323b410a31SNeil Horman __netpoll_cleanup(&nt->np); 7337a163bfbSDan Aloni 7343f315befSVeaceslav Falico spin_lock_irqsave(&target_list_lock, flags); 7353b410a31SNeil Horman dev_put(nt->np.dev); 7363b410a31SNeil Horman nt->np.dev = NULL; 737698cf1c6STejun Heo nt->enabled = false; 738141dfba3SFerenc Wagner stopped = true; 7393f315befSVeaceslav Falico netconsole_target_put(nt); 7403f315befSVeaceslav Falico goto restart; 74117951f34SSatyam Sharma } 74217951f34SSatyam Sharma } 7430bcc1816SSatyam Sharma netconsole_target_put(nt); 744b5427c27SSatyam Sharma } 745b5427c27SSatyam Sharma spin_unlock_irqrestore(&target_list_lock, flags); 7468d8fc29dSAmerigo Wang if (stopped) { 74722ded577SJoe Perches const char *msg = "had an event"; 7488d8fc29dSAmerigo Wang switch (event) { 7498d8fc29dSAmerigo Wang case NETDEV_UNREGISTER: 75022ded577SJoe Perches msg = "unregistered"; 7518d8fc29dSAmerigo Wang break; 752daf9209bSAmerigo Wang case NETDEV_RELEASE: 75322ded577SJoe Perches msg = "released slaves"; 7548d8fc29dSAmerigo Wang break; 7558d8fc29dSAmerigo Wang case NETDEV_JOIN: 75622ded577SJoe Perches msg = "is joining a master device"; 7578d8fc29dSAmerigo Wang break; 7588d8fc29dSAmerigo Wang } 75922ded577SJoe Perches pr_info("network logging stopped on interface %s as it %s\n", 76022ded577SJoe Perches dev->name, msg); 7618d8fc29dSAmerigo Wang } 76217951f34SSatyam Sharma 763b5427c27SSatyam Sharma done: 76417951f34SSatyam Sharma return NOTIFY_DONE; 76517951f34SSatyam Sharma } 76617951f34SSatyam Sharma 76717951f34SSatyam Sharma static struct notifier_block netconsole_netdev_notifier = { 76817951f34SSatyam Sharma .notifier_call = netconsole_netdev_event, 76917951f34SSatyam Sharma }; 77017951f34SSatyam Sharma 771e2f15f9aSTejun Heo /** 772e2f15f9aSTejun Heo * send_ext_msg_udp - send extended log message to target 773e2f15f9aSTejun Heo * @nt: target to send message to 774e2f15f9aSTejun Heo * @msg: extended log message to send 775e2f15f9aSTejun Heo * @msg_len: length of message 776e2f15f9aSTejun Heo * 777e2f15f9aSTejun Heo * Transfer extended log @msg to @nt. If @msg is longer than 778e2f15f9aSTejun Heo * MAX_PRINT_CHUNK, it'll be split and transmitted in multiple chunks with 779e2f15f9aSTejun Heo * ncfrag header field added to identify them. 780e2f15f9aSTejun Heo */ 781e2f15f9aSTejun Heo static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg, 782e2f15f9aSTejun Heo int msg_len) 783e2f15f9aSTejun Heo { 784e2f15f9aSTejun Heo static char buf[MAX_PRINT_CHUNK]; /* protected by target_list_lock */ 785e2f15f9aSTejun Heo const char *header, *body; 786e2f15f9aSTejun Heo int offset = 0; 787e2f15f9aSTejun Heo int header_len, body_len; 788e2f15f9aSTejun Heo 789e2f15f9aSTejun Heo if (msg_len <= MAX_PRINT_CHUNK) { 790e2f15f9aSTejun Heo netpoll_send_udp(&nt->np, msg, msg_len); 791e2f15f9aSTejun Heo return; 792e2f15f9aSTejun Heo } 793e2f15f9aSTejun Heo 794e2f15f9aSTejun Heo /* need to insert extra header fields, detect header and body */ 795e2f15f9aSTejun Heo header = msg; 796e2f15f9aSTejun Heo body = memchr(msg, ';', msg_len); 797e2f15f9aSTejun Heo if (WARN_ON_ONCE(!body)) 798e2f15f9aSTejun Heo return; 799e2f15f9aSTejun Heo 800e2f15f9aSTejun Heo header_len = body - header; 801e2f15f9aSTejun Heo body_len = msg_len - header_len - 1; 802e2f15f9aSTejun Heo body++; 803e2f15f9aSTejun Heo 804e2f15f9aSTejun Heo /* 805e2f15f9aSTejun Heo * Transfer multiple chunks with the following extra header. 806e2f15f9aSTejun Heo * "ncfrag=<byte-offset>/<total-bytes>" 807e2f15f9aSTejun Heo */ 808e2f15f9aSTejun Heo memcpy(buf, header, header_len); 809e2f15f9aSTejun Heo 810e2f15f9aSTejun Heo while (offset < body_len) { 811e2f15f9aSTejun Heo int this_header = header_len; 812e2f15f9aSTejun Heo int this_chunk; 813e2f15f9aSTejun Heo 814e2f15f9aSTejun Heo this_header += scnprintf(buf + this_header, 815e2f15f9aSTejun Heo sizeof(buf) - this_header, 816e2f15f9aSTejun Heo ",ncfrag=%d/%d;", offset, body_len); 817e2f15f9aSTejun Heo 818e2f15f9aSTejun Heo this_chunk = min(body_len - offset, 819e2f15f9aSTejun Heo MAX_PRINT_CHUNK - this_header); 820e2f15f9aSTejun Heo if (WARN_ON_ONCE(this_chunk <= 0)) 821e2f15f9aSTejun Heo return; 822e2f15f9aSTejun Heo 823e2f15f9aSTejun Heo memcpy(buf + this_header, body + offset, this_chunk); 824e2f15f9aSTejun Heo 825e2f15f9aSTejun Heo netpoll_send_udp(&nt->np, buf, this_header + this_chunk); 826e2f15f9aSTejun Heo 827e2f15f9aSTejun Heo offset += this_chunk; 828e2f15f9aSTejun Heo } 829e2f15f9aSTejun Heo } 830e2f15f9aSTejun Heo 831e2f15f9aSTejun Heo static void write_ext_msg(struct console *con, const char *msg, 832e2f15f9aSTejun Heo unsigned int len) 833e2f15f9aSTejun Heo { 834e2f15f9aSTejun Heo struct netconsole_target *nt; 835e2f15f9aSTejun Heo unsigned long flags; 836e2f15f9aSTejun Heo 837e2f15f9aSTejun Heo if ((oops_only && !oops_in_progress) || list_empty(&target_list)) 838e2f15f9aSTejun Heo return; 839e2f15f9aSTejun Heo 840e2f15f9aSTejun Heo spin_lock_irqsave(&target_list_lock, flags); 841e2f15f9aSTejun Heo list_for_each_entry(nt, &target_list, list) 842e2f15f9aSTejun Heo if (nt->extended && nt->enabled && netif_running(nt->np.dev)) 843e2f15f9aSTejun Heo send_ext_msg_udp(nt, msg, len); 844e2f15f9aSTejun Heo spin_unlock_irqrestore(&target_list_lock, flags); 845e2f15f9aSTejun Heo } 846e2f15f9aSTejun Heo 8471da177e4SLinus Torvalds static void write_msg(struct console *con, const char *msg, unsigned int len) 8481da177e4SLinus Torvalds { 8491da177e4SLinus Torvalds int frag, left; 8501da177e4SLinus Torvalds unsigned long flags; 851b5427c27SSatyam Sharma struct netconsole_target *nt; 852b5427c27SSatyam Sharma const char *tmp; 8531da177e4SLinus Torvalds 854c1a60851SAmerigo Wang if (oops_only && !oops_in_progress) 855c1a60851SAmerigo Wang return; 856b5427c27SSatyam Sharma /* Avoid taking lock and disabling interrupts unnecessarily */ 857b5427c27SSatyam Sharma if (list_empty(&target_list)) 858b5427c27SSatyam Sharma return; 859b5427c27SSatyam Sharma 860b5427c27SSatyam Sharma spin_lock_irqsave(&target_list_lock, flags); 861b5427c27SSatyam Sharma list_for_each_entry(nt, &target_list, list) { 862e2f15f9aSTejun Heo if (!nt->extended && nt->enabled && netif_running(nt->np.dev)) { 863b5427c27SSatyam Sharma /* 864b5427c27SSatyam Sharma * We nest this inside the for-each-target loop above 865b5427c27SSatyam Sharma * so that we're able to get as much logging out to 866b5427c27SSatyam Sharma * at least one target if we die inside here, instead 867b5427c27SSatyam Sharma * of unnecessarily keeping all targets in lock-step. 868b5427c27SSatyam Sharma */ 869b5427c27SSatyam Sharma tmp = msg; 8701da177e4SLinus Torvalds for (left = len; left;) { 8711da177e4SLinus Torvalds frag = min(left, MAX_PRINT_CHUNK); 872b5427c27SSatyam Sharma netpoll_send_udp(&nt->np, tmp, frag); 873b5427c27SSatyam Sharma tmp += frag; 8741da177e4SLinus Torvalds left -= frag; 8751da177e4SLinus Torvalds } 8761da177e4SLinus Torvalds } 8770cc120beSSatyam Sharma } 878b5427c27SSatyam Sharma spin_unlock_irqrestore(&target_list_lock, flags); 879b5427c27SSatyam Sharma } 8801da177e4SLinus Torvalds 881e2f15f9aSTejun Heo static struct console netconsole_ext = { 882e2f15f9aSTejun Heo .name = "netcon_ext", 883e2f15f9aSTejun Heo .flags = CON_EXTENDED, /* starts disabled, registered on first use */ 884e2f15f9aSTejun Heo .write = write_ext_msg, 885e2f15f9aSTejun Heo }; 886e2f15f9aSTejun Heo 8871da177e4SLinus Torvalds static struct console netconsole = { 888d938ab44SRandy Dunlap .name = "netcon", 8890517deedSMichael Ellerman .flags = CON_ENABLED, 890d39badf0SSatyam Sharma .write = write_msg, 8911da177e4SLinus Torvalds }; 8921da177e4SLinus Torvalds 893d39badf0SSatyam Sharma static int __init init_netconsole(void) 8941da177e4SLinus Torvalds { 8950bcc1816SSatyam Sharma int err; 896b5427c27SSatyam Sharma struct netconsole_target *nt, *tmp; 897b5427c27SSatyam Sharma unsigned long flags; 898b5427c27SSatyam Sharma char *target_config; 899b5427c27SSatyam Sharma char *input = config; 900b41848b6SStephen Hemminger 9010bcc1816SSatyam Sharma if (strnlen(input, MAX_PARAM_LENGTH)) { 902b5427c27SSatyam Sharma while ((target_config = strsep(&input, ";"))) { 9030bcc1816SSatyam Sharma nt = alloc_param_target(target_config); 904b5427c27SSatyam Sharma if (IS_ERR(nt)) { 905b5427c27SSatyam Sharma err = PTR_ERR(nt); 906b5427c27SSatyam Sharma goto fail; 907b5427c27SSatyam Sharma } 9080517deedSMichael Ellerman /* Dump existing printks when we register */ 909e2f15f9aSTejun Heo if (nt->extended) 910e2f15f9aSTejun Heo netconsole_ext.flags |= CON_PRINTBUFFER | 911e2f15f9aSTejun Heo CON_ENABLED; 912e2f15f9aSTejun Heo else 9130517deedSMichael Ellerman netconsole.flags |= CON_PRINTBUFFER; 9140517deedSMichael Ellerman 915b5427c27SSatyam Sharma spin_lock_irqsave(&target_list_lock, flags); 916b5427c27SSatyam Sharma list_add(&nt->list, &target_list); 917b5427c27SSatyam Sharma spin_unlock_irqrestore(&target_list_lock, flags); 918b5427c27SSatyam Sharma } 9190bcc1816SSatyam Sharma } 9201da177e4SLinus Torvalds 92117951f34SSatyam Sharma err = register_netdevice_notifier(&netconsole_netdev_notifier); 92217951f34SSatyam Sharma if (err) 923b5427c27SSatyam Sharma goto fail; 92417951f34SSatyam Sharma 9250bcc1816SSatyam Sharma err = dynamic_netconsole_init(); 9260bcc1816SSatyam Sharma if (err) 9270bcc1816SSatyam Sharma goto undonotifier; 9280bcc1816SSatyam Sharma 929e2f15f9aSTejun Heo if (netconsole_ext.flags & CON_ENABLED) 930e2f15f9aSTejun Heo register_console(&netconsole_ext); 9311da177e4SLinus Torvalds register_console(&netconsole); 93222ded577SJoe Perches pr_info("network logging started\n"); 933d39badf0SSatyam Sharma 934d39badf0SSatyam Sharma return err; 935b5427c27SSatyam Sharma 9360bcc1816SSatyam Sharma undonotifier: 9370bcc1816SSatyam Sharma unregister_netdevice_notifier(&netconsole_netdev_notifier); 9380bcc1816SSatyam Sharma 939b5427c27SSatyam Sharma fail: 94022ded577SJoe Perches pr_err("cleaning up\n"); 941b5427c27SSatyam Sharma 942b5427c27SSatyam Sharma /* 9430bcc1816SSatyam Sharma * Remove all targets and destroy them (only targets created 9440bcc1816SSatyam Sharma * from the boot/module option exist here). Skipping the list 945b5427c27SSatyam Sharma * lock is safe here, and netpoll_cleanup() will sleep. 946b5427c27SSatyam Sharma */ 947b5427c27SSatyam Sharma list_for_each_entry_safe(nt, tmp, &target_list, list) { 948b5427c27SSatyam Sharma list_del(&nt->list); 9490bcc1816SSatyam Sharma free_param_target(nt); 950b5427c27SSatyam Sharma } 951b5427c27SSatyam Sharma 952b5427c27SSatyam Sharma return err; 9531da177e4SLinus Torvalds } 9541da177e4SLinus Torvalds 955d39badf0SSatyam Sharma static void __exit cleanup_netconsole(void) 9561da177e4SLinus Torvalds { 957b5427c27SSatyam Sharma struct netconsole_target *nt, *tmp; 958df180e36SSatyam Sharma 959e2f15f9aSTejun Heo unregister_console(&netconsole_ext); 9601da177e4SLinus Torvalds unregister_console(&netconsole); 9610bcc1816SSatyam Sharma dynamic_netconsole_exit(); 96217951f34SSatyam Sharma unregister_netdevice_notifier(&netconsole_netdev_notifier); 963b5427c27SSatyam Sharma 964b5427c27SSatyam Sharma /* 9650bcc1816SSatyam Sharma * Targets created via configfs pin references on our module 9660bcc1816SSatyam Sharma * and would first be rmdir(2)'ed from userspace. We reach 9670bcc1816SSatyam Sharma * here only when they are already destroyed, and only those 9680bcc1816SSatyam Sharma * created from the boot/module option are left, so remove and 9690bcc1816SSatyam Sharma * destroy them. Skipping the list lock is safe here, and 9700bcc1816SSatyam Sharma * netpoll_cleanup() will sleep. 971b5427c27SSatyam Sharma */ 972b5427c27SSatyam Sharma list_for_each_entry_safe(nt, tmp, &target_list, list) { 973b5427c27SSatyam Sharma list_del(&nt->list); 9740bcc1816SSatyam Sharma free_param_target(nt); 975b5427c27SSatyam Sharma } 9761da177e4SLinus Torvalds } 9771da177e4SLinus Torvalds 97897c7de05SLin Ming /* 97997c7de05SLin Ming * Use late_initcall to ensure netconsole is 98097c7de05SLin Ming * initialized after network device driver if built-in. 98197c7de05SLin Ming * 98297c7de05SLin Ming * late_initcall() and module_init() are identical if built as module. 98397c7de05SLin Ming */ 98497c7de05SLin Ming late_initcall(init_netconsole); 9851da177e4SLinus Torvalds module_exit(cleanup_netconsole); 986