From 1be0a3dd01a64ef7ab5abfe2bb45ed6c17ee5f62 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 18 Nov 2019 13:53:46 +0200 Subject: [PATCH 1/9] net: ti: icssg-prueth: Fix non promiscuous mode Setting RX_CLASS_GATES_RAW_MASK will bypass filters and will act as promiscuous mode. Signed-off-by: Roger Quadros Signed-off-by: Sekhar Nori --- drivers/net/ethernet/ti/icssg_classifier.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/ti/icssg_classifier.c b/drivers/net/ethernet/ti/icssg_classifier.c index ba021c40957d51..58afdc7bac6ed1 100644 --- a/drivers/net/ethernet/ti/icssg_classifier.c +++ b/drivers/net/ethernet/ti/icssg_classifier.c @@ -257,7 +257,7 @@ void icssg_class_disable(struct regmap *miig_rt, int slice) /* configure gate */ offset = RX_CLASS_GATES_N_REG(slice, n); regmap_read(miig_rt, offset, &data); - /* clear class_raw */ + /* clear class_raw so we go through filters */ data &= ~RX_CLASS_GATES_RAW_MASK; /* set allow and phase mask */ data |= RX_CLASS_GATES_ALLOW_MASK | RX_CLASS_GATES_PHASE_MASK; @@ -275,7 +275,7 @@ void icssg_class_disable(struct regmap *miig_rt, int slice) void icssg_class_default(struct regmap *miig_rt, int slice) { - u32 offset, data; + u32 data; int n; /* defaults */ @@ -296,12 +296,6 @@ void icssg_class_default(struct regmap *miig_rt, int slice) /* set CFG1 for OR_OR_AND for classifier */ rx_class_sel_set_type(miig_rt, slice, n, RX_CLASS_SEL_TYPE_OR_OR_AND); - - /* ungate classifier */ - offset = RX_CLASS_GATES_N_REG(slice, n); - regmap_read(miig_rt, offset, &data); - data |= RX_CLASS_GATES_RAW_MASK; - regmap_write(miig_rt, offset, data); } /* clear CFG2 */ From b3c9f08916a3493041da9b6dc6ec774d1e87a9db Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 18 Nov 2019 13:53:47 +0200 Subject: [PATCH 2/9] net: ti: icssg-prueth: Add allmulti support We can easily support allmulti by using RX_CLASS_FT_MC flag in the classifier. Signed-off-by: Roger Quadros Signed-off-by: Sekhar Nori --- drivers/net/ethernet/ti/icssg_classifier.c | 7 ++++++- drivers/net/ethernet/ti/icssg_prueth.c | 24 +++++++--------------- drivers/net/ethernet/ti/icssg_prueth.h | 3 +-- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/ti/icssg_classifier.c b/drivers/net/ethernet/ti/icssg_classifier.c index 58afdc7bac6ed1..a776faea3658b5 100644 --- a/drivers/net/ethernet/ti/icssg_classifier.c +++ b/drivers/net/ethernet/ti/icssg_classifier.c @@ -273,7 +273,7 @@ void icssg_class_disable(struct regmap *miig_rt, int slice) regmap_write(miig_rt, offs[slice].rx_class_cfg2, 0); } -void icssg_class_default(struct regmap *miig_rt, int slice) +void icssg_class_default(struct regmap *miig_rt, int slice, bool allmulti) { u32 data; int n; @@ -291,6 +291,11 @@ void icssg_class_default(struct regmap *miig_rt, int slice) for (n = 0; n < 5; n++) { /* match on Broadcast or MAC_PRU address */ data = RX_CLASS_FT_BC | RX_CLASS_FT_DA_P; + + /* multicast? */ + if (allmulti) + data |= RX_CLASS_FT_MC; + rx_class_set_or(miig_rt, slice, n, data); /* set CFG1 for OR_OR_AND for classifier */ diff --git a/drivers/net/ethernet/ti/icssg_prueth.c b/drivers/net/ethernet/ti/icssg_prueth.c index 1459dd120470b2..f4e42de22b762b 100644 --- a/drivers/net/ethernet/ti/icssg_prueth.c +++ b/drivers/net/ethernet/ti/icssg_prueth.c @@ -1093,7 +1093,7 @@ static int emac_ndo_open(struct net_device *ndev) ether_addr_copy(emac->mac_addr, ndev->dev_addr); icssg_class_set_mac_addr(prueth->miig_rt, slice, emac->mac_addr); - icssg_class_default(prueth->miig_rt, slice); + icssg_class_default(prueth->miig_rt, slice, 0); netif_carrier_off(ndev); @@ -1324,25 +1324,15 @@ static void emac_ndo_set_rx_mode(struct net_device *ndev) struct prueth_emac *emac = netdev_priv(ndev); struct prueth *prueth = emac->prueth; int slice = prueth_emac_slice(emac); + bool promisc = ndev->flags & IFF_PROMISC; + bool allmulti = ndev->flags & IFF_ALLMULTI; - if (ndev->flags & IFF_PROMISC) { - /* enable promiscuous */ - if (!(emac->flags & IFF_PROMISC)) { - icssg_class_promiscuous(prueth->miig_rt, slice); - emac->flags |= IFF_PROMISC; - } + if (promisc) { + icssg_class_promiscuous(prueth->miig_rt, slice); return; - } else if (ndev->flags & IFF_ALLMULTI) { - /* TODO: enable all multicast */ - } else { - if (emac->flags & IFF_PROMISC) { - /* local MAC + BC only */ - icssg_class_default(prueth->miig_rt, slice); - emac->flags &= ~IFF_PROMISC; - } - - /* TODO: specific multi */ } + + icssg_class_default(prueth->miig_rt, slice, allmulti); } static int emac_set_timestamp_mode(struct prueth_emac *emac, diff --git a/drivers/net/ethernet/ti/icssg_prueth.h b/drivers/net/ethernet/ti/icssg_prueth.h index fe32f22455251c..d28061f0ae3b91 100644 --- a/drivers/net/ethernet/ti/icssg_prueth.h +++ b/drivers/net/ethernet/ti/icssg_prueth.h @@ -135,7 +135,6 @@ struct prueth_emac { int rx_mgm_flow_id_base; spinlock_t lock; /* serialize access */ - unsigned int flags; /* TX HW Timestamping */ u32 tx_ts_cookie; @@ -189,7 +188,7 @@ struct emac_tx_ts_response { /* Classifier helpers */ void icssg_class_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac); void icssg_class_disable(struct regmap *miig_rt, int slice); -void icssg_class_default(struct regmap *miig_rt, int slice); +void icssg_class_default(struct regmap *miig_rt, int slice, bool allmulti); void icssg_class_promiscuous(struct regmap *miig_rt, int slice); /* get PRUSS SLICE number from prueth_emac */ From 6933c4b5de1a4fe17918f50a4f9c902d8d9b4772 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 18 Nov 2019 13:53:48 +0200 Subject: [PATCH 3/9] net: ethernet: ti: icssg-prueth: Simplify promiscuous mode We can bypass all filters by just setting the RX_CLASS_GATES_RAW_MASK in the classifiers thus putting us in promiscuous mode. Signed-off-by: Roger Quadros Signed-off-by: Sekhar Nori --- drivers/net/ethernet/ti/icssg_classifier.c | 30 +--------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/drivers/net/ethernet/ti/icssg_classifier.c b/drivers/net/ethernet/ti/icssg_classifier.c index a776faea3658b5..21b8aeb3520d3c 100644 --- a/drivers/net/ethernet/ti/icssg_classifier.c +++ b/drivers/net/ethernet/ti/icssg_classifier.c @@ -316,37 +316,9 @@ void icssg_class_promiscuous(struct regmap *miig_rt, int slice) /* defaults */ icssg_class_disable(miig_rt, slice); - /* FT1 uses 6 bytes of DA address */ - offset = offs[slice].ft1_start_len; - regmap_write(miig_rt, offset, FT1_LEN(6)); - - /* FT1 type EQ */ - for (n = 0; n < ICSSG_NUM_FT1_SLOTS; n++) - rx_class_ft1_cfg_set_type(miig_rt, slice, n, FT1_CFG_TYPE_EQ); - - /* FT1[0] DA compare address 00-00-00-00-00-00 */ - offset = FT1_N_REG(slice, 0, FT1_DA0); - regmap_write(miig_rt, offset, 0); - offset = FT1_N_REG(slice, 0, FT1_DA1); - regmap_write(miig_rt, offset, 0); - - /* FT1[0] mask FE-FF-FF-FF-FF-FF */ - offset = FT1_N_REG(slice, 0, FT1_DA0_MASK); - regmap_write(miig_rt, offset, 0xfffffffe); - offset = FT1_N_REG(slice, 0, FT1_DA1_MASK); - regmap_write(miig_rt, offset, 0xffff); - /* Setup Classifier */ for (n = 0; n < 5; n++) { - /* match on multicast, broadcast or unicast (ft1-0 address) */ - data = RX_CLASS_FT_MC | RX_CLASS_FT_BC | FT1_MATCH_SLOT(0); - rx_class_set_or(miig_rt, slice, n, data); - - /* set CFG1 for OR_OR_AND for classifier */ - rx_class_sel_set_type(miig_rt, slice, n, - RX_CLASS_SEL_TYPE_OR_OR_AND); - - /* ungate classifier */ + /* set RAW_MASK to bypass filters */ offset = RX_CLASS_GATES_N_REG(slice, n); regmap_read(miig_rt, offset, &data); data |= RX_CLASS_GATES_RAW_MASK; From 9278b9a953ecb98ffb1257891453cef052febf42 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 18 Nov 2019 13:53:49 +0200 Subject: [PATCH 4/9] net: ethernet: ti: icssg-prueth: remove redundant disable FT1 We're already disbling FT1 in icssg_class_disable() so don't do it again. Signed-off-by: Roger Quadros Signed-off-by: Sekhar Nori --- drivers/net/ethernet/ti/icssg_classifier.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/ethernet/ti/icssg_classifier.c b/drivers/net/ethernet/ti/icssg_classifier.c index 21b8aeb3520d3c..8b93f8608be0df 100644 --- a/drivers/net/ethernet/ti/icssg_classifier.c +++ b/drivers/net/ethernet/ti/icssg_classifier.c @@ -281,12 +281,6 @@ void icssg_class_default(struct regmap *miig_rt, int slice, bool allmulti) /* defaults */ icssg_class_disable(miig_rt, slice); - /* FT1 Disabled */ - for (n = 0; n < ICSSG_NUM_FT1_SLOTS; n++) { - rx_class_ft1_cfg_set_type(miig_rt, slice, n, - FT1_CFG_TYPE_DISABLED); - } - /* Setup Classifier */ for (n = 0; n < 5; n++) { /* match on Broadcast or MAC_PRU address */ From b59ba9b5235bc126309e743d647e355426b2ea60 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 22 Nov 2019 11:51:24 +0200 Subject: [PATCH 5/9] dmaengine: ti: clear pkt_info2 pkt type before setting Make sure to clear the old pkt_info2 bits, before setting new bits. Cc: Peter Ujfalusi Signed-off-by: Roger Quadros Signed-off-by: Sekhar Nori --- include/linux/dma/ti-cppi5.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/dma/ti-cppi5.h b/include/linux/dma/ti-cppi5.h index 860f753e6797a8..e1ff884609d60e 100644 --- a/include/linux/dma/ti-cppi5.h +++ b/include/linux/dma/ti-cppi5.h @@ -458,6 +458,7 @@ static inline void cppi5_hdesc_set_pkttype(struct cppi5_host_desc_t *desc, u32 pkt_type) { WARN_ON(!desc); + desc->hdr.pkt_info2 &= ~CPPI5_INFO2_HDESC_PKTTYPE_MASK; desc->hdr.pkt_info2 |= (pkt_type << CPPI5_INFO2_HDESC_PKTTYPE_SHIFT) & CPPI5_INFO2_HDESC_PKTTYPE_MASK; From ceb97a2b440ae08750d98172d38f5c3d20f609be Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 22 Nov 2019 11:51:25 +0200 Subject: [PATCH 6/9] net: ti: icssg_prueth: implement shutdown command Implementing the shutdown command ensures that firmware does a cleanup we cleanly complete all pending DMA transfers. Without this we see TX teardown timeouts at ndo_stop. Acked-by : Murali Karicheri Signed-off-by: Roger Quadros Signed-off-by: Sekhar Nori --- drivers/net/ethernet/ti/icssg_prueth.c | 83 +++++++++++++++++++++++++- drivers/net/ethernet/ti/icssg_prueth.h | 4 ++ 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/ti/icssg_prueth.c b/drivers/net/ethernet/ti/icssg_prueth.c index f4e42de22b762b..32ac6777f51b1a 100644 --- a/drivers/net/ethernet/ti/icssg_prueth.c +++ b/drivers/net/ethernet/ti/icssg_prueth.c @@ -41,6 +41,7 @@ #define MSMC_RAM_SIZE (SZ_64K + SZ_32K + SZ_2K) /* 0x1880 x 8 x 2 */ +#define PRUETH_PKT_TYPE_CMD 0x10 #define PRUETH_NAV_PS_DATA_SIZE 16 /* Protocol specific data size */ #define PRUETH_NAV_SW_DATA_SIZE 16 /* SW related data size */ #define PRUETH_MAX_TX_DESC 512 @@ -468,6 +469,65 @@ static void prueth_xmit_free(struct prueth_tx_chn *tx_chn, k3_knav_pool_free(tx_chn->desc_pool, first_desc); } +static int emac_shutdown(struct net_device *ndev) +{ + struct prueth_emac *emac = netdev_priv(ndev); + struct device *dev = emac->prueth->dev; + dma_addr_t desc_dma, buf_dma; + struct prueth_tx_chn *tx_chn; + struct cppi5_host_desc_t *first_desc; + int ret; + u32 *epib; + u32 *data = emac->cmd_data; + u32 pkt_len = sizeof(emac->cmd_data); + void **swdata; + + data[0] = cpu_to_le32(0x81010000); /* shutdown command */ + + /* Map the linear buffer */ + buf_dma = dma_map_single(dev, data, pkt_len, DMA_TO_DEVICE); + if (dma_mapping_error(dev, buf_dma)) { + netdev_err(ndev, "shutdown: failed to map cmd buffer\n"); + return -EINVAL; + } + + tx_chn = &emac->tx_chns; + + first_desc = k3_knav_pool_alloc(tx_chn->desc_pool); + if (!first_desc) { + netdev_err(ndev, "shutdown: failed to allocate descriptor\n"); + dma_unmap_single(dev, buf_dma, pkt_len, DMA_TO_DEVICE); + return -ENOMEM; + } + + cppi5_hdesc_init(first_desc, CPPI5_INFO0_HDESC_EPIB_PRESENT, + PRUETH_NAV_PS_DATA_SIZE); + cppi5_hdesc_set_pkttype(first_desc, PRUETH_PKT_TYPE_CMD); + epib = first_desc->epib; + epib[0] = 0; + epib[1] = 0; + + cppi5_hdesc_attach_buf(first_desc, buf_dma, pkt_len, buf_dma, pkt_len); + swdata = cppi5_hdesc_get_swdata(first_desc); + *swdata = data; + + cppi5_hdesc_set_pktlen(first_desc, pkt_len); + desc_dma = k3_knav_pool_virt2dma(tx_chn->desc_pool, first_desc); + + ret = k3_nav_udmax_push_tx_chn(tx_chn->tx_chn, first_desc, desc_dma); + if (ret) { + netdev_err(ndev, "shutdown: push failed: %d\n", ret); + goto free_desc; + } + + return 0; + +free_desc: + prueth_xmit_free(tx_chn, dev, first_desc); + + return ret; +} + /** * emac_ndo_start_xmit - EMAC Transmit function * @skb: SKB pointer @@ -656,6 +716,14 @@ static int emac_tx_complete_packets(struct prueth_emac *emac, int budget) desc_tx = k3_knav_pool_dma2virt(tx_chn->desc_pool, desc_dma); swdata = cppi5_hdesc_get_swdata(desc_tx); + + /* was this shutdown cmd's TX complete? */ + if (*(swdata) == emac->cmd_data) { + prueth_xmit_free(tx_chn, dev, desc_tx); + budget++; /* not a data packet */ + continue; + } + skb = *(swdata); prueth_xmit_free(tx_chn, dev, desc_tx); @@ -811,6 +879,7 @@ static irqreturn_t prueth_rx_mgm_irq_thread(int irq, void *dev_id) struct prueth_emac *emac = dev_id; struct sk_buff *skb; int flow = PRUETH_MAX_RX_MGM_FLOWS - 1; + u32 rsp; while (flow--) { skb = prueth_process_rx_mgm(emac, flow); @@ -820,6 +889,9 @@ static irqreturn_t prueth_rx_mgm_irq_thread(int irq, void *dev_id) switch (flow) { case PRUETH_RX_MGM_FLOW_RESPONSE: /* Process command response */ + rsp = le32_to_cpu(*(u32 *)skb->data); + if ((rsp & 0xffff0000) == 0x81010000) + complete(&emac->shutdown_complete); break; case PRUETH_RX_MGM_FLOW_TIMESTAMP: prueth_tx_ts(emac, (void *)skb->data); @@ -1097,6 +1169,7 @@ static int emac_ndo_open(struct net_device *ndev) netif_carrier_off(ndev); + init_completion(&emac->shutdown_complete); ret = prueth_init_tx_chns(emac); if (ret) { dev_err(dev, "failed to init tx channel: %d\n", ret); @@ -1239,6 +1312,14 @@ static int emac_ndo_stop(struct net_device *ndev) phy_stop(emac->phydev); icssg_class_disable(prueth->miig_rt, prueth_emac_slice(emac)); + /* send shutdown command */ + reinit_completion(&emac->shutdown_complete); + emac_shutdown(ndev); + ret = wait_for_completion_timeout(&emac->shutdown_complete, + msecs_to_jiffies(100)); + if (!ret) + netdev_err(ndev, "shutdown completion timeout\n"); + /* tear down and disable UDMA channels */ reinit_completion(&emac->tdown_complete); k3_nav_udmax_tdown_tx_chn(emac->tx_chns.tx_chn, false); @@ -1252,8 +1333,6 @@ static int emac_ndo_stop(struct net_device *ndev) prueth_tx_cleanup); k3_nav_udmax_disable_tx_chn(emac->tx_chns.tx_chn); - /* TODO: send shutdown command */ - k3_nav_udmax_tdown_rx_chn(emac->rx_chns.rx_chn, true); for (i = 0; i < PRUETH_MAX_RX_FLOWS; i++) k3_nav_udmax_reset_rx_chn(emac->rx_chns.rx_chn, i, diff --git a/drivers/net/ethernet/ti/icssg_prueth.h b/drivers/net/ethernet/ti/icssg_prueth.h index d28061f0ae3b91..09be93c8664c93 100644 --- a/drivers/net/ethernet/ti/icssg_prueth.h +++ b/drivers/net/ethernet/ti/icssg_prueth.h @@ -140,6 +140,10 @@ struct prueth_emac { u32 tx_ts_cookie; struct sk_buff *tx_ts_skb; unsigned long state; + + /* shutdown related */ + u32 cmd_data[4]; + struct completion shutdown_complete; }; /** From 7a0ba04fa86e5fcbffb38356753ef1aa75119bbe Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Fri, 22 Nov 2019 11:51:26 +0200 Subject: [PATCH 7/9] net: ethernet: ti: icssg-prueth: add missed stats counters prueth_dma_rx_push() fail Add missed update of stats counters on prueth_dma_rx_push() failure. Acked-by : Murali Karicheri Signed-off-by: Grygorii Strashko Signed-off-by: Sekhar Nori --- drivers/net/ethernet/ti/icssg_prueth.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ti/icssg_prueth.c b/drivers/net/ethernet/ti/icssg_prueth.c index 32ac6777f51b1a..3ded5238505adb 100644 --- a/drivers/net/ethernet/ti/icssg_prueth.c +++ b/drivers/net/ethernet/ti/icssg_prueth.c @@ -409,8 +409,11 @@ static int emac_rx_packet(struct prueth_emac *emac, u32 flow_id) /* queue another RX DMA */ ret = prueth_dma_rx_push(emac, new_skb, &emac->rx_chns); - if (WARN_ON(ret < 0)) + if (WARN_ON(ret < 0)) { dev_kfree_skb_any(new_skb); + ndev->stats.rx_errors++; + ndev->stats.rx_dropped++; + } return ret; } From 653f358d056c3964cdd227d0c7bacf8f07d410e5 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Fri, 22 Nov 2019 11:51:27 +0200 Subject: [PATCH 8/9] net: ethernet: ti: icssg-prueth: optimize napi rx during netif down The RX NAPI handler emac_rx_packet() will return errors now in case there are still packets to process while interface is going down or not ready which will cause break of the RX NAPI polling function and NAPI resceduling. In other words, leftover packets will be processed one by one. In such cases (interface is going down or not ready) it's valid to still have unprocessed packets in RX queues and such packets expected just to be dropped by RX NAPI handler emac_rx_packet(). Hence, update emac_rx_packet() to not return errors for packets received while interface is going down or not ready, so they will be processed in batch by NAPI polling function. Acked-by : Murali Karicheri Signed-off-by: Grygorii Strashko Signed-off-by: Sekhar Nori --- drivers/net/ethernet/ti/icssg_prueth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ti/icssg_prueth.c b/drivers/net/ethernet/ti/icssg_prueth.c index 3ded5238505adb..b201678111a5bf 100644 --- a/drivers/net/ethernet/ti/icssg_prueth.c +++ b/drivers/net/ethernet/ti/icssg_prueth.c @@ -388,7 +388,7 @@ static int emac_rx_packet(struct prueth_emac *emac, u32 flow_id) skb->dev = ndev; if (!netif_running(skb->dev)) { dev_kfree_skb_any(skb); - return -ENODEV; + return 0; } new_skb = netdev_alloc_skb_ip_align(ndev, PRUETH_MAX_PKT_SIZE); From 5c44c1766d950231a28defc725ea4b944f4f44b2 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 22 Nov 2019 11:51:28 +0200 Subject: [PATCH 9/9] net: ethernet: ti: icssg-prueth: fix TX descriptor not available case When no TX descriptors are available, we need to return NETDEV_TX_BUSY. Use netdev_dbg() instead of dev_err() since this is not really an error case. Reported-by: Murali Karicheri Acked-by : Murali Karicheri Signed-off-by: Roger Quadros Signed-off-by: Sekhar Nori --- drivers/net/ethernet/ti/icssg_prueth.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/ti/icssg_prueth.c b/drivers/net/ethernet/ti/icssg_prueth.c index b201678111a5bf..25a3ddc3e703ea 100644 --- a/drivers/net/ethernet/ti/icssg_prueth.c +++ b/drivers/net/ethernet/ti/icssg_prueth.c @@ -577,10 +577,10 @@ static int emac_ndo_start_xmit(struct sk_buff *skb, struct net_device *ndev) first_desc = k3_knav_pool_alloc(tx_chn->desc_pool); if (!first_desc) { - dev_err(dev, "tx: failed to allocate descriptor\n"); + netdev_dbg(ndev, "tx: failed to allocate descriptor\n"); dma_unmap_single(dev, buf_dma, pkt_len, DMA_TO_DEVICE); ret = -ENOMEM; - goto drop_stop_q; + goto drop_stop_q_busy; } cppi5_hdesc_init(first_desc, CPPI5_INFO0_HDESC_EPIB_PRESENT, @@ -686,6 +686,10 @@ static int emac_ndo_start_xmit(struct sk_buff *skb, struct net_device *ndev) netdev_err(ndev, "tx: error: %d\n", ret); return ret; + +drop_stop_q_busy: + netif_stop_queue(ndev); + return NETDEV_TX_BUSY; } /**