Commit 2468346c authored by Vladimir Oltean's avatar Vladimir Oltean Committed by David S. Miller

net: mscc: ocelot: serialize access to the MAC table

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: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent eb016afd
...@@ -20,11 +20,13 @@ struct ocelot_mact_entry { ...@@ -20,11 +20,13 @@ struct ocelot_mact_entry {
enum macaccess_entry_type type; enum macaccess_entry_type type;
}; };
/* Caller must hold &ocelot->mact_lock */
static inline u32 ocelot_mact_read_macaccess(struct ocelot *ocelot) static inline u32 ocelot_mact_read_macaccess(struct ocelot *ocelot)
{ {
return ocelot_read(ocelot, ANA_TABLES_MACACCESS); return ocelot_read(ocelot, ANA_TABLES_MACACCESS);
} }
/* Caller must hold &ocelot->mact_lock */
static inline int ocelot_mact_wait_for_completion(struct ocelot *ocelot) static inline int ocelot_mact_wait_for_completion(struct ocelot *ocelot)
{ {
u32 val; u32 val;
...@@ -36,6 +38,7 @@ static inline int ocelot_mact_wait_for_completion(struct ocelot *ocelot) ...@@ -36,6 +38,7 @@ static inline int ocelot_mact_wait_for_completion(struct ocelot *ocelot)
TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US); TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
} }
/* Caller must hold &ocelot->mact_lock */
static void ocelot_mact_select(struct ocelot *ocelot, static void ocelot_mact_select(struct ocelot *ocelot,
const unsigned char mac[ETH_ALEN], const unsigned char mac[ETH_ALEN],
unsigned int vid) unsigned int vid)
...@@ -67,6 +70,7 @@ int ocelot_mact_learn(struct ocelot *ocelot, int port, ...@@ -67,6 +70,7 @@ int ocelot_mact_learn(struct ocelot *ocelot, int port,
ANA_TABLES_MACACCESS_ENTRYTYPE(type) | ANA_TABLES_MACACCESS_ENTRYTYPE(type) |
ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_LEARN); ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_LEARN);
unsigned int mc_ports; unsigned int mc_ports;
int err;
/* Set MAC_CPU_COPY if the CPU port is used by a multicast entry */ /* Set MAC_CPU_COPY if the CPU port is used by a multicast entry */
if (type == ENTRYTYPE_MACv4) if (type == ENTRYTYPE_MACv4)
...@@ -79,18 +83,28 @@ int ocelot_mact_learn(struct ocelot *ocelot, int port, ...@@ -79,18 +83,28 @@ int ocelot_mact_learn(struct ocelot *ocelot, int port,
if (mc_ports & BIT(ocelot->num_phys_ports)) if (mc_ports & BIT(ocelot->num_phys_ports))
cmd |= ANA_TABLES_MACACCESS_MAC_CPU_COPY; cmd |= ANA_TABLES_MACACCESS_MAC_CPU_COPY;
mutex_lock(&ocelot->mact_lock);
ocelot_mact_select(ocelot, mac, vid); ocelot_mact_select(ocelot, mac, vid);
/* Issue a write command */ /* Issue a write command */
ocelot_write(ocelot, cmd, ANA_TABLES_MACACCESS); 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); EXPORT_SYMBOL(ocelot_mact_learn);
int ocelot_mact_forget(struct ocelot *ocelot, int ocelot_mact_forget(struct ocelot *ocelot,
const unsigned char mac[ETH_ALEN], unsigned int vid) const unsigned char mac[ETH_ALEN], unsigned int vid)
{ {
int err;
mutex_lock(&ocelot->mact_lock);
ocelot_mact_select(ocelot, mac, vid); ocelot_mact_select(ocelot, mac, vid);
/* Issue a forget command */ /* Issue a forget command */
...@@ -98,7 +112,11 @@ int ocelot_mact_forget(struct ocelot *ocelot, ...@@ -98,7 +112,11 @@ int ocelot_mact_forget(struct ocelot *ocelot,
ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_FORGET), ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_FORGET),
ANA_TABLES_MACACCESS); 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); EXPORT_SYMBOL(ocelot_mact_forget);
...@@ -114,7 +132,9 @@ static void ocelot_mact_init(struct ocelot *ocelot) ...@@ -114,7 +132,9 @@ static void ocelot_mact_init(struct ocelot *ocelot)
| ANA_AGENCTRL_LEARN_IGNORE_VLAN, | ANA_AGENCTRL_LEARN_IGNORE_VLAN,
ANA_AGENCTRL); 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); ocelot_write(ocelot, MACACCESS_CMD_INIT, ANA_TABLES_MACACCESS);
} }
...@@ -1170,6 +1190,7 @@ int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid, ...@@ -1170,6 +1190,7 @@ int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
} }
EXPORT_SYMBOL(ocelot_port_fdb_do_dump); EXPORT_SYMBOL(ocelot_port_fdb_do_dump);
/* Caller must hold &ocelot->mact_lock */
static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col, static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col,
struct ocelot_mact_entry *entry) struct ocelot_mact_entry *entry)
{ {
...@@ -1220,33 +1241,40 @@ static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col, ...@@ -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, int ocelot_fdb_dump(struct ocelot *ocelot, int port,
dsa_fdb_dump_cb_t *cb, void *data) dsa_fdb_dump_cb_t *cb, void *data)
{ {
int err = 0;
int i, j; 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. */ /* Loop through all the mac tables entries. */
for (i = 0; i < ocelot->num_mact_rows; i++) { for (i = 0; i < ocelot->num_mact_rows; i++) {
for (j = 0; j < 4; j++) { for (j = 0; j < 4; j++) {
struct ocelot_mact_entry entry; struct ocelot_mact_entry entry;
bool is_static; 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...), /* If the entry is invalid (wrong port, invalid...),
* skip it. * skip it.
*/ */
if (ret == -EINVAL) if (err == -EINVAL)
continue; continue;
else if (ret) else if (err)
return ret; break;
is_static = (entry.type == ENTRYTYPE_LOCKED); is_static = (entry.type == ENTRYTYPE_LOCKED);
ret = cb(entry.mac, entry.vid, is_static, data); err = cb(entry.mac, entry.vid, is_static, data);
if (ret) if (err)
return ret; break;
} }
} }
return 0; mutex_unlock(&ocelot->mact_lock);
return err;
} }
EXPORT_SYMBOL(ocelot_fdb_dump); EXPORT_SYMBOL(ocelot_fdb_dump);
...@@ -2231,6 +2259,7 @@ int ocelot_init(struct ocelot *ocelot) ...@@ -2231,6 +2259,7 @@ int ocelot_init(struct ocelot *ocelot)
mutex_init(&ocelot->stats_lock); mutex_init(&ocelot->stats_lock);
mutex_init(&ocelot->ptp_lock); mutex_init(&ocelot->ptp_lock);
mutex_init(&ocelot->mact_lock);
spin_lock_init(&ocelot->ptp_clock_lock); spin_lock_init(&ocelot->ptp_clock_lock);
spin_lock_init(&ocelot->ts_id_lock); spin_lock_init(&ocelot->ts_id_lock);
snprintf(queue_name, sizeof(queue_name), "%s-stats", snprintf(queue_name, sizeof(queue_name), "%s-stats",
......
...@@ -675,6 +675,9 @@ struct ocelot { ...@@ -675,6 +675,9 @@ struct ocelot {
struct delayed_work stats_work; struct delayed_work stats_work;
struct workqueue_struct *stats_queue; struct workqueue_struct *stats_queue;
/* Lock for serializing access to the MAC table */
struct mutex mact_lock;
struct workqueue_struct *owq; struct workqueue_struct *owq;
u8 ptp:1; u8 ptp:1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment