Skip to content

Commit

Permalink
Merge branch 'ocelot-ptp'
Browse files Browse the repository at this point in the history
Yangbo Lu says:

====================
Support Ocelot PTP Sync one-step timestamping

This patch-set is to support Ocelot PTP Sync one-step timestamping.
Actually before that, this patch-set cleans up and optimizes the
DSA slave tx timestamp request handling process.

Changes for v2:
	- Split tx timestamp optimization patch.
	- Updated doc patch.
	- Freed skb->cb usage in dsa core driver, and moved to device
	  drivers.
	- Other minor fixes.
Changes for v3:
	- Switched sequence of patch #3 and #4 with rebasing to fix build.
	- Replaced hard coded 48 of memset(skb->cb, 0, 48) with sizeof().
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Apr 27, 2021
2 parents 23c9c2b + 39e5308 commit aae0fda
Show file tree
Hide file tree
Showing 18 changed files with 230 additions and 180 deletions.
63 changes: 39 additions & 24 deletions Documentation/networking/timestamping.rst
Original file line number Diff line number Diff line change
Expand Up @@ -630,30 +630,45 @@ hardware timestamping on it. This is because the SO_TIMESTAMPING API does not
allow the delivery of multiple hardware timestamps for the same packet, so
anybody else except for the DSA switch port must be prevented from doing so.

In code, DSA provides for most of the infrastructure for timestamping already,
in generic code: a BPF classifier (``ptp_classify_raw``) is used to identify
PTP event messages (any other packets, including PTP general messages, are not
timestamped), and provides two hooks to drivers:

- ``.port_txtstamp()``: The driver is passed a clone of the timestampable skb
to be transmitted, before actually transmitting it. Typically, a switch will
have a PTP TX timestamp register (or sometimes a FIFO) where the timestamp
becomes available. There may be an IRQ that is raised upon this timestamp's
availability, or the driver might have to poll after invoking
``dev_queue_xmit()`` towards the host interface. Either way, in the
``.port_txtstamp()`` method, the driver only needs to save the clone for
later use (when the timestamp becomes available). Each skb is annotated with
a pointer to its clone, in ``DSA_SKB_CB(skb)->clone``, to ease the driver's
job of keeping track of which clone belongs to which skb.

- ``.port_rxtstamp()``: The original (and only) timestampable skb is provided
to the driver, for it to annotate it with a timestamp, if that is immediately
available, or defer to later. On reception, timestamps might either be
available in-band (through metadata in the DSA header, or attached in other
ways to the packet), or out-of-band (through another RX timestamping FIFO).
Deferral on RX is typically necessary when retrieving the timestamp needs a
sleepable context. In that case, it is the responsibility of the DSA driver
to call ``netif_rx_ni()`` on the freshly timestamped skb.
In the generic layer, DSA provides the following infrastructure for PTP
timestamping:

- ``.port_txtstamp()``: a hook called prior to the transmission of
packets with a hardware TX timestamping request from user space.
This is required for two-step timestamping, since the hardware
timestamp becomes available after the actual MAC transmission, so the
driver must be prepared to correlate the timestamp with the original
packet so that it can re-enqueue the packet back into the socket's
error queue. To save the packet for when the timestamp becomes
available, the driver can call ``skb_clone_sk`` , save the clone pointer
in skb->cb and enqueue a tx skb queue. Typically, a switch will have a
PTP TX timestamp register (or sometimes a FIFO) where the timestamp
becomes available. In case of a FIFO, the hardware might store
key-value pairs of PTP sequence ID/message type/domain number and the
actual timestamp. To perform the correlation correctly between the
packets in a queue waiting for timestamping and the actual timestamps,
drivers can use a BPF classifier (``ptp_classify_raw``) to identify
the PTP transport type, and ``ptp_parse_header`` to interpret the PTP
header fields. There may be an IRQ that is raised upon this
timestamp's availability, or the driver might have to poll after
invoking ``dev_queue_xmit()`` towards the host interface.
One-step TX timestamping do not require packet cloning, since there is
no follow-up message required by the PTP protocol (because the
TX timestamp is embedded into the packet by the MAC), and therefore
user space does not expect the packet annotated with the TX timestamp
to be re-enqueued into its socket's error queue.

