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

Merge branch 'bonding'



Jiri Pirko says:

====================
bonding: introduce bonding options Netlink support

This patchset basically allows "mode" and "active_slave" bonding options
to be propagated and set up via standart RT Netlink interface.

In future other options can be easily added as well.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents b1eda2ac ec76aa49
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@

obj-$(CONFIG_BONDING) += bonding.o

bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o bond_debugfs.o
bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o bond_debugfs.o bond_netlink.o bond_options.o

proc-$(CONFIG_PROC_FS) += bond_procfs.o
bonding-objs += $(proc-y)
+7 −84
Original line number Diff line number Diff line
@@ -1910,61 +1910,6 @@ static int bond_release_and_destroy(struct net_device *bond_dev,
	return ret;
}

/*
 * This function changes the active slave to slave <slave_dev>.
 * It returns -EINVAL in the following cases.
 *  - <slave_dev> is not found in the list.
 *  - There is not active slave now.
 *  - <slave_dev> is already active.
 *  - The link state of <slave_dev> is not BOND_LINK_UP.
 *  - <slave_dev> is not running.
 * In these cases, this function does nothing.
 * In the other cases, current_slave pointer is changed and 0 is returned.
 */
static int bond_ioctl_change_active(struct net_device *bond_dev, struct net_device *slave_dev)
{
	struct bonding *bond = netdev_priv(bond_dev);
	struct slave *old_active = NULL;
	struct slave *new_active = NULL;
	int res = 0;

	if (!USES_PRIMARY(bond->params.mode))
		return -EINVAL;

	/* Verify that bond_dev is indeed the master of slave_dev */
	if (!(slave_dev->flags & IFF_SLAVE) ||
	    !netdev_has_upper_dev(slave_dev, bond_dev))
		return -EINVAL;

	read_lock(&bond->lock);

	old_active = bond->curr_active_slave;
	new_active = bond_get_slave_by_dev(bond, slave_dev);
	/*
	 * Changing to the current active: do nothing; return success.
	 */
	if (new_active && new_active == old_active) {
		read_unlock(&bond->lock);
		return 0;
	}

	if (new_active &&
	    old_active &&
	    new_active->link == BOND_LINK_UP &&
	    IS_UP(new_active->dev)) {
		block_netpoll_tx();
		write_lock_bh(&bond->curr_slave_lock);
		bond_change_active_slave(bond, new_active);
		write_unlock_bh(&bond->curr_slave_lock);
		unblock_netpoll_tx();
	} else
		res = -EINVAL;

	read_unlock(&bond->lock);

	return res;
}

