Skip to content

Commit

Permalink
net: mscc: ocelot: serialize access to the injection/extraction groups
Browse files Browse the repository at this point in the history
As explained by Horatiu Vultur in commit 603ead9 ("net: sparx5: Add
spinlock for frame transmission from CPU") which is for a similar
hardware design, multiple CPUs can simultaneously perform injection
or extraction. There are only 2 register groups for injection and 2
for extraction, and the driver only uses one of each. So we'd better
serialize access using spin locks, otherwise frame corruption is
possible.

Note that unlike in sparx5, FDMA in ocelot does not have this issue
because struct ocelot_fdma_tx_ring already contains an xmit_lock.

I guess this is mostly a problem for NXP LS1028A, as that is dual core.
I don't think VSC7514 is. So I'm blaming the commit where LS1028A (aka
the felix DSA driver) started using register-based packet injection and
extraction.

Fixes: 0a6f17c ("net: dsa: tag_ocelot_8021q: add support for PTP timestamping")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Vladimir Oltean authored and David S. Miller committed Aug 16, 2024
1 parent e1b9e80 commit c5e12ac
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 0 deletions.
11 changes: 11 additions & 0 deletions drivers/net/dsa/ocelot/felix.c
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,9 @@ static int felix_tag_8021q_setup(struct dsa_switch *ds)
* so we need to be careful that there are no extra frames to be
* dequeued over MMIO, since we would never know to discard them.
*/
ocelot_lock_xtr_grp_bh(ocelot, 0);
ocelot_drain_cpu_queue(ocelot, 0);
ocelot_unlock_xtr_grp_bh(ocelot, 0);

return 0;
}
Expand Down Expand Up @@ -1518,6 +1520,8 @@ static void felix_port_deferred_xmit(struct kthread_work *work)
int port = xmit_work->dp->index;
int retries = 10;

ocelot_lock_inj_grp(ocelot, 0);

do {
if (ocelot_can_inject(ocelot, 0))
break;
Expand All @@ -1526,6 +1530,7 @@ static void felix_port_deferred_xmit(struct kthread_work *work)
} while (--retries);

if (!retries) {
ocelot_unlock_inj_grp(ocelot, 0);
dev_err(ocelot->dev, "port %d failed to inject skb\n",
port);
ocelot_port_purge_txtstamp_skb(ocelot, port, skb);
Expand All @@ -1535,6 +1540,8 @@ static void felix_port_deferred_xmit(struct kthread_work *work)

ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb);

ocelot_unlock_inj_grp(ocelot, 0);

consume_skb(skb);
kfree(xmit_work);
}
Expand Down Expand Up @@ -1694,6 +1701,8 @@ static bool felix_check_xtr_pkt(struct ocelot *ocelot)
if (!felix->info->quirk_no_xtr_irq)
return false;

ocelot_lock_xtr_grp(ocelot, grp);

while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)) {
struct sk_buff *skb;
unsigned int type;
Expand Down Expand Up @@ -1730,6 +1739,8 @@ static bool felix_check_xtr_pkt(struct ocelot *ocelot)
ocelot_drain_cpu_queue(ocelot, 0);
}

ocelot_unlock_xtr_grp(ocelot, grp);

return true;
}

Expand Down
52 changes: 52 additions & 0 deletions drivers/net/ethernet/mscc/ocelot.c
Original file line number Diff line number Diff line change
Expand Up @@ -1099,6 +1099,48 @@ void ocelot_ptp_rx_timestamp(struct ocelot *ocelot, struct sk_buff *skb,
}
EXPORT_SYMBOL(ocelot_ptp_rx_timestamp);

void ocelot_lock_inj_grp(struct ocelot *ocelot, int grp)
__acquires(&ocelot->inj_lock)
{
spin_lock(&ocelot->inj_lock);
}
EXPORT_SYMBOL_GPL(ocelot_lock_inj_grp);

void ocelot_unlock_inj_grp(struct ocelot *ocelot, int grp)
__releases(&ocelot->inj_lock)
{
spin_unlock(&ocelot->inj_lock);
}
EXPORT_SYMBOL_GPL(ocelot_unlock_inj_grp);

void ocelot_lock_xtr_grp(struct ocelot *ocelot, int grp)
__acquires(&ocelot->inj_lock)
{
spin_lock(&ocelot->inj_lock);
}
EXPORT_SYMBOL_GPL(ocelot_lock_xtr_grp);

