Netdev List
 help / color / mirror / Atom feed
From: Simon Horman <horms@kernel.org>
To: maciek@machnikowski.net
Cc: Simon Horman <horms@kernel.org>,
	netdev@vger.kernel.org, kuba@kernel.org,
	richardcochran@gmail.com, milena.olech@intel.com,
	willemdebruijn.kernel@gmail.com, andrew@lunn.ch,
	vadim.fedorenko@linux.dev
Subject: Re: [PATCH v10 net-next 2/3] netdevsim: Implement basic ptp support
Date: Tue, 18 Aug 2026 14:58:00 +0100	[thread overview]
Message-ID: <20260818135800.629288-1-horms@kernel.org> (raw)
In-Reply-To: <20260815063148.2952-3-maciek@machnikowski.net>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260815063148.2952-1-maciek@machnikowski.net?part=2
---
--- Patch [2]: [PATCH v10 net-next 2/3] netdevsim: Implement basic ptp support ---
commit ef2b7171af287d8c3a8f5064008830a7dd8f4db3
Author: Maciek Machnikowski <maciek@machnikowski.net>

netdevsim: Implement basic ptp support

Adds virtual hardware timestamping to netdevsim by reading the mock PHC
attached to the transmitting netdevsim and to its peer, reporting a TX
timestamp to the socket and stamping the forwarded skb for the RX side.
Also wires up ndo_hwtstamp_get / ndo_hwtstamp_set and extends
nsim_get_ts_info().

A few questions on the timestamping path below.

> diff --git a/drivers/net/netdevsim/ethtool.c b/drivers/net/netdevsim/ethtool.c
> index 025ea79879f3a..24e7d5592e799 100644
> --- a/drivers/net/netdevsim/ethtool.c
> +++ b/drivers/net/netdevsim/ethtool.c
> @@ -200,7 +200,22 @@ static int nsim_get_ts_info(struct net_device *dev,
>  {
>  	struct netdevsim *ns = netdev_priv(dev);
>  
> +	ethtool_op_get_ts_info(dev, info);
> +	if (!ns->phc) {
> +		info->phc_index = -1;
> +		return 0;
> +	}
> +

[Severity: High]
[Finding: main-1-1]
[Sources: opus-5]
This isn't a bug introduced by this patch, but the new !ns->phc guard looks
like it silently fixes a NULL pointer dereference that exists today, without
a mention in the changelog or a Fixes: tag.

Before this patch nsim_get_ts_info() did:

	info->phc_index = mock_phc_index(ns->phc);

and nsim_ethtool_init() installs the shared nsim_ethtool_ops (including
.get_ts_info = nsim_get_ts_info) on every netdevsim netdev, while only
nsim_init_netdevsim() calls mock_phc_create().  nsim_init_netdevsim_vf()
just assigns nsim_vf_netdev_ops and registers the netdev, so ns->phc stays
NULL for VF netdevs.

With CONFIG_PTP_1588_CLOCK_MOCK=y, mock_phc_index() is the real function:

drivers/ptp/ptp_mock.c:mock_phc_index() {
	return ptp_clock_index(phc->clock);
}

so ethtool -T on a netdevsim VF netdev dereferences NULL.  Would it make
sense to split this guard into its own patch with a Fixes: tag so that
stable tooling can pick it up?

Also, adding ethtool_op_get_ts_info() here changes ethtool -T output for all
netdevsim devices by advertising SOF_TIMESTAMPING_TX_SOFTWARE,
SOF_TIMESTAMPING_RX_SOFTWARE and SOF_TIMESTAMPING_SOFTWARE.  Could the
changelog mention that behaviour change?

>  	info->phc_index = mock_phc_index(ns->phc);
> +	if (info->phc_index < 0)
> +		return 0;
> +
> +	info->so_timestamping |= SOF_TIMESTAMPING_TX_HARDWARE |
> +				 SOF_TIMESTAMPING_RX_HARDWARE |
> +				 SOF_TIMESTAMPING_RAW_HARDWARE;
> +
> +	info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON);
> +	info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL);
>  
>  	return 0;
>  }
> diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
> index 4e9d7e10b527e..e586fd8f3bc37 100644
> --- a/drivers/net/netdevsim/netdev.c
> +++ b/drivers/net/netdevsim/netdev.c

[ ... ]

