2007-07-15 05:55:06 +04:00
/*
* Copyright ( c ) 2007 Patrick McHardy < kaber @ trash . net >
*
* 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 of
* the License , or ( at your option ) any later version .
*
* The code this is based on carried the following copyright notice :
* - - -
* ( C ) Copyright 2001 - 2006
* Alex Zeffertt , Cambridge Broadband Ltd , ajz @ cambridgebroadband . com
* Re - worked by Ben Greear < greearb @ candelatech . com >
* - - -
*/
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/string.h>
2008-05-12 23:21:05 +04:00
# include <linux/rculist.h>
2007-07-15 05:55:06 +04:00
# include <linux/notifier.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/ethtool.h>
# include <linux/if_arp.h>
# include <linux/if_link.h>
# include <linux/if_macvlan.h>
# include <net/rtnetlink.h>
2009-11-26 09:07:10 +03:00
# include <net/xfrm.h>
2007-07-15 05:55:06 +04:00
# define MACVLAN_HASH_SIZE (1 << BITS_PER_BYTE)
struct macvlan_port {
struct net_device * dev ;
struct hlist_head vlan_hash [ MACVLAN_HASH_SIZE ] ;
struct list_head vlans ;
2010-06-07 05:36:29 +04:00
struct rcu_head rcu ;
2010-10-28 17:10:50 +04:00
bool passthru ;
2011-03-22 04:22:22 +03:00
int count ;
2007-07-15 05:55:06 +04:00
} ;
2011-03-22 04:22:22 +03:00
static void macvlan_port_destroy ( struct net_device * dev ) ;
2010-06-15 07:27:57 +04:00
# define macvlan_port_get_rcu(dev) \
( ( struct macvlan_port * ) rcu_dereference ( dev - > rx_handler_data ) )
# define macvlan_port_get(dev) ((struct macvlan_port *) dev->rx_handler_data)
# define macvlan_port_exists(dev) (dev->priv_flags & IFF_MACVLAN_PORT)
2007-07-15 05:55:06 +04:00
static struct macvlan_dev * macvlan_hash_lookup ( const struct macvlan_port * port ,
const unsigned char * addr )
{
struct macvlan_dev * vlan ;
struct hlist_node * n ;
hlist_for_each_entry_rcu ( vlan , n , & port - > vlan_hash [ addr [ 5 ] ] , hlist ) {
2009-09-01 09:46:05 +04:00
if ( ! compare_ether_addr_64bits ( vlan - > dev - > dev_addr , addr ) )
2007-07-15 05:55:06 +04:00
return vlan ;
}
return NULL ;
}
2009-03-13 23:16:13 +03:00
static void macvlan_hash_add ( struct macvlan_dev * vlan )
{
struct macvlan_port * port = vlan - > port ;
const unsigned char * addr = vlan - > dev - > dev_addr ;
hlist_add_head_rcu ( & vlan - > hlist , & port - > vlan_hash [ addr [ 5 ] ] ) ;
}
2011-05-19 16:24:16 +04:00
static void macvlan_hash_del ( struct macvlan_dev * vlan , bool sync )
2009-03-13 23:16:13 +03:00
{
hlist_del_rcu ( & vlan - > hlist ) ;
2011-05-19 16:24:16 +04:00
if ( sync )
synchronize_rcu ( ) ;
2009-03-13 23:16:13 +03:00
}
static void macvlan_hash_change_addr ( struct macvlan_dev * vlan ,
const unsigned char * addr )
{
2011-05-19 16:24:16 +04:00
macvlan_hash_del ( vlan , true ) ;
2009-03-13 23:16:13 +03:00
/* Now that we are unhashed it is safe to change the device
* address without confusing packet delivery .
*/
memcpy ( vlan - > dev - > dev_addr , addr , ETH_ALEN ) ;
macvlan_hash_add ( vlan ) ;
}
static int macvlan_addr_busy ( const struct macvlan_port * port ,
const unsigned char * addr )
{
/* Test to see if the specified multicast address is
* currently in use by the underlying device or
* another macvlan .
*/
2009-09-01 09:46:05 +04:00
if ( ! compare_ether_addr_64bits ( port - > dev - > dev_addr , addr ) )
2009-03-13 23:16:13 +03:00
return 1 ;
if ( macvlan_hash_lookup ( port , addr ) )
return 1 ;
return 0 ;
}
2009-11-26 09:07:09 +03:00
2010-01-30 15:23:40 +03:00
static int macvlan_broadcast_one ( struct sk_buff * skb ,
const struct macvlan_dev * vlan ,
2009-11-26 09:07:10 +03:00
const struct ethhdr * eth , bool local )
2009-11-26 09:07:09 +03:00
{
2010-01-30 15:23:40 +03:00
struct net_device * dev = vlan - > dev ;
2009-11-26 09:07:09 +03:00
if ( ! skb )
return NET_RX_DROP ;
2009-11-26 09:07:10 +03:00
if ( local )
2010-01-30 15:23:40 +03:00
return vlan - > forward ( dev , skb ) ;
2009-11-26 09:07:10 +03:00
2009-11-26 09:07:09 +03:00
skb - > dev = dev ;
if ( ! compare_ether_addr_64bits ( eth - > h_dest ,
dev - > broadcast ) )
skb - > pkt_type = PACKET_BROADCAST ;
else
skb - > pkt_type = PACKET_MULTICAST ;
2010-01-30 15:23:40 +03:00
return vlan - > receive ( skb ) ;
2009-11-26 09:07:09 +03:00
}
2007-07-15 05:55:06 +04:00
static void macvlan_broadcast ( struct sk_buff * skb ,
2009-11-26 09:07:10 +03:00
const struct macvlan_port * port ,
struct net_device * src ,
enum macvlan_mode mode )
2007-07-15 05:55:06 +04:00
{
const struct ethhdr * eth = eth_hdr ( skb ) ;
const struct macvlan_dev * vlan ;
struct hlist_node * n ;
struct sk_buff * nskb ;
unsigned int i ;
2009-11-26 09:07:09 +03:00
int err ;
2007-07-15 05:55:06 +04:00
2008-11-27 02:30:48 +03:00
if ( skb - > protocol = = htons ( ETH_P_PAUSE ) )
return ;
2007-07-15 05:55:06 +04:00
for ( i = 0 ; i < MACVLAN_HASH_SIZE ; i + + ) {
hlist_for_each_entry_rcu ( vlan , n , & port - > vlan_hash [ i ] , hlist ) {
2009-11-26 09:07:10 +03:00
if ( vlan - > dev = = src | | ! ( vlan - > mode & mode ) )
continue ;
2007-07-15 05:55:06 +04:00
nskb = skb_clone ( skb , GFP_ATOMIC ) ;
2010-01-30 15:23:40 +03:00
err = macvlan_broadcast_one ( nskb , vlan , eth ,
2009-11-26 09:07:10 +03:00
mode = = MACVLAN_MODE_BRIDGE ) ;
2009-11-26 09:07:09 +03:00
macvlan_count_rx ( vlan , skb - > len + ETH_HLEN ,
err = = NET_RX_SUCCESS , 1 ) ;
2007-07-15 05:55:06 +04:00
}
}
}
/* called under rcu_read_lock() from netif_receive_skb */
2011-03-12 06:14:39 +03:00
static rx_handler_result_t macvlan_handle_frame ( struct sk_buff * * pskb )
2007-07-15 05:55:06 +04:00
{
2010-06-02 01:52:08 +04:00
struct macvlan_port * port ;
2011-03-12 06:14:39 +03:00
struct sk_buff * skb = * pskb ;
2007-07-15 05:55:06 +04:00
const struct ethhdr * eth = eth_hdr ( skb ) ;
const struct macvlan_dev * vlan ;
2009-11-26 09:07:10 +03:00
const struct macvlan_dev * src ;
2007-07-15 05:55:06 +04:00
struct net_device * dev ;
2010-07-27 13:10:07 +04:00
unsigned int len = 0 ;
int ret = NET_RX_DROP ;
2007-07-15 05:55:06 +04:00
2010-06-15 07:27:57 +04:00
port = macvlan_port_get_rcu ( skb - > dev ) ;
2007-07-15 05:55:06 +04:00
if ( is_multicast_ether_addr ( eth - > h_dest ) ) {
2009-11-26 09:07:10 +03:00
src = macvlan_hash_lookup ( port , eth - > h_source ) ;
if ( ! src )
/* frame comes from an external address */
macvlan_broadcast ( skb , port , NULL ,
MACVLAN_MODE_PRIVATE |
MACVLAN_MODE_VEPA |
2010-10-28 17:10:50 +04:00
MACVLAN_MODE_PASSTHRU |
2009-11-26 09:07:10 +03:00
MACVLAN_MODE_BRIDGE ) ;
else if ( src - > mode = = MACVLAN_MODE_VEPA )
/* flood to everyone except source */
macvlan_broadcast ( skb , port , src - > dev ,
MACVLAN_MODE_VEPA |
MACVLAN_MODE_BRIDGE ) ;
else if ( src - > mode = = MACVLAN_MODE_BRIDGE )
/*
* flood only to VEPA ports , bridge ports
* already saw the frame on the way out .
*/
macvlan_broadcast ( skb , port , src - > dev ,
MACVLAN_MODE_VEPA ) ;
2011-03-12 06:14:39 +03:00
return RX_HANDLER_PASS ;
2007-07-15 05:55:06 +04:00
}
2010-10-28 17:10:50 +04:00
if ( port - > passthru )
vlan = list_first_entry ( & port - > vlans , struct macvlan_dev , list ) ;
else
vlan = macvlan_hash_lookup ( port , eth - > h_dest ) ;
2007-07-15 05:55:06 +04:00
if ( vlan = = NULL )
2011-03-12 06:14:39 +03:00
return RX_HANDLER_PASS ;
2007-07-15 05:55:06 +04:00
dev = vlan - > dev ;
if ( unlikely ( ! ( dev - > flags & IFF_UP ) ) ) {
kfree_skb ( skb ) ;
2011-03-12 06:14:39 +03:00
return RX_HANDLER_CONSUMED ;
2007-07-15 05:55:06 +04:00
}
2009-11-26 09:07:09 +03:00
len = skb - > len + ETH_HLEN ;
2007-07-15 05:55:06 +04:00
skb = skb_share_check ( skb , GFP_ATOMIC ) ;
2009-11-26 09:07:09 +03:00
if ( ! skb )
2010-07-27 13:10:07 +04:00
goto out ;
2007-07-15 05:55:06 +04:00
skb - > dev = dev ;
skb - > pkt_type = PACKET_HOST ;
2010-07-27 13:10:07 +04:00
ret = vlan - > receive ( skb ) ;
out :
macvlan_count_rx ( vlan , len , ret = = NET_RX_SUCCESS , 0 ) ;
2011-03-12 06:14:39 +03:00
return RX_HANDLER_CONSUMED ;
2007-07-15 05:55:06 +04:00
}
2009-11-26 09:07:10 +03:00
static int macvlan_queue_xmit ( struct sk_buff * skb , struct net_device * dev )
{
const struct macvlan_dev * vlan = netdev_priv ( dev ) ;
const struct macvlan_port * port = vlan - > port ;
const struct macvlan_dev * dest ;
2011-03-14 09:08:07 +03:00
__u8 ip_summed = skb - > ip_summed ;
2009-11-26 09:07:10 +03:00
if ( vlan - > mode = = MACVLAN_MODE_BRIDGE ) {
const struct ethhdr * eth = ( void * ) skb - > data ;
2011-03-14 09:08:07 +03:00
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
2009-11-26 09:07:10 +03:00
/* send to other bridge ports directly */
if ( is_multicast_ether_addr ( eth - > h_dest ) ) {
macvlan_broadcast ( skb , port , dev , MACVLAN_MODE_BRIDGE ) ;
goto xmit_world ;
}
dest = macvlan_hash_lookup ( port , eth - > h_dest ) ;
if ( dest & & dest - > mode = = MACVLAN_MODE_BRIDGE ) {
unsigned int length = skb - > len + ETH_HLEN ;
2010-01-30 15:23:40 +03:00
int ret = dest - > forward ( dest - > dev , skb ) ;
2009-11-26 09:07:10 +03:00
macvlan_count_rx ( dest , length ,
ret = = NET_RX_SUCCESS , 0 ) ;
return NET_XMIT_SUCCESS ;
}
}
xmit_world :
2011-03-14 09:08:07 +03:00
skb - > ip_summed = ip_summed ;
2010-01-30 15:23:03 +03:00
skb_set_dev ( skb , vlan - > lowerdev ) ;
2009-11-26 09:07:10 +03:00
return dev_queue_xmit ( skb ) ;
}
2010-01-30 15:23:40 +03:00
netdev_tx_t macvlan_start_xmit ( struct sk_buff * skb ,
struct net_device * dev )
2007-07-15 05:55:06 +04:00
{
unsigned int len = skb - > len ;
int ret ;
2010-11-11 00:14:04 +03:00
const struct macvlan_dev * vlan = netdev_priv ( dev ) ;
2007-07-15 05:55:06 +04:00
2009-11-26 09:07:10 +03:00
ret = macvlan_queue_xmit ( skb , dev ) ;
2010-05-10 08:51:02 +04:00
if ( likely ( ret = = NET_XMIT_SUCCESS | | ret = = NET_XMIT_CN ) ) {
2010-11-11 00:14:04 +03:00
struct macvlan_pcpu_stats * pcpu_stats ;
2009-09-03 04:11:45 +04:00
2010-11-11 00:14:04 +03:00
pcpu_stats = this_cpu_ptr ( vlan - > pcpu_stats ) ;
u64_stats_update_begin ( & pcpu_stats - > syncp ) ;
pcpu_stats - > tx_packets + + ;
pcpu_stats - > tx_bytes + = len ;
u64_stats_update_end ( & pcpu_stats - > syncp ) ;
} else {
this_cpu_inc ( vlan - > pcpu_stats - > tx_dropped ) ;
}
2009-11-10 09:14:24 +03:00
return ret ;
2007-07-15 05:55:06 +04:00
}
2010-01-30 15:23:40 +03:00
EXPORT_SYMBOL_GPL ( macvlan_start_xmit ) ;
2007-07-15 05:55:06 +04:00
static int macvlan_hard_header ( struct sk_buff * skb , struct net_device * dev ,
2007-10-09 12:40:57 +04:00
unsigned short type , const void * daddr ,
const void * saddr , unsigned len )
2007-07-15 05:55:06 +04:00
{
const struct macvlan_dev * vlan = netdev_priv ( dev ) ;
struct net_device * lowerdev = vlan - > lowerdev ;
2007-10-09 12:36:32 +04:00
return dev_hard_header ( skb , lowerdev , type , daddr ,
saddr ? : dev - > dev_addr , len ) ;
2007-07-15 05:55:06 +04:00
}
2007-10-09 12:40:57 +04:00
static const struct header_ops macvlan_hard_header_ops = {
. create = macvlan_hard_header ,
. rebuild = eth_rebuild_header ,
. parse = eth_header_parse ,
. cache = eth_header_cache ,
. cache_update = eth_header_cache_update ,
} ;
2007-07-15 05:55:06 +04:00
static int macvlan_open ( struct net_device * dev )
{
struct macvlan_dev * vlan = netdev_priv ( dev ) ;
struct net_device * lowerdev = vlan - > lowerdev ;
int err ;
2010-10-28 17:10:50 +04:00
if ( vlan - > port - > passthru ) {
dev_set_promiscuity ( lowerdev , 1 ) ;
goto hash_add ;
}
2009-03-13 23:16:13 +03:00
err = - EBUSY ;
if ( macvlan_addr_busy ( vlan - > port , dev - > dev_addr ) )
goto out ;
2010-04-02 01:22:09 +04:00
err = dev_uc_add ( lowerdev , dev - > dev_addr ) ;
2007-07-15 05:55:06 +04:00
if ( err < 0 )
2008-07-15 07:57:07 +04:00
goto out ;
if ( dev - > flags & IFF_ALLMULTI ) {
err = dev_set_allmulti ( lowerdev , 1 ) ;
if ( err < 0 )
goto del_unicast ;
}
2010-10-28 17:10:50 +04:00
hash_add :
2009-03-13 23:16:13 +03:00
macvlan_hash_add ( vlan ) ;
2007-07-15 05:55:06 +04:00
return 0 ;
2008-07-15 07:57:07 +04:00
del_unicast :
2010-04-02 01:22:09 +04:00
dev_uc_del ( lowerdev , dev - > dev_addr ) ;
2008-07-15 07:57:07 +04:00
out :
return err ;
2007-07-15 05:55:06 +04:00
}
static int macvlan_stop ( struct net_device * dev )
{
struct macvlan_dev * vlan = netdev_priv ( dev ) ;
struct net_device * lowerdev = vlan - > lowerdev ;
2010-10-28 17:10:50 +04:00
if ( vlan - > port - > passthru ) {
dev_set_promiscuity ( lowerdev , - 1 ) ;
goto hash_del ;
}
2007-07-15 05:55:06 +04:00
dev_mc_unsync ( lowerdev , dev ) ;
if ( dev - > flags & IFF_ALLMULTI )
dev_set_allmulti ( lowerdev , - 1 ) ;
2010-04-02 01:22:09 +04:00
dev_uc_del ( lowerdev , dev - > dev_addr ) ;
2007-07-15 05:55:06 +04:00
2010-10-28 17:10:50 +04:00
hash_del :
2011-05-19 16:24:16 +04:00
macvlan_hash_del ( vlan , ! dev - > dismantle ) ;
2007-07-15 05:55:06 +04:00
return 0 ;
}
2007-11-20 09:00:42 +03:00
static int macvlan_set_mac_address ( struct net_device * dev , void * p )
{
struct macvlan_dev * vlan = netdev_priv ( dev ) ;
struct net_device * lowerdev = vlan - > lowerdev ;
struct sockaddr * addr = p ;
int err ;
if ( ! is_valid_ether_addr ( addr - > sa_data ) )
return - EADDRNOTAVAIL ;
2009-03-13 23:16:13 +03:00
if ( ! ( dev - > flags & IFF_UP ) ) {
/* Just copy in the new address */
memcpy ( dev - > dev_addr , addr - > sa_data , ETH_ALEN ) ;
} else {
/* Rehash and update the device filters */
if ( macvlan_addr_busy ( vlan - > port , addr - > sa_data ) )
return - EBUSY ;
2007-11-20 09:00:42 +03:00
2010-04-02 01:22:09 +04:00
err = dev_uc_add ( lowerdev , addr - > sa_data ) ;
2009-05-23 03:22:17 +04:00
if ( err )
2009-03-13 23:16:13 +03:00
return err ;
2007-11-20 09:00:42 +03:00
2010-04-02 01:22:09 +04:00
dev_uc_del ( lowerdev , dev - > dev_addr ) ;
2009-03-13 23:16:13 +03:00
macvlan_hash_change_addr ( vlan , addr - > sa_data ) ;
}
2007-11-20 09:00:42 +03:00
return 0 ;
}
2007-07-15 05:55:06 +04:00
static void macvlan_change_rx_flags ( struct net_device * dev , int change )
{
struct macvlan_dev * vlan = netdev_priv ( dev ) ;
struct net_device * lowerdev = vlan - > lowerdev ;
if ( change & IFF_ALLMULTI )
dev_set_allmulti ( lowerdev , dev - > flags & IFF_ALLMULTI ? 1 : - 1 ) ;
}
static void macvlan_set_multicast_list ( struct net_device * dev )
{
struct macvlan_dev * vlan = netdev_priv ( dev ) ;
dev_mc_sync ( vlan - > lowerdev , dev ) ;
}
static int macvlan_change_mtu ( struct net_device * dev , int new_mtu )
{
struct macvlan_dev * vlan = netdev_priv ( dev ) ;
if ( new_mtu < 68 | | vlan - > lowerdev - > mtu < new_mtu )
return - EINVAL ;
dev - > mtu = new_mtu ;
return 0 ;
}
/*
* macvlan network devices have devices nesting below it and are a special
* " super class " of normal network devices ; split their locks off into a
* separate class since they always nest .
*/
static struct lock_class_key macvlan_netdev_xmit_lock_key ;
2008-07-23 01:16:42 +04:00
static struct lock_class_key macvlan_netdev_addr_lock_key ;
2007-07-15 05:55:06 +04:00
# define MACVLAN_FEATURES \
( NETIF_F_SG | NETIF_F_ALL_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \
NETIF_F_GSO | NETIF_F_TSO | NETIF_F_UFO | NETIF_F_GSO_ROBUST | \
2011-04-17 04:15:46 +04:00
NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_GRO | NETIF_F_RXCSUM )
2007-07-15 05:55:06 +04:00
# define MACVLAN_STATE_MASK \
( ( 1 < < __LINK_STATE_NOCARRIER ) | ( 1 < < __LINK_STATE_DORMANT ) )
2008-07-17 11:34:19 +04:00
static void macvlan_set_lockdep_class_one ( struct net_device * dev ,
struct netdev_queue * txq ,
void * _unused )
2008-07-09 10:13:53 +04:00
{
lockdep_set_class ( & txq - > _xmit_lock ,
& macvlan_netdev_xmit_lock_key ) ;
}
static void macvlan_set_lockdep_class ( struct net_device * dev )
{
2008-07-23 01:16:42 +04:00
lockdep_set_class ( & dev - > addr_list_lock ,
& macvlan_netdev_addr_lock_key ) ;
2008-07-17 11:34:19 +04:00
netdev_for_each_tx_queue ( dev , macvlan_set_lockdep_class_one , NULL ) ;
2008-07-09 10:13:53 +04:00
}
2007-07-15 05:55:06 +04:00
static int macvlan_init ( struct net_device * dev )
{
struct macvlan_dev * vlan = netdev_priv ( dev ) ;
const struct net_device * lowerdev = vlan - > lowerdev ;
dev - > state = ( dev - > state & ~ MACVLAN_STATE_MASK ) |
( lowerdev - > state & MACVLAN_STATE_MASK ) ;
dev - > features = lowerdev - > features & MACVLAN_FEATURES ;
2010-11-11 00:14:04 +03:00
dev - > features | = NETIF_F_LLTX ;
2009-11-24 01:18:53 +03:00
dev - > gso_max_size = lowerdev - > gso_max_size ;
2007-07-15 05:55:06 +04:00
dev - > iflink = lowerdev - > ifindex ;
2009-06-10 13:55:02 +04:00
dev - > hard_header_len = lowerdev - > hard_header_len ;
2007-07-15 05:55:06 +04:00
2008-07-09 10:13:53 +04:00
macvlan_set_lockdep_class ( dev ) ;
2010-11-11 00:14:04 +03:00
vlan - > pcpu_stats = alloc_percpu ( struct macvlan_pcpu_stats ) ;
if ( ! vlan - > pcpu_stats )
2009-11-17 11:53:49 +03:00
return - ENOMEM ;
2007-07-15 05:55:06 +04:00
return 0 ;
}
2009-11-17 11:53:49 +03:00
static void macvlan_uninit ( struct net_device * dev )
{
struct macvlan_dev * vlan = netdev_priv ( dev ) ;
2011-03-22 04:22:22 +03:00
struct macvlan_port * port = vlan - > port ;
2009-11-17 11:53:49 +03:00
2010-11-11 00:14:04 +03:00
free_percpu ( vlan - > pcpu_stats ) ;
2011-03-22 04:22:22 +03:00
port - > count - = 1 ;
if ( ! port - > count )
macvlan_port_destroy ( port - > dev ) ;
2009-11-17 11:53:49 +03:00
}
2010-07-08 01:58:56 +04:00
static struct rtnl_link_stats64 * macvlan_dev_get_stats64 ( struct net_device * dev ,
struct rtnl_link_stats64 * stats )
2009-11-17 11:53:49 +03:00
{
struct macvlan_dev * vlan = netdev_priv ( dev ) ;
2010-11-11 00:14:04 +03:00
if ( vlan - > pcpu_stats ) {
struct macvlan_pcpu_stats * p ;
u64 rx_packets , rx_bytes , rx_multicast , tx_packets , tx_bytes ;
u32 rx_errors = 0 , tx_dropped = 0 ;
2010-06-24 04:54:21 +04:00
unsigned int start ;
2009-11-17 11:53:49 +03:00
int i ;
for_each_possible_cpu ( i ) {
2010-11-11 00:14:04 +03:00
p = per_cpu_ptr ( vlan - > pcpu_stats , i ) ;
2010-06-24 04:54:21 +04:00
do {
start = u64_stats_fetch_begin_bh ( & p - > syncp ) ;
rx_packets = p - > rx_packets ;
rx_bytes = p - > rx_bytes ;
rx_multicast = p - > rx_multicast ;
2010-11-11 00:14:04 +03:00
tx_packets = p - > tx_packets ;
tx_bytes = p - > tx_bytes ;
2010-06-24 04:54:21 +04:00
} while ( u64_stats_fetch_retry_bh ( & p - > syncp , start ) ) ;
2010-11-11 00:14:04 +03:00
stats - > rx_packets + = rx_packets ;
stats - > rx_bytes + = rx_bytes ;
stats - > multicast + = rx_multicast ;
stats - > tx_packets + = tx_packets ;
stats - > tx_bytes + = tx_bytes ;
/* rx_errors & tx_dropped are u32, updated
* without syncp protection .
*/
rx_errors + = p - > rx_errors ;
tx_dropped + = p - > tx_dropped ;
2009-11-17 11:53:49 +03:00
}
2010-11-11 00:14:04 +03:00
stats - > rx_errors = rx_errors ;
stats - > rx_dropped = rx_errors ;
stats - > tx_dropped = tx_dropped ;
2009-11-17 11:53:49 +03:00
}
return stats ;
}
2007-07-15 05:55:06 +04:00
static void macvlan_ethtool_get_drvinfo ( struct net_device * dev ,
struct ethtool_drvinfo * drvinfo )
{
snprintf ( drvinfo - > driver , 32 , " macvlan " ) ;
snprintf ( drvinfo - > version , 32 , " 0.1 " ) ;
}
2008-10-30 01:31:53 +03:00
static int macvlan_ethtool_get_settings ( struct net_device * dev ,
struct ethtool_cmd * cmd )
{
const struct macvlan_dev * vlan = netdev_priv ( dev ) ;
2009-04-20 08:49:28 +04:00
return dev_ethtool_get_settings ( vlan - > lowerdev , cmd ) ;
2008-10-30 01:31:53 +03:00
}
2007-07-15 05:55:06 +04:00
static const struct ethtool_ops macvlan_ethtool_ops = {
. get_link = ethtool_op_get_link ,
2008-10-30 01:31:53 +03:00
. get_settings = macvlan_ethtool_get_settings ,
2007-07-15 05:55:06 +04:00
. get_drvinfo = macvlan_ethtool_get_drvinfo ,
} ;
2008-11-20 08:51:06 +03:00
static const struct net_device_ops macvlan_netdev_ops = {
. ndo_init = macvlan_init ,
2009-11-17 11:53:49 +03:00
. ndo_uninit = macvlan_uninit ,
2008-11-20 08:51:06 +03:00
. ndo_open = macvlan_open ,
. ndo_stop = macvlan_stop ,
2008-11-21 07:14:53 +03:00
. ndo_start_xmit = macvlan_start_xmit ,
2008-11-20 08:51:06 +03:00
. ndo_change_mtu = macvlan_change_mtu ,
. ndo_change_rx_flags = macvlan_change_rx_flags ,
. ndo_set_mac_address = macvlan_set_mac_address ,
. ndo_set_multicast_list = macvlan_set_multicast_list ,
2010-06-24 04:54:21 +04:00
. ndo_get_stats64 = macvlan_dev_get_stats64 ,
2008-11-20 08:51:06 +03:00
. ndo_validate_addr = eth_validate_addr ,
} ;
2010-07-22 01:44:31 +04:00
void macvlan_common_setup ( struct net_device * dev )
2007-07-15 05:55:06 +04:00
{
ether_setup ( dev ) ;
net: release dst entry in dev_hard_start_xmit()
One point of contention in high network loads is the dst_release() performed
when a transmited skb is freed. This is because NIC tx completion calls
dev_kree_skb() long after original call to dev_queue_xmit(skb).
CPU cache is cold and the atomic op in dst_release() stalls. On SMP, this is
quite visible if one CPU is 100% handling softirqs for a network device,
since dst_clone() is done by other cpus, involving cache line ping pongs.
It seems right place to release dst is in dev_hard_start_xmit(), for most
devices but ones that are virtual, and some exceptions.
David Miller suggested to define a new device flag, set in alloc_netdev_mq()
(so that most devices set it at init time), and carefuly unset in devices
which dont want a NULL skb->dst in their ndo_start_xmit().
List of devices that must clear this flag is :
- loopback device, because it calls netif_rx() and quoting Patrick :
"ip_route_input() doesn't accept loopback addresses, so loopback packets
already need to have a dst_entry attached."
- appletalk/ipddp.c : needs skb->dst in its xmit function
- And all devices that call again dev_queue_xmit() from their xmit function
(as some classifiers need skb->dst) : bonding, vlan, macvlan, eql, ifb, hdlc_fr
Signed-off-by: Eric Dumazet <dada1@cosmosbay.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-05-19 09:19:19 +04:00
dev - > priv_flags & = ~ IFF_XMIT_DST_RELEASE ;
2008-11-20 08:51:06 +03:00
dev - > netdev_ops = & macvlan_netdev_ops ;
2007-07-15 05:55:06 +04:00
dev - > destructor = free_netdev ;
2007-10-09 12:40:57 +04:00
dev - > header_ops = & macvlan_hard_header_ops ,
2007-07-15 05:55:06 +04:00
dev - > ethtool_ops = & macvlan_ethtool_ops ;
2010-07-22 01:44:31 +04:00
}
EXPORT_SYMBOL_GPL ( macvlan_common_setup ) ;
static void macvlan_setup ( struct net_device * dev )
{
macvlan_common_setup ( dev ) ;
2007-07-15 05:55:06 +04:00
dev - > tx_queue_len = 0 ;
}
static int macvlan_port_create ( struct net_device * dev )
{
struct macvlan_port * port ;
unsigned int i ;
2010-06-02 01:52:08 +04:00
int err ;
2007-07-15 05:55:06 +04:00
if ( dev - > type ! = ARPHRD_ETHER | | dev - > flags & IFF_LOOPBACK )
return - EINVAL ;
port = kzalloc ( sizeof ( * port ) , GFP_KERNEL ) ;
if ( port = = NULL )
return - ENOMEM ;
2010-10-28 17:10:50 +04:00
port - > passthru = false ;
2007-07-15 05:55:06 +04:00
port - > dev = dev ;
INIT_LIST_HEAD ( & port - > vlans ) ;
for ( i = 0 ; i < MACVLAN_HASH_SIZE ; i + + )
INIT_HLIST_HEAD ( & port - > vlan_hash [ i ] ) ;
2010-06-02 01:52:08 +04:00
2010-06-15 07:27:57 +04:00
err = netdev_rx_handler_register ( dev , macvlan_handle_frame , port ) ;
if ( err )
2010-06-02 01:52:08 +04:00
kfree ( port ) ;
2011-05-20 22:59:23 +04:00
else
dev - > priv_flags | = IFF_MACVLAN_PORT ;
2010-06-02 01:52:08 +04:00
return err ;
2007-07-15 05:55:06 +04:00
}
static void macvlan_port_destroy ( struct net_device * dev )
{
2010-06-15 07:27:57 +04:00
struct macvlan_port * port = macvlan_port_get ( dev ) ;
2007-07-15 05:55:06 +04:00
2010-06-15 07:27:57 +04:00
dev - > priv_flags & = ~ IFF_MACVLAN_PORT ;
2010-06-02 01:52:08 +04:00
netdev_rx_handler_unregister ( dev ) ;
2011-03-18 07:00:07 +03:00
kfree_rcu ( port , rcu ) ;
2007-07-15 05:55:06 +04:00
}
static int macvlan_validate ( struct nlattr * tb [ ] , struct nlattr * data [ ] )
{
if ( tb [ IFLA_ADDRESS ] ) {
if ( nla_len ( tb [ IFLA_ADDRESS ] ) ! = ETH_ALEN )
return - EINVAL ;
if ( ! is_valid_ether_addr ( nla_data ( tb [ IFLA_ADDRESS ] ) ) )
return - EADDRNOTAVAIL ;
}
2009-11-26 09:07:11 +03:00
if ( data & & data [ IFLA_MACVLAN_MODE ] ) {
switch ( nla_get_u32 ( data [ IFLA_MACVLAN_MODE ] ) ) {
case MACVLAN_MODE_PRIVATE :
case MACVLAN_MODE_VEPA :
case MACVLAN_MODE_BRIDGE :
2010-10-28 17:10:50 +04:00
case MACVLAN_MODE_PASSTHRU :
2009-11-26 09:07:11 +03:00
break ;
default :
return - EINVAL ;
}
}
2007-07-15 05:55:06 +04:00
return 0 ;
}
2010-01-30 15:23:40 +03:00
int macvlan_common_newlink ( struct net * src_net , struct net_device * dev ,
struct nlattr * tb [ ] , struct nlattr * data [ ] ,
int ( * receive ) ( struct sk_buff * skb ) ,
int ( * forward ) ( struct net_device * dev ,
struct sk_buff * skb ) )
2007-07-15 05:55:06 +04:00
{
struct macvlan_dev * vlan = netdev_priv ( dev ) ;
struct macvlan_port * port ;
struct net_device * lowerdev ;
int err ;
if ( ! tb [ IFLA_LINK ] )
return - EINVAL ;
2009-11-08 11:53:51 +03:00
lowerdev = __dev_get_by_index ( src_net , nla_get_u32 ( tb [ IFLA_LINK ] ) ) ;
2007-07-15 05:55:06 +04:00
if ( lowerdev = = NULL )
return - ENODEV ;
2009-03-13 23:15:37 +03:00
/* When creating macvlans on top of other macvlans - use
* the real device as the lowerdev .
2008-01-11 09:39:28 +03:00
*/
2009-03-13 23:15:37 +03:00
if ( lowerdev - > rtnl_link_ops = = dev - > rtnl_link_ops ) {
struct macvlan_dev * lowervlan = netdev_priv ( lowerdev ) ;
lowerdev = lowervlan - > lowerdev ;
}
2008-01-11 09:39:28 +03:00
2007-07-15 05:55:06 +04:00
if ( ! tb [ IFLA_MTU ] )
dev - > mtu = lowerdev - > mtu ;
else if ( dev - > mtu > lowerdev - > mtu )
return - EINVAL ;
if ( ! tb [ IFLA_ADDRESS ] )
random_ether_addr ( dev - > dev_addr ) ;
2010-06-15 07:27:57 +04:00
if ( ! macvlan_port_exists ( lowerdev ) ) {
2007-07-15 05:55:06 +04:00
err = macvlan_port_create ( lowerdev ) ;
if ( err < 0 )
return err ;
}
2010-06-15 07:27:57 +04:00
port = macvlan_port_get ( lowerdev ) ;
2007-07-15 05:55:06 +04:00
2010-10-28 17:10:50 +04:00
/* Only 1 macvlan device can be created in passthru mode */
if ( port - > passthru )
return - EINVAL ;
2007-07-15 05:55:06 +04:00
vlan - > lowerdev = lowerdev ;
vlan - > dev = dev ;
vlan - > port = port ;
2010-01-30 15:23:40 +03:00
vlan - > receive = receive ;
vlan - > forward = forward ;
2007-07-15 05:55:06 +04:00
2009-11-26 09:07:11 +03:00
vlan - > mode = MACVLAN_MODE_VEPA ;
if ( data & & data [ IFLA_MACVLAN_MODE ] )
vlan - > mode = nla_get_u32 ( data [ IFLA_MACVLAN_MODE ] ) ;
2010-10-28 17:10:50 +04:00
if ( vlan - > mode = = MACVLAN_MODE_PASSTHRU ) {
2011-03-22 04:22:22 +03:00
if ( port - > count )
2010-10-28 17:10:50 +04:00
return - EINVAL ;
port - > passthru = true ;
memcpy ( dev - > dev_addr , lowerdev - > dev_addr , ETH_ALEN ) ;
}
2011-03-22 04:22:22 +03:00
port - > count + = 1 ;
2007-07-15 05:55:06 +04:00
err = register_netdevice ( dev ) ;
if ( err < 0 )
2010-05-24 11:02:25 +04:00
goto destroy_port ;
2007-07-15 05:55:06 +04:00
list_add_tail ( & vlan - > list , & port - > vlans ) ;
2009-12-04 02:59:22 +03:00
netif_stacked_transfer_operstate ( lowerdev , dev ) ;
2010-05-24 11:02:25 +04:00
2007-07-15 05:55:06 +04:00
return 0 ;
2010-05-24 11:02:25 +04:00
destroy_port :
2011-03-22 04:22:22 +03:00
port - > count - = 1 ;
if ( ! port - > count )
2010-05-24 11:02:25 +04:00
macvlan_port_destroy ( lowerdev ) ;
return err ;
2007-07-15 05:55:06 +04:00
}
2010-01-30 15:23:40 +03:00
EXPORT_SYMBOL_GPL ( macvlan_common_newlink ) ;
2007-07-15 05:55:06 +04:00
2010-01-30 15:23:40 +03:00
static int macvlan_newlink ( struct net * src_net , struct net_device * dev ,
struct nlattr * tb [ ] , struct nlattr * data [ ] )
{
return macvlan_common_newlink ( src_net , dev , tb , data ,
netif_rx ,
dev_forward_skb ) ;
}
void macvlan_dellink ( struct net_device * dev , struct list_head * head )
2007-07-15 05:55:06 +04:00
{
struct macvlan_dev * vlan = netdev_priv ( dev ) ;
list_del ( & vlan - > list ) ;
2009-10-27 10:06:36 +03:00
unregister_netdevice_queue ( dev , head ) ;
2007-07-15 05:55:06 +04:00
}
2010-01-30 15:23:40 +03:00
EXPORT_SYMBOL_GPL ( macvlan_dellink ) ;
2007-07-15 05:55:06 +04:00
2009-11-26 09:07:11 +03:00
static int macvlan_changelink ( struct net_device * dev ,
struct nlattr * tb [ ] , struct nlattr * data [ ] )
{
struct macvlan_dev * vlan = netdev_priv ( dev ) ;
if ( data & & data [ IFLA_MACVLAN_MODE ] )
vlan - > mode = nla_get_u32 ( data [ IFLA_MACVLAN_MODE ] ) ;
return 0 ;
}
static size_t macvlan_get_size ( const struct net_device * dev )
{
return nla_total_size ( 4 ) ;
}
static int macvlan_fill_info ( struct sk_buff * skb ,
const struct net_device * dev )
{
struct macvlan_dev * vlan = netdev_priv ( dev ) ;
NLA_PUT_U32 ( skb , IFLA_MACVLAN_MODE , vlan - > mode ) ;
return 0 ;
nla_put_failure :
return - EMSGSIZE ;
}
static const struct nla_policy macvlan_policy [ IFLA_MACVLAN_MAX + 1 ] = {
[ IFLA_MACVLAN_MODE ] = { . type = NLA_U32 } ,
} ;
2010-01-30 15:23:40 +03:00
int macvlan_link_register ( struct rtnl_link_ops * ops )
{
/* common fields */
ops - > priv_size = sizeof ( struct macvlan_dev ) ;
ops - > validate = macvlan_validate ;
ops - > maxtype = IFLA_MACVLAN_MAX ;
ops - > policy = macvlan_policy ;
ops - > changelink = macvlan_changelink ;
ops - > get_size = macvlan_get_size ;
ops - > fill_info = macvlan_fill_info ;
return rtnl_link_register ( ops ) ;
} ;
EXPORT_SYMBOL_GPL ( macvlan_link_register ) ;
static struct rtnl_link_ops macvlan_link_ops = {
2007-07-15 05:55:06 +04:00
. kind = " macvlan " ,
2010-07-22 01:44:31 +04:00
. setup = macvlan_setup ,
2007-07-15 05:55:06 +04:00
. newlink = macvlan_newlink ,
. dellink = macvlan_dellink ,
} ;
static int macvlan_device_event ( struct notifier_block * unused ,
unsigned long event , void * ptr )
{
struct net_device * dev = ptr ;
struct macvlan_dev * vlan , * next ;
struct macvlan_port * port ;
2011-05-09 03:17:57 +04:00
LIST_HEAD ( list_kill ) ;
2007-07-15 05:55:06 +04:00
2010-06-15 07:27:57 +04:00
if ( ! macvlan_port_exists ( dev ) )
2007-07-15 05:55:06 +04:00
return NOTIFY_DONE ;
2010-06-15 07:27:57 +04:00
port = macvlan_port_get ( dev ) ;
2007-07-15 05:55:06 +04:00
switch ( event ) {
case NETDEV_CHANGE :
list_for_each_entry ( vlan , & port - > vlans , list )
2009-12-04 02:59:22 +03:00
netif_stacked_transfer_operstate ( vlan - > lowerdev ,
vlan - > dev ) ;
2007-07-15 05:55:06 +04:00
break ;
case NETDEV_FEAT_CHANGE :
list_for_each_entry ( vlan , & port - > vlans , list ) {
vlan - > dev - > features = dev - > features & MACVLAN_FEATURES ;
2009-11-24 01:18:53 +03:00
vlan - > dev - > gso_max_size = dev - > gso_max_size ;
2007-07-15 05:55:06 +04:00
netdev_features_change ( vlan - > dev ) ;
}
break ;
case NETDEV_UNREGISTER :
2010-09-17 07:22:19 +04:00
/* twiddle thumbs on netns device moves */
if ( dev - > reg_state ! = NETREG_UNREGISTERING )
break ;
2007-07-15 05:55:06 +04:00
list_for_each_entry_safe ( vlan , next , & port - > vlans , list )
2011-05-09 03:17:57 +04:00
vlan - > dev - > rtnl_link_ops - > dellink ( vlan - > dev , & list_kill ) ;
unregister_netdevice_many ( & list_kill ) ;
list_del ( & list_kill ) ;
2007-07-15 05:55:06 +04:00
break ;
2010-03-10 13:30:19 +03:00
case NETDEV_PRE_TYPE_CHANGE :
/* Forbid underlaying device to change its type. */
return NOTIFY_BAD ;
2007-07-15 05:55:06 +04:00
}
return NOTIFY_DONE ;
}
static struct notifier_block macvlan_notifier_block __read_mostly = {
. notifier_call = macvlan_device_event ,
} ;
static int __init macvlan_init_module ( void )
{
int err ;
register_netdevice_notifier ( & macvlan_notifier_block ) ;
2010-01-30 15:23:40 +03:00
err = macvlan_link_register ( & macvlan_link_ops ) ;
2007-07-15 05:55:06 +04:00
if ( err < 0 )
goto err1 ;
return 0 ;
err1 :
unregister_netdevice_notifier ( & macvlan_notifier_block ) ;
return err ;
}
static void __exit macvlan_cleanup_module ( void )
{
rtnl_link_unregister ( & macvlan_link_ops ) ;
unregister_netdevice_notifier ( & macvlan_notifier_block ) ;
}
module_init ( macvlan_init_module ) ;
module_exit ( macvlan_cleanup_module ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Patrick McHardy <kaber@trash.net> " ) ;
MODULE_DESCRIPTION ( " Driver for MAC address based VLANs " ) ;
MODULE_ALIAS_RTNL_LINK ( " macvlan " ) ;