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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
aae0fdac00
@ -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:
|
||||
In the generic layer, DSA provides the following infrastructure for PTP
|
||||
timestamping:
|
||||
|
||||
- ``.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_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()``: 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.
|
||||
- ``.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
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
*/
|
||||
#include <linux/dsa/ocelot.h>
|
||||
#include <linux/if_bridge.h>
|
||||
#include <linux/ptp_classify.h>
|
||||
#include <soc/mscc/ocelot_vcap.h>
|
||||
#include "ocelot.h"
|
||||
#include "ocelot_vcap.h"
|
||||
@ -530,22 +531,92 @@ void ocelot_port_disable(struct ocelot *ocelot, int port)
|
||||
}
|
||||
EXPORT_SYMBOL(ocelot_port_disable);
|
||||
|
||||
void ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port,
|
||||
struct sk_buff *clone)
|
||||
static void ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port,
|
||||
struct sk_buff *clone)
|
||||
{
|
||||
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
||||
|
||||
spin_lock(&ocelot_port->ts_id_lock);
|
||||
|
||||
skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS;
|
||||
/* Store timestamp ID in cb[0] of sk_buff */
|
||||
clone->cb[0] = ocelot_port->ts_id;
|
||||
/* Store timestamp ID in OCELOT_SKB_CB(clone)->ts_id */
|
||||
OCELOT_SKB_CB(clone)->ts_id = ocelot_port->ts_id;
|
||||
ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4;
|
||||
skb_queue_tail(&ocelot_port->tx_skbs, clone);
|
||||
|
||||
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,
|
||||
struct timespec64 *ts)
|
||||
@ -604,7 +675,7 @@ void ocelot_get_txtstamp(struct ocelot *ocelot)
|
||||
spin_lock_irqsave(&port->tx_skbs.lock, flags);
|
||||
|
||||
skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) {
|
||||
if (skb->cb[0] != id)
|
||||
if (OCELOT_SKB_CB(skb)->ts_id != id)
|
||||
continue;
|
||||
__skb_unlink(skb, &port->tx_skbs);
|
||||
skb_match = skb;
|
||||
|
@ -507,21 +507,17 @@ static netdev_tx_t ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
|
||||
/* Check if timestamping is needed */
|
||||
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) {
|
||||
struct sk_buff *clone;
|
||||
|
||||
clone = skb_clone_sk(skb);
|
||||
if (!clone) {
|
||||
kfree_skb(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
ocelot_port_add_txtstamp_skb(ocelot, port, clone);
|
||||
|
||||
rew_op |= clone->cb[0] << 3;
|
||||
if (ocelot_port_txtstamp_request(ocelot, port, skb, &clone)) {
|
||||
kfree_skb(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
if (clone)
|
||||
OCELOT_SKB_CB(skb)->clone = clone;
|
||||
|
||||
rew_op = ocelot_ptp_rew_op(skb);
|
||||
}
|
||||
|
||||
ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb);
|
||||
|
@ -47,11 +47,12 @@ struct sja1105_tagger_data {
|
||||
};
|
||||
|
||||
struct sja1105_skb_cb {
|
||||
struct sk_buff *clone;
|
||||
u32 meta_tstamp;
|
||||
};
|
||||
|
||||
#define SJA1105_SKB_CB(skb) \
|
||||
((struct sja1105_skb_cb *)DSA_SKB_CB_PRIV(skb))
|
||||
((struct sja1105_skb_cb *)((skb)->cb))
|
||||
|
||||
struct sja1105_port {
|
||||
u16 subvlan_map[DSA_8021Q_N_SUBVLAN];
|
||||
|
@ -117,20 +117,6 @@ struct dsa_netdevice_ops {
|
||||
#define MODULE_ALIAS_DSA_TAG_DRIVER(__proto) \
|
||||
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 list_head list;
|
||||
|
||||
@ -740,8 +726,8 @@ struct dsa_switch_ops {
|
||||
struct ifreq *ifr);
|
||||
int (*port_hwtstamp_set)(struct dsa_switch *ds, int port,
|
||||
struct ifreq *ifr);
|
||||
bool (*port_txtstamp)(struct dsa_switch *ds, int port,
|
||||
struct sk_buff *clone, unsigned int type);
|
||||
void (*port_txtstamp)(struct dsa_switch *ds, int port,
|
||||
struct sk_buff *skb);
|
||||
bool (*port_rxtstamp)(struct dsa_switch *ds, int port,
|
||||
struct sk_buff *skb, unsigned int type);
|
||||
|
||||
|
@ -689,6 +689,15 @@ struct ocelot_policer {
|
||||
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_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))
|
||||
@ -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,
|
||||
u32 val, u32 reg, u32 offset);
|
||||
|
||||
/* Packet I/O */
|
||||
#if IS_ENABLED(CONFIG_MSCC_OCELOT_SWITCH_LIB)
|
||||
|
||||
/* Packet I/O */
|
||||
bool ocelot_can_inject(struct ocelot *ocelot, int grp);
|
||||
void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
|
||||
u32 rew_op, 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);
|
||||
|
||||
u32 ocelot_ptp_rew_op(struct sk_buff *skb);
|
||||
#else
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
static inline u32 ocelot_ptp_rew_op(struct sk_buff *skb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Hardware initialization */
|
||||
@ -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_hwstamp_get(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,
|
||||
struct sk_buff *clone);
|
||||
int ocelot_port_txtstamp_request(struct ocelot *ocelot, int port,
|
||||
struct sk_buff *skb,
|
||||
struct sk_buff **clone);
|
||||
void ocelot_get_txtstamp(struct ocelot *ocelot);
|
||||
void ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu);
|
||||
int ocelot_get_max_mtu(struct ocelot *ocelot, int port);
|
||||
|
@ -111,6 +111,8 @@ config NET_DSA_TAG_RTL4_A
|
||||
|
||||
config NET_DSA_TAG_OCELOT
|
||||
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
|
||||
help
|
||||
Say Y or M if you want to enable NPI tagging for the Ocelot switches
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include <linux/if_bridge.h>
|
||||
#include <linux/if_hsr.h>
|
||||
#include <linux/netpoll.h>
|
||||
#include <linux/ptp_classify.h>
|
||||
|
||||
#include "dsa_priv.h"
|
||||
|
||||
@ -556,26 +555,14 @@ static void dsa_skb_tx_timestamp(struct dsa_slave_priv *p,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct dsa_switch *ds = p->dp->ds;
|
||||
struct sk_buff *clone;
|
||||
unsigned int type;
|
||||
|
||||
type = ptp_classify_raw(skb);
|
||||
if (type == PTP_CLASS_NONE)
|
||||
if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))
|
||||
return;
|
||||
|
||||
if (!ds->ops->port_txtstamp)
|
||||
return;
|
||||
|
||||
clone = skb_clone_sk(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);
|
||||
ds->ops->port_txtstamp(ds, p->dp->index, skb);
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
* switch driver
|
||||
*/
|
||||
/* Handle tx timestamp if any */
|
||||
dsa_skb_tx_timestamp(p, skb);
|
||||
|
||||
if (dsa_realloc_skb(skb, dev)) {
|
||||
|
@ -5,33 +5,14 @@
|
||||
#include <soc/mscc/ocelot.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,
|
||||
__be32 ifh_prefix, void **ifh)
|
||||
{
|
||||
struct dsa_port *dp = dsa_slave_to_port(netdev);
|
||||
struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
|
||||
struct dsa_switch *ds = dp->ds;
|
||||
void *injection;
|
||||
__be32 *prefix;
|
||||
u32 rew_op = 0;
|
||||
|
||||
injection = skb_push(skb, OCELOT_TAG_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,
|
||||
ocelot_ifh_set_src(injection, ds->num_ports);
|
||||
ocelot_ifh_set_qos_class(injection, skb->priority);
|
||||
|
||||
/* TX timestamping was requested */
|
||||
if (clone)
|
||||
ocelot_xmit_ptp(dp, injection, clone);
|
||||
rew_op = ocelot_ptp_rew_op(skb);
|
||||
if (rew_op)
|
||||
ocelot_ifh_set_rew_op(injection, rew_op);
|
||||
|
||||
*ifh = injection;
|
||||
}
|
||||
|
@ -13,32 +13,6 @@
|
||||
#include <soc/mscc/ocelot_ptp.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,
|
||||
struct net_device *netdev)
|
||||
{
|
||||
@ -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 queue_mapping = skb_get_queue_mapping(skb);
|
||||
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;
|
||||
|
||||
/* TX timestamping was requested, so inject through MMIO */
|
||||
if (clone)
|
||||
return ocelot_xmit_ptp(dp, skb, clone);
|
||||
rew_op = ocelot_ptp_rew_op(skb);
|
||||
if (rew_op) {
|
||||
if (!ocelot_can_inject(ocelot, 0))
|
||||
return NULL;
|
||||
|
||||
ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dsa_8021q_xmit(skb, netdev, ETH_P_8021Q,
|
||||
((pcp << VLAN_PRIO_SHIFT) | tx_vid));
|
||||
|
Loading…
Reference in New Issue
Block a user