Commit aae0fdac authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'ocelot-ptp'



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: default avatarDavid S. Miller <davem@davemloft.net>
parents 23c9c2b3 39e5308b
Loading
Loading
Loading
Loading
+39 −24
Original line number Diff line number Diff line
@@ -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
^^^^^^^^^^^^^^^^^^^
+17 −11
Original line number Diff line number Diff line
@@ -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;

@@ -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,
+2 −2
Original line number Diff line number Diff line
@@ -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);
+17 −9
Original line number Diff line number Diff line
@@ -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)
+4 −6
Original line number Diff line number Diff line
@@ -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);
@@ -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,
Loading