Author: sbruno
Date: Tue Jun 16 12:10:55 2015
New Revision: 284444
URL: https://svnweb.freebsd.org/changeset/base/284444

Log:
  MFC r283923
  
  Simplify hang detection by stealing the techniques used in ixl(4) and
  applying them to em(4).
  
  Rely on iterations through the local timer, and the tx queue state to
  determine if an actual hang has occurred. Any time a descriptor is used
  (packet sent), the tx queue is flagged as busy. Then when txeof runs, it
  either clears the flag when all is clean, or resets it to 1 if ANY are
  cleaned, if nothing is cleaned it increments the flag.
  
  Local timer simply checks to see if busy ever reaches MAX (10, which
  is compile time configurable), and then sets it as HUNG, at that point
  there is one more timer cycle in which to have any cleans, if not a
  watchdog reset will occur.

Modified:
  stable/10/sys/dev/e1000/if_em.c
  stable/10/sys/dev/e1000/if_em.h
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/dev/e1000/if_em.c
==============================================================================
--- stable/10/sys/dev/e1000/if_em.c     Tue Jun 16 09:52:36 2015        
(r284443)
+++ stable/10/sys/dev/e1000/if_em.c     Tue Jun 16 12:10:55 2015        
(r284444)
@@ -1,6 +1,6 @@
 /******************************************************************************
 
-  Copyright (c) 2001-2014, Intel Corporation 
+  Copyright (c) 2001-2015, Intel Corporation 
   All rights reserved.
   
   Redistribution and use in source and binary forms, with or without 
@@ -953,11 +953,9 @@ em_mq_start_locked(struct ifnet *ifp, st
                         break;
        }
 
-       if (enq > 0) {
-                /* Set the watchdog */
-                txr->queue_status = EM_QUEUE_WORKING;
-               txr->watchdog_time = ticks;
-       }
+       /* Mark the queue as having work */
+       if ((enq > 0) && (txr->busy == EM_TX_IDLE))
+               txr->busy = EM_TX_BUSY;
 
        if (txr->tx_avail < EM_MAX_SCATTER)
                em_txeof(txr);
@@ -1042,12 +1040,12 @@ em_start_locked(struct ifnet *ifp, struc
                        break;
                }
 
+               /* Mark the queue as having work */
+               if (txr->busy == EM_TX_IDLE)
+                       txr->busy = EM_TX_BUSY;
+
                /* Send a copy of the frame to the BPF listener */
                ETHER_BPF_MTAP(ifp, m_head);
-
-               /* Set timeout in case hardware has problems transmitting. */
-               txr->watchdog_time = ticks;
-                txr->queue_status = EM_QUEUE_WORKING;
        }
 
        return;
@@ -2114,8 +2112,6 @@ retry:
         */
        tx_buffer = &txr->tx_buffers[first];
        tx_buffer->next_eop = last;
-       /* Update the watchdog time early and often */
-       txr->watchdog_time = ticks;
 
        /*
         * Advance the Transmit Descriptor Tail (TDT), this tells the E1000
@@ -2297,15 +2293,16 @@ em_local_timer(void *arg)
        ** and the HUNG state will be static if set.
        */
        for (int i = 0; i < adapter->num_queues; i++, txr++) {
-               if ((txr->queue_status == EM_QUEUE_HUNG) &&
-                   (adapter->pause_frames == 0))
+               /* Last cycle a queue was declared hung */
+               if (txr->busy == EM_TX_HUNG)
                        goto hung;
+               if (txr->busy >= EM_TX_MAXTRIES)
+                       txr->busy = EM_TX_HUNG;
                /* Schedule a TX tasklet if needed */
                if (txr->tx_avail <= EM_MAX_SCATTER)
                        taskqueue_enqueue(txr->tq, &txr->tx_task);
        }
        
-       adapter->pause_frames = 0;
        callout_reset(&adapter->timer, hz, em_local_timer, adapter);
 #ifndef DEVICE_POLLING
        /* Trigger an RX interrupt to guarantee mbuf refresh */
@@ -2324,7 +2321,6 @@ hung:
            txr->me, txr->tx_avail, txr->next_to_clean);
        ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
        adapter->watchdog_events++;
-       adapter->pause_frames = 0;
        em_init_locked(adapter);
 }
 
@@ -2392,9 +2388,9 @@ em_update_link_status(struct adapter *ad
                if (bootverbose)
                        device_printf(dev, "Link is Down\n");
                adapter->link_active = 0;
-               /* Link down, disable watchdog */
+               /* Link down, disable hang detection */
                for (int i = 0; i < adapter->num_queues; i++, txr++)
-                       txr->queue_status = EM_QUEUE_IDLE;
+                       txr->busy = EM_TX_IDLE;
                if_link_state_change(ifp, LINK_STATE_DOWN);
        }
 }
@@ -2426,10 +2422,10 @@ em_stop(void *arg)
        ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
        ifp->if_drv_flags |= IFF_DRV_OACTIVE;
 
-        /* Unarm watchdog timer. */
+        /* Disarm Hang Detection. */
        for (int i = 0; i < adapter->num_queues; i++, txr++) {
                EM_TX_LOCK(txr);
-               txr->queue_status = EM_QUEUE_IDLE;
+               txr->busy = EM_TX_IDLE;
                EM_TX_UNLOCK(txr);
        }
 
@@ -3405,7 +3401,7 @@ em_setup_transmit_ring(struct tx_ring *t
 
        /* Set number of descriptors available */
        txr->tx_avail = adapter->num_tx_desc;
-       txr->queue_status = EM_QUEUE_IDLE;
+       txr->busy = EM_TX_IDLE;
 
        /* Clear checksum offload context. */
        txr->last_hw_offload = 0;
@@ -3466,7 +3462,7 @@ em_initialize_transmit_unit(struct adapt
                    E1000_READ_REG(&adapter->hw, E1000_TDBAL(i)),
                    E1000_READ_REG(&adapter->hw, E1000_TDLEN(i)));
 
-               txr->queue_status = EM_QUEUE_IDLE;
+               txr->busy = EM_TX_IDLE;
        }
 
        /* Set the default values for the Tx Inter Packet Gap timer */
@@ -3849,9 +3845,9 @@ em_txeof(struct tx_ring *txr)
                return;
 #endif /* DEV_NETMAP */
 
-       /* No work, make sure watchdog is off */
+       /* No work, make sure hang detection is disabled */
         if (txr->tx_avail == adapter->num_tx_desc) {
-               txr->queue_status = EM_QUEUE_IDLE;
+               txr->busy = EM_TX_IDLE;
                 return;
        }
 
@@ -3894,7 +3890,6 @@ em_txeof(struct tx_ring *txr)
                                tx_buffer->m_head = NULL;
                        }
                        tx_buffer->next_eop = -1;
-                       txr->watchdog_time = ticks;
 
                        if (++first == adapter->num_tx_desc)
                                first = 0;
@@ -3919,14 +3914,16 @@ em_txeof(struct tx_ring *txr)
         txr->next_to_clean = first;
 
        /*
-       ** Watchdog calculation, we know there's
-       ** work outstanding or the first return
-       ** would have been taken, so none processed
-       ** for too long indicates a hang. local timer
-       ** will examine this and do a reset if needed.
+       ** Hang detection: we know there's work outstanding
+       ** or the entry return would have been taken, so no
+       ** descriptor processed here indicates a potential hang.
+       ** The local timer will examine this and do a reset if needed.
        */
-       if ((!processed) && ((ticks - txr->watchdog_time) > EM_WATCHDOG))
-               txr->queue_status = EM_QUEUE_HUNG;
+       if (processed == 0) {
+               if (txr->busy != EM_TX_HUNG)
+                       ++txr->busy;
+       } else /* At least one descriptor was cleaned */
+               txr->busy = EM_TX_BUSY; /* note this clears HUNG */
 
         /*
          * If we have a minimum free, clear IFF_DRV_OACTIVE
@@ -3938,10 +3935,9 @@ em_txeof(struct tx_ring *txr)
         if (txr->tx_avail >= EM_MAX_SCATTER)
                ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
 
-       /* Disable watchdog if all clean */
-       if (txr->tx_avail == adapter->num_tx_desc) {
-               txr->queue_status = EM_QUEUE_IDLE;
-       } 
+       /* Disable hang detection if all clean */
+       if (txr->tx_avail == adapter->num_tx_desc)
+               txr->busy = EM_TX_IDLE;
 }
 
 
