]> err.no Git - linux-2.6/blobdiff - drivers/net/bonding/bond_alb.c
bonding: Convert more locks to _bh, acquire rtnl, for new locking
[linux-2.6] / drivers / net / bonding / bond_alb.c
index 217a2eedee0aafc143e5533aff617b70693927cb..6db5d763f6b612c02685e076b37b0b1d180e2fb0 100644 (file)
@@ -87,27 +87,32 @@ static const int alb_delta_in_ticks = HZ / ALB_TIMER_TICKS_PER_SEC;
 struct learning_pkt {
        u8 mac_dst[ETH_ALEN];
        u8 mac_src[ETH_ALEN];
-       u16 type;
+       __be16 type;
        u8 padding[ETH_ZLEN - ETH_HLEN];
 };
 
 struct arp_pkt {
-       u16     hw_addr_space;
-       u16     prot_addr_space;
+       __be16  hw_addr_space;
+       __be16  prot_addr_space;
        u8      hw_addr_len;
        u8      prot_addr_len;
-       u16     op_code;
+       __be16  op_code;
        u8      mac_src[ETH_ALEN];      /* sender hardware address */
-       u32     ip_src;                 /* sender IP address */
+       __be32  ip_src;                 /* sender IP address */
        u8      mac_dst[ETH_ALEN];      /* target hardware address */
-       u32     ip_dst;                 /* target IP address */
+       __be32  ip_dst;                 /* target IP address */
 };
 #pragma pack()
 
+static inline struct arp_pkt *arp_pkt(const struct sk_buff *skb)
+{
+       return (struct arp_pkt *)skb_network_header(skb);
+}
+
 /* Forward declaration */
 static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[]);
 
