From 0bcfbe3c613d6ed8044404bc1cc3c29ff961d89c Mon Sep 17 00:00:00 2001 From: Chen Minqiang Date: Thu, 15 Mar 2018 04:59:57 +0800 Subject: [PATCH 1/2] essedma: fixup ethernet driver rx bug - modify the error rx ring full conditions - in rare cases, out of memory allocation failure causes the receive queues stop we use the timer to re-alloc rx rings under these circumstances Signed-off-by: Chen Minqiang --- drivers/net/ethernet/qualcomm/essedma/edma.c | 51 ++++++++++++++++++++++-- drivers/net/ethernet/qualcomm/essedma/edma.h | 3 ++ drivers/net/ethernet/qualcomm/essedma/edma_axi.c | 8 ++++ 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/qualcomm/essedma/edma.c b/drivers/net/ethernet/qualcomm/essedma/edma.c index fecc0ba..3f1da93 100644 --- a/drivers/net/ethernet/qualcomm/essedma/edma.c +++ b/drivers/net/ethernet/qualcomm/essedma/edma.c @@ -530,6 +530,47 @@ static int edma_rx_complete_paged(struct sk_buff *skb, u16 num_rfds, u16 length, return sw_next_to_clean; } +static int edma_rfd_desc_unused(struct edma_rfd_desc_ring *erdr) +{ + if (erdr->sw_next_to_clean > erdr->sw_next_to_fill) + return erdr->sw_next_to_clean - erdr->sw_next_to_fill - 1; + return erdr->count + erdr->sw_next_to_clean - erdr->sw_next_to_fill - 1; +} + +void edma_rx_realloc(unsigned long data) +{ + struct edma_per_cpu_queues_info *edma_percpu_info = (struct edma_per_cpu_queues_info *)data; + struct edma_common_info *edma_cinfo = edma_percpu_info->edma_cinfo; + s32 status = edma_percpu_info->rx_realloc_status; + + while (status) { + int queue_id; + int ret_count; + struct edma_rfd_desc_ring *erdr; + + queue_id = ffs(status) - 1; + erdr = edma_cinfo->rfd_ring[queue_id]; + ret_count = edma_alloc_rx_buf(edma_cinfo, erdr, edma_rfd_desc_unused(erdr), queue_id); + if (ret_count == 0) { + edma_percpu_info->rx_realloc_status &= ~(1 << queue_id); + } + status &= ~(1 << queue_id); + } + + if (edma_percpu_info->rx_realloc_status) { + mod_timer(&edma_percpu_info->rx_realloc_timer, jiffies + HZ); + } +} + +static inline void edma_realloc_timer_start(struct napi_struct *napi, int queue_id) +{ + struct edma_per_cpu_queues_info *edma_percpu_info = container_of(napi, + struct edma_per_cpu_queues_info, napi); + + edma_percpu_info->rx_realloc_status |= (1 << queue_id); + mod_timer(&edma_percpu_info->rx_realloc_timer, jiffies + 5 * HZ); /* restart alloc in 5 secs */ +} + /* * edma_rx_complete() * Main api called from the poll function to process rx packets. @@ -754,10 +795,12 @@ static void edma_rx_complete(struct edma_common_info *edma_cinfo, erdr->sw_next_to_clean = sw_next_to_clean; /* Refill here in case refill threshold wasn't reached */ - if (likely(cleaned_count)) { - ret_count = edma_alloc_rx_buf(edma_cinfo, erdr, cleaned_count, queue_id); - if (ret_count) + if (edma_rfd_desc_unused(erdr)) { + ret_count = edma_alloc_rx_buf(edma_cinfo, erdr, edma_rfd_desc_unused(erdr), queue_id); + if (ret_count) { dev_dbg(&pdev->dev, "Not all buffers was reallocated"); + edma_realloc_timer_start(napi, queue_id); + } edma_write_reg(EDMA_REG_RX_SW_CONS_IDX_Q(queue_id), erdr->sw_next_to_clean); } @@ -1801,7 +1844,7 @@ int edma_configure(struct edma_common_info *edma_cinfo) /* Allocate the RX buffer */ for (i = 0, j = 0; i < edma_cinfo->num_rx_queues; i++) { struct edma_rfd_desc_ring *ring = edma_cinfo->rfd_ring[j]; - ret_count = edma_alloc_rx_buf(edma_cinfo, ring, ring->count, j); + ret_count = edma_alloc_rx_buf(edma_cinfo, ring, edma_rfd_desc_unused(ring), j); if (ret_count) { dev_dbg(&edma_cinfo->pdev->dev, "not all rx buffers allocated\n"); } diff --git a/drivers/net/ethernet/qualcomm/essedma/edma.h b/drivers/net/ethernet/qualcomm/essedma/edma.h index 5d6dc73..29c8379 100644 --- a/drivers/net/ethernet/qualcomm/essedma/edma.h +++ b/drivers/net/ethernet/qualcomm/essedma/edma.h @@ -304,6 +304,8 @@ struct edma_per_cpu_queues_info { u32 tx_start; /* tx queue start */ u32 rx_start; /* rx queue start */ struct edma_common_info *edma_cinfo; /* edma common info */ + u32 rx_realloc_status; + struct timer_list rx_realloc_timer; }; /* edma specific common info */ @@ -448,6 +450,7 @@ void edma_change_tx_coalesce(int usecs); void edma_change_rx_coalesce(int usecs); void edma_get_tx_rx_coalesce(u32 *reg_val); void edma_clear_irq_status(void); +void edma_rx_realloc(unsigned long data); void ess_set_port_status_speed(struct edma_common_info *edma_cinfo, struct phy_device *phydev, uint8_t port_id); #endif /* _EDMA_H_ */ diff --git a/drivers/net/ethernet/qualcomm/essedma/edma_axi.c b/drivers/net/ethernet/qualcomm/essedma/edma_axi.c index 81fc1e1..d9f8b52 100644 --- a/drivers/net/ethernet/qualcomm/essedma/edma_axi.c +++ b/drivers/net/ethernet/qualcomm/essedma/edma_axi.c @@ -1131,6 +1131,11 @@ static int edma_axi_probe(struct platform_device *pdev) edma_cinfo->edma_percpu_info[i].rx_status = 0; edma_cinfo->edma_percpu_info[i].edma_cinfo = edma_cinfo; + edma_cinfo->edma_percpu_info[i].rx_realloc_status = 0; + init_timer(&edma_cinfo->edma_percpu_info[i].rx_realloc_timer); + edma_cinfo->edma_percpu_info[i].rx_realloc_timer.function = edma_rx_realloc; + edma_cinfo->edma_percpu_info[i].rx_realloc_timer.data = (unsigned long)&edma_cinfo->edma_percpu_info[i]; + /* Request irq per core */ for (j = edma_cinfo->edma_percpu_info[i].tx_start; j < tx_start[i] + 4; j++) { @@ -1259,7 +1264,10 @@ err_configure: err_rmap_add_fail: edma_free_irqs(adapter[0]); for (i = 0; i < CONFIG_NR_CPUS; i++) + { napi_disable(&edma_cinfo->edma_percpu_info[i].napi); + del_timer_sync(&edma_cinfo->edma_percpu_info[i].rx_realloc_timer); + } err_reset: err_unregister_sysctl_tbl: err_rmap_alloc_fail: -- 2.7.4