- ``.port_rxtstamp()``: On RX, the BPF classifier is run by DSA to
identify PTP event messages (any other packets, including PTP general
messages, are not timestamped). The original (and only) timestampable
skb is provided to the driver, for it to annotate it with a timestamp,
if that is immediately available, or defer to later. On reception,
timestamps might either be available in-band (through metadata in the
DSA header, or attached in other ways to the packet), or out-of-band
(through another RX timestamping FIFO). Deferral on RX is typically
necessary when retrieving the timestamp needs a sleepable context. In
that case, it is the responsibility of the DSA driver to call
``netif_rx_ni()`` on the freshly timestamped skb.

3.2.2 Ethernet PHYs
^^^^^^^^^^^^^^^^^^^
Expand Down
28 changes: 17 additions & 11 deletions drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c
Original file line number Diff line number Diff line change
Expand Up @@ -373,30 +373,38 @@ long hellcreek_hwtstamp_work(struct ptp_clock_info *ptp)
return restart ? 1 : -1;
}

bool hellcreek_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *clone, unsigned int type)
void hellcreek_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *skb)
{
struct hellcreek *hellcreek = ds->priv;
struct hellcreek_port_hwtstamp *ps;
struct ptp_header *hdr;
struct sk_buff *clone;
unsigned int type;

ps = &hellcreek->ports[port].port_hwtstamp;

/* Check if the driver is expected to do HW timestamping */
if (!(skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP))
return false;
type = ptp_classify_raw(skb);
if (type == PTP_CLASS_NONE)
return;

/* Make sure the message is a PTP message that needs to be timestamped
* and the interaction with the HW timestamping is enabled. If not, stop
* here
*/
hdr = hellcreek_should_tstamp(hellcreek, port, clone, type);
hdr = hellcreek_should_tstamp(hellcreek, port, skb, type);
if (!hdr)
return false;
return;

clone = skb_clone_sk(skb);
if (!clone)
return;

if (test_and_set_bit_lock(HELLCREEK_HWTSTAMP_TX_IN_PROGRESS,
&ps->state))
return false;
&ps->state)) {
kfree_skb(clone);
return;
}

ps->tx_skb = clone;

Expand All @@ -406,8 +414,6 @@ bool hellcreek_port_txtstamp(struct dsa_switch *ds, int port,
ps->tx_tstamp_start = jiffies;

ptp_schedule_worker(hellcreek->ptp_clock, 0);

return true;
}

bool hellcreek_port_rxtstamp(struct dsa_switch *ds, int port,
Expand Down
4 changes: 2 additions & 2 deletions drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ int hellcreek_port_hwtstamp_get(struct dsa_switch *ds, int port,

bool hellcreek_port_rxtstamp(struct dsa_switch *ds, int port,
struct sk_buff *clone, unsigned int type);
bool hellcreek_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *clone, unsigned int type);
void hellcreek_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *skb);

int hellcreek_get_ts_info(struct dsa_switch *ds, int port,
struct ethtool_ts_info *info);
Expand Down
26 changes: 17 additions & 9 deletions drivers/net/dsa/mv88e6xxx/hwtstamp.c
Original file line number Diff line number Diff line change
Expand Up @@ -468,30 +468,38 @@ long mv88e6xxx_hwtstamp_work(struct ptp_clock_info *ptp)
return restart ? 1 : -1;
}

bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *clone, unsigned int type)
void mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *skb)
{
struct mv88e6xxx_chip *chip = ds->priv;
struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port];
struct ptp_header *hdr;
struct sk_buff *clone;
unsigned int type;

