Commit 6d709cad authored by Vladimir Oltean's avatar Vladimir Oltean Committed by David S. Miller

net: dsa: move sja1110_process_meta_tstamp inside the tagging protocol driver

The problem is that DSA tagging protocols really must not depend on the
switch driver, because this creates a circular dependency at insmod
time, and the switch driver will effectively not load when the tagging
protocol driver is missing.

The code was structured in the way it was for a reason, though. The DSA
driver-facing API for PTP timestamping relies on the assumption that
two-step TX timestamps are provided by the hardware in an out-of-band
manner, typically by raising an interrupt and making that timestamp
available inside some sort of FIFO which is to be accessed over
SPI/MDIO/etc.

So the API puts .port_txtstamp into dsa_switch_ops, because it is
expected that the switch driver needs to save some state (like put the
skb into a queue until its TX timestamp arrives).

On SJA1110, TX timestamps are provided by the switch as Ethernet
packets, so this makes them be received and processed by the tagging
protocol driver. This in itself is great, because the timestamps are
full 64-bit and do not require reconstruction, and since Ethernet is the
fastest I/O method available to/from the switch, PTP timestamps arrive
very quickly, no matter how bottlenecked the SPI connection is, because
SPI interaction is not needed at all.

DSA's code structure and strict isolation between the tagging protocol
driver and the switch driver break the natural code organization.

When the tagging protocol driver receives a packet which is classified
as a metadata packet containing timestamps, it passes those timestamps
one by one to the switch driver, which then proceeds to compare them
based on the recorded timestamp ID that was generated in .port_txtstamp.

The communication between the tagging protocol and the switch driver is
done through a method exported by the switch driver, sja1110_process_meta_tstamp.
To satisfy build requirements, we force a dependency to build the
tagging protocol driver as a module when the switch driver is a module.
However, as explained in the first paragraph, that causes the circular
dependency.

To solve this, move the skb queue from struct sja1105_private :: struct
sja1105_ptp_data to struct sja1105_private :: struct sja1105_tagger_data.
The latter is a data structure for which hacks have already been put
into place to be able to create persistent storage per switch that is
accessible from the tagging protocol driver (see sja1105_setup_ports).

With the skb queue directly accessible from the tagging protocol driver,
we can now move sja1110_process_meta_tstamp into the tagging driver
itself, and avoid exporting a symbol.