static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
{
	struct bonding *bond = netdev_priv(bond_dev);
@@ -3257,6 +3202,7 @@ static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev,

static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd)
{
	struct bonding *bond = netdev_priv(bond_dev);
	struct net_device *slave_dev = NULL;
	struct ifbond k_binfo;
	struct ifbond __user *u_binfo = NULL;
@@ -3287,7 +3233,6 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd


		if (mii->reg_num == 1) {
			struct bonding *bond = netdev_priv(bond_dev);
			mii->val_out = 0;
			read_lock(&bond->lock);
			read_lock(&bond->curr_slave_lock);
@@ -3359,7 +3304,7 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
			break;
		case BOND_CHANGE_ACTIVE_OLD:
		case SIOCBONDCHANGEACTIVE:
			res = bond_ioctl_change_active(bond_dev, slave_dev);
			res = bond_option_active_slave_set(bond, slave_dev);
			break;
		default:
			res = -EOPNOTSUPP;
@@ -3951,7 +3896,7 @@ static void bond_destructor(struct net_device *bond_dev)
	free_netdev(bond_dev);
}

static void bond_setup(struct net_device *bond_dev)
void bond_setup(struct net_device *bond_dev)
{
	struct bonding *bond = netdev_priv(bond_dev);

@@ -4451,32 +4396,11 @@ static int bond_init(struct net_device *bond_dev)
	return 0;
}

static int bond_validate(struct nlattr *tb[], struct nlattr *data[])
{
	if (tb[IFLA_ADDRESS]) {
		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
			return -EINVAL;
		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
			return -EADDRNOTAVAIL;
	}
	return 0;
}

static unsigned int bond_get_num_tx_queues(void)
unsigned int bond_get_num_tx_queues(void)
{
	return tx_queues;
}

static struct rtnl_link_ops bond_link_ops __read_mostly = {
	.kind			= "bond",
	.priv_size		= sizeof(struct bonding),
	.setup			= bond_setup,
	.validate		= bond_validate,
	.get_num_tx_queues	= bond_get_num_tx_queues,
	.get_num_rx_queues	= bond_get_num_tx_queues, /* Use the same number
							     as for TX queues */
};

/* Create a new bond based on the specified name and bonding parameters.
 * If name is NULL, obtain a suitable "bond%d" name for us.
 * Caller must NOT hold rtnl_lock; we need to release it here before we
@@ -4563,7 +4487,7 @@ static int __init bonding_init(void)
	if (res)
		goto out;

	res = rtnl_link_register(&bond_link_ops);
	res = bond_netlink_init();
	if (res)
		goto err_link;

@@ -4579,7 +4503,7 @@ static int __init bonding_init(void)
out:
	return res;
err:
	rtnl_link_unregister(&bond_link_ops);
	bond_netlink_fini();
err_link:
	unregister_pernet_subsys(&bond_net_ops);
	goto out;
@@ -4592,7 +4516,7 @@ static void __exit bonding_exit(void)

	bond_destroy_debugfs();

	rtnl_link_unregister(&bond_link_ops);
	bond_netlink_fini();
	unregister_pernet_subsys(&bond_net_ops);

#ifdef CONFIG_NET_POLL_CONTROLLER
@@ -4609,4 +4533,3 @@ MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
MODULE_DESCRIPTION(DRV_DESCRIPTION ", v" DRV_VERSION);
MODULE_AUTHOR("Thomas Davis, tadavis@lbl.gov and many others");
MODULE_ALIAS_RTNL_LINK("bond");
+131 −0
Original line number Diff line number Diff line
/*
 * drivers/net/bond/bond_netlink.c - Netlink interface for bonding
 * Copyright (c) 2013 Jiri Pirko <jiri@resnulli.us>
 *
 * 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.
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/if_link.h>
#include <linux/if_ether.h>
#include <net/netlink.h>
#include <net/rtnetlink.h>
#include "bonding.h"

static const struct nla_policy bond_policy[IFLA_BOND_MAX + 1] = {
	[IFLA_BOND_MODE]		= { .type = NLA_U8 },
	[IFLA_BOND_ACTIVE_SLAVE]	= { .type = NLA_U32 },
};

static int bond_validate(struct nlattr *tb[], struct nlattr *data[])
{
	if (tb[IFLA_ADDRESS]) {
		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
			return -EINVAL;
		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
			return -EADDRNOTAVAIL;
	}
	return 0;
}

static int bond_changelink(struct net_device *bond_dev,
			   struct nlattr *tb[], struct nlattr *data[])
{
	struct bonding *bond = netdev_priv(bond_dev);
	int err;

	if (data && data[IFLA_BOND_MODE]) {
		int mode = nla_get_u8(data[IFLA_BOND_MODE]);

		err = bond_option_mode_set(bond, mode);
		if (err)
			return err;
	}
	if (data && data[IFLA_BOND_ACTIVE_SLAVE]) {
		int ifindex = nla_get_u32(data[IFLA_BOND_ACTIVE_SLAVE]);
		struct net_device *slave_dev;

		if (ifindex == 0) {
			slave_dev = NULL;
		} else {
			slave_dev = __dev_get_by_index(dev_net(bond_dev),
						       ifindex);
			if (!slave_dev)
				return -ENODEV;
		}
		err = bond_option_active_slave_set(bond, slave_dev);
		if (err)
			return err;
	}
	return 0;
}

static int bond_newlink(struct net *src_net, struct net_device *bond_dev,
			struct nlattr *tb[], struct nlattr *data[])
{
	int err;

	err = bond_changelink(bond_dev, tb, data);
	if (err < 0)
		return err;

	return register_netdevice(bond_dev);
}

static size_t bond_get_size(const struct net_device *bond_dev)
{
	return nla_total_size(sizeof(u8));	/* IFLA_BOND_MODE */
		+ nla_total_size(sizeof(u32));	/* IFLA_BOND_ACTIVE_SLAVE */
}

static int bond_fill_info(struct sk_buff *skb,
			  const struct net_device *bond_dev)
{
	struct bonding *bond = netdev_priv(bond_dev);
	struct net_device *slave_dev = bond_option_active_slave_get(bond);

	if (nla_put_u8(skb, IFLA_BOND_MODE, bond->params.mode) ||
	    (slave_dev &&
	     nla_put_u32(skb, IFLA_BOND_ACTIVE_SLAVE, slave_dev->ifindex)))
		goto nla_put_failure;
	return 0;

nla_put_failure:
	return -EMSGSIZE;
}

struct rtnl_link_ops bond_link_ops __read_mostly = {
	.kind			= "bond",
	.priv_size		= sizeof(struct bonding),
	.setup			= bond_setup,
	.maxtype		= IFLA_BOND_MAX,
	.policy			= bond_policy,
	.validate		= bond_validate,
	.newlink		= bond_newlink,
	.changelink		= bond_changelink,
	.get_size		= bond_get_size,
	.fill_info		= bond_fill_info,
	.get_num_tx_queues	= bond_get_num_tx_queues,
	.get_num_rx_queues	= bond_get_num_tx_queues, /* Use the same number
							     as for TX queues */
};

int __init bond_netlink_init(void)
{
	return rtnl_link_register(&bond_link_ops);
}

void __exit bond_netlink_fini(void)
{
	rtnl_link_unregister(&bond_link_ops);
}

MODULE_ALIAS_RTNL_LINK("bond");
+142 −0
Original line number Diff line number Diff line
/*
 * drivers/net/bond/bond_options.c - bonding options
 * Copyright (c) 2013 Jiri Pirko <jiri@resnulli.us>
 *
 * 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.
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/errno.h>
#include <linux/if.h>
#include <linux/netdevice.h>
#include <linux/rwlock.h>
#include <linux/rcupdate.h>
#include "bonding.h"

static bool bond_mode_is_valid(int mode)
{
	int i;

	for (i = 0; bond_mode_tbl[i].modename; i++);

	return mode >= 0 && mode < i;
}

int bond_option_mode_set(struct bonding *bond, int mode)
{
	if (!bond_mode_is_valid(mode)) {
		pr_err("invalid mode value %d.\n", mode);
		return -EINVAL;
	}

	if (bond->dev->flags & IFF_UP) {
		pr_err("%s: unable to update mode because interface is up.\n",
		       bond->dev->name);
		return -EPERM;
	}

	if (bond_has_slaves(bond)) {
		pr_err("%s: unable to update mode because bond has slaves.\n",
			bond->dev->name);
		return -EPERM;
	}

	if (BOND_MODE_IS_LB(mode) && bond->params.arp_interval) {
		pr_err("%s: %s mode is incompatible with arp monitoring.\n",
		       bond->dev->name, bond_mode_tbl[mode].modename);
		return -EINVAL;
	}

	/* don't cache arp_validate between modes */
	bond->params.arp_validate = BOND_ARP_VALIDATE_NONE;
	bond->params.mode = mode;
	return 0;
}

static struct net_device *__bond_option_active_slave_get(struct bonding *bond,
							 struct slave *slave)
{
	return USES_PRIMARY(bond->params.mode) && slave ? slave->dev : NULL;
}

struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond)
{
	struct slave *slave = rcu_dereference(bond->curr_active_slave);

	return __bond_option_active_slave_get(bond, slave);
}

struct net_device *bond_option_active_slave_get(struct bonding *bond)
{
	return __bond_option_active_slave_get(bond, bond->curr_active_slave);
}

int bond_option_active_slave_set(struct bonding *bond,
				 struct net_device *slave_dev)
{
	int ret = 0;

	if (slave_dev) {
		if (!netif_is_bond_slave(slave_dev)) {
			pr_err("Device %s is not bonding slave.\n",
			       slave_dev->name);
			return -EINVAL;
		}

		if (bond->dev != netdev_master_upper_dev_get(slave_dev)) {
			pr_err("%s: Device %s is not our slave.\n",
			       bond->dev->name, slave_dev->name);
			return -EINVAL;
		}
	}

	if (!USES_PRIMARY(bond->params.mode)) {
		pr_err("%s: Unable to change active slave; %s is in mode %d\n",
		       bond->dev->name, bond->dev->name, bond->params.mode);
		return -EINVAL;
	}

	block_netpoll_tx();
	read_lock(&bond->lock);
	write_lock_bh(&bond->curr_slave_lock);

	/* check to see if we are clearing active */
	if (!slave_dev) {
		pr_info("%s: Clearing current active slave.\n",
		bond->dev->name);
		rcu_assign_pointer(bond->curr_active_slave, NULL);
		bond_select_active_slave(bond);
	} else {
		struct slave *old_active = bond->curr_active_slave;
		struct slave *new_active = bond_slave_get_rtnl(slave_dev);

		BUG_ON(!new_active);

		if (new_active == old_active) {
			/* do nothing */
			pr_info("%s: %s is already the current active slave.\n",
				bond->dev->name, new_active->dev->name);
		} else {
			if (old_active && (new_active->link == BOND_LINK_UP) &&
			    IS_UP(new_active->dev)) {
				pr_info("%s: Setting %s as active slave.\n",
					bond->dev->name, new_active->dev->name);
				bond_change_active_slave(bond, new_active);
			} else {
				pr_err("%s: Could not set %s as active slave; either %s is down or the link is down.\n",
				       bond->dev->name, new_active->dev->name,
				       new_active->dev->name);
				ret = -EINVAL;
			}
		}
	}

