2005-04-17 02:20:36 +04:00
/*
* linux / drivers / net / netconsole . c
*
* Copyright ( C ) 2001 Ingo Molnar < mingo @ redhat . com >
*
* This file contains the implementation of an IRQ - safe , crash - safe
* kernel console implementation that outputs kernel messages to the
* network .
*
* Modification history :
*
* 2001 - 09 - 17 started by Ingo Molnar .
* 2003 - 08 - 11 2.6 port by Matt Mackall
* simplified options
* generic card hooks
* works non - modular
* 2003 - 09 - 07 rewritten with netpoll api
*/
/****************************************************************
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 , or ( at your option )
* any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <linux/mm.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/console.h>
# include <linux/moduleparam.h>
# include <linux/string.h>
# include <linux/netpoll.h>
MODULE_AUTHOR ( " Maintainer: Matt Mackall <mpm@selenic.com> " ) ;
MODULE_DESCRIPTION ( " Console driver for network interfaces " ) ;
MODULE_LICENSE ( " GPL " ) ;
2007-08-11 02:27:24 +04:00
# define MAX_PARAM_LENGTH 256
# define MAX_PRINT_CHUNK 1000
static char config [ MAX_PARAM_LENGTH ] ;
module_param_string ( netconsole , config , MAX_PARAM_LENGTH , 0 ) ;
2005-04-17 02:20:36 +04:00
MODULE_PARM_DESC ( netconsole , " netconsole=[src-port]@[src-ip]/[dev],[tgt-port]@<tgt-ip>/[tgt-macaddr] \n " ) ;
2007-08-11 02:29:47 +04:00
# ifndef MODULE
static int __init option_setup ( char * opt )
{
strlcpy ( config , opt , MAX_PARAM_LENGTH ) ;
return 1 ;
}
__setup ( " netconsole= " , option_setup ) ;
# endif /* MODULE */
2007-08-11 02:33:40 +04:00
/* Linked list of all configured targets */
static LIST_HEAD ( target_list ) ;
/* This needs to be a spinlock because write_msg() cannot sleep */
static DEFINE_SPINLOCK ( target_list_lock ) ;
2007-08-11 02:32:14 +04:00
/**
* struct netconsole_target - Represents a configured netconsole target .
2007-08-11 02:33:40 +04:00
* @ list : Links this target into the target_list .
2007-08-11 02:32:14 +04:00
* @ np : The netpoll structure for this target .
*/
struct netconsole_target {
2007-08-11 02:33:40 +04:00
struct list_head list ;
2007-08-11 02:32:14 +04:00
struct netpoll np ;
} ;
2007-08-11 02:33:40 +04:00
/* Allocate new target and setup netpoll for it */
static struct netconsole_target * alloc_target ( char * target_config )
{
int err = - ENOMEM ;
struct netconsole_target * nt ;
/* Allocate and initialize with defaults */
nt = kzalloc ( sizeof ( * nt ) , GFP_KERNEL ) ;
if ( ! nt ) {
printk ( KERN_ERR " netconsole: failed to allocate memory \n " ) ;
goto fail ;
}
nt - > np . name = " netconsole " ;
strlcpy ( nt - > np . dev_name , " eth0 " , IFNAMSIZ ) ;
nt - > np . local_port = 6665 ;
nt - > np . remote_port = 6666 ;
memset ( nt - > np . remote_mac , 0xff , ETH_ALEN ) ;
/* Parse parameters and setup netpoll */
err = netpoll_parse_options ( & nt - > np , target_config ) ;
if ( err )
goto fail ;
err = netpoll_setup ( & nt - > np ) ;
if ( err )
goto fail ;
return nt ;
fail :
kfree ( nt ) ;
return ERR_PTR ( err ) ;
}
/* Cleanup netpoll for given target and free it */
static void free_target ( struct netconsole_target * nt )
{
netpoll_cleanup ( & nt - > np ) ;
kfree ( nt ) ;
}
2005-04-17 02:20:36 +04:00
2007-08-11 02:33:01 +04:00
/* Handle network interface device notifications */
static int netconsole_netdev_event ( struct notifier_block * this ,
unsigned long event ,
void * ptr )
{
2007-08-11 02:33:40 +04:00
unsigned long flags ;
struct netconsole_target * nt ;
2007-08-11 02:33:01 +04:00
struct net_device * dev = ptr ;
2007-08-11 02:33:40 +04:00
if ( ! ( event = = NETDEV_CHANGEADDR | | event = = NETDEV_CHANGENAME ) )
goto done ;
spin_lock_irqsave ( & target_list_lock , flags ) ;
list_for_each_entry ( nt , & target_list , list ) {
if ( nt - > np . dev = = dev ) {
switch ( event ) {
case NETDEV_CHANGEADDR :
memcpy ( nt - > np . local_mac , dev - > dev_addr , ETH_ALEN ) ;
break ;
2007-08-11 02:33:01 +04:00
2007-08-11 02:33:40 +04:00
case NETDEV_CHANGENAME :
strlcpy ( nt - > np . dev_name , dev - > name , IFNAMSIZ ) ;
break ;
}
2007-08-11 02:33:01 +04:00
}
}
2007-08-11 02:33:40 +04:00
spin_unlock_irqrestore ( & target_list_lock , flags ) ;
2007-08-11 02:33:01 +04:00
2007-08-11 02:33:40 +04:00
done :
2007-08-11 02:33:01 +04:00
return NOTIFY_DONE ;
}
static struct notifier_block netconsole_netdev_notifier = {
. notifier_call = netconsole_netdev_event ,
} ;
2005-04-17 02:20:36 +04:00
static void write_msg ( struct console * con , const char * msg , unsigned int len )
{
int frag , left ;
unsigned long flags ;
2007-08-11 02:33:40 +04:00
struct netconsole_target * nt ;
const char * tmp ;
/* Avoid taking lock and disabling interrupts unnecessarily */
if ( list_empty ( & target_list ) )
return ;
spin_lock_irqsave ( & target_list_lock , flags ) ;
list_for_each_entry ( nt , & target_list , list ) {
if ( netif_running ( nt - > np . dev ) ) {
/*
* We nest this inside the for - each - target loop above
* so that we ' re able to get as much logging out to
* at least one target if we die inside here , instead
* of unnecessarily keeping all targets in lock - step .
*/
tmp = msg ;
for ( left = len ; left ; ) {
frag = min ( left , MAX_PRINT_CHUNK ) ;
netpoll_send_udp ( & nt - > np , tmp , frag ) ;
tmp + = frag ;
left - = frag ;
}
2007-08-11 02:30:31 +04:00
}
2005-04-17 02:20:36 +04:00
}
2007-08-11 02:33:40 +04:00
spin_unlock_irqrestore ( & target_list_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
static struct console netconsole = {
2007-08-11 02:27:24 +04:00
. name = " netcon " ,
. flags = CON_ENABLED | CON_PRINTBUFFER ,
. write = write_msg ,
2005-04-17 02:20:36 +04:00
} ;
2007-08-11 02:27:24 +04:00
static int __init init_netconsole ( void )
2005-04-17 02:20:36 +04:00
{
2007-08-11 02:27:24 +04:00
int err = 0 ;
2007-08-11 02:33:40 +04:00
struct netconsole_target * nt , * tmp ;
unsigned long flags ;
char * target_config ;
char * input = config ;
2006-10-27 02:46:52 +04:00
2007-08-11 02:33:40 +04:00
if ( ! strnlen ( input , MAX_PARAM_LENGTH ) ) {
2007-08-11 02:27:24 +04:00
printk ( KERN_INFO " netconsole: not configured, aborting \n " ) ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2007-08-11 02:33:40 +04:00
while ( ( target_config = strsep ( & input , " ; " ) ) ) {
nt = alloc_target ( target_config ) ;
if ( IS_ERR ( nt ) ) {
err = PTR_ERR ( nt ) ;
goto fail ;
}
spin_lock_irqsave ( & target_list_lock , flags ) ;
list_add ( & nt - > list , & target_list ) ;
spin_unlock_irqrestore ( & target_list_lock , flags ) ;
}
2005-04-17 02:20:36 +04:00
2007-08-11 02:33:01 +04:00
err = register_netdevice_notifier ( & netconsole_netdev_notifier ) ;
if ( err )
2007-08-11 02:33:40 +04:00
goto fail ;
2007-08-11 02:33:01 +04:00
2005-04-17 02:20:36 +04:00
register_console ( & netconsole ) ;
printk ( KERN_INFO " netconsole: network logging started \n " ) ;
2007-08-11 02:27:24 +04:00
out :
return err ;
2007-08-11 02:33:40 +04:00
fail :
printk ( KERN_ERR " netconsole: cleaning up \n " ) ;
/*
* Remove all targets and destroy them . Skipping the list
* lock is safe here , and netpoll_cleanup ( ) will sleep .
*/
list_for_each_entry_safe ( nt , tmp , & target_list , list ) {
list_del ( & nt - > list ) ;
free_target ( nt ) ;
}
return err ;
2005-04-17 02:20:36 +04:00
}
2007-08-11 02:27:24 +04:00
static void __exit cleanup_netconsole ( void )
2005-04-17 02:20:36 +04:00
{
2007-08-11 02:33:40 +04:00
struct netconsole_target * nt , * tmp ;
2007-08-11 02:32:14 +04:00
2005-04-17 02:20:36 +04:00
unregister_console ( & netconsole ) ;
2007-08-11 02:33:01 +04:00
unregister_netdevice_notifier ( & netconsole_netdev_notifier ) ;
2007-08-11 02:33:40 +04:00
/*
* Remove all targets and destroy them . Skipping the list
* lock is safe here , and netpoll_cleanup ( ) will sleep .
*/
list_for_each_entry_safe ( nt , tmp , & target_list , list ) {
list_del ( & nt - > list ) ;
free_target ( nt ) ;
}
2005-04-17 02:20:36 +04:00
}
module_init ( init_netconsole ) ;
module_exit ( cleanup_netconsole ) ;