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

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
...@@ -630,30 +630,45 @@ hardware timestamping on it. This is because the SO_TIMESTAMPING API does not ...@@ -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 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. 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 the generic layer, DSA provides the following infrastructure for PTP
in generic code: a BPF classifier (``ptp_classify_raw``) is used to identify timestamping:
PTP event messages (any other packets, including PTP general messages, are not
timestamped), and provides two hooks to drivers: - ``.port_txtstamp()``: a hook called prior to the transmission of
packets with a hardware TX timestamping request from user space.
- ``.port_txtstamp()``: The driver is passed a clone of the timestampable skb This is required for two-step timestamping, since the hardware
to be transmitted, before actually transmitting it. Typically, a switch will timestamp becomes available after the actual MAC transmission, so the
have a PTP TX timestamp register (or sometimes a FIFO) where the timestamp driver must be prepared to correlate the timestamp with the original
becomes available. There may be an IRQ that is raised upon this timestamp's packet so that it can re-enqueue the packet back into the socket's
availability, or the driver might have to poll after invoking error queue. To save the packet for when the timestamp becomes
``dev_queue_xmit()`` towards the host interface. Either way, in the available, the driver can call ``skb_clone_sk`` , save the clone pointer
``.port_txtstamp()`` method, the driver only needs to save the clone for in skb->cb and enqueue a tx skb queue. Typically, a switch will have a
later use (when the timestamp becomes available). Each skb is annotated with PTP TX timestamp register (or sometimes a FIFO) where the timestamp
a pointer to its clone, in ``DSA_SKB_CB(skb)->clone``, to ease the driver's becomes available. In case of a FIFO, the hardware might store
job of keeping track of which clone belongs to which skb. key-value pairs of PTP sequence ID/message type/domain number and the
actual timestamp. To perform the correlation correctly between the
- ``.port_rxtstamp()``: The original (and only) timestampable skb is provided packets in a queue waiting for timestamping and the actual timestamps,
to the driver, for it to annotate it with a timestamp, if that is immediately drivers can use a BPF classifier (``ptp_classify_raw``) to identify
available, or defer to later. On reception, timestamps might either be the PTP transport type, and ``ptp_parse_header`` to interpret the PTP
available in-band (through metadata in the DSA header, or attached in other header fields. There may be an IRQ that is raised upon this
ways to the packet), or out-of-band (through another RX timestamping FIFO). timestamp's availability, or the driver might have to poll after
Deferral on RX is typically necessary when retrieving the timestamp needs a invoking ``dev_queue_xmit()`` towards the host interface.
sleepable context. In that case, it is the responsibility of the DSA driver One-step TX timestamping do not require packet cloning, since there is
to call ``netif_rx_ni()`` on the freshly timestamped skb. 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 3.2.2 Ethernet PHYs
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
......
...@@ -373,30 +373,38 @@ long hellcreek_hwtstamp_work(struct ptp_clock_info *ptp) ...@@ -373,30 +373,38 @@ long hellcreek_hwtstamp_work(struct ptp_clock_info *ptp)
return restart ? 1 : -1; return restart ? 1 : -1;
} }
bool hellcreek_port_txtstamp(struct dsa_switch *ds, int port, void hellcreek_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *clone, unsigned int type) struct sk_buff *skb)
{ {
struct hellcreek *hellcreek = ds->priv; struct hellcreek *hellcreek = ds->priv;
struct hellcreek_port_hwtstamp *ps; struct hellcreek_port_hwtstamp *ps;
struct ptp_header *hdr; struct ptp_header *hdr;
struct sk_buff *clone;
unsigned int type;
ps = &hellcreek->ports[port].port_hwtstamp; ps = &hellcreek->ports[port].port_hwtstamp;
/* Check if the driver is expected to do HW timestamping */ type = ptp_classify_raw(skb);
if (!(skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP)) if (type == PTP_CLASS_NONE)
return false; return;
/* Make sure the message is a PTP message that needs to be timestamped /* 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 * and the interaction with the HW timestamping is enabled. If not, stop
* here * here
*/ */
hdr = hellcreek_should_tstamp(hellcreek, port, clone, type); hdr = hellcreek_should_tstamp(hellcreek, port, skb, type);
if (!hdr) if (!hdr)
return false; return;
clone = skb_clone_sk(skb);
if (!clone)
return;
if (test_and_set_bit_lock(HELLCREEK_HWTSTAMP_TX_IN_PROGRESS, if (test_and_set_bit_lock(HELLCREEK_HWTSTAMP_TX_IN_PROGRESS,
&ps->state)) &ps->state)) {
return false; kfree_skb(clone);
return;
}
ps->tx_skb = clone; ps->tx_skb = clone;
...@@ -406,8 +414,6 @@ bool hellcreek_port_txtstamp(struct dsa_switch *ds, int port, ...@@ -406,8 +414,6 @@ bool hellcreek_port_txtstamp(struct dsa_switch *ds, int port,
ps->tx_tstamp_start = jiffies; ps->tx_tstamp_start = jiffies;
ptp_schedule_worker(hellcreek->ptp_clock, 0); ptp_schedule_worker(hellcreek->ptp_clock, 0);
return true;
} }
bool hellcreek_port_rxtstamp(struct dsa_switch *ds, int port, bool hellcreek_port_rxtstamp(struct dsa_switch *ds, int port,
......
...@@ -44,8 +44,8 @@ int hellcreek_port_hwtstamp_get(struct dsa_switch *ds, int port, ...@@ -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, bool hellcreek_port_rxtstamp(struct dsa_switch *ds, int port,
struct sk_buff *clone, unsigned int type); struct sk_buff *clone, unsigned int type);
bool hellcreek_port_txtstamp(struct dsa_switch *ds, int port, void hellcreek_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *clone, unsigned int type); struct sk_buff *skb);
int hellcreek_get_ts_info(struct dsa_switch *ds, int port, int hellcreek_get_ts_info(struct dsa_switch *ds, int port,
struct ethtool_ts_info *info); struct ethtool_ts_info *info);
......
...@@ -468,30 +468,38 @@ long mv88e6xxx_hwtstamp_work(struct ptp_clock_info *ptp) ...@@ -468,30 +468,38 @@ long mv88e6xxx_hwtstamp_work(struct ptp_clock_info *ptp)
return restart ? 1 : -1; return restart ? 1 : -1;
} }
bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port, void mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *clone, unsigned int type) struct sk_buff *skb)
{ {
struct mv88e6xxx_chip *chip = ds->priv; struct mv88e6xxx_chip *chip = ds->priv;
struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port]; struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port];
struct ptp_header *hdr; struct ptp_header *hdr;
struct sk_buff *clone;
unsigned int type;
if (!(skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP)) type = ptp_classify_raw(skb);
return false; if (type == PTP_CLASS_NONE)
return;
hdr = mv88e6xxx_should_tstamp(chip, port, clone, type); hdr = mv88e6xxx_should_tstamp(chip, port, skb, type);
if (!hdr) if (!hdr)
return false; return;
clone = skb_clone_sk(skb);
if (!clone)
return;
if (test_and_set_bit_lock(MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS, if (test_and_set_bit_lock(MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS,
&ps->state)) &ps->state)) {
return false; kfree_skb(clone);
return;
}
ps->tx_skb = clone; ps->tx_skb = clone;
ps->tx_tstamp_start = jiffies; ps->tx_tstamp_start = jiffies;
ps->tx_seq_id = be16_to_cpu(hdr->sequence_id); ps->tx_seq_id = be16_to_cpu(hdr->sequence_id);
ptp_schedule_worker(chip->ptp_clock, 0); ptp_schedule_worker(chip->ptp_clock, 0);
return true;
} }
int mv88e6165_global_disable(struct mv88e6xxx_chip *chip) int mv88e6165_global_disable(struct mv88e6xxx_chip *chip)
......
...@@ -117,8 +117,8 @@ int mv88e6xxx_port_hwtstamp_get(struct dsa_switch *ds, int port, ...@@ -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, bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port,
struct sk_buff *clone, unsigned int type); struct sk_buff *clone, unsigned int type);
bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port, void mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *clone, unsigned int type); struct sk_buff *skb);
int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port, int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port,
struct ethtool_ts_info *info); struct ethtool_ts_info *info);
...@@ -151,11 +151,9 @@ static inline bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port, ...@@ -151,11 +151,9 @@ static inline bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port,
return false; return false;
} }
static inline bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port, static inline void mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *clone, struct sk_buff *skb)
unsigned int type)
{ {
return false;
} }
static inline int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port, static inline int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port,
......
...@@ -1395,19 +1395,20 @@ static bool felix_rxtstamp(struct dsa_switch *ds, int port, ...@@ -1395,19 +1395,20 @@ static bool felix_rxtstamp(struct dsa_switch *ds, int port,
return false; return false;
} }
static bool felix_txtstamp(struct dsa_switch *ds, int port, static void felix_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *clone, unsigned int type) struct sk_buff *skb)
{ {
struct ocelot *ocelot = ds->priv; 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) && if (!ocelot->ptp)
ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { return;
ocelot_port_add_txtstamp_skb(ocelot, port, clone);
return true;
}
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) static int felix_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
......
...@@ -3137,7 +3137,7 @@ static void sja1105_port_deferred_xmit(struct kthread_work *work) ...@@ -3137,7 +3137,7 @@ static void sja1105_port_deferred_xmit(struct kthread_work *work)
struct sk_buff *skb; struct sk_buff *skb;
while ((skb = skb_dequeue(&sp->xmit_queue)) != NULL) { 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); mutex_lock(&priv->mgmt_lock);
......
...@@ -431,20 +431,24 @@ bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port, ...@@ -431,20 +431,24 @@ bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port,
return true; return true;
} }
/* Called from dsa_skb_tx_timestamp. This callback is just to make DSA clone /* Called from dsa_skb_tx_timestamp. This callback is just to clone
* the skb and have it available in DSA_SKB_CB in the .port_deferred_xmit * the skb and have it available in SJA1105_SKB_CB in the .port_deferred_xmit
* callback, where we will timestamp it synchronously. * callback, where we will timestamp it synchronously.
*/ */
bool sja1105_port_txtstamp(struct dsa_switch *ds, int port, void sja1105_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)
struct sk_buff *skb, unsigned int type)
{ {
struct sja1105_private *priv = ds->priv; struct sja1105_private *priv = ds->priv;
struct sja1105_port *sp = &priv->ports[port]; struct sja1105_port *sp = &priv->ports[port];
struct sk_buff *clone;
if (!sp->hwts_tx_en) 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) static int sja1105_ptp_reset(struct dsa_switch *ds)
......
...@@ -104,8 +104,8 @@ void sja1105_ptp_txtstamp_skb(struct dsa_switch *ds, int slot, ...@@ -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, bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port,
struct sk_buff *skb, unsigned int type); struct sk_buff *skb, unsigned int type);
bool sja1105_port_txtstamp(struct dsa_switch *ds, int port, void sja1105_port_txtstamp(struct dsa_switch *ds, int port,
struct sk_buff *skb, unsigned int type); struct sk_buff *skb);
int sja1105_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr); int sja1105_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
*/ */
#include <linux/dsa/ocelot.h> #include <linux/dsa/ocelot.h>
#include <linux/if_bridge.h> #include <linux/if_bridge.h>
#include <linux/ptp_classify.h>
#include <soc/mscc/ocelot_vcap.h> #include <soc/mscc/ocelot_vcap.h>
#include "ocelot.h" #include "ocelot.h"
#include "ocelot_vcap.h" #include "ocelot_vcap.h"
...@@ -530,22 +531,92 @@ void ocelot_port_disable(struct ocelot *ocelot, int port) ...@@ -530,22 +531,92 @@ void ocelot_port_disable(struct ocelot *ocelot, int port)
} }
EXPORT_SYMBOL(ocelot_port_disable); EXPORT_SYMBOL(ocelot_port_disable);
void ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port, static void ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port,
struct sk_buff *clone) struct sk_buff *clone)
{ {
struct ocelot_port *ocelot_port = ocelot->ports[port]; struct ocelot_port *ocelot_port = ocelot->ports[port];
spin_lock(&ocelot_port->ts_id_lock); spin_lock(&ocelot_port->ts_id_lock);
skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS; skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS;
/* Store timestamp ID in cb[0] of sk_buff */ /* Store timestamp ID in OCELOT_SKB_CB(clone)->ts_id */
clone->cb[0] = ocelot_port->ts_id; OCELOT_SKB_CB(clone)->ts_id = ocelot_port->ts_id;
ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4; ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4;
skb_queue_tail(&ocelot_port->tx_skbs, clone); skb_queue_tail(&ocelot_port->tx_skbs, clone);
spin_unlock(&ocelot_port->ts_id_lock); spin_unlock(&ocelot_port->ts_id_lock);
} }
EXPORT_SYMBOL(ocelot_port_add_txtstamp_skb);
u32 ocelot_ptp_rew_op(struct sk_buff *skb)
{
struct sk_buff *clone = OCELOT_SKB_CB(skb)->clone;
u8 ptp_cmd = OCELOT_SKB_CB(skb)->ptp_cmd;
u32 rew_op = 0;
if (ptp_cmd == IFH_REW_OP_TWO_STEP_PTP && clone) {
rew_op = ptp_cmd;
rew_op |= OCELOT_SKB_CB(clone)->ts_id << 3;
} else if (ptp_cmd == IFH_REW_OP_ORIGIN_PTP) {
rew_op = ptp_cmd;
}
return rew_op;
}
EXPORT_SYMBOL(ocelot_ptp_rew_op);
static bool ocelot_ptp_is_onestep_sync(struct sk_buff *skb)
{
struct ptp_header *hdr;
unsigned int ptp_class;
u8 msgtype, twostep;
ptp_class = ptp_classify_raw(skb);
if (ptp_class == PTP_CLASS_NONE)
return false;
hdr = ptp_parse_header(skb, ptp_class);
if (!hdr)
return false;
msgtype = ptp_get_msgtype(hdr, ptp_class);
twostep = hdr->flag_field[0] & 0x2;
if (msgtype == PTP_MSGTYPE_SYNC && twostep == 0)
return true;
return false;
}
int ocelot_port_txtstamp_request(struct ocelot *ocelot, int port,
struct sk_buff *skb,
struct sk_buff **clone)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
u8 ptp_cmd = ocelot_port->ptp_cmd;
/* Store ptp_cmd in OCELOT_SKB_CB(skb)->ptp_cmd */
if (ptp_cmd == IFH_REW_OP_ORIGIN_PTP) {
if (ocelot_ptp_is_onestep_sync(skb)) {
OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd;
return 0;
}
/* Fall back to two-step timestamping */
ptp_cmd = IFH_REW_OP_TWO_STEP_PTP;
}
if (ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
*clone = skb_clone_sk(skb);
if (!(*clone))
return -ENOMEM;
ocelot_port_add_txtstamp_skb(ocelot, port, *clone);
OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd;
}
return 0;
}
EXPORT_SYMBOL(ocelot_port_txtstamp_request);
static void ocelot_get_hwtimestamp(struct ocelot *ocelot, static void ocelot_get_hwtimestamp(struct ocelot *ocelot,
struct timespec64 *ts) struct timespec64 *ts)
...@@ -604,7 +675,7 @@ void ocelot_get_txtstamp(struct ocelot *ocelot) ...@@ -604,7 +675,7 @@ void ocelot_get_txtstamp(struct ocelot *ocelot)
spin_lock_irqsave(&port->tx_skbs.lock, flags); spin_lock_irqsave(&port->tx_skbs.lock, flags);
skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) { skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) {
if (skb->cb[0] != id) if (OCELOT_SKB_CB(skb)->ts_id != id)
continue; continue;
__skb_unlink(skb, &port->tx_skbs); __skb_unlink(skb, &port->tx_skbs);
skb_match = skb; skb_match = skb;
......
...@@ -507,21 +507,17 @@ static netdev_tx_t ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -507,21 +507,17 @@ static netdev_tx_t ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
/* Check if timestamping is needed */ /* Check if timestamping is needed */
if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
rew_op = ocelot_port->ptp_cmd; struct sk_buff *clone = NULL;
if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { if (ocelot_port_txtstamp_request(ocelot, port, skb, &clone)) {
struct sk_buff *clone; kfree_skb(skb);
return NETDEV_TX_OK;
clone = skb_clone_sk(skb); }
if (!clone) {
kfree_skb(skb);
return NETDEV_TX_OK;
}
ocelot_port_add_txtstamp_skb(ocelot, port, clone); if (clone)
OCELOT_SKB_CB(skb)->clone = clone;
rew_op |= clone->cb[0] << 3; rew_op = ocelot_ptp_rew_op(skb);
}
} }
ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb); ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb);
......
...@@ -47,11 +47,12 @@ struct sja1105_tagger_data { ...@@ -47,11 +47,12 @@ struct sja1105_tagger_data {
}; };
struct sja1105_skb_cb { struct sja1105_skb_cb {
struct sk_buff *clone;
u32 meta_tstamp; u32 meta_tstamp;
}; };
#define SJA1105_SKB_CB(skb) \ #define SJA1105_SKB_CB(skb) \
((struct sja1105_skb_cb *)DSA_SKB_CB_PRIV(skb)) ((struct sja1105_skb_cb *)((skb)->cb))
struct sja1105_port { struct sja1105_port {
u16 subvlan_map[DSA_8021Q_N_SUBVLAN]; u16 subvlan_map[DSA_8021Q_N_SUBVLAN];
......
...@@ -117,20 +117,6 @@ struct dsa_netdevice_ops { ...@@ -117,20 +117,6 @@ struct dsa_netdevice_ops {
#define MODULE_ALIAS_DSA_TAG_DRIVER(__proto) \ #define MODULE_ALIAS_DSA_TAG_DRIVER(__proto) \
MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS __stringify(__proto##_VALUE)) MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS __stringify(__proto##_VALUE))
struct dsa_skb_cb {
struct sk_buff *clone;
};
struct __dsa_skb_cb {
struct dsa_skb_cb cb;
u8 priv[48 - sizeof(struct dsa_skb_cb)];
};
#define DSA_SKB_CB(skb) ((struct dsa_skb_cb *)((skb)->cb))
#define DSA_SKB_CB_PRIV(skb) \
((void *)(skb)->cb + offsetof(struct __dsa_skb_cb, priv))
struct dsa_switch_tree { struct dsa_switch_tree {
struct list_head list; struct list_head list;
...@@ -740,8 +726,8 @@ struct dsa_switch_ops { ...@@ -740,8 +726,8 @@ struct dsa_switch_ops {
struct ifreq *ifr); struct ifreq *ifr);
int (*port_hwtstamp_set)(struct dsa_switch *ds, int port, int (*port_hwtstamp_set)(struct dsa_switch *ds, int port,
struct ifreq *ifr); struct ifreq *ifr);
bool (*port_txtstamp)(struct dsa_switch *ds, int port, void (*port_txtstamp)(struct dsa_switch *ds, int port,
struct sk_buff *clone, unsigned int type); struct sk_buff *skb);
bool (*port_rxtstamp)(struct dsa_switch *ds, int port, bool (*port_rxtstamp)(struct dsa_switch *ds, int port,
struct sk_buff *skb, unsigned int type); struct sk_buff *skb, unsigned int type);
......
...@@ -689,6 +689,15 @@ struct ocelot_policer { ...@@ -689,6 +689,15 @@ struct ocelot_policer {
u32 burst; /* bytes */ u32 burst; /* bytes */
}; };
struct ocelot_skb_cb {
struct sk_buff *clone;
u8 ptp_cmd;
u8 ts_id;
};
#define OCELOT_SKB_CB(skb) \
((struct ocelot_skb_cb *)((skb)->cb))
#define ocelot_read_ix(ocelot, reg, gi, ri) __ocelot_read_ix(ocelot, reg, reg##_GSZ * (gi) + reg##_RSZ * (ri)) #define ocelot_read_ix(ocelot, reg, gi, ri) __ocelot_read_ix(ocelot, reg, reg##_GSZ * (gi) + reg##_RSZ * (ri))
#define ocelot_read_gix(ocelot, reg, gi) __ocelot_read_ix(ocelot, reg, reg##_GSZ * (gi)) #define ocelot_read_gix(ocelot, reg, gi) __ocelot_read_ix(ocelot, reg, reg##_GSZ * (gi))
#define ocelot_read_rix(ocelot, reg, ri) __ocelot_read_ix(ocelot, reg, reg##_RSZ * (ri)) #define ocelot_read_rix(ocelot, reg, ri) __ocelot_read_ix(ocelot, reg, reg##_RSZ * (ri))
...@@ -740,15 +749,16 @@ u32 __ocelot_target_read_ix(struct ocelot *ocelot, enum ocelot_target target, ...@@ -740,15 +749,16 @@ u32 __ocelot_target_read_ix(struct ocelot *ocelot, enum ocelot_target target,
void __ocelot_target_write_ix(struct ocelot *ocelot, enum ocelot_target target, void __ocelot_target_write_ix(struct ocelot *ocelot, enum ocelot_target target,
u32 val, u32 reg, u32 offset); u32 val, u32 reg, u32 offset);
/* Packet I/O */
#if IS_ENABLED(CONFIG_MSCC_OCELOT_SWITCH_LIB) #if IS_ENABLED(CONFIG_MSCC_OCELOT_SWITCH_LIB)
/* Packet I/O */
bool ocelot_can_inject(struct ocelot *ocelot, int grp); bool ocelot_can_inject(struct ocelot *ocelot, int grp);
void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp, void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
u32 rew_op, struct sk_buff *skb); u32 rew_op, struct sk_buff *skb);
int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **skb); int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **skb);
void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp); void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp);
u32 ocelot_ptp_rew_op(struct sk_buff *skb);
#else #else
static inline bool ocelot_can_inject(struct ocelot *ocelot, int grp) static inline bool ocelot_can_inject(struct ocelot *ocelot, int grp)
...@@ -772,6 +782,10 @@ static inline void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp) ...@@ -772,6 +782,10 @@ static inline void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp)
{ {
} }
static inline u32 ocelot_ptp_rew_op(struct sk_buff *skb)
{
return 0;
}
#endif #endif
/* Hardware initialization */ /* Hardware initialization */
...@@ -820,8 +834,9 @@ int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid, ...@@ -820,8 +834,9 @@ int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid); int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid);
int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr); int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr);
int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr); int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr);
void ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port, int ocelot_port_txtstamp_request(struct ocelot *ocelot, int port,
struct sk_buff *clone); struct sk_buff *skb,
struct sk_buff **clone);
void ocelot_get_txtstamp(struct ocelot *ocelot); void ocelot_get_txtstamp(struct ocelot *ocelot);
void ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu); void ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu);
int ocelot_get_max_mtu(struct ocelot *ocelot, int port); int ocelot_get_max_mtu(struct ocelot *ocelot, int port);
......
...@@ -111,6 +111,8 @@ config NET_DSA_TAG_RTL4_A ...@@ -111,6 +111,8 @@ config NET_DSA_TAG_RTL4_A
config NET_DSA_TAG_OCELOT config NET_DSA_TAG_OCELOT
tristate "Tag driver for Ocelot family of switches, using NPI port" tristate "Tag driver for Ocelot family of switches, using NPI port"
depends on MSCC_OCELOT_SWITCH_LIB || \
(MSCC_OCELOT_SWITCH_LIB=n && COMPILE_TEST)
select PACKING select PACKING
help help
Say Y or M if you want to enable NPI tagging for the Ocelot switches Say Y or M if you want to enable NPI tagging for the Ocelot switches
......
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
#include <linux/if_bridge.h> #include <linux/if_bridge.h>
#include <linux/if_hsr.h> #include <linux/if_hsr.h>
#include <linux/netpoll.h> #include <linux/netpoll.h>
#include <linux/ptp_classify.h>
#include "dsa_priv.h" #include "dsa_priv.h"
...@@ -556,26 +555,14 @@ static void dsa_skb_tx_timestamp(struct dsa_slave_priv *p, ...@@ -556,26 +555,14 @@ static void dsa_skb_tx_timestamp(struct dsa_slave_priv *p,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct dsa_switch *ds = p->dp->ds; struct dsa_switch *ds = p->dp->ds;
struct sk_buff *clone;
unsigned int type;
type = ptp_classify_raw(skb); if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))
if (type == PTP_CLASS_NONE)
return; return;
if (!ds->ops->port_txtstamp) if (!ds->ops->port_txtstamp)
return; return;
clone = skb_clone_sk(skb); ds->ops->port_txtstamp(ds, p->dp->index, skb);
if (!clone)
return;
if (ds->ops->port_txtstamp(ds, p->dp->index, clone, type)) {
DSA_SKB_CB(skb)->clone = clone;
return;
}
kfree_skb(clone);
} }
netdev_tx_t dsa_enqueue_skb(struct sk_buff *skb, struct net_device *dev) netdev_tx_t dsa_enqueue_skb(struct sk_buff *skb, struct net_device *dev)
...@@ -627,11 +614,9 @@ static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -627,11 +614,9 @@ static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
dev_sw_netstats_tx_add(dev, 1, skb->len); dev_sw_netstats_tx_add(dev, 1, skb->len);
DSA_SKB_CB(skb)->clone = NULL; memset(skb->cb, 0, sizeof(skb->cb));
/* Identify PTP protocol packets, clone them, and pass them to the /* Handle tx timestamp if any */
* switch driver
*/
dsa_skb_tx_timestamp(p, skb); dsa_skb_tx_timestamp(p, skb);
if (dsa_realloc_skb(skb, dev)) { if (dsa_realloc_skb(skb, dev)) {
......
...@@ -5,33 +5,14 @@ ...@@ -5,33 +5,14 @@
#include <soc/mscc/ocelot.h> #include <soc/mscc/ocelot.h>
#include "dsa_priv.h" #include "dsa_priv.h"
static void ocelot_xmit_ptp(struct dsa_port *dp, void *injection,
struct sk_buff *clone)
{
struct ocelot *ocelot = dp->ds->priv;
struct ocelot_port *ocelot_port;
u64 rew_op;
ocelot_port = ocelot->ports[dp->index];
rew_op = ocelot_port->ptp_cmd;
/* Retrieve timestamp ID populated inside skb->cb[0] of the
* clone by ocelot_port_add_txtstamp_skb
*/
if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
rew_op |= clone->cb[0] << 3;
ocelot_ifh_set_rew_op(injection, rew_op);
}
static void ocelot_xmit_common(struct sk_buff *skb, struct net_device *netdev, static void ocelot_xmit_common(struct sk_buff *skb, struct net_device *netdev,
__be32 ifh_prefix, void **ifh) __be32 ifh_prefix, void **ifh)
{ {
struct dsa_port *dp = dsa_slave_to_port(netdev); struct dsa_port *dp = dsa_slave_to_port(netdev);
struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
struct dsa_switch *ds = dp->ds; struct dsa_switch *ds = dp->ds;
void *injection; void *injection;
__be32 *prefix; __be32 *prefix;
u32 rew_op = 0;
injection = skb_push(skb, OCELOT_TAG_LEN); injection = skb_push(skb, OCELOT_TAG_LEN);
prefix = skb_push(skb, OCELOT_SHORT_PREFIX_LEN); prefix = skb_push(skb, OCELOT_SHORT_PREFIX_LEN);
...@@ -42,9 +23,9 @@ static void ocelot_xmit_common(struct sk_buff *skb, struct net_device *netdev, ...@@ -42,9 +23,9 @@ static void ocelot_xmit_common(struct sk_buff *skb, struct net_device *netdev,
ocelot_ifh_set_src(injection, ds->num_ports); ocelot_ifh_set_src(injection, ds->num_ports);
ocelot_ifh_set_qos_class(injection, skb->priority); ocelot_ifh_set_qos_class(injection, skb->priority);
/* TX timestamping was requested */ rew_op = ocelot_ptp_rew_op(skb);
if (clone) if (rew_op)
ocelot_xmit_ptp(dp, injection, clone); ocelot_ifh_set_rew_op(injection, rew_op);
*ifh = injection; *ifh = injection;
} }
......
...@@ -13,32 +13,6 @@ ...@@ -13,32 +13,6 @@
#include <soc/mscc/ocelot_ptp.h> #include <soc/mscc/ocelot_ptp.h>
#include "dsa_priv.h" #include "dsa_priv.h"
static struct sk_buff *ocelot_xmit_ptp(struct dsa_port *dp,
struct sk_buff *skb,
struct sk_buff *clone)
{
struct ocelot *ocelot = dp->ds->priv;
struct ocelot_port *ocelot_port;
int port = dp->index;
u32 rew_op;
if (!ocelot_can_inject(ocelot, 0))
return NULL;
ocelot_port = ocelot->ports[port];
rew_op = ocelot_port->ptp_cmd;
/* Retrieve timestamp ID populated inside skb->cb[0] of the
* clone by ocelot_port_add_txtstamp_skb
*/
if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
rew_op |= clone->cb[0] << 3;
ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb);
return NULL;
}
static struct sk_buff *ocelot_xmit(struct sk_buff *skb, static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
struct net_device *netdev) struct net_device *netdev)
{ {
...@@ -46,11 +20,18 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb, ...@@ -46,11 +20,18 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
u16 tx_vid = dsa_8021q_tx_vid(dp->ds, dp->index); u16 tx_vid = dsa_8021q_tx_vid(dp->ds, dp->index);
u16 queue_mapping = skb_get_queue_mapping(skb); u16 queue_mapping = skb_get_queue_mapping(skb);
u8 pcp = netdev_txq_to_tc(netdev, queue_mapping); u8 pcp = netdev_txq_to_tc(netdev, queue_mapping);
struct sk_buff *clone = DSA_SKB_CB(skb)->clone; struct ocelot *ocelot = dp->ds->priv;
int port = dp->index;
u32 rew_op = 0;
rew_op = ocelot_ptp_rew_op(skb);
if (rew_op) {
if (!ocelot_can_inject(ocelot, 0))
return NULL;
/* TX timestamping was requested, so inject through MMIO */ ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb);
if (clone) return NULL;
return ocelot_xmit_ptp(dp, skb, clone); }
return dsa_8021q_xmit(skb, netdev, ETH_P_8021Q, return dsa_8021q_xmit(skb, netdev, ETH_P_8021Q,
((pcp << VLAN_PRIO_SHIFT) | tx_vid)); ((pcp << VLAN_PRIO_SHIFT) | tx_vid));
......
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