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:
Vladimir Oltean
2019-06-08 15:04:35 +03:00
committed by David S. Miller
parent bb77f36ac2
commit 47ed985e97
6 changed files with 201 additions and 2 deletions

View File

@@ -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)