diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-25 23:17:34 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-25 23:17:34 +0400 |
commit | 4ba9920e5e9c0e16b5ed24292d45322907bb9035 (patch) | |
tree | 7d023baea59ed0886ded1f0b6d1c6385690b88f7 /drivers/net/bonding | |
parent | 82c477669a4665eb4e52030792051e0559ee2a36 (diff) | |
parent | 8b662fe70c68282f78482dc272df0c4f355e49f5 (diff) | |
download | linux-4ba9920e5e9c0e16b5ed24292d45322907bb9035.tar.xz |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller:
1) BPF debugger and asm tool by Daniel Borkmann.
2) Speed up create/bind in AF_PACKET, also from Daniel Borkmann.
3) Correct reciprocal_divide and update users, from Hannes Frederic
Sowa and Daniel Borkmann.
4) Currently we only have a "set" operation for the hw timestamp socket
ioctl, add a "get" operation to match. From Ben Hutchings.
5) Add better trace events for debugging driver datapath problems, also
from Ben Hutchings.
6) Implement auto corking in TCP, from Eric Dumazet. Basically, if we
have a small send and a previous packet is already in the qdisc or
device queue, defer until TX completion or we get more data.
7) Allow userspace to manage ipv6 temporary addresses, from Jiri Pirko.
8) Add a qdisc bypass option for AF_PACKET sockets, from Daniel
Borkmann.
9) Share IP header compression code between Bluetooth and IEEE802154
layers, from Jukka Rissanen.
10) Fix ipv6 router reachability probing, from Jiri Benc.
11) Allow packets to be captured on macvtap devices, from Vlad Yasevich.
12) Support tunneling in GRO layer, from Jerry Chu.
13) Allow bonding to be configured fully using netlink, from Scott
Feldman.
14) Allow AF_PACKET users to obtain the VLAN TPID, just like they can
already get the TCI. From Atzm Watanabe.
15) New "Heavy Hitter" qdisc, from Terry Lam.
16) Significantly improve the IPSEC support in pktgen, from Fan Du.
17) Allow ipv4 tunnels to cache routes, just like sockets. From Tom
Herbert.
18) Add Proportional Integral Enhanced packet scheduler, from Vijay
Subramanian.
19) Allow openvswitch to mmap'd netlink, from Thomas Graf.
20) Key TCP metrics blobs also by source address, not just destination
address. From Christoph Paasch.
21) Support 10G in generic phylib. From Andy Fleming.
22) Try to short-circuit GRO flow compares using device provided RX
hash, if provided. From Tom Herbert.
The wireless and netfilter folks have been busy little bees too.
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (2064 commits)
net/cxgb4: Fix referencing freed adapter
ipv6: reallocate addrconf router for ipv6 address when lo device up
fib_frontend: fix possible NULL pointer dereference
rtnetlink: remove IFLA_BOND_SLAVE definition
rtnetlink: remove check for fill_slave_info in rtnl_have_link_slave_info
qlcnic: update version to 5.3.55
qlcnic: Enhance logic to calculate msix vectors.
qlcnic: Refactor interrupt coalescing code for all adapters.
qlcnic: Update poll controller code path
qlcnic: Interrupt code cleanup
qlcnic: Enhance Tx timeout debugging.
qlcnic: Use bool for rx_mac_learn.
bonding: fix u64 division
rtnetlink: add missing IFLA_BOND_AD_INFO_UNSPEC
sfc: Use the correct maximum TX DMA ring size for SFC9100
Add Shradha Shah as the sfc driver maintainer.
net/vxlan: Share RX skb de-marking and checksum checks with ovs
tulip: cleanup by using ARRAY_SIZE()
ip_tunnel: clear IPCB in ip_tunnel_xmit() in case dst_link_failure() is called
net/cxgb4: Don't retrieve stats during recovery
...
Diffstat (limited to 'drivers/net/bonding')
-rw-r--r-- | drivers/net/bonding/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/bonding/bond_3ad.c | 900 | ||||
-rw-r--r-- | drivers/net/bonding/bond_3ad.h | 2 | ||||
-rw-r--r-- | drivers/net/bonding/bond_alb.c | 49 | ||||
-rw-r--r-- | drivers/net/bonding/bond_alb.h | 3 | ||||
-rw-r--r-- | drivers/net/bonding/bond_main.c | 569 | ||||
-rw-r--r-- | drivers/net/bonding/bond_netlink.c | 464 | ||||
-rw-r--r-- | drivers/net/bonding/bond_options.c | 1191 | ||||
-rw-r--r-- | drivers/net/bonding/bond_options.h | 170 | ||||
-rw-r--r-- | drivers/net/bonding/bond_procfs.c | 37 | ||||
-rw-r--r-- | drivers/net/bonding/bond_sysfs.c | 896 | ||||
-rw-r--r-- | drivers/net/bonding/bond_sysfs_slave.c | 144 | ||||
-rw-r--r-- | drivers/net/bonding/bonding.h | 38 |
13 files changed, 2868 insertions, 1597 deletions
diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile index 5a5d720da929..6f4e80853ed4 100644 --- a/drivers/net/bonding/Makefile +++ b/drivers/net/bonding/Makefile @@ -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 bond_netlink.o bond_options.o +bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o bond_sysfs_slave.o bond_debugfs.o bond_netlink.o bond_options.o proc-$(CONFIG_PROC_FS) += bond_procfs.o bonding-objs += $(proc-y) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 4ced59436558..cce1f1bf90b4 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -34,14 +34,14 @@ #include "bonding.h" #include "bond_3ad.h" -// General definitions +/* General definitions */ #define AD_SHORT_TIMEOUT 1 #define AD_LONG_TIMEOUT 0 #define AD_STANDBY 0x2 #define AD_MAX_TX_IN_SECOND 3 #define AD_COLLECTOR_MAX_DELAY 0 -// Timer definitions(43.4.4 in the 802.3ad standard) +/* Timer definitions (43.4.4 in the 802.3ad standard) */ #define AD_FAST_PERIODIC_TIME 1 #define AD_SLOW_PERIODIC_TIME 30 #define AD_SHORT_TIMEOUT_TIME (3*AD_FAST_PERIODIC_TIME) @@ -49,7 +49,7 @@ #define AD_CHURN_DETECTION_TIME 60 #define AD_AGGREGATE_WAIT_TIME 2 -// Port state definitions(43.4.2.2 in the 802.3ad standard) +/* Port state definitions (43.4.2.2 in the 802.3ad standard) */ #define AD_STATE_LACP_ACTIVITY 0x1 #define AD_STATE_LACP_TIMEOUT 0x2 #define AD_STATE_AGGREGATION 0x4 @@ -59,7 +59,9 @@ #define AD_STATE_DEFAULTED 0x40 #define AD_STATE_EXPIRED 0x80 -// Port Variables definitions used by the State Machines(43.4.7 in the 802.3ad standard) +/* Port Variables definitions used by the State Machines (43.4.7 in the + * 802.3ad standard) + */ #define AD_PORT_BEGIN 0x1 #define AD_PORT_LACP_ENABLED 0x2 #define AD_PORT_ACTOR_CHURN 0x4 @@ -71,27 +73,27 @@ #define AD_PORT_SELECTED 0x100 #define AD_PORT_MOVED 0x200 -// Port Key definitions -// key is determined according to the link speed, duplex and -// user key(which is yet not supported) -// ------------------------------------------------------------ -// Port key : | User key | Speed |Duplex| -// ------------------------------------------------------------ -// 16 6 1 0 +/* Port Key definitions + * key is determined according to the link speed, duplex and + * user key (which is yet not supported) + * -------------------------------------------------------------- + * Port key : | User key | Speed | Duplex | + * -------------------------------------------------------------- + * 16 6 1 0 + */ #define AD_DUPLEX_KEY_BITS 0x1 #define AD_SPEED_KEY_BITS 0x3E #define AD_USER_KEY_BITS 0xFFC0 -//dalloun #define AD_LINK_SPEED_BITMASK_1MBPS 0x1 #define AD_LINK_SPEED_BITMASK_10MBPS 0x2 #define AD_LINK_SPEED_BITMASK_100MBPS 0x4 #define AD_LINK_SPEED_BITMASK_1000MBPS 0x8 #define AD_LINK_SPEED_BITMASK_10000MBPS 0x10 -//endalloun -// compare MAC addresses -#define MAC_ADDRESS_COMPARE(A, B) memcmp(A, B, ETH_ALEN) +/* compare MAC addresses */ +#define MAC_ADDRESS_EQUAL(A, B) \ + ether_addr_equal_64bits((const u8 *)A, (const u8 *)B) static struct mac_addr null_mac_addr = { { 0, 0, 0, 0, 0, 0 } }; static u16 ad_ticks_per_sec; @@ -99,7 +101,7 @@ static const int ad_delta_in_ticks = (AD_TIMER_INTERVAL * HZ) / 1000; static const u8 lacpdu_mcast_addr[ETH_ALEN] = MULTICAST_LACPDU_ADDR; -// ================= main 802.3ad protocol functions ================== +/* ================= main 802.3ad protocol functions ================== */ static int ad_lacpdu_send(struct port *port); static int ad_marker_send(struct port *port, struct bond_marker *marker); static void ad_mux_machine(struct port *port); @@ -113,13 +115,13 @@ static void ad_initialize_agg(struct aggregator *aggregator); static void ad_initialize_port(struct port *port, int lacp_fast); static void ad_enable_collecting_distributing(struct port *port); static void ad_disable_collecting_distributing(struct port *port); -static void ad_marker_info_received(struct bond_marker *marker_info, struct port *port); -static void ad_marker_response_received(struct bond_marker *marker, struct port *port); +static void ad_marker_info_received(struct bond_marker *marker_info, + struct port *port); +static void ad_marker_response_received(struct bond_marker *marker, + struct port *port); -///////////////////////////////////////////////////////////////////////////////// -// ================= api to bonding and kernel code ================== -///////////////////////////////////////////////////////////////////////////////// +/* ================= api to bonding and kernel code ================== */ /** * __get_bond_by_port - get the port's bonding struct @@ -141,25 +143,32 @@ static inline struct bonding *__get_bond_by_port(struct port *port) * * Return the aggregator of the first slave in @bond, or %NULL if it can't be * found. + * The caller must hold RCU or RTNL lock. */ static inline struct aggregator *__get_first_agg(struct port *port) { struct bonding *bond = __get_bond_by_port(port); struct slave *first_slave; + struct aggregator *agg; - // If there's no bond for this port, or bond has no slaves + /* If there's no bond for this port, or bond has no slaves */ if (bond == NULL) return NULL; - first_slave = bond_first_slave(bond); - return first_slave ? &(SLAVE_AD_INFO(first_slave).aggregator) : NULL; + rcu_read_lock(); + first_slave = bond_first_slave_rcu(bond); + agg = first_slave ? &(SLAVE_AD_INFO(first_slave).aggregator) : NULL; + rcu_read_unlock(); + + return agg; } -/* - * __agg_has_partner +/** + * __agg_has_partner - see if we have a partner + * @agg: the agregator we're looking at * * Return nonzero if aggregator has a partner (denoted by a non-zero ether - * address for the partner). Return 0 if not. + * address for the partner). Return 0 if not. */ static inline int __agg_has_partner(struct aggregator *agg) { @@ -169,7 +178,6 @@ static inline int __agg_has_partner(struct aggregator *agg) /** * __disable_port - disable the port's slave * @port: the port we're looking at - * */ static inline void __disable_port(struct port *port) { @@ -179,7 +187,6 @@ static inline void __disable_port(struct port *port) /** * __enable_port - enable the port's slave, if it's up * @port: the port we're looking at - * */ static inline void __enable_port(struct port *port) { @@ -192,7 +199,6 @@ static inline void __enable_port(struct port *port) /** * __port_is_enabled - check if the port's slave is in active state * @port: the port we're looking at - * */ static inline int __port_is_enabled(struct port *port) { @@ -218,7 +224,6 @@ static inline u32 __get_agg_selection_mode(struct port *port) /** * __check_agg_selection_timer - check if the selection timer has expired * @port: the port we're looking at - * */ static inline int __check_agg_selection_timer(struct port *port) { @@ -233,7 +238,6 @@ static inline int __check_agg_selection_timer(struct port *port) /** * __get_state_machine_lock - lock the port's state machines * @port: the port we're looking at - * */ static inline void __get_state_machine_lock(struct port *port) { @@ -243,7 +247,6 @@ static inline void __get_state_machine_lock(struct port *port) /** * __release_state_machine_lock - unlock the port's state machines * @port: the port we're looking at - * */ static inline void __release_state_machine_lock(struct port *port) { @@ -266,10 +269,11 @@ static u16 __get_link_speed(struct port *port) struct slave *slave = port->slave; u16 speed; - /* this if covers only a special case: when the configuration starts with - * link down, it sets the speed to 0. - * This is done in spite of the fact that the e100 driver reports 0 to be - * compatible with MVT in the future.*/ + /* this if covers only a special case: when the configuration starts + * with link down, it sets the speed to 0. + * This is done in spite of the fact that the e100 driver reports 0 + * to be compatible with MVT in the future. + */ if (slave->link != BOND_LINK_UP) speed = 0; else { @@ -291,7 +295,8 @@ static u16 __get_link_speed(struct port *port) break; default: - speed = 0; // unknown speed value from ethtool. shouldn't happen + /* unknown speed value from ethtool. shouldn't happen */ + speed = 0; break; } } @@ -315,8 +320,9 @@ static u8 __get_duplex(struct port *port) u8 retval; - // handling a special case: when the configuration starts with - // link down, it sets the duplex to 0. + /* handling a special case: when the configuration starts with + * link down, it sets the duplex to 0. + */ if (slave->link != BOND_LINK_UP) retval = 0x0; else { @@ -340,15 +346,14 @@ static u8 __get_duplex(struct port *port) /** * __initialize_port_locks - initialize a port's STATE machine spinlock * @port: the slave of the port we're looking at - * */ static inline void __initialize_port_locks(struct slave *slave) { - // make sure it isn't called twice + /* make sure it isn't called twice */ spin_lock_init(&(SLAVE_AD_INFO(slave).state_machine_lock)); } -//conversions +/* Conversions */ /** * __ad_timer_to_ticks - convert a given timer type to AD module ticks @@ -357,39 +362,38 @@ static inline void __initialize_port_locks(struct slave *slave) * * If @timer_type is %current_while_timer, @par indicates long/short timer. * If @timer_type is %periodic_timer, @par is one of %FAST_PERIODIC_TIME, - * %SLOW_PERIODIC_TIME. + * %SLOW_PERIODIC_TIME. */ static u16 __ad_timer_to_ticks(u16 timer_type, u16 par) { u16 retval = 0; /* to silence the compiler */ switch (timer_type) { - case AD_CURRENT_WHILE_TIMER: // for rx machine usage + case AD_CURRENT_WHILE_TIMER: /* for rx machine usage */ if (par) - retval = (AD_SHORT_TIMEOUT_TIME*ad_ticks_per_sec); // short timeout + retval = (AD_SHORT_TIMEOUT_TIME*ad_ticks_per_sec); else - retval = (AD_LONG_TIMEOUT_TIME*ad_ticks_per_sec); // long timeout + retval = (AD_LONG_TIMEOUT_TIME*ad_ticks_per_sec); break; - case AD_ACTOR_CHURN_TIMER: // for local churn machine + case AD_ACTOR_CHURN_TIMER: /* for local churn machine */ retval = (AD_CHURN_DETECTION_TIME*ad_ticks_per_sec); break; - case AD_PERIODIC_TIMER: // for periodic machine - retval = (par*ad_ticks_per_sec); // long timeout + case AD_PERIODIC_TIMER: /* for periodic machine */ + retval = (par*ad_ticks_per_sec); /* long timeout */ break; - case AD_PARTNER_CHURN_TIMER: // for remote churn machine + case AD_PARTNER_CHURN_TIMER: /* for remote churn machine */ retval = (AD_CHURN_DETECTION_TIME*ad_ticks_per_sec); break; - case AD_WAIT_WHILE_TIMER: // for selection machine + case AD_WAIT_WHILE_TIMER: /* for selection machine */ retval = (AD_AGGREGATE_WAIT_TIME*ad_ticks_per_sec); break; } + return retval; } -///////////////////////////////////////////////////////////////////////////////// -// ================= ad_rx_machine helper functions ================== -///////////////////////////////////////////////////////////////////////////////// +/* ================= ad_rx_machine helper functions ================== */ /** * __choose_matched - update a port's matched variable from a received lacpdu @@ -416,17 +420,18 @@ static u16 __ad_timer_to_ticks(u16 timer_type, u16 par) */ static void __choose_matched(struct lacpdu *lacpdu, struct port *port) { - // check if all parameters are alike + /* check if all parameters are alike + * or this is individual link(aggregation == FALSE) + * then update the state machine Matched variable. + */ if (((ntohs(lacpdu->partner_port) == port->actor_port_number) && (ntohs(lacpdu->partner_port_priority) == port->actor_port_priority) && - !MAC_ADDRESS_COMPARE(&(lacpdu->partner_system), &(port->actor_system)) && + MAC_ADDRESS_EQUAL(&(lacpdu->partner_system), &(port->actor_system)) && (ntohs(lacpdu->partner_system_priority) == port->actor_system_priority) && (ntohs(lacpdu->partner_key) == port->actor_oper_port_key) && ((lacpdu->partner_state & AD_STATE_AGGREGATION) == (port->actor_oper_port_state & AD_STATE_AGGREGATION))) || - // or this is individual link(aggregation == FALSE) ((lacpdu->actor_state & AD_STATE_AGGREGATION) == 0) ) { - // update the state machine Matched variable port->sm_vars |= AD_PORT_MATCHED; } else { port->sm_vars &= ~AD_PORT_MATCHED; @@ -448,7 +453,9 @@ static void __record_pdu(struct lacpdu *lacpdu, struct port *port) struct port_params *partner = &port->partner_oper; __choose_matched(lacpdu, port); - // record the new parameter values for the partner operational + /* record the new parameter values for the partner + * operational + */ partner->port_number = ntohs(lacpdu->actor_port); partner->port_priority = ntohs(lacpdu->actor_port_priority); partner->system = lacpdu->actor_system; @@ -456,10 +463,12 @@ static void __record_pdu(struct lacpdu *lacpdu, struct port *port) partner->key = ntohs(lacpdu->actor_key); partner->port_state = lacpdu->actor_state; - // set actor_oper_port_state.defaulted to FALSE + /* set actor_oper_port_state.defaulted to FALSE */ port->actor_oper_port_state &= ~AD_STATE_DEFAULTED; - // set the partner sync. to on if the partner is sync. and the port is matched + /* set the partner sync. to on if the partner is sync, + * and the port is matched + */ if ((port->sm_vars & AD_PORT_MATCHED) && (lacpdu->actor_state & AD_STATE_SYNCHRONIZATION)) partner->port_state |= AD_STATE_SYNCHRONIZATION; @@ -479,11 +488,11 @@ static void __record_pdu(struct lacpdu *lacpdu, struct port *port) static void __record_default(struct port *port) { if (port) { - // record the partner admin parameters + /* record the partner admin parameters */ memcpy(&port->partner_oper, &port->partner_admin, sizeof(struct port_params)); - // set actor_oper_port_state.defaulted to true + /* set actor_oper_port_state.defaulted to true */ port->actor_oper_port_state |= AD_STATE_DEFAULTED; } } @@ -506,14 +515,15 @@ static void __update_selected(struct lacpdu *lacpdu, struct port *port) if (lacpdu && port) { const struct port_params *partner = &port->partner_oper; - // check if any parameter is different + /* check if any parameter is different then + * update the state machine selected variable. + */ if (ntohs(lacpdu->actor_port) != partner->port_number || ntohs(lacpdu->actor_port_priority) != partner->port_priority || - MAC_ADDRESS_COMPARE(&lacpdu->actor_system, &partner->system) || + !MAC_ADDRESS_EQUAL(&lacpdu->actor_system, &partner->system) || ntohs(lacpdu->actor_system_priority) != partner->system_priority || ntohs(lacpdu->actor_key) != partner->key || (lacpdu->actor_state & AD_STATE_AGGREGATION) != (partner->port_state & AD_STATE_AGGREGATION)) { - // update the state machine Selected variable port->sm_vars &= ~AD_PORT_SELECTED; } } @@ -537,15 +547,16 @@ static void __update_default_selected(struct port *port) const struct port_params *admin = &port->partner_admin; const struct port_params *oper = &port->partner_oper; - // check if any parameter is different + /* check if any parameter is different then + * update the state machine selected variable. + */ if (admin->port_number != oper->port_number || admin->port_priority != oper->port_priority || - MAC_ADDRESS_COMPARE(&admin->system, &oper->system) || + !MAC_ADDRESS_EQUAL(&admin->system, &oper->system) || admin->system_priority != oper->system_priority || admin->key != oper->key || (admin->port_state & AD_STATE_AGGREGATION) != (oper->port_state & AD_STATE_AGGREGATION)) { - // update the state machine Selected variable port->sm_vars &= ~AD_PORT_SELECTED; } } @@ -565,12 +576,14 @@ static void __update_default_selected(struct port *port) */ static void __update_ntt(struct lacpdu *lacpdu, struct port *port) { - // validate lacpdu and port + /* validate lacpdu and port */ if (lacpdu && port) { - // check if any parameter is different + /* check if any parameter is different then + * update the port->ntt. + */ if ((ntohs(lacpdu->partner_port) != port->actor_port_number) || (ntohs(lacpdu->partner_port_priority) != port->actor_port_priority) || - MAC_ADDRESS_COMPARE(&(lacpdu->partner_system), &(port->actor_system)) || + !MAC_ADDRESS_EQUAL(&(lacpdu->partner_system), &(port->actor_system)) || (ntohs(lacpdu->partner_system_priority) != port->actor_system_priority) || (ntohs(lacpdu->partner_key) != port->actor_oper_port_key) || ((lacpdu->partner_state & AD_STATE_LACP_ACTIVITY) != (port->actor_oper_port_state & AD_STATE_LACP_ACTIVITY)) || @@ -578,43 +591,12 @@ static void __update_ntt(struct lacpdu *lacpdu, struct port *port) ((lacpdu->partner_state & AD_STATE_SYNCHRONIZATION) != (port->actor_oper_port_state & AD_STATE_SYNCHRONIZATION)) || ((lacpdu->partner_state & AD_STATE_AGGREGATION) != (port->actor_oper_port_state & AD_STATE_AGGREGATION)) ) { - port->ntt = true; } } } /** - * __attach_bond_to_agg - * @port: the port we're looking at - * - * Handle the attaching of the port's control parser/multiplexer and the - * aggregator. This function does nothing since the parser/multiplexer of the - * receive and the parser/multiplexer of the aggregator are already combined. - */ -static void __attach_bond_to_agg(struct port *port) -{ - port = NULL; /* just to satisfy the compiler */ - // This function does nothing since the parser/multiplexer of the receive - // and the parser/multiplexer of the aggregator are already combined -} - -/** - * __detach_bond_from_agg - * @port: the port we're looking at - * - * Handle the detaching of the port's control parser/multiplexer from the - * aggregator. This function does nothing since the parser/multiplexer of the - * receive and the parser/multiplexer of the aggregator are already combined. - */ -static void __detach_bond_from_agg(struct port *port) -{ - port = NULL; /* just to satisfy the compiler */ - // This function does nothing since the parser/multiplexer of the receive - // and the parser/multiplexer of the aggregator are already combined -} - -/** * __agg_ports_are_ready - check if all ports in an aggregator are ready * @aggregator: the aggregator we're looking at * @@ -625,7 +607,9 @@ static int __agg_ports_are_ready(struct aggregator *aggregator) int retval = 1; if (aggregator) { - // scan all ports in this aggregator to verfy if they are all ready + /* scan all ports in this aggregator to verfy if they are + * all ready. + */ for (port = aggregator->lag_ports; port; port = port->next_port_in_aggregator) { @@ -685,7 +669,7 @@ static u32 __get_agg_bandwidth(struct aggregator *aggregator) bandwidth = aggregator->num_of_ports * 10000; break; default: - bandwidth = 0; /*to silence the compiler ....*/ + bandwidth = 0; /* to silence the compiler */ } } return bandwidth; @@ -695,6 +679,7 @@ static u32 __get_agg_bandwidth(struct aggregator *aggregator) * __get_active_agg - get the current active aggregator * @aggregator: the aggregator we're looking at * + * Caller must hold RCU lock. */ static struct aggregator *__get_active_agg(struct aggregator *aggregator) { @@ -702,7 +687,7 @@ static struct aggregator *__get_active_agg(struct aggregator *aggregator) struct list_head *iter; struct slave *slave; - bond_for_each_slave(bond, slave, iter) + bond_for_each_slave_rcu(bond, slave, iter) if (SLAVE_AD_INFO(slave).aggregator.is_active) return &(SLAVE_AD_INFO(slave).aggregator); @@ -712,15 +697,14 @@ static struct aggregator *__get_active_agg(struct aggregator *aggregator) /** * __update_lacpdu_from_port - update a port's lacpdu fields * @port: the port we're looking at - * */ static inline void __update_lacpdu_from_port(struct port *port) { struct lacpdu *lacpdu = &port->lacpdu; const struct port_params *partner = &port->partner_oper; - /* update current actual Actor parameters */ - /* lacpdu->subtype initialized + /* update current actual Actor parameters + * lacpdu->subtype initialized * lacpdu->version_number initialized * lacpdu->tlv_type_actor_info initialized * lacpdu->actor_information_length initialized @@ -756,9 +740,7 @@ static inline void __update_lacpdu_from_port(struct port *port) */ } -////////////////////////////////////////////////////////////////////////////////////// -// ================= main 802.3ad protocol code ====================================== -////////////////////////////////////////////////////////////////////////////////////// +/* ================= main 802.3ad protocol code ========================= */ /** * ad_lacpdu_send - send out a lacpdu packet on a given port @@ -788,11 +770,12 @@ static int ad_lacpdu_send(struct port *port) memcpy(lacpdu_header->hdr.h_dest, lacpdu_mcast_addr, ETH_ALEN); /* Note: source address is set to be the member's PERMANENT address, - because we use it to identify loopback lacpdus in receive. */ + * because we use it to identify loopback lacpdus in receive. + */ memcpy(lacpdu_header->hdr.h_source, slave->perm_hwaddr, ETH_ALEN); lacpdu_header->hdr.h_proto = PKT_TYPE_LACPDU; - lacpdu_header->lacpdu = port->lacpdu; // struct copy + lacpdu_header->lacpdu = port->lacpdu; dev_queue_xmit(skb); @@ -829,11 +812,12 @@ static int ad_marker_send(struct port *port, struct bond_marker *marker) memcpy(marker_header->hdr.h_dest, lacpdu_mcast_addr, ETH_ALEN); /* Note: source address is set to be the member's PERMANENT address, - because we use it to identify loopback MARKERs in receive. */ + * because we use it to identify loopback MARKERs in receive. + */ memcpy(marker_header->hdr.h_source, slave->perm_hwaddr, ETH_ALEN); marker_header->hdr.h_proto = PKT_TYPE_LACPDU; - marker_header->marker = *marker; // struct copy + marker_header->marker = *marker; dev_queue_xmit(skb); @@ -843,72 +827,90 @@ static int ad_marker_send(struct port *port, struct bond_marker *marker) /** * ad_mux_machine - handle a port's mux state machine * @port: the port we're looking at - * */ static void ad_mux_machine(struct port *port) { mux_states_t last_state; - // keep current State Machine state to compare later if it was changed + /* keep current State Machine state to compare later if it was + * changed + */ last_state = port->sm_mux_state; if (port->sm_vars & AD_PORT_BEGIN) { - port->sm_mux_state = AD_MUX_DETACHED; // next state + port->sm_mux_state = AD_MUX_DETACHED; } else { switch (port->sm_mux_state) { case AD_MUX_DETACHED: if ((port->sm_vars & AD_PORT_SELECTED) || (port->sm_vars & AD_PORT_STANDBY)) /* if SELECTED or STANDBY */ - port->sm_mux_state = AD_MUX_WAITING; // next state + port->sm_mux_state = AD_MUX_WAITING; break; case AD_MUX_WAITING: - // if SELECTED == FALSE return to DETACH state - if (!(port->sm_vars & AD_PORT_SELECTED)) { // if UNSELECTED + /* if SELECTED == FALSE return to DETACH state */ + if (!(port->sm_vars & AD_PORT_SELECTED)) { port->sm_vars &= ~AD_PORT_READY_N; - // in order to withhold the Selection Logic to check all ports READY_N value - // every callback cycle to update ready variable, we check READY_N and update READY here + /* in order to withhold the Selection Logic to + * check all ports READY_N value every callback + * cycle to update ready variable, we check + * READY_N and update READY here + */ __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); - port->sm_mux_state = AD_MUX_DETACHED; // next state + port->sm_mux_state = AD_MUX_DETACHED; break; } - // check if the wait_while_timer expired + /* check if the wait_while_timer expired */ if (port->sm_mux_timer_counter && !(--port->sm_mux_timer_counter)) port->sm_vars |= AD_PORT_READY_N; - // in order to withhold the selection logic to check all ports READY_N value - // every callback cycle to update ready variable, we check READY_N and update READY here + /* in order to withhold the selection logic to check + * all ports READY_N value every callback cycle to + * update ready variable, we check READY_N and update + * READY here + */ __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); - // if the wait_while_timer expired, and the port is in READY state, move to ATTACHED state + /* if the wait_while_timer expired, and the port is + * in READY state, move to ATTACHED state + */ if ((port->sm_vars & AD_PORT_READY) && !port->sm_mux_timer_counter) - port->sm_mux_state = AD_MUX_ATTACHED; // next state + port->sm_mux_state = AD_MUX_ATTACHED; break; case AD_MUX_ATTACHED: - // check also if agg_select_timer expired(so the edable port will take place only after this timer) - if ((port->sm_vars & AD_PORT_SELECTED) && (port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION) && !__check_agg_selection_timer(port)) { - port->sm_mux_state = AD_MUX_COLLECTING_DISTRIBUTING;// next state - } else if (!(port->sm_vars & AD_PORT_SELECTED) || (port->sm_vars & AD_PORT_STANDBY)) { // if UNSELECTED or STANDBY + /* check also if agg_select_timer expired (so the + * edable port will take place only after this timer) + */ + if ((port->sm_vars & AD_PORT_SELECTED) && + (port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION) && + !__check_agg_selection_timer(port)) { + port->sm_mux_state = AD_MUX_COLLECTING_DISTRIBUTING; + } else if (!(port->sm_vars & AD_PORT_SELECTED) || + (port->sm_vars & AD_PORT_STANDBY)) { + /* if UNSELECTED or STANDBY */ port->sm_vars &= ~AD_PORT_READY_N; - // in order to withhold the selection logic to check all ports READY_N value - // every callback cycle to update ready variable, we check READY_N and update READY here + /* in order to withhold the selection logic to + * check all ports READY_N value every callback + * cycle to update ready variable, we check + * READY_N and update READY here + */ __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); - port->sm_mux_state = AD_MUX_DETACHED;// next state + port->sm_mux_state = AD_MUX_DETACHED; } break; case AD_MUX_COLLECTING_DISTRIBUTING: - if (!(port->sm_vars & AD_PORT_SELECTED) || (port->sm_vars & AD_PORT_STANDBY) || - !(port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION) - ) { - port->sm_mux_state = AD_MUX_ATTACHED;// next state - + if (!(port->sm_vars & AD_PORT_SELECTED) || + (port->sm_vars & AD_PORT_STANDBY) || + !(port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION)) { + port->sm_mux_state = AD_MUX_ATTACHED; } else { - // if port state hasn't changed make - // sure that a collecting distributing - // port in an active aggregator is enabled + /* if port state hasn't changed make + * sure that a collecting distributing + * port in an active aggregator is enabled + */ if (port->aggregator && port->aggregator->is_active && !__port_is_enabled(port)) { @@ -917,19 +919,18 @@ static void ad_mux_machine(struct port *port) } } break; - default: //to silence the compiler + default: break; } } - // check if the state machine was changed + /* check if the state machine was changed */ if (port->sm_mux_state != last_state) { pr_debug("Mux Machine: Port=%d, Last State=%d, Curr State=%d\n", port->actor_port_number, last_state, port->sm_mux_state); switch (port->sm_mux_state) { case AD_MUX_DETACHED: - __detach_bond_from_agg(port); port->actor_oper_port_state &= ~AD_STATE_SYNCHRONIZATION; ad_disable_collecting_distributing(port); port->actor_oper_port_state &= ~AD_STATE_COLLECTING; @@ -940,7 +941,6 @@ static void ad_mux_machine(struct port *port) port->sm_mux_timer_counter = __ad_timer_to_ticks(AD_WAIT_WHILE_TIMER, 0); break; case AD_MUX_ATTACHED: - __attach_bond_to_agg(port); port->actor_oper_port_state |= AD_STATE_SYNCHRONIZATION; port->actor_oper_port_state &= ~AD_STATE_COLLECTING; port->actor_oper_port_state &= ~AD_STATE_DISTRIBUTING; @@ -953,7 +953,7 @@ static void ad_mux_machine(struct port *port) ad_enable_collecting_distributing(port); port->ntt = true; break; - default: //to silence the compiler + default: break; } } @@ -972,59 +972,63 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) { rx_states_t last_state; - // keep current State Machine state to compare later if it was changed + /* keep current State Machine state to compare later if it was + * changed + */ last_state = port->sm_rx_state; - // check if state machine should change state - // first, check if port was reinitialized + /* check if state machine should change state */ + + /* first, check if port was reinitialized */ if (port->sm_vars & AD_PORT_BEGIN) - /* next state */ port->sm_rx_state = AD_RX_INITIALIZE; - // check if port is not enabled + /* check if port is not enabled */ else if (!(port->sm_vars & AD_PORT_BEGIN) && !port->is_enabled && !(port->sm_vars & AD_PORT_MOVED)) - /* next state */ port->sm_rx_state = AD_RX_PORT_DISABLED; - // check if new lacpdu arrived - else if (lacpdu && ((port->sm_rx_state == AD_RX_EXPIRED) || (port->sm_rx_state == AD_RX_DEFAULTED) || (port->sm_rx_state == AD_RX_CURRENT))) { - port->sm_rx_timer_counter = 0; // zero timer + /* check if new lacpdu arrived */ + else if (lacpdu && ((port->sm_rx_state == AD_RX_EXPIRED) || + (port->sm_rx_state == AD_RX_DEFAULTED) || + (port->sm_rx_state == AD_RX_CURRENT))) { + port->sm_rx_timer_counter = 0; port->sm_rx_state = AD_RX_CURRENT; } else { - // if timer is on, and if it is expired - if (port->sm_rx_timer_counter && !(--port->sm_rx_timer_counter)) { + /* if timer is on, and if it is expired */ + if (port->sm_rx_timer_counter && + !(--port->sm_rx_timer_counter)) { switch (port->sm_rx_state) { case AD_RX_EXPIRED: - port->sm_rx_state = AD_RX_DEFAULTED; // next state + port->sm_rx_state = AD_RX_DEFAULTED; break; case AD_RX_CURRENT: - port->sm_rx_state = AD_RX_EXPIRED; // next state + port->sm_rx_state = AD_RX_EXPIRED; break; - default: //to silence the compiler + default: break; } } else { - // if no lacpdu arrived and no timer is on + /* if no lacpdu arrived and no timer is on */ switch (port->sm_rx_state) { case AD_RX_PORT_DISABLED: if (port->sm_vars & AD_PORT_MOVED) - port->sm_rx_state = AD_RX_INITIALIZE; // next state + port->sm_rx_state = AD_RX_INITIALIZE; else if (port->is_enabled && (port->sm_vars & AD_PORT_LACP_ENABLED)) - port->sm_rx_state = AD_RX_EXPIRED; // next state + port->sm_rx_state = AD_RX_EXPIRED; else if (port->is_enabled && ((port->sm_vars & AD_PORT_LACP_ENABLED) == 0)) - port->sm_rx_state = AD_RX_LACP_DISABLED; // next state + port->sm_rx_state = AD_RX_LACP_DISABLED; break; - default: //to silence the compiler + default: break; } } } - // check if the State machine was changed or new lacpdu arrived + /* check if the State machine was changed or new lacpdu arrived */ if ((port->sm_rx_state != last_state) || (lacpdu)) { pr_debug("Rx Machine: Port=%d, Last State=%d, Curr State=%d\n", port->actor_port_number, last_state, @@ -1039,10 +1043,9 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) __record_default(port); port->actor_oper_port_state &= ~AD_STATE_EXPIRED; port->sm_vars &= ~AD_PORT_MOVED; - port->sm_rx_state = AD_RX_PORT_DISABLED; // next state - - /*- Fall Through -*/ + port->sm_rx_state = AD_RX_PORT_DISABLED; + /* Fall Through */ case AD_RX_PORT_DISABLED: port->sm_vars &= ~AD_PORT_MATCHED; break; @@ -1054,13 +1057,15 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) port->actor_oper_port_state &= ~AD_STATE_EXPIRED; break; case AD_RX_EXPIRED: - //Reset of the Synchronization flag. (Standard 43.4.12) - //This reset cause to disable this port in the COLLECTING_DISTRIBUTING state of the - //mux machine in case of EXPIRED even if LINK_DOWN didn't arrive for the port. + /* Reset of the Synchronization flag (Standard 43.4.12) + * This reset cause to disable this port in the + * COLLECTING_DISTRIBUTING state of the mux machine in + * case of EXPIRED even if LINK_DOWN didn't arrive for + * the port. + */ port->partner_oper.port_state &= ~AD_STATE_SYNCHRONIZATION; port->sm_vars &= ~AD_PORT_MATCHED; - port->partner_oper.port_state |= - AD_STATE_LACP_ACTIVITY; + port->partner_oper.port_state |= AD_STATE_LACP_ACTIVITY; port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(AD_SHORT_TIMEOUT)); port->actor_oper_port_state |= AD_STATE_EXPIRED; break; @@ -1071,12 +1076,12 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) port->actor_oper_port_state &= ~AD_STATE_EXPIRED; break; case AD_RX_CURRENT: - // detect loopback situation - if (!MAC_ADDRESS_COMPARE(&(lacpdu->actor_system), &(port->actor_system))) { - // INFO_RECEIVED_LOOPBACK_FRAMES - pr_err("%s: An illegal loopback occurred on adapter (%s).\n" - "Check the configuration to verify that all adapters are connected to 802.3ad compliant switch ports\n", - port->slave->bond->dev->name, port->slave->dev->name); + /* detect loopback situation */ + if (MAC_ADDRESS_EQUAL(&(lacpdu->actor_system), + &(port->actor_system))) { + pr_err("%s: An illegal loopback occurred on adapter (%s).\nCheck the configuration to verify that all adapters are connected to 802.3ad compliant switch ports\n", + port->slave->bond->dev->name, + port->slave->dev->name); return; } __update_selected(lacpdu, port); @@ -1085,7 +1090,7 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(port->actor_oper_port_state & AD_STATE_LACP_TIMEOUT)); port->actor_oper_port_state &= ~AD_STATE_EXPIRED; break; - default: //to silence the compiler + default: break; } } @@ -1094,13 +1099,14 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) /** * ad_tx_machine - handle a port's tx state machine * @port: the port we're looking at - * */ static void ad_tx_machine(struct port *port) { - // check if tx timer expired, to verify that we do not send more than 3 packets per second + /* check if tx timer expired, to verify that we do not send more than + * 3 packets per second + */ if (port->sm_tx_timer_counter && !(--port->sm_tx_timer_counter)) { - // check if there is something to send + /* check if there is something to send */ if (port->ntt && (port->sm_vars & AD_PORT_LACP_ENABLED)) { __update_lacpdu_from_port(port); @@ -1108,14 +1114,16 @@ static void ad_tx_machine(struct port *port) pr_debug("Sent LACPDU on port %d\n", port->actor_port_number); - /* mark ntt as false, so it will not be sent again until - demanded */ + /* mark ntt as false, so it will not be sent + * again until demanded + */ port->ntt = false; } } - // restart tx timer(to verify that we will not exceed AD_MAX_TX_IN_SECOND - port->sm_tx_timer_counter = - ad_ticks_per_sec/AD_MAX_TX_IN_SECOND; + /* restart tx timer(to verify that we will not exceed + * AD_MAX_TX_IN_SECOND + */ + port->sm_tx_timer_counter = ad_ticks_per_sec/AD_MAX_TX_IN_SECOND; } } @@ -1129,76 +1137,79 @@ static void ad_periodic_machine(struct port *port) { periodic_states_t last_state; - // keep current state machine state to compare later if it was changed + /* keep current state machine state to compare later if it was changed */ last_state = port->sm_periodic_state; - // check if port was reinitialized + /* check if port was reinitialized */ if (((port->sm_vars & AD_PORT_BEGIN) || !(port->sm_vars & AD_PORT_LACP_ENABLED) || !port->is_enabled) || (!(port->actor_oper_port_state & AD_STATE_LACP_ACTIVITY) && !(port->partner_oper.port_state & AD_STATE_LACP_ACTIVITY)) ) { - port->sm_periodic_state = AD_NO_PERIODIC; // next state + port->sm_periodic_state = AD_NO_PERIODIC; } - // check if state machine should change state + /* check if state machine should change state */ else if (port->sm_periodic_timer_counter) { - // check if periodic state machine expired + /* check if periodic state machine expired */ if (!(--port->sm_periodic_timer_counter)) { - // if expired then do tx - port->sm_periodic_state = AD_PERIODIC_TX; // next state + /* if expired then do tx */ + port->sm_periodic_state = AD_PERIODIC_TX; } else { - // If not expired, check if there is some new timeout parameter from the partner state + /* If not expired, check if there is some new timeout + * parameter from the partner state + */ switch (port->sm_periodic_state) { case AD_FAST_PERIODIC: if (!(port->partner_oper.port_state & AD_STATE_LACP_TIMEOUT)) - port->sm_periodic_state = AD_SLOW_PERIODIC; // next state + port->sm_periodic_state = AD_SLOW_PERIODIC; break; case AD_SLOW_PERIODIC: if ((port->partner_oper.port_state & AD_STATE_LACP_TIMEOUT)) { - // stop current timer port->sm_periodic_timer_counter = 0; - port->sm_periodic_state = AD_PERIODIC_TX; // next state + port->sm_periodic_state = AD_PERIODIC_TX; } break; - default: //to silence the compiler + default: break; } } } else { switch (port->sm_periodic_state) { case AD_NO_PERIODIC: - port->sm_periodic_state = AD_FAST_PERIODIC; // next state + port->sm_periodic_state = AD_FAST_PERIODIC; break; case AD_PERIODIC_TX: - if (!(port->partner_oper.port_state - & AD_STATE_LACP_TIMEOUT)) - port->sm_periodic_state = AD_SLOW_PERIODIC; // next state + if (!(port->partner_oper.port_state & + AD_STATE_LACP_TIMEOUT)) + port->sm_periodic_state = AD_SLOW_PERIODIC; else - port->sm_periodic_state = AD_FAST_PERIODIC; // next state + port->sm_periodic_state = AD_FAST_PERIODIC; break; - default: //to silence the compiler + default: break; } } - // check if the state machine was changed + /* check if the state machine was changed */ if (port->sm_periodic_state != last_state) { pr_debug("Periodic Machine: Port=%d, Last State=%d, Curr State=%d\n", port->actor_port_number, last_state, port->sm_periodic_state); switch (port->sm_periodic_state) { case AD_NO_PERIODIC: - port->sm_periodic_timer_counter = 0; // zero timer + port->sm_periodic_timer_counter = 0; break; case AD_FAST_PERIODIC: - port->sm_periodic_timer_counter = __ad_timer_to_ticks(AD_PERIODIC_TIMER, (u16)(AD_FAST_PERIODIC_TIME))-1; // decrement 1 tick we lost in the PERIODIC_TX cycle + /* decrement 1 tick we lost in the PERIODIC_TX cycle */ + port->sm_periodic_timer_counter = __ad_timer_to_ticks(AD_PERIODIC_TIMER, (u16)(AD_FAST_PERIODIC_TIME))-1; break; case AD_SLOW_PERIODIC: - port->sm_periodic_timer_counter = __ad_timer_to_ticks(AD_PERIODIC_TIMER, (u16)(AD_SLOW_PERIODIC_TIME))-1; // decrement 1 tick we lost in the PERIODIC_TX cycle + /* decrement 1 tick we lost in the PERIODIC_TX cycle */ + port->sm_periodic_timer_counter = __ad_timer_to_ticks(AD_PERIODIC_TIMER, (u16)(AD_SLOW_PERIODIC_TIME))-1; break; case AD_PERIODIC_TX: port->ntt = true; break; - default: //to silence the compiler + default: break; } } @@ -1221,30 +1232,38 @@ static void ad_port_selection_logic(struct port *port) struct slave *slave; int found = 0; - // if the port is already Selected, do nothing + /* if the port is already Selected, do nothing */ if (port->sm_vars & AD_PORT_SELECTED) return; bond = __get_bond_by_port(port); - // if the port is connected to other aggregator, detach it + /* if the port is connected to other aggregator, detach it */ if (port->aggregator) { - // detach the port from its former aggregator + /* detach the port from its former aggregator */ temp_aggregator = port->aggregator; for (curr_port = temp_aggregator->lag_ports; curr_port; last_port = curr_port, - curr_port = curr_port->next_port_in_aggregator) { + curr_port = curr_port->next_port_in_aggregator) { if (curr_port == port) { temp_aggregator->num_of_ports--; - if (!last_port) {// if it is the first port attached to the aggregator + /* if it is the first port attached to the + * aggregator + */ + if (!last_port) { temp_aggregator->lag_ports = port->next_port_in_aggregator; - } else {// not the first port attached to the aggregator + } else { + /* not the first port attached to the + * aggregator + */ last_port->next_port_in_aggregator = port->next_port_in_aggregator; } - // clear the port's relations to this aggregator + /* clear the port's relations to this + * aggregator + */ port->aggregator = NULL; port->next_port_in_aggregator = NULL; port->actor_port_aggregator_identifier = 0; @@ -1252,41 +1271,46 @@ static void ad_port_selection_logic(struct port *port) pr_debug("Port %d left LAG %d\n", port->actor_port_number, temp_aggregator->aggregator_identifier); - // if the aggregator is empty, clear its parameters, and set it ready to be attached + /* if the aggregator is empty, clear its + * parameters, and set it ready to be attached + */ if (!temp_aggregator->lag_ports) ad_clear_agg(temp_aggregator); break; } } - if (!curr_port) { // meaning: the port was related to an aggregator but was not on the aggregator port list - pr_warning("%s: Warning: Port %d (on %s) was related to aggregator %d but was not on its port list\n", - port->slave->bond->dev->name, - port->actor_port_number, - port->slave->dev->name, - port->aggregator->aggregator_identifier); + if (!curr_port) { + /* meaning: the port was related to an aggregator + * but was not on the aggregator port list + */ + pr_warn("%s: Warning: Port %d (on %s) was related to aggregator %d but was not on its port list\n", + port->slave->bond->dev->name, + port->actor_port_number, + port->slave->dev->name, + port->aggregator->aggregator_identifier); } } - // search on all aggregators for a suitable aggregator for this port + /* search on all aggregators for a suitable aggregator for this port */ bond_for_each_slave(bond, slave, iter) { aggregator = &(SLAVE_AD_INFO(slave).aggregator); - // keep a free aggregator for later use(if needed) + /* keep a free aggregator for later use(if needed) */ if (!aggregator->lag_ports) { if (!free_aggregator) free_aggregator = aggregator; continue; } - // check if current aggregator suits us - if (((aggregator->actor_oper_aggregator_key == port->actor_oper_port_key) && // if all parameters match AND - !MAC_ADDRESS_COMPARE(&(aggregator->partner_system), &(port->partner_oper.system)) && + /* check if current aggregator suits us */ + if (((aggregator->actor_oper_aggregator_key == port->actor_oper_port_key) && /* if all parameters match AND */ + MAC_ADDRESS_EQUAL(&(aggregator->partner_system), &(port->partner_oper.system)) && (aggregator->partner_system_priority == port->partner_oper.system_priority) && (aggregator->partner_oper_aggregator_key == port->partner_oper.key) ) && - ((MAC_ADDRESS_COMPARE(&(port->partner_oper.system), &(null_mac_addr)) && // partner answers - !aggregator->is_individual) // but is not individual OR + ((!MAC_ADDRESS_EQUAL(&(port->partner_oper.system), &(null_mac_addr)) && /* partner answers */ + !aggregator->is_individual) /* but is not individual OR */ ) ) { - // attach to the founded aggregator + /* attach to the founded aggregator */ port->aggregator = aggregator; port->actor_port_aggregator_identifier = port->aggregator->aggregator_identifier; @@ -1297,23 +1321,26 @@ static void ad_port_selection_logic(struct port *port) port->actor_port_number, port->aggregator->aggregator_identifier); - // mark this port as selected + /* mark this port as selected */ port->sm_vars |= AD_PORT_SELECTED; found = 1; break; } } - // the port couldn't find an aggregator - attach it to a new aggregator + /* the port couldn't find an aggregator - attach it to a new + * aggregator + */ if (!found) { if (free_aggregator) { - // assign port a new aggregator + /* assign port a new aggregator */ port->aggregator = free_aggregator; port->actor_port_aggregator_identifier = port->aggregator->aggregator_identifier; - // update the new aggregator's parameters - // if port was responsed from the end-user + /* update the new aggregator's parameters + * if port was responsed from the end-user + */ if (port->actor_oper_port_key & AD_DUPLEX_KEY_BITS) /* if port is full duplex */ port->aggregator->is_individual = false; @@ -1332,7 +1359,7 @@ static void ad_port_selection_logic(struct port *port) port->aggregator->lag_ports = port; port->aggregator->num_of_ports++; - // mark this port as selected + /* mark this port as selected */ port->sm_vars |= AD_PORT_SELECTED; pr_debug("Port %d joined LAG %d(new LAG)\n", @@ -1344,23 +1371,24 @@ static void ad_port_selection_logic(struct port *port) port->actor_port_number, port->slave->dev->name); } } - // if all aggregator's ports are READY_N == TRUE, set ready=TRUE in all aggregator's ports - // else set ready=FALSE in all aggregator's ports - __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); + /* if all aggregator's ports are READY_N == TRUE, set ready=TRUE + * in all aggregator's ports, else set ready=FALSE in all + * aggregator's ports + */ + __set_agg_ports_ready(port->aggregator, + __agg_ports_are_ready(port->aggregator)); aggregator = __get_first_agg(port); ad_agg_selection_logic(aggregator); } -/* - * Decide if "agg" is a better choice for the new active aggregator that +/* Decide if "agg" is a better choice for the new active aggregator that * the current best, according to the ad_select policy. */ static struct aggregator *ad_agg_selection_test(struct aggregator *best, struct aggregator *curr) { - /* - * 0. If no best, select current. + /* 0. If no best, select current. * * 1. If the current agg is not individual, and the best is * individual, select current. @@ -1416,9 +1444,9 @@ static struct aggregator *ad_agg_selection_test(struct aggregator *best, break; default: - pr_warning("%s: Impossible agg select mode %d\n", - curr->slave->bond->dev->name, - __get_agg_selection_mode(curr->lag_ports)); + pr_warn("%s: Impossible agg select mode %d\n", + curr->slave->bond->dev->name, + __get_agg_selection_mode(curr->lag_ports)); break; } @@ -1428,10 +1456,12 @@ static struct aggregator *ad_agg_selection_test(struct aggregator *best, static int agg_device_up(const struct aggregator *agg) { struct port *port = agg->lag_ports; + if (!port) return 0; - return (netif_running(port->slave->dev) && - netif_carrier_ok(port->slave->dev)); + + return netif_running(port->slave->dev) && + netif_carrier_ok(port->slave->dev); } /** @@ -1467,11 +1497,12 @@ static void ad_agg_selection_logic(struct aggregator *agg) struct slave *slave; struct port *port; + rcu_read_lock(); origin = agg; active = __get_active_agg(agg); best = (active && agg_device_up(active)) ? active : NULL; - bond_for_each_slave(bond, slave, iter) { + bond_for_each_slave_rcu(bond, slave, iter) { agg = &(SLAVE_AD_INFO(slave).aggregator); agg->is_active = 0; @@ -1482,8 +1513,7 @@ static void ad_agg_selection_logic(struct aggregator *agg) if (best && __get_agg_selection_mode(best->lag_ports) == BOND_AD_STABLE) { - /* - * For the STABLE policy, don't replace the old active + /* For the STABLE policy, don't replace the old active * aggregator if it's still active (it has an answering * partner) or if both the best and active don't have an * answering partner. @@ -1491,7 +1521,8 @@ static void ad_agg_selection_logic(struct aggregator *agg) if (active && active->lag_ports && active->lag_ports->is_enabled && (__agg_has_partner(active) || - (!__agg_has_partner(active) && !__agg_has_partner(best)))) { + (!__agg_has_partner(active) && + !__agg_has_partner(best)))) { if (!(!active->actor_oper_aggregator_key && best->actor_oper_aggregator_key)) { best = NULL; @@ -1505,7 +1536,7 @@ static void ad_agg_selection_logic(struct aggregator *agg) active->is_active = 1; } - // if there is new best aggregator, activate it + /* if there is new best aggregator, activate it */ if (best) { pr_debug("best Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n", best->aggregator_identifier, best->num_of_ports, @@ -1516,7 +1547,7 @@ static void ad_agg_selection_logic(struct aggregator *agg) best->lag_ports, best->slave, best->slave ? best->slave->dev->name : "NULL"); - bond_for_each_slave(bond, slave, iter) { + bond_for_each_slave_rcu(bond, slave, iter) { agg = &(SLAVE_AD_INFO(slave).aggregator); pr_debug("Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n", @@ -1526,10 +1557,11 @@ static void ad_agg_selection_logic(struct aggregator *agg) agg->is_individual, agg->is_active); } - // check if any partner replys + /* check if any partner replys */ if (best->is_individual) { - pr_warning("%s: Warning: No 802.3ad response from the link partner for any adapters in the bond\n", - best->slave ? best->slave->bond->dev->name : "NULL"); + pr_warn("%s: Warning: No 802.3ad response from the link partner for any adapters in the bond\n", + best->slave ? + best->slave->bond->dev->name : "NULL"); } best->is_active = 1; @@ -1541,7 +1573,9 @@ static void ad_agg_selection_logic(struct aggregator *agg) best->partner_oper_aggregator_key, best->is_individual, best->is_active); - // disable the ports that were related to the former active_aggregator + /* disable the ports that were related to the former + * active_aggregator + */ if (active) { for (port = active->lag_ports; port; port = port->next_port_in_aggregator) { @@ -1550,8 +1584,7 @@ static void ad_agg_selection_logic(struct aggregator *agg) } } - /* - * if the selected aggregator is of join individuals + /* if the selected aggregator is of join individuals * (partner_system is NULL), enable their ports */ active = __get_active_agg(origin); @@ -1565,13 +1598,14 @@ static void ad_agg_selection_logic(struct aggregator *agg) } } + rcu_read_unlock(); + bond_3ad_set_carrier(bond); } /** * ad_clear_agg - clear a given aggregator's parameters * @aggregator: the aggregator we're looking at - * */ static void ad_clear_agg(struct aggregator *aggregator) { @@ -1595,7 +1629,6 @@ static void ad_clear_agg(struct aggregator *aggregator) /** * ad_initialize_agg - initialize a given aggregator's parameters * @aggregator: the aggregator we're looking at - * */ static void ad_initialize_agg(struct aggregator *aggregator) { @@ -1612,7 +1645,6 @@ static void ad_initialize_agg(struct aggregator *aggregator) * ad_initialize_port - initialize a given port's parameters * @aggregator: the aggregator we're looking at * @lacp_fast: boolean. whether fast periodic should be used - * */ static void ad_initialize_port(struct port *port, int lacp_fast) { @@ -1644,8 +1676,10 @@ static void ad_initialize_port(struct port *port, int lacp_fast) port->ntt = false; port->actor_admin_port_key = 1; port->actor_oper_port_key = 1; - port->actor_admin_port_state = AD_STATE_AGGREGATION | AD_STATE_LACP_ACTIVITY; - port->actor_oper_port_state = AD_STATE_AGGREGATION | AD_STATE_LACP_ACTIVITY; + port->actor_admin_port_state = AD_STATE_AGGREGATION | + AD_STATE_LACP_ACTIVITY; + port->actor_oper_port_state = AD_STATE_AGGREGATION | + AD_STATE_LACP_ACTIVITY; if (lacp_fast) port->actor_oper_port_state |= AD_STATE_LACP_TIMEOUT; @@ -1654,7 +1688,7 @@ static void ad_initialize_port(struct port *port, int lacp_fast) memcpy(&port->partner_oper, &tmpl, sizeof(tmpl)); port->is_enabled = true; - // ****** private parameters ****** + /* private parameters */ port->sm_vars = 0x3; port->sm_rx_state = 0; port->sm_rx_timer_counter = 0; @@ -1692,11 +1726,12 @@ static void ad_enable_collecting_distributing(struct port *port) /** * ad_disable_collecting_distributing - disable a port's transmit/receive * @port: the port we're looking at - * */ static void ad_disable_collecting_distributing(struct port *port) { - if (port->aggregator && MAC_ADDRESS_COMPARE(&(port->aggregator->partner_system), &(null_mac_addr))) { + if (port->aggregator && + !MAC_ADDRESS_EQUAL(&(port->aggregator->partner_system), + &(null_mac_addr))) { pr_debug("Disabling port %d(LAG %d)\n", port->actor_port_number, port->aggregator->aggregator_identifier); @@ -1704,66 +1739,22 @@ static void ad_disable_collecting_distributing(struct port *port) } } -#if 0 -/** - * ad_marker_info_send - send a marker information frame - * @port: the port we're looking at - * - * This function does nothing since we decided not to implement send and handle - * response for marker PDU's, in this stage, but only to respond to marker - * information. - */ -static void ad_marker_info_send(struct port *port) -{ - struct bond_marker marker; - u16 index; - - // fill the marker PDU with the appropriate values - marker.subtype = 0x02; - marker.version_number = 0x01; - marker.tlv_type = AD_MARKER_INFORMATION_SUBTYPE; - marker.marker_length = 0x16; - // convert requester_port to Big Endian - marker.requester_port = (((port->actor_port_number & 0xFF) << 8) |((u16)(port->actor_port_number & 0xFF00) >> 8)); - marker.requester_system = port->actor_system; - // convert requester_port(u32) to Big Endian - marker.requester_transaction_id = - (((++port->transaction_id & 0xFF) << 24) - | ((port->transaction_id & 0xFF00) << 8) - | ((port->transaction_id & 0xFF0000) >> 8) - | ((port->transaction_id & 0xFF000000) >> 24)); - marker.pad = 0; - marker.tlv_type_terminator = 0x00; - marker.terminator_length = 0x00; - for (index = 0; index < 90; index++) - marker.reserved_90[index] = 0; - - // send the marker information - if (ad_marker_send(port, &marker) >= 0) { - pr_debug("Sent Marker Information on port %d\n", - port->actor_port_number); - } -} -#endif - /** * ad_marker_info_received - handle receive of a Marker information frame * @marker_info: Marker info received * @port: the port we're looking at - * */ static void ad_marker_info_received(struct bond_marker *marker_info, struct port *port) { struct bond_marker marker; - // copy the received marker data to the response marker - //marker = *marker_info; + /* copy the received marker data to the response marker */ memcpy(&marker, marker_info, sizeof(struct bond_marker)); - // change the marker subtype to marker response + /* change the marker subtype to marker response */ marker.tlv_type = AD_MARKER_RESPONSE_SUBTYPE; - // send the marker response + /* send the marker response */ if (ad_marker_send(port, &marker) >= 0) { pr_debug("Sent Marker Response on port %d\n", port->actor_port_number); @@ -1780,22 +1771,21 @@ static void ad_marker_info_received(struct bond_marker *marker_info, * information. */ static void ad_marker_response_received(struct bond_marker *marker, - struct port *port) + struct port *port) { - marker = NULL; /* just to satisfy the compiler */ - port = NULL; /* just to satisfy the compiler */ - // DO NOTHING, SINCE WE DECIDED NOT TO IMPLEMENT THIS FEATURE FOR NOW + marker = NULL; + port = NULL; + /* DO NOTHING, SINCE WE DECIDED NOT TO IMPLEMENT THIS FEATURE FOR NOW */ } -////////////////////////////////////////////////////////////////////////////////////// -// ================= AD exported functions to the main bonding code ================== -////////////////////////////////////////////////////////////////////////////////////// +/* ========= AD exported functions to the main bonding code ========= */ -// Check aggregators status in team every T seconds +/* Check aggregators status in team every T seconds */ #define AD_AGGREGATOR_SELECTION_TIMER 8 -/* - * bond_3ad_initiate_agg_selection(struct bonding *bond) +/** + * bond_3ad_initiate_agg_selection - initate aggregator selection + * @bond: bonding struct * * Set the aggregation selection timer, to initiate an agg selection in * the very near future. Called during first initialization, and during @@ -1817,8 +1807,8 @@ static u16 aggregator_identifier; */ void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution) { - // check that the bond is not initialized yet - if (MAC_ADDRESS_COMPARE(&(BOND_AD_INFO(bond).system.sys_mac_addr), + /* check that the bond is not initialized yet */ + if (!MAC_ADDRESS_EQUAL(&(BOND_AD_INFO(bond).system.sys_mac_addr), bond->dev->dev_addr)) { aggregator_identifier = 0; @@ -1826,7 +1816,9 @@ void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution) BOND_AD_INFO(bond).system.sys_priority = 0xFFFF; BOND_AD_INFO(bond).system.sys_mac_addr = *((struct mac_addr *)bond->dev->dev_addr); - // initialize how many times this module is called in one second(should be about every 100ms) + /* initialize how many times this module is called in one + * second (should be about every 100ms) + */ ad_ticks_per_sec = tick_resolution; bond_3ad_initiate_agg_selection(bond, @@ -1842,22 +1834,16 @@ void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution) * Returns: 0 on success * < 0 on error */ -int bond_3ad_bind_slave(struct slave *slave) +void bond_3ad_bind_slave(struct slave *slave) { struct bonding *bond = bond_get_bond_by_slave(slave); struct port *port; struct aggregator *aggregator; - if (bond == NULL) { - pr_err("%s: The slave %s is not attached to its bond\n", - slave->bond->dev->name, slave->dev->name); - return -1; - } - - //check that the slave has not been initialized yet. + /* check that the slave has not been initialized yet. */ if (SLAVE_AD_INFO(slave).port.slave != slave) { - // port initialization + /* port initialization */ port = &(SLAVE_AD_INFO(slave).port); ad_initialize_port(port, bond->params.lacp_fast); @@ -1865,28 +1851,30 @@ int bond_3ad_bind_slave(struct slave *slave) __initialize_port_locks(slave); port->slave = slave; port->actor_port_number = SLAVE_AD_INFO(slave).id; - // key is determined according to the link speed, duplex and user key(which is yet not supported) - // ------------------------------------------------------------ - // Port key : | User key | Speed |Duplex| - // ------------------------------------------------------------ - // 16 6 1 0 - port->actor_admin_port_key = 0; // initialize this parameter + /* key is determined according to the link speed, duplex and user key(which + * is yet not supported) + */ + port->actor_admin_port_key = 0; port->actor_admin_port_key |= __get_duplex(port); port->actor_admin_port_key |= (__get_link_speed(port) << 1); port->actor_oper_port_key = port->actor_admin_port_key; - // if the port is not full duplex, then the port should be not lacp Enabled + /* if the port is not full duplex, then the port should be not + * lacp Enabled + */ if (!(port->actor_oper_port_key & AD_DUPLEX_KEY_BITS)) port->sm_vars &= ~AD_PORT_LACP_ENABLED; - // actor system is the bond's system + /* actor system is the bond's system */ port->actor_system = BOND_AD_INFO(bond).system.sys_mac_addr; - // tx timer(to verify that no more than MAX_TX_IN_SECOND lacpdu's are sent in one second) + /* tx timer(to verify that no more than MAX_TX_IN_SECOND + * lacpdu's are sent in one second) + */ port->sm_tx_timer_counter = ad_ticks_per_sec/AD_MAX_TX_IN_SECOND; port->aggregator = NULL; port->next_port_in_aggregator = NULL; __disable_port(port); - // aggregator initialization + /* aggregator initialization */ aggregator = &(SLAVE_AD_INFO(slave).aggregator); ad_initialize_agg(aggregator); @@ -1897,8 +1885,6 @@ int bond_3ad_bind_slave(struct slave *slave) aggregator->is_active = 0; aggregator->num_of_ports = 0; } - - return 0; } /** @@ -1918,16 +1904,13 @@ void bond_3ad_unbind_slave(struct slave *slave) struct slave *slave_iter; struct list_head *iter; - // find the aggregator related to this slave aggregator = &(SLAVE_AD_INFO(slave).aggregator); - - // find the port related to this slave port = &(SLAVE_AD_INFO(slave).port); - // if slave is null, the whole port is not initialized + /* if slave is null, the whole port is not initialized */ if (!port->slave) { - pr_warning("Warning: %s: Trying to unbind an uninitialized port on %s\n", - slave->bond->dev->name, slave->dev->name); + pr_warn("Warning: %s: Trying to unbind an uninitialized port on %s\n", + slave->bond->dev->name, slave->dev->name); return; } @@ -1939,34 +1922,42 @@ void bond_3ad_unbind_slave(struct slave *slave) __update_lacpdu_from_port(port); ad_lacpdu_send(port); - // check if this aggregator is occupied + /* check if this aggregator is occupied */ if (aggregator->lag_ports) { - // check if there are other ports related to this aggregator except - // the port related to this slave(thats ensure us that there is a - // reason to search for new aggregator, and that we will find one - if ((aggregator->lag_ports != port) || (aggregator->lag_ports->next_port_in_aggregator)) { - // find new aggregator for the related port(s) + /* check if there are other ports related to this aggregator + * except the port related to this slave(thats ensure us that + * there is a reason to search for new aggregator, and that we + * will find one + */ + if ((aggregator->lag_ports != port) || + (aggregator->lag_ports->next_port_in_aggregator)) { + /* find new aggregator for the related port(s) */ bond_for_each_slave(bond, slave_iter, iter) { new_aggregator = &(SLAVE_AD_INFO(slave_iter).aggregator); - // if the new aggregator is empty, or it is connected to our port only - if (!new_aggregator->lag_ports - || ((new_aggregator->lag_ports == port) - && !new_aggregator->lag_ports->next_port_in_aggregator)) + /* if the new aggregator is empty, or it is + * connected to our port only + */ + if (!new_aggregator->lag_ports || + ((new_aggregator->lag_ports == port) && + !new_aggregator->lag_ports->next_port_in_aggregator)) break; } if (!slave_iter) new_aggregator = NULL; - // if new aggregator found, copy the aggregator's parameters - // and connect the related lag_ports to the new aggregator + + /* if new aggregator found, copy the aggregator's + * parameters and connect the related lag_ports to the + * new aggregator + */ if ((new_aggregator) && ((!new_aggregator->lag_ports) || ((new_aggregator->lag_ports == port) && !new_aggregator->lag_ports->next_port_in_aggregator))) { pr_debug("Some port(s) related to LAG %d - replaceing with LAG %d\n", aggregator->aggregator_identifier, new_aggregator->aggregator_identifier); - if ((new_aggregator->lag_ports == port) && new_aggregator->is_active) { + if ((new_aggregator->lag_ports == port) && + new_aggregator->is_active) { pr_info("%s: Removing an active aggregator\n", aggregator->slave->bond->dev->name); - // select new active aggregator select_new_active_agg = 1; } @@ -1982,30 +1973,33 @@ void bond_3ad_unbind_slave(struct slave *slave) new_aggregator->is_active = aggregator->is_active; new_aggregator->num_of_ports = aggregator->num_of_ports; - // update the information that is written on the ports about the aggregator + /* update the information that is written on + * the ports about the aggregator + */ for (temp_port = aggregator->lag_ports; temp_port; temp_port = temp_port->next_port_in_aggregator) { temp_port->aggregator = new_aggregator; temp_port->actor_port_aggregator_identifier = new_aggregator->aggregator_identifier; } - // clear the aggregator ad_clear_agg(aggregator); if (select_new_active_agg) ad_agg_selection_logic(__get_first_agg(port)); } else { - pr_warning("%s: Warning: unbinding aggregator, and could not find a new aggregator for its ports\n", - slave->bond->dev->name); + pr_warn("%s: Warning: unbinding aggregator, and could not find a new aggregator for its ports\n", + slave->bond->dev->name); } - } else { // in case that the only port related to this aggregator is the one we want to remove + } else { + /* in case that the only port related to this + * aggregator is the one we want to remove + */ select_new_active_agg = aggregator->is_active; - // clear the aggregator ad_clear_agg(aggregator); if (select_new_active_agg) { pr_info("%s: Removing an active aggregator\n", slave->bond->dev->name); - // select new active aggregator + /* select new active aggregator */ temp_aggregator = __get_first_agg(port); if (temp_aggregator) ad_agg_selection_logic(temp_aggregator); @@ -2014,15 +2008,19 @@ void bond_3ad_unbind_slave(struct slave *slave) } pr_debug("Unbinding port %d\n", port->actor_port_number); - // find the aggregator that this port is connected to + + /* find the aggregator that this port is connected to */ bond_for_each_slave(bond, slave_iter, iter) { temp_aggregator = &(SLAVE_AD_INFO(slave_iter).aggregator); prev_port = NULL; - // search the port in the aggregator's related ports + /* search the port in the aggregator's related ports */ for (temp_port = temp_aggregator->lag_ports; temp_port; prev_port = temp_port, - temp_port = temp_port->next_port_in_aggregator) { - if (temp_port == port) { // the aggregator found - detach the port from this aggregator + temp_port = temp_port->next_port_in_aggregator) { + if (temp_port == port) { + /* the aggregator found - detach the port from + * this aggregator + */ if (prev_port) prev_port->next_port_in_aggregator = temp_port->next_port_in_aggregator; else @@ -2030,12 +2028,11 @@ void bond_3ad_unbind_slave(struct slave *slave) temp_aggregator->num_of_ports--; if (temp_aggregator->num_of_ports == 0) { select_new_active_agg = temp_aggregator->is_active; - // clear the aggregator ad_clear_agg(temp_aggregator); if (select_new_active_agg) { pr_info("%s: Removing an active aggregator\n", slave->bond->dev->name); - // select new active aggregator + /* select new active aggregator */ ad_agg_selection_logic(__get_first_agg(port)); } } @@ -2069,21 +2066,23 @@ void bond_3ad_state_machine_handler(struct work_struct *work) struct port *port; read_lock(&bond->lock); + rcu_read_lock(); - //check if there are any slaves + /* check if there are any slaves */ if (!bond_has_slaves(bond)) goto re_arm; - // check if agg_select_timer timer after initialize is timed out - if (BOND_AD_INFO(bond).agg_select_timer && !(--BOND_AD_INFO(bond).agg_select_timer)) { - slave = bond_first_slave(bond); + /* check if agg_select_timer timer after initialize is timed out */ + if (BOND_AD_INFO(bond).agg_select_timer && + !(--BOND_AD_INFO(bond).agg_select_timer)) { + slave = bond_first_slave_rcu(bond); port = slave ? &(SLAVE_AD_INFO(slave).port) : NULL; - // select the active aggregator for the bond + /* select the active aggregator for the bond */ if (port) { if (!port->slave) { - pr_warning("%s: Warning: bond's first port is uninitialized\n", - bond->dev->name); + pr_warn("%s: Warning: bond's first port is uninitialized\n", + bond->dev->name); goto re_arm; } @@ -2093,12 +2092,12 @@ void bond_3ad_state_machine_handler(struct work_struct *work) bond_3ad_set_carrier(bond); } - // for each port run the state machines - bond_for_each_slave(bond, slave, iter) { + /* for each port run the state machines */ + bond_for_each_slave_rcu(bond, slave, iter) { port = &(SLAVE_AD_INFO(slave).port); if (!port->slave) { - pr_warning("%s: Warning: Found an uninitialized port\n", - bond->dev->name); + pr_warn("%s: Warning: Found an uninitialized port\n", + bond->dev->name); goto re_arm; } @@ -2114,7 +2113,7 @@ void bond_3ad_state_machine_handler(struct work_struct *work) ad_mux_machine(port); ad_tx_machine(port); - // turn off the BEGIN bit, since we already handled it + /* turn off the BEGIN bit, since we already handled it */ if (port->sm_vars & AD_PORT_BEGIN) port->sm_vars &= ~AD_PORT_BEGIN; @@ -2122,9 +2121,9 @@ void bond_3ad_state_machine_handler(struct work_struct *work) } re_arm: - queue_delayed_work(bond->wq, &bond->ad_work, ad_delta_in_ticks); - + rcu_read_unlock(); read_unlock(&bond->lock); + queue_delayed_work(bond->wq, &bond->ad_work, ad_delta_in_ticks); } /** @@ -2137,7 +2136,8 @@ re_arm: * received frames (loopback). Since only the payload is given to this * function, it check for loopback. */ -static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u16 length) +static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, + u16 length) { struct port *port; int ret = RX_HANDLER_ANOTHER; @@ -2147,8 +2147,8 @@ static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u1 port = &(SLAVE_AD_INFO(slave).port); if (!port->slave) { - pr_warning("%s: Warning: port of slave %s is uninitialized\n", - slave->dev->name, slave->bond->dev->name); + pr_warn("%s: Warning: port of slave %s is uninitialized\n", + slave->dev->name, slave->bond->dev->name); return ret; } @@ -2165,7 +2165,9 @@ static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u1 case AD_TYPE_MARKER: ret = RX_HANDLER_CONSUMED; - // No need to convert fields to Little Endian since we don't use the marker's fields. + /* No need to convert fields to Little Endian since we + * don't use the marker's fields. + */ switch (((struct bond_marker *)lacpdu)->tlv_type) { case AD_MARKER_INFORMATION_SUBTYPE: @@ -2203,8 +2205,8 @@ void bond_3ad_adapter_speed_changed(struct slave *slave) /* if slave is null, the whole port is not initialized */ if (!port->slave) { - pr_warning("Warning: %s: speed changed for uninitialized port on %s\n", - slave->bond->dev->name, slave->dev->name); + pr_warn("Warning: %s: speed changed for uninitialized port on %s\n", + slave->bond->dev->name, slave->dev->name); return; } @@ -2236,8 +2238,8 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave) /* if slave is null, the whole port is not initialized */ if (!port->slave) { - pr_warning("%s: Warning: duplex changed for uninitialized port on %s\n", - slave->bond->dev->name, slave->dev->name); + pr_warn("%s: Warning: duplex changed for uninitialized port on %s\n", + slave->bond->dev->name, slave->dev->name); return; } @@ -2270,8 +2272,8 @@ void bond_3ad_handle_link_change(struct slave *slave, char link) /* if slave is null, the whole port is not initialized */ if (!port->slave) { - pr_warning("Warning: %s: link status changed for uninitialized port on %s\n", - slave->bond->dev->name, slave->dev->name); + pr_warn("Warning: %s: link status changed for uninitialized port on %s\n", + slave->bond->dev->name, slave->dev->name); return; } @@ -2309,10 +2311,13 @@ void bond_3ad_handle_link_change(struct slave *slave, char link) __release_state_machine_lock(port); } -/* - * set link state for bonding master: if we have an active - * aggregator, we're up, if not, we're down. Presumes that we cannot - * have an active aggregator if there are no slaves with link up. +/** + * bond_3ad_set_carrier - set link state for bonding master + * @bond - bonding structure + * + * if we have an active aggregator, we're up, if not, we're down. + * Presumes that we cannot have an active aggregator if there are + * no slaves with link up. * * This behavior complies with IEEE 802.3 section 43.3.9. * @@ -2323,30 +2328,32 @@ int bond_3ad_set_carrier(struct bonding *bond) { struct aggregator *active; struct slave *first_slave; + int ret = 1; - first_slave = bond_first_slave(bond); - if (!first_slave) - return 0; + rcu_read_lock(); + first_slave = bond_first_slave_rcu(bond); + if (!first_slave) { + ret = 0; + goto out; + } active = __get_active_agg(&(SLAVE_AD_INFO(first_slave).aggregator)); if (active) { /* are enough slaves available to consider link up? */ if (active->num_of_ports < bond->params.min_links) { if (netif_carrier_ok(bond->dev)) { netif_carrier_off(bond->dev); - return 1; + goto out; } } else if (!netif_carrier_ok(bond->dev)) { netif_carrier_on(bond->dev); - return 1; + goto out; } - return 0; - } - - if (netif_carrier_ok(bond->dev)) { + } else if (netif_carrier_ok(bond->dev)) { netif_carrier_off(bond->dev); - return 1; } - return 0; +out: + rcu_read_unlock(); + return ret; } /** @@ -2378,7 +2385,8 @@ int __bond_3ad_get_active_agg_info(struct bonding *bond, ad_info->ports = aggregator->num_of_ports; ad_info->actor_key = aggregator->actor_oper_aggregator_key; ad_info->partner_key = aggregator->partner_oper_aggregator_key; - memcpy(ad_info->partner_system, aggregator->partner_system.mac_addr_value, ETH_ALEN); + memcpy(ad_info->partner_system, + aggregator->partner_system.mac_addr_value, ETH_ALEN); return 0; } @@ -2406,13 +2414,12 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev) struct list_head *iter; int slaves_in_agg; int slave_agg_no; - int res = 1; int agg_id; if (__bond_3ad_get_active_agg_info(bond, &ad_info)) { pr_debug("%s: Error: __bond_3ad_get_active_agg_info failed\n", dev->name); - goto out; + goto err_free; } slaves_in_agg = ad_info.ports; @@ -2420,7 +2427,7 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev) if (slaves_in_agg == 0) { pr_debug("%s: Error: active aggregator is empty\n", dev->name); - goto out; + goto err_free; } slave_agg_no = bond_xmit_hash(bond, skb, slaves_in_agg); @@ -2439,7 +2446,7 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev) } if (SLAVE_IS_OK(slave)) { - res = bond_dev_queue_xmit(bond, skb, slave->dev); + bond_dev_queue_xmit(bond, skb, slave->dev); goto out; } } @@ -2447,21 +2454,23 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev) if (slave_agg_no >= 0) { pr_err("%s: Error: Couldn't find a slave to tx on for aggregator ID %d\n", dev->name, agg_id); - goto out; + goto err_free; } /* we couldn't find any suitable slave after the agg_no, so use the - * first suitable found, if found. */ + * first suitable found, if found. + */ if (first_ok_slave) - res = bond_dev_queue_xmit(bond, skb, first_ok_slave->dev); + bond_dev_queue_xmit(bond, skb, first_ok_slave->dev); + else + goto err_free; out: - if (res) { - /* no suitable interface, frame not sent */ - kfree_skb(skb); - } - return NETDEV_TX_OK; +err_free: + /* no suitable interface, frame not sent */ + kfree_skb(skb); + goto out; } int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond, @@ -2483,7 +2492,10 @@ int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond, return ret; } -/* +/** + * bond_3ad_update_lacp_rate - change the lacp rate + * @bond - bonding struct + * * When modify lacp_rate parameter via sysfs, * update actor_oper_port_state of each port. * diff --git a/drivers/net/bonding/bond_3ad.h b/drivers/net/bonding/bond_3ad.h index 5d91ad0cc041..13dc9d3c5e34 100644 --- a/drivers/net/bonding/bond_3ad.h +++ b/drivers/net/bonding/bond_3ad.h @@ -265,7 +265,7 @@ struct ad_slave_info { // ================= AD Exported functions to the main bonding code ================== void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution); -int bond_3ad_bind_slave(struct slave *slave); +void bond_3ad_bind_slave(struct slave *slave); void bond_3ad_unbind_slave(struct slave *slave); void bond_3ad_state_machine_handler(struct work_struct *); void bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout); diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 02872405d35d..a2c47476804d 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -12,8 +12,7 @@ * for more details. * * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * with this program; if not, see <http://www.gnu.org/licenses/>. * * The full GNU General Public License is included in this distribution in the * file called LICENSE. @@ -470,7 +469,7 @@ static void rlb_teach_disabled_mac_on_primary(struct bonding *bond, u8 addr[]) /* slave being removed should not be active at this point * - * Caller must hold bond lock for read + * Caller must hold rtnl. */ static void rlb_clear_slave(struct bonding *bond, struct slave *slave) { @@ -816,7 +815,7 @@ static void rlb_rebalance(struct bonding *bond) for (; hash_index != RLB_NULL_INDEX; hash_index = client_info->used_next) { client_info = &(bond_info->rx_hashtbl[hash_index]); - assigned_slave = rlb_next_rx_slave(bond); + assigned_slave = __rlb_next_rx_slave(bond); if (assigned_slave && (client_info->slave != assigned_slave)) { client_info->slave = assigned_slave; client_info->ntt = 1; @@ -1372,7 +1371,6 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev) int do_tx_balance = 1; u32 hash_index = 0; const u8 *hash_start = NULL; - int res = 1; struct ipv6hdr *ip6hdr; skb_reset_mac_header(skb); @@ -1470,7 +1468,8 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev) ETH_ALEN); } - res = bond_dev_queue_xmit(bond, skb, tx_slave->dev); + bond_dev_queue_xmit(bond, skb, tx_slave->dev); + goto out; } else { if (tx_slave) { _lock_tx_hashtbl(bond); @@ -1479,11 +1478,9 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev) } } - if (res) { - /* no suitable interface, frame not sent */ - kfree_skb(skb); - } - + /* no suitable interface, frame not sent */ + kfree_skb(skb); +out: return NETDEV_TX_OK; } @@ -1495,14 +1492,14 @@ void bond_alb_monitor(struct work_struct *work) struct list_head *iter; struct slave *slave; - read_lock(&bond->lock); - if (!bond_has_slaves(bond)) { bond_info->tx_rebalance_counter = 0; bond_info->lp_counter = 0; goto re_arm; } + rcu_read_lock(); + bond_info->tx_rebalance_counter++; bond_info->lp_counter++; @@ -1515,7 +1512,7 @@ void bond_alb_monitor(struct work_struct *work) */ read_lock(&bond->curr_slave_lock); - bond_for_each_slave(bond, slave, iter) + bond_for_each_slave_rcu(bond, slave, iter) alb_send_learning_packets(slave, slave->dev->dev_addr); read_unlock(&bond->curr_slave_lock); @@ -1528,7 +1525,7 @@ void bond_alb_monitor(struct work_struct *work) read_lock(&bond->curr_slave_lock); - bond_for_each_slave(bond, slave, iter) { + bond_for_each_slave_rcu(bond, slave, iter) { tlb_clear_slave(bond, slave, 1); if (slave == bond->curr_active_slave) { SLAVE_TLB_INFO(slave).load = @@ -1552,11 +1549,9 @@ void bond_alb_monitor(struct work_struct *work) * dev_set_promiscuity requires rtnl and * nothing else. Avoid race with bond_close. */ - read_unlock(&bond->lock); - if (!rtnl_trylock()) { - read_lock(&bond->lock); + rcu_read_unlock(); + if (!rtnl_trylock()) goto re_arm; - } bond_info->rlb_promisc_timeout_counter = 0; @@ -1568,7 +1563,7 @@ void bond_alb_monitor(struct work_struct *work) bond_info->primary_is_promisc = 0; rtnl_unlock(); - read_lock(&bond->lock); + rcu_read_lock(); } if (bond_info->rlb_rebalance) { @@ -1590,11 +1585,9 @@ void bond_alb_monitor(struct work_struct *work) } } } - + rcu_read_unlock(); re_arm: queue_delayed_work(bond->wq, &bond->alb_work, alb_delta_in_ticks); - - read_unlock(&bond->lock); } /* assumption: called before the slave is attached to the bond @@ -1680,14 +1673,11 @@ void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char * If new_slave is NULL, caller must hold curr_slave_lock or * bond->lock for write. * - * If new_slave is not NULL, caller must hold RTNL, bond->lock for - * read and curr_slave_lock for write. Processing here may sleep, so - * no other locks may be held. + * If new_slave is not NULL, caller must hold RTNL, curr_slave_lock + * for write. Processing here may sleep, so no other locks may be held. */ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave) __releases(&bond->curr_slave_lock) - __releases(&bond->lock) - __acquires(&bond->lock) __acquires(&bond->curr_slave_lock) { struct slave *swap_slave; @@ -1723,7 +1713,6 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave tlb_clear_slave(bond, new_slave, 1); write_unlock_bh(&bond->curr_slave_lock); - read_unlock(&bond->lock); ASSERT_RTNL(); @@ -1749,11 +1738,9 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave /* swap mac address */ alb_swap_mac_addr(swap_slave, new_slave); alb_fasten_mac_swap(bond, swap_slave, new_slave); - read_lock(&bond->lock); } else { /* set the new_slave to the bond mac address */ alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr); - read_lock(&bond->lock); alb_send_learning_packets(new_slave, bond->dev->dev_addr); } diff --git a/drivers/net/bonding/bond_alb.h b/drivers/net/bonding/bond_alb.h index 4226044efd08..e09dd4bfafff 100644 --- a/drivers/net/bonding/bond_alb.h +++ b/drivers/net/bonding/bond_alb.h @@ -12,8 +12,7 @@ * for more details. * * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * with this program; if not, see <http://www.gnu.org/licenses/>. * * The full GNU General Public License is included in this distribution in the * file called LICENSE. diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 6191b551a0e8..a7db819bca92 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -79,7 +79,6 @@ #include <net/pkt_sched.h> #include <linux/rculist.h> #include <net/flow_keys.h> -#include <linux/reciprocal_div.h> #include "bonding.h" #include "bond_3ad.h" #include "bond_alb.h" @@ -87,13 +86,11 @@ /*---------------------------- Module parameters ----------------------------*/ /* monitor all links that often (in milliseconds). <=0 disables monitoring */ -#define BOND_LINK_MON_INTERV 0 -#define BOND_LINK_ARP_INTERV 0 static int max_bonds = BOND_DEFAULT_MAX_BONDS; static int tx_queues = BOND_DEFAULT_TX_QUEUES; static int num_peer_notif = 1; -static int miimon = BOND_LINK_MON_INTERV; +static int miimon; static int updelay; static int downdelay; static int use_carrier = 1; @@ -104,7 +101,7 @@ static char *lacp_rate; static int min_links; static char *ad_select; static char *xmit_hash_policy; -static int arp_interval = BOND_LINK_ARP_INTERV; +static int arp_interval; static char *arp_ip_target[BOND_MAX_ARP_TARGETS]; static char *arp_validate; static char *arp_all_targets; @@ -113,6 +110,7 @@ static int all_slaves_active; static struct bond_params bonding_defaults; static int resend_igmp = BOND_DEFAULT_RESEND_IGMP; static int packets_per_slave = 1; +static int lp_interval = BOND_ALB_DEFAULT_LP_INTERVAL; module_param(max_bonds, int, 0); MODULE_PARM_DESC(max_bonds, "Max number of bonded devices"); @@ -189,6 +187,10 @@ module_param(packets_per_slave, int, 0); MODULE_PARM_DESC(packets_per_slave, "Packets to send per slave in balance-rr " "mode; 0 for a random slave, 1 packet per " "slave (default), >1 packets per slave."); +module_param(lp_interval, uint, 0); +MODULE_PARM_DESC(lp_interval, "The number of seconds between instances where " + "the bonding driver sends learning packets to " + "each slaves peer switch. The default is 1."); /*----------------------------- Global variables ----------------------------*/ @@ -204,67 +206,6 @@ static int bond_mode = BOND_MODE_ROUNDROBIN; static int xmit_hashtype = BOND_XMIT_POLICY_LAYER2; static int lacp_fast; -const struct bond_parm_tbl bond_lacp_tbl[] = { -{ "slow", AD_LACP_SLOW}, -{ "fast", AD_LACP_FAST}, -{ NULL, -1}, -}; - -const struct bond_parm_tbl bond_mode_tbl[] = { -{ "balance-rr", BOND_MODE_ROUNDROBIN}, -{ "active-backup", BOND_MODE_ACTIVEBACKUP}, -{ "balance-xor", BOND_MODE_XOR}, -{ "broadcast", BOND_MODE_BROADCAST}, -{ "802.3ad", BOND_MODE_8023AD}, -{ "balance-tlb", BOND_MODE_TLB}, -{ "balance-alb", BOND_MODE_ALB}, -{ NULL, -1}, -}; - -const struct bond_parm_tbl xmit_hashtype_tbl[] = { -{ "layer2", BOND_XMIT_POLICY_LAYER2}, -{ "layer3+4", BOND_XMIT_POLICY_LAYER34}, -{ "layer2+3", BOND_XMIT_POLICY_LAYER23}, -{ "encap2+3", BOND_XMIT_POLICY_ENCAP23}, -{ "encap3+4", BOND_XMIT_POLICY_ENCAP34}, -{ NULL, -1}, -}; - -const struct bond_parm_tbl arp_all_targets_tbl[] = { -{ "any", BOND_ARP_TARGETS_ANY}, -{ "all", BOND_ARP_TARGETS_ALL}, -{ NULL, -1}, -}; - -const struct bond_parm_tbl arp_validate_tbl[] = { -{ "none", BOND_ARP_VALIDATE_NONE}, -{ "active", BOND_ARP_VALIDATE_ACTIVE}, -{ "backup", BOND_ARP_VALIDATE_BACKUP}, -{ "all", BOND_ARP_VALIDATE_ALL}, -{ NULL, -1}, -}; - -const struct bond_parm_tbl fail_over_mac_tbl[] = { -{ "none", BOND_FOM_NONE}, -{ "active", BOND_FOM_ACTIVE}, -{ "follow", BOND_FOM_FOLLOW}, -{ NULL, -1}, -}; - -const struct bond_parm_tbl pri_reselect_tbl[] = { -{ "always", BOND_PRI_RESELECT_ALWAYS}, -{ "better", BOND_PRI_RESELECT_BETTER}, -{ "failure", BOND_PRI_RESELECT_FAILURE}, -{ NULL, -1}, -}; - -struct bond_parm_tbl ad_select_tbl[] = { -{ "stable", BOND_AD_STABLE}, -{ "bandwidth", BOND_AD_BANDWIDTH}, -{ "count", BOND_AD_COUNT}, -{ NULL, -1}, -}; - /*-------------------------- Forward declarations ---------------------------*/ static int bond_init(struct net_device *bond_dev); @@ -299,7 +240,7 @@ const char *bond_mode_name(int mode) * @skb: hw accel VLAN tagged skb to transmit * @slave_dev: slave that is supposed to xmit this skbuff */ -int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, +void bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev) { skb->dev = slave_dev; @@ -312,8 +253,6 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, bond_netpoll_send_skb(bond_get_slave_by_dev(bond, slave_dev), skb); else dev_queue_xmit(skb); - - return 0; } /* @@ -463,6 +402,22 @@ static void bond_update_speed_duplex(struct slave *slave) return; } +const char *bond_slave_link_status(s8 link) +{ + switch (link) { + case BOND_LINK_UP: + return "up"; + case BOND_LINK_FAIL: + return "going down"; + case BOND_LINK_DOWN: + return "down"; + case BOND_LINK_BACK: + return "going back"; + default: + return "unknown"; + } +} + /* * if <dev> supports MII link status reporting, check its link status. * @@ -591,33 +546,22 @@ static int bond_set_allmulti(struct bonding *bond, int inc) * device and retransmit an IGMP JOIN request to the current active * slave. */ -static void bond_resend_igmp_join_requests(struct bonding *bond) +static void bond_resend_igmp_join_requests_delayed(struct work_struct *work) { + struct bonding *bond = container_of(work, struct bonding, + mcast_work.work); + if (!rtnl_trylock()) { queue_delayed_work(bond->wq, &bond->mcast_work, 1); return; } call_netdevice_notifiers(NETDEV_RESEND_IGMP, bond->dev); - rtnl_unlock(); - /* We use curr_slave_lock to protect against concurrent access to - * igmp_retrans from multiple running instances of this function and - * bond_change_active_slave - */ - write_lock_bh(&bond->curr_slave_lock); if (bond->igmp_retrans > 1) { bond->igmp_retrans--; queue_delayed_work(bond->wq, &bond->mcast_work, HZ/5); } - write_unlock_bh(&bond->curr_slave_lock); -} - -static void bond_resend_igmp_join_requests_delayed(struct work_struct *work) -{ - struct bonding *bond = container_of(work, struct bonding, - mcast_work.work); - - bond_resend_igmp_join_requests(bond); + rtnl_unlock(); } /* Flush bond's hardware addresses from slave @@ -697,14 +641,12 @@ static void bond_set_dev_addr(struct net_device *bond_dev, * * Perform special MAC address swapping for fail_over_mac settings * - * Called with RTNL, bond->lock for read, curr_slave_lock for write_bh. + * Called with RTNL, curr_slave_lock for write_bh. */ static void bond_do_fail_over_mac(struct bonding *bond, struct slave *new_active, struct slave *old_active) __releases(&bond->curr_slave_lock) - __releases(&bond->lock) - __acquires(&bond->lock) __acquires(&bond->curr_slave_lock) { u8 tmp_mac[ETH_ALEN]; @@ -715,9 +657,7 @@ static void bond_do_fail_over_mac(struct bonding *bond, case BOND_FOM_ACTIVE: if (new_active) { write_unlock_bh(&bond->curr_slave_lock); - read_unlock(&bond->lock); bond_set_dev_addr(bond->dev, new_active->dev); - read_lock(&bond->lock); write_lock_bh(&bond->curr_slave_lock); } break; @@ -731,7 +671,6 @@ static void bond_do_fail_over_mac(struct bonding *bond, return; write_unlock_bh(&bond->curr_slave_lock); - read_unlock(&bond->lock); if (old_active) { memcpy(tmp_mac, new_active->dev->dev_addr, ETH_ALEN); @@ -761,7 +700,6 @@ static void bond_do_fail_over_mac(struct bonding *bond, pr_err("%s: Error %d setting MAC of slave %s\n", bond->dev->name, -rv, new_active->dev->name); out: - read_lock(&bond->lock); write_lock_bh(&bond->curr_slave_lock); break; default: @@ -821,7 +759,11 @@ static struct slave *bond_find_best_slave(struct bonding *bond) static bool bond_should_notify_peers(struct bonding *bond) { - struct slave *slave = bond->curr_active_slave; + struct slave *slave; + + rcu_read_lock(); + slave = rcu_dereference(bond->curr_active_slave); + rcu_read_unlock(); pr_debug("bond_should_notify_peers: bond %s slave %s\n", bond->dev->name, slave ? slave->dev->name : "NULL"); @@ -846,8 +788,7 @@ static bool bond_should_notify_peers(struct bonding *bond) * because it is apparently the best available slave we have, even though its * updelay hasn't timed out yet. * - * If new_active is not NULL, caller must hold bond->lock for read and - * curr_slave_lock for write_bh. + * If new_active is not NULL, caller must hold curr_slave_lock for write_bh. */ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) { @@ -916,14 +857,12 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) } write_unlock_bh(&bond->curr_slave_lock); - read_unlock(&bond->lock); call_netdevice_notifiers(NETDEV_BONDING_FAILOVER, bond->dev); if (should_notify_peers) call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, bond->dev); - read_lock(&bond->lock); write_lock_bh(&bond->curr_slave_lock); } } @@ -949,7 +888,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) * - The primary_slave has got its link back. * - A slave has got its link back and there's no old curr_active_slave. * - * Caller must hold bond->lock for read and curr_slave_lock for write_bh. + * Caller must hold curr_slave_lock for write_bh. */ void bond_select_active_slave(struct bonding *bond) { @@ -1589,16 +1528,20 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) goto err_unregister; } + res = bond_sysfs_slave_add(new_slave); + if (res) { + pr_debug("Error %d calling bond_sysfs_slave_add\n", res); + goto err_upper_unlink; + } + bond->slave_cnt++; bond_compute_features(bond); bond_set_carrier(bond); if (USES_PRIMARY(bond->params.mode)) { - read_lock(&bond->lock); write_lock_bh(&bond->curr_slave_lock); bond_select_active_slave(bond); write_unlock_bh(&bond->curr_slave_lock); - read_unlock(&bond->lock); } pr_info("%s: enslaving %s as a%s interface with a%s link.\n", @@ -1610,6 +1553,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) return 0; /* Undo stages on error */ +err_upper_unlink: + bond_upper_dev_unlink(bond_dev, slave_dev); + err_unregister: netdev_rx_handler_unregister(slave_dev); @@ -1618,19 +1564,13 @@ err_detach: bond_hw_addr_flush(bond_dev, slave_dev); vlan_vids_del_by_dev(slave_dev, bond_dev); - write_lock_bh(&bond->lock); if (bond->primary_slave == new_slave) bond->primary_slave = NULL; if (bond->curr_active_slave == new_slave) { - bond_change_active_slave(bond, NULL); - write_unlock_bh(&bond->lock); - read_lock(&bond->lock); write_lock_bh(&bond->curr_slave_lock); + bond_change_active_slave(bond, NULL); bond_select_active_slave(bond); write_unlock_bh(&bond->curr_slave_lock); - read_unlock(&bond->lock); - } else { - write_unlock_bh(&bond->lock); } slave_disable_netpoll(new_slave); @@ -1658,7 +1598,7 @@ err_free: err_undo_flags: /* Enslave of first slave has failed and we need to fix master's mac */ if (!bond_has_slaves(bond) && - ether_addr_equal(bond_dev->dev_addr, slave_dev->dev_addr)) + ether_addr_equal_64bits(bond_dev->dev_addr, slave_dev->dev_addr)) eth_hw_addr_random(bond_dev); return res; @@ -1695,23 +1635,21 @@ static int __bond_release_one(struct net_device *bond_dev, } block_netpoll_tx(); - write_lock_bh(&bond->lock); slave = bond_get_slave_by_dev(bond, slave_dev); if (!slave) { /* not a slave of this bond */ pr_info("%s: %s not enslaved\n", bond_dev->name, slave_dev->name); - write_unlock_bh(&bond->lock); unblock_netpoll_tx(); return -EINVAL; } - write_unlock_bh(&bond->lock); - /* release the slave from its bond */ bond->slave_cnt--; + bond_sysfs_slave_del(slave); + bond_upper_dev_unlink(bond_dev, slave_dev); /* unregister rx_handler early so bond_handle_frame wouldn't be called * for this slave anymore. @@ -1720,12 +1658,10 @@ static int __bond_release_one(struct net_device *bond_dev, write_lock_bh(&bond->lock); /* Inform AD package of unbinding of slave. */ - if (bond->params.mode == BOND_MODE_8023AD) { - /* must be called before the slave is - * detached from the list - */ + if (bond->params.mode == BOND_MODE_8023AD) bond_3ad_unbind_slave(slave); - } + + write_unlock_bh(&bond->lock); pr_info("%s: releasing %s interface %s\n", bond_dev->name, @@ -1737,7 +1673,7 @@ static int __bond_release_one(struct net_device *bond_dev, bond->current_arp_slave = NULL; if (!all && !bond->params.fail_over_mac) { - if (ether_addr_equal(bond_dev->dev_addr, slave->perm_hwaddr) && + if (ether_addr_equal_64bits(bond_dev->dev_addr, slave->perm_hwaddr) && bond_has_slaves(bond)) pr_warn("%s: Warning: the permanent HWaddr of %s - %pM - is still in use by %s. Set the HWaddr of %s to a different address to avoid conflicts.\n", bond_dev->name, slave_dev->name, @@ -1748,8 +1684,11 @@ static int __bond_release_one(struct net_device *bond_dev, if (bond->primary_slave == slave) bond->primary_slave = NULL; - if (oldcurrent == slave) + if (oldcurrent == slave) { + write_lock_bh(&bond->curr_slave_lock); bond_change_active_slave(bond, NULL); + write_unlock_bh(&bond->curr_slave_lock); + } if (bond_is_lb(bond)) { /* Must be called only after the slave has been @@ -1757,9 +1696,7 @@ static int __bond_release_one(struct net_device *bond_dev, * has been cleared (if our_slave == old_current), * but before a new active slave is selected. */ - write_unlock_bh(&bond->lock); bond_alb_deinit_slave(bond, slave); - write_lock_bh(&bond->lock); } if (all) { @@ -1770,15 +1707,11 @@ static int __bond_release_one(struct net_device *bond_dev, * is no concern that another slave add/remove event * will interfere. */ - write_unlock_bh(&bond->lock); - read_lock(&bond->lock); write_lock_bh(&bond->curr_slave_lock); bond_select_active_slave(bond); write_unlock_bh(&bond->curr_slave_lock); - read_unlock(&bond->lock); - write_lock_bh(&bond->lock); } if (!bond_has_slaves(bond)) { @@ -1793,7 +1726,6 @@ static int __bond_release_one(struct net_device *bond_dev, } } - write_unlock_bh(&bond->lock); unblock_netpoll_tx(); synchronize_rcu(); @@ -1928,7 +1860,7 @@ static int bond_miimon_inspect(struct bonding *bond) ignore_updelay = !bond->curr_active_slave ? true : false; - bond_for_each_slave(bond, slave, iter) { + bond_for_each_slave_rcu(bond, slave, iter) { slave->new_link = BOND_LINK_NOCHANGE; link_state = bond_check_dev_link(bond, slave->dev, 0); @@ -2119,48 +2051,42 @@ do_failover: * an acquisition of appropriate locks followed by a commit phase to * implement whatever link state changes are indicated. */ -void bond_mii_monitor(struct work_struct *work) +static void bond_mii_monitor(struct work_struct *work) { struct bonding *bond = container_of(work, struct bonding, mii_work.work); bool should_notify_peers = false; unsigned long delay; - read_lock(&bond->lock); - delay = msecs_to_jiffies(bond->params.miimon); if (!bond_has_slaves(bond)) goto re_arm; + rcu_read_lock(); + should_notify_peers = bond_should_notify_peers(bond); if (bond_miimon_inspect(bond)) { - read_unlock(&bond->lock); + rcu_read_unlock(); /* Race avoidance with bond_close cancel of workqueue */ if (!rtnl_trylock()) { - read_lock(&bond->lock); delay = 1; should_notify_peers = false; goto re_arm; } - read_lock(&bond->lock); - bond_miimon_commit(bond); - read_unlock(&bond->lock); rtnl_unlock(); /* might sleep, hold no other locks */ - read_lock(&bond->lock); - } + } else + rcu_read_unlock(); re_arm: if (bond->params.miimon) queue_delayed_work(bond->wq, &bond->mii_work, delay); - read_unlock(&bond->lock); - if (should_notify_peers) { if (!rtnl_trylock()) return; @@ -2414,7 +2340,7 @@ static bool bond_time_in_interval(struct bonding *bond, unsigned long last_act, * arp is transmitted to generate traffic. see activebackup_arp_monitor for * arp monitoring in active backup mode. */ -void bond_loadbalance_arp_mon(struct work_struct *work) +static void bond_loadbalance_arp_mon(struct work_struct *work) { struct bonding *bond = container_of(work, struct bonding, arp_work.work); @@ -2422,12 +2348,12 @@ void bond_loadbalance_arp_mon(struct work_struct *work) struct list_head *iter; int do_failover = 0; - read_lock(&bond->lock); - if (!bond_has_slaves(bond)) goto re_arm; - oldcurrent = bond->curr_active_slave; + rcu_read_lock(); + + oldcurrent = ACCESS_ONCE(bond->curr_active_slave); /* see if any of the previous devices are up now (i.e. they have * xmt and rcv traffic). the curr_active_slave does not come into * the picture unless it is null. also, slave->jiffies is not needed @@ -2436,7 +2362,7 @@ void bond_loadbalance_arp_mon(struct work_struct *work) * TODO: what about up/down delay in arp mode? it wasn't here before * so it can wait */ - bond_for_each_slave(bond, slave, iter) { + bond_for_each_slave_rcu(bond, slave, iter) { unsigned long trans_start = dev_trans_start(slave->dev); if (slave->link != BOND_LINK_UP) { @@ -2498,7 +2424,14 @@ void bond_loadbalance_arp_mon(struct work_struct *work) bond_arp_send_all(bond, slave); } + rcu_read_unlock(); + if (do_failover) { + /* the bond_select_active_slave must hold RTNL + * and curr_slave_lock for write. + */ + if (!rtnl_trylock()) + goto re_arm; block_netpoll_tx(); write_lock_bh(&bond->curr_slave_lock); @@ -2506,14 +2439,13 @@ void bond_loadbalance_arp_mon(struct work_struct *work) write_unlock_bh(&bond->curr_slave_lock); unblock_netpoll_tx(); + rtnl_unlock(); } re_arm: if (bond->params.arp_interval) queue_delayed_work(bond->wq, &bond->arp_work, msecs_to_jiffies(bond->params.arp_interval)); - - read_unlock(&bond->lock); } /* @@ -2522,7 +2454,7 @@ re_arm: * place for the slave. Returns 0 if no changes are found, >0 if changes * to link states must be committed. * - * Called with bond->lock held for read. + * Called with rcu_read_lock hold. */ static int bond_ab_arp_inspect(struct bonding *bond) { @@ -2531,7 +2463,7 @@ static int bond_ab_arp_inspect(struct bonding *bond) struct slave *slave; int commit = 0; - bond_for_each_slave(bond, slave, iter) { + bond_for_each_slave_rcu(bond, slave, iter) { slave->new_link = BOND_LINK_NOCHANGE; last_rx = slave_last_rx(bond, slave); @@ -2593,7 +2525,7 @@ static int bond_ab_arp_inspect(struct bonding *bond) * Called to commit link state changes noted by inspection step of * active-backup mode ARP monitor. * - * Called with RTNL and bond->lock for read. + * Called with RTNL hold. */ static void bond_ab_arp_commit(struct bonding *bond) { @@ -2668,19 +2600,20 @@ do_failover: /* * Send ARP probes for active-backup mode ARP monitor. * - * Called with bond->lock held for read. + * Called with rcu_read_lock hold. */ static void bond_ab_arp_probe(struct bonding *bond) { - struct slave *slave, *before = NULL, *new_slave = NULL; + struct slave *slave, *before = NULL, *new_slave = NULL, + *curr_arp_slave = rcu_dereference(bond->current_arp_slave); struct list_head *iter; bool found = false; read_lock(&bond->curr_slave_lock); - if (bond->current_arp_slave && bond->curr_active_slave) + if (curr_arp_slave && bond->curr_active_slave) pr_info("PROBE: c_arp %s && cas %s BAD\n", - bond->current_arp_slave->dev->name, + curr_arp_slave->dev->name, bond->curr_active_slave->dev->name); if (bond->curr_active_slave) { @@ -2696,15 +2629,15 @@ static void bond_ab_arp_probe(struct bonding *bond) * for becoming the curr_active_slave */ - if (!bond->current_arp_slave) { - bond->current_arp_slave = bond_first_slave(bond); - if (!bond->current_arp_slave) + if (!curr_arp_slave) { + curr_arp_slave = bond_first_slave_rcu(bond); + if (!curr_arp_slave) return; } - bond_set_slave_inactive_flags(bond->current_arp_slave); + bond_set_slave_inactive_flags(curr_arp_slave); - bond_for_each_slave(bond, slave, iter) { + bond_for_each_slave_rcu(bond, slave, iter) { if (!found && !before && IS_UP(slave->dev)) before = slave; @@ -2727,7 +2660,7 @@ static void bond_ab_arp_probe(struct bonding *bond) pr_info("%s: backup interface %s is now down.\n", bond->dev->name, slave->dev->name); } - if (slave == bond->current_arp_slave) + if (slave == curr_arp_slave) found = true; } @@ -2741,54 +2674,48 @@ static void bond_ab_arp_probe(struct bonding *bond) bond_set_slave_active_flags(new_slave); bond_arp_send_all(bond, new_slave); new_slave->jiffies = jiffies; - bond->current_arp_slave = new_slave; - + rcu_assign_pointer(bond->current_arp_slave, new_slave); } -void bond_activebackup_arp_mon(struct work_struct *work) +static void bond_activebackup_arp_mon(struct work_struct *work) { struct bonding *bond = container_of(work, struct bonding, arp_work.work); bool should_notify_peers = false; int delta_in_ticks; - read_lock(&bond->lock); - delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval); if (!bond_has_slaves(bond)) goto re_arm; + rcu_read_lock(); + should_notify_peers = bond_should_notify_peers(bond); if (bond_ab_arp_inspect(bond)) { - read_unlock(&bond->lock); + rcu_read_unlock(); /* Race avoidance with bond_close flush of workqueue */ if (!rtnl_trylock()) { - read_lock(&bond->lock); delta_in_ticks = 1; should_notify_peers = false; goto re_arm; } - read_lock(&bond->lock); - bond_ab_arp_commit(bond); - read_unlock(&bond->lock); rtnl_unlock(); - read_lock(&bond->lock); + rcu_read_lock(); } bond_ab_arp_probe(bond); + rcu_read_unlock(); re_arm: if (bond->params.arp_interval) queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks); - read_unlock(&bond->lock); - if (should_notify_peers) { if (!rtnl_trylock()) return; @@ -2896,9 +2823,27 @@ static int bond_slave_netdev_event(unsigned long event, */ break; case NETDEV_CHANGENAME: - /* - * TODO: handle changing the primary's name - */ + /* we don't care if we don't have primary set */ + if (!USES_PRIMARY(bond->params.mode) || + !bond->params.primary[0]) + break; + + if (slave == bond->primary_slave) { + /* slave's name changed - he's no longer primary */ + bond->primary_slave = NULL; + } else if (!strcmp(slave_dev->name, bond->params.primary)) { + /* we have a new primary slave */ + bond->primary_slave = slave; + } else { /* we didn't change primary - exit */ + break; + } + + pr_info("%s: Primary slave changed to %s, reselecting active slave.\n", + bond->dev->name, bond->primary_slave ? slave_dev->name : + "none"); + write_lock_bh(&bond->curr_slave_lock); + bond_select_active_slave(bond); + write_unlock_bh(&bond->curr_slave_lock); break; case NETDEV_FEAT_CHANGE: bond_compute_features(bond); @@ -3178,6 +3123,7 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd struct ifslave k_sinfo; struct ifslave __user *u_sinfo = NULL; struct mii_ioctl_data *mii = NULL; + struct bond_opt_value newval; struct net *net; int res = 0; @@ -3249,37 +3195,35 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) return -EPERM; - slave_dev = dev_get_by_name(net, ifr->ifr_slave); + slave_dev = __dev_get_by_name(net, ifr->ifr_slave); pr_debug("slave_dev=%p:\n", slave_dev); if (!slave_dev) - res = -ENODEV; - else { - pr_debug("slave_dev->name=%s:\n", slave_dev->name); - switch (cmd) { - case BOND_ENSLAVE_OLD: - case SIOCBONDENSLAVE: - res = bond_enslave(bond_dev, slave_dev); - break; - case BOND_RELEASE_OLD: - case SIOCBONDRELEASE: - res = bond_release(bond_dev, slave_dev); - break; - case BOND_SETHWADDR_OLD: - case SIOCBONDSETHWADDR: - bond_set_dev_addr(bond_dev, slave_dev); - res = 0; - break; - case BOND_CHANGE_ACTIVE_OLD: - case SIOCBONDCHANGEACTIVE: - res = bond_option_active_slave_set(bond, slave_dev); - break; - default: - res = -EOPNOTSUPP; - } + return -ENODEV; - dev_put(slave_dev); + pr_debug("slave_dev->name=%s:\n", slave_dev->name); + switch (cmd) { + case BOND_ENSLAVE_OLD: + case SIOCBONDENSLAVE: + res = bond_enslave(bond_dev, slave_dev); + break; + case BOND_RELEASE_OLD: + case SIOCBONDRELEASE: + res = bond_release(bond_dev, slave_dev); + break; + case BOND_SETHWADDR_OLD: + case SIOCBONDSETHWADDR: + bond_set_dev_addr(bond_dev, slave_dev); + res = 0; + break; + case BOND_CHANGE_ACTIVE_OLD: + case SIOCBONDCHANGEACTIVE: + bond_opt_initstr(&newval, slave_dev->name); + res = __bond_opt_set(bond, BOND_OPT_ACTIVE_SLAVE, &newval); + break; + default: + res = -EOPNOTSUPP; } return res; @@ -3550,7 +3494,7 @@ unwind: * it fails, it tries to find the first available slave for transmission. * The skb is consumed in all cases, thus the function is void. */ -void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int slave_id) +static void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int slave_id) { struct list_head *iter; struct slave *slave; @@ -3590,8 +3534,9 @@ void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int slave_id) */ static u32 bond_rr_gen_slave_id(struct bonding *bond) { - int packets_per_slave = bond->params.packets_per_slave; u32 slave_id; + struct reciprocal_value reciprocal_packets_per_slave; + int packets_per_slave = bond->params.packets_per_slave; switch (packets_per_slave) { case 0: @@ -3601,8 +3546,10 @@ static u32 bond_rr_gen_slave_id(struct bonding *bond) slave_id = bond->rr_tx_counter; break; default: + reciprocal_packets_per_slave = + bond->params.reciprocal_packets_per_slave; slave_id = reciprocal_divide(bond->rr_tx_counter, - packets_per_slave); + reciprocal_packets_per_slave); break; } bond->rr_tx_counter++; @@ -3707,28 +3654,24 @@ static inline int bond_slave_override(struct bonding *bond, struct sk_buff *skb) { struct slave *slave = NULL; - struct slave *check_slave; struct list_head *iter; - int res = 1; if (!skb->queue_mapping) return 1; /* Find out if any slaves have the same mapping as this skb. */ - bond_for_each_slave_rcu(bond, check_slave, iter) { - if (check_slave->queue_id == skb->queue_mapping) { - slave = check_slave; + bond_for_each_slave_rcu(bond, slave, iter) { + if (slave->queue_id == skb->queue_mapping) { + if (slave_can_tx(slave)) { + bond_dev_queue_xmit(bond, skb, slave->dev); + return 0; + } + /* If the slave isn't UP, use default transmit policy. */ break; } } - /* If the slave isn't UP, use default transmit policy. */ - if (slave && slave->queue_id && IS_UP(slave->dev) && - (slave->link == BOND_LINK_UP)) { - res = bond_dev_queue_xmit(bond, skb, slave->dev); - } - - return res; + return 1; } @@ -3941,6 +3884,9 @@ void bond_setup(struct net_device *bond_dev) * capable */ + /* Don't allow bond devices to change network namespaces. */ + bond_dev->features |= NETIF_F_NETNS_LOCAL; + bond_dev->hw_features = BOND_VLAN_FEATURES | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | @@ -3974,6 +3920,29 @@ static void bond_uninit(struct net_device *bond_dev) /*------------------------- Module initialization ---------------------------*/ +int bond_parm_tbl_lookup(int mode, const struct bond_parm_tbl *tbl) +{ + int i; + + for (i = 0; tbl[i].modename; i++) + if (mode == tbl[i].mode) + return tbl[i].mode; + + return -1; +} + +static int bond_parm_tbl_lookup_name(const char *modename, + const struct bond_parm_tbl *tbl) +{ + int i; + + for (i = 0; tbl[i].modename; i++) + if (strcmp(modename, tbl[i].modename) == 0) + return tbl[i].mode; + + return -1; +} + /* * Convert string input module parms. Accept either the * number of the mode or its string name. A bit complicated because @@ -3982,27 +3951,17 @@ static void bond_uninit(struct net_device *bond_dev) */ int bond_parse_parm(const char *buf, const struct bond_parm_tbl *tbl) { - int modeint = -1, i, rv; - char *p, modestr[BOND_MAX_MODENAME_LEN + 1] = { 0, }; + int modeint; + char *p, modestr[BOND_MAX_MODENAME_LEN + 1]; for (p = (char *)buf; *p; p++) if (!(isdigit(*p) || isspace(*p))) break; - if (*p) - rv = sscanf(buf, "%20s", modestr); - else - rv = sscanf(buf, "%d", &modeint); - - if (!rv) - return -1; - - for (i = 0; tbl[i].modename; i++) { - if (modeint == tbl[i].mode) - return tbl[i].mode; - if (strcmp(modestr, tbl[i].modename) == 0) - return tbl[i].mode; - } + if (*p && sscanf(buf, "%20s", modestr) != 0) + return bond_parm_tbl_lookup_name(modestr, tbl); + else if (sscanf(buf, "%d", &modeint) != 0) + return bond_parm_tbl_lookup(modeint, tbl); return -1; } @@ -4010,18 +3969,20 @@ int bond_parse_parm(const char *buf, const struct bond_parm_tbl *tbl) static int bond_check_params(struct bond_params *params) { int arp_validate_value, fail_over_mac_value, primary_reselect_value, i; + struct bond_opt_value newval, *valptr; int arp_all_targets_value; /* * Convert string parameters. */ if (mode) { - bond_mode = bond_parse_parm(mode, bond_mode_tbl); - if (bond_mode == -1) { - pr_err("Error: Invalid bonding mode \"%s\"\n", - mode == NULL ? "NULL" : mode); + bond_opt_initstr(&newval, mode); + valptr = bond_opt_parse(bond_opt_get(BOND_OPT_MODE), &newval); + if (!valptr) { + pr_err("Error: Invalid bonding mode \"%s\"\n", mode); return -EINVAL; } + bond_mode = valptr->value; } if (xmit_hash_policy) { @@ -4030,14 +3991,15 @@ static int bond_check_params(struct bond_params *params) pr_info("xmit_hash_policy param is irrelevant in mode %s\n", bond_mode_name(bond_mode)); } else { - xmit_hashtype = bond_parse_parm(xmit_hash_policy, - xmit_hashtype_tbl); - if (xmit_hashtype == -1) { + bond_opt_initstr(&newval, xmit_hash_policy); + valptr = bond_opt_parse(bond_opt_get(BOND_OPT_XMIT_HASH), + &newval); + if (!valptr) { pr_err("Error: Invalid xmit_hash_policy \"%s\"\n", - xmit_hash_policy == NULL ? "NULL" : xmit_hash_policy); return -EINVAL; } + xmit_hashtype = valptr->value; } } @@ -4046,26 +4008,29 @@ static int bond_check_params(struct bond_params *params) pr_info("lacp_rate param is irrelevant in mode %s\n", bond_mode_name(bond_mode)); } else { - lacp_fast = bond_parse_parm(lacp_rate, bond_lacp_tbl); - if (lacp_fast == -1) { + bond_opt_initstr(&newval, lacp_rate); + valptr = bond_opt_parse(bond_opt_get(BOND_OPT_LACP_RATE), + &newval); + if (!valptr) { pr_err("Error: Invalid lacp rate \"%s\"\n", - lacp_rate == NULL ? "NULL" : lacp_rate); + lacp_rate); return -EINVAL; } + lacp_fast = valptr->value; } } if (ad_select) { - params->ad_select = bond_parse_parm(ad_select, ad_select_tbl); - if (params->ad_select == -1) { - pr_err("Error: Invalid ad_select \"%s\"\n", - ad_select == NULL ? "NULL" : ad_select); + bond_opt_initstr(&newval, lacp_rate); + valptr = bond_opt_parse(bond_opt_get(BOND_OPT_AD_SELECT), + &newval); + if (!valptr) { + pr_err("Error: Invalid ad_select \"%s\"\n", ad_select); return -EINVAL; } - - if (bond_mode != BOND_MODE_8023AD) { + params->ad_select = valptr->value; + if (bond_mode != BOND_MODE_8023AD) pr_warning("ad_select param only affects 802.3ad mode\n"); - } } else { params->ad_select = BOND_AD_STABLE; } @@ -4077,9 +4042,9 @@ static int bond_check_params(struct bond_params *params) } if (miimon < 0) { - pr_warning("Warning: miimon module parameter (%d), not in range 0-%d, so it was reset to %d\n", - miimon, INT_MAX, BOND_LINK_MON_INTERV); - miimon = BOND_LINK_MON_INTERV; + pr_warning("Warning: miimon module parameter (%d), not in range 0-%d, so it was reset to 0\n", + miimon, INT_MAX); + miimon = 0; } if (updelay < 0) { @@ -4106,8 +4071,8 @@ static int bond_check_params(struct bond_params *params) num_peer_notif = 1; } - /* reset values for 802.3ad */ - if (bond_mode == BOND_MODE_8023AD) { + /* reset values for 802.3ad/TLB/ALB */ + if (BOND_NO_USES_ARP(bond_mode)) { if (!miimon) { pr_warning("Warning: miimon must be specified, otherwise bonding will not detect link failure, speed and duplex which are essential for 802.3ad operation\n"); pr_warning("Forcing miimon to 100msec\n"); @@ -4136,22 +4101,13 @@ static int bond_check_params(struct bond_params *params) resend_igmp = BOND_DEFAULT_RESEND_IGMP; } - if (packets_per_slave < 0 || packets_per_slave > USHRT_MAX) { + bond_opt_initval(&newval, packets_per_slave); + if (!bond_opt_parse(bond_opt_get(BOND_OPT_PACKETS_PER_SLAVE), &newval)) { pr_warn("Warning: packets_per_slave (%d) should be between 0 and %u resetting to 1\n", packets_per_slave, USHRT_MAX); packets_per_slave = 1; } - /* reset values for TLB/ALB */ - if ((bond_mode == BOND_MODE_TLB) || - (bond_mode == BOND_MODE_ALB)) { - if (!miimon) { - pr_warning("Warning: miimon must be specified, otherwise bonding will not detect link failure and link speed which are essential for TLB/ALB load balancing\n"); - pr_warning("Forcing miimon to 100msec\n"); - miimon = BOND_DEFAULT_MIIMON; - } - } - if (bond_mode == BOND_MODE_ALB) { pr_notice("In ALB mode you might experience client disconnections upon reconnection of a link if the bonding module updelay parameter (%d msec) is incompatible with the forwarding delay time of the switch\n", updelay); @@ -4191,9 +4147,9 @@ static int bond_check_params(struct bond_params *params) } if (arp_interval < 0) { - pr_warning("Warning: arp_interval module parameter (%d) , not in range 0-%d, so it was reset to %d\n", - arp_interval, INT_MAX, BOND_LINK_ARP_INTERV); - arp_interval = BOND_LINK_ARP_INTERV; + pr_warning("Warning: arp_interval module parameter (%d) , not in range 0-%d, so it was reset to 0\n", + arp_interval, INT_MAX); + arp_interval = 0; } for (arp_ip_count = 0, i = 0; @@ -4232,35 +4188,40 @@ static int bond_check_params(struct bond_params *params) return -EINVAL; } - arp_validate_value = bond_parse_parm(arp_validate, - arp_validate_tbl); - if (arp_validate_value == -1) { + bond_opt_initstr(&newval, arp_validate); + valptr = bond_opt_parse(bond_opt_get(BOND_OPT_ARP_VALIDATE), + &newval); + if (!valptr) { pr_err("Error: invalid arp_validate \"%s\"\n", - arp_validate == NULL ? "NULL" : arp_validate); + arp_validate); return -EINVAL; } - } else + arp_validate_value = valptr->value; + } else { arp_validate_value = 0; + } arp_all_targets_value = 0; if (arp_all_targets) { - arp_all_targets_value = bond_parse_parm(arp_all_targets, - arp_all_targets_tbl); - - if (arp_all_targets_value == -1) { + bond_opt_initstr(&newval, arp_all_targets); + valptr = bond_opt_parse(bond_opt_get(BOND_OPT_ARP_ALL_TARGETS), + &newval); + if (!valptr) { pr_err("Error: invalid arp_all_targets_value \"%s\"\n", arp_all_targets); arp_all_targets_value = 0; + } else { + arp_all_targets_value = valptr->value; } } if (miimon) { pr_info("MII link monitoring set to %d ms\n", miimon); } else if (arp_interval) { + valptr = bond_opt_get_val(BOND_OPT_ARP_VALIDATE, + arp_validate_value); pr_info("ARP monitoring set to %d ms, validate %s, with %d target(s):", - arp_interval, - arp_validate_tbl[arp_validate_value].modename, - arp_ip_count); + arp_interval, valptr->string, arp_ip_count); for (i = 0; i < arp_ip_count; i++) pr_info(" %s", arp_ip_target[i]); @@ -4284,33 +4245,41 @@ static int bond_check_params(struct bond_params *params) } if (primary && primary_reselect) { - primary_reselect_value = bond_parse_parm(primary_reselect, - pri_reselect_tbl); - if (primary_reselect_value == -1) { + bond_opt_initstr(&newval, primary_reselect); + valptr = bond_opt_parse(bond_opt_get(BOND_OPT_PRIMARY_RESELECT), + &newval); + if (!valptr) { pr_err("Error: Invalid primary_reselect \"%s\"\n", - primary_reselect == - NULL ? "NULL" : primary_reselect); + primary_reselect); return -EINVAL; } + primary_reselect_value = valptr->value; } else { primary_reselect_value = BOND_PRI_RESELECT_ALWAYS; } if (fail_over_mac) { - fail_over_mac_value = bond_parse_parm(fail_over_mac, - fail_over_mac_tbl); - if (fail_over_mac_value == -1) { + bond_opt_initstr(&newval, fail_over_mac); + valptr = bond_opt_parse(bond_opt_get(BOND_OPT_FAIL_OVER_MAC), + &newval); + if (!valptr) { pr_err("Error: invalid fail_over_mac \"%s\"\n", - arp_validate == NULL ? "NULL" : arp_validate); + fail_over_mac); return -EINVAL; } - + fail_over_mac_value = valptr->value; if (bond_mode != BOND_MODE_ACTIVEBACKUP) pr_warning("Warning: fail_over_mac only affects active-backup mode.\n"); } else { fail_over_mac_value = BOND_FOM_NONE; } + if (lp_interval == 0) { + pr_warning("Warning: ip_interval must be between 1 and %d, so it was reset to %d\n", + INT_MAX, BOND_ALB_DEFAULT_LP_INTERVAL); + lp_interval = BOND_ALB_DEFAULT_LP_INTERVAL; + } + /* fill params struct with the proper values */ params->mode = bond_mode; params->xmit_policy = xmit_hashtype; @@ -4330,11 +4299,19 @@ static int bond_check_params(struct bond_params *params) params->all_slaves_active = all_slaves_active; params->resend_igmp = resend_igmp; params->min_links = min_links; - params->lp_interval = BOND_ALB_DEFAULT_LP_INTERVAL; - if (packets_per_slave > 1) - params->packets_per_slave = reciprocal_value(packets_per_slave); - else - params->packets_per_slave = packets_per_slave; + params->lp_interval = lp_interval; + params->packets_per_slave = packets_per_slave; + if (packets_per_slave > 0) { + params->reciprocal_packets_per_slave = + reciprocal_value(packets_per_slave); + } else { + /* reciprocal_packets_per_slave is unused if + * packets_per_slave is 0 or 1, just initialize it + */ + params->reciprocal_packets_per_slave = + (struct reciprocal_value) { 0 }; + } + if (primary) { strncpy(params->primary, primary, IFNAMSIZ); params->primary[IFNAMSIZ - 1] = 0; diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index 40e7b1cb4aea..70651f8e8e3b 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -1,6 +1,7 @@ /* * drivers/net/bond/bond_netlink.c - Netlink interface for bonding * Copyright (c) 2013 Jiri Pirko <jiri@resnulli.us> + * Copyright (c) 2013 Scott Feldman <sfeldma@cumulusnetworks.com> * * 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 @@ -20,9 +21,81 @@ #include <net/rtnetlink.h> #include "bonding.h" +static size_t bond_get_slave_size(const struct net_device *bond_dev, + const struct net_device *slave_dev) +{ + return nla_total_size(sizeof(u8)) + /* IFLA_BOND_SLAVE_STATE */ + nla_total_size(sizeof(u8)) + /* IFLA_BOND_SLAVE_MII_STATUS */ + nla_total_size(sizeof(u32)) + /* IFLA_BOND_SLAVE_LINK_FAILURE_COUNT */ + nla_total_size(MAX_ADDR_LEN) + /* IFLA_BOND_SLAVE_PERM_HWADDR */ + nla_total_size(sizeof(u16)) + /* IFLA_BOND_SLAVE_QUEUE_ID */ + nla_total_size(sizeof(u16)) + /* IFLA_BOND_SLAVE_AD_AGGREGATOR_ID */ + 0; +} + +static int bond_fill_slave_info(struct sk_buff *skb, + const struct net_device *bond_dev, + const struct net_device *slave_dev) +{ + struct slave *slave = bond_slave_get_rtnl(slave_dev); + + if (nla_put_u8(skb, IFLA_BOND_SLAVE_STATE, bond_slave_state(slave))) + goto nla_put_failure; + + if (nla_put_u8(skb, IFLA_BOND_SLAVE_MII_STATUS, slave->link)) + goto nla_put_failure; + + if (nla_put_u32(skb, IFLA_BOND_SLAVE_LINK_FAILURE_COUNT, + slave->link_failure_count)) + goto nla_put_failure; + + if (nla_put(skb, IFLA_BOND_SLAVE_PERM_HWADDR, + slave_dev->addr_len, slave->perm_hwaddr)) + goto nla_put_failure; + + if (nla_put_u16(skb, IFLA_BOND_SLAVE_QUEUE_ID, slave->queue_id)) + goto nla_put_failure; + + if (slave->bond->params.mode == BOND_MODE_8023AD) { + const struct aggregator *agg; + + agg = SLAVE_AD_INFO(slave).port.aggregator; + if (agg) + if (nla_put_u16(skb, IFLA_BOND_SLAVE_AD_AGGREGATOR_ID, + agg->aggregator_identifier)) + goto nla_put_failure; + } + + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + static const struct nla_policy bond_policy[IFLA_BOND_MAX + 1] = { [IFLA_BOND_MODE] = { .type = NLA_U8 }, [IFLA_BOND_ACTIVE_SLAVE] = { .type = NLA_U32 }, + [IFLA_BOND_MIIMON] = { .type = NLA_U32 }, + [IFLA_BOND_UPDELAY] = { .type = NLA_U32 }, + [IFLA_BOND_DOWNDELAY] = { .type = NLA_U32 }, + [IFLA_BOND_USE_CARRIER] = { .type = NLA_U8 }, + [IFLA_BOND_ARP_INTERVAL] = { .type = NLA_U32 }, + [IFLA_BOND_ARP_IP_TARGET] = { .type = NLA_NESTED }, + [IFLA_BOND_ARP_VALIDATE] = { .type = NLA_U32 }, + [IFLA_BOND_ARP_ALL_TARGETS] = { .type = NLA_U32 }, + [IFLA_BOND_PRIMARY] = { .type = NLA_U32 }, + [IFLA_BOND_PRIMARY_RESELECT] = { .type = NLA_U8 }, + [IFLA_BOND_FAIL_OVER_MAC] = { .type = NLA_U8 }, + [IFLA_BOND_XMIT_HASH_POLICY] = { .type = NLA_U8 }, + [IFLA_BOND_RESEND_IGMP] = { .type = NLA_U32 }, + [IFLA_BOND_NUM_PEER_NOTIF] = { .type = NLA_U8 }, + [IFLA_BOND_ALL_SLAVES_ACTIVE] = { .type = NLA_U8 }, + [IFLA_BOND_MIN_LINKS] = { .type = NLA_U32 }, + [IFLA_BOND_LP_INTERVAL] = { .type = NLA_U32 }, + [IFLA_BOND_PACKETS_PER_SLAVE] = { .type = NLA_U32 }, + [IFLA_BOND_AD_LACP_RATE] = { .type = NLA_U8 }, + [IFLA_BOND_AD_SELECT] = { .type = NLA_U8 }, + [IFLA_BOND_AD_INFO] = { .type = NLA_NESTED }, }; static int bond_validate(struct nlattr *tb[], struct nlattr *data[]) @@ -40,28 +113,238 @@ static int bond_changelink(struct net_device *bond_dev, struct nlattr *tb[], struct nlattr *data[]) { struct bonding *bond = netdev_priv(bond_dev); + struct bond_opt_value newval; + int miimon = 0; int err; - if (data && data[IFLA_BOND_MODE]) { + if (!data) + return 0; + + if (data[IFLA_BOND_MODE]) { int mode = nla_get_u8(data[IFLA_BOND_MODE]); - err = bond_option_mode_set(bond, mode); + bond_opt_initval(&newval, mode); + err = __bond_opt_set(bond, BOND_OPT_MODE, &newval); if (err) return err; } - if (data && data[IFLA_BOND_ACTIVE_SLAVE]) { + if (data[IFLA_BOND_ACTIVE_SLAVE]) { int ifindex = nla_get_u32(data[IFLA_BOND_ACTIVE_SLAVE]); struct net_device *slave_dev; + char *active_slave = ""; - if (ifindex == 0) { - slave_dev = NULL; - } else { + if (ifindex != 0) { slave_dev = __dev_get_by_index(dev_net(bond_dev), ifindex); if (!slave_dev) return -ENODEV; + active_slave = slave_dev->name; + } + bond_opt_initstr(&newval, active_slave); + err = __bond_opt_set(bond, BOND_OPT_ACTIVE_SLAVE, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_MIIMON]) { + miimon = nla_get_u32(data[IFLA_BOND_MIIMON]); + + bond_opt_initval(&newval, miimon); + err = __bond_opt_set(bond, BOND_OPT_MIIMON, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_UPDELAY]) { + int updelay = nla_get_u32(data[IFLA_BOND_UPDELAY]); + + bond_opt_initval(&newval, updelay); + err = __bond_opt_set(bond, BOND_OPT_UPDELAY, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_DOWNDELAY]) { + int downdelay = nla_get_u32(data[IFLA_BOND_DOWNDELAY]); + + bond_opt_initval(&newval, downdelay); + err = __bond_opt_set(bond, BOND_OPT_DOWNDELAY, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_USE_CARRIER]) { + int use_carrier = nla_get_u8(data[IFLA_BOND_USE_CARRIER]); + + bond_opt_initval(&newval, use_carrier); + err = __bond_opt_set(bond, BOND_OPT_USE_CARRIER, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_ARP_INTERVAL]) { + int arp_interval = nla_get_u32(data[IFLA_BOND_ARP_INTERVAL]); + + if (arp_interval && miimon) { + pr_err("%s: ARP monitoring cannot be used with MII monitoring.\n", + bond->dev->name); + return -EINVAL; } - err = bond_option_active_slave_set(bond, slave_dev); + + bond_opt_initval(&newval, arp_interval); + err = __bond_opt_set(bond, BOND_OPT_ARP_INTERVAL, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_ARP_IP_TARGET]) { + struct nlattr *attr; + int i = 0, rem; + + bond_option_arp_ip_targets_clear(bond); + nla_for_each_nested(attr, data[IFLA_BOND_ARP_IP_TARGET], rem) { + __be32 target = nla_get_be32(attr); + + bond_opt_initval(&newval, target); + err = __bond_opt_set(bond, BOND_OPT_ARP_TARGETS, + &newval); + if (err) + break; + i++; + } + if (i == 0 && bond->params.arp_interval) + pr_warn("%s: removing last arp target with arp_interval on\n", + bond->dev->name); + if (err) + return err; + } + if (data[IFLA_BOND_ARP_VALIDATE]) { + int arp_validate = nla_get_u32(data[IFLA_BOND_ARP_VALIDATE]); + + if (arp_validate && miimon) { + pr_err("%s: ARP validating cannot be used with MII monitoring.\n", + bond->dev->name); + return -EINVAL; + } + + bond_opt_initval(&newval, arp_validate); + err = __bond_opt_set(bond, BOND_OPT_ARP_VALIDATE, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_ARP_ALL_TARGETS]) { + int arp_all_targets = + nla_get_u32(data[IFLA_BOND_ARP_ALL_TARGETS]); + + bond_opt_initval(&newval, arp_all_targets); + err = __bond_opt_set(bond, BOND_OPT_ARP_ALL_TARGETS, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_PRIMARY]) { + int ifindex = nla_get_u32(data[IFLA_BOND_PRIMARY]); + struct net_device *dev; + char *primary = ""; + + dev = __dev_get_by_index(dev_net(bond_dev), ifindex); + if (dev) + primary = dev->name; + + bond_opt_initstr(&newval, primary); + err = __bond_opt_set(bond, BOND_OPT_PRIMARY, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_PRIMARY_RESELECT]) { + int primary_reselect = + nla_get_u8(data[IFLA_BOND_PRIMARY_RESELECT]); + + bond_opt_initval(&newval, primary_reselect); + err = __bond_opt_set(bond, BOND_OPT_PRIMARY_RESELECT, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_FAIL_OVER_MAC]) { + int fail_over_mac = + nla_get_u8(data[IFLA_BOND_FAIL_OVER_MAC]); + + bond_opt_initval(&newval, fail_over_mac); + err = __bond_opt_set(bond, BOND_OPT_FAIL_OVER_MAC, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_XMIT_HASH_POLICY]) { + int xmit_hash_policy = + nla_get_u8(data[IFLA_BOND_XMIT_HASH_POLICY]); + + bond_opt_initval(&newval, xmit_hash_policy); + err = __bond_opt_set(bond, BOND_OPT_XMIT_HASH, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_RESEND_IGMP]) { + int resend_igmp = + nla_get_u32(data[IFLA_BOND_RESEND_IGMP]); + + bond_opt_initval(&newval, resend_igmp); + err = __bond_opt_set(bond, BOND_OPT_RESEND_IGMP, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_NUM_PEER_NOTIF]) { + int num_peer_notif = + nla_get_u8(data[IFLA_BOND_NUM_PEER_NOTIF]); + + bond_opt_initval(&newval, num_peer_notif); + err = __bond_opt_set(bond, BOND_OPT_NUM_PEER_NOTIF, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_ALL_SLAVES_ACTIVE]) { + int all_slaves_active = + nla_get_u8(data[IFLA_BOND_ALL_SLAVES_ACTIVE]); + + bond_opt_initval(&newval, all_slaves_active); + err = __bond_opt_set(bond, BOND_OPT_ALL_SLAVES_ACTIVE, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_MIN_LINKS]) { + int min_links = + nla_get_u32(data[IFLA_BOND_MIN_LINKS]); + + bond_opt_initval(&newval, min_links); + err = __bond_opt_set(bond, BOND_OPT_MINLINKS, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_LP_INTERVAL]) { + int lp_interval = + nla_get_u32(data[IFLA_BOND_LP_INTERVAL]); + + bond_opt_initval(&newval, lp_interval); + err = __bond_opt_set(bond, BOND_OPT_LP_INTERVAL, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_PACKETS_PER_SLAVE]) { + int packets_per_slave = + nla_get_u32(data[IFLA_BOND_PACKETS_PER_SLAVE]); + + bond_opt_initval(&newval, packets_per_slave); + err = __bond_opt_set(bond, BOND_OPT_PACKETS_PER_SLAVE, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_AD_LACP_RATE]) { + int lacp_rate = + nla_get_u8(data[IFLA_BOND_AD_LACP_RATE]); + + bond_opt_initval(&newval, lacp_rate); + err = __bond_opt_set(bond, BOND_OPT_LACP_RATE, &newval); + if (err) + return err; + } + if (data[IFLA_BOND_AD_SELECT]) { + int ad_select = + nla_get_u8(data[IFLA_BOND_AD_SELECT]); + + bond_opt_initval(&newval, ad_select); + err = __bond_opt_set(bond, BOND_OPT_AD_SELECT, &newval); if (err) return err; } @@ -83,7 +366,36 @@ static int bond_newlink(struct net *src_net, struct net_device *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 */ + nla_total_size(sizeof(u32)) + /* IFLA_BOND_ACTIVE_SLAVE */ + nla_total_size(sizeof(u32)) + /* IFLA_BOND_MIIMON */ + nla_total_size(sizeof(u32)) + /* IFLA_BOND_UPDELAY */ + nla_total_size(sizeof(u32)) + /* IFLA_BOND_DOWNDELAY */ + nla_total_size(sizeof(u8)) + /* IFLA_BOND_USE_CARRIER */ + nla_total_size(sizeof(u32)) + /* IFLA_BOND_ARP_INTERVAL */ + /* IFLA_BOND_ARP_IP_TARGET */ + nla_total_size(sizeof(struct nlattr)) + + nla_total_size(sizeof(u32)) * BOND_MAX_ARP_TARGETS + + nla_total_size(sizeof(u32)) + /* IFLA_BOND_ARP_VALIDATE */ + nla_total_size(sizeof(u32)) + /* IFLA_BOND_ARP_ALL_TARGETS */ + nla_total_size(sizeof(u32)) + /* IFLA_BOND_PRIMARY */ + nla_total_size(sizeof(u8)) + /* IFLA_BOND_PRIMARY_RESELECT */ + nla_total_size(sizeof(u8)) + /* IFLA_BOND_FAIL_OVER_MAC */ + nla_total_size(sizeof(u8)) + /* IFLA_BOND_XMIT_HASH_POLICY */ + nla_total_size(sizeof(u32)) + /* IFLA_BOND_RESEND_IGMP */ + nla_total_size(sizeof(u8)) + /* IFLA_BOND_NUM_PEER_NOTIF */ + nla_total_size(sizeof(u8)) + /* IFLA_BOND_ALL_SLAVES_ACTIVE */ + nla_total_size(sizeof(u32)) + /* IFLA_BOND_MIN_LINKS */ + nla_total_size(sizeof(u32)) + /* IFLA_BOND_LP_INTERVAL */ + nla_total_size(sizeof(u32)) + /* IFLA_BOND_PACKETS_PER_SLAVE */ + nla_total_size(sizeof(u8)) + /* IFLA_BOND_AD_LACP_RATE */ + nla_total_size(sizeof(u8)) + /* IFLA_BOND_AD_SELECT */ + nla_total_size(sizeof(struct nlattr)) + /* IFLA_BOND_AD_INFO */ + nla_total_size(sizeof(u16)) + /* IFLA_BOND_AD_INFO_AGGREGATOR */ + nla_total_size(sizeof(u16)) + /* IFLA_BOND_AD_INFO_NUM_PORTS */ + nla_total_size(sizeof(u16)) + /* IFLA_BOND_AD_INFO_ACTOR_KEY */ + nla_total_size(sizeof(u16)) + /* IFLA_BOND_AD_INFO_PARTNER_KEY*/ + nla_total_size(ETH_ALEN) + /* IFLA_BOND_AD_INFO_PARTNER_MAC*/ + 0; } static int bond_fill_info(struct sk_buff *skb, @@ -91,11 +403,139 @@ static int bond_fill_info(struct sk_buff *skb, { struct bonding *bond = netdev_priv(bond_dev); struct net_device *slave_dev = bond_option_active_slave_get(bond); + struct nlattr *targets; + unsigned int packets_per_slave; + int i, targets_added; + + if (nla_put_u8(skb, IFLA_BOND_MODE, bond->params.mode)) + goto nla_put_failure; + + if (slave_dev && + nla_put_u32(skb, IFLA_BOND_ACTIVE_SLAVE, slave_dev->ifindex)) + goto nla_put_failure; + + if (nla_put_u32(skb, IFLA_BOND_MIIMON, bond->params.miimon)) + goto nla_put_failure; + + if (nla_put_u32(skb, IFLA_BOND_UPDELAY, + bond->params.updelay * bond->params.miimon)) + goto nla_put_failure; + + if (nla_put_u32(skb, IFLA_BOND_DOWNDELAY, + bond->params.downdelay * bond->params.miimon)) + goto nla_put_failure; + + if (nla_put_u8(skb, IFLA_BOND_USE_CARRIER, bond->params.use_carrier)) + goto nla_put_failure; - if (nla_put_u8(skb, IFLA_BOND_MODE, bond->params.mode) || - (slave_dev && - nla_put_u32(skb, IFLA_BOND_ACTIVE_SLAVE, slave_dev->ifindex))) + if (nla_put_u32(skb, IFLA_BOND_ARP_INTERVAL, bond->params.arp_interval)) goto nla_put_failure; + + targets = nla_nest_start(skb, IFLA_BOND_ARP_IP_TARGET); + if (!targets) + goto nla_put_failure; + + targets_added = 0; + for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) { + if (bond->params.arp_targets[i]) { + nla_put_be32(skb, i, bond->params.arp_targets[i]); + targets_added = 1; + } + } + + if (targets_added) + nla_nest_end(skb, targets); + else + nla_nest_cancel(skb, targets); + + if (nla_put_u32(skb, IFLA_BOND_ARP_VALIDATE, bond->params.arp_validate)) + goto nla_put_failure; + + if (nla_put_u32(skb, IFLA_BOND_ARP_ALL_TARGETS, + bond->params.arp_all_targets)) + goto nla_put_failure; + + if (bond->primary_slave && + nla_put_u32(skb, IFLA_BOND_PRIMARY, + bond->primary_slave->dev->ifindex)) + goto nla_put_failure; + + if (nla_put_u8(skb, IFLA_BOND_PRIMARY_RESELECT, + bond->params.primary_reselect)) + goto nla_put_failure; + + if (nla_put_u8(skb, IFLA_BOND_FAIL_OVER_MAC, + bond->params.fail_over_mac)) + goto nla_put_failure; + + if (nla_put_u8(skb, IFLA_BOND_XMIT_HASH_POLICY, + bond->params.xmit_policy)) + goto nla_put_failure; + + if (nla_put_u32(skb, IFLA_BOND_RESEND_IGMP, + bond->params.resend_igmp)) + goto nla_put_failure; + + if (nla_put_u8(skb, IFLA_BOND_NUM_PEER_NOTIF, + bond->params.num_peer_notif)) + goto nla_put_failure; + + if (nla_put_u8(skb, IFLA_BOND_ALL_SLAVES_ACTIVE, + bond->params.all_slaves_active)) + goto nla_put_failure; + + if (nla_put_u32(skb, IFLA_BOND_MIN_LINKS, + bond->params.min_links)) + goto nla_put_failure; + + if (nla_put_u32(skb, IFLA_BOND_LP_INTERVAL, + bond->params.lp_interval)) + goto nla_put_failure; + + packets_per_slave = bond->params.packets_per_slave; + if (nla_put_u32(skb, IFLA_BOND_PACKETS_PER_SLAVE, + packets_per_slave)) + goto nla_put_failure; + + if (nla_put_u8(skb, IFLA_BOND_AD_LACP_RATE, + bond->params.lacp_fast)) + goto nla_put_failure; + + if (nla_put_u8(skb, IFLA_BOND_AD_SELECT, + bond->params.ad_select)) + goto nla_put_failure; + + if (bond->params.mode == BOND_MODE_8023AD) { + struct ad_info info; + + if (!bond_3ad_get_active_agg_info(bond, &info)) { + struct nlattr *nest; + + nest = nla_nest_start(skb, IFLA_BOND_AD_INFO); + if (!nest) + goto nla_put_failure; + + if (nla_put_u16(skb, IFLA_BOND_AD_INFO_AGGREGATOR, + info.aggregator_id)) + goto nla_put_failure; + if (nla_put_u16(skb, IFLA_BOND_AD_INFO_NUM_PORTS, + info.ports)) + goto nla_put_failure; + if (nla_put_u16(skb, IFLA_BOND_AD_INFO_ACTOR_KEY, + info.actor_key)) + goto nla_put_failure; + if (nla_put_u16(skb, IFLA_BOND_AD_INFO_PARTNER_KEY, + info.partner_key)) + goto nla_put_failure; + if (nla_put(skb, IFLA_BOND_AD_INFO_PARTNER_MAC, + sizeof(info.partner_system), + &info.partner_system)) + goto nla_put_failure; + + nla_nest_end(skb, nest); + } + } + return 0; nla_put_failure: @@ -116,6 +556,8 @@ struct rtnl_link_ops bond_link_ops __read_mostly = { .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 */ + .get_slave_size = bond_get_slave_size, + .fill_slave_info = bond_fill_slave_info, }; int __init bond_netlink_init(void) diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index ea6f640782b7..11cb943222d5 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -1,6 +1,7 @@ /* * drivers/net/bond/bond_options.c - bonding options * Copyright (c) 2013 Jiri Pirko <jiri@resnulli.us> + * Copyright (c) 2013 Scott Feldman <sfeldma@cumulusnetworks.com> * * 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 @@ -15,39 +16,575 @@ #include <linux/netdevice.h> #include <linux/rwlock.h> #include <linux/rcupdate.h> +#include <linux/ctype.h> +#include <linux/inet.h> #include "bonding.h" -static bool bond_mode_is_valid(int mode) +static struct bond_opt_value bond_mode_tbl[] = { + { "balance-rr", BOND_MODE_ROUNDROBIN, BOND_VALFLAG_DEFAULT}, + { "active-backup", BOND_MODE_ACTIVEBACKUP, 0}, + { "balance-xor", BOND_MODE_XOR, 0}, + { "broadcast", BOND_MODE_BROADCAST, 0}, + { "802.3ad", BOND_MODE_8023AD, 0}, + { "balance-tlb", BOND_MODE_TLB, 0}, + { "balance-alb", BOND_MODE_ALB, 0}, + { NULL, -1, 0}, +}; + +static struct bond_opt_value bond_pps_tbl[] = { + { "default", 1, BOND_VALFLAG_DEFAULT}, + { "maxval", USHRT_MAX, BOND_VALFLAG_MAX}, + { NULL, -1, 0}, +}; + +static struct bond_opt_value bond_xmit_hashtype_tbl[] = { + { "layer2", BOND_XMIT_POLICY_LAYER2, BOND_VALFLAG_DEFAULT}, + { "layer3+4", BOND_XMIT_POLICY_LAYER34, 0}, + { "layer2+3", BOND_XMIT_POLICY_LAYER23, 0}, + { "encap2+3", BOND_XMIT_POLICY_ENCAP23, 0}, + { "encap3+4", BOND_XMIT_POLICY_ENCAP34, 0}, + { NULL, -1, 0}, +}; + +static struct bond_opt_value bond_arp_validate_tbl[] = { + { "none", BOND_ARP_VALIDATE_NONE, BOND_VALFLAG_DEFAULT}, + { "active", BOND_ARP_VALIDATE_ACTIVE, 0}, + { "backup", BOND_ARP_VALIDATE_BACKUP, 0}, + { "all", BOND_ARP_VALIDATE_ALL, 0}, + { NULL, -1, 0}, +}; + +static struct bond_opt_value bond_arp_all_targets_tbl[] = { + { "any", BOND_ARP_TARGETS_ANY, BOND_VALFLAG_DEFAULT}, + { "all", BOND_ARP_TARGETS_ALL, 0}, + { NULL, -1, 0}, +}; + +static struct bond_opt_value bond_fail_over_mac_tbl[] = { + { "none", BOND_FOM_NONE, BOND_VALFLAG_DEFAULT}, + { "active", BOND_FOM_ACTIVE, 0}, + { "follow", BOND_FOM_FOLLOW, 0}, + { NULL, -1, 0}, +}; + +static struct bond_opt_value bond_intmax_tbl[] = { + { "off", 0, BOND_VALFLAG_DEFAULT}, + { "maxval", INT_MAX, BOND_VALFLAG_MAX}, +}; + +static struct bond_opt_value bond_lacp_rate_tbl[] = { + { "slow", AD_LACP_SLOW, 0}, + { "fast", AD_LACP_FAST, 0}, + { NULL, -1, 0}, +}; + +static struct bond_opt_value bond_ad_select_tbl[] = { + { "stable", BOND_AD_STABLE, BOND_VALFLAG_DEFAULT}, + { "bandwidth", BOND_AD_BANDWIDTH, 0}, + { "count", BOND_AD_COUNT, 0}, + { NULL, -1, 0}, +}; + +static struct bond_opt_value bond_num_peer_notif_tbl[] = { + { "off", 0, 0}, + { "maxval", 255, BOND_VALFLAG_MAX}, + { "default", 1, BOND_VALFLAG_DEFAULT}, + { NULL, -1, 0} +}; + +static struct bond_opt_value bond_primary_reselect_tbl[] = { + { "always", BOND_PRI_RESELECT_ALWAYS, BOND_VALFLAG_DEFAULT}, + { "better", BOND_PRI_RESELECT_BETTER, 0}, + { "failure", BOND_PRI_RESELECT_FAILURE, 0}, + { NULL, -1}, +}; + +static struct bond_opt_value bond_use_carrier_tbl[] = { + { "off", 0, 0}, + { "on", 1, BOND_VALFLAG_DEFAULT}, + { NULL, -1, 0} +}; + +static struct bond_opt_value bond_all_slaves_active_tbl[] = { + { "off", 0, BOND_VALFLAG_DEFAULT}, + { "on", 1, 0}, + { NULL, -1, 0} +}; + +static struct bond_opt_value bond_resend_igmp_tbl[] = { + { "off", 0, 0}, + { "maxval", 255, BOND_VALFLAG_MAX}, + { "default", 1, BOND_VALFLAG_DEFAULT}, + { NULL, -1, 0} +}; + +static struct bond_opt_value bond_lp_interval_tbl[] = { + { "minval", 1, BOND_VALFLAG_MIN | BOND_VALFLAG_DEFAULT}, + { "maxval", INT_MAX, BOND_VALFLAG_MAX}, +}; + +static struct bond_option bond_opts[] = { + [BOND_OPT_MODE] = { + .id = BOND_OPT_MODE, + .name = "mode", + .desc = "bond device mode", + .flags = BOND_OPTFLAG_NOSLAVES | BOND_OPTFLAG_IFDOWN, + .values = bond_mode_tbl, + .set = bond_option_mode_set + }, + [BOND_OPT_PACKETS_PER_SLAVE] = { + .id = BOND_OPT_PACKETS_PER_SLAVE, + .name = "packets_per_slave", + .desc = "Packets to send per slave in RR mode", + .unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_ROUNDROBIN)), + .values = bond_pps_tbl, + .set = bond_option_pps_set + }, + [BOND_OPT_XMIT_HASH] = { + .id = BOND_OPT_XMIT_HASH, + .name = "xmit_hash_policy", + .desc = "balance-xor and 802.3ad hashing method", + .values = bond_xmit_hashtype_tbl, + .set = bond_option_xmit_hash_policy_set + }, + [BOND_OPT_ARP_VALIDATE] = { + .id = BOND_OPT_ARP_VALIDATE, + .name = "arp_validate", + .desc = "validate src/dst of ARP probes", + .unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_ACTIVEBACKUP)), + .values = bond_arp_validate_tbl, + .set = bond_option_arp_validate_set + }, + [BOND_OPT_ARP_ALL_TARGETS] = { + .id = BOND_OPT_ARP_ALL_TARGETS, + .name = "arp_all_targets", + .desc = "fail on any/all arp targets timeout", + .values = bond_arp_all_targets_tbl, + .set = bond_option_arp_all_targets_set + }, + [BOND_OPT_FAIL_OVER_MAC] = { + .id = BOND_OPT_FAIL_OVER_MAC, + .name = "fail_over_mac", + .desc = "For active-backup, do not set all slaves to the same MAC", + .flags = BOND_OPTFLAG_NOSLAVES, + .values = bond_fail_over_mac_tbl, + .set = bond_option_fail_over_mac_set + }, + [BOND_OPT_ARP_INTERVAL] = { + .id = BOND_OPT_ARP_INTERVAL, + .name = "arp_interval", + .desc = "arp interval in milliseconds", + .unsuppmodes = BIT(BOND_MODE_8023AD) | BIT(BOND_MODE_TLB) | + BIT(BOND_MODE_ALB), + .values = bond_intmax_tbl, + .set = bond_option_arp_interval_set + }, + [BOND_OPT_ARP_TARGETS] = { + .id = BOND_OPT_ARP_TARGETS, + .name = "arp_ip_target", + .desc = "arp targets in n.n.n.n form", + .flags = BOND_OPTFLAG_RAWVAL, + .set = bond_option_arp_ip_targets_set + }, + [BOND_OPT_DOWNDELAY] = { + .id = BOND_OPT_DOWNDELAY, + .name = "downdelay", + .desc = "Delay before considering link down, in milliseconds", + .values = bond_intmax_tbl, + .set = bond_option_downdelay_set + }, + [BOND_OPT_UPDELAY] = { + .id = BOND_OPT_UPDELAY, + .name = "updelay", + .desc = "Delay before considering link up, in milliseconds", + .values = bond_intmax_tbl, + .set = bond_option_updelay_set + }, + [BOND_OPT_LACP_RATE] = { + .id = BOND_OPT_LACP_RATE, + .name = "lacp_rate", + .desc = "LACPDU tx rate to request from 802.3ad partner", + .flags = BOND_OPTFLAG_IFDOWN, + .unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_8023AD)), + .values = bond_lacp_rate_tbl, + .set = bond_option_lacp_rate_set + }, + [BOND_OPT_MINLINKS] = { + .id = BOND_OPT_MINLINKS, + .name = "min_links", + .desc = "Minimum number of available links before turning on carrier", + .values = bond_intmax_tbl, + .set = bond_option_min_links_set + }, + [BOND_OPT_AD_SELECT] = { + .id = BOND_OPT_AD_SELECT, + .name = "ad_select", + .desc = "803.ad aggregation selection logic", + .flags = BOND_OPTFLAG_IFDOWN, + .values = bond_ad_select_tbl, + .set = bond_option_ad_select_set + }, + [BOND_OPT_NUM_PEER_NOTIF] = { + .id = BOND_OPT_NUM_PEER_NOTIF, + .name = "num_unsol_na", + .desc = "Number of peer notifications to send on failover event", + .values = bond_num_peer_notif_tbl, + .set = bond_option_num_peer_notif_set + }, + [BOND_OPT_MIIMON] = { + .id = BOND_OPT_MIIMON, + .name = "miimon", + .desc = "Link check interval in milliseconds", + .values = bond_intmax_tbl, + .set = bond_option_miimon_set + }, + [BOND_OPT_PRIMARY] = { + .id = BOND_OPT_PRIMARY, + .name = "primary", + .desc = "Primary network device to use", + .flags = BOND_OPTFLAG_RAWVAL, + .unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_ACTIVEBACKUP) | + BIT(BOND_MODE_TLB) | + BIT(BOND_MODE_ALB)), + .set = bond_option_primary_set + }, + [BOND_OPT_PRIMARY_RESELECT] = { + .id = BOND_OPT_PRIMARY_RESELECT, + .name = "primary_reselect", + .desc = "Reselect primary slave once it comes up", + .values = bond_primary_reselect_tbl, + .set = bond_option_primary_reselect_set + }, + [BOND_OPT_USE_CARRIER] = { + .id = BOND_OPT_USE_CARRIER, + .name = "use_carrier", + .desc = "Use netif_carrier_ok (vs MII ioctls) in miimon", + .values = bond_use_carrier_tbl, + .set = bond_option_use_carrier_set + }, + [BOND_OPT_ACTIVE_SLAVE] = { + .id = BOND_OPT_ACTIVE_SLAVE, + .name = "active_slave", + .desc = "Currently active slave", + .flags = BOND_OPTFLAG_RAWVAL, + .unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_ACTIVEBACKUP) | + BIT(BOND_MODE_TLB) | + BIT(BOND_MODE_ALB)), + .set = bond_option_active_slave_set + }, + [BOND_OPT_QUEUE_ID] = { + .id = BOND_OPT_QUEUE_ID, + .name = "queue_id", + .desc = "Set queue id of a slave", + .flags = BOND_OPTFLAG_RAWVAL, + .set = bond_option_queue_id_set + }, + [BOND_OPT_ALL_SLAVES_ACTIVE] = { + .id = BOND_OPT_ALL_SLAVES_ACTIVE, + .name = "all_slaves_active", + .desc = "Keep all frames received on an interface by setting active flag for all slaves", + .values = bond_all_slaves_active_tbl, + .set = bond_option_all_slaves_active_set + }, + [BOND_OPT_RESEND_IGMP] = { + .id = BOND_OPT_RESEND_IGMP, + .name = "resend_igmp", + .desc = "Number of IGMP membership reports to send on link failure", + .values = bond_resend_igmp_tbl, + .set = bond_option_resend_igmp_set + }, + [BOND_OPT_LP_INTERVAL] = { + .id = BOND_OPT_LP_INTERVAL, + .name = "lp_interval", + .desc = "The number of seconds between instances where the bonding driver sends learning packets to each slave's peer switch", + .values = bond_lp_interval_tbl, + .set = bond_option_lp_interval_set + }, + [BOND_OPT_SLAVES] = { + .id = BOND_OPT_SLAVES, + .name = "slaves", + .desc = "Slave membership management", + .flags = BOND_OPTFLAG_RAWVAL, + .set = bond_option_slaves_set + }, + { } +}; + +/* Searches for a value in opt's values[] table */ +struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val) { + struct bond_option *opt; int i; - for (i = 0; bond_mode_tbl[i].modename; i++); + opt = bond_opt_get(option); + if (WARN_ON(!opt)) + return NULL; + for (i = 0; opt->values && opt->values[i].string; i++) + if (opt->values[i].value == val) + return &opt->values[i]; - return mode >= 0 && mode < i; + return NULL; } -int bond_option_mode_set(struct bonding *bond, int mode) +/* Searches for a value in opt's values[] table which matches the flagmask */ +static struct bond_opt_value *bond_opt_get_flags(const struct bond_option *opt, + u32 flagmask) { - if (!bond_mode_is_valid(mode)) { - pr_err("invalid mode value %d.\n", mode); - return -EINVAL; + int i; + + for (i = 0; opt->values && opt->values[i].string; i++) + if (opt->values[i].flags & flagmask) + return &opt->values[i]; + + return NULL; +} + +/* If maxval is missing then there's no range to check. In case minval is + * missing then it's considered to be 0. + */ +static bool bond_opt_check_range(const struct bond_option *opt, u64 val) +{ + struct bond_opt_value *minval, *maxval; + + minval = bond_opt_get_flags(opt, BOND_VALFLAG_MIN); + maxval = bond_opt_get_flags(opt, BOND_VALFLAG_MAX); + if (!maxval || (minval && val < minval->value) || val > maxval->value) + return false; + + return true; +} + +/** + * bond_opt_parse - parse option value + * @opt: the option to parse against + * @val: value to parse + * + * This function tries to extract the value from @val and check if it's + * a possible match for the option and returns NULL if a match isn't found, + * or the struct_opt_value that matched. It also strips the new line from + * @val->string if it's present. + */ +struct bond_opt_value *bond_opt_parse(const struct bond_option *opt, + struct bond_opt_value *val) +{ + char *p, valstr[BOND_OPT_MAX_NAMELEN + 1] = { 0, }; + struct bond_opt_value *tbl, *ret = NULL; + bool checkval; + int i, rv; + + /* No parsing if the option wants a raw val */ + if (opt->flags & BOND_OPTFLAG_RAWVAL) + return val; + + tbl = opt->values; + if (!tbl) + goto out; + + /* ULLONG_MAX is used to bypass string processing */ + checkval = val->value != ULLONG_MAX; + if (!checkval) { + if (!val->string) + goto out; + p = strchr(val->string, '\n'); + if (p) + *p = '\0'; + for (p = val->string; *p; p++) + if (!(isdigit(*p) || isspace(*p))) + break; + /* The following code extracts the string to match or the value + * and sets checkval appropriately + */ + if (*p) { + rv = sscanf(val->string, "%32s", valstr); + } else { + rv = sscanf(val->string, "%llu", &val->value); + checkval = true; + } + if (!rv) + goto out; } - if (bond->dev->flags & IFF_UP) { - pr_err("%s: unable to update mode because interface is up.\n", - bond->dev->name); - return -EPERM; + for (i = 0; tbl[i].string; i++) { + /* Check for exact match */ + if (checkval) { + if (val->value == tbl[i].value) + ret = &tbl[i]; + } else { + if (!strcmp(valstr, "default") && + (tbl[i].flags & BOND_VALFLAG_DEFAULT)) + ret = &tbl[i]; + + if (!strcmp(valstr, tbl[i].string)) + ret = &tbl[i]; + } + /* Found an exact match */ + if (ret) + goto out; } + /* Possible range match */ + if (checkval && bond_opt_check_range(opt, val->value)) + ret = val; +out: + return ret; +} - if (bond_has_slaves(bond)) { - pr_err("%s: unable to update mode because bond has slaves.\n", - bond->dev->name); - return -EPERM; +/* Check opt's dependencies against bond mode and currently set options */ +static int bond_opt_check_deps(struct bonding *bond, + const struct bond_option *opt) +{ + struct bond_params *params = &bond->params; + + if (test_bit(params->mode, &opt->unsuppmodes)) + return -EACCES; + if ((opt->flags & BOND_OPTFLAG_NOSLAVES) && bond_has_slaves(bond)) + return -ENOTEMPTY; + if ((opt->flags & BOND_OPTFLAG_IFDOWN) && (bond->dev->flags & IFF_UP)) + return -EBUSY; + + return 0; +} + +static void bond_opt_dep_print(struct bonding *bond, + const struct bond_option *opt) +{ + struct bond_opt_value *modeval; + struct bond_params *params; + + params = &bond->params; + modeval = bond_opt_get_val(BOND_OPT_MODE, params->mode); + if (test_bit(params->mode, &opt->unsuppmodes)) + pr_err("%s: option %s: mode dependency failed, not supported in mode %s(%llu)\n", + bond->dev->name, opt->name, + modeval->string, modeval->value); +} + +static void bond_opt_error_interpret(struct bonding *bond, + const struct bond_option *opt, + int error, struct bond_opt_value *val) +{ + struct bond_opt_value *minval, *maxval; + char *p; + + switch (error) { + case -EINVAL: + if (val) { + if (val->string) { + /* sometimes RAWVAL opts may have new lines */ + p = strchr(val->string, '\n'); + if (p) + *p = '\0'; + pr_err("%s: option %s: invalid value (%s).\n", + bond->dev->name, opt->name, val->string); + } else { + pr_err("%s: option %s: invalid value (%llu).\n", + bond->dev->name, opt->name, val->value); + } + } + minval = bond_opt_get_flags(opt, BOND_VALFLAG_MIN); + maxval = bond_opt_get_flags(opt, BOND_VALFLAG_MAX); + if (!maxval) + break; + pr_err("%s: option %s: allowed values %llu - %llu.\n", + bond->dev->name, opt->name, minval ? minval->value : 0, + maxval->value); + break; + case -EACCES: + bond_opt_dep_print(bond, opt); + break; + case -ENOTEMPTY: + pr_err("%s: option %s: unable to set because the bond device has slaves.\n", + bond->dev->name, opt->name); + break; + case -EBUSY: + pr_err("%s: option %s: unable to set because the bond device is up.\n", + bond->dev->name, opt->name); + break; + default: + break; + } +} + +/** + * __bond_opt_set - set a bonding option + * @bond: target bond device + * @option: option to set + * @val: value to set it to + * + * This function is used to change the bond's option value, it can be + * used for both enabling/changing an option and for disabling it. RTNL lock + * must be obtained before calling this function. + */ +int __bond_opt_set(struct bonding *bond, + unsigned int option, struct bond_opt_value *val) +{ + struct bond_opt_value *retval = NULL; + const struct bond_option *opt; + int ret = -ENOENT; + + ASSERT_RTNL(); + + opt = bond_opt_get(option); + if (WARN_ON(!val) || WARN_ON(!opt)) + goto out; + ret = bond_opt_check_deps(bond, opt); + if (ret) + goto out; + retval = bond_opt_parse(opt, val); + if (!retval) { + ret = -EINVAL; + goto out; } + ret = opt->set(bond, retval); +out: + if (ret) + bond_opt_error_interpret(bond, opt, ret, val); - if (BOND_NO_USES_ARP(mode) && bond->params.arp_interval) { + return ret; +} + +/** + * bond_opt_tryset_rtnl - try to acquire rtnl and call __bond_opt_set + * @bond: target bond device + * @option: option to set + * @buf: value to set it to + * + * This function tries to acquire RTNL without blocking and if successful + * calls __bond_opt_set. It is mainly used for sysfs option manipulation. + */ +int bond_opt_tryset_rtnl(struct bonding *bond, unsigned int option, char *buf) +{ + struct bond_opt_value optval; + int ret; + + if (!rtnl_trylock()) + return restart_syscall(); + bond_opt_initstr(&optval, buf); + ret = __bond_opt_set(bond, option, &optval); + rtnl_unlock(); + + return ret; +} + +/** + * bond_opt_get - get a pointer to an option + * @option: option for which to return a pointer + * + * This function checks if option is valid and if so returns a pointer + * to its entry in the bond_opts[] option array. + */ +struct bond_option *bond_opt_get(unsigned int option) +{ + if (!BOND_OPT_VALID(option)) + return NULL; + + return &bond_opts[option]; +} + +int bond_option_mode_set(struct bonding *bond, struct bond_opt_value *newval) +{ + if (BOND_NO_USES_ARP(newval->value) && bond->params.arp_interval) { pr_info("%s: %s mode is incompatible with arp monitoring, start mii monitoring\n", - bond->dev->name, bond_mode_tbl[mode].modename); + bond->dev->name, newval->string); /* disable arp monitoring */ bond->params.arp_interval = 0; /* set miimon to default value */ @@ -58,7 +595,8 @@ int bond_option_mode_set(struct bonding *bond, int mode) /* don't cache arp_validate between modes */ bond->params.arp_validate = BOND_ARP_VALIDATE_NONE; - bond->params.mode = mode; + bond->params.mode = newval->value; + return 0; } @@ -81,10 +619,21 @@ struct net_device *bond_option_active_slave_get(struct bonding *bond) } int bond_option_active_slave_set(struct bonding *bond, - struct net_device *slave_dev) + struct bond_opt_value *newval) { + char ifname[IFNAMSIZ] = { 0, }; + struct net_device *slave_dev; int ret = 0; + sscanf(newval->string, "%15s", ifname); /* IFNAMSIZ */ + if (!strlen(ifname) || newval->string[0] == '\n') { + slave_dev = NULL; + } else { + slave_dev = __dev_get_by_name(dev_net(bond->dev), ifname); + if (!slave_dev) + return -ENODEV; + } + if (slave_dev) { if (!netif_is_bond_slave(slave_dev)) { pr_err("Device %s is not bonding slave.\n", @@ -99,14 +648,7 @@ int bond_option_active_slave_set(struct bonding *bond, } } - 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 */ @@ -141,7 +683,604 @@ int bond_option_active_slave_set(struct bonding *bond, } write_unlock_bh(&bond->curr_slave_lock); + unblock_netpoll_tx(); + + return ret; +} + +int bond_option_miimon_set(struct bonding *bond, struct bond_opt_value *newval) +{ + pr_info("%s: Setting MII monitoring interval to %llu.\n", + bond->dev->name, newval->value); + bond->params.miimon = newval->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 (newval->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 (!newval->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); + } + } + + return 0; +} + +int bond_option_updelay_set(struct bonding *bond, struct bond_opt_value *newval) +{ + int value = newval->value; + + if (!bond->params.miimon) { + pr_err("%s: Unable to set up delay as MII monitoring is disabled\n", + bond->dev->name); + return -EPERM; + } + if ((value % bond->params.miimon) != 0) { + pr_warn("%s: Warning: up delay (%d) is not a multiple of miimon (%d), updelay rounded to %d ms\n", + bond->dev->name, value, + bond->params.miimon, + (value / bond->params.miimon) * + bond->params.miimon); + } + bond->params.updelay = value / bond->params.miimon; + pr_info("%s: Setting up delay to %d.\n", + bond->dev->name, + bond->params.updelay * bond->params.miimon); + + return 0; +} + +int bond_option_downdelay_set(struct bonding *bond, + struct bond_opt_value *newval) +{ + int value = newval->value; + + if (!bond->params.miimon) { + pr_err("%s: Unable to set down delay as MII monitoring is disabled\n", + bond->dev->name); + return -EPERM; + } + if ((value % bond->params.miimon) != 0) { + pr_warn("%s: Warning: down delay (%d) is not a multiple of miimon (%d), delay rounded to %d ms\n", + bond->dev->name, value, + bond->params.miimon, + (value / bond->params.miimon) * + bond->params.miimon); + } + bond->params.downdelay = value / bond->params.miimon; + pr_info("%s: Setting down delay to %d.\n", + bond->dev->name, + bond->params.downdelay * bond->params.miimon); + + return 0; +} + +int bond_option_use_carrier_set(struct bonding *bond, + struct bond_opt_value *newval) +{ + pr_info("%s: Setting use_carrier to %llu.\n", + bond->dev->name, newval->value); + bond->params.use_carrier = newval->value; + + return 0; +} + +int bond_option_arp_interval_set(struct bonding *bond, + struct bond_opt_value *newval) +{ + pr_info("%s: Setting ARP monitoring interval to %llu.\n", + bond->dev->name, newval->value); + bond->params.arp_interval = newval->value; + if (newval->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->dev->flags & IFF_UP) { + /* If the interface is up, we may need to fire off + * the ARP timer. If the interface is down, the + * timer will get fired off when the open function + * is called. + */ + if (!newval->value) { + if (bond->params.arp_validate) + bond->recv_probe = NULL; + cancel_delayed_work_sync(&bond->arp_work); + } else { + /* arp_validate can be set only in active-backup mode */ + if (bond->params.arp_validate) + bond->recv_probe = bond_arp_rcv; + cancel_delayed_work_sync(&bond->mii_work); + queue_delayed_work(bond->wq, &bond->arp_work, 0); + } + } + + return 0; +} + +static void _bond_options_arp_ip_target_set(struct bonding *bond, int slot, + __be32 target, + unsigned long last_rx) +{ + __be32 *targets = bond->params.arp_targets; + struct list_head *iter; + struct slave *slave; + + if (slot >= 0 && slot < BOND_MAX_ARP_TARGETS) { + bond_for_each_slave(bond, slave, iter) + slave->target_last_arp_rx[slot] = last_rx; + targets[slot] = target; + } +} + +static int _bond_option_arp_ip_target_add(struct bonding *bond, __be32 target) +{ + __be32 *targets = bond->params.arp_targets; + int ind; + + if (IS_IP_TARGET_UNUSABLE_ADDRESS(target)) { + pr_err("%s: invalid ARP target %pI4 specified for addition\n", + bond->dev->name, &target); + return -EINVAL; + } + + if (bond_get_targets_ip(targets, target) != -1) { /* dup */ + pr_err("%s: ARP target %pI4 is already present\n", + bond->dev->name, &target); + return -EINVAL; + } + + ind = bond_get_targets_ip(targets, 0); /* first free slot */ + if (ind == -1) { + pr_err("%s: ARP target table is full!\n", + bond->dev->name); + return -EINVAL; + } + + pr_info("%s: adding ARP target %pI4.\n", bond->dev->name, &target); + + _bond_options_arp_ip_target_set(bond, ind, target, jiffies); + + return 0; +} + +int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target) +{ + int ret; + + /* not to race with bond_arp_rcv */ + write_lock_bh(&bond->lock); + ret = _bond_option_arp_ip_target_add(bond, target); + write_unlock_bh(&bond->lock); + + return ret; +} + +int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target) +{ + __be32 *targets = bond->params.arp_targets; + struct list_head *iter; + struct slave *slave; + unsigned long *targets_rx; + int ind, i; + + if (IS_IP_TARGET_UNUSABLE_ADDRESS(target)) { + pr_err("%s: invalid ARP target %pI4 specified for removal\n", + bond->dev->name, &target); + return -EINVAL; + } + + ind = bond_get_targets_ip(targets, target); + if (ind == -1) { + pr_err("%s: unable to remove nonexistent ARP target %pI4.\n", + bond->dev->name, &target); + return -EINVAL; + } + + if (ind == 0 && !targets[1] && bond->params.arp_interval) + pr_warn("%s: removing last arp target with arp_interval on\n", + bond->dev->name); + + pr_info("%s: removing ARP target %pI4.\n", bond->dev->name, + &target); + + /* not to race with bond_arp_rcv */ + write_lock_bh(&bond->lock); + + bond_for_each_slave(bond, slave, iter) { + targets_rx = slave->target_last_arp_rx; + for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++) + targets_rx[i] = targets_rx[i+1]; + targets_rx[i] = 0; + } + for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++) + targets[i] = targets[i+1]; + targets[i] = 0; + + write_unlock_bh(&bond->lock); + + return 0; +} + +void bond_option_arp_ip_targets_clear(struct bonding *bond) +{ + int i; + + /* not to race with bond_arp_rcv */ + write_lock_bh(&bond->lock); + for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) + _bond_options_arp_ip_target_set(bond, i, 0, 0); + write_unlock_bh(&bond->lock); +} + +int bond_option_arp_ip_targets_set(struct bonding *bond, + struct bond_opt_value *newval) +{ + int ret = -EPERM; + __be32 target; + + if (newval->string) { + if (!in4_pton(newval->string+1, -1, (u8 *)&target, -1, NULL)) { + pr_err("%s: invalid ARP target %pI4 specified\n", + bond->dev->name, &target); + return ret; + } + if (newval->string[0] == '+') + ret = bond_option_arp_ip_target_add(bond, target); + else if (newval->string[0] == '-') + ret = bond_option_arp_ip_target_rem(bond, target); + else + pr_err("no command found in arp_ip_targets file for bond %s. Use +<addr> or -<addr>.\n", + bond->dev->name); + } else { + target = newval->value; + ret = bond_option_arp_ip_target_add(bond, target); + } + + return ret; +} + +int bond_option_arp_validate_set(struct bonding *bond, + struct bond_opt_value *newval) +{ + pr_info("%s: setting arp_validate to %s (%llu).\n", + bond->dev->name, newval->string, newval->value); + + if (bond->dev->flags & IFF_UP) { + if (!newval->value) + bond->recv_probe = NULL; + else if (bond->params.arp_interval) + bond->recv_probe = bond_arp_rcv; + } + bond->params.arp_validate = newval->value; + + return 0; +} + +int bond_option_arp_all_targets_set(struct bonding *bond, + struct bond_opt_value *newval) +{ + pr_info("%s: setting arp_all_targets to %s (%llu).\n", + bond->dev->name, newval->string, newval->value); + bond->params.arp_all_targets = newval->value; + + return 0; +} + +int bond_option_primary_set(struct bonding *bond, struct bond_opt_value *newval) +{ + char *p, *primary = newval->string; + struct list_head *iter; + struct slave *slave; + + block_netpoll_tx(); + read_lock(&bond->lock); + write_lock_bh(&bond->curr_slave_lock); + + p = strchr(primary, '\n'); + if (p) + *p = '\0'; + /* check to see if we are clearing primary */ + if (!strlen(primary)) { + pr_info("%s: Setting primary slave to None.\n", + bond->dev->name); + bond->primary_slave = NULL; + memset(bond->params.primary, 0, sizeof(bond->params.primary)); + bond_select_active_slave(bond); + goto out; + } + + bond_for_each_slave(bond, slave, iter) { + if (strncmp(slave->dev->name, primary, IFNAMSIZ) == 0) { + pr_info("%s: Setting %s as primary slave.\n", + bond->dev->name, slave->dev->name); + bond->primary_slave = slave; + strcpy(bond->params.primary, slave->dev->name); + bond_select_active_slave(bond); + goto out; + } + } + + if (bond->primary_slave) { + pr_info("%s: Setting primary slave to None.\n", + bond->dev->name); + bond->primary_slave = NULL; + bond_select_active_slave(bond); + } + strncpy(bond->params.primary, primary, IFNAMSIZ); + bond->params.primary[IFNAMSIZ - 1] = 0; + + pr_info("%s: Recording %s as primary, but it has not been enslaved to %s yet.\n", + bond->dev->name, primary, bond->dev->name); + +out: + write_unlock_bh(&bond->curr_slave_lock); read_unlock(&bond->lock); unblock_netpoll_tx(); + + return 0; +} + +int bond_option_primary_reselect_set(struct bonding *bond, + struct bond_opt_value *newval) +{ + pr_info("%s: setting primary_reselect to %s (%llu).\n", + bond->dev->name, newval->string, newval->value); + bond->params.primary_reselect = newval->value; + + block_netpoll_tx(); + write_lock_bh(&bond->curr_slave_lock); + bond_select_active_slave(bond); + write_unlock_bh(&bond->curr_slave_lock); + unblock_netpoll_tx(); + + return 0; +} + +int bond_option_fail_over_mac_set(struct bonding *bond, + struct bond_opt_value *newval) +{ + pr_info("%s: Setting fail_over_mac to %s (%llu).\n", + bond->dev->name, newval->string, newval->value); + bond->params.fail_over_mac = newval->value; + + return 0; +} + +int bond_option_xmit_hash_policy_set(struct bonding *bond, + struct bond_opt_value *newval) +{ + pr_info("%s: setting xmit hash policy to %s (%llu).\n", + bond->dev->name, newval->string, newval->value); + bond->params.xmit_policy = newval->value; + + return 0; +} + +int bond_option_resend_igmp_set(struct bonding *bond, + struct bond_opt_value *newval) +{ + pr_info("%s: Setting resend_igmp to %llu.\n", + bond->dev->name, newval->value); + bond->params.resend_igmp = newval->value; + + return 0; +} + +int bond_option_num_peer_notif_set(struct bonding *bond, + struct bond_opt_value *newval) +{ + bond->params.num_peer_notif = newval->value; + + return 0; +} + +int bond_option_all_slaves_active_set(struct bonding *bond, + struct bond_opt_value *newval) +{ + struct list_head *iter; + struct slave *slave; + + if (newval->value == bond->params.all_slaves_active) + return 0; + bond->params.all_slaves_active = newval->value; + bond_for_each_slave(bond, slave, iter) { + if (!bond_is_active_slave(slave)) { + if (newval->value) + slave->inactive = 0; + else + slave->inactive = 1; + } + } + + return 0; +} + +int bond_option_min_links_set(struct bonding *bond, + struct bond_opt_value *newval) +{ + pr_info("%s: Setting min links value to %llu\n", + bond->dev->name, newval->value); + bond->params.min_links = newval->value; + + return 0; +} + +int bond_option_lp_interval_set(struct bonding *bond, + struct bond_opt_value *newval) +{ + bond->params.lp_interval = newval->value; + + return 0; +} + +int bond_option_pps_set(struct bonding *bond, struct bond_opt_value *newval) +{ + bond->params.packets_per_slave = newval->value; + if (newval->value > 0) { + bond->params.reciprocal_packets_per_slave = + reciprocal_value(newval->value); + } else { + /* reciprocal_packets_per_slave is unused if + * packets_per_slave is 0 or 1, just initialize it + */ + bond->params.reciprocal_packets_per_slave = + (struct reciprocal_value) { 0 }; + } + + return 0; +} + +int bond_option_lacp_rate_set(struct bonding *bond, + struct bond_opt_value *newval) +{ + pr_info("%s: Setting LACP rate to %s (%llu).\n", + bond->dev->name, newval->string, newval->value); + bond->params.lacp_fast = newval->value; + bond_3ad_update_lacp_rate(bond); + + return 0; +} + +int bond_option_ad_select_set(struct bonding *bond, + struct bond_opt_value *newval) +{ + pr_info("%s: Setting ad_select to %s (%llu).\n", + bond->dev->name, newval->string, newval->value); + bond->params.ad_select = newval->value; + + return 0; +} + +int bond_option_queue_id_set(struct bonding *bond, + struct bond_opt_value *newval) +{ + struct slave *slave, *update_slave; + struct net_device *sdev; + struct list_head *iter; + char *delim; + int ret = 0; + u16 qid; + + /* delim will point to queue id if successful */ + delim = strchr(newval->string, ':'); + if (!delim) + goto err_no_cmd; + + /* Terminate string that points to device name and bump it + * up one, so we can read the queue id there. + */ + *delim = '\0'; + if (sscanf(++delim, "%hd\n", &qid) != 1) + goto err_no_cmd; + + /* Check buffer length, valid ifname and queue id */ + if (strlen(newval->string) > IFNAMSIZ || + !dev_valid_name(newval->string) || + qid > bond->dev->real_num_tx_queues) + goto err_no_cmd; + + /* Get the pointer to that interface if it exists */ + sdev = __dev_get_by_name(dev_net(bond->dev), newval->string); + if (!sdev) + goto err_no_cmd; + + /* Search for thes slave and check for duplicate qids */ + update_slave = NULL; + bond_for_each_slave(bond, slave, iter) { + if (sdev == slave->dev) + /* We don't need to check the matching + * slave for dups, since we're overwriting it + */ + update_slave = slave; + else if (qid && qid == slave->queue_id) { + goto err_no_cmd; + } + } + + if (!update_slave) + goto err_no_cmd; + + /* Actually set the qids for the slave */ + update_slave->queue_id = qid; + +out: return ret; + +err_no_cmd: + pr_info("invalid input for queue_id set for %s.\n", + bond->dev->name); + ret = -EPERM; + goto out; + +} + +int bond_option_slaves_set(struct bonding *bond, struct bond_opt_value *newval) +{ + char command[IFNAMSIZ + 1] = { 0, }; + struct net_device *dev; + char *ifname; + int ret; + + sscanf(newval->string, "%16s", command); /* IFNAMSIZ*/ + ifname = command + 1; + if ((strlen(command) <= 1) || + !dev_valid_name(ifname)) + goto err_no_cmd; + + dev = __dev_get_by_name(dev_net(bond->dev), ifname); + if (!dev) { + pr_info("%s: Interface %s does not exist!\n", + bond->dev->name, ifname); + ret = -ENODEV; + goto out; + } + + switch (command[0]) { + case '+': + pr_info("%s: Adding slave %s.\n", bond->dev->name, dev->name); + ret = bond_enslave(bond->dev, dev); + break; + + case '-': + pr_info("%s: Removing slave %s.\n", bond->dev->name, dev->name); + ret = bond_release(bond->dev, dev); + break; + + default: + goto err_no_cmd; + } + +out: + return ret; + +err_no_cmd: + pr_err("no command found in slaves file for bond %s. Use +ifname or -ifname.\n", + bond->dev->name); + ret = -EPERM; + goto out; } diff --git a/drivers/net/bonding/bond_options.h b/drivers/net/bonding/bond_options.h new file mode 100644 index 000000000000..433d37f6940b --- /dev/null +++ b/drivers/net/bonding/bond_options.h @@ -0,0 +1,170 @@ +/* + * drivers/net/bond/bond_options.h - bonding options + * Copyright (c) 2013 Nikolay Aleksandrov <nikolay@redhat.com> + * + * 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. + */ + +#ifndef _BOND_OPTIONS_H +#define _BOND_OPTIONS_H + +#define BOND_OPT_MAX_NAMELEN 32 +#define BOND_OPT_VALID(opt) ((opt) < BOND_OPT_LAST) +#define BOND_MODE_ALL_EX(x) (~(x)) + +/* Option flags: + * BOND_OPTFLAG_NOSLAVES - check if the bond device is empty before setting + * BOND_OPTFLAG_IFDOWN - check if the bond device is down before setting + * BOND_OPTFLAG_RAWVAL - the option parses the value itself + */ +enum { + BOND_OPTFLAG_NOSLAVES = BIT(0), + BOND_OPTFLAG_IFDOWN = BIT(1), + BOND_OPTFLAG_RAWVAL = BIT(2) +}; + +/* Value type flags: + * BOND_VALFLAG_DEFAULT - mark the value as default + * BOND_VALFLAG_(MIN|MAX) - mark the value as min/max + */ +enum { + BOND_VALFLAG_DEFAULT = BIT(0), + BOND_VALFLAG_MIN = BIT(1), + BOND_VALFLAG_MAX = BIT(2) +}; + +/* Option IDs, their bit positions correspond to their IDs */ +enum { + BOND_OPT_MODE, + BOND_OPT_PACKETS_PER_SLAVE, + BOND_OPT_XMIT_HASH, + BOND_OPT_ARP_VALIDATE, + BOND_OPT_ARP_ALL_TARGETS, + BOND_OPT_FAIL_OVER_MAC, + BOND_OPT_ARP_INTERVAL, + BOND_OPT_ARP_TARGETS, + BOND_OPT_DOWNDELAY, + BOND_OPT_UPDELAY, + BOND_OPT_LACP_RATE, + BOND_OPT_MINLINKS, + BOND_OPT_AD_SELECT, + BOND_OPT_NUM_PEER_NOTIF, + BOND_OPT_MIIMON, + BOND_OPT_PRIMARY, + BOND_OPT_PRIMARY_RESELECT, + BOND_OPT_USE_CARRIER, + BOND_OPT_ACTIVE_SLAVE, + BOND_OPT_QUEUE_ID, + BOND_OPT_ALL_SLAVES_ACTIVE, + BOND_OPT_RESEND_IGMP, + BOND_OPT_LP_INTERVAL, + BOND_OPT_SLAVES, + BOND_OPT_LAST +}; + +/* This structure is used for storing option values and for passing option + * values when changing an option. The logic when used as an arg is as follows: + * - if string != NULL -> parse it, if the opt is RAW type then return it, else + * return the parse result + * - if string == NULL -> parse value + */ +struct bond_opt_value { + char *string; + u64 value; + u32 flags; +}; + +struct bonding; + +struct bond_option { + int id; + char *name; + char *desc; + u32 flags; + + /* unsuppmodes is used to denote modes in which the option isn't + * supported. + */ + unsigned long unsuppmodes; + /* supported values which this option can have, can be a subset of + * BOND_OPTVAL_RANGE's value range + */ + struct bond_opt_value *values; + + int (*set)(struct bonding *bond, struct bond_opt_value *val); +}; + +int __bond_opt_set(struct bonding *bond, unsigned int option, + struct bond_opt_value *val); +int bond_opt_tryset_rtnl(struct bonding *bond, unsigned int option, char *buf); +struct bond_opt_value *bond_opt_parse(const struct bond_option *opt, + struct bond_opt_value *val); +struct bond_option *bond_opt_get(unsigned int option); +struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val); + +/* This helper is used to initialize a bond_opt_value structure for parameter + * passing. There should be either a valid string or value, but not both. + * When value is ULLONG_MAX then string will be used. + */ +static inline void __bond_opt_init(struct bond_opt_value *optval, + char *string, u64 value) +{ + memset(optval, 0, sizeof(*optval)); + optval->value = ULLONG_MAX; + if (value == ULLONG_MAX) + optval->string = string; + else + optval->value = value; +} +#define bond_opt_initval(optval, value) __bond_opt_init(optval, NULL, value) +#define bond_opt_initstr(optval, str) __bond_opt_init(optval, str, ULLONG_MAX) + +int bond_option_mode_set(struct bonding *bond, struct bond_opt_value *newval); +int bond_option_pps_set(struct bonding *bond, struct bond_opt_value *newval); +int bond_option_xmit_hash_policy_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_arp_validate_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_arp_all_targets_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_fail_over_mac_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_arp_interval_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_arp_ip_targets_set(struct bonding *bond, + struct bond_opt_value *newval); +void bond_option_arp_ip_targets_clear(struct bonding *bond); +int bond_option_downdelay_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_updelay_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_lacp_rate_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_min_links_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_ad_select_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_num_peer_notif_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_miimon_set(struct bonding *bond, struct bond_opt_value *newval); +int bond_option_primary_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_primary_reselect_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_use_carrier_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_active_slave_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_queue_id_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_all_slaves_active_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_resend_igmp_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_lp_interval_set(struct bonding *bond, + struct bond_opt_value *newval); +int bond_option_slaves_set(struct bonding *bond, struct bond_opt_value *newval); +#endif /* _BOND_OPTIONS_H */ diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c index fb868d6c22da..3ac20e78eafc 100644 --- a/drivers/net/bonding/bond_procfs.c +++ b/drivers/net/bonding/bond_procfs.c @@ -65,6 +65,7 @@ static void bond_info_seq_stop(struct seq_file *seq, void *v) static void bond_info_show_master(struct seq_file *seq) { struct bonding *bond = seq->private; + struct bond_opt_value *optval; struct slave *curr; int i; @@ -76,26 +77,32 @@ static void bond_info_show_master(struct seq_file *seq) bond_mode_name(bond->params.mode)); if (bond->params.mode == BOND_MODE_ACTIVEBACKUP && - bond->params.fail_over_mac) - seq_printf(seq, " (fail_over_mac %s)", - fail_over_mac_tbl[bond->params.fail_over_mac].modename); + bond->params.fail_over_mac) { + optval = bond_opt_get_val(BOND_OPT_FAIL_OVER_MAC, + bond->params.fail_over_mac); + seq_printf(seq, " (fail_over_mac %s)", optval->string); + } seq_printf(seq, "\n"); if (bond->params.mode == BOND_MODE_XOR || bond->params.mode == BOND_MODE_8023AD) { + optval = bond_opt_get_val(BOND_OPT_XMIT_HASH, + bond->params.xmit_policy); seq_printf(seq, "Transmit Hash Policy: %s (%d)\n", - xmit_hashtype_tbl[bond->params.xmit_policy].modename, - bond->params.xmit_policy); + optval->string, bond->params.xmit_policy); } if (USES_PRIMARY(bond->params.mode)) { seq_printf(seq, "Primary Slave: %s", (bond->primary_slave) ? bond->primary_slave->dev->name : "None"); - if (bond->primary_slave) + if (bond->primary_slave) { + optval = bond_opt_get_val(BOND_OPT_PRIMARY_RESELECT, + bond->params.primary_reselect); seq_printf(seq, " (primary_reselect %s)", - pri_reselect_tbl[bond->params.primary_reselect].modename); + optval->string); + } seq_printf(seq, "\nCurrently Active Slave: %s\n", (curr) ? curr->dev->name : "None"); @@ -136,8 +143,10 @@ static void bond_info_show_master(struct seq_file *seq) seq_printf(seq, "LACP rate: %s\n", (bond->params.lacp_fast) ? "fast" : "slow"); seq_printf(seq, "Min links: %d\n", bond->params.min_links); + optval = bond_opt_get_val(BOND_OPT_AD_SELECT, + bond->params.ad_select); seq_printf(seq, "Aggregator selection policy (ad_select): %s\n", - ad_select_tbl[bond->params.ad_select].modename); + optval->string); if (__bond_3ad_get_active_agg_info(bond, &ad_info)) { seq_printf(seq, "bond %s has no active aggregator\n", @@ -159,18 +168,6 @@ static void bond_info_show_master(struct seq_file *seq) } } -static const char *bond_slave_link_status(s8 link) -{ - static const char * const status[] = { - [BOND_LINK_UP] = "up", - [BOND_LINK_FAIL] = "going down", - [BOND_LINK_DOWN] = "down", - [BOND_LINK_BACK] = "going back", - }; - - return status[link]; -} - static void bond_info_show_slave(struct seq_file *seq, const struct slave *slave) { diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index 0ae580bbc5db..643fcc110299 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -12,8 +12,7 @@ * for more details. * * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * with this program; if not, see <http://www.gnu.org/licenses/>. * * The full GNU General Public License is included in this distribution in the * file called LICENSE. @@ -40,7 +39,6 @@ #include <net/net_namespace.h> #include <net/netns/generic.h> #include <linux/nsproxy.h> -#include <linux/reciprocal_div.h> #include "bonding.h" @@ -202,58 +200,15 @@ static ssize_t bonding_store_slaves(struct device *d, struct device_attribute *attr, const char *buffer, size_t count) { - char command[IFNAMSIZ + 1] = { 0, }; - char *ifname; - int res, ret = count; - struct net_device *dev; struct bonding *bond = to_bond(d); + int ret; - if (!rtnl_trylock()) - return restart_syscall(); - - sscanf(buffer, "%16s", command); /* IFNAMSIZ*/ - ifname = command + 1; - if ((strlen(command) <= 1) || - !dev_valid_name(ifname)) - goto err_no_cmd; - - dev = __dev_get_by_name(dev_net(bond->dev), ifname); - if (!dev) { - pr_info("%s: Interface %s does not exist!\n", - bond->dev->name, ifname); - ret = -ENODEV; - goto out; - } - - switch (command[0]) { - case '+': - pr_info("%s: Adding slave %s.\n", bond->dev->name, dev->name); - res = bond_enslave(bond->dev, dev); - break; - - case '-': - pr_info("%s: Removing slave %s.\n", bond->dev->name, dev->name); - res = bond_release(bond->dev, dev); - break; - - default: - goto err_no_cmd; - } - - if (res) - ret = res; - goto out; - -err_no_cmd: - pr_err("no command found in slaves file for bond %s. Use +ifname or -ifname.\n", - bond->dev->name); - ret = -EPERM; + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_SLAVES, (char *)buffer); + if (!ret) + ret = count; -out: - rtnl_unlock(); return ret; } - static DEVICE_ATTR(slaves, S_IRUGO | S_IWUSR, bonding_show_slaves, bonding_store_slaves); @@ -265,37 +220,24 @@ static ssize_t bonding_show_mode(struct device *d, struct device_attribute *attr, char *buf) { struct bonding *bond = to_bond(d); + struct bond_opt_value *val; - return sprintf(buf, "%s %d\n", - bond_mode_tbl[bond->params.mode].modename, - bond->params.mode); + val = bond_opt_get_val(BOND_OPT_MODE, bond->params.mode); + + return sprintf(buf, "%s %d\n", val->string, bond->params.mode); } static ssize_t bonding_store_mode(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - int new_value, ret; struct bonding *bond = to_bond(d); + int ret; - 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); - return -EINVAL; - } - if (!rtnl_trylock()) - return restart_syscall(); - - 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); + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_MODE, (char *)buf); + if (!ret) ret = count; - } - rtnl_unlock(); return ret; } static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, @@ -309,31 +251,23 @@ static ssize_t bonding_show_xmit_hash(struct device *d, char *buf) { struct bonding *bond = to_bond(d); + struct bond_opt_value *val; - return sprintf(buf, "%s %d\n", - xmit_hashtype_tbl[bond->params.xmit_policy].modename, - bond->params.xmit_policy); + val = bond_opt_get_val(BOND_OPT_XMIT_HASH, bond->params.xmit_policy); + + return sprintf(buf, "%s %d\n", val->string, bond->params.xmit_policy); } static ssize_t bonding_store_xmit_hash(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - int new_value, ret = count; struct bonding *bond = to_bond(d); + int ret; - new_value = bond_parse_parm(buf, xmit_hashtype_tbl); - if (new_value < 0) { - pr_err("%s: Ignoring invalid xmit hash policy value %.*s.\n", - bond->dev->name, - (int)strlen(buf) - 1, buf); - ret = -EINVAL; - } else { - bond->params.xmit_policy = new_value; - pr_info("%s: setting xmit hash policy to %s (%d).\n", - bond->dev->name, - xmit_hashtype_tbl[new_value].modename, new_value); - } + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_XMIT_HASH, (char *)buf); + if (!ret) + ret = count; return ret; } @@ -348,10 +282,12 @@ static ssize_t bonding_show_arp_validate(struct device *d, char *buf) { struct bonding *bond = to_bond(d); + struct bond_opt_value *val; - return sprintf(buf, "%s %d\n", - arp_validate_tbl[bond->params.arp_validate].modename, - bond->params.arp_validate); + val = bond_opt_get_val(BOND_OPT_ARP_VALIDATE, + bond->params.arp_validate); + + return sprintf(buf, "%s %d\n", val->string, bond->params.arp_validate); } static ssize_t bonding_store_arp_validate(struct device *d, @@ -359,36 +295,11 @@ static ssize_t bonding_store_arp_validate(struct device *d, const char *buf, size_t count) { struct bonding *bond = to_bond(d); - int new_value, ret = count; + int ret; - if (!rtnl_trylock()) - return restart_syscall(); - new_value = bond_parse_parm(buf, arp_validate_tbl); - if (new_value < 0) { - pr_err("%s: Ignoring invalid arp_validate value %s\n", - bond->dev->name, buf); - ret = -EINVAL; - goto out; - } - if (bond->params.mode != BOND_MODE_ACTIVEBACKUP) { - pr_err("%s: arp_validate only supported in active-backup mode.\n", - bond->dev->name); - ret = -EINVAL; - goto out; - } - pr_info("%s: setting arp_validate to %s (%d).\n", - bond->dev->name, arp_validate_tbl[new_value].modename, - new_value); - - if (bond->dev->flags & IFF_UP) { - if (!new_value) - bond->recv_probe = NULL; - else if (bond->params.arp_interval) - bond->recv_probe = bond_arp_rcv; - } - bond->params.arp_validate = new_value; -out: - rtnl_unlock(); + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ARP_VALIDATE, (char *)buf); + if (!ret) + ret = count; return ret; } @@ -403,10 +314,12 @@ static ssize_t bonding_show_arp_all_targets(struct device *d, char *buf) { struct bonding *bond = to_bond(d); - int value = bond->params.arp_all_targets; + struct bond_opt_value *val; - return sprintf(buf, "%s %d\n", arp_all_targets_tbl[value].modename, - value); + val = bond_opt_get_val(BOND_OPT_ARP_ALL_TARGETS, + bond->params.arp_all_targets); + return sprintf(buf, "%s %d\n", + val->string, bond->params.arp_all_targets); } static ssize_t bonding_store_arp_all_targets(struct device *d, @@ -414,21 +327,13 @@ static ssize_t bonding_store_arp_all_targets(struct device *d, const char *buf, size_t count) { struct bonding *bond = to_bond(d); - int new_value; - - new_value = bond_parse_parm(buf, arp_all_targets_tbl); - if (new_value < 0) { - pr_err("%s: Ignoring invalid arp_all_targets value %s\n", - bond->dev->name, buf); - return -EINVAL; - } - pr_info("%s: setting arp_all_targets to %s (%d).\n", - bond->dev->name, arp_all_targets_tbl[new_value].modename, - new_value); + int ret; - bond->params.arp_all_targets = new_value; + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ARP_ALL_TARGETS, (char *)buf); + if (!ret) + ret = count; - return count; + return ret; } static DEVICE_ATTR(arp_all_targets, S_IRUGO | S_IWUSR, @@ -443,44 +348,25 @@ static ssize_t bonding_show_fail_over_mac(struct device *d, char *buf) { struct bonding *bond = to_bond(d); + struct bond_opt_value *val; - return sprintf(buf, "%s %d\n", - fail_over_mac_tbl[bond->params.fail_over_mac].modename, - bond->params.fail_over_mac); + val = bond_opt_get_val(BOND_OPT_FAIL_OVER_MAC, + bond->params.fail_over_mac); + + return sprintf(buf, "%s %d\n", val->string, bond->params.fail_over_mac); } static ssize_t bonding_store_fail_over_mac(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - int new_value, ret = count; struct bonding *bond = to_bond(d); + int ret; - if (!rtnl_trylock()) - return restart_syscall(); - - if (bond_has_slaves(bond)) { - pr_err("%s: Can't alter fail_over_mac with slaves in bond.\n", - bond->dev->name); - ret = -EPERM; - goto out; - } - - new_value = bond_parse_parm(buf, fail_over_mac_tbl); - if (new_value < 0) { - pr_err("%s: Ignoring invalid fail_over_mac value %s.\n", - bond->dev->name, buf); - ret = -EINVAL; - goto out; - } - - bond->params.fail_over_mac = new_value; - pr_info("%s: Setting fail_over_mac to %s (%d).\n", - bond->dev->name, fail_over_mac_tbl[new_value].modename, - new_value); + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_FAIL_OVER_MAC, (char *)buf); + if (!ret) + ret = count; -out: - rtnl_unlock(); return ret; } @@ -507,61 +393,12 @@ static ssize_t bonding_store_arp_interval(struct device *d, const char *buf, size_t count) { struct bonding *bond = to_bond(d); - int new_value, ret = count; + int ret; + + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ARP_INTERVAL, (char *)buf); + if (!ret) + ret = count; - 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); - ret = -EINVAL; - goto out; - } - if (new_value < 0) { - 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_NO_USES_ARP(bond->params.mode)) { - 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; - } - pr_info("%s: Setting ARP monitoring interval to %d.\n", - bond->dev->name, new_value); - bond->params.arp_interval = new_value; - 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->dev->flags & IFF_UP) { - /* If the interface is up, we may need to fire off - * the ARP timer. If the interface is down, the - * timer will get fired off when the open function - * is called. - */ - if (!new_value) { - if (bond->params.arp_validate) - bond->recv_probe = NULL; - cancel_delayed_work_sync(&bond->arp_work); - } else { - /* arp_validate can be set only in active-backup mode */ - if (bond->params.arp_validate) - bond->recv_probe = bond_arp_rcv; - 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, @@ -574,8 +411,8 @@ static ssize_t bonding_show_arp_targets(struct device *d, struct device_attribute *attr, char *buf) { - int i, res = 0; struct bonding *bond = to_bond(d); + int i, res = 0; for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) { if (bond->params.arp_targets[i]) @@ -584,6 +421,7 @@ static ssize_t bonding_show_arp_targets(struct device *d, } if (res) buf[res-1] = '\n'; /* eat the leftover space */ + return res; } @@ -592,82 +430,12 @@ static ssize_t bonding_store_arp_targets(struct device *d, const char *buf, size_t count) { struct bonding *bond = to_bond(d); - struct list_head *iter; - struct slave *slave; - __be32 newtarget, *targets; - unsigned long *targets_rx; - int ind, i, j, ret = -EINVAL; - - if (!rtnl_trylock()) - return restart_syscall(); - - targets = bond->params.arp_targets; - if (!in4_pton(buf + 1, -1, (u8 *)&newtarget, -1, NULL) || - IS_IP_TARGET_UNUSABLE_ADDRESS(newtarget)) { - pr_err("%s: invalid ARP target %pI4 specified for addition\n", - bond->dev->name, &newtarget); - goto out; - } - /* look for adds */ - if (buf[0] == '+') { - if (bond_get_targets_ip(targets, newtarget) != -1) { /* dup */ - pr_err("%s: ARP target %pI4 is already present\n", - bond->dev->name, &newtarget); - goto out; - } - - ind = bond_get_targets_ip(targets, 0); /* first free slot */ - if (ind == -1) { - pr_err("%s: ARP target table is full!\n", - bond->dev->name); - goto out; - } - - pr_info("%s: adding ARP target %pI4.\n", bond->dev->name, - &newtarget); - /* not to race with bond_arp_rcv */ - write_lock_bh(&bond->lock); - bond_for_each_slave(bond, slave, iter) - slave->target_last_arp_rx[ind] = jiffies; - targets[ind] = newtarget; - write_unlock_bh(&bond->lock); - } else if (buf[0] == '-') { - ind = bond_get_targets_ip(targets, newtarget); - if (ind == -1) { - pr_err("%s: unable to remove nonexistent ARP target %pI4.\n", - bond->dev->name, &newtarget); - goto out; - } - - if (ind == 0 && !targets[1] && bond->params.arp_interval) - pr_warn("%s: removing last arp target with arp_interval on\n", - bond->dev->name); - - pr_info("%s: removing ARP target %pI4.\n", bond->dev->name, - &newtarget); + int ret; - write_lock_bh(&bond->lock); - bond_for_each_slave(bond, slave, iter) { - targets_rx = slave->target_last_arp_rx; - j = ind; - for (; (j < BOND_MAX_ARP_TARGETS-1) && targets[j+1]; j++) - targets_rx[j] = targets_rx[j+1]; - targets_rx[j] = 0; - } - for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++) - targets[i] = targets[i+1]; - targets[i] = 0; - write_unlock_bh(&bond->lock); - } else { - pr_err("no command found in arp_ip_targets file for bond %s. Use +<addr> or -<addr>.\n", - bond->dev->name); - ret = -EPERM; - goto out; - } + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ARP_TARGETS, (char *)buf); + if (!ret) + ret = count; - ret = count; -out: - rtnl_unlock(); return ret; } static DEVICE_ATTR(arp_ip_target, S_IRUGO | S_IWUSR , bonding_show_arp_targets, bonding_store_arp_targets); @@ -690,45 +458,13 @@ static ssize_t bonding_store_downdelay(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - int new_value, ret = count; struct bonding *bond = to_bond(d); + int ret; - 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); - ret = -EPERM; - goto out; - } - - if (sscanf(buf, "%d", &new_value) != 1) { - pr_err("%s: no down delay value specified.\n", bond->dev->name); - ret = -EINVAL; - 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, 0, INT_MAX); - ret = -EINVAL; - goto out; - } else { - if ((new_value % bond->params.miimon) != 0) { - pr_warning("%s: Warning: down delay (%d) is not a multiple of miimon (%d), delay rounded to %d ms\n", - bond->dev->name, new_value, - bond->params.miimon, - (new_value / bond->params.miimon) * - bond->params.miimon); - } - bond->params.downdelay = new_value / bond->params.miimon; - pr_info("%s: Setting down delay to %d.\n", - bond->dev->name, - bond->params.downdelay * bond->params.miimon); - - } + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_DOWNDELAY, (char *)buf); + if (!ret) + ret = count; -out: - rtnl_unlock(); return ret; } static DEVICE_ATTR(downdelay, S_IRUGO | S_IWUSR, @@ -748,45 +484,13 @@ static ssize_t bonding_store_updelay(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - int new_value, ret = count; struct bonding *bond = to_bond(d); + int ret; - 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); - ret = -EPERM; - goto out; - } - - if (sscanf(buf, "%d", &new_value) != 1) { - pr_err("%s: no up delay value specified.\n", - bond->dev->name); - ret = -EINVAL; - goto out; - } - if (new_value < 0) { - 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 { - if ((new_value % bond->params.miimon) != 0) { - pr_warning("%s: Warning: up delay (%d) is not a multiple of miimon (%d), updelay rounded to %d ms\n", - bond->dev->name, new_value, - bond->params.miimon, - (new_value / bond->params.miimon) * - bond->params.miimon); - } - bond->params.updelay = new_value / bond->params.miimon; - pr_info("%s: Setting up delay to %d.\n", - bond->dev->name, - bond->params.updelay * bond->params.miimon); - } + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_UPDELAY, (char *)buf); + if (!ret) + ret = count; -out: - rtnl_unlock(); return ret; } static DEVICE_ATTR(updelay, S_IRUGO | S_IWUSR, @@ -801,10 +505,11 @@ static ssize_t bonding_show_lacp(struct device *d, char *buf) { struct bonding *bond = to_bond(d); + struct bond_opt_value *val; - return sprintf(buf, "%s %d\n", - bond_lacp_tbl[bond->params.lacp_fast].modename, - bond->params.lacp_fast); + val = bond_opt_get_val(BOND_OPT_LACP_RATE, bond->params.lacp_fast); + + return sprintf(buf, "%s %d\n", val->string, bond->params.lacp_fast); } static ssize_t bonding_store_lacp(struct device *d, @@ -812,40 +517,11 @@ static ssize_t bonding_store_lacp(struct device *d, const char *buf, size_t count) { struct bonding *bond = to_bond(d); - int new_value, ret = count; - - if (!rtnl_trylock()) - return restart_syscall(); - - if (bond->dev->flags & IFF_UP) { - pr_err("%s: Unable to update LACP rate because interface is up.\n", - bond->dev->name); - ret = -EPERM; - goto out; - } - - if (bond->params.mode != BOND_MODE_8023AD) { - pr_err("%s: Unable to update LACP rate because bond is not in 802.3ad mode.\n", - bond->dev->name); - ret = -EPERM; - goto out; - } + int ret; - new_value = bond_parse_parm(buf, bond_lacp_tbl); - - 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); - } else { - pr_err("%s: Ignoring invalid LACP rate value %.*s.\n", - bond->dev->name, (int)strlen(buf) - 1, buf); - ret = -EINVAL; - } -out: - rtnl_unlock(); + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_LACP_RATE, (char *)buf); + if (!ret) + ret = count; return ret; } @@ -867,19 +543,12 @@ static ssize_t bonding_store_min_links(struct device *d, { 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; - } + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_MINLINKS, (char *)buf); + if (!ret) + ret = count; - pr_info("%s: Setting min links value to %u\n", - bond->dev->name, new_value); - bond->params.min_links = new_value; - return count; + return ret; } static DEVICE_ATTR(min_links, S_IRUGO | S_IWUSR, bonding_show_min_links, bonding_store_min_links); @@ -889,10 +558,11 @@ static ssize_t bonding_show_ad_select(struct device *d, char *buf) { struct bonding *bond = to_bond(d); + struct bond_opt_value *val; - return sprintf(buf, "%s %d\n", - ad_select_tbl[bond->params.ad_select].modename, - bond->params.ad_select); + val = bond_opt_get_val(BOND_OPT_AD_SELECT, bond->params.ad_select); + + return sprintf(buf, "%s %d\n", val->string, bond->params.ad_select); } @@ -900,29 +570,13 @@ static ssize_t bonding_store_ad_select(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - int new_value, ret = count; struct bonding *bond = to_bond(d); + int ret; - if (bond->dev->flags & IFF_UP) { - pr_err("%s: Unable to update ad_select because interface is up.\n", - bond->dev->name); - ret = -EPERM; - goto out; - } + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_AD_SELECT, (char *)buf); + if (!ret) + ret = count; - new_value = bond_parse_parm(buf, ad_select_tbl); - - if (new_value != -1) { - bond->params.ad_select = new_value; - pr_info("%s: Setting ad_select to %s (%d).\n", - bond->dev->name, ad_select_tbl[new_value].modename, - new_value); - } else { - pr_err("%s: Ignoring invalid ad_select value %.*s.\n", - bond->dev->name, (int)strlen(buf) - 1, buf); - ret = -EINVAL; - } -out: return ret; } static DEVICE_ATTR(ad_select, S_IRUGO | S_IWUSR, @@ -944,8 +598,13 @@ static ssize_t bonding_store_num_peer_notif(struct device *d, const char *buf, size_t count) { struct bonding *bond = to_bond(d); - int err = kstrtou8(buf, 10, &bond->params.num_peer_notif); - return err ? err : count; + int ret; + + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_NUM_PEER_NOTIF, (char *)buf); + if (!ret) + ret = count; + + return ret; } static DEVICE_ATTR(num_grat_arp, S_IRUGO | S_IWUSR, bonding_show_num_peer_notif, bonding_store_num_peer_notif); @@ -971,56 +630,13 @@ static ssize_t bonding_store_miimon(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - int new_value, ret = count; struct bonding *bond = to_bond(d); + int ret; + + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_MIIMON, (char *)buf); + if (!ret) + ret = count; - if (!rtnl_trylock()) - return restart_syscall(); - if (sscanf(buf, "%d", &new_value) != 1) { - pr_err("%s: no miimon value specified.\n", - bond->dev->name); - ret = -EINVAL; - goto out; - } - if (new_value < 0) { - pr_err("%s: Invalid miimon value %d not in range %d-%d; rejected.\n", - bond->dev->name, new_value, 0, INT_MAX); - ret = -EINVAL; - goto out; - } - 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, @@ -1051,58 +667,13 @@ static ssize_t bonding_store_primary(struct device *d, const char *buf, size_t count) { struct bonding *bond = to_bond(d); - struct list_head *iter; - char ifname[IFNAMSIZ]; - struct slave *slave; - - if (!rtnl_trylock()) - return restart_syscall(); - 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 set primary 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 primary */ - if (!strlen(ifname) || buf[0] == '\n') { - pr_info("%s: Setting primary slave to None.\n", - bond->dev->name); - bond->primary_slave = NULL; - memset(bond->params.primary, 0, sizeof(bond->params.primary)); - bond_select_active_slave(bond); - goto out; - } - - bond_for_each_slave(bond, slave, iter) { - if (strncmp(slave->dev->name, ifname, IFNAMSIZ) == 0) { - pr_info("%s: Setting %s as primary slave.\n", - bond->dev->name, slave->dev->name); - bond->primary_slave = slave; - strcpy(bond->params.primary, slave->dev->name); - bond_select_active_slave(bond); - goto out; - } - } - - strncpy(bond->params.primary, ifname, IFNAMSIZ); - bond->params.primary[IFNAMSIZ - 1] = 0; + int ret; - pr_info("%s: Recording %s as primary, " - "but it has not been enslaved to %s yet.\n", - bond->dev->name, ifname, bond->dev->name); -out: - write_unlock_bh(&bond->curr_slave_lock); - read_unlock(&bond->lock); - unblock_netpoll_tx(); - rtnl_unlock(); + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_PRIMARY, (char *)buf); + if (!ret) + ret = count; - return count; + return ret; } static DEVICE_ATTR(primary, S_IRUGO | S_IWUSR, bonding_show_primary, bonding_store_primary); @@ -1115,45 +686,27 @@ static ssize_t bonding_show_primary_reselect(struct device *d, char *buf) { struct bonding *bond = to_bond(d); + struct bond_opt_value *val; + + val = bond_opt_get_val(BOND_OPT_PRIMARY_RESELECT, + bond->params.primary_reselect); return sprintf(buf, "%s %d\n", - pri_reselect_tbl[bond->params.primary_reselect].modename, - bond->params.primary_reselect); + val->string, bond->params.primary_reselect); } static ssize_t bonding_store_primary_reselect(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - int new_value, ret = count; struct bonding *bond = to_bond(d); + int ret; - if (!rtnl_trylock()) - return restart_syscall(); - - new_value = bond_parse_parm(buf, pri_reselect_tbl); - if (new_value < 0) { - pr_err("%s: Ignoring invalid primary_reselect value %.*s.\n", - bond->dev->name, - (int) strlen(buf) - 1, buf); - ret = -EINVAL; - goto out; - } + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_PRIMARY_RESELECT, + (char *)buf); + if (!ret) + ret = count; - bond->params.primary_reselect = new_value; - pr_info("%s: setting primary_reselect to %s (%d).\n", - bond->dev->name, pri_reselect_tbl[new_value].modename, - new_value); - - block_netpoll_tx(); - read_lock(&bond->lock); - write_lock_bh(&bond->curr_slave_lock); - bond_select_active_slave(bond); - write_unlock_bh(&bond->curr_slave_lock); - read_unlock(&bond->lock); - unblock_netpoll_tx(); -out: - rtnl_unlock(); return ret; } static DEVICE_ATTR(primary_reselect, S_IRUGO | S_IWUSR, @@ -1176,25 +729,13 @@ static ssize_t bonding_store_carrier(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - int new_value, ret = count; struct bonding *bond = to_bond(d); + int ret; + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_USE_CARRIER, (char *)buf); + if (!ret) + ret = count; - if (sscanf(buf, "%d", &new_value) != 1) { - pr_err("%s: no use_carrier value specified.\n", - bond->dev->name); - ret = -EINVAL; - goto out; - } - if ((new_value == 0) || (new_value == 1)) { - bond->params.use_carrier = new_value; - pr_info("%s: Setting use_carrier to %d.\n", - bond->dev->name, new_value); - } else { - pr_info("%s: Ignoring invalid use_carrier value %d.\n", - bond->dev->name, new_value); - } -out: return ret; } static DEVICE_ATTR(use_carrier, S_IRUGO | S_IWUSR, @@ -1225,34 +766,14 @@ static ssize_t bonding_store_active_slave(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - int ret; struct bonding *bond = to_bond(d); - char ifname[IFNAMSIZ]; - struct net_device *dev; - - if (!rtnl_trylock()) - return restart_syscall(); - - sscanf(buf, "%15s", ifname); /* IFNAMSIZ */ - if (!strlen(ifname) || buf[0] == '\n') { - dev = NULL; - } else { - dev = __dev_get_by_name(dev_net(bond->dev), ifname); - if (!dev) { - ret = -ENODEV; - goto out; - } - } + int ret; - ret = bond_option_active_slave_set(bond, dev); + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ACTIVE_SLAVE, (char *)buf); if (!ret) ret = count; - out: - rtnl_unlock(); - return ret; - } static DEVICE_ATTR(active_slave, S_IRUGO | S_IWUSR, bonding_show_active_slave, bonding_store_active_slave); @@ -1421,72 +942,15 @@ static ssize_t bonding_store_queue_id(struct device *d, struct device_attribute *attr, const char *buffer, size_t count) { - struct slave *slave, *update_slave; struct bonding *bond = to_bond(d); - struct list_head *iter; - u16 qid; - int ret = count; - char *delim; - struct net_device *sdev = NULL; - - if (!rtnl_trylock()) - return restart_syscall(); - - /* delim will point to queue id if successful */ - delim = strchr(buffer, ':'); - if (!delim) - goto err_no_cmd; - - /* - * Terminate string that points to device name and bump it - * up one, so we can read the queue id there. - */ - *delim = '\0'; - if (sscanf(++delim, "%hd\n", &qid) != 1) - goto err_no_cmd; - - /* Check buffer length, valid ifname and queue id */ - if (strlen(buffer) > IFNAMSIZ || - !dev_valid_name(buffer) || - qid > bond->dev->real_num_tx_queues) - goto err_no_cmd; - - /* Get the pointer to that interface if it exists */ - sdev = __dev_get_by_name(dev_net(bond->dev), buffer); - if (!sdev) - goto err_no_cmd; - - /* Search for thes slave and check for duplicate qids */ - update_slave = NULL; - bond_for_each_slave(bond, slave, iter) { - if (sdev == slave->dev) - /* - * We don't need to check the matching - * slave for dups, since we're overwriting it - */ - update_slave = slave; - else if (qid && qid == slave->queue_id) { - goto err_no_cmd; - } - } - - if (!update_slave) - goto err_no_cmd; + int ret; - /* Actually set the qids for the slave */ - update_slave->queue_id = qid; + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_QUEUE_ID, (char *)buffer); + if (!ret) + ret = count; -out: - rtnl_unlock(); return ret; - -err_no_cmd: - pr_info("invalid input for queue_id set for %s.\n", - bond->dev->name); - ret = -EPERM; - goto out; } - static DEVICE_ATTR(queue_id, S_IRUGO | S_IWUSR, bonding_show_queue_id, bonding_store_queue_id); @@ -1508,42 +972,13 @@ static ssize_t bonding_store_slaves_active(struct device *d, const char *buf, size_t count) { struct bonding *bond = to_bond(d); - int new_value, ret = count; - struct list_head *iter; - struct slave *slave; - - if (!rtnl_trylock()) - return restart_syscall(); - - if (sscanf(buf, "%d", &new_value) != 1) { - pr_err("%s: no all_slaves_active value specified.\n", - bond->dev->name); - ret = -EINVAL; - goto out; - } - - if (new_value == bond->params.all_slaves_active) - goto out; + int ret; - if ((new_value == 0) || (new_value == 1)) { - bond->params.all_slaves_active = new_value; - } else { - pr_info("%s: Ignoring invalid all_slaves_active value %d.\n", - bond->dev->name, new_value); - ret = -EINVAL; - goto out; - } + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ALL_SLAVES_ACTIVE, + (char *)buf); + if (!ret) + ret = count; - bond_for_each_slave(bond, slave, iter) { - if (!bond_is_active_slave(slave)) { - if (new_value) - slave->inactive = 0; - else - slave->inactive = 1; - } - } -out: - rtnl_unlock(); return ret; } static DEVICE_ATTR(all_slaves_active, S_IRUGO | S_IWUSR, @@ -1565,27 +1000,13 @@ static ssize_t bonding_store_resend_igmp(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - int new_value, ret = count; struct bonding *bond = to_bond(d); + int ret; - if (sscanf(buf, "%d", &new_value) != 1) { - pr_err("%s: no resend_igmp value specified.\n", - bond->dev->name); - ret = -EINVAL; - goto out; - } - - if (new_value < 0 || new_value > 255) { - pr_err("%s: Invalid resend_igmp value %d not in range 0-255; rejected.\n", - bond->dev->name, new_value); - ret = -EINVAL; - goto out; - } + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_RESEND_IGMP, (char *)buf); + if (!ret) + ret = count; - pr_info("%s: Setting resend_igmp to %d.\n", - bond->dev->name, new_value); - bond->params.resend_igmp = new_value; -out: return ret; } @@ -1606,24 +1027,12 @@ static ssize_t bonding_store_lp_interval(struct device *d, const char *buf, size_t count) { struct bonding *bond = to_bond(d); - int new_value, ret = count; - - if (sscanf(buf, "%d", &new_value) != 1) { - pr_err("%s: no lp interval value specified.\n", - bond->dev->name); - ret = -EINVAL; - goto out; - } + int ret; - if (new_value <= 0) { - pr_err ("%s: lp_interval must be between 1 and %d\n", - bond->dev->name, INT_MAX); - ret = -EINVAL; - goto out; - } + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_LP_INTERVAL, (char *)buf); + if (!ret) + ret = count; - bond->params.lp_interval = new_value; -out: return ret; } @@ -1636,10 +1045,6 @@ static ssize_t bonding_show_packets_per_slave(struct device *d, { struct bonding *bond = to_bond(d); unsigned int packets_per_slave = bond->params.packets_per_slave; - - if (packets_per_slave > 1) - packets_per_slave = reciprocal_value(packets_per_slave); - return sprintf(buf, "%u\n", packets_per_slave); } @@ -1648,28 +1053,13 @@ static ssize_t bonding_store_packets_per_slave(struct device *d, const char *buf, size_t count) { struct bonding *bond = to_bond(d); - int new_value, ret = count; + int ret; + + ret = bond_opt_tryset_rtnl(bond, BOND_OPT_PACKETS_PER_SLAVE, + (char *)buf); + if (!ret) + ret = count; - if (sscanf(buf, "%d", &new_value) != 1) { - pr_err("%s: no packets_per_slave value specified.\n", - bond->dev->name); - ret = -EINVAL; - goto out; - } - if (new_value < 0 || new_value > USHRT_MAX) { - pr_err("%s: packets_per_slave must be between 0 and %u\n", - bond->dev->name, USHRT_MAX); - ret = -EINVAL; - goto out; - } - if (bond->params.mode != BOND_MODE_ROUNDROBIN) - pr_warn("%s: Warning: packets_per_slave has effect only in balance-rr mode\n", - bond->dev->name); - if (new_value > 1) - bond->params.packets_per_slave = reciprocal_value(new_value); - else - bond->params.packets_per_slave = new_value; -out: return ret; } diff --git a/drivers/net/bonding/bond_sysfs_slave.c b/drivers/net/bonding/bond_sysfs_slave.c new file mode 100644 index 000000000000..2e4eec5450c8 --- /dev/null +++ b/drivers/net/bonding/bond_sysfs_slave.c @@ -0,0 +1,144 @@ +/* Sysfs attributes of bond slaves + * + * Copyright (c) 2014 Scott Feldman <sfeldma@cumulusnetworks.com> + * + * 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. + */ + +#include <linux/capability.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> + +#include "bonding.h" + +struct slave_attribute { + struct attribute attr; + ssize_t (*show)(struct slave *, char *); +}; + +#define SLAVE_ATTR(_name, _mode, _show) \ +const struct slave_attribute slave_attr_##_name = { \ + .attr = {.name = __stringify(_name), \ + .mode = _mode }, \ + .show = _show, \ +}; +#define SLAVE_ATTR_RO(_name) \ + SLAVE_ATTR(_name, S_IRUGO, _name##_show) + +static ssize_t state_show(struct slave *slave, char *buf) +{ + switch (bond_slave_state(slave)) { + case BOND_STATE_ACTIVE: + return sprintf(buf, "active\n"); + case BOND_STATE_BACKUP: + return sprintf(buf, "backup\n"); + default: + return sprintf(buf, "UNKONWN\n"); + } +} +static SLAVE_ATTR_RO(state); + +static ssize_t mii_status_show(struct slave *slave, char *buf) +{ + return sprintf(buf, "%s\n", bond_slave_link_status(slave->link)); +} +static SLAVE_ATTR_RO(mii_status); + +static ssize_t link_failure_count_show(struct slave *slave, char *buf) +{ + return sprintf(buf, "%d\n", slave->link_failure_count); +} +static SLAVE_ATTR_RO(link_failure_count); + +static ssize_t perm_hwaddr_show(struct slave *slave, char *buf) +{ + return sprintf(buf, "%pM\n", slave->perm_hwaddr); +} +static SLAVE_ATTR_RO(perm_hwaddr); + +static ssize_t queue_id_show(struct slave *slave, char *buf) +{ + return sprintf(buf, "%d\n", slave->queue_id); +} +static SLAVE_ATTR_RO(queue_id); + +static ssize_t ad_aggregator_id_show(struct slave *slave, char *buf) +{ + const struct aggregator *agg; + + if (slave->bond->params.mode == BOND_MODE_8023AD) { + agg = SLAVE_AD_INFO(slave).port.aggregator; + if (agg) + return sprintf(buf, "%d\n", + agg->aggregator_identifier); + } + + return sprintf(buf, "N/A\n"); +} +static SLAVE_ATTR_RO(ad_aggregator_id); + +static const struct slave_attribute *slave_attrs[] = { + &slave_attr_state, + &slave_attr_mii_status, + &slave_attr_link_failure_count, + &slave_attr_perm_hwaddr, + &slave_attr_queue_id, + &slave_attr_ad_aggregator_id, + NULL +}; + +#define to_slave_attr(_at) container_of(_at, struct slave_attribute, attr) +#define to_slave(obj) container_of(obj, struct slave, kobj) + +static ssize_t slave_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct slave_attribute *slave_attr = to_slave_attr(attr); + struct slave *slave = to_slave(kobj); + + return slave_attr->show(slave, buf); +} + +static const struct sysfs_ops slave_sysfs_ops = { + .show = slave_show, +}; + +static struct kobj_type slave_ktype = { +#ifdef CONFIG_SYSFS + .sysfs_ops = &slave_sysfs_ops, +#endif +}; + +int bond_sysfs_slave_add(struct slave *slave) +{ + const struct slave_attribute **a; + int err; + + err = kobject_init_and_add(&slave->kobj, &slave_ktype, + &(slave->dev->dev.kobj), "bonding_slave"); + if (err) + return err; + + for (a = slave_attrs; *a; ++a) { + err = sysfs_create_file(&slave->kobj, &((*a)->attr)); + if (err) { + kobject_del(&slave->kobj); + return err; + } + } + + return 0; +} + +void bond_sysfs_slave_del(struct slave *slave) +{ + const struct slave_attribute **a; + + for (a = slave_attrs; *a; ++a) + sysfs_remove_file(&slave->kobj, &((*a)->attr)); + + kobject_del(&slave->kobj); +} diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index a9f4f9f4d8ce..1a9062f4e0d6 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -23,8 +23,11 @@ #include <linux/netpoll.h> #include <linux/inetdevice.h> #include <linux/etherdevice.h> +#include <linux/reciprocal_div.h> + #include "bond_3ad.h" #include "bond_alb.h" +#include "bond_options.h" #define DRV_VERSION "3.7.1" #define DRV_RELDATE "April 27, 2011" @@ -101,6 +104,10 @@ netdev_adjacent_get_private(bond_slave_list(bond)->prev) : \ NULL) +/* Caller must have rcu_read_lock */ +#define bond_first_slave_rcu(bond) \ + netdev_lower_get_first_private_rcu(bond->dev) + #define bond_is_first_slave(bond, pos) (pos == bond_first_slave(bond)) #define bond_is_last_slave(bond, pos) (pos == bond_last_slave(bond)) @@ -167,6 +174,7 @@ struct bond_params { int resend_igmp; int lp_interval; int packets_per_slave; + struct reciprocal_value reciprocal_packets_per_slave; }; struct bond_parm_tbl { @@ -199,6 +207,7 @@ struct slave { #ifdef CONFIG_NET_POLL_CONTROLLER struct netpoll *np; #endif + struct kobject kobj; }; /* @@ -280,12 +289,18 @@ static inline bool bond_is_lb(const struct bonding *bond) static inline void bond_set_active_slave(struct slave *slave) { - slave->backup = 0; + if (slave->backup) { + slave->backup = 0; + rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_KERNEL); + } } static inline void bond_set_backup_slave(struct slave *slave) { - slave->backup = 1; + if (!slave->backup) { + slave->backup = 1; + rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_KERNEL); + } } static inline int bond_slave_state(struct slave *slave) @@ -394,8 +409,8 @@ static inline __be32 bond_confirm_addr(struct net_device *dev, __be32 dst, __be3 in_dev = __in_dev_get_rcu(dev); if (in_dev) - addr = inet_confirm_addr(in_dev, dst, local, RT_SCOPE_HOST); - + addr = inet_confirm_addr(dev_net(dev), in_dev, dst, local, + RT_SCOPE_HOST); rcu_read_unlock(); return addr; } @@ -412,19 +427,18 @@ static inline bool slave_can_tx(struct slave *slave) struct bond_net; int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, struct slave *slave); -int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev); -void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int slave_id); +void bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev); int bond_create(struct net *net, const char *name); int bond_create_sysfs(struct bond_net *net); void bond_destroy_sysfs(struct bond_net *net); void bond_prepare_sysfs_group(struct bonding *bond); +int bond_sysfs_slave_add(struct slave *slave); +void bond_sysfs_slave_del(struct slave *slave); int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev); int bond_release(struct net_device *bond_dev, struct net_device *slave_dev); -void bond_mii_monitor(struct work_struct *); -void bond_loadbalance_arp_mon(struct work_struct *); -void bond_activebackup_arp_mon(struct work_struct *); int bond_xmit_hash(struct bonding *bond, struct sk_buff *skb, int count); int bond_parse_parm(const char *mode_arg, const struct bond_parm_tbl *tbl); +int bond_parm_tbl_lookup(int mode, const struct bond_parm_tbl *tbl); void bond_select_active_slave(struct bonding *bond); void bond_change_active_slave(struct bonding *bond, struct slave *new_active); void bond_create_debugfs(void); @@ -437,10 +451,11 @@ void bond_setup(struct net_device *bond_dev); unsigned int bond_get_num_tx_queues(void); int bond_netlink_init(void); void bond_netlink_fini(void); -int bond_option_mode_set(struct bonding *bond, int mode); -int bond_option_active_slave_set(struct bonding *bond, struct net_device *slave_dev); +int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target); +int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target); struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond); struct net_device *bond_option_active_slave_get(struct bonding *bond); +const char *bond_slave_link_status(s8 link); struct bond_net { struct net * net; /* Associated network namespace */ @@ -520,7 +535,6 @@ static inline int bond_get_targets_ip(__be32 *targets, __be32 ip) /* exported from bond_main.c */ extern int bond_net_id; extern const struct bond_parm_tbl bond_lacp_tbl[]; -extern const struct bond_parm_tbl bond_mode_tbl[]; extern const struct bond_parm_tbl xmit_hashtype_tbl[]; extern const struct bond_parm_tbl arp_validate_tbl[]; extern const struct bond_parm_tbl arp_all_targets_tbl[]; |