2011-10-26 06:26:31 +04:00
/*
2012-05-04 05:55:23 +04:00
* Copyright ( c ) 2007 - 2012 Nicira , Inc .
2011-10-26 06:26:31 +04:00
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA
* 02110 - 1301 , USA
*/
# include <linux/hardirq.h>
# include <linux/if_vlan.h>
# include <linux/kernel.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/ethtool.h>
# include <linux/skbuff.h>
2012-05-25 22:29:30 +04:00
# include <net/dst.h>
# include <net/xfrm.h>
2014-06-26 11:58:26 +04:00
# include <net/rtnetlink.h>
2012-05-25 22:29:30 +04:00
2011-10-26 06:26:31 +04:00
# include "datapath.h"
# include "vport-internal_dev.h"
# include "vport-netdev.h"
struct internal_dev {
struct vport * vport ;
} ;
2014-10-22 19:29:06 +04:00
static struct vport_ops ovs_internal_vport_ops ;
2011-10-26 06:26:31 +04:00
static struct internal_dev * internal_dev_priv ( struct net_device * netdev )
{
return netdev_priv ( netdev ) ;
}
/* Called with rcu_read_lock_bh. */
static int internal_dev_xmit ( struct sk_buff * skb , struct net_device * netdev )
{
2015-08-30 03:44:07 +03:00
int len , err ;
len = skb - > len ;
2011-10-26 06:26:31 +04:00
rcu_read_lock ( ) ;
2015-08-30 03:44:07 +03:00
err = ovs_vport_receive ( internal_dev_priv ( netdev ) - > vport , skb , NULL ) ;
2011-10-26 06:26:31 +04:00
rcu_read_unlock ( ) ;
2015-08-30 03:44:07 +03:00
if ( likely ( ! err ) ) {
struct pcpu_sw_netstats * tstats = this_cpu_ptr ( netdev - > tstats ) ;
u64_stats_update_begin ( & tstats - > syncp ) ;
tstats - > tx_bytes + = len ;
tstats - > tx_packets + + ;
u64_stats_update_end ( & tstats - > syncp ) ;
} else {
netdev - > stats . tx_errors + + ;
}
2011-10-26 06:26:31 +04:00
return 0 ;
}
static int internal_dev_open ( struct net_device * netdev )
{
netif_start_queue ( netdev ) ;
return 0 ;
}
static int internal_dev_stop ( struct net_device * netdev )
{
netif_stop_queue ( netdev ) ;
return 0 ;
}
static void internal_dev_getinfo ( struct net_device * netdev ,
struct ethtool_drvinfo * info )
{
2013-01-06 04:44:26 +04:00
strlcpy ( info - > driver , " openvswitch " , sizeof ( info - > driver ) ) ;
2011-10-26 06:26:31 +04:00
}
static const struct ethtool_ops internal_dev_ethtool_ops = {
. get_drvinfo = internal_dev_getinfo ,
. get_link = ethtool_op_get_link ,
} ;
static int internal_dev_change_mtu ( struct net_device * netdev , int new_mtu )
{
if ( new_mtu < 68 )
return - EINVAL ;
netdev - > mtu = new_mtu ;
return 0 ;
}
static void internal_dev_destructor ( struct net_device * dev )
{
struct vport * vport = ovs_internal_dev_get_vport ( dev ) ;
ovs_vport_free ( vport ) ;
free_netdev ( dev ) ;
}
2015-10-19 18:31:55 +03:00
static struct rtnl_link_stats64 *
internal_get_stats ( struct net_device * dev , struct rtnl_link_stats64 * stats )
{
int i ;
memset ( stats , 0 , sizeof ( * stats ) ) ;
stats - > rx_errors = dev - > stats . rx_errors ;
stats - > tx_errors = dev - > stats . tx_errors ;
stats - > tx_dropped = dev - > stats . tx_dropped ;
stats - > rx_dropped = dev - > stats . rx_dropped ;
for_each_possible_cpu ( i ) {
const struct pcpu_sw_netstats * percpu_stats ;
struct pcpu_sw_netstats local_stats ;
unsigned int start ;
percpu_stats = per_cpu_ptr ( dev - > tstats , i ) ;
do {
start = u64_stats_fetch_begin_irq ( & percpu_stats - > syncp ) ;
local_stats = * percpu_stats ;
} while ( u64_stats_fetch_retry_irq ( & percpu_stats - > syncp , start ) ) ;
stats - > rx_bytes + = local_stats . rx_bytes ;
stats - > rx_packets + = local_stats . rx_packets ;
stats - > tx_bytes + = local_stats . tx_bytes ;
stats - > tx_packets + = local_stats . tx_packets ;
}
return stats ;
}
2016-03-18 19:54:50 +03:00
static void internal_set_rx_headroom ( struct net_device * dev , int new_hr )
2016-02-26 12:45:39 +03:00
{
dev - > needed_headroom = new_hr ;
}
2011-10-26 06:26:31 +04:00
static const struct net_device_ops internal_dev_netdev_ops = {
. ndo_open = internal_dev_open ,
. ndo_stop = internal_dev_stop ,
. ndo_start_xmit = internal_dev_xmit ,
2012-11-29 23:55:05 +04:00
. ndo_set_mac_address = eth_mac_addr ,
2011-10-26 06:26:31 +04:00
. ndo_change_mtu = internal_dev_change_mtu ,
2015-10-19 18:31:55 +03:00
. ndo_get_stats64 = internal_get_stats ,
2016-02-26 12:45:39 +03:00
. ndo_set_rx_headroom = internal_set_rx_headroom ,
2011-10-26 06:26:31 +04:00
} ;
2014-06-26 11:58:26 +04:00
static struct rtnl_link_ops internal_dev_link_ops __read_mostly = {
. kind = " openvswitch " ,
} ;
2011-10-26 06:26:31 +04:00
static void do_setup ( struct net_device * netdev )
{
ether_setup ( netdev ) ;
netdev - > netdev_ops = & internal_dev_netdev_ops ;
netdev - > priv_flags & = ~ IFF_TX_SKB_SHARING ;
2016-02-26 12:45:39 +03:00
netdev - > priv_flags | = IFF_LIVE_ADDR_CHANGE | IFF_OPENVSWITCH |
IFF_PHONY_HEADROOM ;
2011-10-26 06:26:31 +04:00
netdev - > destructor = internal_dev_destructor ;
2014-05-11 04:12:32 +04:00
netdev - > ethtool_ops = & internal_dev_ethtool_ops ;
2014-06-26 11:58:26 +04:00
netdev - > rtnl_link_ops = & internal_dev_link_ops ;
2011-10-26 06:26:31 +04:00
netdev - > tx_queue_len = 0 ;
netdev - > features = NETIF_F_LLTX | NETIF_F_SG | NETIF_F_FRAGLIST |
2014-07-18 02:14:15 +04:00
NETIF_F_HIGHDMA | NETIF_F_HW_CSUM |
NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ENCAP_ALL ;
2011-10-26 06:26:31 +04:00
netdev - > vlan_features = netdev - > features ;
2014-07-18 02:14:15 +04:00
netdev - > hw_enc_features = netdev - > features ;
2013-04-19 06:04:27 +04:00
netdev - > features | = NETIF_F_HW_VLAN_CTAG_TX ;
2011-10-26 06:26:31 +04:00
netdev - > hw_features = netdev - > features & ~ NETIF_F_LLTX ;
2014-07-18 02:14:15 +04:00
2012-02-15 10:45:40 +04:00
eth_hw_addr_random ( netdev ) ;
2011-10-26 06:26:31 +04:00
}
static struct vport * internal_dev_create ( const struct vport_parms * parms )
{
struct vport * vport ;
struct internal_dev * internal_dev ;
int err ;
2015-07-21 11:44:04 +03:00
vport = ovs_vport_alloc ( 0 , & ovs_internal_vport_ops , parms ) ;
2011-10-26 06:26:31 +04:00
if ( IS_ERR ( vport ) ) {
err = PTR_ERR ( vport ) ;
goto error ;
}
2015-07-21 11:44:04 +03:00
vport - > dev = alloc_netdev ( sizeof ( struct internal_dev ) ,
parms - > name , NET_NAME_UNKNOWN , do_setup ) ;
if ( ! vport - > dev ) {
2011-10-26 06:26:31 +04:00
err = - ENOMEM ;
goto error_free_vport ;
}
2015-10-19 18:31:55 +03:00
vport - > dev - > tstats = netdev_alloc_pcpu_stats ( struct pcpu_sw_netstats ) ;
if ( ! vport - > dev - > tstats ) {
err = - ENOMEM ;
goto error_free_netdev ;
}
2016-02-26 12:45:39 +03:00
vport - > dev - > needed_headroom = vport - > dp - > max_headroom ;
2011-10-26 06:26:31 +04:00
2015-07-21 11:44:04 +03:00
dev_net_set ( vport - > dev , ovs_dp_get_net ( vport - > dp ) ) ;
internal_dev = internal_dev_priv ( vport - > dev ) ;
2011-10-26 06:26:31 +04:00
internal_dev - > vport = vport ;
2012-02-23 07:58:59 +04:00
/* Restrict bridge port to current netns. */
if ( vport - > port_no = = OVSP_LOCAL )
2015-07-21 11:44:04 +03:00
vport - > dev - > features | = NETIF_F_NETNS_LOCAL ;
2012-02-23 07:58:59 +04:00
2013-04-16 00:23:03 +04:00
rtnl_lock ( ) ;
2015-07-21 11:44:04 +03:00
err = register_netdevice ( vport - > dev ) ;
2011-10-26 06:26:31 +04:00
if ( err )
2015-10-19 18:31:55 +03:00
goto error_unlock ;
2011-10-26 06:26:31 +04:00
2015-07-21 11:44:04 +03:00
dev_set_promiscuity ( vport - > dev , 1 ) ;
2013-04-16 00:23:03 +04:00
rtnl_unlock ( ) ;
2015-07-21 11:44:04 +03:00
netif_start_queue ( vport - > dev ) ;
2011-10-26 06:26:31 +04:00
return vport ;
2015-10-19 18:31:55 +03:00
error_unlock :
2013-04-16 00:23:03 +04:00
rtnl_unlock ( ) ;
2015-10-19 18:31:55 +03:00
free_percpu ( vport - > dev - > tstats ) ;
error_free_netdev :
2015-07-21 11:44:04 +03:00
free_netdev ( vport - > dev ) ;
2011-10-26 06:26:31 +04:00
error_free_vport :
ovs_vport_free ( vport ) ;
error :
return ERR_PTR ( err ) ;
}
static void internal_dev_destroy ( struct vport * vport )
{
2015-07-21 11:44:04 +03:00
netif_stop_queue ( vport - > dev ) ;
2013-04-16 00:23:03 +04:00
rtnl_lock ( ) ;
2015-07-21 11:44:04 +03:00
dev_set_promiscuity ( vport - > dev , - 1 ) ;
2011-10-26 06:26:31 +04:00
/* unregister_netdevice() waits for an RCU grace period. */
2015-07-21 11:44:04 +03:00
unregister_netdevice ( vport - > dev ) ;
2015-10-19 18:31:55 +03:00
free_percpu ( vport - > dev - > tstats ) ;
2013-04-16 00:23:03 +04:00
rtnl_unlock ( ) ;
2011-10-26 06:26:31 +04:00
}
2015-10-21 09:00:10 +03:00
static netdev_tx_t internal_dev_recv ( struct sk_buff * skb )
2011-10-26 06:26:31 +04:00
{
2015-10-21 09:00:10 +03:00
struct net_device * netdev = skb - > dev ;
2015-08-30 03:44:07 +03:00
struct pcpu_sw_netstats * stats ;
2011-10-26 06:26:31 +04:00
2014-09-09 00:17:21 +04:00
if ( unlikely ( ! ( netdev - > flags & IFF_UP ) ) ) {
kfree_skb ( skb ) ;
2015-08-30 03:44:07 +03:00
netdev - > stats . rx_dropped + + ;
2015-10-21 09:00:10 +03:00
return NETDEV_TX_OK ;
2014-09-09 00:17:21 +04:00
}
2012-05-25 22:29:30 +04:00
skb_dst_drop ( skb ) ;
nf_reset ( skb ) ;
secpath_reset ( skb ) ;
2011-10-26 06:26:31 +04:00
skb - > pkt_type = PACKET_HOST ;
skb - > protocol = eth_type_trans ( skb , netdev ) ;
2013-06-13 22:11:44 +04:00
skb_postpull_rcsum ( skb , eth_hdr ( skb ) , ETH_HLEN ) ;
2011-10-26 06:26:31 +04:00
2015-08-30 03:44:07 +03:00
stats = this_cpu_ptr ( netdev - > tstats ) ;
u64_stats_update_begin ( & stats - > syncp ) ;
stats - > rx_packets + + ;
stats - > rx_bytes + = skb - > len ;
u64_stats_update_end ( & stats - > syncp ) ;
2011-10-26 06:26:31 +04:00
2015-08-30 03:44:07 +03:00
netif_rx ( skb ) ;
2015-10-21 09:00:10 +03:00
return NETDEV_TX_OK ;
2011-10-26 06:26:31 +04:00
}
2014-10-22 19:29:06 +04:00
static struct vport_ops ovs_internal_vport_ops = {
2011-10-26 06:26:31 +04:00
. type = OVS_VPORT_TYPE_INTERNAL ,
. create = internal_dev_create ,
. destroy = internal_dev_destroy ,
. send = internal_dev_recv ,
} ;
int ovs_is_internal_dev ( const struct net_device * netdev )
{
return netdev - > netdev_ops = = & internal_dev_netdev_ops ;
}
struct vport * ovs_internal_dev_get_vport ( struct net_device * netdev )
{
if ( ! ovs_is_internal_dev ( netdev ) )
return NULL ;
return internal_dev_priv ( netdev ) - > vport ;
}
2014-06-26 11:58:26 +04:00
int ovs_internal_dev_rtnl_link_register ( void )
{
2014-10-22 19:29:06 +04:00
int err ;
err = rtnl_link_register ( & internal_dev_link_ops ) ;
if ( err < 0 )
return err ;
err = ovs_vport_ops_register ( & ovs_internal_vport_ops ) ;
if ( err < 0 )
rtnl_link_unregister ( & internal_dev_link_ops ) ;
return err ;
2014-06-26 11:58:26 +04:00
}
void ovs_internal_dev_rtnl_link_unregister ( void )
{
2014-10-22 19:29:06 +04:00
ovs_vport_ops_unregister ( & ovs_internal_vport_ops ) ;
2014-06-26 11:58:26 +04:00
rtnl_link_unregister ( & internal_dev_link_ops ) ;
}