void ocelot_unlock_xtr_grp(struct ocelot *ocelot, int grp)
__releases(&ocelot->inj_lock)
{
spin_unlock(&ocelot->inj_lock);
}
EXPORT_SYMBOL_GPL(ocelot_unlock_xtr_grp);

void ocelot_lock_xtr_grp_bh(struct ocelot *ocelot, int grp)
__acquires(&ocelot->xtr_lock)
{
spin_lock_bh(&ocelot->xtr_lock);
}
EXPORT_SYMBOL_GPL(ocelot_lock_xtr_grp_bh);

void ocelot_unlock_xtr_grp_bh(struct ocelot *ocelot, int grp)
__releases(&ocelot->xtr_lock)
{
spin_unlock_bh(&ocelot->xtr_lock);
}
EXPORT_SYMBOL_GPL(ocelot_unlock_xtr_grp_bh);

int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
{
u64 timestamp, src_port, len;
Expand All @@ -1109,6 +1151,8 @@ int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
u32 val, *buf;
int err;

lockdep_assert_held(&ocelot->xtr_lock);

err = ocelot_xtr_poll_xfh(ocelot, grp, xfh);
if (err)
return err;
Expand Down Expand Up @@ -1184,6 +1228,8 @@ bool ocelot_can_inject(struct ocelot *ocelot, int grp)
{
u32 val = ocelot_read(ocelot, QS_INJ_STATUS);

lockdep_assert_held(&ocelot->inj_lock);

if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))))
return false;
if (val & QS_INJ_STATUS_WMARK_REACHED(BIT(grp)))
Expand Down Expand Up @@ -1236,6 +1282,8 @@ void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
u32 ifh[OCELOT_TAG_LEN / 4];
unsigned int i, count, last;

lockdep_assert_held(&ocelot->inj_lock);

ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) |
QS_INJ_CTRL_SOF, QS_INJ_CTRL, grp);

Expand Down Expand Up @@ -1272,6 +1320,8 @@ EXPORT_SYMBOL(ocelot_port_inject_frame);

void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp)
{
lockdep_assert_held(&ocelot->xtr_lock);

while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp))
ocelot_read_rix(ocelot, QS_XTR_RD, grp);
}
Expand Down Expand Up @@ -2954,6 +3004,8 @@ int ocelot_init(struct ocelot *ocelot)
mutex_init(&ocelot->fwd_domain_lock);
spin_lock_init(&ocelot->ptp_clock_lock);
spin_lock_init(&ocelot->ts_id_lock);
spin_lock_init(&ocelot->inj_lock);
spin_lock_init(&ocelot->xtr_lock);

ocelot->owq = alloc_ordered_workqueue("ocelot-owq", 0);
if (!ocelot->owq)
Expand Down
4 changes: 4 additions & 0 deletions drivers/net/ethernet/mscc/ocelot_vsc7514.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
struct ocelot *ocelot = arg;
int grp = 0, err;

ocelot_lock_xtr_grp(ocelot, grp);

while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)) {
struct sk_buff *skb;

Expand All @@ -69,6 +71,8 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
if (err < 0)
ocelot_drain_cpu_queue(ocelot, 0);

ocelot_unlock_xtr_grp(ocelot, grp);

return IRQ_HANDLED;
}

Expand Down
9 changes: 9 additions & 0 deletions include/soc/mscc/ocelot.h
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,9 @@ struct ocelot {
const u32 *const *map;
struct list_head stats_regions;

spinlock_t inj_lock;
spinlock_t xtr_lock;

u32 pool_size[OCELOT_SB_NUM][OCELOT_SB_POOL_NUM];
int packet_buffer_size;
int num_frame_refs;
Expand Down Expand Up @@ -966,6 +969,12 @@ void __ocelot_target_write_ix(struct ocelot *ocelot, enum ocelot_target target,
u32 val, u32 reg, u32 offset);

/* Packet I/O */
void ocelot_lock_inj_grp(struct ocelot *ocelot, int grp);
void ocelot_unlock_inj_grp(struct ocelot *ocelot, int grp);
void ocelot_lock_xtr_grp(struct ocelot *ocelot, int grp);
void ocelot_unlock_xtr_grp(struct ocelot *ocelot, int grp);
void ocelot_lock_xtr_grp_bh(struct ocelot *ocelot, int grp);
void ocelot_unlock_xtr_grp_bh(struct ocelot *ocelot, int grp);
bool ocelot_can_inject(struct ocelot *ocelot, int grp);
void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
u32 rew_op, struct sk_buff *skb);
Expand Down

0 comments on commit c5e12ac

Please sign in to comment.