diff options
author | Nikolay Aleksandrov <nikolay@redhat.com> | 2014-09-09 17:17:00 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-09-09 20:31:35 -0400 |
commit | 059b47e8aaf997245bc531e980581de492315fe6 (patch) | |
tree | e6f179bf980f970af3e53659b491828570f6616d /drivers/net/bonding | |
parent | ecfede424e95b211050f777c3ae96356926ed1c4 (diff) |
bonding: convert primary_slave to use RCU
This is necessary mainly for two bonding call sites: procfs and
sysfs as it was dereferenced without any real protection.
Signed-off-by: Nikolay Aleksandrov <nikolay@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/bonding')
-rw-r--r-- | drivers/net/bonding/bond_main.c | 42 | ||||
-rw-r--r-- | drivers/net/bonding/bond_netlink.c | 7 | ||||
-rw-r--r-- | drivers/net/bonding/bond_options.c | 8 | ||||
-rw-r--r-- | drivers/net/bonding/bond_procfs.c | 8 | ||||
-rw-r--r-- | drivers/net/bonding/bond_sysfs.c | 10 | ||||
-rw-r--r-- | drivers/net/bonding/bonding.h | 2 |
6 files changed, 43 insertions, 34 deletions
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index dcd331bd0c17..629037f79213 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c | |||
@@ -708,7 +708,7 @@ out: | |||
708 | 708 | ||
709 | static bool bond_should_change_active(struct bonding *bond) | 709 | static bool bond_should_change_active(struct bonding *bond) |
710 | { | 710 | { |
711 | struct slave *prim = bond->primary_slave; | 711 | struct slave *prim = rtnl_dereference(bond->primary_slave); |
712 | struct slave *curr = bond_deref_active_protected(bond); | 712 | struct slave *curr = bond_deref_active_protected(bond); |
713 | 713 | ||
714 | if (!prim || !curr || curr->link != BOND_LINK_UP) | 714 | if (!prim || !curr || curr->link != BOND_LINK_UP) |
@@ -732,13 +732,14 @@ static bool bond_should_change_active(struct bonding *bond) | |||
732 | */ | 732 | */ |
733 | static struct slave *bond_find_best_slave(struct bonding *bond) | 733 | static struct slave *bond_find_best_slave(struct bonding *bond) |
734 | { | 734 | { |
735 | struct slave *slave, *bestslave = NULL; | 735 | struct slave *slave, *bestslave = NULL, *primary; |
736 | struct list_head *iter; | 736 | struct list_head *iter; |
737 | int mintime = bond->params.updelay; | 737 | int mintime = bond->params.updelay; |
738 | 738 | ||
739 | if (bond->primary_slave && bond->primary_slave->link == BOND_LINK_UP && | 739 | primary = rtnl_dereference(bond->primary_slave); |
740 | if (primary && primary->link == BOND_LINK_UP && | ||
740 | bond_should_change_active(bond)) | 741 | bond_should_change_active(bond)) |
741 | return bond->primary_slave; | 742 | return primary; |
742 | 743 | ||
743 | bond_for_each_slave(bond, slave, iter) { | 744 | bond_for_each_slave(bond, slave, iter) { |
744 | if (slave->link == BOND_LINK_UP) | 745 | if (slave->link == BOND_LINK_UP) |
@@ -1482,7 +1483,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) | |||
1482 | if (bond_uses_primary(bond) && bond->params.primary[0]) { | 1483 | if (bond_uses_primary(bond) && bond->params.primary[0]) { |
1483 | /* if there is a primary slave, remember it */ | 1484 | /* if there is a primary slave, remember it */ |
1484 | if (strcmp(bond->params.primary, new_slave->dev->name) == 0) { | 1485 | if (strcmp(bond->params.primary, new_slave->dev->name) == 0) { |
1485 | bond->primary_slave = new_slave; | 1486 | rcu_assign_pointer(bond->primary_slave, new_slave); |
1486 | bond->force_primary = true; | 1487 | bond->force_primary = true; |
1487 | } | 1488 | } |
1488 | } | 1489 | } |
@@ -1596,8 +1597,8 @@ err_detach: | |||
1596 | bond_hw_addr_flush(bond_dev, slave_dev); | 1597 | bond_hw_addr_flush(bond_dev, slave_dev); |
1597 | 1598 | ||
1598 | vlan_vids_del_by_dev(slave_dev, bond_dev); | 1599 | vlan_vids_del_by_dev(slave_dev, bond_dev); |
1599 | if (bond->primary_slave == new_slave) | 1600 | if (rcu_access_pointer(bond->primary_slave) == new_slave) |
1600 | bond->primary_slave = NULL; | 1601 | RCU_INIT_POINTER(bond->primary_slave, NULL); |
1601 | if (rcu_access_pointer(bond->curr_active_slave) == new_slave) { | 1602 | if (rcu_access_pointer(bond->curr_active_slave) == new_slave) { |
1602 | block_netpoll_tx(); | 1603 | block_netpoll_tx(); |
1603 | write_lock_bh(&bond->curr_slave_lock); | 1604 | write_lock_bh(&bond->curr_slave_lock); |
@@ -1606,6 +1607,8 @@ err_detach: | |||
1606 | write_unlock_bh(&bond->curr_slave_lock); | 1607 | write_unlock_bh(&bond->curr_slave_lock); |
1607 | unblock_netpoll_tx(); | 1608 | unblock_netpoll_tx(); |
1608 | } | 1609 | } |
1610 | /* either primary_slave or curr_active_slave might've changed */ | ||
1611 | synchronize_rcu(); | ||
1609 | slave_disable_netpoll(new_slave); | 1612 | slave_disable_netpoll(new_slave); |
1610 | 1613 | ||
1611 | err_close: | 1614 | err_close: |
@@ -1714,8 +1717,8 @@ static int __bond_release_one(struct net_device *bond_dev, | |||
1714 | bond_dev->name, slave_dev->name); | 1717 | bond_dev->name, slave_dev->name); |
1715 | } | 1718 | } |
1716 | 1719 | ||
1717 | if (bond->primary_slave == slave) | 1720 | if (rtnl_dereference(bond->primary_slave) == slave) |
1718 | bond->primary_slave = NULL; | 1721 | RCU_INIT_POINTER(bond->primary_slave, NULL); |
1719 | 1722 | ||
1720 | if (oldcurrent == slave) { | 1723 | if (oldcurrent == slave) { |
1721 | write_lock_bh(&bond->curr_slave_lock); | 1724 | write_lock_bh(&bond->curr_slave_lock); |
@@ -1976,7 +1979,7 @@ static int bond_miimon_inspect(struct bonding *bond) | |||
1976 | static void bond_miimon_commit(struct bonding *bond) | 1979 | static void bond_miimon_commit(struct bonding *bond) |
1977 | { | 1980 | { |
1978 | struct list_head *iter; | 1981 | struct list_head *iter; |
1979 | struct slave *slave; | 1982 | struct slave *slave, *primary; |
1980 | 1983 | ||
1981 | bond_for_each_slave(bond, slave, iter) { | 1984 | bond_for_each_slave(bond, slave, iter) { |
1982 | switch (slave->new_link) { | 1985 | switch (slave->new_link) { |
@@ -1987,13 +1990,14 @@ static void bond_miimon_commit(struct bonding *bond) | |||
1987 | slave->link = BOND_LINK_UP; | 1990 | slave->link = BOND_LINK_UP; |
1988 | slave->last_link_up = jiffies; | 1991 | slave->last_link_up = jiffies; |
1989 | 1992 | ||
1993 | primary = rtnl_dereference(bond->primary_slave); | ||
1990 | if (BOND_MODE(bond) == BOND_MODE_8023AD) { | 1994 | if (BOND_MODE(bond) == BOND_MODE_8023AD) { |
1991 | /* prevent it from being the active one */ | 1995 | /* prevent it from being the active one */ |
1992 | bond_set_backup_slave(slave); | 1996 | bond_set_backup_slave(slave); |
1993 | } else if (BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) { | 1997 | } else if (BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) { |
1994 | /* make it immediately active */ | 1998 | /* make it immediately active */ |
1995 | bond_set_active_slave(slave); | 1999 | bond_set_active_slave(slave); |
1996 | } else if (slave != bond->primary_slave) { | 2000 | } else if (slave != primary) { |
1997 | /* prevent it from being the active one */ | 2001 | /* prevent it from being the active one */ |
1998 | bond_set_backup_slave(slave); | 2002 | bond_set_backup_slave(slave); |
1999 | } | 2003 | } |
@@ -2011,8 +2015,7 @@ static void bond_miimon_commit(struct bonding *bond) | |||
2011 | bond_alb_handle_link_change(bond, slave, | 2015 | bond_alb_handle_link_change(bond, slave, |
2012 | BOND_LINK_UP); | 2016 | BOND_LINK_UP); |
2013 | 2017 | ||
2014 | if (!bond->curr_active_slave || | 2018 | if (!bond->curr_active_slave || slave == primary) |
2015 | (slave == bond->primary_slave)) | ||
2016 | goto do_failover; | 2019 | goto do_failover; |
2017 | 2020 | ||
2018 | continue; | 2021 | continue; |
@@ -2633,7 +2636,7 @@ static void bond_ab_arp_commit(struct bonding *bond) | |||
2633 | slave->dev->name); | 2636 | slave->dev->name); |
2634 | 2637 | ||
2635 | if (!rtnl_dereference(bond->curr_active_slave) || | 2638 | if (!rtnl_dereference(bond->curr_active_slave) || |
2636 | (slave == bond->primary_slave)) | 2639 | slave == rtnl_dereference(bond->primary_slave)) |
2637 | goto do_failover; | 2640 | goto do_failover; |
2638 | 2641 | ||
2639 | } | 2642 | } |
@@ -2860,7 +2863,7 @@ static int bond_master_netdev_event(unsigned long event, | |||
2860 | static int bond_slave_netdev_event(unsigned long event, | 2863 | static int bond_slave_netdev_event(unsigned long event, |
2861 | struct net_device *slave_dev) | 2864 | struct net_device *slave_dev) |
2862 | { | 2865 | { |
2863 | struct slave *slave = bond_slave_get_rtnl(slave_dev); | 2866 | struct slave *slave = bond_slave_get_rtnl(slave_dev), *primary; |
2864 | struct bonding *bond; | 2867 | struct bonding *bond; |
2865 | struct net_device *bond_dev; | 2868 | struct net_device *bond_dev; |
2866 | u32 old_speed; | 2869 | u32 old_speed; |
@@ -2874,6 +2877,7 @@ static int bond_slave_netdev_event(unsigned long event, | |||
2874 | return NOTIFY_DONE; | 2877 | return NOTIFY_DONE; |
2875 | bond_dev = slave->bond->dev; | 2878 | bond_dev = slave->bond->dev; |
2876 | bond = slave->bond; | 2879 | bond = slave->bond; |
2880 | primary = rtnl_dereference(bond->primary_slave); | ||
2877 | 2881 | ||
2878 | switch (event) { | 2882 | switch (event) { |
2879 | case NETDEV_UNREGISTER: | 2883 | case NETDEV_UNREGISTER: |
@@ -2921,18 +2925,18 @@ static int bond_slave_netdev_event(unsigned long event, | |||
2921 | !bond->params.primary[0]) | 2925 | !bond->params.primary[0]) |
2922 | break; | 2926 | break; |
2923 | 2927 | ||
2924 | if (slave == bond->primary_slave) { | 2928 | if (slave == primary) { |
2925 | /* slave's name changed - he's no longer primary */ | 2929 | /* slave's name changed - he's no longer primary */ |
2926 | bond->primary_slave = NULL; | 2930 | RCU_INIT_POINTER(bond->primary_slave, NULL); |
2927 | } else if (!strcmp(slave_dev->name, bond->params.primary)) { | 2931 | } else if (!strcmp(slave_dev->name, bond->params.primary)) { |
2928 | /* we have a new primary slave */ | 2932 | /* we have a new primary slave */ |
2929 | bond->primary_slave = slave; | 2933 | rcu_assign_pointer(bond->primary_slave, slave); |
2930 | } else { /* we didn't change primary - exit */ | 2934 | } else { /* we didn't change primary - exit */ |
2931 | break; | 2935 | break; |
2932 | } | 2936 | } |
2933 | 2937 | ||
2934 | netdev_info(bond->dev, "Primary slave changed to %s, reselecting active slave\n", | 2938 | netdev_info(bond->dev, "Primary slave changed to %s, reselecting active slave\n", |
2935 | bond->primary_slave ? slave_dev->name : "none"); | 2939 | primary ? slave_dev->name : "none"); |
2936 | 2940 | ||
2937 | block_netpoll_tx(); | 2941 | block_netpoll_tx(); |
2938 | write_lock_bh(&bond->curr_slave_lock); | 2942 | write_lock_bh(&bond->curr_slave_lock); |
diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index e1489d9df2a4..c13d83e15ace 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c | |||
@@ -443,6 +443,7 @@ static int bond_fill_info(struct sk_buff *skb, | |||
443 | unsigned int packets_per_slave; | 443 | unsigned int packets_per_slave; |
444 | int ifindex, i, targets_added; | 444 | int ifindex, i, targets_added; |
445 | struct nlattr *targets; | 445 | struct nlattr *targets; |
446 | struct slave *primary; | ||
446 | 447 | ||
447 | if (nla_put_u8(skb, IFLA_BOND_MODE, BOND_MODE(bond))) | 448 | if (nla_put_u8(skb, IFLA_BOND_MODE, BOND_MODE(bond))) |
448 | goto nla_put_failure; | 449 | goto nla_put_failure; |
@@ -492,9 +493,9 @@ static int bond_fill_info(struct sk_buff *skb, | |||
492 | bond->params.arp_all_targets)) | 493 | bond->params.arp_all_targets)) |
493 | goto nla_put_failure; | 494 | goto nla_put_failure; |
494 | 495 | ||
495 | if (bond->primary_slave && | 496 | primary = rtnl_dereference(bond->primary_slave); |
496 | nla_put_u32(skb, IFLA_BOND_PRIMARY, | 497 | if (primary && |
497 | bond->primary_slave->dev->ifindex)) | 498 | nla_put_u32(skb, IFLA_BOND_PRIMARY, primary->dev->ifindex)) |
498 | goto nla_put_failure; | 499 | goto nla_put_failure; |
499 | 500 | ||
500 | if (nla_put_u8(skb, IFLA_BOND_PRIMARY_RESELECT, | 501 | if (nla_put_u8(skb, IFLA_BOND_PRIMARY_RESELECT, |
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index d8dc17faa6b4..7c9e176baecc 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c | |||
@@ -1090,7 +1090,7 @@ static int bond_option_primary_set(struct bonding *bond, | |||
1090 | /* check to see if we are clearing primary */ | 1090 | /* check to see if we are clearing primary */ |
1091 | if (!strlen(primary)) { | 1091 | if (!strlen(primary)) { |
1092 | netdev_info(bond->dev, "Setting primary slave to None\n"); | 1092 | netdev_info(bond->dev, "Setting primary slave to None\n"); |
1093 | bond->primary_slave = NULL; | 1093 | RCU_INIT_POINTER(bond->primary_slave, NULL); |
1094 | memset(bond->params.primary, 0, sizeof(bond->params.primary)); | 1094 | memset(bond->params.primary, 0, sizeof(bond->params.primary)); |
1095 | bond_select_active_slave(bond); | 1095 | bond_select_active_slave(bond); |
1096 | goto out; | 1096 | goto out; |
@@ -1100,16 +1100,16 @@ static int bond_option_primary_set(struct bonding *bond, | |||
1100 | if (strncmp(slave->dev->name, primary, IFNAMSIZ) == 0) { | 1100 | if (strncmp(slave->dev->name, primary, IFNAMSIZ) == 0) { |
1101 | netdev_info(bond->dev, "Setting %s as primary slave\n", | 1101 | netdev_info(bond->dev, "Setting %s as primary slave\n", |
1102 | slave->dev->name); | 1102 | slave->dev->name); |
1103 | bond->primary_slave = slave; | 1103 | rcu_assign_pointer(bond->primary_slave, slave); |
1104 | strcpy(bond->params.primary, slave->dev->name); | 1104 | strcpy(bond->params.primary, slave->dev->name); |
1105 | bond_select_active_slave(bond); | 1105 | bond_select_active_slave(bond); |
1106 | goto out; | 1106 | goto out; |
1107 | } | 1107 | } |
1108 | } | 1108 | } |
1109 | 1109 | ||
1110 | if (bond->primary_slave) { | 1110 | if (rtnl_dereference(bond->primary_slave)) { |
1111 | netdev_info(bond->dev, "Setting primary slave to None\n"); | 1111 | netdev_info(bond->dev, "Setting primary slave to None\n"); |
1112 | bond->primary_slave = NULL; | 1112 | RCU_INIT_POINTER(bond->primary_slave, NULL); |
1113 | bond_select_active_slave(bond); | 1113 | bond_select_active_slave(bond); |
1114 | } | 1114 | } |
1115 | strncpy(bond->params.primary, primary, IFNAMSIZ); | 1115 | strncpy(bond->params.primary, primary, IFNAMSIZ); |
diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c index de62c0385dfb..1a9fe1ba4c60 100644 --- a/drivers/net/bonding/bond_procfs.c +++ b/drivers/net/bonding/bond_procfs.c | |||
@@ -66,7 +66,7 @@ static void bond_info_show_master(struct seq_file *seq) | |||
66 | { | 66 | { |
67 | struct bonding *bond = seq->private; | 67 | struct bonding *bond = seq->private; |
68 | const struct bond_opt_value *optval; | 68 | const struct bond_opt_value *optval; |
69 | struct slave *curr; | 69 | struct slave *curr, *primary; |
70 | int i; | 70 | int i; |
71 | 71 | ||
72 | curr = rcu_dereference(bond->curr_active_slave); | 72 | curr = rcu_dereference(bond->curr_active_slave); |
@@ -92,10 +92,10 @@ static void bond_info_show_master(struct seq_file *seq) | |||
92 | } | 92 | } |
93 | 93 | ||
94 | if (bond_uses_primary(bond)) { | 94 | if (bond_uses_primary(bond)) { |
95 | primary = rcu_dereference(bond->primary_slave); | ||
95 | seq_printf(seq, "Primary Slave: %s", | 96 | seq_printf(seq, "Primary Slave: %s", |
96 | (bond->primary_slave) ? | 97 | primary ? primary->dev->name : "None"); |
97 | bond->primary_slave->dev->name : "None"); | 98 | if (primary) { |
98 | if (bond->primary_slave) { | ||
99 | optval = bond_opt_get_val(BOND_OPT_PRIMARY_RESELECT, | 99 | optval = bond_opt_get_val(BOND_OPT_PRIMARY_RESELECT, |
100 | bond->params.primary_reselect); | 100 | bond->params.primary_reselect); |
101 | seq_printf(seq, " (primary_reselect %s)", | 101 | seq_printf(seq, " (primary_reselect %s)", |
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index 98db8edd9c75..5555517284db 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c | |||
@@ -425,11 +425,15 @@ static ssize_t bonding_show_primary(struct device *d, | |||
425 | struct device_attribute *attr, | 425 | struct device_attribute *attr, |
426 | char *buf) | 426 | char *buf) |
427 | { | 427 | { |
428 | int count = 0; | ||
429 | struct bonding *bond = to_bond(d); | 428 | struct bonding *bond = to_bond(d); |
429 | struct slave *primary; | ||
430 | int count = 0; | ||
430 | 431 | ||
431 | if (bond->primary_slave) | 432 | rcu_read_lock(); |
432 | count = sprintf(buf, "%s\n", bond->primary_slave->dev->name); | 433 | primary = rcu_dereference(bond->primary_slave); |
434 | if (primary) | ||
435 | count = sprintf(buf, "%s\n", primary->dev->name); | ||
436 | rcu_read_unlock(); | ||
433 | 437 | ||
434 | return count; | 438 | return count; |
435 | } | 439 | } |
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index aace510d08d1..c798561a6f01 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h | |||
@@ -195,7 +195,7 @@ struct bonding { | |||
195 | struct net_device *dev; /* first - useful for panic debug */ | 195 | struct net_device *dev; /* first - useful for panic debug */ |
196 | struct slave __rcu *curr_active_slave; | 196 | struct slave __rcu *curr_active_slave; |
197 | struct slave __rcu *current_arp_slave; | 197 | struct slave __rcu *current_arp_slave; |
198 | struct slave *primary_slave; | 198 | struct slave __rcu *primary_slave; |
199 | bool force_primary; | 199 | bool force_primary; |
200 | s32 slave_cnt; /* never change this value outside the attach/detach wrappers */ | 200 | s32 slave_cnt; /* never change this value outside the attach/detach wrappers */ |
201 | int (*recv_probe)(const struct sk_buff *, struct bonding *, | 201 | int (*recv_probe)(const struct sk_buff *, struct bonding *, |