Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 14 Oct 2005 04:31:35 +0400 (MSD)
From:      Dmitrij Tejblum <tejblum@yandex-team.ru>
To:        FreeBSD-gnats-submit@FreeBSD.org
Subject:   kern/87418: [if_em] [patch] The em driver sometimes stop receive data
Message-ID:  <200510140031.j9E0VZFr092939@walrus18.yandex.ru>
Resent-Message-ID: <200510140040.j9E0eG8m095176@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         87418
>Category:       kern
>Synopsis:       [if_em] [patch] The em driver sometimes stop receive data
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Oct 14 00:40:15 GMT 2005
>Closed-Date:
>Last-Modified:
>Originator:     Dmitrij Tejblum
>Release:        FreeBSD 5.4-STABLE i386
>Organization:
OOO Yandex
>Environment:


>Description:

The receive function em_process_receive_interrupts() unlock the adapter
while ether_input() process the packet, and then lock it back. In the 
meantime, em_init() may be called (either from em_watchdog() from softclock
interrupt or from the ifconfig program). em_init() reset the card, in
particular it set adapter->next_rx_desc_to_check to 0 and reset hardware 
RX Head and Tail descriptor pointers. The loop in
em_process_receive_interrupts() does not expect these things to change, and
a mess may result.

>How-To-Repeat:

>Fix:

The following patch tries to make em_process_receive_interrupts() fully
reenterant (1.73 was incomplete) and fix the described problem.

(I removed a code for FreeBSD 4: I don't want to deal with it and BTW Intel
themselves ships different drivers for RELENG_4, RELENG_5 and RELENG_6.)

Index: dev/em/if_em.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/em/if_em.c,v
retrieving revision 1.75
diff -u -p -r1.75 if_em.c
--- dev/em/if_em.c	5 Oct 2005 10:09:14 -0000	1.75
+++ dev/em/if_em.c	14 Oct 2005 00:27:28 -0000
@@ -2785,7 +2785,7 @@ static void
 em_process_receive_interrupts(struct adapter * adapter, int count)
 {
 	struct ifnet        *ifp;
-	struct mbuf         *mp;
+	struct mbuf         *mp, *m;
 #if __FreeBSD_version < 500000
         struct ether_header *eh;
 #endif
@@ -2811,6 +2811,7 @@ em_process_receive_interrupts(struct ada
 
 	while ((current_desc->status & E1000_RXD_STAT_DD) && (count != 0)) {
 		
+		m = NULL;
 		mp = adapter->rx_buffer_area[i].m_head;
 		bus_dmamap_sync(adapter->rxtag, adapter->rx_buffer_area[i].map,
 				BUS_DMASYNC_POSTREAD);
@@ -2895,19 +2896,6 @@ em_process_receive_interrupts(struct ada
                                 adapter->fmp->m_pkthdr.rcvif = ifp;
                                  ifp->if_ipackets++;
 
-#if __FreeBSD_version < 500000
-                                eh = mtod(adapter->fmp, struct ether_header *);
-                                /* Remove ethernet header from mbuf */
-                                m_adj(adapter->fmp, sizeof(struct ether_header));
-                                em_receive_checksum(adapter, current_desc,
-                                                    adapter->fmp);
-                                if (current_desc->status & E1000_RXD_STAT_VP)
-                                        VLAN_INPUT_TAG(eh, adapter->fmp,
-                                                       (current_desc->special & 
-							E1000_RXD_SPC_VLAN_MASK));
-                                else
-                                        ether_input(ifp, eh, adapter->fmp);
-#else
 
                                 em_receive_checksum(adapter, current_desc,
                                                     adapter->fmp);
@@ -2917,15 +2905,9 @@ em_process_receive_interrupts(struct ada
 							E1000_RXD_SPC_VLAN_MASK),
 						       adapter->fmp = NULL);
  
-                                if (adapter->fmp != NULL) {
-					struct mbuf *m = adapter->fmp;
-
-					adapter->fmp = NULL;
-					EM_UNLOCK(adapter);
-                                        (*ifp->if_input)(ifp, m);
-					EM_LOCK(adapter);
-				}
-#endif
+                                if (adapter->fmp != NULL)
+					m = adapter->fmp;
+                                adapter->fmp = NULL;
                                 adapter->lmp = NULL;
                         }
 		} else {
@@ -2944,11 +2926,16 @@ em_process_receive_interrupts(struct ada
                 E1000_WRITE_REG(&adapter->hw, RDT, i);
 
                 /* Advance our pointers to the next descriptor */
-                if (++i == adapter->num_rx_desc) {
+                if (++i == adapter->num_rx_desc)
                         i = 0;
-                        current_desc = adapter->rx_desc_base;
-                } else
-			current_desc++;
+                if (m != NULL) {
+                        adapter->next_rx_desc_to_check = i;
+                        EM_UNLOCK(adapter);
+                        (*ifp->if_input)(ifp, m);
+                        EM_LOCK(adapter);
+                        i = adapter->next_rx_desc_to_check;
+                }
+                current_desc = &adapter->rx_desc_base[i];
 	}
 	bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map,
 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200510140031.j9E0VZFr092939>