Merge branch 'convert-3-drivers-to-ndo_hwtstamp-api'

Vadim Fedorenko says:

====================
convert 3 drivers to ndo_hwtstamp API

Convert tg3, bnxt_en and mlx5 to use ndo_hwtstamp API. These 3 drivers
were chosen because I have access to the HW and is able to test the
changes. Also there is a selftest provided to validated that the driver
correctly sets up timestamp configuration, according to what is exposed
as supported by the hardware. Selftest allows driver to fallback to some
wider scope of RX timestamping, i.e. it allows the driver to set up
ptpv2-event filter when ptpv2-l2-event is requested.
====================

Link: https://patch.msgid.link/20250923173310.139623-1-vadim.fedorenko@linux.dev
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2025-09-24 18:14:26 -07:00
6 changed files with 166 additions and 64 deletions
+2 -6
View File
@@ -13278,12 +13278,6 @@ static int bnxt_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return bnxt_hwrm_port_phy_write(bp, mdio->phy_id, mdio->reg_num,
mdio->val_in);
case SIOCSHWTSTAMP:
return bnxt_hwtstamp_set(dev, ifr);
case SIOCGHWTSTAMP:
return bnxt_hwtstamp_get(dev, ifr);
default:
/* do nothing */
break;
@@ -15806,6 +15800,8 @@ static const struct net_device_ops bnxt_netdev_ops = {
.ndo_xdp_xmit = bnxt_xdp_xmit,
.ndo_bridge_getlink = bnxt_bridge_getlink,
.ndo_bridge_setlink = bnxt_bridge_setlink,
.ndo_hwtstamp_get = bnxt_hwtstamp_get,
.ndo_hwtstamp_set = bnxt_hwtstamp_set,
};
static void bnxt_get_queue_stats_rx(struct net_device *dev, int i,
+16 -19
View File
@@ -560,10 +560,11 @@ static int bnxt_hwrm_ptp_cfg(struct bnxt *bp)
return bnxt_ptp_cfg_tstamp_filters(bp);
}
int bnxt_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
int bnxt_hwtstamp_set(struct net_device *dev,
struct kernel_hwtstamp_config *stmpconf,
struct netlink_ext_ack *extack)
{
struct bnxt *bp = netdev_priv(dev);
struct hwtstamp_config stmpconf;
struct bnxt_ptp_cfg *ptp;
u16 old_rxctl;
int old_rx_filter, rc;
@@ -573,17 +574,14 @@ int bnxt_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
if (!ptp)
return -EOPNOTSUPP;
if (copy_from_user(&stmpconf, ifr->ifr_data, sizeof(stmpconf)))
return -EFAULT;
if (stmpconf.tx_type != HWTSTAMP_TX_ON &&
stmpconf.tx_type != HWTSTAMP_TX_OFF)
if (stmpconf->tx_type != HWTSTAMP_TX_ON &&
stmpconf->tx_type != HWTSTAMP_TX_OFF)
return -ERANGE;
old_rx_filter = ptp->rx_filter;
old_rxctl = ptp->rxctl;
old_tx_tstamp_en = ptp->tx_tstamp_en;
switch (stmpconf.rx_filter) {
switch (stmpconf->rx_filter) {
case HWTSTAMP_FILTER_NONE:
ptp->rxctl = 0;
ptp->rx_filter = HWTSTAMP_FILTER_NONE;
@@ -616,7 +614,7 @@ int bnxt_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
return -ERANGE;
}
if (stmpconf.tx_type == HWTSTAMP_TX_ON)
if (stmpconf->tx_type == HWTSTAMP_TX_ON)
ptp->tx_tstamp_en = 1;
else
ptp->tx_tstamp_en = 0;
@@ -625,9 +623,8 @@ int bnxt_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
if (rc)
goto ts_set_err;
stmpconf.rx_filter = ptp->rx_filter;
return copy_to_user(ifr->ifr_data, &stmpconf, sizeof(stmpconf)) ?
-EFAULT : 0;
stmpconf->rx_filter = ptp->rx_filter;
return 0;
ts_set_err:
ptp->rx_filter = old_rx_filter;
@@ -636,22 +633,22 @@ ts_set_err:
return rc;
}
int bnxt_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
int bnxt_hwtstamp_get(struct net_device *dev,
struct kernel_hwtstamp_config *stmpconf)
{
struct bnxt *bp = netdev_priv(dev);
struct hwtstamp_config stmpconf;
struct bnxt_ptp_cfg *ptp;
ptp = bp->ptp_cfg;
if (!ptp)
return -EOPNOTSUPP;
stmpconf.flags = 0;
stmpconf.tx_type = ptp->tx_tstamp_en ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
stmpconf->flags = 0;
stmpconf->tx_type = ptp->tx_tstamp_en ? HWTSTAMP_TX_ON
: HWTSTAMP_TX_OFF;
stmpconf.rx_filter = ptp->rx_filter;
return copy_to_user(ifr->ifr_data, &stmpconf, sizeof(stmpconf)) ?
-EFAULT : 0;
stmpconf->rx_filter = ptp->rx_filter;
return 0;
}
static int bnxt_map_regs(struct bnxt *bp, u32 *reg_arr, int count, int reg_win)
@@ -160,8 +160,11 @@ void bnxt_ptp_update_current_time(struct bnxt *bp);
void bnxt_ptp_pps_event(struct bnxt *bp, u32 data1, u32 data2);
int bnxt_ptp_cfg_tstamp_filters(struct bnxt *bp);
void bnxt_ptp_reapply_pps(struct bnxt *bp);
int bnxt_hwtstamp_set(struct net_device *dev, struct ifreq *ifr);
int bnxt_hwtstamp_get(struct net_device *dev, struct ifreq *ifr);
int bnxt_hwtstamp_set(struct net_device *dev,
struct kernel_hwtstamp_config *stmpconf,
struct netlink_ext_ack *extack);
int bnxt_hwtstamp_get(struct net_device *dev,
struct kernel_hwtstamp_config *stmpconf);
void bnxt_ptp_free_txts_skbs(struct bnxt_ptp_cfg *ptp);
int bnxt_ptp_get_txts_prod(struct bnxt_ptp_cfg *ptp, u16 *prod);
void bnxt_get_tx_ts_p5(struct bnxt *bp, struct sk_buff *skb, u16 prod);
+29 -37
View File
@@ -13929,22 +13929,20 @@ static void tg3_self_test(struct net_device *dev, struct ethtool_test *etest,
}
static int tg3_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
static int tg3_hwtstamp_set(struct net_device *dev,
struct kernel_hwtstamp_config *stmpconf,
struct netlink_ext_ack *extack)
{
struct tg3 *tp = netdev_priv(dev);
struct hwtstamp_config stmpconf;
if (!tg3_flag(tp, PTP_CAPABLE))
return -EOPNOTSUPP;
if (copy_from_user(&stmpconf, ifr->ifr_data, sizeof(stmpconf)))
return -EFAULT;
if (stmpconf.tx_type != HWTSTAMP_TX_ON &&
stmpconf.tx_type != HWTSTAMP_TX_OFF)
if (stmpconf->tx_type != HWTSTAMP_TX_ON &&
stmpconf->tx_type != HWTSTAMP_TX_OFF)
return -ERANGE;
switch (stmpconf.rx_filter) {
switch (stmpconf->rx_filter) {
case HWTSTAMP_FILTER_NONE:
tp->rxptpctl = 0;
break;
@@ -14004,74 +14002,72 @@ static int tg3_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
tw32(TG3_RX_PTP_CTL,
tp->rxptpctl | TG3_RX_PTP_CTL_HWTS_INTERLOCK);
if (stmpconf.tx_type == HWTSTAMP_TX_ON)
if (stmpconf->tx_type == HWTSTAMP_TX_ON)
tg3_flag_set(tp, TX_TSTAMP_EN);
else
tg3_flag_clear(tp, TX_TSTAMP_EN);
return copy_to_user(ifr->ifr_data, &stmpconf, sizeof(stmpconf)) ?
-EFAULT : 0;
return 0;
}
static int tg3_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
static int tg3_hwtstamp_get(struct net_device *dev,
struct kernel_hwtstamp_config *stmpconf)
{
struct tg3 *tp = netdev_priv(dev);
struct hwtstamp_config stmpconf;
if (!tg3_flag(tp, PTP_CAPABLE))
return -EOPNOTSUPP;
stmpconf.flags = 0;
stmpconf.tx_type = (tg3_flag(tp, TX_TSTAMP_EN) ?
HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF);
stmpconf->flags = 0;
stmpconf->tx_type = tg3_flag(tp, TX_TSTAMP_EN) ?
HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
switch (tp->rxptpctl) {
case 0:
stmpconf.rx_filter = HWTSTAMP_FILTER_NONE;
stmpconf->rx_filter = HWTSTAMP_FILTER_NONE;
break;
case TG3_RX_PTP_CTL_RX_PTP_V1_EN | TG3_RX_PTP_CTL_ALL_V1_EVENTS:
stmpconf.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
stmpconf->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
break;
case TG3_RX_PTP_CTL_RX_PTP_V1_EN | TG3_RX_PTP_CTL_SYNC_EVNT:
stmpconf.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_SYNC;
stmpconf->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_SYNC;
break;
case TG3_RX_PTP_CTL_RX_PTP_V1_EN | TG3_RX_PTP_CTL_DELAY_REQ:
stmpconf.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ;
stmpconf->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ;
break;
case TG3_RX_PTP_CTL_RX_PTP_V2_EN | TG3_RX_PTP_CTL_ALL_V2_EVENTS:
stmpconf.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
stmpconf->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
break;
case TG3_RX_PTP_CTL_RX_PTP_V2_L2_EN | TG3_RX_PTP_CTL_ALL_V2_EVENTS:
stmpconf.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
stmpconf->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
break;
case TG3_RX_PTP_CTL_RX_PTP_V2_L4_EN | TG3_RX_PTP_CTL_ALL_V2_EVENTS:
stmpconf.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
stmpconf->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
break;
case TG3_RX_PTP_CTL_RX_PTP_V2_EN | TG3_RX_PTP_CTL_SYNC_EVNT:
stmpconf.rx_filter = HWTSTAMP_FILTER_PTP_V2_SYNC;
stmpconf->rx_filter = HWTSTAMP_FILTER_PTP_V2_SYNC;
break;
case TG3_RX_PTP_CTL_RX_PTP_V2_L2_EN | TG3_RX_PTP_CTL_SYNC_EVNT:
stmpconf.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_SYNC;
stmpconf->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_SYNC;
break;
case TG3_RX_PTP_CTL_RX_PTP_V2_L4_EN | TG3_RX_PTP_CTL_SYNC_EVNT:
stmpconf.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_SYNC;
stmpconf->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_SYNC;
break;
case TG3_RX_PTP_CTL_RX_PTP_V2_EN | TG3_RX_PTP_CTL_DELAY_REQ:
stmpconf.rx_filter = HWTSTAMP_FILTER_PTP_V2_DELAY_REQ;
stmpconf->rx_filter = HWTSTAMP_FILTER_PTP_V2_DELAY_REQ;
break;
case TG3_RX_PTP_CTL_RX_PTP_V2_L2_EN | TG3_RX_PTP_CTL_DELAY_REQ:
stmpconf.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ;
stmpconf->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ;
break;
case TG3_RX_PTP_CTL_RX_PTP_V2_L4_EN | TG3_RX_PTP_CTL_DELAY_REQ:
stmpconf.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ;
stmpconf->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ;
break;
default:
WARN_ON_ONCE(1);
return -ERANGE;
}
return copy_to_user(ifr->ifr_data, &stmpconf, sizeof(stmpconf)) ?
-EFAULT : 0;
return 0;
}
static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
@@ -14126,12 +14122,6 @@ static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return err;
case SIOCSHWTSTAMP:
return tg3_hwtstamp_set(dev, ifr);
case SIOCGHWTSTAMP:
return tg3_hwtstamp_get(dev, ifr);
default:
/* do nothing */
break;
@@ -14407,6 +14397,8 @@ static const struct net_device_ops tg3_netdev_ops = {
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = tg3_poll_controller,
#endif
.ndo_hwtstamp_get = tg3_hwtstamp_get,
.ndo_hwtstamp_set = tg3_hwtstamp_set,
};
static void tg3_get_eeprom_size(struct tg3 *tp)
@@ -15,6 +15,7 @@ TEST_PROGS = \
iou-zcrx.py \
irq.py \
loopback.sh \
nic_timestamp.py \
pp_alloc_fail.py \
rss_api.py \
rss_ctx.py \
+113
View File
@@ -0,0 +1,113 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
"""
Tests related to configuration of HW timestamping
"""
import errno
from lib.py import ksft_run, ksft_exit, ksft_ge, ksft_eq, KsftSkipEx
from lib.py import NetDrvEnv, EthtoolFamily, NlError
def __get_hwtimestamp_support(cfg):
""" Retrieve supported configuration information """
try:
tsinfo = cfg.ethnl.tsinfo_get({'header': {'dev-name': cfg.ifname}})
except NlError as e:
if e.error == errno.EOPNOTSUPP:
raise KsftSkipEx("timestamping configuration is not supported") from e
raise
ctx = {}
tx = tsinfo.get('tx-types', {})
rx = tsinfo.get('rx-filters', {})
bits = tx.get('bits', {})
ctx['tx'] = bits.get('bit', [])
bits = rx.get('bits', {})
ctx['rx'] = bits.get('bit', [])
return ctx
def __get_hwtimestamp_config(cfg):
""" Retrieve current TS configuration information """
try:
tscfg = cfg.ethnl.tsconfig_get({'header': {'dev-name': cfg.ifname}})
except NlError as e:
if e.error == errno.EOPNOTSUPP:
raise KsftSkipEx("timestamping configuration is not supported via netlink") from e
raise
return tscfg
def __set_hwtimestamp_config(cfg, ts):
""" Setup new TS configuration information """
ts['header'] = {'dev-name': cfg.ifname}
try:
res = cfg.ethnl.tsconfig_set(ts)
except NlError as e:
if e.error == errno.EOPNOTSUPP:
raise KsftSkipEx("timestamping configuration is not supported via netlink") from e
raise
return res
def test_hwtstamp_tx(cfg):
"""
Test TX timestamp configuration.
The driver should apply provided config and report back proper state.
"""
orig_tscfg = __get_hwtimestamp_config(cfg)
ts = __get_hwtimestamp_support(cfg)
tx = ts['tx']
for t in tx:
tscfg = orig_tscfg
tscfg['tx-types']['bits']['bit'] = [t]
res = __set_hwtimestamp_config(cfg, tscfg)
if res is None:
res = __get_hwtimestamp_config(cfg)
ksft_eq(res['tx-types']['bits']['bit'], [t])
__set_hwtimestamp_config(cfg, orig_tscfg)
def test_hwtstamp_rx(cfg):
"""
Test RX timestamp configuration.
The filter configuration is taken from the list of supported filters.
The driver should apply the config without error and report back proper state.
Some extension of the timestamping scope is allowed for PTP filters.
"""
orig_tscfg = __get_hwtimestamp_config(cfg)
ts = __get_hwtimestamp_support(cfg)
rx = ts['rx']
for r in rx:
tscfg = orig_tscfg
tscfg['rx-filters']['bits']['bit'] = [r]
res = __set_hwtimestamp_config(cfg, tscfg)
if res is None:
res = __get_hwtimestamp_config(cfg)
if r['index'] == 0 or r['index'] == 1:
ksft_eq(res['rx-filters']['bits']['bit'][0]['index'], r['index'])
else:
# the driver can fallback to some value which has higher coverage for timestamping
ksft_ge(res['rx-filters']['bits']['bit'][0]['index'], r['index'])
__set_hwtimestamp_config(cfg, orig_tscfg)
def main() -> None:
""" Ksft boiler plate main """
with NetDrvEnv(__file__, nsim_test=False) as cfg:
cfg.ethnl = EthtoolFamily()
ksft_run([test_hwtstamp_tx, test_hwtstamp_rx], args=(cfg,))
ksft_exit()
if __name__ == "__main__":
main()