Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: ptp: introduce .supported_perout_flags to ptp_clock_info

The PTP_PEROUT_REQUEST2 ioctl has gained support for flags specifying
specific output behavior including PTP_PEROUT_ONE_SHOT,
PTP_PEROUT_DUTY_CYCLE, PTP_PEROUT_PHASE.

Driver authors are notorious for not checking the flags of the request.
This results in misinterpreting the request, generating an output signal
that does not match the requested value. It is anticipated that even more
flags will be added in the future, resulting in even more broken requests.

Expecting these issues to be caught during review or playing whack-a-mole
after the fact is not a great solution.

Instead, introduce the supported_perout_flags field in the ptp_clock_info
structure. Update the core character device logic to explicitly reject any
request which has a flag not on this list.

This ensures that drivers must 'opt in' to the flags they support. Drivers
which don't set the .supported_perout_flags field will not need to check
that unsupported flags aren't passed, as the core takes care of this.

Update the drivers which do support flags to set this new field.

Note the following driver files set n_per_out to a non-zero value but did
not check the flags at all:

• drivers/ptp/ptp_clockmatrix.c
• drivers/ptp/ptp_idt82p33.c
• drivers/ptp/ptp_fc3.c
• drivers/net/ethernet/ti/am65-cpts.c
• drivers/net/ethernet/aquantia/atlantic/aq_ptp.c
• drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
• drivers/net/dsa/sja1105/sja1105_ptp.c
• drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c
• drivers/net/ethernet/mscc/ocelot_vsc7514.c
• drivers/net/ethernet/intel/i40e/i40e_ptp.c

Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Link: https://patch.msgid.link/20250414-jk-supported-perout-flags-v2-2-f6b17d15475c@intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Jacob Keller and committed by
Jakub Kicinski
d9f3e9ec 7c571ac5

+21 -57
-4
drivers/net/dsa/sja1105/sja1105_ptp.c
··· 737 737 if (perout->index != 0) 738 738 return -EOPNOTSUPP; 739 739 740 - /* Reject requests with unsupported flags */ 741 - if (perout->flags) 742 - return -EOPNOTSUPP; 743 - 744 740 mutex_lock(&ptp_data->lock); 745 741 746 742 rc = sja1105_change_ptp_clk_pin_func(priv, PTP_PF_PEROUT);
+1 -3
drivers/net/ethernet/intel/ice/ice_ptp.c
··· 1797 1797 struct ice_hw *hw = &pf->hw; 1798 1798 int pin_desc_idx; 1799 1799 1800 - if (rq->flags & ~PTP_PEROUT_PHASE) 1801 - return -EOPNOTSUPP; 1802 - 1803 1800 pin_desc_idx = ice_ptp_find_pin_idx(pf, PTP_PF_PEROUT, rq->index); 1804 1801 if (pin_desc_idx < 0) 1805 1802 return -EIO; ··· 2732 2735 info->supported_extts_flags = PTP_RISING_EDGE | 2733 2736 PTP_FALLING_EDGE | 2734 2737 PTP_STRICT_FLAGS; 2738 + info->supported_perout_flags = PTP_PEROUT_PHASE; 2735 2739 2736 2740 switch (pf->hw.mac_type) { 2737 2741 case ICE_MAC_E810:
-4
drivers/net/ethernet/intel/igc/igc_ptp.c
··· 293 293 return 0; 294 294 295 295 case PTP_CLK_REQ_PEROUT: 296 - /* Reject requests with unsupported flags */ 297 - if (rq->perout.flags) 298 - return -EOPNOTSUPP; 299 - 300 296 if (on) { 301 297 pin = ptp_find_pin(igc->ptp_clock, PTP_PF_PEROUT, 302 298 rq->perout.index);
+3 -12
drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c
··· 813 813 return 0; 814 814 } 815 815 816 - static bool mlx5_perout_verify_flags(struct mlx5_core_dev *mdev, unsigned int flags) 817 - { 818 - return ((!mlx5_npps_real_time_supported(mdev) && flags) || 819 - (mlx5_npps_real_time_supported(mdev) && flags & ~PTP_PEROUT_DUTY_CYCLE)); 820 - } 821 - 822 816 static int mlx5_perout_configure(struct ptp_clock_info *ptp, 823 817 struct ptp_clock_request *rq, 824 818 int on) ··· 844 850 rt_mode = mlx5_real_time_mode(mdev); 845 851 846 852 if (!MLX5_PPS_CAP(mdev)) { 847 - err = -EOPNOTSUPP; 848 - goto unlock; 849 - } 850 - 851 - /* Reject requests with unsupported flags */ 852 - if (mlx5_perout_verify_flags(mdev, rq->perout.flags)) { 853 853 err = -EOPNOTSUPP; 854 854 goto unlock; 855 855 } ··· 1018 1030 clock->ptp_info.supported_extts_flags = PTP_RISING_EDGE | 1019 1031 PTP_FALLING_EDGE | 1020 1032 PTP_STRICT_FLAGS; 1033 + 1034 + if (mlx5_npps_real_time_supported(mdev)) 1035 + clock->ptp_info.supported_perout_flags = PTP_PEROUT_DUTY_CYCLE; 1021 1036 1022 1037 for (i = 0; i < clock->ptp_info.n_pins; i++) { 1023 1038 snprintf(clock->ptp_info.pin_config[i].name,
+1 -4
drivers/net/ethernet/microchip/lan743x_ptp.c
··· 463 463 struct lan743x_ptp_perout *perout = &ptp->perout[index]; 464 464 int ret = 0; 465 465 466 - /* Reject requests with unsupported flags */ 467 - if (perout_request->flags & ~PTP_PEROUT_DUTY_CYCLE) 468 - return -EOPNOTSUPP; 469 - 470 466 if (on) { 471 467 perout_pin = ptp_find_pin(ptp->ptp_clock, PTP_PF_PEROUT, 472 468 perout_request->index); ··· 1536 1540 ptp->ptp_clock_info.supported_extts_flags = PTP_RISING_EDGE | 1537 1541 PTP_FALLING_EDGE | 1538 1542 PTP_STRICT_FLAGS; 1543 + ptp->ptp_clock_info.supported_perout_flags = PTP_PEROUT_DUTY_CYCLE; 1539 1544 ptp->ptp_clock_info.pin_config = ptp->pin_config; 1540 1545 ptp->ptp_clock_info.adjfine = lan743x_ptpci_adjfine; 1541 1546 ptp->ptp_clock_info.adjtime = lan743x_ptpci_adjtime;
+2 -4
drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c
··· 815 815 bool pps = false; 816 816 int pin; 817 817 818 - if (rq->perout.flags & ~(PTP_PEROUT_DUTY_CYCLE | 819 - PTP_PEROUT_PHASE)) 820 - return -EOPNOTSUPP; 821 - 822 818 pin = ptp_find_pin(phc->clock, PTP_PF_PEROUT, rq->perout.index); 823 819 if (pin == -1 || pin >= LAN966X_PHC_PINS_NUM) 824 820 return -EINVAL; ··· 970 974 .n_pins = LAN966X_PHC_PINS_NUM, 971 975 .supported_extts_flags = PTP_RISING_EDGE | 972 976 PTP_STRICT_FLAGS, 977 + .supported_perout_flags = PTP_PEROUT_DUTY_CYCLE | 978 + PTP_PEROUT_PHASE, 973 979 }; 974 980 975 981 static int lan966x_ptp_phc_init(struct lan966x *lan966x,
-5
drivers/net/ethernet/mscc/ocelot_ptp.c
··· 211 211 212 212 switch (rq->type) { 213 213 case PTP_CLK_REQ_PEROUT: 214 - /* Reject requests with unsupported flags */ 215 - if (rq->perout.flags & ~(PTP_PEROUT_DUTY_CYCLE | 216 - PTP_PEROUT_PHASE)) 217 - return -EOPNOTSUPP; 218 - 219 214 pin = ptp_find_pin(ocelot->ptp_clock, PTP_PF_PEROUT, 220 215 rq->perout.index); 221 216 if (pin == 0)
+2
drivers/net/ethernet/mscc/ocelot_vsc7514.c
··· 108 108 .n_ext_ts = 0, 109 109 .n_per_out = OCELOT_PTP_PINS_NUM, 110 110 .n_pins = OCELOT_PTP_PINS_NUM, 111 + .supported_perout_flags = PTP_PEROUT_DUTY_CYCLE | 112 + PTP_PEROUT_PHASE, 111 113 .pps = 0, 112 114 .gettime64 = ocelot_ptp_gettime64, 113 115 .settime64 = ocelot_ptp_settime64,
-4
drivers/net/ethernet/renesas/ravb_ptp.c
··· 206 206 unsigned long flags; 207 207 int error = 0; 208 208 209 - /* Reject requests with unsupported flags */ 210 - if (req->flags) 211 - return -EOPNOTSUPP; 212 - 213 209 if (req->index) 214 210 return -EINVAL; 215 211
-3
drivers/net/phy/dp83640.c
··· 506 506 return 0; 507 507 508 508 case PTP_CLK_REQ_PEROUT: 509 - /* Reject requests with unsupported flags */ 510 - if (rq->perout.flags) 511 - return -EOPNOTSUPP; 512 509 if (rq->perout.index >= N_PER_OUT) 513 510 return -EINVAL; 514 511 return periodic_output(clock, rq, on, rq->perout.index);
+2 -7
drivers/net/phy/micrel.c
··· 3236 3236 int pulse_width; 3237 3237 int pin, event; 3238 3238 3239 - /* Reject requests with unsupported flags */ 3240 - if (rq->perout.flags & ~PTP_PEROUT_DUTY_CYCLE) 3241 - return -EOPNOTSUPP; 3242 - 3243 3239 mutex_lock(&shared->shared_lock); 3244 3240 event = rq->perout.index; 3245 3241 pin = ptp_find_pin(shared->ptp_clock, PTP_PF_PEROUT, event); ··· 3911 3915 shared->ptp_clock_info.supported_extts_flags = PTP_RISING_EDGE | 3912 3916 PTP_FALLING_EDGE | 3913 3917 PTP_STRICT_FLAGS; 3918 + shared->ptp_clock_info.supported_perout_flags = PTP_PEROUT_DUTY_CYCLE; 3914 3919 shared->ptp_clock_info.pin_config = shared->pin_config; 3915 3920 shared->ptp_clock_info.n_per_out = LAN8814_PTP_PEROUT_NUM; 3916 3921 shared->ptp_clock_info.adjfine = lan8814_ptpci_adjfine; ··· 5063 5066 int pin; 5064 5067 int ret; 5065 5068 5066 - if (rq->perout.flags & ~PTP_PEROUT_DUTY_CYCLE) 5067 - return -EOPNOTSUPP; 5068 - 5069 5069 pin = ptp_find_pin(ptp_priv->ptp_clock, PTP_PF_PEROUT, rq->perout.index); 5070 5070 if (pin == -1 || pin >= LAN8841_PTP_GPIO_NUM) 5071 5071 return -EINVAL; ··· 5306 5312 .n_per_out = LAN8841_PTP_GPIO_NUM, 5307 5313 .n_ext_ts = LAN8841_PTP_GPIO_NUM, 5308 5314 .n_pins = LAN8841_PTP_GPIO_NUM, 5315 + .supported_perout_flags = PTP_PEROUT_DUTY_CYCLE, 5309 5316 }; 5310 5317 5311 5318 #define LAN8841_OPERATION_MODE_STRAP_LOW_REGISTER 3
+1 -4
drivers/net/phy/microchip_rds_ptp.c
··· 224 224 struct phy_device *phydev = clock->phydev; 225 225 int ret, event_pin, pulsewidth; 226 226 227 - /* Reject requests with unsupported flags */ 228 - if (perout->flags & ~PTP_PEROUT_DUTY_CYCLE) 229 - return -EOPNOTSUPP; 230 - 231 227 event_pin = ptp_find_pin(clock->ptp_clock, PTP_PF_PEROUT, 232 228 perout->index); 233 229 if (event_pin != clock->event_pin) ··· 1255 1259 clock->caps.pps = 0; 1256 1260 clock->caps.n_pins = MCHP_RDS_PTP_N_PIN; 1257 1261 clock->caps.n_per_out = MCHP_RDS_PTP_N_PEROUT; 1262 + clock->caps.supported_perout_flags = PTP_PEROUT_DUTY_CYCLE; 1258 1263 clock->caps.pin_config = clock->pin_config; 1259 1264 clock->caps.adjfine = mchp_rds_ptp_ltc_adjfine; 1260 1265 clock->caps.adjtime = mchp_rds_ptp_ltc_adjtime;
+1 -3
drivers/net/phy/nxp-c45-tja11xx.c
··· 763 763 struct phy_device *phydev = priv->phydev; 764 764 int pin; 765 765 766 - if (perout->flags & ~PTP_PEROUT_PHASE) 767 - return -EOPNOTSUPP; 768 - 769 766 pin = ptp_find_pin(priv->ptp_clock, PTP_PF_PEROUT, perout->index); 770 767 if (pin < 0) 771 768 return pin; ··· 956 959 .supported_extts_flags = PTP_RISING_EDGE | 957 960 PTP_FALLING_EDGE | 958 961 PTP_STRICT_FLAGS, 962 + .supported_perout_flags = PTP_PEROUT_PHASE, 959 963 }; 960 964 961 965 priv->ptp_clock = ptp_clock_register(&priv->caps,
+2
drivers/ptp/ptp_chardev.c
··· 324 324 err = -EINVAL; 325 325 break; 326 326 } 327 + if (req.perout.flags & ~ptp->info->supported_perout_flags) 328 + return -EOPNOTSUPP; 327 329 req.type = PTP_CLK_REQ_PEROUT; 328 330 enable = req.perout.period.sec || req.perout.period.nsec; 329 331 if (mutex_lock_interruptible(&ptp->pincfg_mux))
+6
include/linux/ptp_clock_kernel.h
··· 69 69 * @n_pins: The number of programmable pins. 70 70 * @pps: Indicates whether the clock supports a PPS callback. 71 71 * 72 + * @supported_perout_flags: The set of flags the driver supports for the 73 + * PTP_PEROUT_REQUEST ioctl. The PTP core will 74 + * reject a request with any flag not specified 75 + * here. 76 + * 72 77 * @supported_extts_flags: The set of flags the driver supports for the 73 78 * PTP_EXTTS_REQUEST ioctl. The PTP core will use 74 79 * this list to reject unsupported requests. ··· 190 185 int n_per_out; 191 186 int n_pins; 192 187 int pps; 188 + unsigned int supported_perout_flags; 193 189 unsigned int supported_extts_flags; 194 190 struct ptp_pin_desc *pin_config; 195 191 int (*adjfine)(struct ptp_clock_info *ptp, long scaled_ppm);