net: dsa: forward timestamping callbacks to switch drivers
Forward the rx/tx timestamp machinery from the dsa infrastructure to the switch driver. On the rx side, defer delivery of skbs until we have an rx timestamp. This mimicks the behavior of skb_defer_rx_timestamp. On the tx side, identify PTP packets, clone them, and pass them to the underlying switch driver before we transmit. This mimicks the behavior of skb_tx_timestamp. Adjusted txstamp API to keep the allocation and freeing of the clone in the same central function by Richard Cochran Signed-off-by: Brandon Streiff <brandon.streiff@ni.com> Signed-off-by: Richard Cochran <richardcochran@gmail.com> Signed-off-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
0336369d3a
commit
90af1059c5
3 changed files with 71 additions and 0 deletions
|
@ -102,6 +102,7 @@ struct dsa_platform_data {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct packet_type;
|
struct packet_type;
|
||||||
|
struct dsa_switch;
|
||||||
|
|
||||||
struct dsa_device_ops {
|
struct dsa_device_ops {
|
||||||
struct sk_buff *(*xmit)(struct sk_buff *skb, struct net_device *dev);
|
struct sk_buff *(*xmit)(struct sk_buff *skb, struct net_device *dev);
|
||||||
|
@ -484,6 +485,10 @@ 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,
|
||||||
|
struct sk_buff *clone, unsigned int type);
|
||||||
|
bool (*port_rxtstamp)(struct dsa_switch *ds, int port,
|
||||||
|
struct sk_buff *skb, unsigned int type);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dsa_switch_driver {
|
struct dsa_switch_driver {
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
#include <linux/phy_fixed.h>
|
#include <linux/phy_fixed.h>
|
||||||
|
#include <linux/ptp_classify.h>
|
||||||
#include <linux/gpio/consumer.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/etherdevice.h>
|
#include <linux/etherdevice.h>
|
||||||
|
|
||||||
|
@ -122,6 +123,38 @@ struct net_device *dsa_dev_to_net_device(struct device *dev)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(dsa_dev_to_net_device);
|
EXPORT_SYMBOL_GPL(dsa_dev_to_net_device);
|
||||||
|
|
||||||
|
/* Determine if we should defer delivery of skb until we have a rx timestamp.
|
||||||
|
*
|
||||||
|
* Called from dsa_switch_rcv. For now, this will only work if tagging is
|
||||||
|
* enabled on the switch. Normally the MAC driver would retrieve the hardware
|
||||||
|
* timestamp when it reads the packet out of the hardware. However in a DSA
|
||||||
|
* switch, the DSA driver owning the interface to which the packet is
|
||||||
|
* delivered is never notified unless we do so here.
|
||||||
|
*/
|
||||||
|
static bool dsa_skb_defer_rx_timestamp(struct dsa_slave_priv *p,
|
||||||
|
struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct dsa_switch *ds = p->dp->ds;
|
||||||
|
unsigned int type;
|
||||||
|
|
||||||
|
if (skb_headroom(skb) < ETH_HLEN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
__skb_push(skb, ETH_HLEN);
|
||||||
|
|
||||||
|
type = ptp_classify_raw(skb);
|
||||||
|
|
||||||
|
__skb_pull(skb, ETH_HLEN);
|
||||||
|
|
||||||
|
if (type == PTP_CLASS_NONE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (likely(ds->ops->port_rxtstamp))
|
||||||
|
return ds->ops->port_rxtstamp(ds, p->dp->index, skb, type);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
|
static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||||
struct packet_type *pt, struct net_device *unused)
|
struct packet_type *pt, struct net_device *unused)
|
||||||
{
|
{
|
||||||
|
@ -157,6 +190,9 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||||
s->rx_bytes += skb->len;
|
s->rx_bytes += skb->len;
|
||||||
u64_stats_update_end(&s->syncp);
|
u64_stats_update_end(&s->syncp);
|
||||||
|
|
||||||
|
if (dsa_skb_defer_rx_timestamp(p, skb))
|
||||||
|
return 0;
|
||||||
|
|
||||||
netif_receive_skb(skb);
|
netif_receive_skb(skb);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <net/tc_act/tc_mirred.h>
|
#include <net/tc_act/tc_mirred.h>
|
||||||
#include <linux/if_bridge.h>
|
#include <linux/if_bridge.h>
|
||||||
#include <linux/netpoll.h>
|
#include <linux/netpoll.h>
|
||||||
|
#include <linux/ptp_classify.h>
|
||||||
|
|
||||||
#include "dsa_priv.h"
|
#include "dsa_priv.h"
|
||||||
|
|
||||||
|
@ -401,6 +402,30 @@ static inline netdev_tx_t dsa_slave_netpoll_send_skb(struct net_device *dev,
|
||||||
return NETDEV_TX_OK;
|
return NETDEV_TX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
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))
|
||||||
|
return;
|
||||||
|
|
||||||
|
kfree_skb(clone);
|
||||||
|
}
|
||||||
|
|
||||||
static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
|
static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct dsa_slave_priv *p = netdev_priv(dev);
|
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||||
|
@ -413,6 +438,11 @@ static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
s->tx_bytes += skb->len;
|
s->tx_bytes += skb->len;
|
||||||
u64_stats_update_end(&s->syncp);
|
u64_stats_update_end(&s->syncp);
|
||||||
|
|
||||||
|
/* Identify PTP protocol packets, clone them, and pass them to the
|
||||||
|
* switch driver
|
||||||
|
*/
|
||||||
|
dsa_skb_tx_timestamp(p, skb);
|
||||||
|
|
||||||
/* Transmit function may have to reallocate the original SKB,
|
/* Transmit function may have to reallocate the original SKB,
|
||||||
* in which case it must have freed it. Only free it here on error.
|
* in which case it must have freed it. Only free it here on error.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in a new issue