@@ -5176,12 +5172,7 @@ em_update_stats_counters(struct adapter 
        adapter->stats.rlec += E1000_READ_REG(&adapter->hw, E1000_RLEC);
        adapter->stats.xonrxc += E1000_READ_REG(&adapter->hw, E1000_XONRXC);
        adapter->stats.xontxc += E1000_READ_REG(&adapter->hw, E1000_XONTXC);
-       /*
-       ** For watchdog management we need to know if we have been
-       ** paused during the last interval, so capture that here.
-       */
-       adapter->pause_frames = E1000_READ_REG(&adapter->hw, E1000_XOFFRXC);
-       adapter->stats.xoffrxc += adapter->pause_frames;
+       adapter->stats.xoffrxc += E1000_READ_REG(&adapter->hw, E1000_XOFFRXC);
        adapter->stats.xofftxc += E1000_READ_REG(&adapter->hw, E1000_XOFFTXC);
        adapter->stats.fcruc += E1000_READ_REG(&adapter->hw, E1000_FCRUC);
        adapter->stats.prc64 += E1000_READ_REG(&adapter->hw, E1000_PRC64);
@@ -5798,7 +5789,7 @@ em_print_debug_info(struct adapter *adap
        device_printf(dev, "hw rdh = %d, hw rdt = %d\n",
            E1000_READ_REG(&adapter->hw, E1000_RDH(0)),
            E1000_READ_REG(&adapter->hw, E1000_RDT(0)));
-       device_printf(dev, "Tx Queue Status = %d\n", txr->queue_status);
+       device_printf(dev, "Tx Queue Status = %d\n", txr->busy);
        device_printf(dev, "TX descriptors avail = %d\n",
            txr->tx_avail);
        device_printf(dev, "Tx Descriptors avail failure = %ld\n",

Modified: stable/10/sys/dev/e1000/if_em.h
==============================================================================
--- stable/10/sys/dev/e1000/if_em.h     Tue Jun 16 09:52:36 2015        
(r284443)
+++ stable/10/sys/dev/e1000/if_em.h     Tue Jun 16 12:10:55 2015        
(r284444)
@@ -1,6 +1,6 @@
 /******************************************************************************
 
-  Copyright (c) 2001-2011, Intel Corporation 
+  Copyright (c) 2001-2015, Intel Corporation 
   All rights reserved.
   
   Redistribution and use in source and binary forms, with or without 
@@ -188,9 +188,19 @@
 #define EM_EEPROM_APME                 0x400;
 #define EM_82544_APME                  0x0004;
 
-#define EM_QUEUE_IDLE                  0
-#define EM_QUEUE_WORKING               1
-#define EM_QUEUE_HUNG                  2
+/*
+ * Driver state logic for the detection of a hung state
+ * in hardware.  Set TX_HUNG whenever a TX packet is used
+ * (data is sent) and clear it when txeof() is invoked if
+ * any descriptors from the ring are cleaned/reclaimed.
+ * Increment internal counter if no descriptors are cleaned
+ * and compare to TX_MAXTRIES.  When counter > TX_MAXTRIES,
+ * reset adapter.
+ */
+#define EM_TX_IDLE                     0x00000000
+#define EM_TX_BUSY                     0x00000001
+#define EM_TX_HUNG                     0x80000000
+#define EM_TX_MAXTRIES                 10
 
 /*
  * TDBA/RDBA should be aligned on 16 byte boundary. But TDLEN/RDLEN should be
@@ -281,8 +291,7 @@ struct tx_ring {
         u32                     me;
         u32                     msix;
        u32                     ims;
-        int                    queue_status;
-        int                     watchdog_time;
+        int                    busy;
        struct em_dma_alloc     txdma;
        struct e1000_tx_desc    *tx_base;
         struct task             tx_task;
@@ -368,7 +377,6 @@ struct adapter {
        int             if_flags;
        int             max_frame_size;
        int             min_frame_size;
-       int             pause_frames;
        struct mtx      core_mtx;
        int             em_insert_vlan_header;
        u32             ims;
_______________________________________________
svn-src-stable-10@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-stable-10
To unsubscribe, send any mail to "svn-src-stable-10-unsubscr...@freebsd.org"

Reply via email to