Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f82ff596 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'L3_master_device'



David Ahern says:

====================
net: L3 master device

The VRF device is essentially a Layer 3 master device used to associate
netdevices with a specific routing table and to influence FIB lookups
via 'ip rules' and controlling the oif/iif used for the lookup.

This series generalizes the VRF into L3 master device, l3mdev. Similar
to switchdev it has a Kconfig option and separate set of operations
in net_device allowing it to be completely compiled out if not wanted.
The l3mdev methods rely on the 'master' aspect and use of
netdev_master_upper_dev_get_rcu to retrieve the master device from a
given netdevice if it is enslaved to an L3_MASTER.

The VRF device is converted to use the l3mdev operations. At the end the
vrf_ptr is no longer and removed, as are all direct references to VRF.
The end result is a much simpler implementation for VRF.

Thanks to Nikolay for suggestions (eg., use of the master linkage which
is the key to making this work) and to Roopa, Andy and Shrijeet for
early reviews.

v3
- added license header to l3mdev.c

- export symbols in l3mdev.c for use with GPL modules

- removed netdevice header from l3mdev.h (not needed) and fixed
  typo in comment

v2
- rebased to top of net-next
- addressed Niks comments (checking master, removing extra lines, and
  flipping the order of patches 1 and 2)
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents e6934f3e 9478d12d
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -6095,6 +6095,13 @@ F: Documentation/auxdisplay/ks0108
F:	drivers/auxdisplay/ks0108.c
F:	include/linux/ks0108.h

L3MDEV
M:	David Ahern <dsa@cumulusnetworks.com>
L:	netdev@vger.kernel.org
S:	Maintained
F:	net/l3mdev
F:	include/net/l3mdev.h

LAPB module
L:	linux-x25@vger.kernel.org
S:	Orphan
@@ -11266,7 +11273,6 @@ M: Shrijeet Mukherjee <shm@cumulusnetworks.com>
L:	netdev@vger.kernel.org
S:	Maintained
F:	drivers/net/vrf.c
F:	include/net/vrf.h
F:	Documentation/networking/vrf.txt

VT1211 HARDWARE MONITOR DRIVER
+1 −0
Original line number Diff line number Diff line
@@ -299,6 +299,7 @@ config NLMON
config NET_VRF
	tristate "Virtual Routing and Forwarding (Lite)"
	depends on IP_MULTIPLE_TABLES && IPV6_MULTIPLE_TABLES
	depends on NET_L3_MASTER_DEV
	---help---
	  This option enables the support for mapping interfaces into VRF's. The
	  support enables VRF devices.
+48 −33
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@
#include <net/rtnetlink.h>
#include <net/route.h>
#include <net/addrconf.h>
#include <net/vrf.h>
#include <net/l3mdev.h>

#define DRV_NAME	"vrf"
#define DRV_VERSION	"1.0"
@@ -44,6 +44,21 @@
#define vrf_master_get_rcu(dev) \
	((struct net_device *)rcu_dereference(dev->rx_handler_data))

struct slave {
	struct list_head        list;
	struct net_device       *dev;
};

struct slave_queue {
	struct list_head        all_slaves;
};

struct net_vrf {
	struct slave_queue      queue;
	struct rtable           *rth;
	u32                     tb_id;
};

struct pcpu_dstats {
	u64			tx_pkts;
	u64			tx_bytes;
@@ -395,18 +410,15 @@ static void __vrf_insert_slave(struct slave_queue *queue, struct slave *slave)

static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
{
	struct net_vrf_dev *vrf_ptr = kmalloc(sizeof(*vrf_ptr), GFP_KERNEL);
	struct slave *slave = kzalloc(sizeof(*slave), GFP_KERNEL);
	struct net_vrf *vrf = netdev_priv(dev);
	struct slave_queue *queue = &vrf->queue;
	int ret = -ENOMEM;

	if (!slave || !vrf_ptr)
	if (!slave)
		goto out_fail;

	slave->dev = port_dev;
	vrf_ptr->ifindex = dev->ifindex;
	vrf_ptr->tb_id = vrf->tb_id;

	/* register the packet handler for slave ports */
	ret = netdev_rx_handler_register(port_dev, vrf_handle_frame, dev);
@@ -423,7 +435,6 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)

	port_dev->flags |= IFF_SLAVE;
	__vrf_insert_slave(queue, slave);
	rcu_assign_pointer(port_dev->vrf_ptr, vrf_ptr);
	cycle_netdev(port_dev);

