Commit 950a419d authored by Vladimir Oltean's avatar Vladimir Oltean Committed by David S. Miller

net: dsa: tag_sja1105: split sja1105_tagger_data into private and public sections

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: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent fcbf979a
...@@ -58,11 +58,10 @@ enum sja1105_ptp_clk_mode { ...@@ -58,11 +58,10 @@ enum sja1105_ptp_clk_mode {
#define ptp_data_to_sja1105(d) \ #define ptp_data_to_sja1105(d) \
container_of((d), struct sja1105_private, ptp_data) container_of((d), struct sja1105_private, ptp_data)
/* Must be called only with the tagger_data->state bit /* Must be called only while the RX timestamping state of the tagger
* SJA1105_HWTS_RX_EN cleared * is turned off
*/ */
static int sja1105_change_rxtstamping(struct sja1105_private *priv, static int sja1105_change_rxtstamping(struct sja1105_private *priv,
struct sja1105_tagger_data *tagger_data,
bool on) bool on)
{ {
struct sja1105_ptp_data *ptp_data = &priv->ptp_data; struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
...@@ -74,11 +73,6 @@ static int sja1105_change_rxtstamping(struct sja1105_private *priv, ...@@ -74,11 +73,6 @@ static int sja1105_change_rxtstamping(struct sja1105_private *priv,
general_params->send_meta1 = on; general_params->send_meta1 = on;
general_params->send_meta0 = 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); ptp_cancel_worker_sync(ptp_data->clock);
skb_queue_purge(&ptp_data->skb_txtstamp_queue); skb_queue_purge(&ptp_data->skb_txtstamp_queue);
skb_queue_purge(&ptp_data->skb_rxtstamp_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) ...@@ -117,17 +111,17 @@ int sja1105_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr)
break; break;
} }
if (rx_on != test_bit(SJA1105_HWTS_RX_EN, &tagger_data->state)) { if (rx_on != tagger_data->rxtstamp_get_state(ds)) {
clear_bit(SJA1105_HWTS_RX_EN, &tagger_data->state); 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) { if (rc < 0) {
dev_err(ds->dev, dev_err(ds->dev,
"Failed to change RX timestamping: %d\n", rc); "Failed to change RX timestamping: %d\n", rc);
return rc; return rc;
} }
if (rx_on) 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))) 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) ...@@ -146,7 +140,7 @@ int sja1105_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr)
config.tx_type = HWTSTAMP_TX_ON; config.tx_type = HWTSTAMP_TX_ON;
else else
config.tx_type = HWTSTAMP_TX_OFF; 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; config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
else else
config.rx_filter = HWTSTAMP_FILTER_NONE; config.rx_filter = HWTSTAMP_FILTER_NONE;
...@@ -423,7 +417,7 @@ bool sja1105_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb) ...@@ -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_private *priv = ds->priv;
struct sja1105_ptp_data *ptp_data = &priv->ptp_data; 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; return false;
/* We need to read the full PTP clock to reconstruct the Rx /* We need to read the full PTP clock to reconstruct the Rx
......
...@@ -35,8 +35,6 @@ ...@@ -35,8 +35,6 @@
#define SJA1105_META_SMAC 0x222222222222ull #define SJA1105_META_SMAC 0x222222222222ull
#define SJA1105_META_DMAC 0x0180C200000Eull #define SJA1105_META_DMAC 0x0180C200000Eull
#define SJA1105_HWTS_RX_EN 0
enum sja1110_meta_tstamp { enum sja1110_meta_tstamp {
SJA1110_META_TSTAMP_TX = 0, SJA1110_META_TSTAMP_TX = 0,
SJA1110_META_TSTAMP_RX = 1, SJA1110_META_TSTAMP_RX = 1,
...@@ -50,16 +48,13 @@ struct sja1105_deferred_xmit_work { ...@@ -50,16 +48,13 @@ struct sja1105_deferred_xmit_work {
/* Global tagger data */ /* Global tagger data */
struct sja1105_tagger_data { struct sja1105_tagger_data {
struct sk_buff *stampable_skb; /* Tagger to switch */
/* 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;
void (*xmit_work_fn)(struct kthread_work *work); void (*xmit_work_fn)(struct kthread_work *work);
void (*meta_tstamp_handler)(struct dsa_switch *ds, int port, u8 ts_id, void (*meta_tstamp_handler)(struct dsa_switch *ds, int port, u8 ts_id,
enum sja1110_meta_tstamp dir, u64 tstamp); 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 { struct sja1105_skb_cb {
......
...@@ -53,6 +53,25 @@ ...@@ -53,6 +53,25 @@
#define SJA1110_TX_TRAILER_LEN 4 #define SJA1110_TX_TRAILER_LEN 4
#define SJA1110_MAX_PADDING_LEN 15 #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 */ /* 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) 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, ...@@ -120,12 +139,13 @@ static struct sk_buff *sja1105_defer_xmit(struct dsa_port *dp,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct sja1105_tagger_data *tagger_data = sja1105_tagger_data(dp->ds); 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); void (*xmit_work_fn)(struct kthread_work *work);
struct sja1105_deferred_xmit_work *xmit_work; struct sja1105_deferred_xmit_work *xmit_work;
struct kthread_worker *xmit_worker; struct kthread_worker *xmit_worker;
xmit_work_fn = tagger_data->xmit_work_fn; 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) if (!xmit_work_fn || !xmit_worker)
return NULL; return NULL;
...@@ -362,32 +382,32 @@ static struct sk_buff ...@@ -362,32 +382,32 @@ static struct sk_buff
*/ */
if (is_link_local) { if (is_link_local) {
struct dsa_port *dp = dsa_slave_to_port(skb->dev); 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 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. */ /* Do normal processing. */
return skb; return skb;
spin_lock(&tagger_data->meta_lock); spin_lock(&priv->meta_lock);
/* Was this a link-local frame instead of the meta /* Was this a link-local frame instead of the meta
* that we were expecting? * that we were expecting?
*/ */
if (tagger_data->stampable_skb) { if (priv->stampable_skb) {
dev_err_ratelimited(ds->dev, dev_err_ratelimited(ds->dev,
"Expected meta frame, is %12llx " "Expected meta frame, is %12llx "
"in the DSA master multicast filter?\n", "in the DSA master multicast filter?\n",
SJA1105_META_DMAC); SJA1105_META_DMAC);
kfree_skb(tagger_data->stampable_skb); kfree_skb(priv->stampable_skb);
} }
/* Hold a reference to avoid dsa_switch_rcv /* Hold a reference to avoid dsa_switch_rcv
* from freeing the skb. * from freeing the skb.
*/ */
tagger_data->stampable_skb = skb_get(skb); priv->stampable_skb = skb_get(skb);
spin_unlock(&tagger_data->meta_lock); spin_unlock(&priv->meta_lock);
/* Tell DSA we got nothing */ /* Tell DSA we got nothing */
return NULL; return NULL;
...@@ -400,22 +420,22 @@ static struct sk_buff ...@@ -400,22 +420,22 @@ static struct sk_buff
*/ */
} else if (is_meta) { } else if (is_meta) {
struct dsa_port *dp = dsa_slave_to_port(skb->dev); 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 dsa_switch *ds = dp->ds;
struct sk_buff *stampable_skb; 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 /* Drop the meta frame if we're not in the right state
* to process it. * to process it.
*/ */
if (!test_bit(SJA1105_HWTS_RX_EN, &tagger_data->state)) if (!test_bit(SJA1105_HWTS_RX_EN, &priv->state))
return NULL; return NULL;
spin_lock(&tagger_data->meta_lock); spin_lock(&priv->meta_lock);
stampable_skb = tagger_data->stampable_skb; stampable_skb = priv->stampable_skb;
tagger_data->stampable_skb = NULL; priv->stampable_skb = NULL;
/* Was this a meta frame instead of the link-local /* Was this a meta frame instead of the link-local
* that we were expecting? * that we were expecting?
...@@ -423,14 +443,14 @@ static struct sk_buff ...@@ -423,14 +443,14 @@ static struct sk_buff
if (!stampable_skb) { if (!stampable_skb) {
dev_err_ratelimited(ds->dev, dev_err_ratelimited(ds->dev,
"Unexpected meta frame\n"); "Unexpected meta frame\n");
spin_unlock(&tagger_data->meta_lock); spin_unlock(&priv->meta_lock);
return NULL; return NULL;
} }
if (stampable_skb->dev != skb->dev) { if (stampable_skb->dev != skb->dev) {
dev_err_ratelimited(ds->dev, dev_err_ratelimited(ds->dev,
"Meta frame on wrong port\n"); "Meta frame on wrong port\n");
spin_unlock(&tagger_data->meta_lock); spin_unlock(&priv->meta_lock);
return NULL; return NULL;
} }
...@@ -441,12 +461,36 @@ static struct sk_buff ...@@ -441,12 +461,36 @@ static struct sk_buff
skb = stampable_skb; skb = stampable_skb;
sja1105_transfer_meta(skb, meta); sja1105_transfer_meta(skb, meta);
spin_unlock(&tagger_data->meta_lock); spin_unlock(&priv->meta_lock);
} }
return skb; 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) static bool sja1105_skb_has_tag_8021q(const struct sk_buff *skb)
{ {
u16 tpid = ntohs(eth_hdr(skb)->h_proto); u16 tpid = ntohs(eth_hdr(skb)->h_proto);
...@@ -699,26 +743,27 @@ static void sja1110_flow_dissect(const struct sk_buff *skb, __be16 *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) static void sja1105_disconnect(struct dsa_switch_tree *dst)
{ {
struct sja1105_tagger_data *tagger_data; struct sja1105_tagger_private *priv;
struct dsa_port *dp; struct dsa_port *dp;
list_for_each_entry(dp, &dst->ports, list) { 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; continue;
if (tagger_data->xmit_worker) if (priv->xmit_worker)
kthread_destroy_worker(tagger_data->xmit_worker); kthread_destroy_worker(priv->xmit_worker);
kfree(tagger_data); kfree(priv);
dp->ds->tagger_data = NULL; dp->ds->priv = NULL;
} }
} }
static int sja1105_connect(struct dsa_switch_tree *dst) static int sja1105_connect(struct dsa_switch_tree *dst)
{ {
struct sja1105_tagger_data *tagger_data; struct sja1105_tagger_data *tagger_data;
struct sja1105_tagger_private *priv;
struct kthread_worker *xmit_worker; struct kthread_worker *xmit_worker;
struct dsa_port *dp; struct dsa_port *dp;
int err; int err;
...@@ -727,13 +772,13 @@ static int sja1105_connect(struct dsa_switch_tree *dst) ...@@ -727,13 +772,13 @@ static int sja1105_connect(struct dsa_switch_tree *dst)
if (dp->ds->tagger_data) if (dp->ds->tagger_data)
continue; continue;
tagger_data = kzalloc(sizeof(*tagger_data), GFP_KERNEL); priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!tagger_data) { if (!priv) {
err = -ENOMEM; err = -ENOMEM;
goto out; 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", xmit_worker = kthread_create_worker(0, "dsa%d:%d_xmit",
dst->index, dp->ds->index); dst->index, dp->ds->index);
...@@ -742,8 +787,12 @@ static int sja1105_connect(struct dsa_switch_tree *dst) ...@@ -742,8 +787,12 @@ static int sja1105_connect(struct dsa_switch_tree *dst)
goto out; goto out;
} }
tagger_data->xmit_worker = xmit_worker; priv->xmit_worker = xmit_worker;
dp->ds->tagger_data = tagger_data; /* 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; return 0;
......
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