if (!(skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP))
return false;
type = ptp_classify_raw(skb);
if (type == PTP_CLASS_NONE)
return;

hdr = mv88e6xxx_should_tstamp(chip, port, clone, type);
hdr = mv88e6xxx_should_tstamp(chip, port, skb, type);
if (!hdr)
return false;
return;

clone = skb_clone_sk(skb);
if (!clone)
return;

if (test_and_set_bit_lock(MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS,
&ps->state))
return false;
&ps->state)) {
kfree_skb(clone);
return;
}

ps->tx_skb = clone;
ps->tx_tstamp_start = jiffies;
ps->tx_seq_id = be16_to_cpu(hdr->sequence_id);

ptp_schedule_worker(chip->ptp_clock, 0);
return true;
}

int mv88e6165_global_disable(struct mv88e6xxx_chip *chip)
Expand Down
10 changes: 4 additions & 6 deletions drivers/net/dsa/mv88e6xxx/hwtstamp.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ int mv88e6xxx_port_hwtstamp_get(struct dsa_switch *ds, int port,

bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port,
struct sk_buff *clone, unsigned int type);
bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *clone, unsigned int type);
void mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *skb);

int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port,
struct ethtool_ts_info *info);
Expand Down Expand Up @@ -151,11 +151,9 @@ static inline bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port,
return false;
}

static inline bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *clone,
unsigned int type)
static inline void mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *skb)
{
return false;
}

static inline int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port,
Expand Down
19 changes: 10 additions & 9 deletions drivers/net/dsa/ocelot/felix.c
Original file line number Diff line number Diff line change
Expand Up @@ -1395,19 +1395,20 @@ static bool felix_rxtstamp(struct dsa_switch *ds, int port,
return false;
}

static bool felix_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *clone, unsigned int type)
static void felix_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *skb)
{
struct ocelot *ocelot = ds->priv;
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct sk_buff *clone = NULL;

if (ocelot->ptp && (skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP) &&
ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
ocelot_port_add_txtstamp_skb(ocelot, port, clone);
return true;
}
if (!ocelot->ptp)
return;

return false;
if (ocelot_port_txtstamp_request(ocelot, port, skb, &clone))
return;

if (clone)
OCELOT_SKB_CB(skb)->clone = clone;
}

static int felix_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/dsa/sja1105/sja1105_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -3137,7 +3137,7 @@ static void sja1105_port_deferred_xmit(struct kthread_work *work)
struct sk_buff *skb;

while ((skb = skb_dequeue(&sp->xmit_queue)) != NULL) {
struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
struct sk_buff *clone = SJA1105_SKB_CB(skb)->clone;

mutex_lock(&priv->mgmt_lock);

Expand Down
16 changes: 10 additions & 6 deletions drivers/net/dsa/sja1105/sja1105_ptp.c
Original file line number Diff line number Diff line change
Expand Up @@ -431,20 +431,24 @@ bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port,
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
/* Called from dsa_skb_tx_timestamp. This callback is just to clone
* the skb and have it available in SJA1105_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)
void sja1105_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)
{
struct sja1105_private *priv = ds->priv;
struct sja1105_port *sp = &priv->ports[port];
struct sk_buff *clone;

if (!sp->hwts_tx_en)
return false;
return;

return true;
clone = skb_clone_sk(skb);
if (!clone)
return;

SJA1105_SKB_CB(skb)->clone = clone;
}

static int sja1105_ptp_reset(struct dsa_switch *ds)
Expand Down
4 changes: 2 additions & 2 deletions drivers/net/dsa/sja1105/sja1105_ptp.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ void sja1105_ptp_txtstamp_skb(struct dsa_switch *ds, int slot,
bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port,
struct sk_buff *skb, unsigned int type);

bool sja1105_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *skb, unsigned int type);
void sja1105_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *skb);

int sja1105_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr);

Expand Down
Loading

0 comments on commit aae0fda

Please sign in to comment.