| From foo@baz Wed May 28 21:03:54 PDT 2014 |
| From: Vlad Yasevich <vyasevic@redhat.com> |
| Date: Fri, 16 May 2014 17:04:53 -0400 |
| Subject: net: Find the nesting level of a given device by type. |
| |
| From: Vlad Yasevich <vyasevic@redhat.com> |
| |
| [ Upstream commit 4085ebe8c31face855fd01ee40372cb4aab1df3a ] |
| |
| Multiple devices in the kernel can be stacked/nested and they |
| need to know their nesting level for the purposes of lockdep. |
| This patch provides a generic function that determines a nesting |
| level of a particular device by its type (ex: vlan, macvlan, etc). |
| We only care about nesting of the same type of devices. |
| |
| For example: |
| eth0 <- vlan0.10 <- macvlan0 <- vlan1.20 |
| |
| The nesting level of vlan1.20 would be 1, since there is another vlan |
| in the stack under it. |
| |
| Signed-off-by: Vlad Yasevich <vyasevic@redhat.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| include/linux/netdevice.h | 10 +++++++++ |
| net/core/dev.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++ |
| 2 files changed, 60 insertions(+) |
| |
| --- a/include/linux/netdevice.h |
| +++ b/include/linux/netdevice.h |
| @@ -2988,6 +2988,14 @@ void *netdev_lower_get_next_private_rcu( |
| priv; \ |
| priv = netdev_lower_get_next_private_rcu(dev, &(iter))) |
| |
| +void *netdev_lower_get_next(struct net_device *dev, |
| + struct list_head **iter); |
| +#define netdev_for_each_lower_dev(dev, ldev, iter) \ |
| + for (iter = &(dev)->adj_list.lower, \ |
| + ldev = netdev_lower_get_next(dev, &(iter)); \ |
| + ldev; \ |
| + ldev = netdev_lower_get_next(dev, &(iter))) |
| + |
| void *netdev_adjacent_get_private(struct list_head *adj_list); |
| void *netdev_lower_get_first_private_rcu(struct net_device *dev); |
| struct net_device *netdev_master_upper_dev_get(struct net_device *dev); |
| @@ -3003,6 +3011,8 @@ void netdev_upper_dev_unlink(struct net_ |
| void netdev_adjacent_rename_links(struct net_device *dev, char *oldname); |
| void *netdev_lower_dev_get_private(struct net_device *dev, |
| struct net_device *lower_dev); |
| +int dev_get_nest_level(struct net_device *dev, |
| + bool (*type_check)(struct net_device *dev)); |
| int skb_checksum_help(struct sk_buff *skb); |
| struct sk_buff *__skb_gso_segment(struct sk_buff *skb, |
| netdev_features_t features, bool tx_path); |
| --- a/net/core/dev.c |
| +++ b/net/core/dev.c |
| @@ -4605,6 +4605,32 @@ void *netdev_lower_get_next_private_rcu( |
| EXPORT_SYMBOL(netdev_lower_get_next_private_rcu); |
| |
| /** |
| + * netdev_lower_get_next - Get the next device from the lower neighbour |
| + * list |
| + * @dev: device |
| + * @iter: list_head ** of the current position |
| + * |
| + * Gets the next netdev_adjacent from the dev's lower neighbour |
| + * list, starting from iter position. The caller must hold RTNL lock or |
| + * its own locking that guarantees that the neighbour lower |
| + * list will remain unchainged. |
| + */ |
| +void *netdev_lower_get_next(struct net_device *dev, struct list_head **iter) |
| +{ |
| + struct netdev_adjacent *lower; |
| + |
| + lower = list_entry((*iter)->next, struct netdev_adjacent, list); |
| + |
| + if (&lower->list == &dev->adj_list.lower) |
| + return NULL; |
| + |
| + *iter = &lower->list; |
| + |
| + return lower->dev; |
| +} |
| +EXPORT_SYMBOL(netdev_lower_get_next); |
| + |
| +/** |
| * netdev_lower_get_first_private_rcu - Get the first ->private from the |
| * lower neighbour list, RCU |
| * variant |
| @@ -5054,6 +5080,30 @@ void *netdev_lower_dev_get_private(struc |
| } |
| EXPORT_SYMBOL(netdev_lower_dev_get_private); |
| |
| + |
| +int dev_get_nest_level(struct net_device *dev, |
| + bool (*type_check)(struct net_device *dev)) |
| +{ |
| + struct net_device *lower = NULL; |
| + struct list_head *iter; |
| + int max_nest = -1; |
| + int nest; |
| + |
| + ASSERT_RTNL(); |
| + |
| + netdev_for_each_lower_dev(dev, lower, iter) { |
| + nest = dev_get_nest_level(lower, type_check); |
| + if (max_nest < nest) |
| + max_nest = nest; |
| + } |
| + |
| + if (type_check(dev)) |
| + max_nest++; |
| + |
| + return max_nest; |
| +} |
| +EXPORT_SYMBOL(dev_get_nest_level); |
| + |
| static void dev_change_rx_flags(struct net_device *dev, int flags) |
| { |
| const struct net_device_ops *ops = dev->netdev_ops; |