-static inline u8 _simple_hash(u8 *hash_start, int hash_size)
+static inline u8 _simple_hash(const u8 *hash_start, int hash_size)
 {
        int i;
        u8 hash = 0;
@@ -123,12 +128,12 @@ static inline u8 _simple_hash(u8 *hash_start, int hash_size)
 
 static inline void _lock_tx_hashtbl(struct bonding *bond)
 {
-       spin_lock(&(BOND_ALB_INFO(bond).tx_hashtbl_lock));
+       spin_lock_bh(&(BOND_ALB_INFO(bond).tx_hashtbl_lock));
 }
 
 static inline void _unlock_tx_hashtbl(struct bonding *bond)
 {
-       spin_unlock(&(BOND_ALB_INFO(bond).tx_hashtbl_lock));
+       spin_unlock_bh(&(BOND_ALB_INFO(bond).tx_hashtbl_lock));
 }
 
 /* Caller must hold tx_hashtbl lock */
@@ -300,12 +305,12 @@ static struct slave *tlb_choose_channel(struct bonding *bond, u32 hash_index, u3
 /*********************** rlb specific functions ***************************/
 static inline void _lock_rx_hashtbl(struct bonding *bond)
 {
-       spin_lock(&(BOND_ALB_INFO(bond).rx_hashtbl_lock));
+       spin_lock_bh(&(BOND_ALB_INFO(bond).rx_hashtbl_lock));
 }
 
 static inline void _unlock_rx_hashtbl(struct bonding *bond)
 {
-       spin_unlock(&(BOND_ALB_INFO(bond).rx_hashtbl_lock));
+       spin_unlock_bh(&(BOND_ALB_INFO(bond).rx_hashtbl_lock));
 }
 
 /* when an ARP REPLY is received from a client update its info
@@ -340,6 +345,9 @@ static int rlb_arp_recv(struct sk_buff *skb, struct net_device *bond_dev, struct
        struct arp_pkt *arp = (struct arp_pkt *)skb->data;
        int res = NET_RX_DROP;
 
+       if (bond_dev->nd_net != &init_net)
+               goto out;
+
        if (!(bond_dev->flags & IFF_MASTER))
                goto out;
 
@@ -464,13 +472,13 @@ static void rlb_clear_slave(struct bonding *bond, struct slave *slave)
 
        _unlock_rx_hashtbl(bond);
 
-       write_lock(&bond->curr_slave_lock);
+       write_lock_bh(&bond->curr_slave_lock);
 
        if (slave != bond->curr_active_slave) {
                rlb_teach_disabled_mac_on_primary(bond, slave->dev->dev_addr);
        }
 
-       write_unlock(&bond->curr_slave_lock);
+       write_unlock_bh(&bond->curr_slave_lock);
 }
 
 static void rlb_update_client(struct rlb_client_info *client_info)
@@ -574,7 +582,7 @@ static void rlb_req_update_slave_clients(struct bonding *bond, struct slave *sla
 }
 
 /* mark all clients using src_ip to be updated */
-static void rlb_req_update_subnet_clients(struct bonding *bond, u32 src_ip)
+static void rlb_req_update_subnet_clients(struct bonding *bond, __be32 src_ip)
 {
        struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
        struct rlb_client_info *client_info;
@@ -613,7 +621,7 @@ static void rlb_req_update_subnet_clients(struct bonding *bond, u32 src_ip)
 static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bond)
 {
        struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
-       struct arp_pkt *arp = (struct arp_pkt *)skb->nh.raw;
+       struct arp_pkt *arp = arp_pkt(skb);
        struct slave *assigned_slave;
        struct rlb_client_info *client_info;
        u32 hash_index = 0;
@@ -701,7 +709,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon
  */
 static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond)
 {
-       struct arp_pkt *arp = (struct arp_pkt *)skb->nh.raw;
+       struct arp_pkt *arp = arp_pkt(skb);
        struct slave *tx_slave = NULL;
 
        if (arp->op_code == __constant_htons(ARPOP_REPLY)) {
@@ -890,8 +898,8 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[])
                data = skb_put(skb, size);
                memcpy(data, &pkt, size);
 
-               skb->mac.raw = data;
-               skb->nh.raw = data + ETH_HLEN;
+               skb_reset_mac_header(skb);
+               skb->network_header = skb->mac_header + ETH_HLEN;
                skb->protocol = pkt.type;
                skb->priority = TC_PRIO_CONTROL;
                skb->dev = slave->dev;
@@ -951,19 +959,34 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[], int hw)
        return 0;
 }
 
-/* Caller must hold bond lock for write or curr_slave_lock for write*/
+/*
+ * Swap MAC addresses between two slaves.
+ *
+ * Called with RTNL held, and no other locks.
+ *
+ */
+
 static void alb_swap_mac_addr(struct bonding *bond, struct slave *slave1, struct slave *slave2)
 {
-       struct slave *disabled_slave = NULL;
        u8 tmp_mac_addr[ETH_ALEN];
-       int slaves_state_differ;
-
-       slaves_state_differ = (SLAVE_IS_OK(slave1) != SLAVE_IS_OK(slave2));
 
        memcpy(tmp_mac_addr, slave1->dev->dev_addr, ETH_ALEN);
        alb_set_slave_mac_addr(slave1, slave2->dev->dev_addr, bond->alb_info.rlb_enabled);
        alb_set_slave_mac_addr(slave2, tmp_mac_addr, bond->alb_info.rlb_enabled);
 
+}
+
+/*
+ * Send learning packets after MAC address swap.
+ *
+ * Called with RTNL and bond->lock held for read.
+ */
+static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1,
+                               struct slave *slave2)
+{
+       int slaves_state_differ = (SLAVE_IS_OK(slave1) != SLAVE_IS_OK(slave2));
+       struct slave *disabled_slave = NULL;
+
        /* fasten the change in the switch */
        if (SLAVE_IS_OK(slave1)) {
                alb_send_learning_packets(slave1, slave1->dev->dev_addr);
@@ -1036,7 +1059,9 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla
                }
 
                if (found) {
+                       /* locking: needs RTNL and nothing else */
                        alb_swap_mac_addr(bond, slave, tmp_slave);
+                       alb_fasten_mac_swap(bond, slave, tmp_slave);
                }
        }
 }
@@ -1259,14 +1284,14 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
        struct ethhdr *eth_data;
        struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
        struct slave *tx_slave = NULL;
-       static const u32 ip_bcast = 0xffffffff;
+       static const __be32 ip_bcast = htonl(0xffffffff);
        int hash_size = 0;
        int do_tx_balance = 1;
        u32 hash_index = 0;
-       u8 *hash_start = NULL;
+       const u8 *hash_start = NULL;
        int res = 1;
 
