[PATCH net-next 10/11] net: dsa: tag_sja1105: split sja1105_tagger_data into private and public sections

Vladimir Oltean
2021-12-08T20:06:14Z

The sja1105 driver messes with the tagging protocol's state when PTP RX
timestamping is enabled/disabled. This is fundamentally necessary
because the tagger needs to know what to do when it receives a PTP
packet. If RX timestamping is enabled, then a metadata follow-up frame
is expected, and this holds the (partial) timestamp. So the tagger plays
hide-and-seek with the network stack until it also gets the metadata
frame, and then presents a single packet, the timestamped PTP packet.
But when RX timestamping isn't enabled, there is no metadata frame
expected, so the hide-and-seek game must be turned off and the packet
must be delivered right away to the network stack.

Considering this, we create a pseudo isolation by devising two tagger
methods callable by the switch: one to get the RX timestamping state,
and one to set it. Since we can't export symbols between the tagger and
the switch driver, these methods are exposed through function pointers.

After this change, the public portion of the sja1105_tagger_data
contains only function pointers.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
drivers/net/dsa/sja1105/sja1105_ptp.c | 22 ++----
include/linux/dsa/sja1105.h | 13 +--
net/dsa/tag_sja1105.c | 109 +++++++++++++++++++-------
3 files changed, 91 insertions(+), 53 deletions(-)

diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c
index a9f7e4ae0bb2..be3068a935af 100644
--- a/drivers/net/dsa/sja1105/sja1105_ptp.c
+++ b/drivers/net/dsa/sja1105/sja1105_ptp.c
@@ -58,11 +58,10 @@ enum sja1105_ptp_clk_mode {
#define ptp_data_to_sja1105(d) \
container_of((d), struct sja1105_private, ptp_data)

-/* Must be called only with the tagger_data->state bit
- * SJA1105_HWTS_RX_EN cleared
+/* Must be called only while the RX timestamping state of the tagger
+ * is turned off
*/
static int sja1105_change_rxtstamping(struct sja1105_private *priv,
- struct sja1105_tagger_data *tagger_data,
bool on)
{
struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
@@ -74,11 +73,6 @@ static int sja1105_change_rxtstamping(struct sja1105_private *priv,
general_params->send_meta1 = on;
general_params->send_meta0 = on;

- /* Initialize the meta state machine to a known state */
- if (tagger_data->stampable_skb) {
- kfree_skb(tagger_data->stampable_skb);
- tagger_data->stampable_skb = NULL;
- }
ptp_cancel_worker_sync(ptp_data->clock);
skb_queue_purge(&ptp_data->skb_txtstamp_queue);
skb_queue_purge(&ptp_data->skb_rxtstamp_queue);
@@ -117,17 +111,17 @@ int sja1105_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr)
break;
}

- if (rx_on != test_bit(SJA1105_HWTS_RX_EN, &tagger_data->state)) {
- clear_bit(SJA1105_HWTS_RX_EN, &tagger_data->state);
+ if (rx_on != tagger_data->rxtstamp_get_state(ds)) {
+ tagger_data->rxtstamp_set_state(ds, false);

- rc = sja1105_change_rxtstamping(priv, tagger_data, rx_on);
+ rc = sja1105_change_rxtstamping(priv, rx_on);
if (rc < 0) {
dev_err(ds->dev,
"Failed to change RX timestamping: %d\n", rc);
return rc;
}
if (rx_on)
- set_bit(SJA1105_HWTS_RX_EN, &tagger_data->state);
+ tagger_data->rxtstamp_set_state(ds, true);
}

if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
@@ -146,7 +140,7 @@ int sja1105_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr)
config.tx_type = HWTSTAMP_TX_ON;
else
config.tx_type = HWTSTAMP_TX_OFF;
- if (test_bit(SJA1105_HWTS_RX_EN, &tagger_data->state))
+ if (tagger_data->rxtstamp_get_state(ds))
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
else
config.rx_filter = HWTSTAMP_FILTER_NONE;
@@ -423,7 +417,7 @@ bool sja1105_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)
struct sja1105_private *priv = ds->priv;
struct sja1105_ptp_data *ptp_data = &priv->ptp_data;

- if (!test_bit(SJA1105_HWTS_RX_EN, &tagger_data->state))
+ if (!tagger_data->rxtstamp_get_state(ds))
return false;

/* We need to read the full PTP clock to reconstruct the Rx
diff --git a/include/linux/dsa/sja1105.h b/include/linux/dsa/sja1105.h
index d216211b64f8..e9cb1ae6d742 100644
--- a/include/linux/dsa/sja1105.h
+++ b/include/linux/dsa/sja1105.h
@@ -35,8 +35,6 @@
#define SJA1105_META_SMAC 0x222222222222ull
#define SJA1105_META_DMAC 0x0180C200000Eull

-#define SJA1105_HWTS_RX_EN 0
-
enum sja1110_meta_tstamp {
SJA1110_META_TSTAMP_TX = 0,
SJA1110_META_TSTAMP_RX = 1,
@@ -50,16 +48,13 @@ struct sja1105_deferred_xmit_work {

/* Global tagger data */
struct sja1105_tagger_data {
- struct sk_buff *stampable_skb;
- /* Protects concurrent access to the meta state machine
- * from taggers running on multiple ports on SMP systems
- */
- spinlock_t meta_lock;
- unsigned long state;
- struct kthread_worker *xmit_worker;
+ /* Tagger to switch */
void (*xmit_work_fn)(struct kthread_work *work);
void (*meta_tstamp_handler)(struct dsa_switch *ds, int port, u8 ts_id,
enum sja1110_meta_tstamp dir, u64 tstamp);
+ /* Switch to tagger */
+ bool (*rxtstamp_get_state)(struct dsa_switch *ds);
+ void (*rxtstamp_set_state)(struct dsa_switch *ds, bool on);
};

