Commit fc0663d6 authored by Arnd Bergmann's avatar Arnd Bergmann Committed by David S. Miller

macvlan: allow multiple driver backends

This makes it possible to hook into the macvlan driver
from another kernel module. In particular, the goal is
to extend it with the macvtap backend that provides
a tun/tap compatible interface directly on the macvlan
device.
Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8a83a00b
......@@ -39,31 +39,6 @@ struct macvlan_port {
struct list_head vlans;
};
/**
* struct macvlan_rx_stats - MACVLAN percpu rx stats
* @rx_packets: number of received packets
* @rx_bytes: number of received bytes
* @multicast: number of received multicast packets
* @rx_errors: number of errors
*/
struct macvlan_rx_stats {
unsigned long rx_packets;
unsigned long rx_bytes;
unsigned long multicast;
unsigned long rx_errors;
};
struct macvlan_dev {
struct net_device *dev;
struct list_head list;
struct hlist_node hlist;
struct macvlan_port *port;
struct net_device *lowerdev;
struct macvlan_rx_stats *rx_stats;
enum macvlan_mode mode;
};
static struct macvlan_dev *macvlan_hash_lookup(const struct macvlan_port *port,
const unsigned char *addr)
{
......@@ -118,31 +93,17 @@ static int macvlan_addr_busy(const struct macvlan_port *port,
return 0;
}
static inline void macvlan_count_rx(const struct macvlan_dev *vlan,
unsigned int len, bool success,
bool multicast)
{
struct macvlan_rx_stats *rx_stats;
rx_stats = per_cpu_ptr(vlan->rx_stats, smp_processor_id());
if (likely(success)) {
rx_stats->rx_packets++;;
rx_stats->rx_bytes += len;
if (multicast)
rx_stats->multicast++;
} else {
rx_stats->rx_errors++;
}
}
static int macvlan_broadcast_one(struct sk_buff *skb, struct net_device *dev,
static int macvlan_broadcast_one(struct sk_buff *skb,
const struct macvlan_dev *vlan,
const struct ethhdr *eth, bool local)
{
struct net_device *dev = vlan->dev;
if (!skb)
return NET_RX_DROP;
if (local)
return dev_forward_skb(dev, skb);
return vlan->forward(dev, skb);
skb->dev = dev;
if (!compare_ether_addr_64bits(eth->h_dest,
......@@ -151,7 +112,7 @@ static int macvlan_broadcast_one(struct sk_buff *skb, struct net_device *dev,
else
skb->pkt_type = PACKET_MULTICAST;
return netif_rx(skb);
return vlan->receive(skb);
}
static void macvlan_broadcast(struct sk_buff *skb,
......@@ -175,7 +136,7 @@ static void macvlan_broadcast(struct sk_buff *skb,
continue;
nskb = skb_clone(skb, GFP_ATOMIC);
err = macvlan_broadcast_one(nskb, vlan->dev, eth,
err = macvlan_broadcast_one(nskb, vlan, eth,
mode == MACVLAN_MODE_BRIDGE);
macvlan_count_rx(vlan, skb->len + ETH_HLEN,
err == NET_RX_SUCCESS, 1);
......@@ -238,7 +199,7 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
skb->dev = dev;
skb->pkt_type = PACKET_HOST;
netif_rx(skb);
vlan->receive(skb);
return NULL;
}
......@@ -260,7 +221,7 @@ static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
dest = macvlan_hash_lookup(port, eth->h_dest);
if (dest && dest->mode == MACVLAN_MODE_BRIDGE) {
unsigned int length = skb->len + ETH_HLEN;
int ret = dev_forward_skb(dest->dev, skb);
int ret = dest->forward(dest->dev, skb);
macvlan_count_rx(dest, length,
ret == NET_RX_SUCCESS, 0);
......@@ -273,7 +234,7 @@ static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
return dev_queue_xmit(skb);
}
static netdev_tx_t macvlan_start_xmit(struct sk_buff *skb,
netdev_tx_t macvlan_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
int i = skb_get_queue_mapping(skb);
......@@ -290,6 +251,7 @@ static netdev_tx_t macvlan_start_xmit(struct sk_buff *skb,
return ret;
}
EXPORT_SYMBOL_GPL(macvlan_start_xmit);
static int macvlan_hard_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type, const void *daddr,
......@@ -623,8 +585,11 @@ static int macvlan_get_tx_queues(struct net *net,
return 0;
}
static int macvlan_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
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))
{
struct macvlan_dev *vlan = netdev_priv(dev);
struct macvlan_port *port;
......@@ -664,6 +629,8 @@ static int macvlan_newlink(struct net *src_net, struct net_device *dev,
vlan->lowerdev = lowerdev;
vlan->dev = dev;
vlan->port = port;
vlan->receive = receive;
vlan->forward = forward;
vlan->mode = MACVLAN_MODE_VEPA;
if (data && data[IFLA_MACVLAN_MODE])
......@@ -677,8 +644,17 @@ static int macvlan_newlink(struct net *src_net, struct net_device *dev,
netif_stacked_transfer_operstate(lowerdev, dev);
return 0;
}
EXPORT_SYMBOL_GPL(macvlan_common_newlink);
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);
}
static void macvlan_dellink(struct net_device *dev, struct list_head *head)
void macvlan_dellink(struct net_device *dev, struct list_head *head)
{
struct macvlan_dev *vlan = netdev_priv(dev);
struct macvlan_port *port = vlan->port;
......@@ -689,6 +665,7 @@ static void macvlan_dellink(struct net_device *dev, struct list_head *head)
if (list_empty(&port->vlans))
macvlan_port_destroy(port->dev);
}
EXPORT_SYMBOL_GPL(macvlan_dellink);
static int macvlan_changelink(struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
......@@ -720,19 +697,27 @@ static const struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX + 1] = {
[IFLA_MACVLAN_MODE] = { .type = NLA_U32 },
};
static struct rtnl_link_ops macvlan_link_ops __read_mostly = {
int macvlan_link_register(struct rtnl_link_ops *ops)
{
/* common fields */
ops->priv_size = sizeof(struct macvlan_dev);
ops->get_tx_queues = macvlan_get_tx_queues;
ops->setup = macvlan_setup;
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 = {
.kind = "macvlan",
.priv_size = sizeof(struct macvlan_dev),
.get_tx_queues = macvlan_get_tx_queues,
.setup = macvlan_setup,
.validate = macvlan_validate,
.newlink = macvlan_newlink,
.dellink = macvlan_dellink,
.maxtype = IFLA_MACVLAN_MAX,
.policy = macvlan_policy,
.changelink = macvlan_changelink,
.get_size = macvlan_get_size,
.fill_info = macvlan_fill_info,
};
static int macvlan_device_event(struct notifier_block *unused,
......@@ -761,7 +746,7 @@ static int macvlan_device_event(struct notifier_block *unused,
break;
case NETDEV_UNREGISTER:
list_for_each_entry_safe(vlan, next, &port->vlans, list)
macvlan_dellink(vlan->dev, NULL);
vlan->dev->rtnl_link_ops->dellink(vlan->dev, NULL);
break;
}
return NOTIFY_DONE;
......@@ -778,7 +763,7 @@ static int __init macvlan_init_module(void)
register_netdevice_notifier(&macvlan_notifier_block);
macvlan_handle_frame_hook = macvlan_handle_frame;
err = rtnl_link_register(&macvlan_link_ops);
err = macvlan_link_register(&macvlan_link_ops);
if (err < 0)
goto err1;
return 0;
......
#ifndef _LINUX_IF_MACVLAN_H
#define _LINUX_IF_MACVLAN_H
#include <linux/if_link.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/netlink.h>
#include <net/netlink.h>
struct macvlan_port;
struct macvtap_queue;
/**
* struct macvlan_rx_stats - MACVLAN percpu rx stats
* @rx_packets: number of received packets
* @rx_bytes: number of received bytes
* @multicast: number of received multicast packets
* @rx_errors: number of errors
*/
struct macvlan_rx_stats {
unsigned long rx_packets;
unsigned long rx_bytes;
unsigned long multicast;
unsigned long rx_errors;
};
struct macvlan_dev {
struct net_device *dev;
struct list_head list;
struct hlist_node hlist;
struct macvlan_port *port;
struct net_device *lowerdev;
struct macvlan_rx_stats *rx_stats;
enum macvlan_mode mode;
int (*receive)(struct sk_buff *skb);
int (*forward)(struct net_device *dev, struct sk_buff *skb);
};
static inline void macvlan_count_rx(const struct macvlan_dev *vlan,
unsigned int len, bool success,
bool multicast)
{
struct macvlan_rx_stats *rx_stats;
rx_stats = per_cpu_ptr(vlan->rx_stats, smp_processor_id());
if (likely(success)) {
rx_stats->rx_packets++;;
rx_stats->rx_bytes += len;
if (multicast)
rx_stats->multicast++;
} else {
rx_stats->rx_errors++;
}
}
extern 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));
extern void macvlan_count_rx(const struct macvlan_dev *vlan,
unsigned int len, bool success,
bool multicast);
extern void macvlan_dellink(struct net_device *dev, struct list_head *head);
extern int macvlan_link_register(struct rtnl_link_ops *ops);
extern netdev_tx_t macvlan_start_xmit(struct sk_buff *skb,
struct net_device *dev);
extern struct sk_buff *(*macvlan_handle_frame_hook)(struct sk_buff *);
#endif /* _LINUX_IF_MACVLAN_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment