@@ -32,6 +32,7 @@
#include <linux/phy_link_topology.h>
#include <linux/pse-pd/pse.h>
#include <linux/property.h>
+#include <linux/ptp_clock_kernel.h>
#include <linux/rtnetlink.h>
#include <linux/sfp.h>
#include <linux/skbuff.h>
@@ -1998,6 +1999,16 @@ void phy_detach(struct phy_device *phydev)
phy_suspend(phydev);
if (dev) {
+ struct hwtstamp_provider *hwtstamp;
+
+ hwtstamp = rtnl_dereference(dev->hwtstamp);
+ /* Disable timestamp if selected */
+ if (hwtstamp && ptp_clock_phydev(hwtstamp->ptp) == phydev) {
+ rcu_assign_pointer(dev->hwtstamp, NULL);
+ call_rcu(&hwtstamp->rcu_head,
+ remove_hwtstamp_provider);
+ }
+
phydev->attached_dev->phydev = NULL;
phydev->attached_dev = NULL;
phy_link_topo_del_phy(dev, phydev);
@@ -98,3 +98,13 @@ void ptp_clock_put(struct device *dev, struct ptp_clock *ptp)
put_device(&ptp->dev);
module_put(ptp->info->owner);
}
+
+void remove_hwtstamp_provider(struct rcu_head *rcu_head)
+{
+ struct hwtstamp_provider *hwtstamp;
+
+ hwtstamp = container_of(rcu_head, struct hwtstamp_provider, rcu_head);
+ ptp_clock_put(hwtstamp->dev, hwtstamp->ptp);
+ kfree(hwtstamp);
+}
+EXPORT_SYMBOL(remove_hwtstamp_provider);
@@ -19,6 +19,22 @@ enum hwtstamp_source {
HWTSTAMP_SOURCE_PHYLIB,
};
+/**
+ * struct hwtstamp_provider - Description of a hwtstamp provider object
+ *
+ * @rcu_head: RCU callback used to free the struct.
+ * @dev: device attached to the hwtstamp provider used to put the ptp clock.
+ * @ptp: PTP clock pointer of the hwtstamp provider.
+ * @qualifier: hwtstamp provider qualifier.
+ */
+
+struct hwtstamp_provider {
+ struct rcu_head rcu_head;
+ struct device *dev;
+ struct ptp_clock *ptp;
+ enum hwtstamp_provider_qualifier qualifier;
+};
+
/**
* struct kernel_hwtstamp_config - Kernel copy of struct hwtstamp_config
*
@@ -31,6 +47,7 @@ enum hwtstamp_source {
* copied the ioctl request back to user space
* @source: indication whether timestamps should come from the netdev or from
* an attached phylib PHY
+ * @qualifier: qualifier of the hwtstamp provider
*
* Prefer using this structure for in-kernel processing of hardware
* timestamping configuration, over the inextensible struct hwtstamp_config
@@ -43,6 +60,7 @@ struct kernel_hwtstamp_config {
struct ifreq *ifr;
bool copied_to_user;
enum hwtstamp_source source;
+ enum hwtstamp_provider_qualifier qualifier;
};
static inline void hwtstamp_config_to_kernel(struct kernel_hwtstamp_config *kernel_cfg,
@@ -81,6 +81,7 @@ struct xdp_metadata_ops;
struct xdp_md;
struct ethtool_netdev_state;
struct phy_link_topology;
+struct hwtstamp_provider;
typedef u32 xdp_features_t;
@@ -2031,6 +2032,7 @@ enum netdev_reg_state {
* @gro_flush_timeout: timeout for GRO layer in NAPI
* @napi_defer_hard_irqs: If not zero, provides a counter that would
* allow to avoid NIC hard IRQ, on busy queues.
+ * @hwtstamp: Tracks which PTP performs hardware packet time stamping.
*
* FIXME: cleanup struct net_device such that network protocol info
* moves out.
@@ -2440,6 +2442,9 @@ struct net_device {
*/
struct net_shaper_hierarchy *net_shaper_hierarchy;
#endif
+
+ struct hwtstamp_provider __rcu *hwtstamp;
+
u8 priv[] ____cacheline_aligned
__counted_by(priv_len);
} ____cacheline_aligned;
@@ -444,6 +444,14 @@ struct ptp_clock *ptp_clock_get_by_index(struct device *dev, int index);
void ptp_clock_put(struct device *dev, struct ptp_clock *ptp);
+/**
+ * remove_hwtstamp_provider() - Put and free the hwtstamp provider
+ *
+ * @rcu_head: RCU callback head.
+ */
+
+void remove_hwtstamp_provider(struct rcu_head *rcu_head);
+
/**
* ptp_find_pin() - obtain the pin index of a given auxiliary function
*
@@ -532,6 +540,8 @@ static inline void ptp_clock_put(struct device *dev, struct ptp_clock *ptp)
static inline struct ptp_clock *ptp_clock_get_by_index(struct device *dev,
int index)
{ return NULL; }
+static inline void remove_hwtstamp_provider(struct rcu_head *rcu_head)
+{ return; }
static inline int ptp_find_pin(struct ptp_clock *ptp,
enum ptp_pin_function func, unsigned int chan)
{ return -1; }
@@ -13,6 +13,17 @@
#include <linux/types.h>
#include <linux/socket.h> /* for SO_TIMESTAMPING */
+/*
+ * Possible type of hwtstamp provider. Mainly "precise" the default one
+ * is for IEEE 1588 quality and "approx" is for NICs DMA point.
+ */
+enum hwtstamp_provider_qualifier {
+ HWTSTAMP_PROVIDER_QUALIFIER_PRECISE,
+ HWTSTAMP_PROVIDER_QUALIFIER_APPROX,
+
+ HWTSTAMP_PROVIDER_QUALIFIER_CNT,
+};
+
/* SO_TIMESTAMPING flags */
enum {
SOF_TIMESTAMPING_TX_HARDWARE = (1<<0),
@@ -6,6 +6,7 @@
#include <linux/rtnetlink.h>
#include <linux/net_tstamp.h>
#include <linux/phylib_stubs.h>
+#include <linux/ptp_clock_kernel.h>
#include <linux/wireless.h>
#include <linux/if_bridge.h>
#include <net/dsa_stubs.h>
@@ -269,6 +270,22 @@ static int dev_eth_ioctl(struct net_device *dev,
int dev_get_hwtstamp_phylib(struct net_device *dev,
struct kernel_hwtstamp_config *cfg)
{
+ struct hwtstamp_provider *hwtstamp;
+
+ hwtstamp = rtnl_dereference(dev->hwtstamp);
+ if (hwtstamp) {
+ struct ptp_clock *ptp = hwtstamp->ptp;
+
+ cfg->qualifier = hwtstamp->qualifier;
+ if (ptp_clock_from_phylib(ptp))
+ return phy_hwtstamp_get(ptp_clock_phydev(ptp), cfg);
+
+ if (ptp_clock_from_netdev(ptp))
+ return dev->netdev_ops->ndo_hwtstamp_get(dev, cfg);
+
+ return -EOPNOTSUPP;
+ }
+
if (phy_is_default_hwtstamp(dev->phydev))
return phy_hwtstamp_get(dev->phydev, cfg);
@@ -324,11 +341,33 @@ int dev_set_hwtstamp_phylib(struct net_device *dev,
struct netlink_ext_ack *extack)
{
const struct net_device_ops *ops = dev->netdev_ops;
- bool phy_ts = phy_is_default_hwtstamp(dev->phydev);
struct kernel_hwtstamp_config old_cfg = {};
+ struct hwtstamp_provider *hwtstamp;
+ struct phy_device *phydev;
bool changed = false;
+ bool phy_ts;
int err;
+ hwtstamp = rtnl_dereference(dev->hwtstamp);
+ if (hwtstamp) {
+ struct ptp_clock *ptp = hwtstamp->ptp;
+
+ if (ptp_clock_from_phylib(ptp)) {
+ phy_ts = true;
+ phydev = ptp_clock_phydev(ptp);
+ } else if (ptp_clock_from_netdev(ptp)) {
+ phy_ts = false;
+ } else {
+ return -EOPNOTSUPP;
+ }
+
+ cfg->qualifier = hwtstamp->qualifier;
+ } else {
+ phy_ts = phy_is_default_hwtstamp(dev->phydev);
+ if (phy_ts)
+ phydev = dev->phydev;
+ }
+
cfg->source = phy_ts ? HWTSTAMP_SOURCE_PHYLIB : HWTSTAMP_SOURCE_NETDEV;
if (phy_ts && dev->see_all_hwtstamp_requests) {
@@ -350,7 +389,7 @@ int dev_set_hwtstamp_phylib(struct net_device *dev,
changed = kernel_hwtstamp_config_changed(&old_cfg, cfg);
if (phy_ts) {
- err = phy_hwtstamp_set(dev->phydev, cfg, extack);
+ err = phy_hwtstamp_set(phydev, cfg, extack);
if (err) {
if (changed)
ops->ndo_hwtstamp_set(dev, &old_cfg, NULL);
@@ -9,6 +9,7 @@
#include <linux/ptp_classify.h>
#include <linux/skbuff.h>
#include <linux/export.h>
+#include <linux/ptp_clock_kernel.h>
static unsigned int classify(const struct sk_buff *skb)
{
@@ -21,19 +22,38 @@ static unsigned int classify(const struct sk_buff *skb)
void skb_clone_tx_timestamp(struct sk_buff *skb)
{
+ struct hwtstamp_provider *hwtstamp;
struct mii_timestamper *mii_ts;
+ struct phy_device *phydev;
struct sk_buff *clone;
unsigned int type;
- if (!skb->sk || !skb->dev ||
- !phy_is_default_hwtstamp(skb->dev->phydev))
+ if (!skb->sk || !skb->dev)
return;
+ rcu_read_lock();
+ hwtstamp = rcu_dereference(skb->dev->hwtstamp);
+ if (hwtstamp) {
+ if (!ptp_clock_from_phylib(hwtstamp->ptp)) {
+ rcu_read_unlock();
+ return;
+ }
+
+ phydev = ptp_clock_phydev(hwtstamp->ptp);
+ } else {
+ phydev = skb->dev->phydev;
+ if (!phy_is_default_hwtstamp(phydev)) {
+ rcu_read_unlock();
+ return;
+ }
+ }
+ rcu_read_unlock();
+
type = classify(skb);
if (type == PTP_CLASS_NONE)
return;
- mii_ts = skb->dev->phydev->mii_ts;
+ mii_ts = phydev->mii_ts;
if (likely(mii_ts->txtstamp)) {
clone = skb_clone_sk(skb);
if (!clone)
@@ -45,12 +65,32 @@ EXPORT_SYMBOL_GPL(skb_clone_tx_timestamp);
bool skb_defer_rx_timestamp(struct sk_buff *skb)
{
+ struct hwtstamp_provider *hwtstamp;
struct mii_timestamper *mii_ts;
+ struct phy_device *phydev;
unsigned int type;
- if (!skb->dev || !phy_is_default_hwtstamp(skb->dev->phydev))
+ if (!skb->dev)
return false;
+ rcu_read_lock();
+ hwtstamp = rcu_dereference(skb->dev->hwtstamp);
+ if (hwtstamp) {
+ if (!ptp_clock_from_phylib(hwtstamp->ptp)) {
+ rcu_read_unlock();
+ return false;
+ }
+
+ phydev = ptp_clock_phydev(hwtstamp->ptp);
+ } else {
+ phydev = skb->dev->phydev;
+ if (!phy_is_default_hwtstamp(phydev)) {
+ rcu_read_unlock();
+ return false;
+ }
+ }
+ rcu_read_unlock();
+
if (skb_headroom(skb) < ETH_HLEN)
return false;
@@ -63,7 +103,7 @@ bool skb_defer_rx_timestamp(struct sk_buff *skb)
if (type == PTP_CLASS_NONE)
return false;
- mii_ts = skb->dev->phydev->mii_ts;
+ mii_ts = phydev->mii_ts;
if (likely(mii_ts->rxtstamp))
return mii_ts->rxtstamp(mii_ts, skb, type);