Commit 1a49cf81 authored by Milena Olech's avatar Milena Olech Committed by Tony Nguyen
Browse files

idpf: add Tx timestamp flows



Add functions to request Tx timestamp for the PTP packets, read the Tx
timestamp when the completion tag for that packet is being received,
extend the Tx timestamp value and set the supported timestamping modes.

Tx timestamp is requested for the PTP packets by setting a TSYN bit and
index value in the Tx context descriptor. The driver assumption is that
the Tx timestamp value is ready to be read when the completion tag is
received. Then the driver schedules delayed work and the Tx timestamp
value read is requested through virtchnl message. At the end, the Tx
timestamp value is extended to 64-bit and provided back to the skb.

Reviewed-by: default avatarJacob Keller <jacob.e.keller@intel.com>
Co-developed-by: default avatarJosh Hay <joshua.a.hay@intel.com>
Signed-off-by: default avatarJosh Hay <joshua.a.hay@intel.com>
Signed-off-by: default avatarMilena Olech <milena.olech@intel.com>
Tested-by: default avatarSamuel Salin <Samuel.salin@intel.com>
Signed-off-by: default avatarTony Nguyen <anthony.l.nguyen@intel.com>
parent 4901e83a
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -293,6 +293,8 @@ struct idpf_port_stats {
 * @link_up: True if link is up
 * @sw_marker_wq: workqueue for marker packets
 * @tx_tstamp_caps: Capabilities negotiated for Tx timestamping
 * @tstamp_config: The Tx tstamp config
 * @tstamp_task: Tx timestamping task
 */
struct idpf_vport {
	u16 num_txq;
@@ -339,6 +341,8 @@ struct idpf_vport {
	wait_queue_head_t sw_marker_wq;

	struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
	struct kernel_hwtstamp_config tstamp_config;
	struct work_struct tstamp_task;
};

/**
+66 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
/* Copyright (C) 2023 Intel Corporation */

#include "idpf.h"
#include "idpf_ptp.h"

/**
 * idpf_get_rxnfc - command to get RX flow classification rules
@@ -1312,6 +1313,70 @@ static int idpf_get_link_ksettings(struct net_device *netdev,
	return 0;
}

/**
 * idpf_get_timestamp_filters - Get the supported timestamping mode
 * @vport: Virtual port structure
 * @info: ethtool timestamping info structure
 *
 * Get the Tx/Rx timestamp filters.
 */
static void idpf_get_timestamp_filters(const struct idpf_vport *vport,
				       struct kernel_ethtool_ts_info *info)
{
	info->so_timestamping = SOF_TIMESTAMPING_RX_HARDWARE |
				SOF_TIMESTAMPING_RAW_HARDWARE;

	info->tx_types = BIT(HWTSTAMP_TX_OFF);

	if (!vport->tx_tstamp_caps ||
	    vport->adapter->ptp->tx_tstamp_access == IDPF_PTP_NONE)
		return;

	info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE |
				 SOF_TIMESTAMPING_TX_HARDWARE;

	info->tx_types |= BIT(HWTSTAMP_TX_ON);
}

/**
 * idpf_get_ts_info - Get device PHC association
 * @netdev: network interface device structure
 * @info: ethtool timestamping info structure
 *
 * Return: 0 on success, -errno otherwise.
 */
static int idpf_get_ts_info(struct net_device *netdev,
			    struct kernel_ethtool_ts_info *info)
{
	struct idpf_netdev_priv *np = netdev_priv(netdev);
	struct idpf_vport *vport;
	int err = 0;

	if (!mutex_trylock(&np->adapter->vport_ctrl_lock))
		return -EBUSY;

	vport = idpf_netdev_to_vport(netdev);

	if (!vport->adapter->ptp) {
		err = -EOPNOTSUPP;
		goto unlock;
	}

	if (idpf_is_cap_ena(vport->adapter, IDPF_OTHER_CAPS, VIRTCHNL2_CAP_PTP) &&
	    vport->adapter->ptp->clock) {
		info->phc_index = ptp_clock_index(vport->adapter->ptp->clock);
		idpf_get_timestamp_filters(vport, info);
	} else {
		pci_dbg(vport->adapter->pdev, "PTP clock not detected\n");
		err = ethtool_op_get_ts_info(netdev, info);
	}

unlock:
	mutex_unlock(&np->adapter->vport_ctrl_lock);

	return err;
}

static const struct ethtool_ops idpf_ethtool_ops = {
	.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
				     ETHTOOL_COALESCE_USE_ADAPTIVE,
@@ -1336,6 +1401,7 @@ static const struct ethtool_ops idpf_ethtool_ops = {
	.get_ringparam		= idpf_get_ringparam,
	.set_ringparam		= idpf_set_ringparam,
	.get_link_ksettings	= idpf_get_link_ksettings,
	.get_ts_info		= idpf_get_ts_info,
};

/**
+12 −1
Original line number Diff line number Diff line
@@ -282,7 +282,18 @@ struct idpf_flex_tx_tso_ctx_qw {
	u8 flex;
};

struct idpf_flex_tx_ctx_desc {
union idpf_flex_tx_ctx_desc {
	/* DTYPE = IDPF_TX_DESC_DTYPE_CTX (0x01) */
	struct {
		__le64 qw0;
#define IDPF_TX_CTX_L2TAG2_M	GENMASK_ULL(47, 32)
		__le64 qw1;
#define IDPF_TX_CTX_DTYPE_M	GENMASK_ULL(3, 0)
#define IDPF_TX_CTX_CMD_M	GENMASK_ULL(15, 4)
#define IDPF_TX_CTX_TSYN_REG_M	GENMASK_ULL(47, 30)
#define IDPF_TX_CTX_MSS_M	GENMASK_ULL(50, 63)
	} tsyn;

	/* DTYPE = IDPF_TX_DESC_DTYPE_FLEX_TSO_CTX (0x05) */
	struct {
		struct idpf_flex_tx_tso_ctx_qw qw0;
+55 −0
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@

#include "idpf.h"
#include "idpf_virtchnl.h"
#include "idpf_ptp.h"

static const struct net_device_ops idpf_netdev_ops;

@@ -2326,6 +2327,58 @@ void idpf_free_dma_mem(struct idpf_hw *hw, struct idpf_dma_mem *mem)
	mem->pa = 0;
}

static int idpf_hwtstamp_set(struct net_device *netdev,
			     struct kernel_hwtstamp_config *config,
			     struct netlink_ext_ack *extack)
{
	struct idpf_vport *vport;
	int err;

	idpf_vport_ctrl_lock(netdev);
	vport = idpf_netdev_to_vport(netdev);

	if (!vport->link_up) {
		idpf_vport_ctrl_unlock(netdev);
		return -EPERM;
	}

	if (!idpf_ptp_is_vport_tx_tstamp_ena(vport)) {
		idpf_vport_ctrl_unlock(netdev);
		return -EOPNOTSUPP;
	}

	err = idpf_ptp_set_timestamp_mode(vport, config);

	idpf_vport_ctrl_unlock(netdev);

	return err;
}

static int idpf_hwtstamp_get(struct net_device *netdev,
			     struct kernel_hwtstamp_config *config)
{
	struct idpf_vport *vport;

	idpf_vport_ctrl_lock(netdev);
	vport = idpf_netdev_to_vport(netdev);

	if (!vport->link_up) {
		idpf_vport_ctrl_unlock(netdev);
		return -EPERM;
	}

	if (!idpf_ptp_is_vport_tx_tstamp_ena(vport)) {
		idpf_vport_ctrl_unlock(netdev);
		return 0;
	}

	*config = vport->tstamp_config;

	idpf_vport_ctrl_unlock(netdev);

	return 0;
}

static const struct net_device_ops idpf_netdev_ops = {
	.ndo_open = idpf_open,
	.ndo_stop = idpf_stop,
@@ -2338,4 +2391,6 @@ static const struct net_device_ops idpf_netdev_ops = {
	.ndo_get_stats64 = idpf_get_stats64,
	.ndo_set_features = idpf_set_features,
	.ndo_tx_timeout = idpf_tx_timeout,
	.ndo_hwtstamp_get = idpf_hwtstamp_get,
	.ndo_hwtstamp_set = idpf_hwtstamp_set,
};
+240 −2
Original line number Diff line number Diff line
@@ -198,6 +198,37 @@ static int idpf_ptp_gettimex64(struct ptp_clock_info *info,
	return 0;
}

/**
 * idpf_ptp_update_cached_phctime - Update the cached PHC time values
 * @adapter: Driver specific private structure
 *
 * This function updates the system time values which are cached in the adapter
 * structure.
 *
 * This function must be called periodically to ensure that the cached value
 * is never more than 2 seconds old.
 *
 * Return: 0 on success, -errno otherwise.
 */
static int idpf_ptp_update_cached_phctime(struct idpf_adapter *adapter)
{
	u64 systime;
	int err;

	err = idpf_ptp_read_src_clk_reg(adapter, &systime, NULL);
	if (err)
		return -EACCES;

	/* Update the cached PHC time stored in the adapter structure.
	 * These values are used to extend Tx timestamp values to 64 bit
	 * expected by the stack.
	 */
	WRITE_ONCE(adapter->ptp->cached_phc_time, systime);
	WRITE_ONCE(adapter->ptp->cached_phc_jiffies, jiffies);

	return 0;
}

/**
 * idpf_ptp_settime64 - Set the time of the clock
 * @info: the driver's PTP info structure
@@ -229,6 +260,11 @@ static int idpf_ptp_settime64(struct ptp_clock_info *info,
		return err;
	}

	err = idpf_ptp_update_cached_phctime(adapter);
	if (err)
		pci_warn(adapter->pdev,
			 "Unable to immediately update cached PHC time\n");

	return 0;
}

@@ -285,6 +321,11 @@ static int idpf_ptp_adjtime(struct ptp_clock_info *info, s64 delta)
		return err;
	}

	err = idpf_ptp_update_cached_phctime(adapter);
	if (err)
		pci_warn(adapter->pdev,
			 "Unable to immediately update cached PHC time\n");

	return 0;
}

@@ -349,6 +390,168 @@ static int idpf_ptp_gpio_enable(struct ptp_clock_info *info,
	return -EOPNOTSUPP;
}

/**
 * idpf_ptp_tstamp_extend_32b_to_64b - Convert a 32b nanoseconds Tx or Rx
 *				       timestamp value to 64b.
 * @cached_phc_time: recently cached copy of PHC time
 * @in_timestamp: Ingress/egress 32b nanoseconds timestamp value
 *
 * Hardware captures timestamps which contain only 32 bits of nominal
 * nanoseconds, as opposed to the 64bit timestamps that the stack expects.
 *
 * Return: Tx timestamp value extended to 64 bits based on cached PHC time.
 */
u64 idpf_ptp_tstamp_extend_32b_to_64b(u64 cached_phc_time, u32 in_timestamp)
{
	u32 delta, phc_time_lo;
	u64 ns;

	/* Extract the lower 32 bits of the PHC time */
	phc_time_lo = (u32)cached_phc_time;

	/* Calculate the delta between the lower 32bits of the cached PHC
	 * time and the in_timestamp value.
	 */
	delta = in_timestamp - phc_time_lo;

	if (delta > U32_MAX / 2) {
		/* Reverse the delta calculation here */
		delta = phc_time_lo - in_timestamp;
		ns = cached_phc_time - delta;
	} else {
		ns = cached_phc_time + delta;
	}

	return ns;
}

/**
 * idpf_ptp_extend_ts - Convert a 40b timestamp to 64b nanoseconds
 * @vport: Virtual port structure
 * @in_tstamp: Ingress/egress timestamp value
 *
 * It is assumed that the caller verifies the timestamp is valid prior to
 * calling this function.
 *
 * Extract the 32bit nominal nanoseconds and extend them. Use the cached PHC
 * time stored in the device private PTP structure as the basis for timestamp
 * extension.
 *
 * Return: Tx timestamp value extended to 64 bits.
 */
u64 idpf_ptp_extend_ts(struct idpf_vport *vport, u64 in_tstamp)
{
	struct idpf_ptp *ptp = vport->adapter->ptp;
	unsigned long discard_time;

	discard_time = ptp->cached_phc_jiffies + 2 * HZ;

	if (time_is_before_jiffies(discard_time))
		return 0;

	return idpf_ptp_tstamp_extend_32b_to_64b(ptp->cached_phc_time,
						 lower_32_bits(in_tstamp));
}

/**
 * idpf_ptp_request_ts - Request an available Tx timestamp index
 * @tx_q: Transmit queue on which the Tx timestamp is requested
 * @skb: The SKB to associate with this timestamp request
 * @idx: Index of the Tx timestamp latch
 *
 * Request tx timestamp index negotiated during PTP init that will be set into
 * Tx descriptor.
 *
 * Return: 0 and the index that can be provided to Tx descriptor on success,
 * -errno otherwise.
 */
int idpf_ptp_request_ts(struct idpf_tx_queue *tx_q, struct sk_buff *skb,
			u32 *idx)
{
	struct idpf_ptp_tx_tstamp *ptp_tx_tstamp;
	struct list_head *head;

	/* Get the index from the free latches list */
	spin_lock(&tx_q->cached_tstamp_caps->latches_lock);

	head = &tx_q->cached_tstamp_caps->latches_free;
	if (list_empty(head)) {
		spin_unlock(&tx_q->cached_tstamp_caps->latches_lock);
		return -ENOBUFS;
	}

	ptp_tx_tstamp = list_first_entry(head, struct idpf_ptp_tx_tstamp,
					 list_member);
	list_del(&ptp_tx_tstamp->list_member);

	ptp_tx_tstamp->skb = skb_get(skb);
	skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;

	/* Move the element to the used latches list */
	list_add(&ptp_tx_tstamp->list_member,
		 &tx_q->cached_tstamp_caps->latches_in_use);
	spin_unlock(&tx_q->cached_tstamp_caps->latches_lock);

	*idx = ptp_tx_tstamp->idx;

	return 0;
}

/**
 * idpf_ptp_set_timestamp_mode - Setup driver for requested timestamp mode
 * @vport: Virtual port structure
 * @config: Hwtstamp settings requested or saved
 *
 * Return: 0 on success, -errno otherwise.
 */
int idpf_ptp_set_timestamp_mode(struct idpf_vport *vport,
				struct kernel_hwtstamp_config *config)
{
	switch (config->tx_type) {
	case HWTSTAMP_TX_OFF:
		break;
	case HWTSTAMP_TX_ON:
		if (!idpf_ptp_is_vport_tx_tstamp_ena(vport))
			return -EINVAL;
		break;
	default:
		return -EINVAL;
	}

	vport->tstamp_config.tx_type = config->tx_type;
	*config = vport->tstamp_config;

	return 0;
}

/**
 * idpf_tstamp_task - Delayed task to handle Tx tstamps
 * @work: work_struct handle
 */
void idpf_tstamp_task(struct work_struct *work)
{
	struct idpf_vport *vport;

	vport = container_of(work, struct idpf_vport, tstamp_task);

	idpf_ptp_get_tx_tstamp(vport);
}

/**
 * idpf_ptp_do_aux_work - Do PTP periodic work
 * @info: Driver's PTP info structure
 *
 * Return: Number of jiffies to periodic work.
 */
static long idpf_ptp_do_aux_work(struct ptp_clock_info *info)
{
	struct idpf_adapter *adapter = idpf_ptp_info_to_adapter(info);

	idpf_ptp_update_cached_phctime(adapter);

	return msecs_to_jiffies(500);
}

/**
 * idpf_ptp_set_caps - Set PTP capabilities
 * @adapter: Driver specific private structure
@@ -370,6 +573,7 @@ static void idpf_ptp_set_caps(const struct idpf_adapter *adapter)
	info->adjtime = idpf_ptp_adjtime;
	info->verify = idpf_ptp_verify_pin;
	info->enable = idpf_ptp_gpio_enable;
	info->do_aux_work = idpf_ptp_do_aux_work;
}

/**
@@ -413,6 +617,8 @@ static void idpf_ptp_release_vport_tstamp(struct idpf_vport *vport)
	struct idpf_ptp_tx_tstamp *ptp_tx_tstamp, *tmp;
	struct list_head *head;

	cancel_work_sync(&vport->tstamp_task);

	/* Remove list with free latches */
	spin_lock_bh(&vport->tx_tstamp_caps->latches_lock);

@@ -451,6 +657,27 @@ static void idpf_ptp_release_tstamp(struct idpf_adapter *adapter)
	}
}

/**
 * idpf_ptp_get_txq_tstamp_capability - Verify the timestamping capability
 *					for a given tx queue.
 * @txq: Transmit queue
 *
 * Since performing timestamp flows requires reading the device clock value and
 * the support in the Control Plane, the function checks both factors and
 * summarizes the support for the timestamping.
 *
 * Return: true if the timestamping is supported, false otherwise.
 */
bool idpf_ptp_get_txq_tstamp_capability(struct idpf_tx_queue *txq)
{
	if (!txq || !txq->cached_tstamp_caps)
		return false;
	else if (txq->cached_tstamp_caps->access)
		return true;
	else
		return false;
}

/**
 * idpf_ptp_init - Initialize PTP hardware clock support
 * @adapter: Driver specific private structure
@@ -491,6 +718,9 @@ int idpf_ptp_init(struct idpf_adapter *adapter)
	if (err)
		goto free_ptp;

	if (adapter->ptp->get_dev_clk_time_access != IDPF_PTP_NONE)
		ptp_schedule_worker(adapter->ptp->clock, 0);

	/* Write the default increment time value if the clock adjustments
	 * are enabled.
	 */
@@ -516,6 +746,9 @@ int idpf_ptp_init(struct idpf_adapter *adapter)
	return 0;

remove_clock:
	if (adapter->ptp->get_dev_clk_time_access != IDPF_PTP_NONE)
		ptp_cancel_worker_sync(adapter->ptp->clock);

	ptp_clock_unregister(adapter->ptp->clock);
	adapter->ptp->clock = NULL;

@@ -537,11 +770,16 @@ void idpf_ptp_release(struct idpf_adapter *adapter)
	if (!ptp)
		return;

	if (ptp->tx_tstamp_access != IDPF_PTP_NONE)
	if (ptp->tx_tstamp_access != IDPF_PTP_NONE &&
	    ptp->get_dev_clk_time_access != IDPF_PTP_NONE)
		idpf_ptp_release_tstamp(adapter);

	if (ptp->clock)
	if (ptp->clock) {
		if (adapter->ptp->get_dev_clk_time_access != IDPF_PTP_NONE)
			ptp_cancel_worker_sync(adapter->ptp->clock);

		ptp_clock_unregister(ptp->clock);
	}

	kfree(ptp);
	adapter->ptp = NULL;
Loading