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