]> Pileus Git - ~andy/linux/blobdiff - drivers/net/bonding/bond_sysfs.c
bonding: fix race condition in bonding_store_slaves_active
[~andy/linux] / drivers / net / bonding / bond_sysfs.c
index dc15d248443fd833ae17199a41f28ab6712b5d5c..1877ed7ca0864ed4adfb614de862ed40dde730bd 100644 (file)
@@ -513,6 +513,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);
@@ -539,10 +541,6 @@ static ssize_t bonding_store_arp_interval(struct device *d,
                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 (!bond->params.arp_targets[0]) {
                pr_info("%s: ARP monitoring has been set up, but no ARP targets have been specified.\n",
@@ -554,19 +552,12 @@ 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);
-
-                       queue_delayed_work(bond->wq, &bond->arp_work, 0);
-               }
+               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,
@@ -962,6 +953,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);
@@ -993,10 +986,6 @@ static ssize_t bonding_store_miimon(struct device *d,
                                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) {
@@ -1005,15 +994,12 @@ static ssize_t bonding_store_miimon(struct device *d,
                         * 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);
-                       }
+                       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,
@@ -1060,7 +1046,7 @@ static ssize_t bonding_store_primary(struct device *d,
                goto out;
        }
 
-       sscanf(buf, "%16s", ifname); /* IFNAMSIZ */
+       sscanf(buf, "%15s", ifname); /* IFNAMSIZ */
 
        /* check to see if we are clearing primary */
        if (!strlen(ifname) || buf[0] == '\n') {
@@ -1237,7 +1223,7 @@ static ssize_t bonding_store_active_slave(struct device *d,
                goto out;
        }
 
-       sscanf(buf, "%16s", ifname); /* IFNAMSIZ */
+       sscanf(buf, "%15s", ifname); /* IFNAMSIZ */
 
        /* check to see if we are clearing active */
        if (!strlen(ifname) || buf[0] == '\n') {
@@ -1582,6 +1568,7 @@ static ssize_t bonding_store_slaves_active(struct device *d,
                goto out;
        }
 
+       read_lock(&bond->lock);
        bond_for_each_slave(bond, slave, i) {
                if (!bond_is_active_slave(slave)) {
                        if (new_value)
@@ -1590,6 +1577,7 @@ static ssize_t bonding_store_slaves_active(struct device *d,
                                slave->inactive = 1;
                }
        }
+       read_unlock(&bond->lock);
 out:
        return ret;
 }