From: Vladimir Oltean <olteanv@gmail.com>
To: f.fainelli@gmail.com, vivien.didelot@gmail.com, andrew@lunn.ch,
davem@davemloft.net, richardcochran@gmail.com,
john.stultz@linaro.org, tglx@linutronix.de, sboyd@kernel.org
Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
Vladimir Oltean <olteanv@gmail.com>
Subject: [PATCH v3 net-next 16/17] net: dsa: sja1105: Add a state machine for RX timestamping
Date: Tue, 4 Jun 2019 20:07:55 +0300 [thread overview]
Message-ID: <20190604170756.14338-17-olteanv@gmail.com> (raw)
In-Reply-To: <20190604170756.14338-1-olteanv@gmail.com>
Meta frame reception relies on the hardware keeping its promise that it
will send no other traffic towards the CPU port between a link-local
frame and a meta frame. Otherwise there is no other way to associate
the meta frame with the link-local frame it's holding a timestamp of.
The receive function is made stateful, and buffers a timestampable frame
until its meta frame arrives, then merges the two, drops the meta and
releases the link-local frame up the stack.
Signed-off-by: Vladimir Oltean <olteanv@gmail.com>
---
Changes in v3:
Split from previous 09/10 patch (no functional changes).
Changes in v2:
None.
drivers/net/dsa/sja1105/sja1105_main.c | 65 +++++++++++++
include/linux/dsa/sja1105.h | 7 ++
net/dsa/tag_sja1105.c | 121 ++++++++++++++++++++++++-
3 files changed, 192 insertions(+), 1 deletion(-)
diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c
index fceca0e5984f..46a851f54b83 100644
--- a/drivers/net/dsa/sja1105/sja1105_main.c
+++ b/drivers/net/dsa/sja1105/sja1105_main.c
@@ -1600,6 +1600,14 @@ static int sja1105_setup(struct dsa_switch *ds)
return sja1105_setup_8021q_tagging(ds, true);
}
+static void sja1105_teardown(struct dsa_switch *ds)
+{
+ struct sja1105_private *priv = ds->priv;
+
+ cancel_work_sync(&priv->tagger_data.rxtstamp_work);
+ skb_queue_purge(&priv->tagger_data.skb_rxtstamp_queue);
+}
+
static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot,
struct sk_buff *skb, bool takets)
{
@@ -1747,6 +1755,60 @@ static int sja1105_set_ageing_time(struct dsa_switch *ds,
return sja1105_static_config_reload(priv);
}
+#define to_tagger(d) \
+ container_of((d), struct sja1105_tagger_data, rxtstamp_work)
+#define to_sja1105(d) \
+ container_of((d), struct sja1105_private, tagger_data)
+
+static void sja1105_rxtstamp_work(struct work_struct *work)
+{
+ struct sja1105_tagger_data *data = to_tagger(work);
+ struct sja1105_private *priv = to_sja1105(data);
+ struct sk_buff *skb;
+ u64 now;
+
+ mutex_lock(&priv->ptp_lock);
+
+ now = priv->tstamp_cc.read(&priv->tstamp_cc);
+
+ while ((skb = skb_dequeue(&data->skb_rxtstamp_queue)) != NULL) {
+ struct skb_shared_hwtstamps *shwt = skb_hwtstamps(skb);
+ u64 ts;
+
+ *shwt = (struct skb_shared_hwtstamps) {0};
+
+ ts = SJA1105_SKB_CB(skb)->meta_tstamp;
+ ts = sja1105_tstamp_reconstruct(priv, now, ts);
+ ts = timecounter_cyc2time(&priv->tstamp_tc, ts);
+
+ shwt->hwtstamp = ns_to_ktime(ts);
+ netif_rx_ni(skb);
+ }
+
+ mutex_unlock(&priv->ptp_lock);
+}
+
+/* Called from dsa_skb_defer_rx_timestamp */
+bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port,
+ struct sk_buff *skb, unsigned int type)
+{
+ struct sja1105_private *priv = ds->priv;
+ struct sja1105_tagger_data *data = &priv->tagger_data;
+
+ if (!data->hwts_rx_en)
+ return false;
+
+ if (!sja1105_is_link_local(skb))
+ return false;
+
+ /* We need to read the full PTP clock to reconstruct the Rx
+ * timestamp. For that we need a sleepable context.
+ */
+ skb_queue_tail(&data->skb_rxtstamp_queue, skb);
+ schedule_work(&data->rxtstamp_work);
+ return true;
+}
+
/* Called from dsa_skb_tx_timestamp. This callback is just to make DSA clone
* the skb and have it available in DSA_SKB_CB in the .port_deferred_xmit
* callback, where we will timestamp it synchronously.
@@ -1766,6 +1828,7 @@ bool sja1105_port_txtstamp(struct dsa_switch *ds, int port,
static const struct dsa_switch_ops sja1105_switch_ops = {
.get_tag_protocol = sja1105_get_tag_protocol,
.setup = sja1105_setup,
+ .teardown = sja1105_teardown,
.set_ageing_time = sja1105_set_ageing_time,
.phylink_validate = sja1105_phylink_validate,
.phylink_mac_config = sja1105_mac_config,
@@ -1787,6 +1850,7 @@ static const struct dsa_switch_ops sja1105_switch_ops = {
.port_mdb_add = sja1105_mdb_add,
.port_mdb_del = sja1105_mdb_del,
.port_deferred_xmit = sja1105_port_deferred_xmit,
+ .port_rxtstamp = sja1105_port_rxtstamp,
.port_txtstamp = sja1105_port_txtstamp,
};
@@ -1885,6 +1949,7 @@ static int sja1105_probe(struct spi_device *spi)
tagger_data = &priv->tagger_data;
skb_queue_head_init(&tagger_data->skb_rxtstamp_queue);
+ INIT_WORK(&tagger_data->rxtstamp_work, sja1105_rxtstamp_work);
/* Connections between dsa_port and sja1105_port */
for (i = 0; i < SJA1105_NUM_PORTS; i++) {
diff --git a/include/linux/dsa/sja1105.h b/include/linux/dsa/sja1105.h
index b83f6e428e5d..794ee76aae56 100644
--- a/include/linux/dsa/sja1105.h
+++ b/include/linux/dsa/sja1105.h
@@ -45,6 +45,13 @@ struct sja1105_tagger_data {
bool hwts_rx_en;
};
+struct sja1105_skb_cb {
+ u32 meta_tstamp;
+};
+
+#define SJA1105_SKB_CB(skb) \
+ ((struct sja1105_skb_cb *)DSA_SKB_CB_PRIV(skb))
+
struct sja1105_port {
struct sja1105_tagger_data *data;
struct dsa_port *dp;
diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c
index 6ec9a32dda62..aac48b42f210 100644
--- a/net/dsa/tag_sja1105.c
+++ b/net/dsa/tag_sja1105.c
@@ -94,6 +94,124 @@ static struct sk_buff *sja1105_xmit(struct sk_buff *skb,
((pcp << VLAN_PRIO_SHIFT) | tx_vid));
}
+static void sja1105_transfer_meta(struct sk_buff *skb,
+ const struct sja1105_meta *meta)
+{
+ struct ethhdr *hdr = eth_hdr(skb);
+
+ hdr->h_dest[3] = meta->dmac_byte_3;
+ hdr->h_dest[4] = meta->dmac_byte_4;
+ SJA1105_SKB_CB(skb)->meta_tstamp = meta->tstamp;
+}
+
+/* This is a simple state machine which follows the hardware mechanism of
+ * generating RX timestamps:
+ *
+ * After each timestampable skb (all traffic for which send_meta1 and
+ * send_meta0 is true, aka all MAC-filtered link-local traffic) a meta frame
+ * containing a partial timestamp is immediately generated by the switch and
+ * sent as a follow-up to the link-local frame on the CPU port.
+ *
+ * The meta frames have no unique identifier (such as sequence number) by which
+ * one may pair them to the correct timestampable frame.
+ * Instead, the switch has internal logic that ensures no frames are sent on
+ * the CPU port between a link-local timestampable frame and its corresponding
+ * meta follow-up. It also ensures strict ordering between ports (lower ports
+ * have higher priority towards the CPU port). For this reason, a per-port
+ * data structure is not needed/desirable.
+ *
+ * This function pairs the link-local frame with its partial timestamp from the
+ * meta follow-up frame. The full timestamp will be reconstructed later in a
+ * work queue.
+ */
+static struct sk_buff
+*sja1105_rcv_meta_state_machine(struct sk_buff *skb,
+ struct sja1105_meta *meta,
+ bool is_link_local,
+ bool is_meta)
+{
+ struct sja1105_port *sp;
+ struct dsa_port *dp;
+
+ dp = dsa_slave_to_port(skb->dev);
+ sp = dp->priv;
+
+ /* Step 1: A timestampable frame was received.
+ * Buffer it until we get its meta frame.
+ */
+ if (is_link_local && sp->data->hwts_rx_en) {
+ spin_lock(&sp->data->meta_lock);
+ /* Was this a link-local frame instead of the meta
+ * that we were expecting?
+ */
+ if (sp->data->stampable_skb) {
+ dev_err_ratelimited(dp->ds->dev,
+ "Expected meta frame, is %12llx "
+ "in the DSA master multicast filter?\n",
+ SJA1105_META_DMAC);
+ }
+
+ /* Hold a reference to avoid dsa_switch_rcv
+ * from freeing the skb.
+ */
+ sp->data->stampable_skb = skb_get(skb);
+ spin_unlock(&sp->data->meta_lock);
+
+ /* Tell DSA we got nothing */
+ return NULL;
+
+ /* Step 2: The meta frame arrived.
+ * Time to take the stampable skb out of the closet, annotate it
+ * with the partial timestamp, and pretend that we received it
+ * just now (basically masquerade the buffered frame as the meta
+ * frame, which serves no further purpose).
+ */
+ } else if (is_meta) {
+ struct sk_buff *stampable_skb;
+
+ spin_lock(&sp->data->meta_lock);
+
+ stampable_skb = sp->data->stampable_skb;
+ sp->data->stampable_skb = NULL;
+
+ /* Was this a meta frame instead of the link-local
+ * that we were expecting?
+ */
+ if (!stampable_skb) {
+ dev_err_ratelimited(dp->ds->dev,
+ "Unexpected meta frame\n");
+ spin_unlock(&sp->data->meta_lock);
+ return NULL;
+ }
+
+ if (stampable_skb->dev != skb->dev) {
+ dev_err_ratelimited(dp->ds->dev,
+ "Meta frame on wrong port\n");
+ spin_unlock(&sp->data->meta_lock);
+ return NULL;
+ }
+
+ /* Free the meta frame and give DSA the buffered stampable_skb
+ * for further processing up the network stack.
+ */
+ kfree_skb(skb);
+
+ skb = skb_copy(stampable_skb, GFP_ATOMIC);
+ if (!skb) {
+ dev_err_ratelimited(dp->ds->dev,
+ "Failed to copy stampable skb\n");
+ return NULL;
+ }
+ sja1105_transfer_meta(skb, meta);
+ /* The cached copy will be freed now */
+ skb_unref(stampable_skb);
+
+ spin_unlock(&sp->data->meta_lock);
+ }
+
+ return skb;
+}
+
static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
struct net_device *netdev,
struct packet_type *pt)
@@ -151,7 +269,8 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
if (is_tagged)
skb = dsa_8021q_remove_header(skb);
- return skb;
+ return sja1105_rcv_meta_state_machine(skb, &meta, is_link_local,
+ is_meta);
}
static struct dsa_device_ops sja1105_netdev_ops = {
--
2.17.1
next prev parent reply other threads:[~2019-06-04 17:08 UTC|newest]
Thread overview: 40+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-06-04 17:07 [PATCH v3 net-next 00/17] PTP support for the SJA1105 DSA driver Vladimir Oltean
2019-06-04 17:07 ` [PATCH v3 net-next 01/17] net: dsa: Keep a pointer to the skb clone for TX timestamping Vladimir Oltean
2019-06-04 20:31 ` Florian Fainelli
2019-06-04 17:07 ` [PATCH v3 net-next 02/17] net: dsa: Add teardown callback for drivers Vladimir Oltean
2019-06-04 18:15 ` Andrew Lunn
2019-06-04 20:32 ` Florian Fainelli
2019-06-04 17:07 ` [PATCH v3 net-next 03/17] net: dsa: tag_8021q: Create helper function for removing VLAN header Vladimir Oltean
2019-06-04 17:07 ` [PATCH v3 net-next 04/17] net: dsa: sja1105: Move sja1105_change_tpid into sja1105_vlan_filtering Vladimir Oltean
2019-06-04 20:58 ` Florian Fainelli
2019-06-04 17:07 ` [PATCH v3 net-next 05/17] net: dsa: sja1105: Reverse TPID and TPID2 Vladimir Oltean
2019-06-04 20:33 ` Florian Fainelli
2019-06-04 17:07 ` [PATCH v3 net-next 06/17] net: dsa: sja1105: Limit use of incl_srcpt to bridge+vlan mode Vladimir Oltean
2019-06-04 20:35 ` Florian Fainelli
2019-06-04 17:07 ` [PATCH v3 net-next 07/17] net: dsa: sja1105: Add support for the PTP clock Vladimir Oltean
2019-06-04 17:07 ` [PATCH v3 net-next 08/17] net: dsa: sja1105: Move sja1105_is_link_local to include/linux Vladimir Oltean
2019-06-04 20:35 ` Florian Fainelli
2019-06-04 17:07 ` [PATCH v3 net-next 09/17] net: dsa: sja1105: Add logic for TX timestamping Vladimir Oltean
2019-06-04 17:07 ` [PATCH v3 net-next 10/17] net: dsa: sja1105: Build a minimal understanding of meta frames Vladimir Oltean
2019-06-04 21:00 ` Florian Fainelli
2019-06-04 17:07 ` [PATCH v3 net-next 11/17] net: dsa: sja1105: Add support for the AVB Parameters Table Vladimir Oltean
2019-06-04 17:07 ` [PATCH v3 net-next 12/17] net: dsa: sja1105: Make sja1105_is_link_local not match meta frames Vladimir Oltean
2019-06-04 17:07 ` [PATCH v3 net-next 13/17] net: dsa: sja1105: Receive and decode " Vladimir Oltean
2019-06-04 17:07 ` [PATCH v3 net-next 14/17] net: dsa: sja1105: Add a global sja1105_tagger_data structure Vladimir Oltean
2019-06-04 17:07 ` [PATCH v3 net-next 15/17] net: dsa: sja1105: Increase priority of CPU-trapped frames Vladimir Oltean
2019-06-04 17:07 ` Vladimir Oltean [this message]
2019-06-04 17:07 ` [PATCH v3 net-next 17/17] net: dsa: sja1105: Expose PTP timestamping ioctls to userspace Vladimir Oltean
2019-06-05 3:22 ` [PATCH v3 net-next 00/17] PTP support for the SJA1105 DSA driver David Miller
2019-06-05 9:13 ` Vladimir Oltean
2019-06-05 11:33 ` Vladimir Oltean
2019-06-05 17:45 ` Richard Cochran
2019-06-05 17:50 ` Vladimir Oltean
2019-06-05 17:52 ` Vladimir Oltean
2019-06-05 18:08 ` Vladimir Oltean
2019-06-06 3:11 ` Richard Cochran
2019-06-06 13:40 ` Vladimir Oltean
2019-06-07 3:32 ` Richard Cochran
2019-06-07 9:43 ` Vladimir Oltean
2019-06-05 18:44 ` David Miller
2019-06-07 19:15 ` David Miller
2019-06-07 19:38 ` Vladimir Oltean
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190604170756.14338-17-olteanv@gmail.com \
--to=olteanv@gmail.com \
--cc=andrew@lunn.ch \
--cc=davem@davemloft.net \
--cc=f.fainelli@gmail.com \
--cc=john.stultz@linaro.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=richardcochran@gmail.com \
--cc=sboyd@kernel.org \
--cc=tglx@linutronix.de \
--cc=vivien.didelot@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).