Fixes: 566b18c8 ("net: dsa: sja1105: implement TX timestamping for SJA1110")
Link: https://lore.kernel.org/netdev/20210908220834.d7gmtnwrorhharna@skbuf/Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 68a81bb2
...@@ -64,6 +64,7 @@ enum sja1105_ptp_clk_mode { ...@@ -64,6 +64,7 @@ enum sja1105_ptp_clk_mode {
static int sja1105_change_rxtstamping(struct sja1105_private *priv, static int sja1105_change_rxtstamping(struct sja1105_private *priv,
bool on) bool on)
{ {
struct sja1105_tagger_data *tagger_data = &priv->tagger_data;
struct sja1105_ptp_data *ptp_data = &priv->ptp_data; struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
struct sja1105_general_params_entry *general_params; struct sja1105_general_params_entry *general_params;
struct sja1105_table *table; struct sja1105_table *table;
...@@ -79,7 +80,7 @@ static int sja1105_change_rxtstamping(struct sja1105_private *priv, ...@@ -79,7 +80,7 @@ static int sja1105_change_rxtstamping(struct sja1105_private *priv,
priv->tagger_data.stampable_skb = NULL; priv->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(&tagger_data->skb_txtstamp_queue);
skb_queue_purge(&ptp_data->skb_rxtstamp_queue); skb_queue_purge(&ptp_data->skb_rxtstamp_queue);
return sja1105_static_config_reload(priv, SJA1105_RX_HWTSTAMPING); return sja1105_static_config_reload(priv, SJA1105_RX_HWTSTAMPING);
...@@ -452,40 +453,6 @@ bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port, ...@@ -452,40 +453,6 @@ bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port,
return priv->info->rxtstamp(ds, port, skb); return priv->info->rxtstamp(ds, port, skb);
} }
void sja1110_process_meta_tstamp(struct dsa_switch *ds, int port, u8 ts_id,
enum sja1110_meta_tstamp dir, u64 tstamp)
{
struct sja1105_private *priv = ds->priv;
struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
struct sk_buff *skb, *skb_tmp, *skb_match = NULL;
struct skb_shared_hwtstamps shwt = {0};
/* We don't care about RX timestamps on the CPU port */
if (dir == SJA1110_META_TSTAMP_RX)
return;
spin_lock(&ptp_data->skb_txtstamp_queue.lock);
skb_queue_walk_safe(&ptp_data->skb_txtstamp_queue, skb, skb_tmp) {
if (SJA1105_SKB_CB(skb)->ts_id != ts_id)
continue;
__skb_unlink(skb, &ptp_data->skb_txtstamp_queue);
skb_match = skb;
break;
}
spin_unlock(&ptp_data->skb_txtstamp_queue.lock);
if (WARN_ON(!skb_match))
return;
shwt.hwtstamp = ns_to_ktime(sja1105_ticks_to_ns(tstamp));
skb_complete_tx_timestamp(skb_match, &shwt);
}
EXPORT_SYMBOL_GPL(sja1110_process_meta_tstamp);
/* In addition to cloning the skb which is done by the common /* In addition to cloning the skb which is done by the common
* sja1105_port_txtstamp, we need to generate a timestamp ID and save the * sja1105_port_txtstamp, we need to generate a timestamp ID and save the
* packet to the TX timestamping queue. * packet to the TX timestamping queue.
...@@ -494,7 +461,6 @@ void sja1110_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb) ...@@ -494,7 +461,6 @@ void sja1110_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)
{ {
struct sk_buff *clone = SJA1105_SKB_CB(skb)->clone; struct sk_buff *clone = SJA1105_SKB_CB(skb)->clone;
struct sja1105_private *priv = ds->priv; struct sja1105_private *priv = ds->priv;
struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
struct sja1105_port *sp = &priv->ports[port]; struct sja1105_port *sp = &priv->ports[port];
u8 ts_id; u8 ts_id;
...@@ -510,7 +476,7 @@ void sja1110_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb) ...@@ -510,7 +476,7 @@ void sja1110_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)
spin_unlock(&sp->data->meta_lock); spin_unlock(&sp->data->meta_lock);
skb_queue_tail(&ptp_data->skb_txtstamp_queue, clone); skb_queue_tail(&sp->data->skb_txtstamp_queue, clone);
} }
/* Called from dsa_skb_tx_timestamp. This callback is just to clone /* Called from dsa_skb_tx_timestamp. This callback is just to clone
...@@ -953,7 +919,7 @@ int sja1105_ptp_clock_register(struct dsa_switch *ds) ...@@ -953,7 +919,7 @@ int sja1105_ptp_clock_register(struct dsa_switch *ds)
/* Only used on SJA1105 */ /* Only used on SJA1105 */
skb_queue_head_init(&ptp_data->skb_rxtstamp_queue); skb_queue_head_init(&ptp_data->skb_rxtstamp_queue);
/* Only used on SJA1110 */ /* Only used on SJA1110 */
skb_queue_head_init(&ptp_data->skb_txtstamp_queue); skb_queue_head_init(&tagger_data->skb_txtstamp_queue);
spin_lock_init(&tagger_data->meta_lock); spin_lock_init(&tagger_data->meta_lock);
ptp_data->clock = ptp_clock_register(&ptp_data->caps, ds->dev); ptp_data->clock = ptp_clock_register(&ptp_data->caps, ds->dev);
...@@ -971,6 +937,7 @@ int sja1105_ptp_clock_register(struct dsa_switch *ds) ...@@ -971,6 +937,7 @@ int sja1105_ptp_clock_register(struct dsa_switch *ds)
void sja1105_ptp_clock_unregister(struct dsa_switch *ds) void sja1105_ptp_clock_unregister(struct dsa_switch *ds)
{ {
struct sja1105_private *priv = ds->priv; struct sja1105_private *priv = ds->priv;
struct sja1105_tagger_data *tagger_data = &priv->tagger_data;
struct sja1105_ptp_data *ptp_data = &priv->ptp_data; struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
if (IS_ERR_OR_NULL(ptp_data->clock)) if (IS_ERR_OR_NULL(ptp_data->clock))
...@@ -978,7 +945,7 @@ void sja1105_ptp_clock_unregister(struct dsa_switch *ds) ...@@ -978,7 +945,7 @@ void sja1105_ptp_clock_unregister(struct dsa_switch *ds)
del_timer_sync(&ptp_data->extts_timer); del_timer_sync(&ptp_data->extts_timer);
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(&tagger_data->skb_txtstamp_queue);
skb_queue_purge(&ptp_data->skb_rxtstamp_queue); skb_queue_purge(&ptp_data->skb_rxtstamp_queue);
ptp_clock_unregister(ptp_data->clock); ptp_clock_unregister(ptp_data->clock);
ptp_data->clock = NULL; ptp_data->clock = NULL;
......
...@@ -8,21 +8,6 @@ ...@@ -8,21 +8,6 @@
#if IS_ENABLED(CONFIG_NET_DSA_SJA1105_PTP) #if IS_ENABLED(CONFIG_NET_DSA_SJA1105_PTP)
/* Timestamps are in units of 8 ns clock ticks (equivalent to
* a fixed 125 MHz clock).
*/
#define SJA1105_TICK_NS 8
static inline s64 ns_to_sja1105_ticks(s64 ns)
{
return ns / SJA1105_TICK_NS;
}
static inline s64 sja1105_ticks_to_ns(s64 ticks)
{
return ticks * SJA1105_TICK_NS;
}
/* Calculate the first base_time in the future that satisfies this /* Calculate the first base_time in the future that satisfies this
* relationship: * relationship:
* *
...@@ -77,10 +62,6 @@ struct sja1105_ptp_data { ...@@ -77,10 +62,6 @@ struct sja1105_ptp_data {
struct timer_list extts_timer; struct timer_list extts_timer;
/* Used only on SJA1105 to reconstruct partial timestamps */ /* Used only on SJA1105 to reconstruct partial timestamps */
struct sk_buff_head skb_rxtstamp_queue; struct sk_buff_head skb_rxtstamp_queue;
/* Used on SJA1110 where meta frames are generated only for
* 2-step TX timestamps
*/
struct sk_buff_head skb_txtstamp_queue;
struct ptp_clock_info caps; struct ptp_clock_info caps;
struct ptp_clock *clock; struct ptp_clock *clock;
struct sja1105_ptp_cmd cmd; struct sja1105_ptp_cmd cmd;
......
...@@ -48,6 +48,10 @@ struct sja1105_tagger_data { ...@@ -48,6 +48,10 @@ struct sja1105_tagger_data {
spinlock_t meta_lock; spinlock_t meta_lock;
unsigned long state; unsigned long state;
u8 ts_id; u8 ts_id;
/* Used on SJA1110 where meta frames are generated only for
* 2-step TX timestamps
*/
struct sk_buff_head skb_txtstamp_queue;
}; };
struct sja1105_skb_cb { struct sja1105_skb_cb {
...@@ -68,25 +72,20 @@ struct sja1105_port { ...@@ -68,25 +72,20 @@ struct sja1105_port {
bool hwts_tx_en; bool hwts_tx_en;
}; };
enum sja1110_meta_tstamp { /* Timestamps are in units of 8 ns clock ticks (equivalent to
SJA1110_META_TSTAMP_TX = 0, * a fixed 125 MHz clock).
SJA1110_META_TSTAMP_RX = 1, */
}; #define SJA1105_TICK_NS 8
#if IS_ENABLED(CONFIG_NET_DSA_SJA1105_PTP)
void sja1110_process_meta_tstamp(struct dsa_switch *ds, int port, u8 ts_id,
enum sja1110_meta_tstamp dir, u64 tstamp);
#else
static inline void sja1110_process_meta_tstamp(struct dsa_switch *ds, int port, static inline s64 ns_to_sja1105_ticks(s64 ns)
u8 ts_id, enum sja1110_meta_tstamp dir,
u64 tstamp)
{ {
return ns / SJA1105_TICK_NS;
} }
#endif /* IS_ENABLED(CONFIG_NET_DSA_SJA1105_PTP) */ static inline s64 sja1105_ticks_to_ns(s64 ticks)
{
return ticks * SJA1105_TICK_NS;
}
#if IS_ENABLED(CONFIG_NET_DSA_SJA1105) #if IS_ENABLED(CONFIG_NET_DSA_SJA1105)
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/dsa/sja1105.h> #include <linux/dsa/sja1105.h>
#include <linux/dsa/8021q.h> #include <linux/dsa/8021q.h>
#include <linux/skbuff.h>
#include <linux/packing.h> #include <linux/packing.h>
#include "dsa_priv.h" #include "dsa_priv.h"
...@@ -53,6 +54,11 @@ ...@@ -53,6 +54,11 @@
#define SJA1110_TX_TRAILER_LEN 4 #define SJA1110_TX_TRAILER_LEN 4
#define SJA1110_MAX_PADDING_LEN 15 #define SJA1110_MAX_PADDING_LEN 15
enum sja1110_meta_tstamp {
SJA1110_META_TSTAMP_TX = 0,
SJA1110_META_TSTAMP_RX = 1,
};
/* 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)
{ {
...@@ -520,6 +526,43 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, ...@@ -520,6 +526,43 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
is_meta); is_meta);
} }
static void sja1110_process_meta_tstamp(struct dsa_switch *ds, int port,
u8 ts_id, enum sja1110_meta_tstamp dir,
u64 tstamp)
{
struct sk_buff *skb, *skb_tmp, *skb_match = NULL;
struct dsa_port *dp = dsa_to_port(ds, port);
struct skb_shared_hwtstamps shwt = {0};
struct sja1105_port *sp = dp->priv;
if (!dsa_port_is_sja1105(dp))
return;
/* We don't care about RX timestamps on the CPU port */
if (dir == SJA1110_META_TSTAMP_RX)
return;
spin_lock(&sp->data->skb_txtstamp_queue.lock);
skb_queue_walk_safe(&sp->data->skb_txtstamp_queue, skb, skb_tmp) {
if (SJA1105_SKB_CB(skb)->ts_id != ts_id)
continue;
__skb_unlink(skb, &sp->data->skb_txtstamp_queue);
skb_match = skb;
break;
}
spin_unlock(&sp->data->skb_txtstamp_queue.lock);
if (WARN_ON(!skb_match))
return;
shwt.hwtstamp = ns_to_ktime(sja1105_ticks_to_ns(tstamp));
skb_complete_tx_timestamp(skb_match, &shwt);
}
static struct sk_buff *sja1110_rcv_meta(struct sk_buff *skb, u16 rx_header) static struct sk_buff *sja1110_rcv_meta(struct sk_buff *skb, u16 rx_header)
{ {
u8 *buf = dsa_etype_header_pos_rx(skb) + SJA1110_HEADER_LEN; u8 *buf = dsa_etype_header_pos_rx(skb) + SJA1110_HEADER_LEN;
......
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