Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 351272
b: refs/heads/master
c: 9ff162a
h: refs/heads/master
v: v3
  • Loading branch information
Jiri Pirko authored and David S. Miller committed Jan 4, 2013
1 parent f7a754c commit 8d9cba6
Show file tree
Hide file tree
Showing 3 changed files with 250 additions and 5 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 04e406dcc54cfd84d333ea673fa986fb5a1840e7
refs/heads/master: 9ff162a8b96c96238773972e26288a366e403b0c
14 changes: 14 additions & 0 deletions trunk/include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,8 @@ struct net_device {
* which this device is member of.
*/

struct list_head upper_dev_list; /* List of upper devices */

/* Interface address info used in eth_type_trans() */
unsigned char *dev_addr; /* hw address, (before bcast
because most packets are
Expand Down Expand Up @@ -2636,6 +2638,18 @@ extern int netdev_max_backlog;
extern int netdev_tstamp_prequeue;
extern int weight_p;
extern int bpf_jit_enable;

extern bool netdev_has_upper_dev(struct net_device *dev,
struct net_device *upper_dev);
extern bool netdev_has_any_upper_dev(struct net_device *dev);
extern struct net_device *netdev_master_upper_dev_get(struct net_device *dev);
extern struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev);
extern int netdev_upper_dev_link(struct net_device *dev,
struct net_device *upper_dev);
extern int netdev_master_upper_dev_link(struct net_device *dev,
struct net_device *upper_dev);
extern void netdev_upper_dev_unlink(struct net_device *dev,
struct net_device *upper_dev);
extern int netdev_set_master(struct net_device *dev, struct net_device *master);
extern int netdev_set_bond_master(struct net_device *dev,
struct net_device *master);
Expand Down
239 changes: 235 additions & 4 deletions trunk/net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -4600,6 +4600,232 @@ static int __init dev_proc_init(void)
#endif /* CONFIG_PROC_FS */


struct netdev_upper {
struct net_device *dev;
bool master;
struct list_head list;
struct rcu_head rcu;
struct list_head search_list;
};

static void __append_search_uppers(struct list_head *search_list,
struct net_device *dev)
{
struct netdev_upper *upper;

list_for_each_entry(upper, &dev->upper_dev_list, list) {
/* check if this upper is not already in search list */
if (list_empty(&upper->search_list))
list_add_tail(&upper->search_list, search_list);
}
}

static bool __netdev_search_upper_dev(struct net_device *dev,
struct net_device *upper_dev)
{
LIST_HEAD(search_list);
struct netdev_upper *upper;
struct netdev_upper *tmp;
bool ret = false;

__append_search_uppers(&search_list, dev);
list_for_each_entry(upper, &search_list, search_list) {
if (upper->dev == upper_dev) {
ret = true;
break;
}
__append_search_uppers(&search_list, upper->dev);
}
list_for_each_entry_safe(upper, tmp, &search_list, search_list)
INIT_LIST_HEAD(&upper->search_list);
return ret;
}

static struct netdev_upper *__netdev_find_upper(struct net_device *dev,
struct net_device *upper_dev)
{
struct netdev_upper *upper;

list_for_each_entry(upper, &dev->upper_dev_list, list) {
if (upper->dev == upper_dev)
return upper;
}
return NULL;
}

/**
* netdev_has_upper_dev - Check if device is linked to an upper device
* @dev: device
* @upper_dev: upper device to check
*
* Find out if a device is linked to specified upper device and return true
* in case it is. Note that this checks only immediate upper device,
* not through a complete stack of devices. The caller must hold the RTNL lock.
*/
bool netdev_has_upper_dev(struct net_device *dev,
struct net_device *upper_dev)
{
ASSERT_RTNL();

return __netdev_find_upper(dev, upper_dev);
}
EXPORT_SYMBOL(netdev_has_upper_dev);

/**
* netdev_has_any_upper_dev - Check if device is linked to some device
* @dev: device
*
* Find out if a device is linked to an upper device and return true in case
* it is. The caller must hold the RTNL lock.
*/
bool netdev_has_any_upper_dev(struct net_device *dev)
{
ASSERT_RTNL();

return !list_empty(&dev->upper_dev_list);
}
EXPORT_SYMBOL(netdev_has_any_upper_dev);

/**
* netdev_master_upper_dev_get - Get master upper device
* @dev: device
*
* Find a master upper device and return pointer to it or NULL in case
* it's not there. The caller must hold the RTNL lock.
*/
struct net_device *netdev_master_upper_dev_get(struct net_device *dev)
{
struct netdev_upper *upper;

ASSERT_RTNL();

if (list_empty(&dev->upper_dev_list))
return NULL;

upper = list_first_entry(&dev->upper_dev_list,
struct netdev_upper, list);
if (likely(upper->master))
return upper->dev;
return NULL;
}
EXPORT_SYMBOL(netdev_master_upper_dev_get);

/**
* netdev_master_upper_dev_get_rcu - Get master upper device
* @dev: device
*
* Find a master upper device and return pointer to it or NULL in case
* it's not there. The caller must hold the RCU read lock.
*/
struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev)
{
struct netdev_upper *upper;

upper = list_first_or_null_rcu(&dev->upper_dev_list,
struct netdev_upper, list);
if (upper && likely(upper->master))
return upper->dev;
return NULL;
}
EXPORT_SYMBOL(netdev_master_upper_dev_get_rcu);

static int __netdev_upper_dev_link(struct net_device *dev,
struct net_device *upper_dev, bool master)
{
struct netdev_upper *upper;

ASSERT_RTNL();

if (dev == upper_dev)
return -EBUSY;

/* To prevent loops, check if dev is not upper device to upper_dev. */
if (__netdev_search_upper_dev(upper_dev, dev))
return -EBUSY;

if (__netdev_find_upper(dev, upper_dev))
return -EEXIST;

if (master && netdev_master_upper_dev_get(dev))
return -EBUSY;

upper = kmalloc(sizeof(*upper), GFP_KERNEL);
if (!upper)
return -ENOMEM;

upper->dev = upper_dev;
upper->master = master;
INIT_LIST_HEAD(&upper->search_list);

/* Ensure that master upper link is always the first item in list. */
if (master)
list_add_rcu(&upper->list, &dev->upper_dev_list);
else
list_add_tail_rcu(&upper->list, &dev->upper_dev_list);
dev_hold(upper_dev);

return 0;
}

/**
* netdev_upper_dev_link - Add a link to the upper device
* @dev: device
* @upper_dev: new upper device
*
* Adds a link to device which is upper to this one. The caller must hold
* the RTNL lock. On a failure a negative errno code is returned.
* On success the reference counts are adjusted and the function
* returns zero.
*/
int netdev_upper_dev_link(struct net_device *dev,
struct net_device *upper_dev)
{
return __netdev_upper_dev_link(dev, upper_dev, false);
}
EXPORT_SYMBOL(netdev_upper_dev_link);

/**
* netdev_master_upper_dev_link - Add a master link to the upper device
* @dev: device
* @upper_dev: new upper device
*
* Adds a link to device which is upper to this one. In this case, only
* one master upper device can be linked, although other non-master devices
* might be linked as well. The caller must hold the RTNL lock.
* On a failure a negative errno code is returned. On success the reference
* counts are adjusted and the function returns zero.
*/
int netdev_master_upper_dev_link(struct net_device *dev,
struct net_device *upper_dev)
{
return __netdev_upper_dev_link(dev, upper_dev, true);
}
EXPORT_SYMBOL(netdev_master_upper_dev_link);

/**
* netdev_upper_dev_unlink - Removes a link to upper device
* @dev: device
* @upper_dev: new upper device
*
* Removes a link to device which is upper to this one. The caller must hold
* the RTNL lock.
*/
void netdev_upper_dev_unlink(struct net_device *dev,
struct net_device *upper_dev)
{
struct netdev_upper *upper;

ASSERT_RTNL();

upper = __netdev_find_upper(dev, upper_dev);
if (!upper)
return;
list_del_rcu(&upper->list);
dev_put(upper_dev);
kfree_rcu(upper, rcu);
}
EXPORT_SYMBOL(netdev_upper_dev_unlink);

/**
* netdev_set_master - set up master pointer
* @slave: slave device
Expand All @@ -4613,19 +4839,23 @@ static int __init dev_proc_init(void)
int netdev_set_master(struct net_device *slave, struct net_device *master)
{
struct net_device *old = slave->master;
int err;

ASSERT_RTNL();

if (master) {
if (old)
return -EBUSY;
dev_hold(master);
err = netdev_master_upper_dev_link(slave, master);
if (err)
return err;
}

slave->master = master;

if (old)
dev_put(old);
netdev_upper_dev_unlink(slave, master);

return 0;
}
EXPORT_SYMBOL(netdev_set_master);
Expand Down Expand Up @@ -5503,8 +5733,8 @@ static void rollback_registered_many(struct list_head *head)
if (dev->netdev_ops->ndo_uninit)
dev->netdev_ops->ndo_uninit(dev);

/* Notifier chain MUST detach us from master device. */
WARN_ON(dev->master);
/* Notifier chain MUST detach us all upper devices. */
WARN_ON(netdev_has_any_upper_dev(dev));

/* Remove entries from kobject tree */
netdev_unregister_kobject(dev);
Expand Down Expand Up @@ -6212,6 +6442,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
INIT_LIST_HEAD(&dev->napi_list);
INIT_LIST_HEAD(&dev->unreg_list);
INIT_LIST_HEAD(&dev->link_watch_list);
INIT_LIST_HEAD(&dev->upper_dev_list);
dev->priv_flags = IFF_XMIT_DST_RELEASE;
setup(dev);

Expand Down

0 comments on commit 8d9cba6

Please sign in to comment.