summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIoana Radulescu <ruxandra.radulescu@nxp.com>2017-09-15 14:22:49 (GMT)
committerXie Xiaobo <xiaobo.xie@nxp.com>2017-12-12 07:32:41 (GMT)
commitd5671827e9a1660418493a1767fbe6f63bb69e5c (patch)
tree93948c451553e6715a1ed3a3f67b3e08bfd5b6a6
parente11bc97f0e926a688a7d18100bbac3955d3b8f92 (diff)
downloadlinux-d5671827e9a1660418493a1767fbe6f63bb69e5c.tar.xz
staging: fsl-dpaa2/eth: Fix potential endless loop
We incorrectly assumed that dpaa2_io_release() can only return -EBUSY as an error code, when in fact it can also fail in case some of its arguments don't have valid values. Make sure we only retry the operation while the portal is busy and abort for all other error cases, otherwise we risk entering an endless loop. Signed-off-by: Ioana Radulescu <ruxandra.radulescu@nxp.com> Integrated-by: Guanhua Gao <guanhua.gao@nxp.com>
-rw-r--r--drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c55
1 files changed, 32 insertions, 23 deletions
diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c
index 3c6f69b..3fbb614 100644
--- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c
+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c
@@ -216,6 +216,21 @@ static struct sk_buff *build_frag_skb(struct dpaa2_eth_priv *priv,
return skb;
}
+static void free_bufs(struct dpaa2_eth_priv *priv, u64 *buf_array, int count)
+{
+ struct device *dev = priv->net_dev->dev.parent;
+ void *vaddr;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ /* Same logic as on regular Rx path */
+ vaddr = dpaa2_eth_iova_to_virt(priv->iommu_domain, buf_array[i]);
+ dma_unmap_single(dev, buf_array[i], DPAA2_ETH_RX_BUF_SIZE,
+ DMA_FROM_DEVICE);
+ put_page(virt_to_head_page(vaddr));
+ }
+}
+
/* Main Rx frame processing routine */
static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv,
struct dpaa2_eth_channel *ch,
@@ -883,7 +898,7 @@ static int add_bufs(struct dpaa2_eth_priv *priv, u16 bpid)
u64 buf_array[DPAA2_ETH_BUFS_PER_CMD];
void *buf;
dma_addr_t addr;
- int i;
+ int i, err;
for (i = 0; i < DPAA2_ETH_BUFS_PER_CMD; i++) {
/* Allocate buffer visible to WRIOP + skb shared info +
@@ -910,22 +925,26 @@ static int add_bufs(struct dpaa2_eth_priv *priv, u16 bpid)
}
release_bufs:
- /* In case the portal is busy, retry until successful.
- * The buffer release function would only fail if the QBMan portal
- * was busy, which implies portal contention (i.e. more CPUs than
- * portals, i.e. GPPs w/o affine DPIOs). For all practical purposes,
- * there is little we can realistically do, short of giving up -
- * in which case we'd risk depleting the buffer pool and never again
- * receiving the Rx interrupt which would kick-start the refill logic.
- * So just keep retrying, at the risk of being moved to ksoftirqd.
- */
- while (dpaa2_io_service_release(NULL, bpid, buf_array, i))
+ /* In case the portal is busy, retry until successful */
+ do {
+ err = dpaa2_io_service_release(NULL, bpid, buf_array, i);
cpu_relax();
+ } while (err == -EBUSY);
+
+ /* If release command failed, clean up and bail out; not much
+ * else we can do about it
+ */
+ if (unlikely(err)) {
+ free_bufs(priv, buf_array, i);
+ return 0;
+ }
+
return i;
err_map:
put_page(virt_to_head_page(buf));
err_alloc:
+ /* If we managed to allocate at least some buffers, release them */
if (i)
goto release_bufs;
@@ -968,10 +987,8 @@ static int seed_pool(struct dpaa2_eth_priv *priv, u16 bpid)
*/
static void drain_bufs(struct dpaa2_eth_priv *priv, int count)
{
- struct device *dev = priv->net_dev->dev.parent;
u64 buf_array[DPAA2_ETH_BUFS_PER_CMD];
- void *vaddr;
- int ret, i;
+ int ret;
do {
ret = dpaa2_io_service_acquire(NULL, priv->bpid,
@@ -980,15 +997,7 @@ static void drain_bufs(struct dpaa2_eth_priv *priv, int count)
netdev_err(priv->net_dev, "dpaa2_io_service_acquire() failed\n");
return;
}
- for (i = 0; i < ret; i++) {
- /* Same logic as on regular Rx path */
- vaddr = dpaa2_eth_iova_to_virt(priv->iommu_domain,
- buf_array[i]);
- dma_unmap_single(dev, buf_array[i],
- DPAA2_ETH_RX_BUF_SIZE,
- DMA_FROM_DEVICE);
- put_page(virt_to_head_page(vaddr));
- }
+ free_bufs(priv, buf_array, ret);
} while (ret);
}