1de6cc651SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * linux/drivers/net/netconsole.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (C) 2001 Ingo Molnar <mingo@redhat.com>
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * This file contains the implementation of an IRQ-safe, crash-safe
81da177e4SLinus Torvalds * kernel console implementation that outputs kernel messages to the
91da177e4SLinus Torvalds * network.
101da177e4SLinus Torvalds *
111da177e4SLinus Torvalds * Modification history:
121da177e4SLinus Torvalds *
131da177e4SLinus Torvalds * 2001-09-17 started by Ingo Molnar.
141da177e4SLinus Torvalds * 2003-08-11 2.6 port by Matt Mackall
151da177e4SLinus Torvalds * simplified options
161da177e4SLinus Torvalds * generic card hooks
171da177e4SLinus Torvalds * works non-modular
181da177e4SLinus Torvalds * 2003-09-07 rewritten with netpoll api
191da177e4SLinus Torvalds */
201da177e4SLinus Torvalds
211da177e4SLinus Torvalds /****************************************************************
221da177e4SLinus Torvalds *
231da177e4SLinus Torvalds ****************************************************************/
241da177e4SLinus Torvalds
2522ded577SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2622ded577SJoe Perches
271da177e4SLinus Torvalds #include <linux/mm.h>
281da177e4SLinus Torvalds #include <linux/init.h>
291da177e4SLinus Torvalds #include <linux/module.h>
305a0e3ad6STejun Heo #include <linux/slab.h>
311da177e4SLinus Torvalds #include <linux/console.h>
321da177e4SLinus Torvalds #include <linux/moduleparam.h>
334cd5773aSAndy Shevchenko #include <linux/kernel.h>
341da177e4SLinus Torvalds #include <linux/string.h>
351da177e4SLinus Torvalds #include <linux/netpoll.h>
360bcc1816SSatyam Sharma #include <linux/inet.h>
370bcc1816SSatyam Sharma #include <linux/configfs.h>
381667c942SJoe Perches #include <linux/etherdevice.h>
39c62c0a17SBreno Leitao #include <linux/utsname.h>
401da177e4SLinus Torvalds
411da177e4SLinus Torvalds MODULE_AUTHOR("Maintainer: Matt Mackall <mpm@selenic.com>");
421da177e4SLinus Torvalds MODULE_DESCRIPTION("Console driver for network interfaces");
431da177e4SLinus Torvalds MODULE_LICENSE("GPL");
441da177e4SLinus Torvalds
45d39badf0SSatyam Sharma #define MAX_PARAM_LENGTH 256
46d39badf0SSatyam Sharma #define MAX_PRINT_CHUNK 1000
47d39badf0SSatyam Sharma
48d39badf0SSatyam Sharma static char config[MAX_PARAM_LENGTH];
49d39badf0SSatyam Sharma module_param_string(netconsole, config, MAX_PARAM_LENGTH, 0);
5061a2d07dSNiels de Vos MODULE_PARM_DESC(netconsole, " netconsole=[src-port]@[src-ip]/[dev],[tgt-port]@<tgt-ip>/[tgt-macaddr]");
511da177e4SLinus Torvalds
52c1a60851SAmerigo Wang static bool oops_only = false;
53c1a60851SAmerigo Wang module_param(oops_only, bool, 0600);
54c1a60851SAmerigo Wang MODULE_PARM_DESC(oops_only, "Only log oops messages");
55c1a60851SAmerigo Wang
56d2b60881SSatyam Sharma #ifndef MODULE
option_setup(char * opt)57d2b60881SSatyam Sharma static int __init option_setup(char *opt)
58d2b60881SSatyam Sharma {
59fb3ceec1SWolfram Sang strscpy(config, opt, MAX_PARAM_LENGTH);
60d2b60881SSatyam Sharma return 1;
61d2b60881SSatyam Sharma }
62d2b60881SSatyam Sharma __setup("netconsole=", option_setup);
63d2b60881SSatyam Sharma #endif /* MODULE */
64d2b60881SSatyam Sharma
65b5427c27SSatyam Sharma /* Linked list of all configured targets */
66b5427c27SSatyam Sharma static LIST_HEAD(target_list);
67b5427c27SSatyam Sharma
68b5427c27SSatyam Sharma /* This needs to be a spinlock because write_msg() cannot sleep */
69b5427c27SSatyam Sharma static DEFINE_SPINLOCK(target_list_lock);
70b5427c27SSatyam Sharma
71e2f15f9aSTejun Heo /*
72e2f15f9aSTejun Heo * Console driver for extended netconsoles. Registered on the first use to
73e2f15f9aSTejun Heo * avoid unnecessarily enabling ext message formatting.
74e2f15f9aSTejun Heo */
75e2f15f9aSTejun Heo static struct console netconsole_ext;
76e2f15f9aSTejun Heo
77df180e36SSatyam Sharma /**
78df180e36SSatyam Sharma * struct netconsole_target - Represents a configured netconsole target.
79b5427c27SSatyam Sharma * @list: Links this target into the target_list.
800bcc1816SSatyam Sharma * @item: Links us into the configfs subsystem hierarchy.
810bcc1816SSatyam Sharma * @enabled: On / off knob to enable / disable target.
820bcc1816SSatyam Sharma * Visible from userspace (read-write).
830bcc1816SSatyam Sharma * We maintain a strict 1:1 correspondence between this and
840bcc1816SSatyam Sharma * whether the corresponding netpoll is active or inactive.
850bcc1816SSatyam Sharma * Also, other parameters of a target may be modified at
860bcc1816SSatyam Sharma * runtime only when it is disabled (enabled == 0).
87a8eb1a00SLee Jones * @extended: Denotes whether console is extended or not.
88c62c0a17SBreno Leitao * @release: Denotes whether kernel release version should be prepended
89c62c0a17SBreno Leitao * to the message. Depends on extended console.
90df180e36SSatyam Sharma * @np: The netpoll structure for this target.
910bcc1816SSatyam Sharma * Contains the other userspace visible parameters:
920bcc1816SSatyam Sharma * dev_name (read-write)
930bcc1816SSatyam Sharma * local_port (read-write)
940bcc1816SSatyam Sharma * remote_port (read-write)
950bcc1816SSatyam Sharma * local_ip (read-write)
960bcc1816SSatyam Sharma * remote_ip (read-write)
970bcc1816SSatyam Sharma * local_mac (read-only)
980bcc1816SSatyam Sharma * remote_mac (read-write)
99df180e36SSatyam Sharma */
100df180e36SSatyam Sharma struct netconsole_target {
101b5427c27SSatyam Sharma struct list_head list;
1020bcc1816SSatyam Sharma #ifdef CONFIG_NETCONSOLE_DYNAMIC
1030bcc1816SSatyam Sharma struct config_item item;
1040bcc1816SSatyam Sharma #endif
105698cf1c6STejun Heo bool enabled;
106e2f15f9aSTejun Heo bool extended;
107c62c0a17SBreno Leitao bool release;
108df180e36SSatyam Sharma struct netpoll np;
109df180e36SSatyam Sharma };
110df180e36SSatyam Sharma
1110bcc1816SSatyam Sharma #ifdef CONFIG_NETCONSOLE_DYNAMIC
1120bcc1816SSatyam Sharma
1130bcc1816SSatyam Sharma static struct configfs_subsystem netconsole_subsys;
114369e5a88STejun Heo static DEFINE_MUTEX(dynamic_netconsole_mutex);
1150bcc1816SSatyam Sharma
dynamic_netconsole_init(void)1160bcc1816SSatyam Sharma static int __init dynamic_netconsole_init(void)
1170bcc1816SSatyam Sharma {
1180bcc1816SSatyam Sharma config_group_init(&netconsole_subsys.su_group);
1190bcc1816SSatyam Sharma mutex_init(&netconsole_subsys.su_mutex);
1200bcc1816SSatyam Sharma return configfs_register_subsystem(&netconsole_subsys);
1210bcc1816SSatyam Sharma }
1220bcc1816SSatyam Sharma
dynamic_netconsole_exit(void)1230bcc1816SSatyam Sharma static void __exit dynamic_netconsole_exit(void)
1240bcc1816SSatyam Sharma {
1250bcc1816SSatyam Sharma configfs_unregister_subsystem(&netconsole_subsys);
1260bcc1816SSatyam Sharma }
1270bcc1816SSatyam Sharma
1280bcc1816SSatyam Sharma /*
1290bcc1816SSatyam Sharma * Targets that were created by parsing the boot/module option string
1300bcc1816SSatyam Sharma * do not exist in the configfs hierarchy (and have NULL names) and will
1310bcc1816SSatyam Sharma * never go away, so make these a no-op for them.
1320bcc1816SSatyam Sharma */
netconsole_target_get(struct netconsole_target * nt)1330bcc1816SSatyam Sharma static void netconsole_target_get(struct netconsole_target *nt)
1340bcc1816SSatyam Sharma {
1350bcc1816SSatyam Sharma if (config_item_name(&nt->item))
1360bcc1816SSatyam Sharma config_item_get(&nt->item);
1370bcc1816SSatyam Sharma }
1380bcc1816SSatyam Sharma
netconsole_target_put(struct netconsole_target * nt)1390bcc1816SSatyam Sharma static void netconsole_target_put(struct netconsole_target *nt)
1400bcc1816SSatyam Sharma {
1410bcc1816SSatyam Sharma if (config_item_name(&nt->item))
1420bcc1816SSatyam Sharma config_item_put(&nt->item);
1430bcc1816SSatyam Sharma }
1440bcc1816SSatyam Sharma
1450bcc1816SSatyam Sharma #else /* !CONFIG_NETCONSOLE_DYNAMIC */
1460bcc1816SSatyam Sharma
dynamic_netconsole_init(void)1470bcc1816SSatyam Sharma static int __init dynamic_netconsole_init(void)
1480bcc1816SSatyam Sharma {
1490bcc1816SSatyam Sharma return 0;
1500bcc1816SSatyam Sharma }
1510bcc1816SSatyam Sharma
dynamic_netconsole_exit(void)1520bcc1816SSatyam Sharma static void __exit dynamic_netconsole_exit(void)
1530bcc1816SSatyam Sharma {
1540bcc1816SSatyam Sharma }
1550bcc1816SSatyam Sharma
1560bcc1816SSatyam Sharma /*
1570bcc1816SSatyam Sharma * No danger of targets going away from under us when dynamic
1580bcc1816SSatyam Sharma * reconfigurability is off.
1590bcc1816SSatyam Sharma */
netconsole_target_get(struct netconsole_target * nt)1600bcc1816SSatyam Sharma static void netconsole_target_get(struct netconsole_target *nt)
1610bcc1816SSatyam Sharma {
1620bcc1816SSatyam Sharma }
1630bcc1816SSatyam Sharma
netconsole_target_put(struct netconsole_target * nt)1640bcc1816SSatyam Sharma static void netconsole_target_put(struct netconsole_target *nt)
1650bcc1816SSatyam Sharma {
1660bcc1816SSatyam Sharma }
1670bcc1816SSatyam Sharma
1680bcc1816SSatyam Sharma #endif /* CONFIG_NETCONSOLE_DYNAMIC */
1690bcc1816SSatyam Sharma
170b0a9e2c9SBreno Leitao /* Allocate and initialize with defaults.
1710bcc1816SSatyam Sharma * Note that these targets get their config_item fields zeroed-out.
1720bcc1816SSatyam Sharma */
alloc_and_init(void)173b0a9e2c9SBreno Leitao static struct netconsole_target *alloc_and_init(void)
174b0a9e2c9SBreno Leitao {
175b0a9e2c9SBreno Leitao struct netconsole_target *nt;
176b0a9e2c9SBreno Leitao
177b5427c27SSatyam Sharma nt = kzalloc(sizeof(*nt), GFP_KERNEL);
178e404decbSJoe Perches if (!nt)
179b0a9e2c9SBreno Leitao return nt;
180b5427c27SSatyam Sharma
181fad361a2SBreno Leitao if (IS_ENABLED(CONFIG_NETCONSOLE_EXTENDED_LOG))
182fad361a2SBreno Leitao nt->extended = true;
183fad361a2SBreno Leitao if (IS_ENABLED(CONFIG_NETCONSOLE_PREPEND_RELEASE))
184fad361a2SBreno Leitao nt->release = true;
185fad361a2SBreno Leitao
186b5427c27SSatyam Sharma nt->np.name = "netconsole";
187fb3ceec1SWolfram Sang strscpy(nt->np.dev_name, "eth0", IFNAMSIZ);
188b5427c27SSatyam Sharma nt->np.local_port = 6665;
189b5427c27SSatyam Sharma nt->np.remote_port = 6666;
1901667c942SJoe Perches eth_broadcast_addr(nt->np.remote_mac);
191b5427c27SSatyam Sharma
192b0a9e2c9SBreno Leitao return nt;
193b0a9e2c9SBreno Leitao }
194b0a9e2c9SBreno Leitao
195b0a9e2c9SBreno Leitao /* Allocate new target (from boot/module param) and setup netpoll for it */
alloc_param_target(char * target_config)196b0a9e2c9SBreno Leitao static struct netconsole_target *alloc_param_target(char *target_config)
197b0a9e2c9SBreno Leitao {
198b0a9e2c9SBreno Leitao struct netconsole_target *nt;
199b0a9e2c9SBreno Leitao int err;
200b0a9e2c9SBreno Leitao
201b0a9e2c9SBreno Leitao nt = alloc_and_init();
202b0a9e2c9SBreno Leitao if (!nt) {
203b0a9e2c9SBreno Leitao err = -ENOMEM;
204b0a9e2c9SBreno Leitao goto fail;
205b0a9e2c9SBreno Leitao }
206b0a9e2c9SBreno Leitao
207e2f15f9aSTejun Heo if (*target_config == '+') {
208e2f15f9aSTejun Heo nt->extended = true;
209e2f15f9aSTejun Heo target_config++;
210e2f15f9aSTejun Heo }
211e2f15f9aSTejun Heo
212c62c0a17SBreno Leitao if (*target_config == 'r') {
213c62c0a17SBreno Leitao if (!nt->extended) {
214c62c0a17SBreno Leitao pr_err("Netconsole configuration error. Release feature requires extended log message");
215b0a9e2c9SBreno Leitao err = -EINVAL;
216c62c0a17SBreno Leitao goto fail;
217c62c0a17SBreno Leitao }
218c62c0a17SBreno Leitao nt->release = true;
219c62c0a17SBreno Leitao target_config++;
220c62c0a17SBreno Leitao }
221c62c0a17SBreno Leitao
222b5427c27SSatyam Sharma /* Parse parameters and setup netpoll */
223b5427c27SSatyam Sharma err = netpoll_parse_options(&nt->np, target_config);
224b5427c27SSatyam Sharma if (err)
225b5427c27SSatyam Sharma goto fail;
226b5427c27SSatyam Sharma
227b5427c27SSatyam Sharma err = netpoll_setup(&nt->np);
228b5427c27SSatyam Sharma if (err)
229b5427c27SSatyam Sharma goto fail;
230b5427c27SSatyam Sharma
231698cf1c6STejun Heo nt->enabled = true;
2320bcc1816SSatyam Sharma
233b5427c27SSatyam Sharma return nt;
234b5427c27SSatyam Sharma
235b5427c27SSatyam Sharma fail:
236b5427c27SSatyam Sharma kfree(nt);
237b5427c27SSatyam Sharma return ERR_PTR(err);
238b5427c27SSatyam Sharma }
239b5427c27SSatyam Sharma
2400bcc1816SSatyam Sharma /* Cleanup netpoll for given target (from boot/module param) and free it */
free_param_target(struct netconsole_target * nt)2410bcc1816SSatyam Sharma static void free_param_target(struct netconsole_target *nt)
242b5427c27SSatyam Sharma {
243b5427c27SSatyam Sharma netpoll_cleanup(&nt->np);
244b5427c27SSatyam Sharma kfree(nt);
245b5427c27SSatyam Sharma }
2461da177e4SLinus Torvalds
2470bcc1816SSatyam Sharma #ifdef CONFIG_NETCONSOLE_DYNAMIC
2480bcc1816SSatyam Sharma
2490bcc1816SSatyam Sharma /*
2500bcc1816SSatyam Sharma * Our subsystem hierarchy is:
2510bcc1816SSatyam Sharma *
2520bcc1816SSatyam Sharma * /sys/kernel/config/netconsole/
2530bcc1816SSatyam Sharma * |
2540bcc1816SSatyam Sharma * <target>/
2550bcc1816SSatyam Sharma * | enabled
256c62c0a17SBreno Leitao * | release
2570bcc1816SSatyam Sharma * | dev_name
2580bcc1816SSatyam Sharma * | local_port
2590bcc1816SSatyam Sharma * | remote_port
2600bcc1816SSatyam Sharma * | local_ip
2610bcc1816SSatyam Sharma * | remote_ip
2620bcc1816SSatyam Sharma * | local_mac
2630bcc1816SSatyam Sharma * | remote_mac
2640bcc1816SSatyam Sharma * |
2650bcc1816SSatyam Sharma * <target>/...
2660bcc1816SSatyam Sharma */
2670bcc1816SSatyam Sharma
to_target(struct config_item * item)2680bcc1816SSatyam Sharma static struct netconsole_target *to_target(struct config_item *item)
2690bcc1816SSatyam Sharma {
2700bcc1816SSatyam Sharma return item ?
2710bcc1816SSatyam Sharma container_of(item, struct netconsole_target, item) :
2720bcc1816SSatyam Sharma NULL;
2730bcc1816SSatyam Sharma }
2740bcc1816SSatyam Sharma
2750bcc1816SSatyam Sharma /*
2760bcc1816SSatyam Sharma * Attribute operations for netconsole_target.
2770bcc1816SSatyam Sharma */
2780bcc1816SSatyam Sharma
enabled_show(struct config_item * item,char * buf)279ea9ed9cfSChristoph Hellwig static ssize_t enabled_show(struct config_item *item, char *buf)
2800bcc1816SSatyam Sharma {
2819f64b6e4SBreno Leitao return sysfs_emit(buf, "%d\n", to_target(item)->enabled);
2820bcc1816SSatyam Sharma }
2830bcc1816SSatyam Sharma
extended_show(struct config_item * item,char * buf)284ea9ed9cfSChristoph Hellwig static ssize_t extended_show(struct config_item *item, char *buf)
285e2f15f9aSTejun Heo {
2869f64b6e4SBreno Leitao return sysfs_emit(buf, "%d\n", to_target(item)->extended);
287e2f15f9aSTejun Heo }
288e2f15f9aSTejun Heo
release_show(struct config_item * item,char * buf)289c62c0a17SBreno Leitao static ssize_t release_show(struct config_item *item, char *buf)
290c62c0a17SBreno Leitao {
2919f64b6e4SBreno Leitao return sysfs_emit(buf, "%d\n", to_target(item)->release);
292c62c0a17SBreno Leitao }
293c62c0a17SBreno Leitao
dev_name_show(struct config_item * item,char * buf)294ea9ed9cfSChristoph Hellwig static ssize_t dev_name_show(struct config_item *item, char *buf)
2950bcc1816SSatyam Sharma {
2969f64b6e4SBreno Leitao return sysfs_emit(buf, "%s\n", to_target(item)->np.dev_name);
2970bcc1816SSatyam Sharma }
2980bcc1816SSatyam Sharma
local_port_show(struct config_item * item,char * buf)299ea9ed9cfSChristoph Hellwig static ssize_t local_port_show(struct config_item *item, char *buf)
3000bcc1816SSatyam Sharma {
3019f64b6e4SBreno Leitao return sysfs_emit(buf, "%d\n", to_target(item)->np.local_port);
3020bcc1816SSatyam Sharma }
3030bcc1816SSatyam Sharma
remote_port_show(struct config_item * item,char * buf)304ea9ed9cfSChristoph Hellwig static ssize_t remote_port_show(struct config_item *item, char *buf)
3050bcc1816SSatyam Sharma {
3069f64b6e4SBreno Leitao return sysfs_emit(buf, "%d\n", to_target(item)->np.remote_port);
3070bcc1816SSatyam Sharma }
3080bcc1816SSatyam Sharma
local_ip_show(struct config_item * item,char * buf)309ea9ed9cfSChristoph Hellwig static ssize_t local_ip_show(struct config_item *item, char *buf)
3100bcc1816SSatyam Sharma {
311ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item);
312ea9ed9cfSChristoph Hellwig
313b3d936f3SCong Wang if (nt->np.ipv6)
3149f64b6e4SBreno Leitao return sysfs_emit(buf, "%pI6c\n", &nt->np.local_ip.in6);
315b3d936f3SCong Wang else
3169f64b6e4SBreno Leitao return sysfs_emit(buf, "%pI4\n", &nt->np.local_ip);
3170bcc1816SSatyam Sharma }
3180bcc1816SSatyam Sharma
remote_ip_show(struct config_item * item,char * buf)319ea9ed9cfSChristoph Hellwig static ssize_t remote_ip_show(struct config_item *item, char *buf)
3200bcc1816SSatyam Sharma {
321ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item);
322ea9ed9cfSChristoph Hellwig
323b3d936f3SCong Wang if (nt->np.ipv6)
3249f64b6e4SBreno Leitao return sysfs_emit(buf, "%pI6c\n", &nt->np.remote_ip.in6);
325b3d936f3SCong Wang else
3269f64b6e4SBreno Leitao return sysfs_emit(buf, "%pI4\n", &nt->np.remote_ip);
3270bcc1816SSatyam Sharma }
3280bcc1816SSatyam Sharma
local_mac_show(struct config_item * item,char * buf)329ea9ed9cfSChristoph Hellwig static ssize_t local_mac_show(struct config_item *item, char *buf)
3300bcc1816SSatyam Sharma {
331ea9ed9cfSChristoph Hellwig struct net_device *dev = to_target(item)->np.dev;
332e174961cSJohannes Berg static const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
33309538641SStephen Hemminger
3349f64b6e4SBreno Leitao return sysfs_emit(buf, "%pM\n", dev ? dev->dev_addr : bcast);
3350bcc1816SSatyam Sharma }
3360bcc1816SSatyam Sharma
remote_mac_show(struct config_item * item,char * buf)337ea9ed9cfSChristoph Hellwig static ssize_t remote_mac_show(struct config_item *item, char *buf)
3380bcc1816SSatyam Sharma {
3399f64b6e4SBreno Leitao return sysfs_emit(buf, "%pM\n", to_target(item)->np.remote_mac);
3400bcc1816SSatyam Sharma }
3410bcc1816SSatyam Sharma
3420bcc1816SSatyam Sharma /*
3430bcc1816SSatyam Sharma * This one is special -- targets created through the configfs interface
3440bcc1816SSatyam Sharma * are not enabled (and the corresponding netpoll activated) by default.
3450bcc1816SSatyam Sharma * The user is expected to set the desired parameters first (which
3460bcc1816SSatyam Sharma * would enable him to dynamically add new netpoll targets for new
3470bcc1816SSatyam Sharma * network interfaces as and when they come up).
3480bcc1816SSatyam Sharma */
enabled_store(struct config_item * item,const char * buf,size_t count)349ea9ed9cfSChristoph Hellwig static ssize_t enabled_store(struct config_item *item,
350ea9ed9cfSChristoph Hellwig const char *buf, size_t count)
3510bcc1816SSatyam Sharma {
352ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item);
35345e526e8SNikolay Aleksandrov unsigned long flags;
354004a04b9SBreno Leitao bool enabled;
3550bcc1816SSatyam Sharma int err;
3560bcc1816SSatyam Sharma
357ea9ed9cfSChristoph Hellwig mutex_lock(&dynamic_netconsole_mutex);
358004a04b9SBreno Leitao err = kstrtobool(buf, &enabled);
359004a04b9SBreno Leitao if (err)
360ea9ed9cfSChristoph Hellwig goto out_unlock;
361ea9ed9cfSChristoph Hellwig
362ea9ed9cfSChristoph Hellwig err = -EINVAL;
363698cf1c6STejun Heo if ((bool)enabled == nt->enabled) {
36422ded577SJoe Perches pr_info("network logging has already %s\n",
365d5123480SGao feng nt->enabled ? "started" : "stopped");
366ea9ed9cfSChristoph Hellwig goto out_unlock;
367d5123480SGao feng }
3680bcc1816SSatyam Sharma
369698cf1c6STejun Heo if (enabled) { /* true */
370c62c0a17SBreno Leitao if (nt->release && !nt->extended) {
371c62c0a17SBreno Leitao pr_err("Not enabling netconsole. Release feature requires extended log message");
372c62c0a17SBreno Leitao goto out_unlock;
373c62c0a17SBreno Leitao }
374c62c0a17SBreno Leitao
3752c6b4b70SJohn Ogness if (nt->extended && !console_is_registered(&netconsole_ext))
376e2f15f9aSTejun Heo register_console(&netconsole_ext);
377e2f15f9aSTejun Heo
3780bcc1816SSatyam Sharma /*
3790bcc1816SSatyam Sharma * Skip netpoll_parse_options() -- all the attributes are
3800bcc1816SSatyam Sharma * already configured via configfs. Just print them out.
3810bcc1816SSatyam Sharma */
3820bcc1816SSatyam Sharma netpoll_print_options(&nt->np);
3830bcc1816SSatyam Sharma
3840bcc1816SSatyam Sharma err = netpoll_setup(&nt->np);
385c7c6effdSNikolay Aleksandrov if (err)
386ea9ed9cfSChristoph Hellwig goto out_unlock;
3870bcc1816SSatyam Sharma
3884a6a97e2SJoe Perches pr_info("network logging started\n");
389698cf1c6STejun Heo } else { /* false */
39045e526e8SNikolay Aleksandrov /* We need to disable the netconsole before cleaning it up
39145e526e8SNikolay Aleksandrov * otherwise we might end up in write_msg() with
392698cf1c6STejun Heo * nt->np.dev == NULL and nt->enabled == true
39345e526e8SNikolay Aleksandrov */
39445e526e8SNikolay Aleksandrov spin_lock_irqsave(&target_list_lock, flags);
395698cf1c6STejun Heo nt->enabled = false;
39645e526e8SNikolay Aleksandrov spin_unlock_irqrestore(&target_list_lock, flags);
3970bcc1816SSatyam Sharma netpoll_cleanup(&nt->np);
3980bcc1816SSatyam Sharma }
3990bcc1816SSatyam Sharma
4000bcc1816SSatyam Sharma nt->enabled = enabled;
4010bcc1816SSatyam Sharma
402ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex);
4030bcc1816SSatyam Sharma return strnlen(buf, count);
404ea9ed9cfSChristoph Hellwig out_unlock:
405ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex);
406ea9ed9cfSChristoph Hellwig return err;
4070bcc1816SSatyam Sharma }
4080bcc1816SSatyam Sharma
release_store(struct config_item * item,const char * buf,size_t count)409c62c0a17SBreno Leitao static ssize_t release_store(struct config_item *item, const char *buf,
410c62c0a17SBreno Leitao size_t count)
411c62c0a17SBreno Leitao {
412c62c0a17SBreno Leitao struct netconsole_target *nt = to_target(item);
413004a04b9SBreno Leitao bool release;
414c62c0a17SBreno Leitao int err;
415c62c0a17SBreno Leitao
416c62c0a17SBreno Leitao mutex_lock(&dynamic_netconsole_mutex);
417c62c0a17SBreno Leitao if (nt->enabled) {
418c62c0a17SBreno Leitao pr_err("target (%s) is enabled, disable to update parameters\n",
419c62c0a17SBreno Leitao config_item_name(&nt->item));
420c62c0a17SBreno Leitao err = -EINVAL;
421c62c0a17SBreno Leitao goto out_unlock;
422c62c0a17SBreno Leitao }
423c62c0a17SBreno Leitao
424004a04b9SBreno Leitao err = kstrtobool(buf, &release);
425004a04b9SBreno Leitao if (err)
426c62c0a17SBreno Leitao goto out_unlock;
427c62c0a17SBreno Leitao
428c62c0a17SBreno Leitao nt->release = release;
429c62c0a17SBreno Leitao
430c62c0a17SBreno Leitao mutex_unlock(&dynamic_netconsole_mutex);
431c62c0a17SBreno Leitao return strnlen(buf, count);
432c62c0a17SBreno Leitao out_unlock:
433c62c0a17SBreno Leitao mutex_unlock(&dynamic_netconsole_mutex);
434c62c0a17SBreno Leitao return err;
435c62c0a17SBreno Leitao }
436c62c0a17SBreno Leitao
extended_store(struct config_item * item,const char * buf,size_t count)437ea9ed9cfSChristoph Hellwig static ssize_t extended_store(struct config_item *item, const char *buf,
438e2f15f9aSTejun Heo size_t count)
439e2f15f9aSTejun Heo {
440ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item);
441004a04b9SBreno Leitao bool extended;
442e2f15f9aSTejun Heo int err;
443e2f15f9aSTejun Heo
444ea9ed9cfSChristoph Hellwig mutex_lock(&dynamic_netconsole_mutex);
445e2f15f9aSTejun Heo if (nt->enabled) {
446e2f15f9aSTejun Heo pr_err("target (%s) is enabled, disable to update parameters\n",
447e2f15f9aSTejun Heo config_item_name(&nt->item));
448ea9ed9cfSChristoph Hellwig err = -EINVAL;
449ea9ed9cfSChristoph Hellwig goto out_unlock;
450e2f15f9aSTejun Heo }
451e2f15f9aSTejun Heo
452004a04b9SBreno Leitao err = kstrtobool(buf, &extended);
453004a04b9SBreno Leitao if (err)
454ea9ed9cfSChristoph Hellwig goto out_unlock;
455e2f15f9aSTejun Heo
456e2f15f9aSTejun Heo nt->extended = extended;
457e2f15f9aSTejun Heo
458ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex);
459e2f15f9aSTejun Heo return strnlen(buf, count);
460ea9ed9cfSChristoph Hellwig out_unlock:
461ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex);
462ea9ed9cfSChristoph Hellwig return err;
463e2f15f9aSTejun Heo }
464e2f15f9aSTejun Heo
dev_name_store(struct config_item * item,const char * buf,size_t count)465ea9ed9cfSChristoph Hellwig static ssize_t dev_name_store(struct config_item *item, const char *buf,
4660bcc1816SSatyam Sharma size_t count)
4670bcc1816SSatyam Sharma {
468ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item);
4690bcc1816SSatyam Sharma size_t len;
4700bcc1816SSatyam Sharma
471ea9ed9cfSChristoph Hellwig mutex_lock(&dynamic_netconsole_mutex);
4720bcc1816SSatyam Sharma if (nt->enabled) {
47322ded577SJoe Perches pr_err("target (%s) is enabled, disable to update parameters\n",
4740bcc1816SSatyam Sharma config_item_name(&nt->item));
475ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex);
4760bcc1816SSatyam Sharma return -EINVAL;
4770bcc1816SSatyam Sharma }
4780bcc1816SSatyam Sharma
479fb3ceec1SWolfram Sang strscpy(nt->np.dev_name, buf, IFNAMSIZ);
4800bcc1816SSatyam Sharma
4810bcc1816SSatyam Sharma /* Get rid of possible trailing newline from echo(1) */
4820bcc1816SSatyam Sharma len = strnlen(nt->np.dev_name, IFNAMSIZ);
4830bcc1816SSatyam Sharma if (nt->np.dev_name[len - 1] == '\n')
4840bcc1816SSatyam Sharma nt->np.dev_name[len - 1] = '\0';
4850bcc1816SSatyam Sharma
486ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex);
4870bcc1816SSatyam Sharma return strnlen(buf, count);
4880bcc1816SSatyam Sharma }
4890bcc1816SSatyam Sharma
local_port_store(struct config_item * item,const char * buf,size_t count)490ea9ed9cfSChristoph Hellwig static ssize_t local_port_store(struct config_item *item, const char *buf,
4910bcc1816SSatyam Sharma size_t count)
4920bcc1816SSatyam Sharma {
493ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item);
494ea9ed9cfSChristoph Hellwig int rv = -EINVAL;
4950bcc1816SSatyam Sharma
496ea9ed9cfSChristoph Hellwig mutex_lock(&dynamic_netconsole_mutex);
4970bcc1816SSatyam Sharma if (nt->enabled) {
49822ded577SJoe Perches pr_err("target (%s) is enabled, disable to update parameters\n",
4990bcc1816SSatyam Sharma config_item_name(&nt->item));
500ea9ed9cfSChristoph Hellwig goto out_unlock;
5010bcc1816SSatyam Sharma }
5020bcc1816SSatyam Sharma
50399f823f9SAlexey Dobriyan rv = kstrtou16(buf, 10, &nt->np.local_port);
50499f823f9SAlexey Dobriyan if (rv < 0)
505ea9ed9cfSChristoph Hellwig goto out_unlock;
506ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex);
5070bcc1816SSatyam Sharma return strnlen(buf, count);
508ea9ed9cfSChristoph Hellwig out_unlock:
509ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex);
510ea9ed9cfSChristoph Hellwig return rv;
5110bcc1816SSatyam Sharma }
5120bcc1816SSatyam Sharma
remote_port_store(struct config_item * item,const char * buf,size_t count)513ea9ed9cfSChristoph Hellwig static ssize_t remote_port_store(struct config_item *item,
514ea9ed9cfSChristoph Hellwig const char *buf, size_t count)
5150bcc1816SSatyam Sharma {
516ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item);
517ea9ed9cfSChristoph Hellwig int rv = -EINVAL;
5180bcc1816SSatyam Sharma
519ea9ed9cfSChristoph Hellwig mutex_lock(&dynamic_netconsole_mutex);
5200bcc1816SSatyam Sharma if (nt->enabled) {
52122ded577SJoe Perches pr_err("target (%s) is enabled, disable to update parameters\n",
5220bcc1816SSatyam Sharma config_item_name(&nt->item));
523ea9ed9cfSChristoph Hellwig goto out_unlock;
5240bcc1816SSatyam Sharma }
5250bcc1816SSatyam Sharma
52699f823f9SAlexey Dobriyan rv = kstrtou16(buf, 10, &nt->np.remote_port);
52799f823f9SAlexey Dobriyan if (rv < 0)
528ea9ed9cfSChristoph Hellwig goto out_unlock;
529ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex);
5300bcc1816SSatyam Sharma return strnlen(buf, count);
531ea9ed9cfSChristoph Hellwig out_unlock:
532ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex);
533ea9ed9cfSChristoph Hellwig return rv;
5340bcc1816SSatyam Sharma }
5350bcc1816SSatyam Sharma
local_ip_store(struct config_item * item,const char * buf,size_t count)536ea9ed9cfSChristoph Hellwig static ssize_t local_ip_store(struct config_item *item, const char *buf,
5370bcc1816SSatyam Sharma size_t count)
5380bcc1816SSatyam Sharma {
539ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item);
540ea9ed9cfSChristoph Hellwig
541ea9ed9cfSChristoph Hellwig mutex_lock(&dynamic_netconsole_mutex);
5420bcc1816SSatyam Sharma if (nt->enabled) {
54322ded577SJoe Perches pr_err("target (%s) is enabled, disable to update parameters\n",
5440bcc1816SSatyam Sharma config_item_name(&nt->item));
545ea9ed9cfSChristoph Hellwig goto out_unlock;
5460bcc1816SSatyam Sharma }
5470bcc1816SSatyam Sharma
548b3d936f3SCong Wang if (strnchr(buf, count, ':')) {
549b3d936f3SCong Wang const char *end;
550b3d936f3SCong Wang if (in6_pton(buf, count, nt->np.local_ip.in6.s6_addr, -1, &end) > 0) {
551b3d936f3SCong Wang if (*end && *end != '\n') {
55222ded577SJoe Perches pr_err("invalid IPv6 address at: <%c>\n", *end);
553ea9ed9cfSChristoph Hellwig goto out_unlock;
554b3d936f3SCong Wang }
555b3d936f3SCong Wang nt->np.ipv6 = true;
556b3d936f3SCong Wang } else
557ea9ed9cfSChristoph Hellwig goto out_unlock;
558b3d936f3SCong Wang } else {
559b3d936f3SCong Wang if (!nt->np.ipv6) {
560b7394d24SCong Wang nt->np.local_ip.ip = in_aton(buf);
561b3d936f3SCong Wang } else
562ea9ed9cfSChristoph Hellwig goto out_unlock;
563ea9ed9cfSChristoph Hellwig }
564ea9ed9cfSChristoph Hellwig
565ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex);
566ea9ed9cfSChristoph Hellwig return strnlen(buf, count);
567ea9ed9cfSChristoph Hellwig out_unlock:
568ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex);
569b3d936f3SCong Wang return -EINVAL;
570b3d936f3SCong Wang }
5710bcc1816SSatyam Sharma
remote_ip_store(struct config_item * item,const char * buf,size_t count)572ea9ed9cfSChristoph Hellwig static ssize_t remote_ip_store(struct config_item *item, const char *buf,
5730bcc1816SSatyam Sharma size_t count)
5740bcc1816SSatyam Sharma {
575ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item);
576ea9ed9cfSChristoph Hellwig
577ea9ed9cfSChristoph Hellwig mutex_lock(&dynamic_netconsole_mutex);
5780bcc1816SSatyam Sharma if (nt->enabled) {
57922ded577SJoe Perches pr_err("target (%s) is enabled, disable to update parameters\n",
5800bcc1816SSatyam Sharma config_item_name(&nt->item));
581ea9ed9cfSChristoph Hellwig goto out_unlock;
5820bcc1816SSatyam Sharma }
5830bcc1816SSatyam Sharma
584b3d936f3SCong Wang if (strnchr(buf, count, ':')) {
585b3d936f3SCong Wang const char *end;
586b3d936f3SCong Wang if (in6_pton(buf, count, nt->np.remote_ip.in6.s6_addr, -1, &end) > 0) {
587b3d936f3SCong Wang if (*end && *end != '\n') {
58822ded577SJoe Perches pr_err("invalid IPv6 address at: <%c>\n", *end);
589ea9ed9cfSChristoph Hellwig goto out_unlock;
590b3d936f3SCong Wang }
591b3d936f3SCong Wang nt->np.ipv6 = true;
592b3d936f3SCong Wang } else
593ea9ed9cfSChristoph Hellwig goto out_unlock;
594b3d936f3SCong Wang } else {
595b3d936f3SCong Wang if (!nt->np.ipv6) {
596b7394d24SCong Wang nt->np.remote_ip.ip = in_aton(buf);
597b3d936f3SCong Wang } else
598ea9ed9cfSChristoph Hellwig goto out_unlock;
599ea9ed9cfSChristoph Hellwig }
600ea9ed9cfSChristoph Hellwig
601ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex);
602ea9ed9cfSChristoph Hellwig return strnlen(buf, count);
603ea9ed9cfSChristoph Hellwig out_unlock:
604ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex);
605b3d936f3SCong Wang return -EINVAL;
606b3d936f3SCong Wang }
6070bcc1816SSatyam Sharma
remote_mac_store(struct config_item * item,const char * buf,size_t count)608ea9ed9cfSChristoph Hellwig static ssize_t remote_mac_store(struct config_item *item, const char *buf,
6090bcc1816SSatyam Sharma size_t count)
6100bcc1816SSatyam Sharma {
611ea9ed9cfSChristoph Hellwig struct netconsole_target *nt = to_target(item);
6120bcc1816SSatyam Sharma u8 remote_mac[ETH_ALEN];
6130bcc1816SSatyam Sharma
614ea9ed9cfSChristoph Hellwig mutex_lock(&dynamic_netconsole_mutex);
6150bcc1816SSatyam Sharma if (nt->enabled) {
61622ded577SJoe Perches pr_err("target (%s) is enabled, disable to update parameters\n",
6170bcc1816SSatyam Sharma config_item_name(&nt->item));
618ea9ed9cfSChristoph Hellwig goto out_unlock;
6190bcc1816SSatyam Sharma }
6200bcc1816SSatyam Sharma
6214940fc88SAlexey Dobriyan if (!mac_pton(buf, remote_mac))
622ea9ed9cfSChristoph Hellwig goto out_unlock;
6234940fc88SAlexey Dobriyan if (buf[3 * ETH_ALEN - 1] && buf[3 * ETH_ALEN - 1] != '\n')
624ea9ed9cfSChristoph Hellwig goto out_unlock;
6250bcc1816SSatyam Sharma memcpy(nt->np.remote_mac, remote_mac, ETH_ALEN);
6260bcc1816SSatyam Sharma
627ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex);
6280bcc1816SSatyam Sharma return strnlen(buf, count);
629ea9ed9cfSChristoph Hellwig out_unlock:
630ea9ed9cfSChristoph Hellwig mutex_unlock(&dynamic_netconsole_mutex);
631ea9ed9cfSChristoph Hellwig return -EINVAL;
6320bcc1816SSatyam Sharma }
6330bcc1816SSatyam Sharma
634ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR(, enabled);
635ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR(, extended);
636ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR(, dev_name);
637ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR(, local_port);
638ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR(, remote_port);
639ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR(, local_ip);
640ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR(, remote_ip);
641ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR_RO(, local_mac);
642ea9ed9cfSChristoph Hellwig CONFIGFS_ATTR(, remote_mac);
643c62c0a17SBreno Leitao CONFIGFS_ATTR(, release);
6440bcc1816SSatyam Sharma
6450bcc1816SSatyam Sharma static struct configfs_attribute *netconsole_target_attrs[] = {
646ea9ed9cfSChristoph Hellwig &attr_enabled,
647ea9ed9cfSChristoph Hellwig &attr_extended,
648c62c0a17SBreno Leitao &attr_release,
649ea9ed9cfSChristoph Hellwig &attr_dev_name,
650ea9ed9cfSChristoph Hellwig &attr_local_port,
651ea9ed9cfSChristoph Hellwig &attr_remote_port,
652ea9ed9cfSChristoph Hellwig &attr_local_ip,
653ea9ed9cfSChristoph Hellwig &attr_remote_ip,
654ea9ed9cfSChristoph Hellwig &attr_local_mac,
655ea9ed9cfSChristoph Hellwig &attr_remote_mac,
6560bcc1816SSatyam Sharma NULL,
6570bcc1816SSatyam Sharma };
6580bcc1816SSatyam Sharma
6590bcc1816SSatyam Sharma /*
6600bcc1816SSatyam Sharma * Item operations and type for netconsole_target.
6610bcc1816SSatyam Sharma */
6620bcc1816SSatyam Sharma
netconsole_target_release(struct config_item * item)6630bcc1816SSatyam Sharma static void netconsole_target_release(struct config_item *item)
6640bcc1816SSatyam Sharma {
6650bcc1816SSatyam Sharma kfree(to_target(item));
6660bcc1816SSatyam Sharma }
6670bcc1816SSatyam Sharma
6680bcc1816SSatyam Sharma static struct configfs_item_operations netconsole_target_item_ops = {
6690bcc1816SSatyam Sharma .release = netconsole_target_release,
6700bcc1816SSatyam Sharma };
6710bcc1816SSatyam Sharma
6720d4a4406SBhumika Goyal static const struct config_item_type netconsole_target_type = {
6730bcc1816SSatyam Sharma .ct_attrs = netconsole_target_attrs,
6740bcc1816SSatyam Sharma .ct_item_ops = &netconsole_target_item_ops,
6750bcc1816SSatyam Sharma .ct_owner = THIS_MODULE,
6760bcc1816SSatyam Sharma };
6770bcc1816SSatyam Sharma
6780bcc1816SSatyam Sharma /*
6790bcc1816SSatyam Sharma * Group operations and type for netconsole_subsys.
6800bcc1816SSatyam Sharma */
6810bcc1816SSatyam Sharma
make_netconsole_target(struct config_group * group,const char * name)682f89ab861SJoel Becker static struct config_item *make_netconsole_target(struct config_group *group,
683f89ab861SJoel Becker const char *name)
6840bcc1816SSatyam Sharma {
6850bcc1816SSatyam Sharma struct netconsole_target *nt;
686b0a9e2c9SBreno Leitao unsigned long flags;
6870bcc1816SSatyam Sharma
688b0a9e2c9SBreno Leitao nt = alloc_and_init();
689e404decbSJoe Perches if (!nt)
690a6795e9eSJoel Becker return ERR_PTR(-ENOMEM);
6910bcc1816SSatyam Sharma
6920bcc1816SSatyam Sharma /* Initialize the config_item member */
6930bcc1816SSatyam Sharma config_item_init_type_name(&nt->item, name, &netconsole_target_type);
6940bcc1816SSatyam Sharma
6950bcc1816SSatyam Sharma /* Adding, but it is disabled */
6960bcc1816SSatyam Sharma spin_lock_irqsave(&target_list_lock, flags);
6970bcc1816SSatyam Sharma list_add(&nt->list, &target_list);
6980bcc1816SSatyam Sharma spin_unlock_irqrestore(&target_list_lock, flags);
6990bcc1816SSatyam Sharma
700f89ab861SJoel Becker return &nt->item;
7010bcc1816SSatyam Sharma }
7020bcc1816SSatyam Sharma
drop_netconsole_target(struct config_group * group,struct config_item * item)7030bcc1816SSatyam Sharma static void drop_netconsole_target(struct config_group *group,
7040bcc1816SSatyam Sharma struct config_item *item)
7050bcc1816SSatyam Sharma {
7060bcc1816SSatyam Sharma unsigned long flags;
7070bcc1816SSatyam Sharma struct netconsole_target *nt = to_target(item);
7080bcc1816SSatyam Sharma
7090bcc1816SSatyam Sharma spin_lock_irqsave(&target_list_lock, flags);
7100bcc1816SSatyam Sharma list_del(&nt->list);
7110bcc1816SSatyam Sharma spin_unlock_irqrestore(&target_list_lock, flags);
7120bcc1816SSatyam Sharma
7130bcc1816SSatyam Sharma /*
7140bcc1816SSatyam Sharma * The target may have never been enabled, or was manually disabled
7150bcc1816SSatyam Sharma * before being removed so netpoll may have already been cleaned up.
7160bcc1816SSatyam Sharma */
7170bcc1816SSatyam Sharma if (nt->enabled)
7180bcc1816SSatyam Sharma netpoll_cleanup(&nt->np);
7190bcc1816SSatyam Sharma
7200bcc1816SSatyam Sharma config_item_put(&nt->item);
7210bcc1816SSatyam Sharma }
7220bcc1816SSatyam Sharma
7230bcc1816SSatyam Sharma static struct configfs_group_operations netconsole_subsys_group_ops = {
7240bcc1816SSatyam Sharma .make_item = make_netconsole_target,
7250bcc1816SSatyam Sharma .drop_item = drop_netconsole_target,
7260bcc1816SSatyam Sharma };
7270bcc1816SSatyam Sharma
7280d4a4406SBhumika Goyal static const struct config_item_type netconsole_subsys_type = {
7290bcc1816SSatyam Sharma .ct_group_ops = &netconsole_subsys_group_ops,
7300bcc1816SSatyam Sharma .ct_owner = THIS_MODULE,
7310bcc1816SSatyam Sharma };
7320bcc1816SSatyam Sharma
7330bcc1816SSatyam Sharma /* The netconsole configfs subsystem */
7340bcc1816SSatyam Sharma static struct configfs_subsystem netconsole_subsys = {
7350bcc1816SSatyam Sharma .su_group = {
7360bcc1816SSatyam Sharma .cg_item = {
7370bcc1816SSatyam Sharma .ci_namebuf = "netconsole",
7380bcc1816SSatyam Sharma .ci_type = &netconsole_subsys_type,
7390bcc1816SSatyam Sharma },
7400bcc1816SSatyam Sharma },
7410bcc1816SSatyam Sharma };
7420bcc1816SSatyam Sharma
7430bcc1816SSatyam Sharma #endif /* CONFIG_NETCONSOLE_DYNAMIC */
7440bcc1816SSatyam Sharma
74517951f34SSatyam Sharma /* Handle network interface device notifications */
netconsole_netdev_event(struct notifier_block * this,unsigned long event,void * ptr)74617951f34SSatyam Sharma static int netconsole_netdev_event(struct notifier_block *this,
747351638e7SJiri Pirko unsigned long event, void *ptr)
74817951f34SSatyam Sharma {
749b5427c27SSatyam Sharma unsigned long flags;
750b5427c27SSatyam Sharma struct netconsole_target *nt;
751351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr);
752141dfba3SFerenc Wagner bool stopped = false;
75317951f34SSatyam Sharma
7540e34e931SWANG Cong if (!(event == NETDEV_CHANGENAME || event == NETDEV_UNREGISTER ||
755daf9209bSAmerigo Wang event == NETDEV_RELEASE || event == NETDEV_JOIN))
756b5427c27SSatyam Sharma goto done;
757b5427c27SSatyam Sharma
758b5427c27SSatyam Sharma spin_lock_irqsave(&target_list_lock, flags);
7593f315befSVeaceslav Falico restart:
760b5427c27SSatyam Sharma list_for_each_entry(nt, &target_list, list) {
7610bcc1816SSatyam Sharma netconsole_target_get(nt);
76217951f34SSatyam Sharma if (nt->np.dev == dev) {
76317951f34SSatyam Sharma switch (event) {
76417951f34SSatyam Sharma case NETDEV_CHANGENAME:
765fb3ceec1SWolfram Sang strscpy(nt->np.dev_name, dev->name, IFNAMSIZ);
76617951f34SSatyam Sharma break;
767daf9209bSAmerigo Wang case NETDEV_RELEASE:
7688d8fc29dSAmerigo Wang case NETDEV_JOIN:
7692382b15bSBruno Prémont case NETDEV_UNREGISTER:
770c71380ffSNikolay Aleksandrov /* rtnl_lock already held
7713f315befSVeaceslav Falico * we might sleep in __netpoll_cleanup()
7723b410a31SNeil Horman */
773*9e541c2dSBreno Leitao nt->enabled = false;
7743f315befSVeaceslav Falico spin_unlock_irqrestore(&target_list_lock, flags);
7757a163bfbSDan Aloni
7763b410a31SNeil Horman __netpoll_cleanup(&nt->np);
7777a163bfbSDan Aloni
7783f315befSVeaceslav Falico spin_lock_irqsave(&target_list_lock, flags);
779d62607c3SJakub Kicinski netdev_put(nt->np.dev, &nt->np.dev_tracker);
7803b410a31SNeil Horman nt->np.dev = NULL;
781141dfba3SFerenc Wagner stopped = true;
7823f315befSVeaceslav Falico netconsole_target_put(nt);
7833f315befSVeaceslav Falico goto restart;
78417951f34SSatyam Sharma }
78517951f34SSatyam Sharma }
7860bcc1816SSatyam Sharma netconsole_target_put(nt);
787b5427c27SSatyam Sharma }
788b5427c27SSatyam Sharma spin_unlock_irqrestore(&target_list_lock, flags);
7898d8fc29dSAmerigo Wang if (stopped) {
79022ded577SJoe Perches const char *msg = "had an event";
7918d8fc29dSAmerigo Wang switch (event) {
7928d8fc29dSAmerigo Wang case NETDEV_UNREGISTER:
79322ded577SJoe Perches msg = "unregistered";
7948d8fc29dSAmerigo Wang break;
795daf9209bSAmerigo Wang case NETDEV_RELEASE:
79622ded577SJoe Perches msg = "released slaves";
7978d8fc29dSAmerigo Wang break;
7988d8fc29dSAmerigo Wang case NETDEV_JOIN:
79922ded577SJoe Perches msg = "is joining a master device";
8008d8fc29dSAmerigo Wang break;
8018d8fc29dSAmerigo Wang }
80222ded577SJoe Perches pr_info("network logging stopped on interface %s as it %s\n",
80322ded577SJoe Perches dev->name, msg);
8048d8fc29dSAmerigo Wang }
80517951f34SSatyam Sharma
806b5427c27SSatyam Sharma done:
80717951f34SSatyam Sharma return NOTIFY_DONE;
80817951f34SSatyam Sharma }
80917951f34SSatyam Sharma
81017951f34SSatyam Sharma static struct notifier_block netconsole_netdev_notifier = {
81117951f34SSatyam Sharma .notifier_call = netconsole_netdev_event,
81217951f34SSatyam Sharma };
81317951f34SSatyam Sharma
814e2f15f9aSTejun Heo /**
815e2f15f9aSTejun Heo * send_ext_msg_udp - send extended log message to target
816e2f15f9aSTejun Heo * @nt: target to send message to
817e2f15f9aSTejun Heo * @msg: extended log message to send
818e2f15f9aSTejun Heo * @msg_len: length of message
819e2f15f9aSTejun Heo *
820e2f15f9aSTejun Heo * Transfer extended log @msg to @nt. If @msg is longer than
821e2f15f9aSTejun Heo * MAX_PRINT_CHUNK, it'll be split and transmitted in multiple chunks with
822e2f15f9aSTejun Heo * ncfrag header field added to identify them.
823e2f15f9aSTejun Heo */
send_ext_msg_udp(struct netconsole_target * nt,const char * msg,int msg_len)824e2f15f9aSTejun Heo static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg,
825e2f15f9aSTejun Heo int msg_len)
826e2f15f9aSTejun Heo {
827e2f15f9aSTejun Heo static char buf[MAX_PRINT_CHUNK]; /* protected by target_list_lock */
828e2f15f9aSTejun Heo const char *header, *body;
829e2f15f9aSTejun Heo int offset = 0;
830e2f15f9aSTejun Heo int header_len, body_len;
831c62c0a17SBreno Leitao const char *msg_ready = msg;
832c62c0a17SBreno Leitao const char *release;
833c62c0a17SBreno Leitao int release_len = 0;
834e2f15f9aSTejun Heo
835c62c0a17SBreno Leitao if (nt->release) {
836c62c0a17SBreno Leitao release = init_utsname()->release;
837c62c0a17SBreno Leitao release_len = strlen(release) + 1;
838c62c0a17SBreno Leitao }
839c62c0a17SBreno Leitao
840c62c0a17SBreno Leitao if (msg_len + release_len <= MAX_PRINT_CHUNK) {
841c62c0a17SBreno Leitao /* No fragmentation needed */
842c62c0a17SBreno Leitao if (nt->release) {
843c62c0a17SBreno Leitao scnprintf(buf, MAX_PRINT_CHUNK, "%s,%s", release, msg);
844c62c0a17SBreno Leitao msg_len += release_len;
845c62c0a17SBreno Leitao msg_ready = buf;
846c62c0a17SBreno Leitao }
847c62c0a17SBreno Leitao netpoll_send_udp(&nt->np, msg_ready, msg_len);
848e2f15f9aSTejun Heo return;
849e2f15f9aSTejun Heo }
850e2f15f9aSTejun Heo
851e2f15f9aSTejun Heo /* need to insert extra header fields, detect header and body */
852e2f15f9aSTejun Heo header = msg;
853e2f15f9aSTejun Heo body = memchr(msg, ';', msg_len);
854e2f15f9aSTejun Heo if (WARN_ON_ONCE(!body))
855e2f15f9aSTejun Heo return;
856e2f15f9aSTejun Heo
857e2f15f9aSTejun Heo header_len = body - header;
858e2f15f9aSTejun Heo body_len = msg_len - header_len - 1;
859e2f15f9aSTejun Heo body++;
860e2f15f9aSTejun Heo
861e2f15f9aSTejun Heo /*
862e2f15f9aSTejun Heo * Transfer multiple chunks with the following extra header.
863e2f15f9aSTejun Heo * "ncfrag=<byte-offset>/<total-bytes>"
864e2f15f9aSTejun Heo */
865c62c0a17SBreno Leitao if (nt->release)
866c62c0a17SBreno Leitao scnprintf(buf, MAX_PRINT_CHUNK, "%s,", release);
867c62c0a17SBreno Leitao memcpy(buf + release_len, header, header_len);
868c62c0a17SBreno Leitao header_len += release_len;
869e2f15f9aSTejun Heo
870e2f15f9aSTejun Heo while (offset < body_len) {
871e2f15f9aSTejun Heo int this_header = header_len;
872e2f15f9aSTejun Heo int this_chunk;
873e2f15f9aSTejun Heo
874e2f15f9aSTejun Heo this_header += scnprintf(buf + this_header,
875e2f15f9aSTejun Heo sizeof(buf) - this_header,
876e2f15f9aSTejun Heo ",ncfrag=%d/%d;", offset, body_len);
877e2f15f9aSTejun Heo
878e2f15f9aSTejun Heo this_chunk = min(body_len - offset,
879e2f15f9aSTejun Heo MAX_PRINT_CHUNK - this_header);
880e2f15f9aSTejun Heo if (WARN_ON_ONCE(this_chunk <= 0))
881e2f15f9aSTejun Heo return;
882e2f15f9aSTejun Heo
883e2f15f9aSTejun Heo memcpy(buf + this_header, body + offset, this_chunk);
884e2f15f9aSTejun Heo
885e2f15f9aSTejun Heo netpoll_send_udp(&nt->np, buf, this_header + this_chunk);
886e2f15f9aSTejun Heo
887e2f15f9aSTejun Heo offset += this_chunk;
888e2f15f9aSTejun Heo }
889e2f15f9aSTejun Heo }
890e2f15f9aSTejun Heo
write_ext_msg(struct console * con,const char * msg,unsigned int len)891e2f15f9aSTejun Heo static void write_ext_msg(struct console *con, const char *msg,
892e2f15f9aSTejun Heo unsigned int len)
893e2f15f9aSTejun Heo {
894e2f15f9aSTejun Heo struct netconsole_target *nt;
895e2f15f9aSTejun Heo unsigned long flags;
896e2f15f9aSTejun Heo
897e2f15f9aSTejun Heo if ((oops_only && !oops_in_progress) || list_empty(&target_list))
898e2f15f9aSTejun Heo return;
899e2f15f9aSTejun Heo
900e2f15f9aSTejun Heo spin_lock_irqsave(&target_list_lock, flags);
901e2f15f9aSTejun Heo list_for_each_entry(nt, &target_list, list)
902e2f15f9aSTejun Heo if (nt->extended && nt->enabled && netif_running(nt->np.dev))
903e2f15f9aSTejun Heo send_ext_msg_udp(nt, msg, len);
904e2f15f9aSTejun Heo spin_unlock_irqrestore(&target_list_lock, flags);
905e2f15f9aSTejun Heo }
906e2f15f9aSTejun Heo
write_msg(struct console * con,const char * msg,unsigned int len)9071da177e4SLinus Torvalds static void write_msg(struct console *con, const char *msg, unsigned int len)
9081da177e4SLinus Torvalds {
9091da177e4SLinus Torvalds int frag, left;
9101da177e4SLinus Torvalds unsigned long flags;
911b5427c27SSatyam Sharma struct netconsole_target *nt;
912b5427c27SSatyam Sharma const char *tmp;
9131da177e4SLinus Torvalds
914c1a60851SAmerigo Wang if (oops_only && !oops_in_progress)
915c1a60851SAmerigo Wang return;
916b5427c27SSatyam Sharma /* Avoid taking lock and disabling interrupts unnecessarily */
917b5427c27SSatyam Sharma if (list_empty(&target_list))
918b5427c27SSatyam Sharma return;
919b5427c27SSatyam Sharma
920b5427c27SSatyam Sharma spin_lock_irqsave(&target_list_lock, flags);
921b5427c27SSatyam Sharma list_for_each_entry(nt, &target_list, list) {
922e2f15f9aSTejun Heo if (!nt->extended && nt->enabled && netif_running(nt->np.dev)) {
923b5427c27SSatyam Sharma /*
924b5427c27SSatyam Sharma * We nest this inside the for-each-target loop above
925b5427c27SSatyam Sharma * so that we're able to get as much logging out to
926b5427c27SSatyam Sharma * at least one target if we die inside here, instead
927b5427c27SSatyam Sharma * of unnecessarily keeping all targets in lock-step.
928b5427c27SSatyam Sharma */
929b5427c27SSatyam Sharma tmp = msg;
9301da177e4SLinus Torvalds for (left = len; left;) {
9311da177e4SLinus Torvalds frag = min(left, MAX_PRINT_CHUNK);
932b5427c27SSatyam Sharma netpoll_send_udp(&nt->np, tmp, frag);
933b5427c27SSatyam Sharma tmp += frag;
9341da177e4SLinus Torvalds left -= frag;
9351da177e4SLinus Torvalds }
9361da177e4SLinus Torvalds }
9370cc120beSSatyam Sharma }
938b5427c27SSatyam Sharma spin_unlock_irqrestore(&target_list_lock, flags);
939b5427c27SSatyam Sharma }
9401da177e4SLinus Torvalds
941e2f15f9aSTejun Heo static struct console netconsole_ext = {
942e2f15f9aSTejun Heo .name = "netcon_ext",
9432c6b4b70SJohn Ogness .flags = CON_ENABLED | CON_EXTENDED,
944e2f15f9aSTejun Heo .write = write_ext_msg,
945e2f15f9aSTejun Heo };
946e2f15f9aSTejun Heo
9471da177e4SLinus Torvalds static struct console netconsole = {
948d938ab44SRandy Dunlap .name = "netcon",
9490517deedSMichael Ellerman .flags = CON_ENABLED,
950d39badf0SSatyam Sharma .write = write_msg,
9511da177e4SLinus Torvalds };
9521da177e4SLinus Torvalds
init_netconsole(void)953d39badf0SSatyam Sharma static int __init init_netconsole(void)
9541da177e4SLinus Torvalds {
9550bcc1816SSatyam Sharma int err;
956b5427c27SSatyam Sharma struct netconsole_target *nt, *tmp;
9572c6b4b70SJohn Ogness bool extended = false;
958b5427c27SSatyam Sharma unsigned long flags;
959b5427c27SSatyam Sharma char *target_config;
960b5427c27SSatyam Sharma char *input = config;
961b41848b6SStephen Hemminger
9620bcc1816SSatyam Sharma if (strnlen(input, MAX_PARAM_LENGTH)) {
963b5427c27SSatyam Sharma while ((target_config = strsep(&input, ";"))) {
9640bcc1816SSatyam Sharma nt = alloc_param_target(target_config);
965b5427c27SSatyam Sharma if (IS_ERR(nt)) {
966b5427c27SSatyam Sharma err = PTR_ERR(nt);
967b5427c27SSatyam Sharma goto fail;
968b5427c27SSatyam Sharma }
9690517deedSMichael Ellerman /* Dump existing printks when we register */
9702c6b4b70SJohn Ogness if (nt->extended) {
9712c6b4b70SJohn Ogness extended = true;
9722c6b4b70SJohn Ogness netconsole_ext.flags |= CON_PRINTBUFFER;
9732c6b4b70SJohn Ogness } else {
9740517deedSMichael Ellerman netconsole.flags |= CON_PRINTBUFFER;
9752c6b4b70SJohn Ogness }
9760517deedSMichael Ellerman
977b5427c27SSatyam Sharma spin_lock_irqsave(&target_list_lock, flags);
978b5427c27SSatyam Sharma list_add(&nt->list, &target_list);
979b5427c27SSatyam Sharma spin_unlock_irqrestore(&target_list_lock, flags);
980b5427c27SSatyam Sharma }
9810bcc1816SSatyam Sharma }
9821da177e4SLinus Torvalds
98317951f34SSatyam Sharma err = register_netdevice_notifier(&netconsole_netdev_notifier);
98417951f34SSatyam Sharma if (err)
985b5427c27SSatyam Sharma goto fail;
98617951f34SSatyam Sharma
9870bcc1816SSatyam Sharma err = dynamic_netconsole_init();
9880bcc1816SSatyam Sharma if (err)
9890bcc1816SSatyam Sharma goto undonotifier;
9900bcc1816SSatyam Sharma
9912c6b4b70SJohn Ogness if (extended)
992e2f15f9aSTejun Heo register_console(&netconsole_ext);
9931da177e4SLinus Torvalds register_console(&netconsole);
99422ded577SJoe Perches pr_info("network logging started\n");
995d39badf0SSatyam Sharma
996d39badf0SSatyam Sharma return err;
997b5427c27SSatyam Sharma
9980bcc1816SSatyam Sharma undonotifier:
9990bcc1816SSatyam Sharma unregister_netdevice_notifier(&netconsole_netdev_notifier);
10000bcc1816SSatyam Sharma
1001b5427c27SSatyam Sharma fail:
100222ded577SJoe Perches pr_err("cleaning up\n");
1003b5427c27SSatyam Sharma
1004b5427c27SSatyam Sharma /*
10050bcc1816SSatyam Sharma * Remove all targets and destroy them (only targets created
10060bcc1816SSatyam Sharma * from the boot/module option exist here). Skipping the list
1007b5427c27SSatyam Sharma * lock is safe here, and netpoll_cleanup() will sleep.
1008b5427c27SSatyam Sharma */
1009b5427c27SSatyam Sharma list_for_each_entry_safe(nt, tmp, &target_list, list) {
1010b5427c27SSatyam Sharma list_del(&nt->list);
10110bcc1816SSatyam Sharma free_param_target(nt);
1012b5427c27SSatyam Sharma }
1013b5427c27SSatyam Sharma
1014b5427c27SSatyam Sharma return err;
10151da177e4SLinus Torvalds }
10161da177e4SLinus Torvalds
cleanup_netconsole(void)1017d39badf0SSatyam Sharma static void __exit cleanup_netconsole(void)
10181da177e4SLinus Torvalds {
1019b5427c27SSatyam Sharma struct netconsole_target *nt, *tmp;
1020df180e36SSatyam Sharma
10212c6b4b70SJohn Ogness if (console_is_registered(&netconsole_ext))
1022e2f15f9aSTejun Heo unregister_console(&netconsole_ext);
10231da177e4SLinus Torvalds unregister_console(&netconsole);
10240bcc1816SSatyam Sharma dynamic_netconsole_exit();
102517951f34SSatyam Sharma unregister_netdevice_notifier(&netconsole_netdev_notifier);
1026b5427c27SSatyam Sharma
1027b5427c27SSatyam Sharma /*
10280bcc1816SSatyam Sharma * Targets created via configfs pin references on our module
10290bcc1816SSatyam Sharma * and would first be rmdir(2)'ed from userspace. We reach
10300bcc1816SSatyam Sharma * here only when they are already destroyed, and only those
10310bcc1816SSatyam Sharma * created from the boot/module option are left, so remove and
10320bcc1816SSatyam Sharma * destroy them. Skipping the list lock is safe here, and
10330bcc1816SSatyam Sharma * netpoll_cleanup() will sleep.
1034b5427c27SSatyam Sharma */
1035b5427c27SSatyam Sharma list_for_each_entry_safe(nt, tmp, &target_list, list) {
1036b5427c27SSatyam Sharma list_del(&nt->list);
10370bcc1816SSatyam Sharma free_param_target(nt);
1038b5427c27SSatyam Sharma }
10391da177e4SLinus Torvalds }
10401da177e4SLinus Torvalds
104197c7de05SLin Ming /*
104297c7de05SLin Ming * Use late_initcall to ensure netconsole is
104397c7de05SLin Ming * initialized after network device driver if built-in.
104497c7de05SLin Ming *
104597c7de05SLin Ming * late_initcall() and module_init() are identical if built as module.
104697c7de05SLin Ming */
104797c7de05SLin Ming late_initcall(init_netconsole);
10481da177e4SLinus Torvalds module_exit(cleanup_netconsole);
1049