Bug 236119 - Intel Ethernet 82547 device cannot transfer data
Summary: Intel Ethernet 82547 device cannot transfer data
Status: New
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: 12.0-STABLE
Hardware: Any Any
: --- Affects Some People
Assignee: freebsd-net mailing list
URL:
Keywords: IntelNetworking, patch, regression
Depends on:
Blocks:
 
Reported: 2019-03-01 02:59 UTC by Jeff Gibbons
Modified: 2019-03-07 16:07 UTC (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jeff Gibbons 2019-03-01 02:59:30 UTC
When upgrading some of my machines from FreeBSD 11 to FreeBSD 12, I found that some of them could not transfer data via Ethernet after the upgrade.  ifconfig seemed to work normally, including showing "status: active", but that interface could neither send nor receive data packets, no matter what I tried.  And a few minutes after booting my systems, they would silently hang, with no response whatsoever, even on a serial console.

I eventually narrowed down the problem to my machines with Intel 82547GI chips, which look like this in "pciconf -lv":

em0@pci0:1:1:0: class=0x020000 card=0x10758086 chip=0x10758086 rev=0x00 hdr=0x00
    vendor     = 'Intel Corporation'
    device     = '82547GI Gigabit Ethernet Controller'
    class      = network
    subclass   = ethernet

By comparing the FreeBSD 11 code (which I knew worked) with the FreeBSD 12 code which had the problem, I found the solution: the 82547 is an "edge" case which slipped through the cracks when the Intel em driver was modified for FreeBSD 12.  Here is what I had to change in if_em.c to make the Intel 82547 Ethernet chips work:

root@prod:~ # svn diff /usr/src/sys/dev/e1000/if_em.c
Index: /usr/src/sys/dev/e1000/if_em.c
===================================================================
--- /usr/src/sys/dev/e1000/if_em.c      (revision 344229)
+++ /usr/src/sys/dev/e1000/if_em.c      (working copy)
@@ -31,13 +31,16 @@
 #include <sys/sbuf.h>
 #include <machine/_inttypes.h>
 
-#define em_mac_min e1000_82547
+// jagwas: #define em_mac_min e1000_82547
+#define em_mac_min e1000_82571   // jag; 25feb2019;
+                                 //   so (adapter->hw.mac.type < em_mac_min)
+                                 //   is true for 82547GI and below
 #define igb_mac_min e1000_82575
 
 /*********************************************************************
  *  Driver version:
  *********************************************************************/
-char em_driver_version[] = "7.6.1-k";
+char em_driver_version[] = "7.6.1-k+jag3";
 
 /*********************************************************************
  *  PCI Device ID Table
@@ -2476,6 +2479,24 @@
        case e1000_i211:
                pba = E1000_PBA_34K;
                break;
+                        // jag; 26feb2019 added this case section; adapted from
+                        //      FreeBSD 11 /usr/src/sys/dev/e1000/if_lem.c
+       case e1000_82547:
+       case e1000_82547_rev_2: /* 82547: Total Packet Buffer is 40K */
+               if (adapter->hw.mac.max_frame_size > 8192)
+                       pba = E1000_PBA_22K; /* 22K for Rx, 18K for Tx */
+               else
+                       pba = E1000_PBA_30K; /* 30K for Rx, 10K for Tx */
+
+               // jag; the following would be needed for plain 82547 (before GI)
+               // and would also require adding the elements set here to
+               // struct adapter in if_em.h:
+               // adapter->tx_fifo_head = 0;
+               // adapter->tx_head_addr = pba << EM_TX_HEAD_ADDR_SHIFT;
+               // adapter->tx_fifo_size =
+               //     (E1000_PBA_40K - pba) << EM_PBA_BYTES_SHIFT;
+
+               break;
        default:
                if (adapter->hw.mac.max_frame_size > 8192)
                        pba = E1000_PBA_40K; /* 40K for Rx, 24K for Tx */
root@prod:~ #

With that change, my interfaces all now work fine, and my systems don't hang (I think the hanging problem was because the pre-change if_em.c was assigning more packet buffer space than the 82547GI actually has).

I believe this change will also help some of the other 82547 models to work, but I don't have any of those chips and so cannot test them.  Those chips also seem to need some workarounds for special cases (like jumbo frames which cross buffer boundaries); those workarounds are implemented in FreeBSD 11 but not in FreeBSD 12, and I didn't implement them as part of this change because my 82547GI chip doesn't need them.  (For example, see the lem_82547_fifo_workaround() function; search for "82547" in the /usr/src/sys/dev/e1000/if_lem.c file in FreeBSD 11 to see other workarounds).

This problem seems to exist on CURRENT, too (from looking at the code; I have not tested that).