diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index f08de623e6f7..663d2ce8414f 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -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)