tl;dr: Zero-length packets from sis(4) on net4801 result in negative
length mbufs causing uvm faults.

I have observed uvm faults shortly after bringing up a sis(4) interface on
a Soekris net4801:

uvm_fault(0xd3adbbf0, 0xd3ee5000, 0, 1) -> e
kernel: page fault trap, code=0
Stopped at      memcpy+0x13:    repe movsl      (%esi),%es:(%edi)

ddb> trace
memcpy(d6025e00,1a280e,3b9aca00,2,1) at memcpy+0x13
m_copym2(d6025e00,e,3b9aca00,2) at m_copym2+0x19
bridge_m_dup(d6025e00,d3eed000,f13c1d78,d02a0d4b) at bridge_m_dup+0x2f
bridge_localbroadcast(d3eed000,d3ee0c00,f13c1dda,d6025e00) at bridge_localbroad
cast+0x47
bridge_broadcast(d3eed000,d3d3d834,f13c1dda,d6025e00,d3d3d834) at bridge_broadc
ast+0xaf
bridgeintr_frame(d3eed000,d3d3d834,d6025e00,d3ed9700) at bridgeintr_frame+0x1ed

bridge_process(d3d3d834,d6025e00,f13c1e6c,f13c1e6c,d6025c00) at bridge_process+
0x257
bridgeintr(d029312e,d3b22b20,d6025c00,f13c1ea8,d3d5c800) at bridgeintr+0x54
netintr(0,d020224c,0,f13c1efc) at netintr+0x47
softintr_dispatch(1) at softintr_dispatch+0x6c
Xsoftnet() at Xsoftnet+0x12
--- interrupt ---
0:

ddb> show mbuf 0xd6025e00
mbuf 0xd6025e00
m_type: 1       m_flags: b<M_EXT,M_PKTHDR,M_EXTWR>
m_next: 0x0     m_nextpkt: 0x0
m_data: 0xd3d42000      m_len: 4294967292
m_dat: 0xd6025e14       m_pktdat: 0xd6025e44
m_ptkhdr.ph_ifidx: 1    m_pkthdr.len: -4
m_ptkhdr.ph_tags: 0x0   m_pkthdr.ph_tagsset: 0
m_pkthdr.ph_flowid: 0
m_pkthdr.csum_flags: 0
m_pkthdr.ether_vtag: 0  m_ptkhdr.ph_rtableid: 0
m_pkthdr.pf.statekey: 0x0       m_pkthdr.pf.inp 0x0
m_pkthdr.pf.qid: 0      m_pkthdr.pf.tag: 0
m_pkthdr.pf.flags: 0
m_pkthdr.pf.routed: 0   m_pkthdr.pf.prio: 3
m_ext.ext_buf: 0xd3d42000       m_ext.ext_size: 2048
m_ext.ext_free: 0xd027a717      m_ext.ext_arg: 0xd3b0050c
m_ext.ext_nextref: 0xd6025e00   m_ext.ext_prevref: 0xd6025e00

# dmesg | grep sis0
sis0 at pci0 dev 6 function 0 vendor 0x100b product 0x0020 rev 0x00, DP83816A: 
irq 10, address 00:00:24:c4:fe:3c
nsphyter0 at sis0 phy 0: DP83815 10/100 PHY, rev. 1

I traced this to the DP83816A sometimes producing a small number of zero-
length packets soon after initialisation. The sis(4) driver unconditionally
subtracts ETHER_CRC_LEN from the packet length. When the packet length is
zero this results in a negative length mbuf.

#define SIS_RXBYTES(x)  \
        ((letoh32((x)->sis_ctl) & SIS_CMDSTS_BUFLEN) - ETHER_CRC_LEN)

total_len = SIS_RXBYTES(cur_rx);

m->m_pkthdr.len = m->m_len = total_len;

I haven't been able to nail down the root cause of this behaviour. It is
not a recently introduced issue. For me this happens commonly enough that
it is readily reproduced, but the zero-length packets do not occur on
every boot and not every zero-length packet results in a uvm fault.

A web search didn't turn up any similar reports so perhaps there is
something unusual about my situation. Maybe having sis0 on a bridge
introduces a code path that increases the likelihood of a uvm_fault in
response to a negative length mbuf.

The descriptor cmdsts for these zero-length packets is consistently:

    0xd0000000 = SIS_CMDSTS_OWN | SIS_CMDSTS_MORE | SIS_CMDSTS_CRC

I found the following comment in the DP83816A datasheet:

    Care must be taken when setting DRTH to a value lower than the number
    of bytes needed to determine if packet should be accepted or rejected.
    In this case, the packet might be rejected after the bus master
    operation to begin transferring the packet into memory has begun. When
    this occurs, neither the OK bit or any error status bit in the
    descriptor's cmdsts will be set.

sis(4) sets DRTH to 64 bytes, which is sufficient for packet identification.
In the end, maybe this is just buggy DP83816A behaviour.

In any case the following diff works around the problem for me.

Nathanael


Index: if_sisreg.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_sisreg.h,v
retrieving revision 1.34
diff -u -p -r1.34 if_sisreg.h
--- if_sisreg.h 11 Feb 2015 21:36:02 -0000      1.34
+++ if_sisreg.h 6 Jan 2016 14:33:11 -0000
@@ -343,8 +343,7 @@ struct sis_desc {
 #define SIS_LASTDESC(x)                (!(letoh32((x)->sis_ctl) & 
SIS_CMDSTS_MORE)))
 #define SIS_OWNDESC(x)         (letoh32((x)->sis_ctl) & SIS_CMDSTS_OWN)
 #define SIS_INC(x, y)          (x) = ((x) == ((y)-1)) ? 0 : (x)+1
-#define SIS_RXBYTES(x) \
-       ((letoh32((x)->sis_ctl) & SIS_CMDSTS_BUFLEN) - ETHER_CRC_LEN)
+#define SIS_RXBYTES(x)         (letoh32((x)->sis_ctl) & SIS_CMDSTS_BUFLEN)
 
 #define SIS_RXSTAT_COLL                0x00010000
 #define SIS_RXSTAT_LOOPBK      0x00020000
Index: if_sis.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_sis.c,v
retrieving revision 1.132
diff -u -p -r1.132 if_sis.c
--- if_sis.c    25 Nov 2015 03:09:59 -0000      1.132
+++ if_sis.c    6 Jan 2016 14:33:11 -0000
@@ -1389,6 +1389,18 @@ sis_rxeof(struct sis_softc *sc)
                if_rxr_put(&sc->sis_cdata.sis_rx_ring, 1);
 
                /*
+                * DP83816A sometimes produces zero-length packets
+                * shortly after initialisation.
+                */
+               if (total_len == 0) {
+                       m_freem(m);
+                       continue;
+               }
+
+               /* The ethernet CRC is always included */
+               total_len -= ETHER_CRC_LEN;
+
+               /*
                 * If an error occurs, update stats, clear the
                 * status word and leave the mbuf cluster in place:
                 * it should simply get re-used next time this descriptor

Reply via email to