net/mlx5: Implement PTM cross timestamping support
authorRahul Rameshbabu <rrameshbabu@nvidia.com>
Tue, 30 Jul 2024 13:40:54 +0000 (16:40 +0300)
committerJakub Kicinski <kuba@kernel.org>
Mon, 5 Aug 2024 23:44:44 +0000 (16:44 -0700)
Expose Precision Time Measurement support through related PTP ioctl.

The performance of PTM on ConnectX-7 was evaluated using both real-time
(RTC) and free-running (FRC) clocks under traffic and no traffic
conditions. Tests with phc2sys measured the maximum offset values at a 50Hz
rate, with and without PTM.

Results:

1. No traffic
+-----+--------+--------+
|     | No-PTM | PTM    |
+-----+--------+--------+
| FRC | 125 ns | <29 ns |
+-----+--------+--------+
| RTC | 248 ns | <34 ns |
+-----+--------+--------+

2. With traffic
+-----+--------+--------+
|     | No-PTM | PTM    |
+-----+--------+--------+
| FRC | 254 ns | <40 ns |
+-----+--------+--------+
| RTC | 255 ns | <45 ns |
+-----+--------+--------+

Signed-off-by: Rahul Rameshbabu <rrameshbabu@nvidia.com>
Co-developed-by: Carolina Jubran <cjubran@nvidia.com>
Signed-off-by: Carolina Jubran <cjubran@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
Tested-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Link: https://patch.msgid.link/20240730134055.1835261-4-tariqt@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c

index 0361741..b306ae7 100644 (file)
 #include "lib/eq.h"
 #include "en.h"
 #include "clock.h"
+#ifdef CONFIG_X86
+#include <linux/timekeeping.h>
+#include <linux/cpufeature.h>
+#endif /* CONFIG_X86 */
 
 enum {
        MLX5_PIN_MODE_IN                = 0x0,
@@ -148,6 +152,87 @@ static int mlx5_set_mtutc(struct mlx5_core_dev *dev, u32 *mtutc, u32 size)
                                    MLX5_REG_MTUTC, 0, 1);
 }
 
+#ifdef CONFIG_X86
+static bool mlx5_is_ptm_source_time_available(struct mlx5_core_dev *dev)
+{
+       u32 out[MLX5_ST_SZ_DW(mtptm_reg)] = {0};
+       u32 in[MLX5_ST_SZ_DW(mtptm_reg)] = {0};
+       int err;
+
+       if (!MLX5_CAP_MCAM_REG3(dev, mtptm))
+               return false;
+
+       err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out), MLX5_REG_MTPTM,
+                                  0, 0);
+       if (err)
+               return false;
+
+       return !!MLX5_GET(mtptm_reg, out, psta);
+}
+
+static int mlx5_mtctr_syncdevicetime(ktime_t *device_time,
+                                    struct system_counterval_t *sys_counterval,
+                                    void *ctx)
+{
+       u32 out[MLX5_ST_SZ_DW(mtctr_reg)] = {0};
+       u32 in[MLX5_ST_SZ_DW(mtctr_reg)] = {0};
+       struct mlx5_core_dev *mdev = ctx;
+       bool real_time_mode;
+       u64 host, device;
+       int err;
+
+       real_time_mode = mlx5_real_time_mode(mdev);
+
+       MLX5_SET(mtctr_reg, in, first_clock_timestamp_request,
+                MLX5_MTCTR_REQUEST_PTM_ROOT_CLOCK);
+       MLX5_SET(mtctr_reg, in, second_clock_timestamp_request,
+                real_time_mode ? MLX5_MTCTR_REQUEST_REAL_TIME_CLOCK :
+                MLX5_MTCTR_REQUEST_FREE_RUNNING_COUNTER);
+
+       err = mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out), MLX5_REG_MTCTR,
+                                  0, 0);
+       if (err)
+               return err;
+
+       if (!MLX5_GET(mtctr_reg, out, first_clock_valid) ||
+           !MLX5_GET(mtctr_reg, out, second_clock_valid))
+               return -EINVAL;
+
+       host = MLX5_GET64(mtctr_reg, out, first_clock_timestamp);
+       *sys_counterval = (struct system_counterval_t) {
+                       .cycles = host,
+                       .cs_id = CSID_X86_ART,
+                       .use_nsecs = true,
+       };
+
+       device = MLX5_GET64(mtctr_reg, out, second_clock_timestamp);
+       if (real_time_mode)
+               *device_time = ns_to_ktime(REAL_TIME_TO_NS(device >> 32, device & U32_MAX));
+       else
+               *device_time = mlx5_timecounter_cyc2time(&mdev->clock, device);
+
+       return 0;
+}
+
+static int mlx5_ptp_getcrosststamp(struct ptp_clock_info *ptp,
+                                  struct system_device_crosststamp *cts)
+{
+       struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, ptp_info);
+       struct system_time_snapshot history_begin = {0};
+       struct mlx5_core_dev *mdev;
+
+       mdev = container_of(clock, struct mlx5_core_dev, clock);
+
+       if (!mlx5_is_ptm_source_time_available(mdev))
+               return -EBUSY;
+
+       ktime_get_snapshot(&history_begin);
+
+       return get_device_system_crosststamp(mlx5_mtctr_syncdevicetime, mdev,
+                                            &history_begin, cts);
+}
+#endif /* CONFIG_X86 */
+
 static u64 mlx5_read_time(struct mlx5_core_dev *dev,
                          struct ptp_system_timestamp *sts,
                          bool real_time)
@@ -1034,6 +1119,12 @@ static void mlx5_init_timer_clock(struct mlx5_core_dev *mdev)
        if (MLX5_CAP_MCAM_REG(mdev, mtutc))
                mlx5_init_timer_max_freq_adjustment(mdev);
 
+#ifdef CONFIG_X86
+       if (MLX5_CAP_MCAM_REG3(mdev, mtptm) &&
+           MLX5_CAP_MCAM_REG3(mdev, mtctr) && boot_cpu_has(X86_FEATURE_ART))
+               clock->ptp_info.getcrosststamp = mlx5_ptp_getcrosststamp;
+#endif /* CONFIG_X86 */
+
        mlx5_timecounter_init(mdev);
        mlx5_init_clock_info(mdev);
        mlx5_init_overflow_period(clock);