	return 0;
@@ -431,14 +442,13 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
out_unregister:
	netdev_rx_handler_unregister(port_dev);
out_fail:
	kfree(vrf_ptr);
	kfree(slave);
	return ret;
}

static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
{
	if (netif_is_vrf(port_dev) || vrf_is_slave(port_dev))
	if (netif_is_l3_master(port_dev) || vrf_is_slave(port_dev))
		return -EINVAL;

	return do_vrf_add_slave(dev, port_dev);
@@ -447,21 +457,15 @@ static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
/* inverse of do_vrf_add_slave */
static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
{
	struct net_vrf_dev *vrf_ptr = rtnl_dereference(port_dev->vrf_ptr);
	struct net_vrf *vrf = netdev_priv(dev);
	struct slave_queue *queue = &vrf->queue;
	struct slave *slave;

	RCU_INIT_POINTER(port_dev->vrf_ptr, NULL);

	netdev_upper_dev_unlink(port_dev, dev);
	port_dev->flags &= ~IFF_SLAVE;

	netdev_rx_handler_unregister(port_dev);

	/* after netdev_rx_handler_unregister for synchronize_rcu */
	kfree(vrf_ptr);

	cycle_netdev(port_dev);

	slave = __vrf_find_slave_dev(queue, port_dev);
@@ -529,6 +533,33 @@ static const struct net_device_ops vrf_netdev_ops = {
	.ndo_del_slave		= vrf_del_slave,
};

static u32 vrf_fib_table(const struct net_device *dev)
{
	struct net_vrf *vrf = netdev_priv(dev);

	return vrf->tb_id;
}

static struct rtable *vrf_get_rtable(const struct net_device *dev,
				     const struct flowi4 *fl4)
{
	struct rtable *rth = NULL;

	if (!(fl4->flowi4_flags & FLOWI_FLAG_VRFSRC)) {
		struct net_vrf *vrf = netdev_priv(dev);

		rth = vrf->rth;
		atomic_inc(&rth->dst.__refcnt);
	}

	return rth;
}

static const struct l3mdev_ops vrf_l3mdev_ops = {
	.l3mdev_fib_table	= vrf_fib_table,
	.l3mdev_get_rtable	= vrf_get_rtable,
};

static void vrf_get_drvinfo(struct net_device *dev,
			    struct ethtool_drvinfo *info)
{
@@ -546,6 +577,7 @@ static void vrf_setup(struct net_device *dev)

	/* Initialize the device structure. */
	dev->netdev_ops = &vrf_netdev_ops;
	dev->l3mdev_ops = &vrf_l3mdev_ops;
	dev->ethtool_ops = &vrf_ethtool_ops;
	dev->destructor = free_netdev;

@@ -572,10 +604,6 @@ static int vrf_validate(struct nlattr *tb[], struct nlattr *data[])

static void vrf_dellink(struct net_device *dev, struct list_head *head)
{
	struct net_vrf_dev *vrf_ptr = rtnl_dereference(dev->vrf_ptr);

	RCU_INIT_POINTER(dev->vrf_ptr, NULL);
	kfree_rcu(vrf_ptr, rcu);
	unregister_netdevice_queue(dev, head);
}

@@ -583,7 +611,6 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev,
		       struct nlattr *tb[], struct nlattr *data[])
{
	struct net_vrf *vrf = netdev_priv(dev);
	struct net_vrf_dev *vrf_ptr;
	int err;

	if (!data || !data[IFLA_VRF_TABLE])
@@ -591,26 +618,15 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev,

	vrf->tb_id = nla_get_u32(data[IFLA_VRF_TABLE]);

	dev->priv_flags |= IFF_VRF_MASTER;

	err = -ENOMEM;
	vrf_ptr = kmalloc(sizeof(*dev->vrf_ptr), GFP_KERNEL);
	if (!vrf_ptr)
		goto out_fail;

	vrf_ptr->ifindex = dev->ifindex;
	vrf_ptr->tb_id = vrf->tb_id;
	dev->priv_flags |= IFF_L3MDEV_MASTER;

	err = register_netdevice(dev);
	if (err < 0)
		goto out_fail;

	rcu_assign_pointer(dev->vrf_ptr, vrf_ptr);

	return 0;

out_fail:
	kfree(vrf_ptr);
	free_netdev(dev);
	return err;
}
@@ -654,10 +670,9 @@ static int vrf_device_event(struct notifier_block *unused,

	/* only care about unregister events to drop slave references */
	if (event == NETDEV_UNREGISTER) {
		struct net_vrf_dev *vrf_ptr = rtnl_dereference(dev->vrf_ptr);
		struct net_device *vrf_dev;

		if (!vrf_ptr || netif_is_vrf(dev))
		if (netif_is_l3_master(dev))
			goto out;

		vrf_dev = netdev_master_upper_dev_get(dev);
+8 −28
Original line number Diff line number Diff line
@@ -1258,7 +1258,7 @@ struct net_device_ops {
 * @IFF_LIVE_ADDR_CHANGE: device supports hardware address
 *	change when it's running
 * @IFF_MACVLAN: Macvlan device
 * @IFF_VRF_MASTER: device is a VRF master
 * @IFF_L3MDEV_MASTER: device is an L3 master device
 * @IFF_NO_QUEUE: device can run without qdisc attached
 * @IFF_OPENVSWITCH: device is a Open vSwitch master
 */
@@ -1283,7 +1283,7 @@ enum netdev_priv_flags {
	IFF_XMIT_DST_RELEASE_PERM	= 1<<17,
	IFF_IPVLAN_MASTER		= 1<<18,
	IFF_IPVLAN_SLAVE		= 1<<19,
	IFF_VRF_MASTER			= 1<<20,
	IFF_L3MDEV_MASTER		= 1<<20,
	IFF_NO_QUEUE			= 1<<21,
	IFF_OPENVSWITCH			= 1<<22,
};
@@ -1308,7 +1308,7 @@ enum netdev_priv_flags {
#define IFF_XMIT_DST_RELEASE_PERM	IFF_XMIT_DST_RELEASE_PERM
#define IFF_IPVLAN_MASTER		IFF_IPVLAN_MASTER
#define IFF_IPVLAN_SLAVE		IFF_IPVLAN_SLAVE
#define IFF_VRF_MASTER			IFF_VRF_MASTER
#define IFF_L3MDEV_MASTER		IFF_L3MDEV_MASTER
#define IFF_NO_QUEUE			IFF_NO_QUEUE
#define IFF_OPENVSWITCH			IFF_OPENVSWITCH

@@ -1427,7 +1427,6 @@ enum netdev_priv_flags {
 *	@dn_ptr:	DECnet specific data
 *	@ip6_ptr:	IPv6 specific data
 *	@ax25_ptr:	AX.25 specific data
 *	@vrf_ptr:	VRF specific data
 *	@ieee80211_ptr:	IEEE 802.11 specific data, assign before registering
 *
 *	@last_rx:	Time of last Rx
@@ -1587,6 +1586,9 @@ struct net_device {
#ifdef CONFIG_NET_SWITCHDEV
	const struct switchdev_ops *switchdev_ops;
#endif
#ifdef CONFIG_NET_L3_MASTER_DEV
	const struct l3mdev_ops	*l3mdev_ops;
#endif

	const struct header_ops *header_ops;

@@ -1646,7 +1648,6 @@ struct net_device {
	struct dn_dev __rcu     *dn_ptr;
	struct inet6_dev __rcu	*ip6_ptr;
	void			*ax25_ptr;
	struct net_vrf_dev __rcu *vrf_ptr;
	struct wireless_dev	*ieee80211_ptr;
	struct wpan_dev		*ieee802154_ptr;
#if IS_ENABLED(CONFIG_MPLS_ROUTING)
@@ -3824,9 +3825,9 @@ static inline bool netif_supports_nofcs(struct net_device *dev)
	return dev->priv_flags & IFF_SUPP_NOFCS;
}

static inline bool netif_is_vrf(const struct net_device *dev)
static inline bool netif_is_l3_master(const struct net_device *dev)
{
	return dev->priv_flags & IFF_VRF_MASTER;
	return dev->priv_flags & IFF_L3MDEV_MASTER;
}

static inline bool netif_is_bridge_master(const struct net_device *dev)
@@ -3839,27 +3840,6 @@ static inline bool netif_is_ovs_master(const struct net_device *dev)
	return dev->priv_flags & IFF_OPENVSWITCH;
}

static inline bool netif_index_is_vrf(struct net *net, int ifindex)
{
	bool rc = false;

#if IS_ENABLED(CONFIG_NET_VRF)
	struct net_device *dev;

	if (ifindex == 0)
		return false;

	rcu_read_lock();

	dev = dev_get_by_index_rcu(net, ifindex);
	if (dev)
		rc = netif_is_vrf(dev);

	rcu_read_unlock();
#endif
	return rc;
}

/* This device needs to keep skb dst for qdisc enqueue or ndo_start_xmit() */
static inline void netif_keep_dst(struct net_device *dev)
{

include/net/l3mdev.h

0 → 100644
+149 −0
Original line number Diff line number Diff line
/*
 * include/net/l3mdev.h - L3 master device API
 * Copyright (c) 2015 Cumulus Networks
 * Copyright (c) 2015 David Ahern <dsa@cumulusnetworks.com>
 *
 * 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.
 */
#ifndef _NET_L3MDEV_H_
#define _NET_L3MDEV_H_

/**
 * struct l3mdev_ops - l3mdev operations
 *
 * @l3mdev_fib_table: Get FIB table id to use for lookups
 *
 * @l3mdev_get_rtable: Get cached IPv4 rtable (dst_entry) for device
 */

struct l3mdev_ops {
	u32		(*l3mdev_fib_table)(const struct net_device *dev);
	struct rtable *	(*l3mdev_get_rtable)(const struct net_device *dev,
					     const struct flowi4 *fl4);
};

#ifdef CONFIG_NET_L3_MASTER_DEV

int l3mdev_master_ifindex_rcu(struct net_device *dev);
static inline int l3mdev_master_ifindex(struct net_device *dev)
{
	int ifindex;

	rcu_read_lock();
	ifindex = l3mdev_master_ifindex_rcu(dev);
	rcu_read_unlock();

	return ifindex;
}

/* get index of an interface to use for FIB lookups. For devices
 * enslaved to an L3 master device FIB lookups are based on the
 * master index
 */
static inline int l3mdev_fib_oif_rcu(struct net_device *dev)
{
	return l3mdev_master_ifindex_rcu(dev) ? : dev->ifindex;
}

static inline int l3mdev_fib_oif(struct net_device *dev)
{
	int oif;

	rcu_read_lock();
	oif = l3mdev_fib_oif_rcu(dev);
	rcu_read_unlock();

	return oif;
}

u32 l3mdev_fib_table_rcu(const struct net_device *dev);
u32 l3mdev_fib_table_by_index(struct net *net, int ifindex);
static inline u32 l3mdev_fib_table(const struct net_device *dev)
{
	u32 tb_id;

	rcu_read_lock();
	tb_id = l3mdev_fib_table_rcu(dev);
	rcu_read_unlock();

	return tb_id;
}

static inline struct rtable *l3mdev_get_rtable(const struct net_device *dev,
					       const struct flowi4 *fl4)
{
	if (netif_is_l3_master(dev) && dev->l3mdev_ops->l3mdev_get_rtable)
		return dev->l3mdev_ops->l3mdev_get_rtable(dev, fl4);

	return NULL;
}

static inline bool netif_index_is_l3_master(struct net *net, int ifindex)
{
	struct net_device *dev;
	bool rc = false;

	if (ifindex == 0)
		return false;

	rcu_read_lock();

	dev = dev_get_by_index_rcu(net, ifindex);
	if (dev)
		rc = netif_is_l3_master(dev);

	rcu_read_unlock();

	return rc;
}

#else

static inline int l3mdev_master_ifindex_rcu(struct net_device *dev)
{
	return 0;
}
static inline int l3mdev_master_ifindex(struct net_device *dev)
{
	return 0;
}

static inline int l3mdev_fib_oif_rcu(struct net_device *dev)
{
	return dev ? dev->ifindex : 0;
}
static inline int l3mdev_fib_oif(struct net_device *dev)
{
	return dev ? dev->ifindex : 0;
}

static inline u32 l3mdev_fib_table_rcu(const struct net_device *dev)
{
	return 0;
}
static inline u32 l3mdev_fib_table(const struct net_device *dev)
{
	return 0;
}
static inline u32 l3mdev_fib_table_by_index(struct net *net, int ifindex)
{
	return 0;
}

static inline struct rtable *l3mdev_get_rtable(const struct net_device *dev,
					       const struct flowi4 *fl4)
{
	return NULL;
}

static inline bool netif_index_is_l3_master(struct net *net, int ifindex)
{
	return false;
}

#endif

#endif /* _NET_L3MDEV_H_ */
Loading