-       skb->mac.raw = (unsigned char *)skb->data;
+       skb_reset_mac_header(skb);
        eth_data = eth_hdr(skb);
 
        /* make sure that the curr_active_slave and the slaves list do
@@ -1280,15 +1305,18 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
        }
 
        switch (ntohs(skb->protocol)) {
-       case ETH_P_IP:
+       case ETH_P_IP: {
+               const struct iphdr *iph = ip_hdr(skb);
+
                if ((memcmp(eth_data->h_dest, mac_bcast, ETH_ALEN) == 0) ||
-                   (skb->nh.iph->daddr == ip_bcast) ||
-                   (skb->nh.iph->protocol == IPPROTO_IGMP)) {
+                   (iph->daddr == ip_bcast) ||
+                   (iph->protocol == IPPROTO_IGMP)) {
                        do_tx_balance = 0;
                        break;
                }
-               hash_start = (char*)&(skb->nh.iph->daddr);
-               hash_size = sizeof(skb->nh.iph->daddr);
+               hash_start = (char *)&(iph->daddr);
+               hash_size = sizeof(iph->daddr);
+       }
                break;
        case ETH_P_IPV6:
                if (memcmp(eth_data->h_dest, mac_bcast, ETH_ALEN) == 0) {
@@ -1296,12 +1324,11 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                        break;
                }
 
-               hash_start = (char*)&(skb->nh.ipv6h->daddr);
-               hash_size = sizeof(skb->nh.ipv6h->daddr);
+               hash_start = (char *)&(ipv6_hdr(skb)->daddr);
+               hash_size = sizeof(ipv6_hdr(skb)->daddr);
                break;
        case ETH_P_IPX:
-               if (ipx_hdr(skb)->ipx_checksum !=
-                   __constant_htons(IPX_NO_CHECKSUM)) {
+               if (ipx_hdr(skb)->ipx_checksum != IPX_NO_CHECKSUM) {
                        /* something is wrong with this packet */
                        do_tx_balance = 0;
                        break;
@@ -1365,8 +1392,10 @@ out:
        return 0;
 }
 
-void bond_alb_monitor(struct bonding *bond)
+void bond_alb_monitor(struct work_struct *work)
 {
+       struct bonding *bond = container_of(work, struct bonding,
+                                           alb_work.work);
        struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
        struct slave *slave;
        int i;
@@ -1469,7 +1498,7 @@ void bond_alb_monitor(struct bonding *bond)
        }
 
 re_arm:
-       mod_timer(&(bond_info->alb_timer), jiffies + alb_delta_in_ticks);
+       queue_delayed_work(bond->wq, &bond->alb_work, alb_delta_in_ticks);
 out:
        read_unlock(&bond->lock);
 }
@@ -1490,11 +1519,11 @@ int bond_alb_init_slave(struct bonding *bond, struct slave *slave)
        /* caller must hold the bond lock for write since the mac addresses
         * are compared and may be swapped.
         */
-       write_lock_bh(&bond->lock);
+       read_lock(&bond->lock);
 
        res = alb_handle_addr_collision_on_attach(bond, slave);
 
-       write_unlock_bh(&bond->lock);
+       read_unlock(&bond->lock);
 
        if (res) {
                return res;
@@ -1559,13 +1588,21 @@ void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char
  * Set the bond->curr_active_slave to @new_slave and handle
  * mac address swapping and promiscuity changes as needed.
  *
- * Caller must hold bond curr_slave_lock for write (or bond lock for write)
+ * 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.
  */
 void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave)
 {
        struct slave *swap_slave;
        int i;
 
+       if (new_slave)
+               ASSERT_RTNL();
+
        if (bond->curr_active_slave == new_slave) {
                return;
        }
@@ -1598,6 +1635,19 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
                }
        }
 
+       /*
+        * Arrange for swap_slave and new_slave to temporarily be
+        * ignored so we can mess with their MAC addresses without
+        * fear of interference from transmit activity.
+        */
+       if (swap_slave) {
+               tlb_clear_slave(bond, swap_slave, 1);
+       }
+       tlb_clear_slave(bond, new_slave, 1);
+
+       write_unlock_bh(&bond->curr_slave_lock);
+       read_unlock(&bond->lock);
+
        /* curr_active_slave must be set before calling alb_swap_mac_addr */
        if (swap_slave) {
                /* swap mac address */
@@ -1606,11 +1656,23 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
                /* set the new_slave to the bond mac address */
                alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr,
                                       bond->alb_info.rlb_enabled);
+       }
+
+       read_lock(&bond->lock);
+
+       if (swap_slave) {
+               alb_fasten_mac_swap(bond, swap_slave, new_slave);
+       } else {
                /* fasten bond mac on new current slave */
                alb_send_learning_packets(new_slave, bond->dev->dev_addr);
        }
+
+       write_lock_bh(&bond->curr_slave_lock);
 }
 
+/*
+ * Called with RTNL
+ */
 int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
 {
        struct bonding *bond = bond_dev->priv;
@@ -1647,8 +1709,12 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
                }
        }
 
+       write_unlock_bh(&bond->curr_slave_lock);
+       read_unlock(&bond->lock);
+
        if (swap_slave) {
                alb_swap_mac_addr(bond, swap_slave, bond->curr_active_slave);
+               alb_fasten_mac_swap(bond, swap_slave, bond->curr_active_slave);
        } else {
                alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr,
                                       bond->alb_info.rlb_enabled);
@@ -1660,6 +1726,9 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
                }
        }
 
+       read_lock(&bond->lock);
+       write_lock_bh(&bond->curr_slave_lock);
+
        return 0;
 }