Skip to content

Commit

Permalink
macvlan: allow multiple driver backends
Browse files Browse the repository at this point in the history
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: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
arndb authored and davem330 committed Feb 4, 2010
1 parent 8a83a00 commit fc0663d
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 64 deletions.
113 changes: 49 additions & 64 deletions drivers/net/macvlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}

Expand All @@ -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);

Expand All @@ -273,8 +234,8 @@ 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,
struct net_device *dev)
netdev_tx_t macvlan_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
int i = skb_get_queue_mapping(skb);
struct netdev_queue *txq = netdev_get_tx_queue(dev, i);
Expand All @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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])
Expand All @@ -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 void macvlan_dellink(struct net_device *dev, struct list_head *head)
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)
{
struct macvlan_dev *vlan = netdev_priv(dev);
struct macvlan_port *port = vlan->port;
Expand All @@ -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[])
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
70 changes: 70 additions & 0 deletions include/linux/if_macvlan.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,76 @@
#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 */

0 comments on commit fc0663d

Please sign in to comment.