From b37cf8619e0844cb57afda4dcbe3516fd0c44124 Mon Sep 17 00:00:00 2001 From: Andrei Danaila Date: Fri, 6 Feb 2015 10:50:32 -0800 Subject: [PATCH] mhi: rmnet: Enable support for overflow events MHI RmNet will now wait for all packet fragments from MHI before sending the received packet up the network stack. Change-Id: I3f0958d029a5763e7e0b1e4ab43f17fd98583a48 Signed-off-by: Andrei Danaila --- drivers/net/ethernet/msm/msm_rmnet_mhi.c | 263 ++++++++++++++--------- 1 file changed, 161 insertions(+), 102 deletions(-) diff --git a/drivers/net/ethernet/msm/msm_rmnet_mhi.c b/drivers/net/ethernet/msm/msm_rmnet_mhi.c index 4f1d86aed07e..f024d6d24be8 100644 --- a/drivers/net/ethernet/msm/msm_rmnet_mhi.c +++ b/drivers/net/ethernet/msm/msm_rmnet_mhi.c @@ -47,7 +47,12 @@ enum DBG_LVL { MSG_reserved = 0x80000000 }; -enum DBG_LVL rmnet_ipc_log_lvl = MSG_INFO; +struct __packed mhi_skb_priv { + dma_addr_t dma_addr; + size_t dma_size; +}; + +enum DBG_LVL rmnet_ipc_log_lvl = MSG_VERBOSE; enum DBG_LVL rmnet_msg_lvl = MSG_CRITICAL; module_param(rmnet_msg_lvl , uint, S_IRUGO | S_IWUSR); @@ -55,6 +60,10 @@ MODULE_PARM_DESC(rmnet_msg_lvl, "dbg lvl"); module_param(rmnet_ipc_log_lvl, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(rmnet_ipc_log_lvl, "dbg lvl"); +unsigned int mru = MHI_DEFAULT_MRU; +module_param(mru, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(mru, "MRU interface setting"); + void *rmnet_ipc_log; #define rmnet_log(_msg_lvl, _msg, ...) do { \ @@ -108,6 +117,11 @@ module_param_array(rx_napi_budget_overflow, ulong, 0, S_IRUGO); MODULE_PARM_DESC(rx_napi_budget_overflow, "Budget hit with more items to read counter"); +unsigned long rx_fragmentation[MHI_RMNET_DEVICE_COUNT]; +module_param_array(rx_fragmentation, ulong, 0, S_IRUGO); +MODULE_PARM_DESC(rx_fragmentation, + "Number of fragmented packets received"); + struct rmnet_mhi_private { int dev_index; struct mhi_client_handle *tx_client_handle; @@ -128,50 +142,63 @@ struct rmnet_mhi_private { atomic_t irq_masked_cntr; rwlock_t out_chan_full_lock; atomic_t pending_data; -}; - -struct tx_buffer_priv { - dma_addr_t dma_addr; + struct sk_buff *frag_skb; }; static struct rmnet_mhi_private rmnet_mhi_ctxt_list[MHI_RMNET_DEVICE_COUNT]; -static dma_addr_t rmnet_mhi_internal_get_dma_addr(struct sk_buff *skb, - enum dma_data_direction dir) +static int rmnet_mhi_process_fragment(struct rmnet_mhi_private *rmnet_mhi_ptr, + struct sk_buff *skb, int frag) { - if (dir == DMA_TO_DEVICE) { - struct tx_buffer_priv *tx_priv = - (struct tx_buffer_priv *)(skb->cb); - return tx_priv->dma_addr; - } else /* DMA_FROM_DEVICE */{ - uintptr_t *cb_ptr = 0; - cb_ptr = (uintptr_t *)skb->cb; - return (dma_addr_t)(uintptr_t)(*cb_ptr); + struct sk_buff *temp_skb; + if (rmnet_mhi_ptr->frag_skb) { + /* Merge the new skb into the old fragment */ + temp_skb = skb_copy_expand(rmnet_mhi_ptr->frag_skb, + MHI_RX_HEADROOM, + skb->len, + GFP_ATOMIC); + if (!temp_skb) { + kfree(rmnet_mhi_ptr->frag_skb); + rmnet_mhi_ptr->frag_skb = NULL; + return -ENOMEM; + } + kfree_skb(rmnet_mhi_ptr->frag_skb); + rmnet_mhi_ptr->frag_skb = temp_skb; + memcpy(skb_put(rmnet_mhi_ptr->frag_skb, skb->len), + skb->data, + skb->len); + kfree_skb(skb); + if (!frag) { + /* Last fragmented piece was received, ship it */ + netif_receive_skb(rmnet_mhi_ptr->frag_skb); + rmnet_mhi_ptr->frag_skb = NULL; + } + } else { + if (frag) { + /* This is the first fragment */ + rmnet_mhi_ptr->frag_skb = skb; + rx_fragmentation[rmnet_mhi_ptr->dev_index]++; + } else { + netif_receive_skb(skb); + } } + return 0; } - static void rmnet_mhi_internal_clean_unmap_buffers(struct net_device *dev, struct sk_buff_head *queue, enum dma_data_direction dir) { - struct rmnet_mhi_private *rmnet_mhi_ptr = - *(struct rmnet_mhi_private **)netdev_priv(dev); + struct mhi_skb_priv *skb_priv; + rmnet_log(MSG_INFO, "Entered\n"); while (!skb_queue_empty(queue)) { struct sk_buff *skb = skb_dequeue(queue); + skb_priv = (struct mhi_skb_priv *)(skb->cb); if (skb != 0) { - dma_addr_t dma_addr = - rmnet_mhi_internal_get_dma_addr(skb, dir); - if (dir == DMA_FROM_DEVICE) - dma_unmap_single(&(dev->dev), - dma_addr, - (rmnet_mhi_ptr->mru - MHI_RX_HEADROOM), + dma_unmap_single(&dev->dev, + skb_priv->dma_addr, + skb_priv->dma_size, dir); - else - dma_unmap_single(&(dev->dev), - dma_addr, - skb->len, - dir); kfree_skb(skb); } } @@ -207,18 +234,20 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget) enum MHI_STATUS res = MHI_STATUS_reserved; bool should_reschedule = true; struct sk_buff *skb; - dma_addr_t dma_addr; - uintptr_t *cb_ptr; + struct mhi_skb_priv *skb_priv; + int r, cur_mru; rmnet_log(MSG_VERBOSE, "Entered\n"); + rmnet_mhi_ptr->mru = mru; while (received_packets < budget) { struct mhi_result *result = mhi_poll(rmnet_mhi_ptr->rx_client_handle); if (result->transaction_status == MHI_STATUS_DEVICE_NOT_READY) { continue; - } else if (result->transaction_status != MHI_STATUS_SUCCESS) { + } else if (result->transaction_status != MHI_STATUS_SUCCESS && + result->transaction_status != MHI_STATUS_OVERFLOW) { rmnet_log(MSG_CRITICAL, - "mhi_poll failed, error is %d\n", + "mhi_poll failed, error %d\n", result->transaction_status); break; } @@ -237,27 +266,41 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget) break; } - cb_ptr = (uintptr_t *)skb->cb; - dma_addr = (dma_addr_t)(uintptr_t)(*cb_ptr); + skb_priv = (struct mhi_skb_priv *)(skb->cb); /* Sanity check, ensuring that this is actually the buffer */ - if (unlikely(dma_addr != result->payload_buf)) { + if (unlikely(skb_priv->dma_addr != result->payload_buf || + skb_priv->dma_size < result->bytes_xferd)) { rmnet_log(MSG_CRITICAL, - "Buf mismatch, expected 0x%lx, got 0x%lx", - (uintptr_t)dma_addr, - (uintptr_t)result->payload_buf); - break; + "BUG: expected 0x%lx size 0x%x, got 0x%lx, size 0x%x", + (uintptr_t)skb_priv->dma_addr, + skb_priv->dma_size, + (uintptr_t)result->payload_buf, + result->bytes_xferd); + BUG(); } - dma_unmap_single(&(dev->dev), dma_addr, - (rmnet_mhi_ptr->mru - MHI_RX_HEADROOM), + dma_unmap_single(&dev->dev, + skb_priv->dma_addr, + skb_priv->dma_size, DMA_FROM_DEVICE); + + /* Setup the tail to the end of data */ skb_put(skb, result->bytes_xferd); skb->dev = dev; skb->protocol = rmnet_mhi_ip_type_trans(skb); - netif_receive_skb(skb); + if (result->transaction_status == MHI_STATUS_OVERFLOW) + r = rmnet_mhi_process_fragment(rmnet_mhi_ptr, skb, 1); + else + r = rmnet_mhi_process_fragment(rmnet_mhi_ptr, skb, 0); + if (r) { + rmnet_log(MSG_CRITICAL, + "Failed to process fragmented packet ret %d", + r); + BUG(); + } /* Statistics */ received_packets++; @@ -265,23 +308,35 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget) dev->stats.rx_bytes += result->bytes_xferd; /* Need to allocate a new buffer instead of this one */ - skb = alloc_skb(rmnet_mhi_ptr->mru, GFP_ATOMIC); - + cur_mru = rmnet_mhi_ptr->mru; + skb = alloc_skb(cur_mru, GFP_ATOMIC); if (unlikely(!skb)) { rmnet_log(MSG_CRITICAL, "Can't allocate a new RX buffer for MHI"); break; } + skb_priv = (struct mhi_skb_priv *)(skb->cb); + skb_priv->dma_size = cur_mru; + rmnet_log(MSG_VERBOSE, + "Allocated SKB of MRU 0x%x, SKB_DATA 0%p SKB_LEN 0x%x\n", + rmnet_mhi_ptr->mru, skb->data, skb->len); + /* Reserve headroom, tail == data */ skb_reserve(skb, MHI_RX_HEADROOM); - - cb_ptr = (uintptr_t *)skb->cb; - dma_addr = dma_map_single(&(dev->dev), skb->data, - (rmnet_mhi_ptr->mru - MHI_RX_HEADROOM), + skb_priv->dma_size -= MHI_RX_HEADROOM; + skb_priv->dma_addr = dma_map_single(&dev->dev, + skb->data, + skb_priv->dma_size, DMA_FROM_DEVICE); - *cb_ptr = (uintptr_t)dma_addr; - if (unlikely(dma_mapping_error(&(dev->dev), dma_addr))) { + rmnet_log(MSG_VERBOSE, + "Mapped SKB %p to DMA Addr 0x%lx, DMA_SIZE: 0x%lx\n", + skb->data, + (uintptr_t)skb_priv->dma_addr, + (uintptr_t)skb_priv->dma_size); + + if (unlikely(dma_mapping_error(&dev->dev, + skb_priv->dma_addr))) { rmnet_log(MSG_CRITICAL, "DMA mapping error in polling function"); dev_kfree_skb_irq(skb); @@ -290,21 +345,19 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget) res = mhi_queue_xfer( rmnet_mhi_ptr->rx_client_handle, - (uintptr_t)dma_addr, rmnet_mhi_ptr->mru, MHI_EOT); + skb_priv->dma_addr, skb_priv->dma_size, MHI_EOT); if (unlikely(MHI_STATUS_SUCCESS != res)) { rmnet_log(MSG_CRITICAL, "mhi_queue_xfer failed, error %d", res); - dma_unmap_single(&(dev->dev), dma_addr, - (rmnet_mhi_ptr->mru - MHI_RX_HEADROOM), + dma_unmap_single(&dev->dev, skb_priv->dma_addr, + skb_priv->dma_size, DMA_FROM_DEVICE); dev_kfree_skb_irq(skb); break; } - - skb_queue_tail(&(rmnet_mhi_ptr->rx_buffers), skb); - + skb_queue_tail(&rmnet_mhi_ptr->rx_buffers, skb); } /* while (received_packets < budget) or any other error */ napi_complete(napi); @@ -340,12 +393,12 @@ void rmnet_mhi_clean_buffers(struct net_device *dev) rmnet_log(MSG_INFO, "Entered\n"); /* Clean TX buffers */ rmnet_mhi_internal_clean_unmap_buffers(dev, - &(rmnet_mhi_ptr->tx_buffers), + &rmnet_mhi_ptr->tx_buffers, DMA_TO_DEVICE); /* Clean RX buffers */ rmnet_mhi_internal_clean_unmap_buffers(dev, - &(rmnet_mhi_ptr->rx_buffers), + &rmnet_mhi_ptr->rx_buffers, DMA_FROM_DEVICE); rmnet_log(MSG_INFO, "Exited\n"); } @@ -368,56 +421,57 @@ static int rmnet_mhi_init_inbound(struct rmnet_mhi_private *rmnet_mhi_ptr) { u32 i; enum MHI_STATUS res; + struct mhi_skb_priv *rx_priv; + u32 cur_mru = rmnet_mhi_ptr->mru; + struct sk_buff *skb; + rmnet_log(MSG_INFO, "Entered\n"); - rmnet_mhi_ptr->tx_buffers_max = - mhi_get_max_desc( - rmnet_mhi_ptr->tx_client_handle); - rmnet_mhi_ptr->rx_buffers_max = - mhi_get_max_desc( - rmnet_mhi_ptr->rx_client_handle); + rmnet_mhi_ptr->tx_buffers_max = mhi_get_max_desc( + rmnet_mhi_ptr->tx_client_handle); + rmnet_mhi_ptr->rx_buffers_max = mhi_get_max_desc( + rmnet_mhi_ptr->rx_client_handle); for (i = 0; i < rmnet_mhi_ptr->rx_buffers_max; i++) { - struct sk_buff *skb = 0; - dma_addr_t dma_addr; - dma_addr_t *cb_ptr = 0; - skb = alloc_skb(rmnet_mhi_ptr->mru, - rmnet_mhi_ptr->allocation_flags); + skb = alloc_skb(cur_mru, rmnet_mhi_ptr->allocation_flags); if (!skb) { rmnet_log(MSG_CRITICAL, "SKB allocation failure during open"); return -ENOMEM; } + rx_priv = (struct mhi_skb_priv *)(skb->cb); skb_reserve(skb, MHI_RX_HEADROOM); - cb_ptr = (dma_addr_t *)skb->cb; - dma_addr = dma_map_single(&(rmnet_mhi_ptr->dev->dev), skb->data, - (rmnet_mhi_ptr->mru - MHI_RX_HEADROOM), - DMA_FROM_DEVICE); - *cb_ptr = dma_addr; - if (dma_mapping_error(&(rmnet_mhi_ptr->dev->dev), dma_addr)) { + rx_priv->dma_size = cur_mru - MHI_RX_HEADROOM; + rx_priv->dma_addr = dma_map_single(&rmnet_mhi_ptr->dev->dev, + skb->data, + rx_priv->dma_size, + DMA_FROM_DEVICE); + if (dma_mapping_error(&rmnet_mhi_ptr->dev->dev, + rx_priv->dma_addr)) { rmnet_log(MSG_CRITICAL, "DMA mapping for RX buffers has failed"); kfree_skb(skb); return -EIO; } - skb_queue_tail(&(rmnet_mhi_ptr->rx_buffers), skb); + skb_queue_tail(&rmnet_mhi_ptr->rx_buffers, skb); } /* Submit the RX buffers */ for (i = 0; i < rmnet_mhi_ptr->rx_buffers_max; i++) { - struct sk_buff *skb = skb_dequeue(&(rmnet_mhi_ptr->rx_buffers)); + skb = skb_dequeue(&rmnet_mhi_ptr->rx_buffers); + rx_priv = (struct mhi_skb_priv *)(skb->cb); res = mhi_queue_xfer(rmnet_mhi_ptr->rx_client_handle, - *((dma_addr_t *)(skb->cb)), - rmnet_mhi_ptr->mru - MHI_RX_HEADROOM, - MHI_EOT); + rx_priv->dma_addr, + rx_priv->dma_size, + MHI_EOT); if (MHI_STATUS_SUCCESS != res) { rmnet_log(MSG_CRITICAL, "mhi_queue_xfer failed, error %d", res); return -EIO; } - skb_queue_tail(&(rmnet_mhi_ptr->rx_buffers), skb); + skb_queue_tail(&rmnet_mhi_ptr->rx_buffers, skb); } rmnet_log(MSG_INFO, "Exited\n"); return 0; @@ -446,28 +500,25 @@ static void rmnet_mhi_tx_cb(struct mhi_result *result) "NULL buffer returned, error"); break; } else { - struct tx_buffer_priv *tx_priv = - (struct tx_buffer_priv *)(skb->cb); - dma_addr_t dma_addr = tx_priv->dma_addr; - int data_len = skb->len; + struct mhi_skb_priv *tx_priv = + (struct mhi_skb_priv *)(skb->cb); dma_unmap_single(&(dev->dev), - dma_addr, - skb->len, + tx_priv->dma_addr, + tx_priv->dma_size, DMA_TO_DEVICE); kfree_skb(skb); burst_counter++; /* Update statistics */ dev->stats.tx_packets++; - dev->stats.tx_bytes += data_len; + dev->stats.tx_bytes += skb->len; /* The payload is expected to be the phy addr. Comparing to see if it's the last skb to replenish */ - if (dma_addr == - result->payload_buf) + if (tx_priv->dma_addr == result->payload_buf) break; } } /* While TX queue is not empty */ @@ -592,25 +643,29 @@ static int rmnet_mhi_xmit(struct sk_buff *skb, struct net_device *dev) enum MHI_STATUS res = MHI_STATUS_reserved; unsigned long flags; int retry = 0; - struct tx_buffer_priv *tx_priv; - dma_addr_t dma_addr; + struct mhi_skb_priv *tx_priv; - rmnet_log(MSG_VERBOSE, "Entered\n"); - dma_addr = dma_map_single(&(dev->dev), skb->data, skb->len, + rmnet_log(MSG_VERBOSE, "Entered chan %d\n", rmnet_mhi_ptr->tx_channel); + + tx_priv = (struct mhi_skb_priv *)(skb->cb); + tx_priv->dma_size = skb->len; + tx_priv->dma_addr = dma_map_single(&dev->dev, skb->data, + tx_priv->dma_size, DMA_TO_DEVICE); - if (dma_mapping_error(&(dev->dev), dma_addr)) { + + if (dma_mapping_error(&dev->dev, tx_priv->dma_addr)) { rmnet_log(MSG_CRITICAL, "DMA mapping error in transmit function\n"); return NETDEV_TX_BUSY; } /* DMA mapping is OK, need to update the cb field properly */ - tx_priv = (struct tx_buffer_priv *)(skb->cb); - tx_priv->dma_addr = dma_addr; do { retry = 0; res = mhi_queue_xfer(rmnet_mhi_ptr->tx_client_handle, - dma_addr, skb->len, MHI_EOT); + tx_priv->dma_addr, + tx_priv->dma_size, + MHI_EOT); if (MHI_STATUS_RING_FULL == res) { write_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock, @@ -646,8 +701,8 @@ static int rmnet_mhi_xmit(struct sk_buff *skb, struct net_device *dev) return 0; rmnet_mhi_xmit_error_cleanup: - dma_unmap_single(&(dev->dev), dma_addr, skb->len, - DMA_TO_DEVICE); + dma_unmap_single(&(dev->dev), tx_priv->dma_addr, tx_priv->dma_size, + DMA_TO_DEVICE); rmnet_log(MSG_VERBOSE, "Ring full\n"); write_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock, flags); return NETDEV_TX_BUSY; @@ -678,7 +733,11 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr) ext_cmd.u.data); return -EINVAL; } - rmnet_mhi_ptr->mru = ext_cmd.u.data; + rmnet_log(MSG_INFO, + "MRU change request to 0x%x\n", + ext_cmd.u.data); + mru = ext_cmd.u.data; + rmnet_mhi_ptr->mru = mru; break; case RMNET_IOCTL_GET_EPID: ext_cmd.u.data =