From c80c4a1ea47f354584c8055015561c4f1ece8f7a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 21 Jan 2021 05:48:17 -0600 Subject: net: ipa: count actual work done in gsi_channel_poll() There is an off-by-one problem in gsi_channel_poll(). The count of transactions completed is incremented each time through the loop *before* determining whether there is any more work to do. As a result, if we exit the loop early the counter its value is one more than the number of transactions actually processed. Instead, increment the count after processing, to ensure it reflects the number of processed transactions. The result is more naturally described as a for loop rather than a while loop, so change that. Signed-off-by: Alex Elder Signed-off-by: Jakub Kicinski --- drivers/net/ipa/gsi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/ipa/gsi.c b/drivers/net/ipa/gsi.c index 5b29f7d9d6ac..56a5eb61b20c 100644 --- a/drivers/net/ipa/gsi.c +++ b/drivers/net/ipa/gsi.c @@ -1543,13 +1543,12 @@ static struct gsi_trans *gsi_channel_poll_one(struct gsi_channel *channel) static int gsi_channel_poll(struct napi_struct *napi, int budget) { struct gsi_channel *channel; - int count = 0; + int count; channel = container_of(napi, struct gsi_channel, napi); - while (count < budget) { + for (count = 0; count < budget; count++) { struct gsi_trans *trans; - count++; trans = gsi_channel_poll_one(channel); if (!trans) break; -- cgit v1.2.3 From 148604e7eafb2f6af275d60b9ab27e7a9622e93f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 21 Jan 2021 05:48:18 -0600 Subject: net: ipa: heed napi_complete() return value Pay attention to the return value of napi_complete(), completing polling only if it returns true. Just use napi rather than &channel->napi as the argument passed to napi_complete(). Signed-off-by: Alex Elder Signed-off-by: Jakub Kicinski --- drivers/net/ipa/gsi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/ipa/gsi.c b/drivers/net/ipa/gsi.c index 56a5eb61b20c..634f514e861e 100644 --- a/drivers/net/ipa/gsi.c +++ b/drivers/net/ipa/gsi.c @@ -1555,10 +1555,8 @@ static int gsi_channel_poll(struct napi_struct *napi, int budget) gsi_trans_complete(trans); } - if (count < budget) { - napi_complete(&channel->napi); + if (count < budget && napi_complete(napi)) gsi_irq_ieob_enable(channel->gsi, channel->evt_ring_id); - } return count; } -- cgit v1.2.3 From 223f5b34b409828b2f9a15d5e4ec0da0563d17ec Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 21 Jan 2021 05:48:19 -0600 Subject: net: ipa: have gsi_channel_update() return a value Have gsi_channel_update() return the first transaction in the updated completed transaction list, or NULL if no new transactions have been added. Signed-off-by: Alex Elder Signed-off-by: Jakub Kicinski --- drivers/net/ipa/gsi.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/net/ipa/gsi.c b/drivers/net/ipa/gsi.c index 634f514e861e..6e5817e16c0f 100644 --- a/drivers/net/ipa/gsi.c +++ b/drivers/net/ipa/gsi.c @@ -1452,7 +1452,7 @@ void gsi_channel_doorbell(struct gsi_channel *channel) } /* Consult hardware, move any newly completed transactions to completed list */ -static void gsi_channel_update(struct gsi_channel *channel) +static struct gsi_trans *gsi_channel_update(struct gsi_channel *channel) { u32 evt_ring_id = channel->evt_ring_id; struct gsi *gsi = channel->gsi; @@ -1471,7 +1471,7 @@ static void gsi_channel_update(struct gsi_channel *channel) offset = GSI_EV_CH_E_CNTXT_4_OFFSET(evt_ring_id); index = gsi_ring_index(ring, ioread32(gsi->virt + offset)); if (index == ring->index % ring->count) - return; + return NULL; /* Get the transaction for the latest completed event. Take a * reference to keep it from completing before we give the events @@ -1496,6 +1496,8 @@ static void gsi_channel_update(struct gsi_channel *channel) gsi_evt_ring_doorbell(channel->gsi, channel->evt_ring_id, index); gsi_trans_free(trans); + + return gsi_channel_trans_complete(channel); } /** @@ -1516,11 +1518,8 @@ static struct gsi_trans *gsi_channel_poll_one(struct gsi_channel *channel) /* Get the first transaction from the completed list */ trans = gsi_channel_trans_complete(channel); - if (!trans) { - /* List is empty; see if there's more to do */ - gsi_channel_update(channel); - trans = gsi_channel_trans_complete(channel); - } + if (!trans) /* List is empty; see if there's more to do */ + trans = gsi_channel_update(channel); if (trans) gsi_trans_move_polled(trans); -- cgit v1.2.3 From 5725593e6f182607993364f56ab1c0468d68016f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 21 Jan 2021 05:48:20 -0600 Subject: net: ipa: repurpose gsi_irq_ieob_disable() Rename gsi_irq_ieob_disable() to be gsi_irq_ieob_disable_one(). Introduce a new function gsi_irq_ieob_disable() that takes a mask of events to disable rather than a single event id. This will be used in the next patch. Rename gsi_irq_ieob_enable() to be gsi_irq_ieob_enable_one() to be consistent. Signed-off-by: Alex Elder Signed-off-by: Jakub Kicinski --- drivers/net/ipa/gsi.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/net/ipa/gsi.c b/drivers/net/ipa/gsi.c index 6e5817e16c0f..0391f5a207c9 100644 --- a/drivers/net/ipa/gsi.c +++ b/drivers/net/ipa/gsi.c @@ -272,7 +272,7 @@ static void gsi_irq_ch_ctrl_disable(struct gsi *gsi) iowrite32(0, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET); } -static void gsi_irq_ieob_enable(struct gsi *gsi, u32 evt_ring_id) +static void gsi_irq_ieob_enable_one(struct gsi *gsi, u32 evt_ring_id) { bool enable_ieob = !gsi->ieob_enabled_bitmap; u32 val; @@ -286,11 +286,11 @@ static void gsi_irq_ieob_enable(struct gsi *gsi, u32 evt_ring_id) gsi_irq_type_enable(gsi, GSI_IEOB); } -static void gsi_irq_ieob_disable(struct gsi *gsi, u32 evt_ring_id) +static void gsi_irq_ieob_disable(struct gsi *gsi, u32 event_mask) { u32 val; - gsi->ieob_enabled_bitmap &= ~BIT(evt_ring_id); + gsi->ieob_enabled_bitmap &= ~event_mask; /* Disable the interrupt type if this was the last enabled channel */ if (!gsi->ieob_enabled_bitmap) @@ -300,6 +300,11 @@ static void gsi_irq_ieob_disable(struct gsi *gsi, u32 evt_ring_id) iowrite32(val, gsi->virt + GSI_CNTXT_SRC_IEOB_IRQ_MSK_OFFSET); } +static void gsi_irq_ieob_disable_one(struct gsi *gsi, u32 evt_ring_id) +{ + gsi_irq_ieob_disable(gsi, BIT(evt_ring_id)); +} + /* Enable all GSI_interrupt types */ static void gsi_irq_enable(struct gsi *gsi) { @@ -766,13 +771,13 @@ static void gsi_channel_freeze(struct gsi_channel *channel) napi_disable(&channel->napi); - gsi_irq_ieob_disable(channel->gsi, channel->evt_ring_id); + gsi_irq_ieob_disable_one(channel->gsi, channel->evt_ring_id); } /* Allow transactions to be used on the channel again. */ static void gsi_channel_thaw(struct gsi_channel *channel) { - gsi_irq_ieob_enable(channel->gsi, channel->evt_ring_id); + gsi_irq_ieob_enable_one(channel->gsi, channel->evt_ring_id); napi_enable(&channel->napi); } @@ -1207,7 +1212,7 @@ static void gsi_isr_ieob(struct gsi *gsi) event_mask ^= BIT(evt_ring_id); - gsi_irq_ieob_disable(gsi, evt_ring_id); + gsi_irq_ieob_disable_one(gsi, evt_ring_id); napi_schedule(&gsi->evt_ring[evt_ring_id].channel->napi); } } @@ -1555,7 +1560,7 @@ static int gsi_channel_poll(struct napi_struct *napi, int budget) } if (count < budget && napi_complete(napi)) - gsi_irq_ieob_enable(channel->gsi, channel->evt_ring_id); + gsi_irq_ieob_enable_one(channel->gsi, channel->evt_ring_id); return count; } -- cgit v1.2.3 From 7bd9785f683a7dafd8ea59a863a614da685d92f7 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 21 Jan 2021 05:48:21 -0600 Subject: net: ipa: disable IEOB interrupts before clearing Currently in gsi_isr_ieob(), event ring IEOB interrupts are disabled one at a time. The loop disables the IEOB interrupt for all event rings represented in the event mask. Instead, just disable them all at once. Disable them all *before* clearing the interrupt condition. This guarantees we'll schedule NAPI for each event once, before another IEOB interrupt could be signaled. Signed-off-by: Alex Elder Signed-off-by: Jakub Kicinski --- drivers/net/ipa/gsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ipa/gsi.c b/drivers/net/ipa/gsi.c index 0391f5a207c9..f79cf3c327c1 100644 --- a/drivers/net/ipa/gsi.c +++ b/drivers/net/ipa/gsi.c @@ -1205,6 +1205,7 @@ static void gsi_isr_ieob(struct gsi *gsi) u32 event_mask; event_mask = ioread32(gsi->virt + GSI_CNTXT_SRC_IEOB_IRQ_OFFSET); + gsi_irq_ieob_disable(gsi, event_mask); iowrite32(event_mask, gsi->virt + GSI_CNTXT_SRC_IEOB_IRQ_CLR_OFFSET); while (event_mask) { @@ -1212,7 +1213,6 @@ static void gsi_isr_ieob(struct gsi *gsi) event_mask ^= BIT(evt_ring_id); - gsi_irq_ieob_disable_one(gsi, evt_ring_id); napi_schedule(&gsi->evt_ring[evt_ring_id].channel->napi); } } -- cgit v1.2.3