mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:04:41 +01:00
octeon_ep: avoid compiler and IQ/OQ reordering
Utilize READ_ONCE and WRITE_ONCE APIs for IO queue Tx/Rx
variable access to prevent compiler optimization and reordering.
Additionally, ensure IO queue OUT/IN_CNT registers are flushed
by performing a read-back after writing.
The compiler could reorder reads/writes to pkts_pending, last_pkt_count,
etc., causing stale values to be used when calculating packets to process
or register updates to send to hardware. The Octeon hardware requires a
read-back after writing to OUT_CNT/IN_CNT registers to ensure the write
has been flushed through any posted write buffers before the interrupt
resend bit is set. Without this, we have observed cases where the hardware
didn't properly update its internal state.
wmb/rmb only provides ordering guarantees but doesn't prevent the compiler
from performing optimizations like caching in registers, load tearing etc.
Fixes: 37d79d0596 ("octeon_ep: add Tx/Rx processing and interrupt support")
Signed-off-by: Sathesh Edara <sedara@marvell.com>
Signed-off-by: Shinas Rasheed <srasheed@marvell.com>
Signed-off-by: Vimlesh Kumar <vimleshk@marvell.com>
Link: https://patch.msgid.link/20260227091402.1773833-3-vimleshk@marvell.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
parent
18c04a808c
commit
43b3160cb6
2 changed files with 32 additions and 16 deletions
|
|
@ -561,17 +561,22 @@ static void octep_clean_irqs(struct octep_device *oct)
|
|||
*/
|
||||
static void octep_update_pkt(struct octep_iq *iq, struct octep_oq *oq)
|
||||
{
|
||||
u32 pkts_pend = oq->pkts_pending;
|
||||
u32 pkts_pend = READ_ONCE(oq->pkts_pending);
|
||||
u32 last_pkt_count = READ_ONCE(oq->last_pkt_count);
|
||||
u32 pkts_processed = READ_ONCE(iq->pkts_processed);
|
||||
u32 pkt_in_done = READ_ONCE(iq->pkt_in_done);
|
||||
|
||||
netdev_dbg(iq->netdev, "enabling intr for Q-%u\n", iq->q_no);
|
||||
if (iq->pkts_processed) {
|
||||
writel(iq->pkts_processed, iq->inst_cnt_reg);
|
||||
iq->pkt_in_done -= iq->pkts_processed;
|
||||
iq->pkts_processed = 0;
|
||||
if (pkts_processed) {
|
||||
writel(pkts_processed, iq->inst_cnt_reg);
|
||||
readl(iq->inst_cnt_reg);
|
||||
WRITE_ONCE(iq->pkt_in_done, (pkt_in_done - pkts_processed));
|
||||
WRITE_ONCE(iq->pkts_processed, 0);
|
||||
}
|
||||
if (oq->last_pkt_count - pkts_pend) {
|
||||
writel(oq->last_pkt_count - pkts_pend, oq->pkts_sent_reg);
|
||||
oq->last_pkt_count = pkts_pend;
|
||||
if (last_pkt_count - pkts_pend) {
|
||||
writel(last_pkt_count - pkts_pend, oq->pkts_sent_reg);
|
||||
readl(oq->pkts_sent_reg);
|
||||
WRITE_ONCE(oq->last_pkt_count, pkts_pend);
|
||||
}
|
||||
|
||||
/* Flush the previous wrties before writing to RESEND bit */
|
||||
|
|
|
|||
|
|
@ -324,10 +324,16 @@ static int octep_oq_check_hw_for_pkts(struct octep_device *oct,
|
|||
struct octep_oq *oq)
|
||||
{
|
||||
u32 pkt_count, new_pkts;
|
||||
u32 last_pkt_count, pkts_pending;
|
||||
|
||||
pkt_count = readl(oq->pkts_sent_reg);
|
||||
new_pkts = pkt_count - oq->last_pkt_count;
|
||||
last_pkt_count = READ_ONCE(oq->last_pkt_count);
|
||||
new_pkts = pkt_count - last_pkt_count;
|
||||
|
||||
if (pkt_count < last_pkt_count) {
|
||||
dev_err(oq->dev, "OQ-%u pkt_count(%u) < oq->last_pkt_count(%u)\n",
|
||||
oq->q_no, pkt_count, last_pkt_count);
|
||||
}
|
||||
/* Clear the hardware packets counter register if the rx queue is
|
||||
* being processed continuously with-in a single interrupt and
|
||||
* reached half its max value.
|
||||
|
|
@ -338,8 +344,9 @@ static int octep_oq_check_hw_for_pkts(struct octep_device *oct,
|
|||
pkt_count = readl(oq->pkts_sent_reg);
|
||||
new_pkts += pkt_count;
|
||||
}
|
||||
oq->last_pkt_count = pkt_count;
|
||||
oq->pkts_pending += new_pkts;
|
||||
WRITE_ONCE(oq->last_pkt_count, pkt_count);
|
||||
pkts_pending = READ_ONCE(oq->pkts_pending);
|
||||
WRITE_ONCE(oq->pkts_pending, (pkts_pending + new_pkts));
|
||||
return new_pkts;
|
||||
}
|
||||
|
||||
|
|
@ -414,7 +421,7 @@ static int __octep_oq_process_rx(struct octep_device *oct,
|
|||
u16 rx_ol_flags;
|
||||
u32 read_idx;
|
||||
|
||||
read_idx = oq->host_read_idx;
|
||||
read_idx = READ_ONCE(oq->host_read_idx);
|
||||
rx_bytes = 0;
|
||||
desc_used = 0;
|
||||
for (pkt = 0; pkt < pkts_to_process; pkt++) {
|
||||
|
|
@ -499,7 +506,7 @@ static int __octep_oq_process_rx(struct octep_device *oct,
|
|||
napi_gro_receive(oq->napi, skb);
|
||||
}
|
||||
|
||||
oq->host_read_idx = read_idx;
|
||||
WRITE_ONCE(oq->host_read_idx, read_idx);
|
||||
oq->refill_count += desc_used;
|
||||
oq->stats->packets += pkt;
|
||||
oq->stats->bytes += rx_bytes;
|
||||
|
|
@ -522,22 +529,26 @@ int octep_oq_process_rx(struct octep_oq *oq, int budget)
|
|||
{
|
||||
u32 pkts_available, pkts_processed, total_pkts_processed;
|
||||
struct octep_device *oct = oq->octep_dev;
|
||||
u32 pkts_pending;
|
||||
|
||||
pkts_available = 0;
|
||||
pkts_processed = 0;
|
||||
total_pkts_processed = 0;
|
||||
while (total_pkts_processed < budget) {
|
||||
/* update pending count only when current one exhausted */
|
||||
if (oq->pkts_pending == 0)
|
||||
pkts_pending = READ_ONCE(oq->pkts_pending);
|
||||
if (pkts_pending == 0)
|
||||
octep_oq_check_hw_for_pkts(oct, oq);
|
||||
pkts_pending = READ_ONCE(oq->pkts_pending);
|
||||
pkts_available = min(budget - total_pkts_processed,
|
||||
oq->pkts_pending);
|
||||
pkts_pending);
|
||||
if (!pkts_available)
|
||||
break;
|
||||
|
||||
pkts_processed = __octep_oq_process_rx(oct, oq,
|
||||
pkts_available);
|
||||
oq->pkts_pending -= pkts_processed;
|
||||
pkts_pending = READ_ONCE(oq->pkts_pending);
|
||||
WRITE_ONCE(oq->pkts_pending, (pkts_pending - pkts_processed));
|
||||
total_pkts_processed += pkts_processed;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue