aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Brandeburg <jesse.brandeburg@intel.com>2009-04-04 19:36:53 -0400
committerDavid S. Miller <davem@davemloft.net>2009-04-04 19:36:53 -0400
commit81c522851436dbc058c9c0c11b32e60d76b180ce (patch)
treed665ad0664f8a362baf496da9d4578dab2d0c397
parent2b05e0021b21e1dc484a1237b7fa674e8a3704bb (diff)
e1000: fix loss of multicast packets
e1000 (and e1000e, igb, ixgbe, ixgb) all do a series of operations each time a multicast address is added. The flow goes something like 1) stack adds one multicast address 2) stack passes whole current list of unicast and multicast addresses to driver 3) driver clears entire list in hardware 4) driver programs each multicast address using iomem in a loop This was causing multicast packets to be lost during the reprogramming process. reference with test program: http://kerneltrap.org/mailarchive/linux-netdev/2009/3/14/5160514/thread Thanks to Dave Boutcher for his report and test program. This driver fix prepares an array all at once in memory and programs it in one shot to the hardware, not requiring an "erase" cycle. Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/e1000/e1000_main.c34
1 files changed, 26 insertions, 8 deletions
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
index 93b861d032b5..a65023d772cb 100644
--- a/drivers/net/e1000/e1000_main.c
+++ b/drivers/net/e1000/e1000_main.c
@@ -2335,6 +2335,12 @@ static void e1000_set_rx_mode(struct net_device *netdev)
2335 int mta_reg_count = (hw->mac_type == e1000_ich8lan) ? 2335 int mta_reg_count = (hw->mac_type == e1000_ich8lan) ?
2336 E1000_NUM_MTA_REGISTERS_ICH8LAN : 2336 E1000_NUM_MTA_REGISTERS_ICH8LAN :
2337 E1000_NUM_MTA_REGISTERS; 2337 E1000_NUM_MTA_REGISTERS;
2338 u32 *mcarray = kcalloc(mta_reg_count, sizeof(u32), GFP_ATOMIC);
2339
2340 if (!mcarray) {
2341 DPRINTK(PROBE, ERR, "memory allocation failed\n");
2342 return;
2343 }
2338 2344
2339 if (hw->mac_type == e1000_ich8lan) 2345 if (hw->mac_type == e1000_ich8lan)
2340 rar_entries = E1000_RAR_ENTRIES_ICH8LAN; 2346 rar_entries = E1000_RAR_ENTRIES_ICH8LAN;
@@ -2401,22 +2407,34 @@ static void e1000_set_rx_mode(struct net_device *netdev)
2401 } 2407 }
2402 WARN_ON(uc_ptr != NULL); 2408 WARN_ON(uc_ptr != NULL);
2403 2409
2404 /* clear the old settings from the multicast hash table */
2405
2406 for (i = 0; i < mta_reg_count; i++) {
2407 E1000_WRITE_REG_ARRAY(hw, MTA, i, 0);
2408 E1000_WRITE_FLUSH();
2409 }
2410
2411 /* load any remaining addresses into the hash table */ 2410 /* load any remaining addresses into the hash table */
2412 2411
2413 for (; mc_ptr; mc_ptr = mc_ptr->next) { 2412 for (; mc_ptr; mc_ptr = mc_ptr->next) {
2413 u32 hash_reg, hash_bit, mta;
2414 hash_value = e1000_hash_mc_addr(hw, mc_ptr->da_addr); 2414 hash_value = e1000_hash_mc_addr(hw, mc_ptr->da_addr);
2415 e1000_mta_set(hw, hash_value); 2415 hash_reg = (hash_value >> 5) & 0x7F;
2416 hash_bit = hash_value & 0x1F;
2417 mta = (1 << hash_bit);
2418 mcarray[hash_reg] |= mta;
2416 } 2419 }
2417 2420
2421 /* write the hash table completely, write from bottom to avoid
2422 * both stupid write combining chipsets, and flushing each write */
2423 for (i = mta_reg_count - 1; i >= 0 ; i--) {
2424 /*
2425 * If we are on an 82544 has an errata where writing odd
2426 * offsets overwrites the previous even offset, but writing
2427 * backwards over the range solves the issue by always
2428 * writing the odd offset first
2429 */
2430 E1000_WRITE_REG_ARRAY(hw, MTA, i, mcarray[i]);
2431 }
2432 E1000_WRITE_FLUSH();
2433
2418 if (hw->mac_type == e1000_82542_rev2_0) 2434 if (hw->mac_type == e1000_82542_rev2_0)
2419 e1000_leave_82542_rst(adapter); 2435 e1000_leave_82542_rst(adapter);
2436
2437 kfree(mcarray);
2420} 2438}
2421 2439
2422/* Need to wait a few seconds after link up to get diagnostic information from 2440/* Need to wait a few seconds after link up to get diagnostic information from