net: dsa: sja1105: Add logic for TX timestamping
On TX, timestamping is performed synchronously from the port_deferred_xmit worker thread. In management routes, the switch is requested to take egress timestamps (again partial), which are reconstructed and appended to a clone of the skb that was just sent. The cloning is done by DSA and we retrieve the pointer from the structure that DSA keeps in skb->cb. Then these clones are enqueued to the socket's error queue for application-level processing. Signed-off-by: Vladimir Oltean <olteanv@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
bb77f36ac2
commit
47ed985e97
@@ -1565,7 +1565,7 @@ static int sja1105_setup(struct dsa_switch *ds)
|
||||
}
|
||||
|
||||
static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot,
|
||||
struct sk_buff *skb)
|
||||
struct sk_buff *skb, bool takets)
|
||||
{
|
||||
struct sja1105_mgmt_entry mgmt_route = {0};
|
||||
struct sja1105_private *priv = ds->priv;
|
||||
@@ -1578,6 +1578,8 @@ static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot,
|
||||
mgmt_route.macaddr = ether_addr_to_u64(hdr->h_dest);
|
||||
mgmt_route.destports = BIT(port);
|
||||
mgmt_route.enfport = 1;
|
||||
mgmt_route.tsreg = 0;
|
||||
mgmt_route.takets = takets;
|
||||
|
||||
rc = sja1105_dynamic_config_write(priv, BLK_IDX_MGMT_ROUTE,
|
||||
slot, &mgmt_route, true);
|
||||
@@ -1629,7 +1631,11 @@ static netdev_tx_t sja1105_port_deferred_xmit(struct dsa_switch *ds, int port,
|
||||
{
|
||||
struct sja1105_private *priv = ds->priv;
|
||||
struct sja1105_port *sp = &priv->ports[port];
|
||||
struct skb_shared_hwtstamps shwt = {0};
|
||||
int slot = sp->mgmt_slot;
|
||||
struct sk_buff *clone;
|
||||
u64 now, ts;
|
||||
int rc;
|
||||
|
||||
/* The tragic fact about the switch having 4x2 slots for installing
|
||||
* management routes is that all of them except one are actually
|
||||
@@ -1647,8 +1653,36 @@ static netdev_tx_t sja1105_port_deferred_xmit(struct dsa_switch *ds, int port,
|
||||
*/
|
||||
mutex_lock(&priv->mgmt_lock);
|
||||
|
||||
sja1105_mgmt_xmit(ds, port, slot, skb);
|
||||
/* The clone, if there, was made by dsa_skb_tx_timestamp */
|
||||
clone = DSA_SKB_CB(skb)->clone;
|
||||
|
||||
sja1105_mgmt_xmit(ds, port, slot, skb, !!clone);
|
||||
|
||||
if (!clone)
|
||||
goto out;
|
||||
|
||||
skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS;
|
||||
|
||||
mutex_lock(&priv->ptp_lock);
|
||||
|
||||
now = priv->tstamp_cc.read(&priv->tstamp_cc);
|
||||
|
||||
rc = sja1105_ptpegr_ts_poll(priv, slot, &ts);
|
||||
if (rc < 0) {
|
||||
dev_err(ds->dev, "xmit: timed out polling for tstamp\n");
|
||||
kfree_skb(clone);
|
||||
goto out_unlock_ptp;
|
||||
}
|
||||
|
||||
ts = sja1105_tstamp_reconstruct(priv, now, ts);
|
||||
ts = timecounter_cyc2time(&priv->tstamp_tc, ts);
|
||||
|
||||
shwt.hwtstamp = ns_to_ktime(ts);
|
||||
skb_complete_tx_timestamp(clone, &shwt);
|
||||
|
||||
out_unlock_ptp:
|
||||
mutex_unlock(&priv->ptp_lock);
|
||||
out:
|
||||
mutex_unlock(&priv->mgmt_lock);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
@@ -1677,6 +1711,22 @@ static int sja1105_set_ageing_time(struct dsa_switch *ds,
|
||||
return sja1105_static_config_reload(priv);
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
bool sja1105_port_txtstamp(struct dsa_switch *ds, int port,
|
||||
struct sk_buff *skb, unsigned int type)
|
||||
{
|
||||
struct sja1105_private *priv = ds->priv;
|
||||
struct sja1105_port *sp = &priv->ports[port];
|
||||
|
||||
if (!sp->hwts_tx_en)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct dsa_switch_ops sja1105_switch_ops = {
|
||||
.get_tag_protocol = sja1105_get_tag_protocol,
|
||||
.setup = sja1105_setup,
|
||||
@@ -1701,6 +1751,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_txtstamp = sja1105_port_txtstamp,
|
||||
};
|
||||
|
||||
static int sja1105_check_device_id(struct sja1105_private *priv)
|
||||
|
||||
Reference in New Issue
Block a user