> @@ -164,6 +171,44 @@ static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev)
>  		skb_linearize(skb);
>  
>  	skb_tx_timestamp(skb);
> +
> +	if (unlikely(READ_ONCE(peer_ns->tstamp_config.rx_filter) !=
> +		     HWTSTAMP_FILTER_NONE))
> +		ptp_info_rx = mock_phc_get_ptp_info(peer_ns->phc);
> +
> +	if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
> +	    READ_ONCE(ns->tstamp_config.tx_type) == HWTSTAMP_TX_ON)
> +		ptp_info_tx = mock_phc_get_ptp_info(ns->phc);
> +
> +	if (unlikely(ptp_info_tx || ptp_info_rx)) {
> +		/* Capture both timestamps closely */
> +		if (ptp_info_tx)
> +			ptp_info_tx->gettime64(ptp_info_tx, &tx_ts);
> +		if (ptp_info_rx)
> +			ptp_info_rx->gettime64(ptp_info_rx, &rx_ts);
> +
> +		/* If TX hardware timestamping is enabled report TX timestamp */
> +		if (ptp_info_tx) {
> +			shhwtstamps.hwtstamp = timespec64_to_ktime(tx_ts);
> +			skb_tstamp_tx(skb_orig, &shhwtstamps);
> +		}

[Severity: Medium]
[Finding: gpt-5-6-sol-3-10]
[Sources: gpt-5-6-sol, opus-5]
Can a socket receive two SCM_TSTAMP_SND reports for the same packet here?

The hardware report is emitted after skb_tx_timestamp(), which already
delivered the software one:

include/linux/skbuff.h:skb_tx_timestamp() {
	skb_clone_tx_timestamp(skb);
	if (skb_shinfo(skb)->tx_flags & (SKBTX_SW_TSTAMP | SKBTX_BPF))
		skb_tstamp_tx(skb, NULL);
}

The only suppression of the software report lives in __skb_tstamp_tx():

net/core/skbuff.c:__skb_tstamp_tx() {
	if (!hwtstamps && !(tsflags & SOF_TIMESTAMPING_OPT_TX_SWHW) &&
	    skb_shinfo(orig_skb)->tx_flags & SKBTX_IN_PROGRESS)
		return;

netdevsim never sets SKBTX_IN_PROGRESS anywhere - the new code only tests
SKBTX_HW_TSTAMP - so for a socket that enabled both
SOF_TIMESTAMPING_TX_SOFTWARE and SOF_TIMESTAMPING_TX_HARDWARE without
SOF_TIMESTAMPING_OPT_TX_SWHW both reports are delivered.

Would following the usual driver sequence work here, i.e. test
SKBTX_HW_TSTAMP and set SKBTX_IN_PROGRESS before calling
skb_tx_timestamp()?

> +
> +		/* Unshare the skb, stamping it must not corrupt cloned copies
> +		 * and prevents returning single tstamp for Tx and Rx
> +		 */
> +		skb = skb_unshare(skb_orig, GFP_ATOMIC);
> +		if (unlikely(!skb)) {
> +			if (psp_ext)
> +				__skb_ext_put(psp_ext);
> +			goto out_drop_cnt;
> +		}

[Severity: Low]
[Finding: main-3-8]
[Sources: opus-5]
Should this skb_unshare() be gated on ptp_info_rx?

The enclosing condition is ptp_info_tx || ptp_info_rx, but the only write
into the skb below is guarded by if (ptp_info_rx).  On the TX-only path
(peer rx_filter == HWTSTAMP_FILTER_NONE) every timestamped frame gets copied
by skb_copy() for nothing, which also linearizes the buffer that the
hds_config logic just above deliberately left non-linear, and does not carry
queue_mapping.

If the GFP_ATOMIC copy then fails, the frame is dropped via out_drop_cnt
even though nothing needed to be written into it, and after the TX
timestamp was already handed to the socket by skb_tstamp_tx(skb_orig, ...).

[Severity: Low]
[Finding: main-5-12]
[Sources: opus-5]
Does skb_unshare() cover the case this comment describes?

include/linux/skbuff.h:skb_unshare() {
	might_sleep_if(gfpflags_allow_blocking(pri));
	if (skb_cloned(skb)) {
	...
}

The copy is made only for clones, so an skb with skb->users > 1 and
cloned == 0 is returned unchanged.  pktgen does
refcount_add(burst, &pkt_dev->skb->users) and resubmits the same skb, so the
skb_hwtstamps(skb) write below touches skb_shared_info that the other
reference holder still owns, and that the peer's NAPI path may read
concurrently after nsim_forward_skb() -> nsim_napi_rx() queues it on
rq->skb_queue.  Should the driver check skb_shared() as well, or stamp a
private copy?

> +
> +		/* If RX hardware timestamping is enabled report RX timestamp */
> +		if (ptp_info_rx)
> +			skb_hwtstamps(skb)->hwtstamp =
> +				timespec64_to_ktime(rx_ts);
> +	}
> +

[Severity: Critical]
[Finding: main-6-13]
[Sources: opus-5]
Can this write corrupt kernel memory for AF_XDP TX skbs?

skb_hwtstamps() returns a pointer into a union in skb_shared_info:

include/linux/skbuff.h:struct skb_shared_info {
	union {
		struct skb_shared_hwtstamps hwtstamps;
		struct xsk_tx_metadata_compl xsk_meta;
	};

struct xsk_tx_metadata_compl has a single member, u64 *tx_timestamp, and the
AF_XDP destructor dereferences it with only a NULL check:

net/xdp/xsk.c:xsk_destruct_skb() {
	struct xsk_tx_metadata_compl *compl = &skb_shinfo(skb)->xsk_meta;

	if (compl->tx_timestamp) {
		/* sw completion timestamp, not a real one */
		*compl->tx_timestamp = ktime_get_tai_fast_ns();
	}

Every generic-xmit AF_XDP skb carries that destructor
(xsk_skb_init_misc() sets skb->destructor = xsk_destruct_skb), and it is
freshly allocated and not cloned, so skb_unshare() above returns the same
skb with the destructor and the overwritten shinfo intact.
__dev_forward_skb() -> skb_scrub_packet() does not orphan the skb or reset
skb_shinfo(), so the value survives to the destructor.

The RX branch is gated only on the peer's setting:

	if (unlikely(READ_ONCE(peer_ns->tstamp_config.rx_filter) !=
		     HWTSTAMP_FILTER_NONE))
		ptp_info_rx = mock_phc_get_ptp_info(peer_ns->phc);

so any AF_XDP frame forwarded while the peer has RX timestamping enabled
gets a non-NULL, PHC-derived value in xsk_meta.tx_timestamp, and the
destructor performs an 8-byte write to that address.  The mock PHC value is
settable from userspace through clock_settime() on /dev/ptpN via
mock_phc_settime64().

Real NICs avoid this because they only stamp skbs they allocated for RX.
Since netdevsim reuses the TX skb as the RX skb, should it orphan the skb
(clearing skb->destructor) or stamp a private copy before touching
skb_shinfo()->hwtstamps?

>  	if (unlikely(nsim_forward_skb(dev, peer_dev,
>  				      skb, rq, psp_ext) == NET_RX_DROP))
>  		goto out_drop_cnt;
> @@ -185,6 +230,63 @@ static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev)
>  	return NETDEV_TX_OK;
>  }
>  
> +static int nsim_set_ts_config(struct net_device *netdev,
> +			      struct kernel_hwtstamp_config *config,
> +			      struct netlink_ext_ack *extack)
> +{
> +	struct netdevsim *ns = netdev_priv(netdev);
> +	int rx_filter;
> +
> +	if (!ns->phc)
> +		return -EOPNOTSUPP;
> +

[ ... ]

> +static int nsim_get_ts_config(struct net_device *netdev,
> +			      struct kernel_hwtstamp_config *config)
> +{
> +	struct netdevsim *ns = netdev_priv(netdev);
> +
> +	*config = ns->tstamp_config;
> +	return 0;
> +}

[Severity: Low]
[Finding: gpt-5-6-sol-2-6]
[Sources: gpt-5-6-sol, opus-5]
Should nsim_get_ts_config() carry the same !ns->phc check as
nsim_set_ts_config()?

drivers/net/Kconfig allows netdevsim without the mock clock:

	depends on PTP_1588_CLOCK_MOCK || PTP_1588_CLOCK_MOCK=n

and with CONFIG_PTP_1588_CLOCK_MOCK=n the stub is used:

include/linux/ptp_mock.h {
static inline struct mock_phc *mock_phc_create(struct device *dev)
{
	return NULL;
}

nsim_init_netdevsim() only rejects IS_ERR(), so ns->phc stays NULL.  In that
configuration SIOCGHWTSTAMP succeeds and reports tx_type =
HWTSTAMP_TX_OFF / rx_filter = HWTSTAMP_FILTER_NONE, i.e. "hardware
timestamping present but disabled", while nsim_get_ts_info() reports
phc_index = -1 with no hardware so_timestamping and every set attempt is
rejected with -EOPNOTSUPP.  Other converted drivers keep the get/set pair
symmetric here.

[ ... ]

  reply	other threads:[~2026-08-18 13:58 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-15  6:31 [PATCH v10 net-next 0/3] Implement PTP support in netdevsim Maciek Machnikowski
2026-08-15  6:31 ` [PATCH v10 net-next 1/3] ptp_mock: Expose ptp_clock_info to external drivers Maciek Machnikowski
2026-08-15  6:31 ` [PATCH v10 net-next 2/3] netdevsim: Implement basic ptp support Maciek Machnikowski
2026-08-18 13:58   ` Simon Horman [this message]
2026-08-15  6:31 ` [PATCH v10 net-next 3/3] selftests: drivers/net: Implement ptp4l sync test using netdevsim Maciek Machnikowski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260818135800.629288-1-horms@kernel.org \
    --to=horms@kernel.org \
    --cc=andrew@lunn.ch \
    --cc=kuba@kernel.org \
    --cc=maciek@machnikowski.net \
    --cc=milena.olech@intel.com \
    --cc=netdev@vger.kernel.org \
    --cc=richardcochran@gmail.com \
    --cc=vadim.fedorenko@linux.dev \
    --cc=willemdebruijn.kernel@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox