diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c
index 2dd2fa801829..2fe7fd0202d2 100644
--- a/drivers/net/ethernet/cortina/gemini.c
+++ b/drivers/net/ethernet/cortina/gemini.c
@@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
+#include <linux/net.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
@@ -736,7 +737,8 @@ gmac_get_queue_page(struct gemini_ethernet *geth,
mapping = addr & PAGE_MASK;
if (!geth->freeq_pages) {
- dev_err(geth->dev, "try to get page with no page list\n");
+ dev_err_ratelimited(geth->dev,
+ "try to get page with no page list\n");
return NULL;
}
@@ -1441,7 +1443,7 @@ static struct sk_buff *gmac_skb_if_good_frame(struct gemini_ethernet_port *port,
}
static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget,
- unsigned int *freeq_consumed)
+ unsigned int *freeq_consumed, bool *reschedule)
{
struct gemini_ethernet_port *port = netdev_priv(netdev);
unsigned short m = (1 << port->rxq_order) - 1;@@ -1449,6 +1451,8 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget,
void __iomem *ptr_reg = port->rxq_rwptr;
unsigned int frag_nr = port->rx_frag_nr;
struct sk_buff *skb = port->rx_skb;
+ /* Bound malformed chains while allowing maximum fragments per frame. */
+ unsigned int desc_limit = budget * MAX_SKB_FRAGS;
unsigned int consumed = 0;
unsigned int frame_len, frag_len;
struct gmac_rxdesc *rx = NULL;
@@ -1475,7 +1479,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget,
r = rw.bits.rptr;
w = rw.bits.wptr;
- while (budget && w != r) {
+ while (budget && consumed < desc_limit && w != r) {
page = NULL;
rx = port->rxq_ring + r;
word0 = rx->word0;@@ -1502,15 +1506,18 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget,
}
if (!mapping) {
- netdev_err(netdev,
- "rxq[%u]: HW BUG: zero DMA desc\n", r);
+ if (net_ratelimit())
+ netdev_err(netdev,
+ "rxq[%u]: HW BUG: zero DMA desc\n",
+ r);
goto err_drop;
}
/* Freeq pointers are one page off */
gpage = gmac_get_queue_page(geth, port, mapping + PAGE_SIZE);
if (!gpage) {
- dev_err(geth->dev, "could not find mapping\n");
+ dev_err_ratelimited(geth->dev,
+ "could not find mapping\n");
goto err_drop;
}
page = gpage->page;@@ -1535,7 +1542,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget,
if (frag_nr == MAX_SKB_FRAGS)
goto err_drop;
- if (frag_len == 0)
+ if (frag_len == 0 && net_ratelimit())
netdev_err(netdev, "Received fragment with len = 0\n");
skb_fill_page_desc(skb, frag_nr, page, page_offs, frag_len);
@@ -1579,6 +1586,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget,
port->rx_frag_nr = frag_nr;
port->rx_dropping = dropping;
*freeq_consumed = consumed;
+ *reschedule = budget && w != r;
writew(r, ptr_reg);
return received;
}
@@ -1590,12 +1598,13 @@ static int gmac_napi_poll(struct napi_struct *napi, int budget)
unsigned int freeq_threshold;
unsigned int freeq_consumed;
unsigned int received;
+ bool reschedule;
freeq_threshold = 1 << (geth->freeq_order - 1);
u64_stats_update_begin(&port->rx_stats_syncp);
- received = gmac_rx(napi->dev, budget, &freeq_consumed);
- if (received < budget)
+ received = gmac_rx(napi->dev, budget, &freeq_consumed, &reschedule);
+ if (!reschedule && received < budget)
++port->rx_napi_exits;
u64_stats_update_end(&port->rx_stats_syncp);
@@ -1606,10 +1615,11 @@ static int gmac_napi_poll(struct napi_struct *napi, int budget)
geth_fill_freeq(geth, true);
}
- if (received < budget && napi_complete_done(napi, received))
+ if (!reschedule && received < budget &&
+ napi_complete_done(napi, received))
gmac_enable_rx_irq(napi->dev, 1);
- return received;
+ return reschedule ? budget : received;
}
static void gmac_dump_dma_state(struct net_device *netdev)
--
2.55.0