2005-04-16 15:20:36 -07:00
/*
2007-10-16 01:27:29 -07:00
* Copyright ( C ) 2001 - 2007 Jeff Dike ( jdike @ { addtoit , linux . intel } . com )
2007-02-10 01:43:53 -08:00
* Copyright ( C ) 2001 Lennert Buytenhek ( buytenh @ gnu . org ) and
2005-04-16 15:20:36 -07:00
* James Leu ( jleu @ mindspring . net ) .
* Copyright ( C ) 2001 by various other people who didn ' t put their name here .
* Licensed under the GPL .
*/
2007-10-16 01:27:29 -07:00
# include <linux/bootmem.h>
# include <linux/etherdevice.h>
# include <linux/ethtool.h>
# include <linux/inetdevice.h>
# include <linux/init.h>
# include <linux/list.h>
# include <linux/netdevice.h>
# include <linux/platform_device.h>
# include <linux/rtnetlink.h>
# include <linux/skbuff.h>
# include <linux/spinlock.h>
2005-04-16 15:20:36 -07:00
# include "init.h"
# include "irq_kern.h"
2007-10-16 01:27:29 -07:00
# include "irq_user.h"
# include "mconsole_kern.h"
# include "net_kern.h"
# include "net_user.h"
2005-04-16 15:20:36 -07:00
2006-09-25 23:33:07 -07:00
static inline void set_ether_mac ( struct net_device * dev , unsigned char * addr )
{
memcpy ( dev - > dev_addr , addr , ETH_ALEN ) ;
}
2005-04-16 15:20:36 -07:00
# define DRIVER_NAME "uml-netdev"
static DEFINE_SPINLOCK ( opened_lock ) ;
2006-01-06 00:18:54 -08:00
static LIST_HEAD ( opened ) ;
2005-04-16 15:20:36 -07:00
2007-10-16 01:27:32 -07:00
/*
* The drop_skb is used when we can ' t allocate an skb . The
* packet is read into drop_skb in order to get the data off the
* connection to the host .
* It is reallocated whenever a maximum packet size is seen which is
* larger than any seen before . update_drop_skb is called from
* eth_configure when a new interface is added .
*/
static DEFINE_SPINLOCK ( drop_lock ) ;
static struct sk_buff * drop_skb ;
static int drop_max ;
static int update_drop_skb ( int max )
{
struct sk_buff * new ;
unsigned long flags ;
int err = 0 ;
spin_lock_irqsave ( & drop_lock , flags ) ;
if ( max < = drop_max )
goto out ;
err = - ENOMEM ;
new = dev_alloc_skb ( max ) ;
if ( new = = NULL )
goto out ;
skb_put ( new , max ) ;
kfree_skb ( drop_skb ) ;
drop_skb = new ;
drop_max = max ;
err = 0 ;
out :
spin_unlock_irqrestore ( & drop_lock , flags ) ;
return err ;
}
2005-04-16 15:20:36 -07:00
static int uml_net_rx ( struct net_device * dev )
{
2008-12-04 15:07:33 -08:00
struct uml_net_private * lp = netdev_priv ( dev ) ;
2005-04-16 15:20:36 -07:00
int pkt_len ;
struct sk_buff * skb ;
/* If we can't allocate memory, try again next round. */
2007-10-16 01:27:31 -07:00
skb = dev_alloc_skb ( lp - > max_packet ) ;
2005-04-16 15:20:36 -07:00
if ( skb = = NULL ) {
2007-10-16 01:27:32 -07:00
drop_skb - > dev = dev ;
/* Read a packet into drop_skb and don't do anything with it. */
( * lp - > read ) ( lp - > fd , drop_skb , lp ) ;
2009-03-26 15:11:16 +00:00
dev - > stats . rx_dropped + + ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
skb - > dev = dev ;
2007-10-16 01:27:31 -07:00
skb_put ( skb , lp - > max_packet ) ;
2007-03-19 15:30:44 -07:00
skb_reset_mac_header ( skb ) ;
2007-10-16 01:27:31 -07:00
pkt_len = ( * lp - > read ) ( lp - > fd , skb , lp ) ;
2005-04-16 15:20:36 -07:00
if ( pkt_len > 0 ) {
skb_trim ( skb , pkt_len ) ;
skb - > protocol = ( * lp - > protocol ) ( skb ) ;
2009-03-26 15:11:16 +00:00
dev - > stats . rx_bytes + = skb - > len ;
dev - > stats . rx_packets + + ;
2007-12-10 17:14:46 -08:00
netif_rx ( skb ) ;
2005-04-16 15:20:36 -07:00
return pkt_len ;
}
kfree_skb ( skb ) ;
return pkt_len ;
}
2006-12-13 00:33:50 -08:00
static void uml_dev_close ( struct work_struct * work )
[PATCH] uml: fix spinlock recursion and sleep-inside-spinlock in error path
In this error path, when the interface has had a problem, we call dev_close(),
which is disallowed for two reasons:
*) takes again the UML internal spinlock, inside the ->stop method of this
device
*) can be called in process context only, while we're in interrupt context.
I've also thought that calling dev_close() may be a wrong policy to follow,
but it's not up to me to decide that.
However, we may end up with multiple dev_close() queued on the same device.
But the initial test for (dev->flags & IFF_UP) makes this harmless, though -
and dev_close() is supposed to care about races with itself. So there's no
harm in delaying the shutdown, IMHO.
Something to mark the interface as "going to shutdown" would be appreciated,
but dev_deactivate has the same problems as dev_close(), so we can't use it
either.
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-18 17:42:56 -08:00
{
2006-12-13 00:33:50 -08:00
struct uml_net_private * lp =
container_of ( work , struct uml_net_private , work ) ;
dev_close ( lp - > dev ) ;
[PATCH] uml: fix spinlock recursion and sleep-inside-spinlock in error path
In this error path, when the interface has had a problem, we call dev_close(),
which is disallowed for two reasons:
*) takes again the UML internal spinlock, inside the ->stop method of this
device
*) can be called in process context only, while we're in interrupt context.
I've also thought that calling dev_close() may be a wrong policy to follow,
but it's not up to me to decide that.
However, we may end up with multiple dev_close() queued on the same device.
But the initial test for (dev->flags & IFF_UP) makes this harmless, though -
and dev_close() is supposed to care about races with itself. So there's no
harm in delaying the shutdown, IMHO.
Something to mark the interface as "going to shutdown" would be appreciated,
but dev_deactivate has the same problems as dev_close(), so we can't use it
either.
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-18 17:42:56 -08:00
}
2008-04-28 02:13:57 -07:00
static irqreturn_t uml_net_interrupt ( int irq , void * dev_id )
2005-04-16 15:20:36 -07:00
{
struct net_device * dev = dev_id ;
2008-12-04 15:07:33 -08:00
struct uml_net_private * lp = netdev_priv ( dev ) ;
2005-04-16 15:20:36 -07:00
int err ;
2007-10-16 01:27:29 -07:00
if ( ! netif_running ( dev ) )
return IRQ_NONE ;
2005-04-16 15:20:36 -07:00
spin_lock ( & lp - > lock ) ;
2007-10-16 01:27:29 -07:00
while ( ( err = uml_net_rx ( dev ) ) > 0 ) ;
if ( err < 0 ) {
2007-02-10 01:43:53 -08:00
printk ( KERN_ERR
" Device '%s' read returned %d, shutting it down \n " ,
2005-04-16 15:20:36 -07:00
dev - > name , err ) ;
[PATCH] uml: fix spinlock recursion and sleep-inside-spinlock in error path
In this error path, when the interface has had a problem, we call dev_close(),
which is disallowed for two reasons:
*) takes again the UML internal spinlock, inside the ->stop method of this
device
*) can be called in process context only, while we're in interrupt context.
I've also thought that calling dev_close() may be a wrong policy to follow,
but it's not up to me to decide that.
However, we may end up with multiple dev_close() queued on the same device.
But the initial test for (dev->flags & IFF_UP) makes this harmless, though -
and dev_close() is supposed to care about races with itself. So there's no
harm in delaying the shutdown, IMHO.
Something to mark the interface as "going to shutdown" would be appreciated,
but dev_deactivate has the same problems as dev_close(), so we can't use it
either.
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-18 17:42:56 -08:00
/* dev_close can't be called in interrupt context, and takes
* again lp - > lock .
* And dev_close ( ) can be safely called multiple times on the
* same device , since it tests for ( dev - > flags & IFF_UP ) . So
2006-12-13 00:33:50 -08:00
* there ' s no harm in delaying the device shutdown .
* Furthermore , the workqueue will not re - enqueue an already
* enqueued work item . */
schedule_work ( & lp - > work ) ;
2005-04-16 15:20:36 -07:00
goto out ;
}
reactivate_fd ( lp - > fd , UM_ETH_IRQ ) ;
[PATCH] uml: fix spinlock recursion and sleep-inside-spinlock in error path
In this error path, when the interface has had a problem, we call dev_close(),
which is disallowed for two reasons:
*) takes again the UML internal spinlock, inside the ->stop method of this
device
*) can be called in process context only, while we're in interrupt context.
I've also thought that calling dev_close() may be a wrong policy to follow,
but it's not up to me to decide that.
However, we may end up with multiple dev_close() queued on the same device.
But the initial test for (dev->flags & IFF_UP) makes this harmless, though -
and dev_close() is supposed to care about races with itself. So there's no
harm in delaying the shutdown, IMHO.
Something to mark the interface as "going to shutdown" would be appreciated,
but dev_deactivate has the same problems as dev_close(), so we can't use it
either.
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-18 17:42:56 -08:00
out :
2005-04-16 15:20:36 -07:00
spin_unlock ( & lp - > lock ) ;
2007-02-10 01:43:56 -08:00
return IRQ_HANDLED ;
2005-04-16 15:20:36 -07:00
}
static int uml_net_open ( struct net_device * dev )
{
2008-12-04 15:07:33 -08:00
struct uml_net_private * lp = netdev_priv ( dev ) ;
2005-04-16 15:20:36 -07:00
int err ;
2007-10-16 01:27:29 -07:00
if ( lp - > fd > = 0 ) {
2005-04-16 15:20:36 -07:00
err = - ENXIO ;
goto out ;
}
lp - > fd = ( * lp - > open ) ( & lp - > user ) ;
2007-10-16 01:27:29 -07:00
if ( lp - > fd < 0 ) {
2005-04-16 15:20:36 -07:00
err = lp - > fd ;
goto out ;
}
err = um_request_irq ( dev - > irq , lp - > fd , IRQ_READ , uml_net_interrupt ,
2006-07-01 19:29:27 -07:00
IRQF_DISABLED | IRQF_SHARED , dev - > name , dev ) ;
2007-10-16 01:27:29 -07:00
if ( err ! = 0 ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_ERR " uml_net_open: failed to get irq(%d) \n " , err ) ;
err = - ENETUNREACH ;
2006-02-07 12:58:42 -08:00
goto out_close ;
2005-04-16 15:20:36 -07:00
}
lp - > tl . data = ( unsigned long ) & lp - > user ;
netif_start_queue ( dev ) ;
/* clear buffer - it can happen that the host side of the interface
* is full when we get here . In this case , new data is never queued ,
* SIGIOs never arrive , and the net never works .
*/
2007-10-16 01:27:29 -07:00
while ( ( err = uml_net_rx ( dev ) ) > 0 ) ;
2005-04-16 15:20:36 -07:00
2006-02-07 12:58:42 -08:00
spin_lock ( & opened_lock ) ;
list_add ( & lp - > list , & opened ) ;
spin_unlock ( & opened_lock ) ;
return 0 ;
out_close :
2007-10-16 01:27:29 -07:00
if ( lp - > close ! = NULL ) ( * lp - > close ) ( lp - > fd , & lp - > user ) ;
2006-02-07 12:58:42 -08:00
lp - > fd = - 1 ;
out :
return err ;
2005-04-16 15:20:36 -07:00
}
static int uml_net_close ( struct net_device * dev )
{
2008-12-04 15:07:33 -08:00
struct uml_net_private * lp = netdev_priv ( dev ) ;
2007-02-10 01:43:53 -08:00
2005-04-16 15:20:36 -07:00
netif_stop_queue ( dev ) ;
free_irq ( dev - > irq , dev ) ;
2007-10-16 01:27:29 -07:00
if ( lp - > close ! = NULL )
2005-04-16 15:20:36 -07:00
( * lp - > close ) ( lp - > fd , & lp - > user ) ;
lp - > fd = - 1 ;
2006-02-07 12:58:42 -08:00
spin_lock ( & opened_lock ) ;
list_del ( & lp - > list ) ;
spin_unlock ( & opened_lock ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static int uml_net_start_xmit ( struct sk_buff * skb , struct net_device * dev )
{
2008-12-04 15:07:33 -08:00
struct uml_net_private * lp = netdev_priv ( dev ) ;
2005-04-16 15:20:36 -07:00
unsigned long flags ;
int len ;
netif_stop_queue ( dev ) ;
spin_lock_irqsave ( & lp - > lock , flags ) ;
2007-10-16 01:27:31 -07:00
len = ( * lp - > write ) ( lp - > fd , skb , lp ) ;
2005-04-16 15:20:36 -07:00
2007-10-16 01:27:29 -07:00
if ( len = = skb - > len ) {
2009-03-26 15:11:16 +00:00
dev - > stats . tx_packets + + ;
dev - > stats . tx_bytes + = skb - > len ;
2005-04-16 15:20:36 -07:00
dev - > trans_start = jiffies ;
netif_start_queue ( dev ) ;
/* this is normally done in the interrupt when tx finishes */
netif_wake_queue ( dev ) ;
2007-02-10 01:43:53 -08:00
}
2007-10-16 01:27:29 -07:00
else if ( len = = 0 ) {
2005-04-16 15:20:36 -07:00
netif_start_queue ( dev ) ;
2009-03-26 15:11:16 +00:00
dev - > stats . tx_dropped + + ;
2005-04-16 15:20:36 -07:00
}
else {
netif_start_queue ( dev ) ;
printk ( KERN_ERR " uml_net_start_xmit: failed(%d) \n " , len ) ;
}
spin_unlock_irqrestore ( & lp - > lock , flags ) ;
dev_kfree_skb ( skb ) ;
2009-06-23 06:03:08 +00:00
return NETDEV_TX_OK ;
2005-04-16 15:20:36 -07:00
}
static void uml_net_set_multicast_list ( struct net_device * dev )
{
2008-03-26 00:16:29 -07:00
return ;
2005-04-16 15:20:36 -07:00
}
static void uml_net_tx_timeout ( struct net_device * dev )
{
dev - > trans_start = jiffies ;
netif_wake_queue ( dev ) ;
}
static int uml_net_set_mac ( struct net_device * dev , void * addr )
{
2008-12-04 15:07:33 -08:00
struct uml_net_private * lp = netdev_priv ( dev ) ;
2005-04-16 15:20:36 -07:00
struct sockaddr * hwaddr = addr ;
2006-09-27 01:50:31 -07:00
spin_lock_irq ( & lp - > lock ) ;
2006-09-25 23:33:07 -07:00
set_ether_mac ( dev , hwaddr - > sa_data ) ;
2006-09-27 01:50:31 -07:00
spin_unlock_irq ( & lp - > lock ) ;
2005-04-16 15:20:36 -07:00
2007-02-10 01:43:56 -08:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
static int uml_net_change_mtu ( struct net_device * dev , int new_mtu )
{
dev - > mtu = new_mtu ;
2007-10-16 01:27:31 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2005-11-07 06:21:21 +01:00
static void uml_net_get_drvinfo ( struct net_device * dev ,
struct ethtool_drvinfo * info )
2005-04-16 15:20:36 -07:00
{
2005-11-07 06:21:21 +01:00
strcpy ( info - > driver , DRIVER_NAME ) ;
strcpy ( info - > version , " 42 " ) ;
2005-04-16 15:20:36 -07:00
}
2009-09-02 01:03:33 -07:00
static const struct ethtool_ops uml_net_ethtool_ops = {
2005-11-07 06:21:21 +01:00
. get_drvinfo = uml_net_get_drvinfo ,
. get_link = ethtool_op_get_link ,
} ;
2008-04-28 02:13:57 -07:00
static void uml_net_user_timer_expire ( unsigned long _conn )
2005-04-16 15:20:36 -07:00
{
# ifdef undef
struct connection * conn = ( struct connection * ) _conn ;
dprintk ( KERN_INFO " uml_net_user_timer_expire [%p] \n " , conn ) ;
do_connect ( conn ) ;
# endif
}
2007-05-06 14:51:13 -07:00
static void setup_etheraddr ( char * str , unsigned char * addr , char * name )
[PATCH] uml: mechanical tidying after random MACs change
Mechanical, hopefully non-functional changes stemming from
setup_etheraddr always succeeding now that it always assigns a MAC,
either from the command line or generated randomly:
the test of the return of setup_etheraddr is removed, and code
dependent on it succeeding is now unconditional
setup_etheraddr can now be made void
struct uml_net.have_mac is now always 1, so tests of it can be
similarly removed, and uses of it can be replaced with 1
struct uml_net.have_mac is no longer used, so it can be removed
struct uml_net_private.have_mac is copied from struct uml_net, so
it is always 1
tests of uml_net_private.have_mac can be removed
uml_net_private.have_mac can now be removed
the only call to dev_ip_addr was removed, so it can be deleted
It also turns out that setup_etheraddr is called only once, from the same
file, so it can be static and its declaration removed from net_kern.h.
Similarly, set_ether_mac is defined and called only from one file.
Finally, setup_etheraddr and set_ether_mac were moved to avoid needing forward
declarations.
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-09-29 01:58:50 -07:00
{
char * end ;
int i ;
2007-10-16 01:27:29 -07:00
if ( str = = NULL )
[PATCH] uml: mechanical tidying after random MACs change
Mechanical, hopefully non-functional changes stemming from
setup_etheraddr always succeeding now that it always assigns a MAC,
either from the command line or generated randomly:
the test of the return of setup_etheraddr is removed, and code
dependent on it succeeding is now unconditional
setup_etheraddr can now be made void
struct uml_net.have_mac is now always 1, so tests of it can be
similarly removed, and uses of it can be replaced with 1
struct uml_net.have_mac is no longer used, so it can be removed
struct uml_net_private.have_mac is copied from struct uml_net, so
it is always 1
tests of uml_net_private.have_mac can be removed
uml_net_private.have_mac can now be removed
the only call to dev_ip_addr was removed, so it can be deleted
It also turns out that setup_etheraddr is called only once, from the same
file, so it can be static and its declaration removed from net_kern.h.
Similarly, set_ether_mac is defined and called only from one file.
Finally, setup_etheraddr and set_ether_mac were moved to avoid needing forward
declarations.
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-09-29 01:58:50 -07:00
goto random ;
2008-02-04 22:31:25 -08:00
for ( i = 0 ; i < 6 ; i + + ) {
[PATCH] uml: mechanical tidying after random MACs change
Mechanical, hopefully non-functional changes stemming from
setup_etheraddr always succeeding now that it always assigns a MAC,
either from the command line or generated randomly:
the test of the return of setup_etheraddr is removed, and code
dependent on it succeeding is now unconditional
setup_etheraddr can now be made void
struct uml_net.have_mac is now always 1, so tests of it can be
similarly removed, and uses of it can be replaced with 1
struct uml_net.have_mac is no longer used, so it can be removed
struct uml_net_private.have_mac is copied from struct uml_net, so
it is always 1
tests of uml_net_private.have_mac can be removed
uml_net_private.have_mac can now be removed
the only call to dev_ip_addr was removed, so it can be deleted
It also turns out that setup_etheraddr is called only once, from the same
file, so it can be static and its declaration removed from net_kern.h.
Similarly, set_ether_mac is defined and called only from one file.
Finally, setup_etheraddr and set_ether_mac were moved to avoid needing forward
declarations.
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-09-29 01:58:50 -07:00
addr [ i ] = simple_strtoul ( str , & end , 16 ) ;
2007-10-16 01:27:29 -07:00
if ( ( end = = str ) | |
( ( * end ! = ' : ' ) & & ( * end ! = ' , ' ) & & ( * end ! = ' \0 ' ) ) ) {
[PATCH] uml: mechanical tidying after random MACs change
Mechanical, hopefully non-functional changes stemming from
setup_etheraddr always succeeding now that it always assigns a MAC,
either from the command line or generated randomly:
the test of the return of setup_etheraddr is removed, and code
dependent on it succeeding is now unconditional
setup_etheraddr can now be made void
struct uml_net.have_mac is now always 1, so tests of it can be
similarly removed, and uses of it can be replaced with 1
struct uml_net.have_mac is no longer used, so it can be removed
struct uml_net_private.have_mac is copied from struct uml_net, so
it is always 1
tests of uml_net_private.have_mac can be removed
uml_net_private.have_mac can now be removed
the only call to dev_ip_addr was removed, so it can be deleted
It also turns out that setup_etheraddr is called only once, from the same
file, so it can be static and its declaration removed from net_kern.h.
Similarly, set_ether_mac is defined and called only from one file.
Finally, setup_etheraddr and set_ether_mac were moved to avoid needing forward
declarations.
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-09-29 01:58:50 -07:00
printk ( KERN_ERR
" setup_etheraddr: failed to parse '%s' "
" as an ethernet address \n " , str ) ;
goto random ;
}
str = end + 1 ;
}
2007-05-06 14:51:13 -07:00
if ( is_multicast_ether_addr ( addr ) ) {
[PATCH] uml: mechanical tidying after random MACs change
Mechanical, hopefully non-functional changes stemming from
setup_etheraddr always succeeding now that it always assigns a MAC,
either from the command line or generated randomly:
the test of the return of setup_etheraddr is removed, and code
dependent on it succeeding is now unconditional
setup_etheraddr can now be made void
struct uml_net.have_mac is now always 1, so tests of it can be
similarly removed, and uses of it can be replaced with 1
struct uml_net.have_mac is no longer used, so it can be removed
struct uml_net_private.have_mac is copied from struct uml_net, so
it is always 1
tests of uml_net_private.have_mac can be removed
uml_net_private.have_mac can now be removed
the only call to dev_ip_addr was removed, so it can be deleted
It also turns out that setup_etheraddr is called only once, from the same
file, so it can be static and its declaration removed from net_kern.h.
Similarly, set_ether_mac is defined and called only from one file.
Finally, setup_etheraddr and set_ether_mac were moved to avoid needing forward
declarations.
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-09-29 01:58:50 -07:00
printk ( KERN_ERR
2007-05-06 14:51:13 -07:00
" Attempt to assign a multicast ethernet address to a "
[PATCH] uml: mechanical tidying after random MACs change
Mechanical, hopefully non-functional changes stemming from
setup_etheraddr always succeeding now that it always assigns a MAC,
either from the command line or generated randomly:
the test of the return of setup_etheraddr is removed, and code
dependent on it succeeding is now unconditional
setup_etheraddr can now be made void
struct uml_net.have_mac is now always 1, so tests of it can be
similarly removed, and uses of it can be replaced with 1
struct uml_net.have_mac is no longer used, so it can be removed
struct uml_net_private.have_mac is copied from struct uml_net, so
it is always 1
tests of uml_net_private.have_mac can be removed
uml_net_private.have_mac can now be removed
the only call to dev_ip_addr was removed, so it can be deleted
It also turns out that setup_etheraddr is called only once, from the same
file, so it can be static and its declaration removed from net_kern.h.
Similarly, set_ether_mac is defined and called only from one file.
Finally, setup_etheraddr and set_ether_mac were moved to avoid needing forward
declarations.
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-09-29 01:58:50 -07:00
" device disallowed \n " ) ;
goto random ;
}
2007-05-06 14:51:13 -07:00
if ( ! is_valid_ether_addr ( addr ) ) {
printk ( KERN_ERR
" Attempt to assign an invalid ethernet address to a "
" device disallowed \n " ) ;
goto random ;
}
if ( ! is_local_ether_addr ( addr ) ) {
printk ( KERN_WARNING
2008-02-04 22:31:25 -08:00
" Warning: Assigning a globally valid ethernet "
2007-05-08 00:35:04 -07:00
" address to a device \n " ) ;
2008-02-04 22:31:25 -08:00
printk ( KERN_WARNING " You should set the 2nd rightmost bit in "
" the first byte of the MAC, \n " ) ;
2007-05-08 00:35:04 -07:00
printk ( KERN_WARNING " i.e. %02x:%02x:%02x:%02x:%02x:%02x \n " ,
addr [ 0 ] | 0x02 , addr [ 1 ] , addr [ 2 ] , addr [ 3 ] , addr [ 4 ] ,
addr [ 5 ] ) ;
2007-05-06 14:51:13 -07:00
}
[PATCH] uml: mechanical tidying after random MACs change
Mechanical, hopefully non-functional changes stemming from
setup_etheraddr always succeeding now that it always assigns a MAC,
either from the command line or generated randomly:
the test of the return of setup_etheraddr is removed, and code
dependent on it succeeding is now unconditional
setup_etheraddr can now be made void
struct uml_net.have_mac is now always 1, so tests of it can be
similarly removed, and uses of it can be replaced with 1
struct uml_net.have_mac is no longer used, so it can be removed
struct uml_net_private.have_mac is copied from struct uml_net, so
it is always 1
tests of uml_net_private.have_mac can be removed
uml_net_private.have_mac can now be removed
the only call to dev_ip_addr was removed, so it can be deleted
It also turns out that setup_etheraddr is called only once, from the same
file, so it can be static and its declaration removed from net_kern.h.
Similarly, set_ether_mac is defined and called only from one file.
Finally, setup_etheraddr and set_ether_mac were moved to avoid needing forward
declarations.
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-09-29 01:58:50 -07:00
return ;
random :
2007-05-06 14:51:13 -07:00
printk ( KERN_INFO
" Choosing a random ethernet address for device %s \n " , name ) ;
2006-09-29 15:50:28 -07:00
random_ether_addr ( addr ) ;
[PATCH] uml: mechanical tidying after random MACs change
Mechanical, hopefully non-functional changes stemming from
setup_etheraddr always succeeding now that it always assigns a MAC,
either from the command line or generated randomly:
the test of the return of setup_etheraddr is removed, and code
dependent on it succeeding is now unconditional
setup_etheraddr can now be made void
struct uml_net.have_mac is now always 1, so tests of it can be
similarly removed, and uses of it can be replaced with 1
struct uml_net.have_mac is no longer used, so it can be removed
struct uml_net_private.have_mac is copied from struct uml_net, so
it is always 1
tests of uml_net_private.have_mac can be removed
uml_net_private.have_mac can now be removed
the only call to dev_ip_addr was removed, so it can be deleted
It also turns out that setup_etheraddr is called only once, from the same
file, so it can be static and its declaration removed from net_kern.h.
Similarly, set_ether_mac is defined and called only from one file.
Finally, setup_etheraddr and set_ether_mac were moved to avoid needing forward
declarations.
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-09-29 01:58:50 -07:00
}
2005-04-16 15:20:36 -07:00
static DEFINE_SPINLOCK ( devices_lock ) ;
2006-01-06 00:18:54 -08:00
static LIST_HEAD ( devices ) ;
2005-04-16 15:20:36 -07:00
2005-11-09 22:32:44 +00:00
static struct platform_driver uml_net_driver = {
. driver = {
. name = DRIVER_NAME ,
} ,
2005-04-16 15:20:36 -07:00
} ;
2007-05-06 14:51:29 -07:00
static void net_device_release ( struct device * dev )
{
2009-05-04 12:40:54 -07:00
struct uml_net * device = dev_get_drvdata ( dev ) ;
2007-05-06 14:51:29 -07:00
struct net_device * netdev = device - > dev ;
2008-12-04 15:07:33 -08:00
struct uml_net_private * lp = netdev_priv ( netdev ) ;
2007-05-06 14:51:29 -07:00
2007-10-16 01:27:29 -07:00
if ( lp - > remove ! = NULL )
2007-05-06 14:51:29 -07:00
( * lp - > remove ) ( & lp - > user ) ;
list_del ( & device - > list ) ;
kfree ( device ) ;
free_netdev ( netdev ) ;
}
2009-03-26 15:11:17 +00:00
static const struct net_device_ops uml_netdev_ops = {
. ndo_open = uml_net_open ,
. ndo_stop = uml_net_close ,
. ndo_start_xmit = uml_net_start_xmit ,
. ndo_set_multicast_list = uml_net_set_multicast_list ,
. ndo_tx_timeout = uml_net_tx_timeout ,
. ndo_set_mac_address = uml_net_set_mac ,
. ndo_change_mtu = uml_net_change_mtu ,
. ndo_set_mac_address = eth_mac_addr ,
. ndo_validate_addr = eth_validate_addr ,
} ;
2008-02-04 22:31:17 -08:00
/*
* Ensures that platform_driver_register is called only once by
* eth_configure . Will be set in an initcall .
*/
static int driver_registered ;
uml: network interface hotplug error handling
This fixes a number of problems associated with network interface hotplug.
The userspace initialization function can fail in some cases, but the
failure was never passed back to eth_configure, which proceeded with the
configuration. This results in a zombie device that is present, but can't
work. This is fixed by allowing the initialization routines to return an
error, which is checked, and the configuration aborted on failure.
eth_configure failed to check for many failures. Even when it did check,
it didn't undo whatever initializations has already happened, so a present,
but partially initialized and non-working device could result. It now
checks everything that can fail, and bails out, undoing whatever had been
done.
The return value of eth_configure was always ignored, so it is now just
void.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-06 14:51:04 -07:00
static void eth_configure ( int n , void * init , char * mac ,
struct transport * transport )
2005-04-16 15:20:36 -07:00
{
struct uml_net * device ;
struct net_device * dev ;
struct uml_net_private * lp ;
2007-05-06 14:51:15 -07:00
int err , size ;
2005-04-16 15:20:36 -07:00
2007-05-06 14:51:15 -07:00
size = transport - > private_size + sizeof ( struct uml_net_private ) ;
2005-04-16 15:20:36 -07:00
2006-12-12 19:54:52 +01:00
device = kzalloc ( sizeof ( * device ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( device = = NULL ) {
2007-05-06 14:51:14 -07:00
printk ( KERN_ERR " eth_configure failed to allocate struct "
" uml_net \n " ) ;
uml: network interface hotplug error handling
This fixes a number of problems associated with network interface hotplug.
The userspace initialization function can fail in some cases, but the
failure was never passed back to eth_configure, which proceeded with the
configuration. This results in a zombie device that is present, but can't
work. This is fixed by allowing the initialization routines to return an
error, which is checked, and the configuration aborted on failure.
eth_configure failed to check for many failures. Even when it did check,
it didn't undo whatever initializations has already happened, so a present,
but partially initialized and non-working device could result. It now
checks everything that can fail, and bails out, undoing whatever had been
done.
The return value of eth_configure was always ignored, so it is now just
void.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-06 14:51:04 -07:00
return ;
2005-04-16 15:20:36 -07:00
}
2007-05-06 14:51:14 -07:00
dev = alloc_etherdev ( size ) ;
if ( dev = = NULL ) {
printk ( KERN_ERR " eth_configure: failed to allocate struct "
" net_device for eth%d \n " , n ) ;
goto out_free_device ;
}
2005-04-16 15:20:36 -07:00
INIT_LIST_HEAD ( & device - > list ) ;
device - > index = n ;
2007-05-06 14:51:13 -07:00
/* If this name ends up conflicting with an existing registered
* netdevice , that is OK , register_netdev { , ice } ( ) will notice this
* and fail .
*/
2007-05-06 14:51:14 -07:00
snprintf ( dev - > name , sizeof ( dev - > name ) , " eth%d " , n ) ;
2007-05-06 14:51:13 -07:00
2007-05-06 14:51:14 -07:00
setup_etheraddr ( mac , device - > mac , dev - > name ) ;
2005-04-16 15:20:36 -07:00
2008-10-27 17:47:26 -07:00
printk ( KERN_INFO " Netdevice %d (%pM) : " , n , device - > mac ) ;
2005-04-16 15:20:36 -07:00
2008-12-04 15:07:33 -08:00
lp = netdev_priv ( dev ) ;
2006-01-18 17:42:55 -08:00
/* This points to the transport private data. It's still clear, but we
* must memset it to 0 * now * . Let ' s help the drivers . */
memset ( lp , 0 , size ) ;
2006-12-13 00:33:50 -08:00
INIT_WORK ( & lp - > work , uml_dev_close ) ;
2006-01-18 17:42:55 -08:00
2005-04-16 15:20:36 -07:00
/* sysfs register */
if ( ! driver_registered ) {
2005-11-09 22:32:44 +00:00
platform_driver_register ( & uml_net_driver ) ;
2005-04-16 15:20:36 -07:00
driver_registered = 1 ;
}
device - > pdev . id = n ;
device - > pdev . name = DRIVER_NAME ;
2007-05-06 14:51:29 -07:00
device - > pdev . dev . release = net_device_release ;
2009-05-04 12:40:54 -07:00
dev_set_drvdata ( & device - > pdev . dev , device ) ;
2007-10-16 01:27:29 -07:00
if ( platform_device_register ( & device - > pdev ) )
uml: network interface hotplug error handling
This fixes a number of problems associated with network interface hotplug.
The userspace initialization function can fail in some cases, but the
failure was never passed back to eth_configure, which proceeded with the
configuration. This results in a zombie device that is present, but can't
work. This is fixed by allowing the initialization routines to return an
error, which is checked, and the configuration aborted on failure.
eth_configure failed to check for many failures. Even when it did check,
it didn't undo whatever initializations has already happened, so a present,
but partially initialized and non-working device could result. It now
checks everything that can fail, and bails out, undoing whatever had been
done.
The return value of eth_configure was always ignored, so it is now just
void.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-06 14:51:04 -07:00
goto out_free_netdev ;
2005-04-16 15:20:36 -07:00
SET_NETDEV_DEV ( dev , & device - > pdev . dev ) ;
device - > dev = dev ;
uml: network interface hotplug error handling
This fixes a number of problems associated with network interface hotplug.
The userspace initialization function can fail in some cases, but the
failure was never passed back to eth_configure, which proceeded with the
configuration. This results in a zombie device that is present, but can't
work. This is fixed by allowing the initialization routines to return an
error, which is checked, and the configuration aborted on failure.
eth_configure failed to check for many failures. Even when it did check,
it didn't undo whatever initializations has already happened, so a present,
but partially initialized and non-working device could result. It now
checks everything that can fail, and bails out, undoing whatever had been
done.
The return value of eth_configure was always ignored, so it is now just
void.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-06 14:51:04 -07:00
/*
* These just fill in a data structure , so there ' s no failure
* to be worried about .
*/
2005-04-16 15:20:36 -07:00
( * transport - > kern - > init ) ( dev , init ) ;
* lp = ( ( struct uml_net_private )
{ . list = LIST_HEAD_INIT ( lp - > list ) ,
. dev = dev ,
. fd = - 1 ,
. mac = { 0xfe , 0xfd , 0x0 , 0x0 , 0x0 , 0x0 } ,
2007-10-16 01:27:31 -07:00
. max_packet = transport - > user - > max_packet ,
2005-04-16 15:20:36 -07:00
. protocol = transport - > kern - > protocol ,
. open = transport - > user - > open ,
. close = transport - > user - > close ,
. remove = transport - > user - > remove ,
. read = transport - > kern - > read ,
. write = transport - > kern - > write ,
. add_address = transport - > user - > add_address ,
2007-10-16 01:27:31 -07:00
. delete_address = transport - > user - > delete_address } ) ;
2005-04-16 15:20:36 -07:00
init_timer ( & lp - > tl ) ;
spin_lock_init ( & lp - > lock ) ;
lp - > tl . function = uml_net_user_timer_expire ;
[PATCH] uml: mechanical tidying after random MACs change
Mechanical, hopefully non-functional changes stemming from
setup_etheraddr always succeeding now that it always assigns a MAC,
either from the command line or generated randomly:
the test of the return of setup_etheraddr is removed, and code
dependent on it succeeding is now unconditional
setup_etheraddr can now be made void
struct uml_net.have_mac is now always 1, so tests of it can be
similarly removed, and uses of it can be replaced with 1
struct uml_net.have_mac is no longer used, so it can be removed
struct uml_net_private.have_mac is copied from struct uml_net, so
it is always 1
tests of uml_net_private.have_mac can be removed
uml_net_private.have_mac can now be removed
the only call to dev_ip_addr was removed, so it can be deleted
It also turns out that setup_etheraddr is called only once, from the same
file, so it can be static and its declaration removed from net_kern.h.
Similarly, set_ether_mac is defined and called only from one file.
Finally, setup_etheraddr and set_ether_mac were moved to avoid needing forward
declarations.
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-09-29 01:58:50 -07:00
memcpy ( lp - > mac , device - > mac , sizeof ( lp - > mac ) ) ;
2005-04-16 15:20:36 -07:00
uml: network interface hotplug error handling
This fixes a number of problems associated with network interface hotplug.
The userspace initialization function can fail in some cases, but the
failure was never passed back to eth_configure, which proceeded with the
configuration. This results in a zombie device that is present, but can't
work. This is fixed by allowing the initialization routines to return an
error, which is checked, and the configuration aborted on failure.
eth_configure failed to check for many failures. Even when it did check,
it didn't undo whatever initializations has already happened, so a present,
but partially initialized and non-working device could result. It now
checks everything that can fail, and bails out, undoing whatever had been
done.
The return value of eth_configure was always ignored, so it is now just
void.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-06 14:51:04 -07:00
if ( ( transport - > user - > init ! = NULL ) & &
( ( * transport - > user - > init ) ( & lp - > user , dev ) ! = 0 ) )
goto out_unregister ;
2005-04-16 15:20:36 -07:00
[PATCH] uml: mechanical tidying after random MACs change
Mechanical, hopefully non-functional changes stemming from
setup_etheraddr always succeeding now that it always assigns a MAC,
either from the command line or generated randomly:
the test of the return of setup_etheraddr is removed, and code
dependent on it succeeding is now unconditional
setup_etheraddr can now be made void
struct uml_net.have_mac is now always 1, so tests of it can be
similarly removed, and uses of it can be replaced with 1
struct uml_net.have_mac is no longer used, so it can be removed
struct uml_net_private.have_mac is copied from struct uml_net, so
it is always 1
tests of uml_net_private.have_mac can be removed
uml_net_private.have_mac can now be removed
the only call to dev_ip_addr was removed, so it can be deleted
It also turns out that setup_etheraddr is called only once, from the same
file, so it can be static and its declaration removed from net_kern.h.
Similarly, set_ether_mac is defined and called only from one file.
Finally, setup_etheraddr and set_ether_mac were moved to avoid needing forward
declarations.
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-09-29 01:58:50 -07:00
set_ether_mac ( dev , device - > mac ) ;
2007-10-16 01:27:31 -07:00
dev - > mtu = transport - > user - > mtu ;
2009-03-26 15:11:17 +00:00
dev - > netdev_ops = & uml_netdev_ops ;
uml: network interface hotplug error handling
This fixes a number of problems associated with network interface hotplug.
The userspace initialization function can fail in some cases, but the
failure was never passed back to eth_configure, which proceeded with the
configuration. This results in a zombie device that is present, but can't
work. This is fixed by allowing the initialization routines to return an
error, which is checked, and the configuration aborted on failure.
eth_configure failed to check for many failures. Even when it did check,
it didn't undo whatever initializations has already happened, so a present,
but partially initialized and non-working device could result. It now
checks everything that can fail, and bails out, undoing whatever had been
done.
The return value of eth_configure was always ignored, so it is now just
void.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-06 14:51:04 -07:00
dev - > ethtool_ops = & uml_net_ethtool_ops ;
dev - > watchdog_timeo = ( HZ > > 1 ) ;
dev - > irq = UM_ETH_IRQ ;
2005-04-16 15:20:36 -07:00
2007-10-16 01:27:32 -07:00
err = update_drop_skb ( lp - > max_packet ) ;
if ( err )
goto out_undo_user_init ;
uml: network interface hotplug error handling
This fixes a number of problems associated with network interface hotplug.
The userspace initialization function can fail in some cases, but the
failure was never passed back to eth_configure, which proceeded with the
configuration. This results in a zombie device that is present, but can't
work. This is fixed by allowing the initialization routines to return an
error, which is checked, and the configuration aborted on failure.
eth_configure failed to check for many failures. Even when it did check,
it didn't undo whatever initializations has already happened, so a present,
but partially initialized and non-working device could result. It now
checks everything that can fail, and bails out, undoing whatever had been
done.
The return value of eth_configure was always ignored, so it is now just
void.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-06 14:51:04 -07:00
rtnl_lock ( ) ;
err = register_netdevice ( dev ) ;
rtnl_unlock ( ) ;
if ( err )
goto out_undo_user_init ;
spin_lock ( & devices_lock ) ;
list_add ( & device - > list , & devices ) ;
spin_unlock ( & devices_lock ) ;
return ;
out_undo_user_init :
2007-05-06 14:51:14 -07:00
if ( transport - > user - > remove ! = NULL )
uml: network interface hotplug error handling
This fixes a number of problems associated with network interface hotplug.
The userspace initialization function can fail in some cases, but the
failure was never passed back to eth_configure, which proceeded with the
configuration. This results in a zombie device that is present, but can't
work. This is fixed by allowing the initialization routines to return an
error, which is checked, and the configuration aborted on failure.
eth_configure failed to check for many failures. Even when it did check,
it didn't undo whatever initializations has already happened, so a present,
but partially initialized and non-working device could result. It now
checks everything that can fail, and bails out, undoing whatever had been
done.
The return value of eth_configure was always ignored, so it is now just
void.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-06 14:51:04 -07:00
( * transport - > user - > remove ) ( & lp - > user ) ;
out_unregister :
platform_device_unregister ( & device - > pdev ) ;
2007-05-08 00:35:04 -07:00
return ; /* platform_device_unregister frees dev and device */
uml: network interface hotplug error handling
This fixes a number of problems associated with network interface hotplug.
The userspace initialization function can fail in some cases, but the
failure was never passed back to eth_configure, which proceeded with the
configuration. This results in a zombie device that is present, but can't
work. This is fixed by allowing the initialization routines to return an
error, which is checked, and the configuration aborted on failure.
eth_configure failed to check for many failures. Even when it did check,
it didn't undo whatever initializations has already happened, so a present,
but partially initialized and non-working device could result. It now
checks everything that can fail, and bails out, undoing whatever had been
done.
The return value of eth_configure was always ignored, so it is now just
void.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-06 14:51:04 -07:00
out_free_netdev :
free_netdev ( dev ) ;
2007-05-06 14:51:14 -07:00
out_free_device :
uml: network interface hotplug error handling
This fixes a number of problems associated with network interface hotplug.
The userspace initialization function can fail in some cases, but the
failure was never passed back to eth_configure, which proceeded with the
configuration. This results in a zombie device that is present, but can't
work. This is fixed by allowing the initialization routines to return an
error, which is checked, and the configuration aborted on failure.
eth_configure failed to check for many failures. Even when it did check,
it didn't undo whatever initializations has already happened, so a present,
but partially initialized and non-working device could result. It now
checks everything that can fail, and bails out, undoing whatever had been
done.
The return value of eth_configure was always ignored, so it is now just
void.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-06 14:51:04 -07:00
kfree ( device ) ;
2005-04-16 15:20:36 -07:00
}
static struct uml_net * find_device ( int n )
{
struct uml_net * device ;
struct list_head * ele ;
spin_lock ( & devices_lock ) ;
2007-10-16 01:27:29 -07:00
list_for_each ( ele , & devices ) {
2005-04-16 15:20:36 -07:00
device = list_entry ( ele , struct uml_net , list ) ;
2007-10-16 01:27:29 -07:00
if ( device - > index = = n )
2005-04-16 15:20:36 -07:00
goto out ;
}
device = NULL ;
out :
spin_unlock ( & devices_lock ) ;
2007-02-10 01:43:56 -08:00
return device ;
2005-04-16 15:20:36 -07:00
}
2007-02-10 01:43:53 -08:00
static int eth_parse ( char * str , int * index_out , char * * str_out ,
char * * error_out )
2005-04-16 15:20:36 -07:00
{
char * end ;
2009-08-18 11:18:35 -07:00
int n , err = - EINVAL ;
2005-04-16 15:20:36 -07:00
n = simple_strtoul ( str , & end , 0 ) ;
2007-10-16 01:27:29 -07:00
if ( end = = str ) {
2007-02-10 01:43:53 -08:00
* error_out = " Bad device number " ;
return err ;
2005-04-16 15:20:36 -07:00
}
2007-02-10 01:43:53 -08:00
2005-04-16 15:20:36 -07:00
str = end ;
2007-10-16 01:27:29 -07:00
if ( * str ! = ' = ' ) {
2007-02-10 01:43:53 -08:00
* error_out = " Expected '=' after device number " ;
return err ;
2005-04-16 15:20:36 -07:00
}
2007-02-10 01:43:53 -08:00
2005-04-16 15:20:36 -07:00
str + + ;
2007-10-16 01:27:29 -07:00
if ( find_device ( n ) ) {
2007-02-10 01:43:53 -08:00
* error_out = " Device already configured " ;
return err ;
2005-04-16 15:20:36 -07:00
}
2007-02-10 01:43:53 -08:00
* index_out = n ;
2005-04-16 15:20:36 -07:00
* str_out = str ;
2007-02-10 01:43:53 -08:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
struct eth_init {
struct list_head list ;
char * init ;
int index ;
} ;
2007-02-10 01:43:56 -08:00
static DEFINE_SPINLOCK ( transports_lock ) ;
static LIST_HEAD ( transports ) ;
2005-04-16 15:20:36 -07:00
/* Filled in during early boot */
2007-02-10 01:44:04 -08:00
static LIST_HEAD ( eth_cmd_line ) ;
2005-04-16 15:20:36 -07:00
static int check_transport ( struct transport * transport , char * eth , int n ,
void * * init_out , char * * mac_out )
{
int len ;
len = strlen ( transport - > name ) ;
2007-10-16 01:27:29 -07:00
if ( strncmp ( eth , transport - > name , len ) )
2007-02-10 01:43:56 -08:00
return 0 ;
2005-04-16 15:20:36 -07:00
eth + = len ;
2007-10-16 01:27:29 -07:00
if ( * eth = = ' , ' )
2005-04-16 15:20:36 -07:00
eth + + ;
2007-10-16 01:27:29 -07:00
else if ( * eth ! = ' \0 ' )
2007-02-10 01:43:56 -08:00
return 0 ;
2005-04-16 15:20:36 -07:00
* init_out = kmalloc ( transport - > setup_size , GFP_KERNEL ) ;
2007-10-16 01:27:29 -07:00
if ( * init_out = = NULL )
2007-02-10 01:43:56 -08:00
return 1 ;
2005-04-16 15:20:36 -07:00
2007-10-16 01:27:29 -07:00
if ( ! transport - > setup ( eth , mac_out , * init_out ) ) {
2005-04-16 15:20:36 -07:00
kfree ( * init_out ) ;
* init_out = NULL ;
}
2007-02-10 01:43:56 -08:00
return 1 ;
2005-04-16 15:20:36 -07:00
}
void register_transport ( struct transport * new )
{
struct list_head * ele , * next ;
struct eth_init * eth ;
void * init ;
char * mac = NULL ;
int match ;
2007-02-10 01:43:56 -08:00
spin_lock ( & transports_lock ) ;
BUG_ON ( ! list_empty ( & new - > list ) ) ;
2005-04-16 15:20:36 -07:00
list_add ( & new - > list , & transports ) ;
2007-02-10 01:43:56 -08:00
spin_unlock ( & transports_lock ) ;
2005-04-16 15:20:36 -07:00
2007-10-16 01:27:29 -07:00
list_for_each_safe ( ele , next , & eth_cmd_line ) {
2005-04-16 15:20:36 -07:00
eth = list_entry ( ele , struct eth_init , list ) ;
match = check_transport ( new , eth - > init , eth - > index , & init ,
& mac ) ;
2007-10-16 01:27:29 -07:00
if ( ! match )
2005-04-16 15:20:36 -07:00
continue ;
2007-10-16 01:27:29 -07:00
else if ( init ! = NULL ) {
2005-04-16 15:20:36 -07:00
eth_configure ( eth - > index , init , mac , new ) ;
kfree ( init ) ;
}
list_del ( & eth - > list ) ;
}
}
static int eth_setup_common ( char * str , int index )
{
struct list_head * ele ;
struct transport * transport ;
void * init ;
char * mac = NULL ;
2007-02-10 01:44:04 -08:00
int found = 0 ;
2005-04-16 15:20:36 -07:00
2007-02-10 01:44:04 -08:00
spin_lock ( & transports_lock ) ;
2007-10-16 01:27:29 -07:00
list_for_each ( ele , & transports ) {
2005-04-16 15:20:36 -07:00
transport = list_entry ( ele , struct transport , list ) ;
2007-10-16 01:27:29 -07:00
if ( ! check_transport ( transport , str , index , & init , & mac ) )
2005-04-16 15:20:36 -07:00
continue ;
2007-10-16 01:27:29 -07:00
if ( init ! = NULL ) {
2005-04-16 15:20:36 -07:00
eth_configure ( index , init , mac , transport ) ;
kfree ( init ) ;
}
2007-02-10 01:44:04 -08:00
found = 1 ;
break ;
2005-04-16 15:20:36 -07:00
}
2007-02-10 01:44:04 -08:00
spin_unlock ( & transports_lock ) ;
return found ;
2005-04-16 15:20:36 -07:00
}
2007-07-23 18:43:48 -07:00
static int __init eth_setup ( char * str )
2005-04-16 15:20:36 -07:00
{
struct eth_init * new ;
2007-02-10 01:43:53 -08:00
char * error ;
2005-04-16 15:20:36 -07:00
int n , err ;
2007-02-10 01:43:53 -08:00
err = eth_parse ( str , & n , & str , & error ) ;
2007-10-16 01:27:29 -07:00
if ( err ) {
2007-02-10 01:43:53 -08:00
printk ( KERN_ERR " eth_setup - Couldn't parse '%s' : %s \n " ,
str , error ) ;
2006-09-27 01:50:43 -07:00
return 1 ;
2007-02-10 01:43:53 -08:00
}
2005-04-16 15:20:36 -07:00
2006-09-27 01:50:43 -07:00
new = alloc_bootmem ( sizeof ( * new ) ) ;
2007-10-16 01:27:29 -07:00
if ( new = = NULL ) {
printk ( KERN_ERR " eth_init : alloc_bootmem failed \n " ) ;
2006-09-27 01:50:43 -07:00
return 1 ;
2005-04-16 15:20:36 -07:00
}
INIT_LIST_HEAD ( & new - > list ) ;
new - > index = n ;
new - > init = str ;
list_add_tail ( & new - > list , & eth_cmd_line ) ;
2006-09-27 01:50:43 -07:00
return 1 ;
2005-04-16 15:20:36 -07:00
}
__setup ( " eth " , eth_setup ) ;
__uml_help ( eth_setup ,
" eth[0-9]+=<transport>,<options> \n "
" Configure a network device. \n \n "
) ;
2007-02-10 01:43:53 -08:00
static int net_config ( char * str , char * * error_out )
2005-04-16 15:20:36 -07:00
{
int n , err ;
2007-02-10 01:43:53 -08:00
err = eth_parse ( str , & n , & str , error_out ) ;
2007-10-16 01:27:29 -07:00
if ( err )
2007-02-10 01:43:53 -08:00
return err ;
2005-04-16 15:20:36 -07:00
2007-02-10 01:43:53 -08:00
/* This string is broken up and the pieces used by the underlying
* driver . So , it is freed only if eth_setup_common fails .
*/
2006-01-06 00:18:48 -08:00
str = kstrdup ( str , GFP_KERNEL ) ;
2007-10-16 01:27:29 -07:00
if ( str = = NULL ) {
2007-02-10 01:43:53 -08:00
* error_out = " net_config failed to strdup string " ;
return - ENOMEM ;
2005-04-16 15:20:36 -07:00
}
err = ! eth_setup_common ( str , n ) ;
2007-10-16 01:27:29 -07:00
if ( err )
2005-04-16 15:20:36 -07:00
kfree ( str ) ;
2007-10-16 01:27:29 -07:00
return err ;
2005-04-16 15:20:36 -07:00
}
2005-06-25 14:55:25 -07:00
static int net_id ( char * * str , int * start_out , int * end_out )
{
2007-10-16 01:27:29 -07:00
char * end ;
int n ;
2005-06-25 14:55:25 -07:00
n = simple_strtoul ( * str , & end , 0 ) ;
2007-10-16 01:27:29 -07:00
if ( ( * end ! = ' \0 ' ) | | ( end = = * str ) )
2005-06-25 14:55:25 -07:00
return - 1 ;
2007-10-16 01:27:29 -07:00
* start_out = n ;
* end_out = n ;
* str = end ;
return n ;
2005-06-25 14:55:25 -07:00
}
2007-02-10 01:43:53 -08:00
static int net_remove ( int n , char * * error_out )
2005-04-16 15:20:36 -07:00
{
struct uml_net * device ;
struct net_device * dev ;
struct uml_net_private * lp ;
device = find_device ( n ) ;
2007-10-16 01:27:29 -07:00
if ( device = = NULL )
2005-06-25 14:55:25 -07:00
return - ENODEV ;
2005-04-16 15:20:36 -07:00
dev = device - > dev ;
2008-12-04 15:07:33 -08:00
lp = netdev_priv ( dev ) ;
2007-10-16 01:27:29 -07:00
if ( lp - > fd > 0 )
2007-02-10 01:43:56 -08:00
return - EBUSY ;
2005-04-16 15:20:36 -07:00
unregister_netdev ( dev ) ;
platform_device_unregister ( & device - > pdev ) ;
2005-06-25 14:55:25 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
static struct mc_device net_mc = {
2007-02-10 01:44:01 -08:00
. list = LIST_HEAD_INIT ( net_mc . list ) ,
2005-04-16 15:20:36 -07:00
. name = " eth " ,
. config = net_config ,
. get_config = NULL ,
2007-02-10 01:43:56 -08:00
. id = net_id ,
2005-04-16 15:20:36 -07:00
. remove = net_remove ,
} ;
2007-11-14 17:00:28 -08:00
# ifdef CONFIG_INET
2005-04-16 15:20:36 -07:00
static int uml_inetaddr_event ( struct notifier_block * this , unsigned long event ,
void * ptr )
{
struct in_ifaddr * ifa = ptr ;
struct net_device * dev = ifa - > ifa_dev - > dev ;
struct uml_net_private * lp ;
void ( * proc ) ( unsigned char * , unsigned char * , void * ) ;
unsigned char addr_buf [ 4 ] , netmask_buf [ 4 ] ;
2009-04-02 16:56:49 -07:00
if ( dev - > netdev_ops - > ndo_open ! = uml_net_open )
2007-02-10 01:43:56 -08:00
return NOTIFY_DONE ;
2005-04-16 15:20:36 -07:00
2008-12-04 15:07:33 -08:00
lp = netdev_priv ( dev ) ;
2005-04-16 15:20:36 -07:00
proc = NULL ;
2007-10-16 01:27:29 -07:00
switch ( event ) {
2005-04-16 15:20:36 -07:00
case NETDEV_UP :
proc = lp - > add_address ;
break ;
case NETDEV_DOWN :
proc = lp - > delete_address ;
break ;
}
2007-10-16 01:27:29 -07:00
if ( proc ! = NULL ) {
2005-11-07 00:58:47 -08:00
memcpy ( addr_buf , & ifa - > ifa_address , sizeof ( addr_buf ) ) ;
memcpy ( netmask_buf , & ifa - > ifa_mask , sizeof ( netmask_buf ) ) ;
2005-04-16 15:20:36 -07:00
( * proc ) ( addr_buf , netmask_buf , & lp - > user ) ;
}
2007-02-10 01:43:56 -08:00
return NOTIFY_DONE ;
2005-04-16 15:20:36 -07:00
}
2007-02-10 01:44:04 -08:00
/* uml_net_init shouldn't be called twice on two CPUs at the same time */
2008-04-28 02:13:57 -07:00
static struct notifier_block uml_inetaddr_notifier = {
2005-04-16 15:20:36 -07:00
. notifier_call = uml_inetaddr_event ,
} ;
2007-11-14 17:00:28 -08:00
static void inet_register ( void )
2005-04-16 15:20:36 -07:00
{
struct list_head * ele ;
2007-02-10 01:43:53 -08:00
struct uml_net_private * lp ;
2005-04-16 15:20:36 -07:00
struct in_device * ip ;
struct in_ifaddr * in ;
register_inetaddr_notifier ( & uml_inetaddr_notifier ) ;
/* Devices may have been opened already, so the uml_inetaddr_notifier
* didn ' t get a chance to run for them . This fakes it so that
* addresses which have already been set up get handled properly .
*/
2007-02-10 01:44:04 -08:00
spin_lock ( & opened_lock ) ;
2007-10-16 01:27:29 -07:00
list_for_each ( ele , & opened ) {
2005-04-16 15:20:36 -07:00
lp = list_entry ( ele , struct uml_net_private , list ) ;
ip = lp - > dev - > ip_ptr ;
2007-10-16 01:27:29 -07:00
if ( ip = = NULL )
2007-02-10 01:44:04 -08:00
continue ;
2005-04-16 15:20:36 -07:00
in = ip - > ifa_list ;
2007-10-16 01:27:29 -07:00
while ( in ! = NULL ) {
2005-04-16 15:20:36 -07:00
uml_inetaddr_event ( NULL , NETDEV_UP , in ) ;
in = in - > ifa_next ;
}
2007-02-10 01:43:53 -08:00
}
2007-02-10 01:44:04 -08:00
spin_unlock ( & opened_lock ) ;
2007-11-14 17:00:28 -08:00
}
# else
static inline void inet_register ( void )
{
}
# endif
2005-04-16 15:20:36 -07:00
2007-11-14 17:00:28 -08:00
static int uml_net_init ( void )
{
mconsole_register_dev ( & net_mc ) ;
inet_register ( ) ;
2007-02-10 01:44:04 -08:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
__initcall ( uml_net_init ) ;
static void close_devices ( void )
{
struct list_head * ele ;
struct uml_net_private * lp ;
2007-02-10 01:44:04 -08:00
spin_lock ( & opened_lock ) ;
2007-10-16 01:27:29 -07:00
list_for_each ( ele , & opened ) {
2005-04-16 15:20:36 -07:00
lp = list_entry ( ele , struct uml_net_private , list ) ;
2006-01-06 00:19:06 -08:00
free_irq ( lp - > dev - > irq , lp - > dev ) ;
2007-10-16 01:27:29 -07:00
if ( ( lp - > close ! = NULL ) & & ( lp - > fd > = 0 ) )
2005-04-16 15:20:36 -07:00
( * lp - > close ) ( lp - > fd , & lp - > user ) ;
2007-10-16 01:27:29 -07:00
if ( lp - > remove ! = NULL )
2007-02-10 01:44:04 -08:00
( * lp - > remove ) ( & lp - > user ) ;
2005-04-16 15:20:36 -07:00
}
2007-02-10 01:44:04 -08:00
spin_unlock ( & opened_lock ) ;
2005-04-16 15:20:36 -07:00
}
__uml_exitcall ( close_devices ) ;
2007-02-10 01:43:53 -08:00
void iter_addresses ( void * d , void ( * cb ) ( unsigned char * , unsigned char * ,
void * ) ,
2005-04-16 15:20:36 -07:00
void * arg )
{
struct net_device * dev = d ;
struct in_device * ip = dev - > ip_ptr ;
struct in_ifaddr * in ;
unsigned char address [ 4 ] , netmask [ 4 ] ;
2007-10-16 01:27:29 -07:00
if ( ip = = NULL ) return ;
2005-04-16 15:20:36 -07:00
in = ip - > ifa_list ;
2007-10-16 01:27:29 -07:00
while ( in ! = NULL ) {
2005-11-07 00:58:47 -08:00
memcpy ( address , & in - > ifa_address , sizeof ( address ) ) ;
memcpy ( netmask , & in - > ifa_mask , sizeof ( netmask ) ) ;
2005-04-16 15:20:36 -07:00
( * cb ) ( address , netmask , arg ) ;
in = in - > ifa_next ;
}
}
int dev_netmask ( void * d , void * m )
{
struct net_device * dev = d ;
struct in_device * ip = dev - > ip_ptr ;
struct in_ifaddr * in ;
2006-09-28 18:00:55 -07:00
__be32 * mask_out = m ;
2005-04-16 15:20:36 -07:00
2007-10-16 01:27:29 -07:00
if ( ip = = NULL )
return 1 ;
2005-04-16 15:20:36 -07:00
in = ip - > ifa_list ;
2007-10-16 01:27:29 -07:00
if ( in = = NULL )
return 1 ;
2005-04-16 15:20:36 -07:00
* mask_out = in - > ifa_mask ;
2007-10-16 01:27:29 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
void * get_output_buffer ( int * len_out )
{
void * ret ;
ret = ( void * ) __get_free_pages ( GFP_KERNEL , 0 ) ;
2007-10-16 01:27:29 -07:00
if ( ret ) * len_out = PAGE_SIZE ;
2005-04-16 15:20:36 -07:00
else * len_out = 0 ;
2007-02-10 01:43:56 -08:00
return ret ;
2005-04-16 15:20:36 -07:00
}
void free_output_buffer ( void * buffer )
{
free_pages ( ( unsigned long ) buffer , 0 ) ;
}
2007-02-10 01:43:53 -08:00
int tap_setup_common ( char * str , char * type , char * * dev_name , char * * mac_out ,
2005-04-16 15:20:36 -07:00
char * * gate_addr )
{
char * remain ;
remain = split_if_spec ( str , dev_name , mac_out , gate_addr , NULL ) ;
2007-10-16 01:27:29 -07:00
if ( remain ! = NULL ) {
printk ( KERN_ERR " tap_setup_common - Extra garbage on "
" specification : '%s' \n " , remain ) ;
return 1 ;
2005-04-16 15:20:36 -07:00
}
2007-10-16 01:27:29 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
unsigned short eth_protocol ( struct sk_buff * skb )
{
2007-10-16 01:27:29 -07:00
return eth_type_trans ( skb , skb - > dev ) ;
2005-04-16 15:20:36 -07:00
}