	write_unlock_bh(&bond->curr_slave_lock);
	read_unlock(&bond->lock);
	unblock_netpoll_tx();
	return ret;
}
+28 −99
Original line number Diff line number Diff line
@@ -283,49 +283,26 @@ static ssize_t bonding_store_mode(struct device *d,
				  struct device_attribute *attr,
				  const char *buf, size_t count)
{
	int new_value, ret = count;
	int new_value, ret;
	struct bonding *bond = to_bond(d);

	if (!rtnl_trylock())
		return restart_syscall();

	if (bond->dev->flags & IFF_UP) {
		pr_err("unable to update mode of %s because interface is up.\n",
		       bond->dev->name);
		ret = -EPERM;
		goto out;
	}

	if (bond_has_slaves(bond)) {
		pr_err("unable to update mode of %s because it has slaves.\n",
			bond->dev->name);
		ret = -EPERM;
		goto out;
	}

	new_value = bond_parse_parm(buf, bond_mode_tbl);
	if (new_value < 0)  {
		pr_err("%s: Ignoring invalid mode value %.*s.\n",
		       bond->dev->name, (int)strlen(buf) - 1, buf);
		ret = -EINVAL;
		goto out;
	}
	if ((new_value == BOND_MODE_ALB ||
	     new_value == BOND_MODE_TLB) &&
	    bond->params.arp_interval) {
		pr_err("%s: %s mode is incompatible with arp monitoring.\n",
		       bond->dev->name, bond_mode_tbl[new_value].modename);
		ret = -EINVAL;
		goto out;
		return -EINVAL;
	}
	if (!rtnl_trylock())
		return restart_syscall();

	/* don't cache arp_validate between modes */
	bond->params.arp_validate = BOND_ARP_VALIDATE_NONE;
	bond->params.mode = new_value;
	ret = bond_option_mode_set(bond, new_value);
	if (!ret) {
		pr_info("%s: setting mode to %s (%d).\n",
			bond->dev->name, bond_mode_tbl[new_value].modename,
			new_value);
out:
		ret = count;
	}

	rtnl_unlock();
	return ret;
}
@@ -1242,13 +1219,13 @@ static ssize_t bonding_show_active_slave(struct device *d,
					 char *buf)
{
	struct bonding *bond = to_bond(d);
	struct slave *curr;
	struct net_device *slave_dev;
	int count = 0;

	rcu_read_lock();
	curr = rcu_dereference(bond->curr_active_slave);
	if (USES_PRIMARY(bond->params.mode) && curr)
		count = sprintf(buf, "%s\n", curr->dev->name);
	slave_dev = bond_option_active_slave_get_rcu(bond);
	if (slave_dev)
		count = sprintf(buf, "%s\n", slave_dev->name);
	rcu_read_unlock();

	return count;
@@ -1258,81 +1235,33 @@ static ssize_t bonding_store_active_slave(struct device *d,
					  struct device_attribute *attr,
					  const char *buf, size_t count)
{
	struct slave *slave, *old_active, *new_active;
	int ret;
	struct bonding *bond = to_bond(d);
	struct list_head *iter;
	char ifname[IFNAMSIZ];
	struct net_device *dev;

	if (!rtnl_trylock())
		return restart_syscall();

	old_active = new_active = NULL;
	block_netpoll_tx();
	read_lock(&bond->lock);
	write_lock_bh(&bond->curr_slave_lock);

	if (!USES_PRIMARY(bond->params.mode)) {
		pr_info("%s: Unable to change active slave; %s is in mode %d\n",
			bond->dev->name, bond->dev->name, bond->params.mode);
		goto out;
	}

	sscanf(buf, "%15s", ifname); /* IFNAMSIZ */

	/* check to see if we are clearing active */
	if (!strlen(ifname) || buf[0] == '\n') {
		pr_info("%s: Clearing current active slave.\n",
			bond->dev->name);
		rcu_assign_pointer(bond->curr_active_slave, NULL);
		bond_select_active_slave(bond);
		goto out;
	}

	bond_for_each_slave(bond, slave, iter) {
		if (strncmp(slave->dev->name, ifname, IFNAMSIZ) == 0) {
			old_active = bond->curr_active_slave;
			new_active = slave;
			if (new_active == old_active) {
				/* do nothing */
				pr_info("%s: %s is already the current"
					" active slave.\n",
					bond->dev->name,
					slave->dev->name);
				goto out;
		dev = NULL;
	} else {
				if ((new_active) &&
				    (old_active) &&
				    (new_active->link == BOND_LINK_UP) &&
				    IS_UP(new_active->dev)) {
					pr_info("%s: Setting %s as active"
						" slave.\n",
						bond->dev->name,
						slave->dev->name);
					bond_change_active_slave(bond,
								 new_active);
				} else {
					pr_info("%s: Could not set %s as"
						" active slave; either %s is"
						" down or the link is down.\n",
						bond->dev->name,
						slave->dev->name,
						slave->dev->name);
				}
		dev = __dev_get_by_name(dev_net(bond->dev), ifname);
		if (!dev) {
			ret = -ENODEV;
			goto out;
		}
	}
	}

	pr_info("%s: Unable to set %.*s as active slave.\n",
		bond->dev->name, (int)strlen(buf) - 1, buf);
 out:
	write_unlock_bh(&bond->curr_slave_lock);
	read_unlock(&bond->lock);
	unblock_netpoll_tx();
	ret = bond_option_active_slave_set(bond, dev);
	if (!ret)
		ret = count;

 out:
	rtnl_unlock();

	return count;
	return ret;

}
static DEVICE_ATTR(active_slave, S_IRUGO | S_IWUSR,
Loading