struct sja1105_skb_cb {
diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c
index fe6a6d95bb26..93d2484b2480 100644
--- a/net/dsa/tag_sja1105.c
+++ b/net/dsa/tag_sja1105.c
@@ -53,6 +53,25 @@
#define SJA1110_TX_TRAILER_LEN 4
#define SJA1110_MAX_PADDING_LEN 15

+#define SJA1105_HWTS_RX_EN 0
+
+struct sja1105_tagger_private {
+ struct sja1105_tagger_data data; /* Must be first */
+ unsigned long state;
+ /* Protects concurrent access to the meta state machine
+ * from taggers running on multiple ports on SMP systems
+ */
+ spinlock_t meta_lock;
+ struct sk_buff *stampable_skb;
+ struct kthread_worker *xmit_worker;
+};
+
+static struct sja1105_tagger_private *
+sja1105_tagger_private(struct dsa_switch *ds)
+{
+ return ds->tagger_data;
+}
+
/* Similar to is_link_local_ether_addr(hdr->h_dest) but also covers PTP */
static inline bool sja1105_is_link_local(const struct sk_buff *skb)
{
@@ -120,12 +139,13 @@ static struct sk_buff *sja1105_defer_xmit(struct dsa_port *dp,
struct sk_buff *skb)
{
struct sja1105_tagger_data *tagger_data = sja1105_tagger_data(dp->ds);
+ struct sja1105_tagger_private *priv = sja1105_tagger_private(dp->ds);
void (*xmit_work_fn)(struct kthread_work *work);
struct sja1105_deferred_xmit_work *xmit_work;
struct kthread_worker *xmit_worker;

xmit_work_fn = tagger_data->xmit_work_fn;
- xmit_worker = tagger_data->xmit_worker;
+ xmit_worker = priv->xmit_worker;

if (!xmit_work_fn || !xmit_worker)
return NULL;
@@ -362,32 +382,32 @@ static struct sk_buff
*/
if (is_link_local) {
struct dsa_port *dp = dsa_slave_to_port(skb->dev);
- struct sja1105_tagger_data *tagger_data;
+ struct sja1105_tagger_private *priv;
struct dsa_switch *ds = dp->ds;

- tagger_data = sja1105_tagger_data(ds);
+ priv = sja1105_tagger_private(ds);

- if (!test_bit(SJA1105_HWTS_RX_EN, &tagger_data->state))
+ if (!test_bit(SJA1105_HWTS_RX_EN, &priv->state))
/* Do normal processing. */
return skb;

- spin_lock(&tagger_data->meta_lock);
+ spin_lock(&priv->meta_lock);
/* Was this a link-local frame instead of the meta
* that we were expecting?
*/
- if (tagger_data->stampable_skb) {
+ if (priv->stampable_skb) {
dev_err_ratelimited(ds->dev,
"Expected meta frame, is %12llx "
"in the DSA master multicast filter?\n",
SJA1105_META_DMAC);
- kfree_skb(tagger_data->stampable_skb);
+ kfree_skb(priv->stampable_skb);
}

/* Hold a reference to avoid dsa_switch_rcv
* from freeing the skb.
*/
- tagger_data->stampable_skb = skb_get(skb);
- spin_unlock(&tagger_data->meta_lock);
+ priv->stampable_skb = skb_get(skb);
+ spin_unlock(&priv->meta_lock);

/* Tell DSA we got nothing */
return NULL;
@@ -400,22 +420,22 @@ static struct sk_buff
*/
} else if (is_meta) {
struct dsa_port *dp = dsa_slave_to_port(skb->dev);
- struct sja1105_tagger_data *tagger_data;
+ struct sja1105_tagger_private *priv;
struct dsa_switch *ds = dp->ds;
struct sk_buff *stampable_skb;

- tagger_data = sja1105_tagger_data(ds);
+ priv = sja1105_tagger_private(ds);

/* Drop the meta frame if we're not in the right state
* to process it.
*/
- if (!test_bit(SJA1105_HWTS_RX_EN, &tagger_data->state))
+ if (!test_bit(SJA1105_HWTS_RX_EN, &priv->state))
return NULL;

- spin_lock(&tagger_data->meta_lock);
+ spin_lock(&priv->meta_lock);

- stampable_skb = tagger_data->stampable_skb;
- tagger_data->stampable_skb = NULL;
+ stampable_skb = priv->stampable_skb;
+ priv->stampable_skb = NULL;

/* Was this a meta frame instead of the link-local
* that we were expecting?
@@ -423,14 +443,14 @@ static struct sk_buff
if (!stampable_skb) {
dev_err_ratelimited(ds->dev,
"Unexpected meta frame\n");
- spin_unlock(&tagger_data->meta_lock);
+ spin_unlock(&priv->meta_lock);
return NULL;
}

if (stampable_skb->dev != skb->dev) {
dev_err_ratelimited(ds->dev,
"Meta frame on wrong port\n");
- spin_unlock(&tagger_data->meta_lock);
+ spin_unlock(&priv->meta_lock);
return NULL;
}

@@ -441,12 +461,36 @@ static struct sk_buff
skb = stampable_skb;
sja1105_transfer_meta(skb, meta);

- spin_unlock(&tagger_data->meta_lock);
+ spin_unlock(&priv->meta_lock);
}

return skb;
}

+static bool sja1105_rxtstamp_get_state(struct dsa_switch *ds)
+{
+ struct sja1105_tagger_private *priv = sja1105_tagger_private(ds);
+
+ return test_bit(SJA1105_HWTS_RX_EN, &priv->state);
+}
+
+static void sja1105_rxtstamp_set_state(struct dsa_switch *ds, bool on)
+{
+ struct sja1105_tagger_private *priv = sja1105_tagger_private(ds);
+
+ if (on)
+ set_bit(SJA1105_HWTS_RX_EN, &priv->state);
+ else
+ clear_bit(SJA1105_HWTS_RX_EN, &priv->state);
+
+ /* Initialize the meta state machine to a known state */
+ if (!priv->stampable_skb)
+ return;
+
+ kfree_skb(priv->stampable_skb);
+ priv->stampable_skb = NULL;
+}
+
static bool sja1105_skb_has_tag_8021q(const struct sk_buff *skb)
{
u16 tpid = ntohs(eth_hdr(skb)->h_proto);
@@ -699,26 +743,27 @@ static void sja1110_flow_dissect(const struct sk_buff *skb, __be16 *proto,

static void sja1105_disconnect(struct dsa_switch_tree *dst)
{
- struct sja1105_tagger_data *tagger_data;
+ struct sja1105_tagger_private *priv;
struct dsa_port *dp;

list_for_each_entry(dp, &dst->ports, list) {
- tagger_data = dp->ds->tagger_data;
+ priv = dp->ds->tagger_data;

- if (!tagger_data)
+ if (!priv)
continue;

- if (tagger_data->xmit_worker)
- kthread_destroy_worker(tagger_data->xmit_worker);
+ if (priv->xmit_worker)
+ kthread_destroy_worker(priv->xmit_worker);

- kfree(tagger_data);
- dp->ds->tagger_data = NULL;
+ kfree(priv);
+ dp->ds->priv = NULL;
}
}

static int sja1105_connect(struct dsa_switch_tree *dst)
{
struct sja1105_tagger_data *tagger_data;
+ struct sja1105_tagger_private *priv;
struct kthread_worker *xmit_worker;
struct dsa_port *dp;
int err;
@@ -727,13 +772,13 @@ static int sja1105_connect(struct dsa_switch_tree *dst)
if (dp->ds->tagger_data)
continue;

- tagger_data = kzalloc(sizeof(*tagger_data), GFP_KERNEL);
- if (!tagger_data) {
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
err = -ENOMEM;
goto out;
}

- spin_lock_init(&tagger_data->meta_lock);
+ spin_lock_init(&priv->meta_lock);

xmit_worker = kthread_create_worker(0, "dsa%d:%d_xmit",
dst->index, dp->ds->index);
@@ -742,8 +787,12 @@ static int sja1105_connect(struct dsa_switch_tree *dst)
goto out;
}

- tagger_data->xmit_worker = xmit_worker;
- dp->ds->tagger_data = tagger_data;
+ priv->xmit_worker = xmit_worker;
+ /* Export functions for switch driver use */
+ tagger_data = &priv->data;
+ tagger_data->rxtstamp_get_state = sja1105_rxtstamp_get_state;
+ tagger_data->rxtstamp_set_state = sja1105_rxtstamp_set_state;
+ dp->ds->tagger_data = priv;
}

return 0;
--
2.25.1