Merge tag 'net-6.1-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
[sfrench/cifs-2.6.git] / drivers / net / ethernet / engleder / tsnep_main.c
index 48fb391951ddebae381d7efc7b6db9b35eb7449b..13d5ff4e0e0200dba853182bd8dd89be30efcda2 100644 (file)
@@ -542,6 +542,27 @@ static bool tsnep_tx_poll(struct tsnep_tx *tx, int napi_budget)
        return (budget != 0);
 }
 
        return (budget != 0);
 }
 
+static bool tsnep_tx_pending(struct tsnep_tx *tx)
+{
+       unsigned long flags;
+       struct tsnep_tx_entry *entry;
+       bool pending = false;
+
+       spin_lock_irqsave(&tx->lock, flags);
+
+       if (tx->read != tx->write) {
+               entry = &tx->entry[tx->read];
+               if ((__le32_to_cpu(entry->desc_wb->properties) &
+                    TSNEP_TX_DESC_OWNER_MASK) ==
+                   (entry->properties & TSNEP_TX_DESC_OWNER_MASK))
+                       pending = true;
+       }
+
+       spin_unlock_irqrestore(&tx->lock, flags);
+
+       return pending;
+}
+
 static int tsnep_tx_open(struct tsnep_adapter *adapter, void __iomem *addr,
                         int queue_index, struct tsnep_tx *tx)
 {
 static int tsnep_tx_open(struct tsnep_adapter *adapter, void __iomem *addr,
                         int queue_index, struct tsnep_tx *tx)
 {
@@ -821,6 +842,19 @@ static int tsnep_rx_poll(struct tsnep_rx *rx, struct napi_struct *napi,
        return done;
 }
 
        return done;
 }
 
+static bool tsnep_rx_pending(struct tsnep_rx *rx)
+{
+       struct tsnep_rx_entry *entry;
+
+       entry = &rx->entry[rx->read];
+       if ((__le32_to_cpu(entry->desc_wb->properties) &
+            TSNEP_DESC_OWNER_COUNTER_MASK) ==
+           (entry->properties & TSNEP_DESC_OWNER_COUNTER_MASK))
+               return true;
+
+       return false;
+}
+
 static int tsnep_rx_open(struct tsnep_adapter *adapter, void __iomem *addr,
                         int queue_index, struct tsnep_rx *rx)
 {
 static int tsnep_rx_open(struct tsnep_adapter *adapter, void __iomem *addr,
                         int queue_index, struct tsnep_rx *rx)
 {
@@ -866,6 +900,17 @@ static void tsnep_rx_close(struct tsnep_rx *rx)
        tsnep_rx_ring_cleanup(rx);
 }
 
        tsnep_rx_ring_cleanup(rx);
 }
 
+static bool tsnep_pending(struct tsnep_queue *queue)
+{
+       if (queue->tx && tsnep_tx_pending(queue->tx))
+               return true;
+
+       if (queue->rx && tsnep_rx_pending(queue->rx))
+               return true;
+
+       return false;
+}
+
 static int tsnep_poll(struct napi_struct *napi, int budget)
 {
        struct tsnep_queue *queue = container_of(napi, struct tsnep_queue,
 static int tsnep_poll(struct napi_struct *napi, int budget)
 {
        struct tsnep_queue *queue = container_of(napi, struct tsnep_queue,
@@ -886,9 +931,19 @@ static int tsnep_poll(struct napi_struct *napi, int budget)
        if (!complete)
                return budget;
 
        if (!complete)
                return budget;
 
-       if (likely(napi_complete_done(napi, done)))
+       if (likely(napi_complete_done(napi, done))) {
                tsnep_enable_irq(queue->adapter, queue->irq_mask);
 
                tsnep_enable_irq(queue->adapter, queue->irq_mask);
 
+               /* reschedule if work is already pending, prevent rotten packets
+                * which are transmitted or received after polling but before
+                * interrupt enable
+                */
+               if (tsnep_pending(queue)) {
+                       tsnep_disable_irq(queue->adapter, queue->irq_mask);
+                       napi_schedule(napi);
+               }
+       }
+
        return min(done, budget - 1);
 }
 
        return min(done, budget - 1);
 }