xref: /openbmc/linux/drivers/net/netconsole.c (revision fad361a2)
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 
181*fad361a2SBreno Leitao 	if (IS_ENABLED(CONFIG_NETCONSOLE_EXTENDED_LOG))
182*fad361a2SBreno Leitao 		nt->extended = true;
183*fad361a2SBreno Leitao 	if (IS_ENABLED(CONFIG_NETCONSOLE_PREPEND_RELEASE))
184*fad361a2SBreno Leitao 		nt->release = true;
185*fad361a2SBreno 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 				 */
7733f315befSVeaceslav Falico 				spin_unlock_irqrestore(&target_list_lock, flags);
7747a163bfbSDan Aloni 
7753b410a31SNeil Horman 				__netpoll_cleanup(&nt->np);
7767a163bfbSDan Aloni 
7773f315befSVeaceslav Falico 				spin_lock_irqsave(&target_list_lock, flags);
778d62607c3SJakub Kicinski 				netdev_put(nt->np.dev, &nt->np.dev_tracker);
7793b410a31SNeil Horman 				nt->np.dev = NULL;
780698cf1c6STejun Heo 				nt->enabled = false;
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