Skip to content

Commit

Permalink
net: mscc: ocelot: serialize access to the MAC table
Browse files Browse the repository at this point in the history
DSA would like to remove the rtnl_lock from its
SWITCHDEV_FDB_{ADD,DEL}_TO_DEVICE handlers, and the felix driver uses
the same MAC table functions as ocelot.

This means that the MAC table functions will no longer be implicitly
serialized with respect to each other by the rtnl_mutex, we need to add
a dedicated lock in ocelot for the non-atomic operations of selecting a
MAC table row, reading/writing what we want and polling for completion.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Vladimir Oltean authored and David S. Miller committed Oct 24, 2021
1 parent 1681ae1 commit f2c4bdf
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 12 deletions.
53 changes: 41 additions & 12 deletions drivers/net/ethernet/mscc/ocelot.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ struct ocelot_mact_entry {
};

static inline u32 ocelot_mact_read_macaccess(struct ocelot *ocelot)
__must_hold(&ocelot->mact_lock)
{
return ocelot_read(ocelot, ANA_TABLES_MACACCESS);
}

static inline int ocelot_mact_wait_for_completion(struct ocelot *ocelot)
__must_hold(&ocelot->mact_lock)
{
u32 val;

Expand All @@ -39,6 +41,7 @@ static inline int ocelot_mact_wait_for_completion(struct ocelot *ocelot)
static void ocelot_mact_select(struct ocelot *ocelot,
const unsigned char mac[ETH_ALEN],
unsigned int vid)
__must_hold(&ocelot->mact_lock)
{
u32 macl = 0, mach = 0;

Expand Down Expand Up @@ -67,6 +70,7 @@ int ocelot_mact_learn(struct ocelot *ocelot, int port,
ANA_TABLES_MACACCESS_ENTRYTYPE(type) |
ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_LEARN);
unsigned int mc_ports;
int err;

/* Set MAC_CPU_COPY if the CPU port is used by a multicast entry */
if (type == ENTRYTYPE_MACv4)
Expand All @@ -79,26 +83,40 @@ int ocelot_mact_learn(struct ocelot *ocelot, int port,
if (mc_ports & BIT(ocelot->num_phys_ports))
cmd |= ANA_TABLES_MACACCESS_MAC_CPU_COPY;

mutex_lock(&ocelot->mact_lock);

ocelot_mact_select(ocelot, mac, vid);

/* Issue a write command */
ocelot_write(ocelot, cmd, ANA_TABLES_MACACCESS);

return ocelot_mact_wait_for_completion(ocelot);
err = ocelot_mact_wait_for_completion(ocelot);

mutex_unlock(&ocelot->mact_lock);

return err;
}
EXPORT_SYMBOL(ocelot_mact_learn);

int ocelot_mact_forget(struct ocelot *ocelot,
const unsigned char mac[ETH_ALEN], unsigned int vid)
{
int err;

mutex_lock(&ocelot->mact_lock);

ocelot_mact_select(ocelot, mac, vid);

/* Issue a forget command */
ocelot_write(ocelot,
ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_FORGET),
ANA_TABLES_MACACCESS);

return ocelot_mact_wait_for_completion(ocelot);
err = ocelot_mact_wait_for_completion(ocelot);

mutex_unlock(&ocelot->mact_lock);

return err;
}
EXPORT_SYMBOL(ocelot_mact_forget);

Expand All @@ -114,7 +132,9 @@ static void ocelot_mact_init(struct ocelot *ocelot)
| ANA_AGENCTRL_LEARN_IGNORE_VLAN,
ANA_AGENCTRL);

/* Clear the MAC table */
/* Clear the MAC table. We are not concurrent with anyone, so
* holding &ocelot->mact_lock is pointless.
*/
ocelot_write(ocelot, MACACCESS_CMD_INIT, ANA_TABLES_MACACCESS);
}

Expand Down Expand Up @@ -1172,6 +1192,7 @@ EXPORT_SYMBOL(ocelot_port_fdb_do_dump);

static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col,
struct ocelot_mact_entry *entry)
__must_hold(&ocelot->mact_lock)
{
u32 val, dst, macl, mach;
char mac[ETH_ALEN];
Expand Down Expand Up @@ -1220,33 +1241,40 @@ static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col,
int ocelot_fdb_dump(struct ocelot *ocelot, int port,
dsa_fdb_dump_cb_t *cb, void *data)
{
int err = 0;
int i, j;

/* We could take the lock just around ocelot_mact_read, but doing so
* thousands of times in a row seems rather pointless and inefficient.
*/
mutex_lock(&ocelot->mact_lock);

/* Loop through all the mac tables entries. */
for (i = 0; i < ocelot->num_mact_rows; i++) {
for (j = 0; j < 4; j++) {
struct ocelot_mact_entry entry;
bool is_static;
int ret;

ret = ocelot_mact_read(ocelot, port, i, j, &entry);
err = ocelot_mact_read(ocelot, port, i, j, &entry);
/* If the entry is invalid (wrong port, invalid...),
* skip it.
*/
if (ret == -EINVAL)
if (err == -EINVAL)
continue;
else if (ret)
return ret;
else if (err)
break;

is_static = (entry.type == ENTRYTYPE_LOCKED);

ret = cb(entry.mac, entry.vid, is_static, data);
if (ret)
return ret;
err = cb(entry.mac, entry.vid, is_static, data);
if (err)
break;
}
}

return 0;
mutex_unlock(&ocelot->mact_lock);

return err;
}
EXPORT_SYMBOL(ocelot_fdb_dump);

Expand Down Expand Up @@ -2231,6 +2259,7 @@ int ocelot_init(struct ocelot *ocelot)

mutex_init(&ocelot->stats_lock);
mutex_init(&ocelot->ptp_lock);
mutex_init(&ocelot->mact_lock);
spin_lock_init(&ocelot->ptp_clock_lock);
spin_lock_init(&ocelot->ts_id_lock);
snprintf(queue_name, sizeof(queue_name), "%s-stats",
Expand Down
3 changes: 3 additions & 0 deletions include/soc/mscc/ocelot.h
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,9 @@ struct ocelot {
struct delayed_work stats_work;
struct workqueue_struct *stats_queue;

/* Lock for serializing access to the MAC table */
struct mutex mact_lock;

struct workqueue_struct *owq;

u8 ptp:1;
Expand Down

0 comments on commit f2c4bdf

Please sign in to comment.