From b853f31940c84cce6a3f5cb4f529b029906ee69c Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Mon, 25 Aug 2014 16:22:11 +0200 Subject: net: moxa: clear DESC1 on ndo_start_xmit() TX buffer length is not cleared on ndo_start_xmit(). Failing to do so can bug/hang the controller and cause TX interrupts to stop altogether. Remove the readl() and compute a new value for DESC1. Addresses https://bugzilla.kernel.org/show_bug.cgi?id=69031 Signed-off-by: Jonas Jensen Signed-off-by: David S. Miller --- drivers/net/ethernet/moxa/moxart_ether.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/net/ethernet/moxa/moxart_ether.c') diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c index 5020fd47825d..eed70d9f0d9d 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.c +++ b/drivers/net/ethernet/moxa/moxart_ether.c @@ -346,10 +346,9 @@ static int moxart_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev) len = ETH_ZLEN; } - txdes1 = readl(desc + TX_REG_OFFSET_DESC1); - txdes1 |= TX_DESC1_LTS | TX_DESC1_FTS; - txdes1 &= ~(TX_DESC1_FIFO_COMPLETE | TX_DESC1_INTR_COMPLETE); - txdes1 |= (len & TX_DESC1_BUF_SIZE_MASK); + txdes1 = TX_DESC1_LTS | TX_DESC1_FTS | (len & TX_DESC1_BUF_SIZE_MASK); + if (tx_head == TX_DESC_NUM_MASK) + txdes1 |= TX_DESC1_END; writel(txdes1, desc + TX_REG_OFFSET_DESC1); writel(TX_DESC0_DMA_OWN, desc + TX_REG_OFFSET_DESC0); -- cgit v1.2.3 From 9fe1b3bc8d9182e950a744e4a47187420efa67f3 Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Mon, 25 Aug 2014 16:22:22 +0200 Subject: net: moxa: replace build_skb() with netdev_alloc_skb_ip_align() / memcpy() build_skb() is used to make skbs out of existing RX ring memory which is bad because the RX ring is allocated only once, on probe. Memory corruption occur because said memory is reclaimed, i.e. __kfree_skb() (and eventually put_page()). Replace build_skb() with netdev_alloc_skb_ip_align() and use memcpy(). Remove SKB_DATA_ALIGN() from RX buffer size while we're at it. Addresses https://bugzilla.kernel.org/show_bug.cgi?id=69041 Signed-off-by: Jonas Jensen Signed-off-by: David S. Miller --- drivers/net/ethernet/moxa/moxart_ether.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/net/ethernet/moxa/moxart_ether.c') diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c index eed70d9f0d9d..d66058d5c429 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.c +++ b/drivers/net/ethernet/moxa/moxart_ether.c @@ -226,13 +226,15 @@ static int moxart_rx_poll(struct napi_struct *napi, int budget) if (len > RX_BUF_SIZE) len = RX_BUF_SIZE; - skb = build_skb(priv->rx_buf[rx_head], priv->rx_buf_size); + skb = netdev_alloc_skb_ip_align(ndev, len); + if (unlikely(!skb)) { - net_dbg_ratelimited("build_skb failed\n"); + net_dbg_ratelimited("netdev_alloc_skb_ip_align failed\n"); priv->stats.rx_dropped++; priv->stats.rx_errors++; } + memcpy(skb->data, priv->rx_buf[rx_head], len); skb_put(skb, len); skb->protocol = eth_type_trans(skb, ndev); napi_gro_receive(&priv->napi, skb); @@ -464,8 +466,7 @@ static int moxart_mac_probe(struct platform_device *pdev) spin_lock_init(&priv->txlock); priv->tx_buf_size = TX_BUF_SIZE; - priv->rx_buf_size = RX_BUF_SIZE + - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + priv->rx_buf_size = RX_BUF_SIZE; priv->tx_desc_base = dma_alloc_coherent(NULL, TX_REG_DESC_SIZE * TX_DESC_NUM, &priv->tx_base, -- cgit v1.2.3 From 777fbc31447f111a1492c90fae2bc616f836d407 Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Mon, 25 Aug 2014 16:22:32 +0200 Subject: net: moxa: synchronize DMA memory DMA memory should be synchronized before data is passed to/from controller. Add dma_sync_single_for_cpu(.., DMA_FROM_DEVICE) to RX path and dma_sync_single_for_device(.., DMA_TO_DEVICE) to TX path. Signed-off-by: Jonas Jensen Signed-off-by: David S. Miller --- drivers/net/ethernet/moxa/moxart_ether.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/net/ethernet/moxa/moxart_ether.c') diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c index d66058d5c429..983d01949795 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.c +++ b/drivers/net/ethernet/moxa/moxart_ether.c @@ -226,6 +226,9 @@ static int moxart_rx_poll(struct napi_struct *napi, int budget) if (len > RX_BUF_SIZE) len = RX_BUF_SIZE; + dma_sync_single_for_cpu(&ndev->dev, + priv->rx_mapping[rx_head], + priv->rx_buf_size, DMA_FROM_DEVICE); skb = netdev_alloc_skb_ip_align(ndev, len); if (unlikely(!skb)) { @@ -348,6 +351,9 @@ static int moxart_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev) len = ETH_ZLEN; } + dma_sync_single_for_device(&ndev->dev, priv->tx_mapping[tx_head], + priv->tx_buf_size, DMA_TO_DEVICE); + txdes1 = TX_DESC1_LTS | TX_DESC1_FTS | (len & TX_DESC1_BUF_SIZE_MASK); if (tx_head == TX_DESC_NUM_MASK) txdes1 |= TX_DESC1_END; -- cgit v1.2.3 From 2b7890e757bc42f963c835521c2657bd16d7b7d6 Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Mon, 25 Aug 2014 16:22:40 +0200 Subject: net: moxa: continue loop on skb allocation failure If netdev_alloc_skb_ip_align() fails, subsequent code will try to dereference an invalid pointer. Continue to next descriptor on error. While we're at it, 1. eliminate the chance of an endless loop, replace the main loop with while(rx < budget) 2. use napi_complete() and remove the explicit napi_gro_flush() Signed-off-by: Jonas Jensen Signed-off-by: David S. Miller --- drivers/net/ethernet/moxa/moxart_ether.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/net/ethernet/moxa/moxart_ether.c') diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c index 983d01949795..2f12c88c66ab 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.c +++ b/drivers/net/ethernet/moxa/moxart_ether.c @@ -206,7 +206,7 @@ static int moxart_rx_poll(struct napi_struct *napi, int budget) int rx_head = priv->rx_head; int rx = 0; - while (1) { + while (rx < budget) { desc = priv->rx_desc_base + (RX_REG_DESC_SIZE * rx_head); desc0 = readl(desc + RX_REG_OFFSET_DESC0); @@ -218,7 +218,7 @@ static int moxart_rx_poll(struct napi_struct *napi, int budget) net_dbg_ratelimited("packet error\n"); priv->stats.rx_dropped++; priv->stats.rx_errors++; - continue; + goto rx_next; } len = desc0 & RX_DESC0_FRAME_LEN_MASK; @@ -235,6 +235,7 @@ static int moxart_rx_poll(struct napi_struct *napi, int budget) net_dbg_ratelimited("netdev_alloc_skb_ip_align failed\n"); priv->stats.rx_dropped++; priv->stats.rx_errors++; + goto rx_next; } memcpy(skb->data, priv->rx_buf[rx_head], len); @@ -249,18 +250,15 @@ static int moxart_rx_poll(struct napi_struct *napi, int budget) if (desc0 & RX_DESC0_MULTICAST) priv->stats.multicast++; +rx_next: writel(RX_DESC0_DMA_OWN, desc + RX_REG_OFFSET_DESC0); rx_head = RX_NEXT(rx_head); priv->rx_head = rx_head; - - if (rx >= budget) - break; } if (rx < budget) { - napi_gro_flush(napi, false); - __napi_complete(napi); + napi_complete(napi); } priv->reg_imr |= RPKT_FINISH_M; -- cgit v1.2.3