diff options
Diffstat (limited to 'drivers/net/bonding/bond_sysfs.c')
-rw-r--r-- | drivers/net/bonding/bond_sysfs.c | 219 |
1 files changed, 140 insertions, 79 deletions
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index 8a96735..cf95bd8 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -55,8 +55,8 @@ static ssize_t bonding_show_bonds(struct class *cls, struct class_attribute *attr, char *buf) { - struct net *net = current->nsproxy->net_ns; - struct bond_net *bn = net_generic(net, bond_net_id); + struct bond_net *bn = + container_of(attr, struct bond_net, class_attr_bonding_masters); int res = 0; struct bonding *bond; @@ -79,9 +79,8 @@ static ssize_t bonding_show_bonds(struct class *cls, return res; } -static struct net_device *bond_get_by_name(struct net *net, const char *ifname) +static struct net_device *bond_get_by_name(struct bond_net *bn, const char *ifname) { - struct bond_net *bn = net_generic(net, bond_net_id); struct bonding *bond; list_for_each_entry(bond, &bn->dev_list, bond_list) { @@ -103,7 +102,8 @@ static ssize_t bonding_store_bonds(struct class *cls, struct class_attribute *attr, const char *buffer, size_t count) { - struct net *net = current->nsproxy->net_ns; + struct bond_net *bn = + container_of(attr, struct bond_net, class_attr_bonding_masters); char command[IFNAMSIZ + 1] = {0, }; char *ifname; int rv, res = count; @@ -116,7 +116,7 @@ static ssize_t bonding_store_bonds(struct class *cls, if (command[0] == '+') { pr_info("%s is being created...\n", ifname); - rv = bond_create(net, ifname); + rv = bond_create(bn->net, ifname); if (rv) { if (rv == -EEXIST) pr_info("%s already exists.\n", ifname); @@ -128,7 +128,7 @@ static ssize_t bonding_store_bonds(struct class *cls, struct net_device *bond_dev; rtnl_lock(); - bond_dev = bond_get_by_name(net, ifname); + bond_dev = bond_get_by_name(bn, ifname); if (bond_dev) { pr_info("%s is being deleted...\n", ifname); unregister_netdevice(bond_dev); @@ -150,9 +150,24 @@ err_no_cmd: return -EPERM; } +static const void *bonding_namespace(struct class *cls, + const struct class_attribute *attr) +{ + const struct bond_net *bn = + container_of(attr, struct bond_net, class_attr_bonding_masters); + return bn->net; +} + /* class attribute for bond_masters file. This ends up in /sys/class/net */ -static CLASS_ATTR(bonding_masters, S_IWUSR | S_IRUGO, - bonding_show_bonds, bonding_store_bonds); +static const struct class_attribute class_attr_bonding_masters = { + .attr = { + .name = "bonding_masters", + .mode = S_IWUSR | S_IRUGO, + }, + .show = bonding_show_bonds, + .store = bonding_store_bonds, + .namespace = bonding_namespace, +}; int bond_create_slave_symlinks(struct net_device *master, struct net_device *slave) @@ -169,6 +184,11 @@ int bond_create_slave_symlinks(struct net_device *master, sprintf(linkname, "slave_%s", slave->name); ret = sysfs_create_link(&(master->dev.kobj), &(slave->dev.kobj), linkname); + + /* free the master link created earlier in case of error */ + if (ret) + sysfs_remove_link(&(slave->dev.kobj), "master"); + return ret; } @@ -304,6 +324,13 @@ static ssize_t bonding_store_mode(struct device *d, goto out; } + if (bond->slave_cnt > 0) { + 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", @@ -492,6 +519,8 @@ static ssize_t bonding_store_arp_interval(struct device *d, int new_value, ret = count; struct bonding *bond = to_bond(d); + if (!rtnl_trylock()) + return restart_syscall(); if (sscanf(buf, "%d", &new_value) != 1) { pr_err("%s: no arp_interval value specified.\n", bond->dev->name); @@ -499,14 +528,15 @@ static ssize_t bonding_store_arp_interval(struct device *d, goto out; } if (new_value < 0) { - pr_err("%s: Invalid arp_interval value %d not in range 1-%d; rejected.\n", + pr_err("%s: Invalid arp_interval value %d not in range 0-%d; rejected.\n", bond->dev->name, new_value, INT_MAX); ret = -EINVAL; goto out; } if (bond->params.mode == BOND_MODE_ALB || - bond->params.mode == BOND_MODE_TLB) { - pr_info("%s: ARP monitoring cannot be used with ALB/TLB. Only MII monitoring is supported on %s.\n", + bond->params.mode == BOND_MODE_TLB || + bond->params.mode == BOND_MODE_8023AD) { + pr_info("%s: ARP monitoring cannot be used with ALB/TLB/802.3ad. Only MII monitoring is supported on %s.\n", bond->dev->name, bond->dev->name); ret = -EINVAL; goto out; @@ -514,18 +544,15 @@ static ssize_t bonding_store_arp_interval(struct device *d, pr_info("%s: Setting ARP monitoring interval to %d.\n", bond->dev->name, new_value); bond->params.arp_interval = new_value; - if (bond->params.miimon) { - pr_info("%s: ARP monitoring cannot be used with MII monitoring. %s Disabling MII monitoring.\n", - bond->dev->name, bond->dev->name); - bond->params.miimon = 0; - if (delayed_work_pending(&bond->mii_work)) { - cancel_delayed_work(&bond->mii_work); - flush_workqueue(bond->wq); + if (new_value) { + if (bond->params.miimon) { + pr_info("%s: ARP monitoring cannot be used with MII monitoring. %s Disabling MII monitoring.\n", + bond->dev->name, bond->dev->name); + bond->params.miimon = 0; } - } - if (!bond->params.arp_targets[0]) { - pr_info("%s: ARP monitoring has been set up, but no ARP targets have been specified.\n", - bond->dev->name); + if (!bond->params.arp_targets[0]) + pr_info("%s: ARP monitoring has been set up, but no ARP targets have been specified.\n", + bond->dev->name); } if (bond->dev->flags & IFF_UP) { /* If the interface is up, we may need to fire off @@ -533,19 +560,15 @@ static ssize_t bonding_store_arp_interval(struct device *d, * timer will get fired off when the open function * is called. */ - if (!delayed_work_pending(&bond->arp_work)) { - if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) - INIT_DELAYED_WORK(&bond->arp_work, - bond_activebackup_arp_mon); - else - INIT_DELAYED_WORK(&bond->arp_work, - bond_loadbalance_arp_mon); - + if (!new_value) { + cancel_delayed_work_sync(&bond->arp_work); + } else { + cancel_delayed_work_sync(&bond->mii_work); queue_delayed_work(bond->wq, &bond->arp_work, 0); } } - out: + rtnl_unlock(); return ret; } static DEVICE_ATTR(arp_interval, S_IRUGO | S_IWUSR, @@ -671,6 +694,8 @@ static ssize_t bonding_store_downdelay(struct device *d, int new_value, ret = count; struct bonding *bond = to_bond(d); + if (!rtnl_trylock()) + return restart_syscall(); if (!(bond->params.miimon)) { pr_err("%s: Unable to set down delay as MII monitoring is disabled\n", bond->dev->name); @@ -685,7 +710,7 @@ static ssize_t bonding_store_downdelay(struct device *d, } if (new_value < 0) { pr_err("%s: Invalid down delay value %d not in range %d-%d; rejected.\n", - bond->dev->name, new_value, 1, INT_MAX); + bond->dev->name, new_value, 0, INT_MAX); ret = -EINVAL; goto out; } else { @@ -704,6 +729,7 @@ static ssize_t bonding_store_downdelay(struct device *d, } out: + rtnl_unlock(); return ret; } static DEVICE_ATTR(downdelay, S_IRUGO | S_IWUSR, @@ -726,6 +752,8 @@ static ssize_t bonding_store_updelay(struct device *d, int new_value, ret = count; struct bonding *bond = to_bond(d); + if (!rtnl_trylock()) + return restart_syscall(); if (!(bond->params.miimon)) { pr_err("%s: Unable to set up delay as MII monitoring is disabled\n", bond->dev->name); @@ -740,8 +768,8 @@ static ssize_t bonding_store_updelay(struct device *d, goto out; } if (new_value < 0) { - pr_err("%s: Invalid down delay value %d not in range %d-%d; rejected.\n", - bond->dev->name, new_value, 1, INT_MAX); + pr_err("%s: Invalid up delay value %d not in range %d-%d; rejected.\n", + bond->dev->name, new_value, 0, INT_MAX); ret = -EINVAL; goto out; } else { @@ -759,6 +787,7 @@ static ssize_t bonding_store_updelay(struct device *d, } out: + rtnl_unlock(); return ret; } static DEVICE_ATTR(updelay, S_IRUGO | S_IWUSR, @@ -804,6 +833,7 @@ static ssize_t bonding_store_lacp(struct device *d, if ((new_value == 1) || (new_value == 0)) { bond->params.lacp_fast = new_value; + bond_3ad_update_lacp_rate(bond); pr_info("%s: Setting LACP rate to %s (%d).\n", bond->dev->name, bond_lacp_tbl[new_value].modename, new_value); @@ -818,6 +848,38 @@ out: static DEVICE_ATTR(lacp_rate, S_IRUGO | S_IWUSR, bonding_show_lacp, bonding_store_lacp); +static ssize_t bonding_show_min_links(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct bonding *bond = to_bond(d); + + return sprintf(buf, "%d\n", bond->params.min_links); +} + +static ssize_t bonding_store_min_links(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bonding *bond = to_bond(d); + int ret; + unsigned int new_value; + + ret = kstrtouint(buf, 0, &new_value); + if (ret < 0) { + pr_err("%s: Ignoring invalid min links value %s.\n", + bond->dev->name, buf); + return ret; + } + + pr_info("%s: Setting min links value to %u\n", + bond->dev->name, new_value); + bond->params.min_links = new_value; + return count; +} +static DEVICE_ATTR(min_links, S_IRUGO | S_IWUSR, + bonding_show_min_links, bonding_store_min_links); + static ssize_t bonding_show_ad_select(struct device *d, struct device_attribute *attr, char *buf) @@ -908,6 +970,8 @@ static ssize_t bonding_store_miimon(struct device *d, int new_value, ret = count; struct bonding *bond = to_bond(d); + if (!rtnl_trylock()) + return restart_syscall(); if (sscanf(buf, "%d", &new_value) != 1) { pr_err("%s: no miimon value specified.\n", bond->dev->name); @@ -916,50 +980,43 @@ static ssize_t bonding_store_miimon(struct device *d, } if (new_value < 0) { pr_err("%s: Invalid miimon value %d not in range %d-%d; rejected.\n", - bond->dev->name, new_value, 1, INT_MAX); + bond->dev->name, new_value, 0, INT_MAX); ret = -EINVAL; goto out; - } else { - pr_info("%s: Setting MII monitoring interval to %d.\n", - bond->dev->name, new_value); - bond->params.miimon = new_value; - if (bond->params.updelay) - pr_info("%s: Note: Updating updelay (to %d) since it is a multiple of the miimon value.\n", - bond->dev->name, - bond->params.updelay * bond->params.miimon); - if (bond->params.downdelay) - pr_info("%s: Note: Updating downdelay (to %d) since it is a multiple of the miimon value.\n", - bond->dev->name, - bond->params.downdelay * bond->params.miimon); - if (bond->params.arp_interval) { - pr_info("%s: MII monitoring cannot be used with ARP monitoring. Disabling ARP monitoring...\n", - bond->dev->name); - bond->params.arp_interval = 0; - if (bond->params.arp_validate) { - bond->params.arp_validate = - BOND_ARP_VALIDATE_NONE; - } - if (delayed_work_pending(&bond->arp_work)) { - cancel_delayed_work(&bond->arp_work); - flush_workqueue(bond->wq); - } - } - - if (bond->dev->flags & IFF_UP) { - /* If the interface is up, we may need to fire off - * the MII timer. If the interface is down, the - * timer will get fired off when the open function - * is called. - */ - if (!delayed_work_pending(&bond->mii_work)) { - INIT_DELAYED_WORK(&bond->mii_work, - bond_mii_monitor); - queue_delayed_work(bond->wq, - &bond->mii_work, 0); - } + } + pr_info("%s: Setting MII monitoring interval to %d.\n", + bond->dev->name, new_value); + bond->params.miimon = new_value; + if (bond->params.updelay) + pr_info("%s: Note: Updating updelay (to %d) since it is a multiple of the miimon value.\n", + bond->dev->name, + bond->params.updelay * bond->params.miimon); + if (bond->params.downdelay) + pr_info("%s: Note: Updating downdelay (to %d) since it is a multiple of the miimon value.\n", + bond->dev->name, + bond->params.downdelay * bond->params.miimon); + if (new_value && bond->params.arp_interval) { + pr_info("%s: MII monitoring cannot be used with ARP monitoring. Disabling ARP monitoring...\n", + bond->dev->name); + bond->params.arp_interval = 0; + if (bond->params.arp_validate) + bond->params.arp_validate = BOND_ARP_VALIDATE_NONE; + } + if (bond->dev->flags & IFF_UP) { + /* If the interface is up, we may need to fire off + * the MII timer. If the interface is down, the + * timer will get fired off when the open function + * is called. + */ + if (!new_value) { + cancel_delayed_work_sync(&bond->mii_work); + } else { + cancel_delayed_work_sync(&bond->arp_work); + queue_delayed_work(bond->wq, &bond->mii_work, 0); } } out: + rtnl_unlock(); return ret; } static DEVICE_ATTR(miimon, S_IRUGO | S_IWUSR, @@ -1611,6 +1668,7 @@ static struct attribute *per_bond_attrs[] = { &dev_attr_queue_id.attr, &dev_attr_all_slaves_active.attr, &dev_attr_resend_igmp.attr, + &dev_attr_min_links.attr, NULL, }; @@ -1623,11 +1681,14 @@ static struct attribute_group bonding_group = { * Initialize sysfs. This sets up the bonding_masters file in * /sys/class/net. */ -int bond_create_sysfs(void) +int bond_create_sysfs(struct bond_net *bn) { int ret; - ret = netdev_class_create_file(&class_attr_bonding_masters); + bn->class_attr_bonding_masters = class_attr_bonding_masters; + sysfs_attr_init(&bn->class_attr_bonding_masters.attr); + + ret = netdev_class_create_file(&bn->class_attr_bonding_masters); /* * Permit multiple loads of the module by ignoring failures to * create the bonding_masters sysfs file. Bonding devices @@ -1641,7 +1702,7 @@ int bond_create_sysfs(void) */ if (ret == -EEXIST) { /* Is someone being kinky and naming a device bonding_master? */ - if (__dev_get_by_name(&init_net, + if (__dev_get_by_name(bn->net, class_attr_bonding_masters.attr.name)) pr_err("network device named %s already exists in sysfs", class_attr_bonding_masters.attr.name); @@ -1655,9 +1716,9 @@ int bond_create_sysfs(void) /* * Remove /sys/class/net/bonding_masters. */ -void bond_destroy_sysfs(void) +void bond_destroy_sysfs(struct bond_net *bn) { - netdev_class_remove_file(&class_attr_bonding_masters); + netdev_class_remove_file(&bn->class_attr_bonding_masters); } /* |