Re: [PATCH v2] posix-timers: add multi_clock_gettime system call

From: Richard Cochran
Date: Mon Nov 27 2023 - 19:12:06 EST


On Mon, Nov 27, 2023 at 05:39:01PM +0200, Sagi Maimon wrote:
> Some user space applications need to read some clocks.
> Each read requires moving from user space to kernel space.
> This asymmetry causes the measured offset to have a significant error.

Adding time/clock gurus (jstultz, tglx) on CC for visibility...

Thanks,
Richard


>
> Introduce a new system call multi_clock_gettime, which can be used to measure
> the offset between multiple clocks, from variety of types: PHC, virtual PHC
> and various system clocks (CLOCK_REALTIME, CLOCK_MONOTONIC, etc).
> The offset includes the total time that the driver needs to read the clock
> timestamp.
>
> New system call allows the reading of a list of clocks - up to PTP_MAX_CLOCKS.
> Supported clocks IDs: PHC, virtual PHC and various system clocks.
> Up to PTP_MAX_SAMPLES times (per clock) in a single system call read.
> The system call returns n_clocks timestamps for each measurement:
> - clock 0 timestamp
> - ...
> - clock n timestamp
>
> Signed-off-by: Sagi Maimon <maimon.sagi@xxxxxxxxx>
> ---
> Addressed comments from:
> - Richard Cochran : https://www.spinics.net/lists/netdev/msg951723.html
>
> Changes since version 1:
> - Change multi PHC ioctl implamantation into systemcall.
>
> arch/x86/entry/syscalls/syscall_32.tbl | 1 +
> arch/x86/entry/syscalls/syscall_64.tbl | 1 +
> include/linux/posix-timers.h | 24 ++++++++++
> include/linux/syscalls.h | 3 +-
> include/uapi/asm-generic/unistd.h | 12 ++++-
> kernel/sys_ni.c | 1 +
> kernel/time/posix-timers.c | 62 ++++++++++++++++++++++++++
> 7 files changed, 102 insertions(+), 2 deletions(-)
>
> diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
> index c8fac5205803..070efd266e7e 100644
> --- a/arch/x86/entry/syscalls/syscall_32.tbl
> +++ b/arch/x86/entry/syscalls/syscall_32.tbl
> @@ -461,3 +461,4 @@
> 454 i386 futex_wake sys_futex_wake
> 455 i386 futex_wait sys_futex_wait
> 456 i386 futex_requeue sys_futex_requeue
> +457 i386 multi_clock_gettime sys_multi_clock_gettime32
> \ No newline at end of file
> diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
> index 8cb8bf68721c..f790330244bb 100644
> --- a/arch/x86/entry/syscalls/syscall_64.tbl
> +++ b/arch/x86/entry/syscalls/syscall_64.tbl
> @@ -378,6 +378,7 @@
> 454 common futex_wake sys_futex_wake
> 455 common futex_wait sys_futex_wait
> 456 common futex_requeue sys_futex_requeue
> +457 common multi_clock_gettime sys_multi_clock_gettime
>
> #
> # Due to a historical design error, certain syscalls are numbered differently
> diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
> index d607f51404fc..426a45441ab5 100644
> --- a/include/linux/posix-timers.h
> +++ b/include/linux/posix-timers.h
> @@ -260,4 +260,28 @@ void set_process_cpu_timer(struct task_struct *task, unsigned int clock_idx,
> int update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new);
>
> void posixtimer_rearm(struct kernel_siginfo *info);
> +
> +#define MULTI_PTP_MAX_CLOCKS 12 /* Max number of clocks */
> +#define MULTI_PTP_MAX_SAMPLES 10 /* Max allowed offset measurement samples. */
> +
> +struct __ptp_multi_clock_get {
> + unsigned int n_clocks; /* Desired number of clocks. */
> + unsigned int n_samples; /* Desired number of measurements per clock. */
> + const clockid_t clkid_arr[MULTI_PTP_MAX_CLOCKS]; /* list of clock IDs */
> + /*
> + * Array of list of n_clocks clocks time samples n_samples times.
> + */
> + struct __kernel_timespec ts[MULTI_PTP_MAX_SAMPLES][MULTI_PTP_MAX_CLOCKS];
> +};
> +
> +struct __ptp_multi_clock_get32 {
> + unsigned int n_clocks; /* Desired number of clocks. */
> + unsigned int n_samples; /* Desired number of measurements per clock. */
> + const clockid_t clkid_arr[MULTI_PTP_MAX_CLOCKS]; /* list of clock IDs */
> + /*
> + * Array of list of n_clocks clocks time samples n_samples times.
> + */
> + struct old_timespec32 ts[MULTI_PTP_MAX_SAMPLES][MULTI_PTP_MAX_CLOCKS];
> +};
> +
> #endif
> diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
> index fd9d12de7e92..afcf68e83d63 100644
> --- a/include/linux/syscalls.h
> +++ b/include/linux/syscalls.h
> @@ -1161,7 +1161,8 @@ asmlinkage long sys_mmap_pgoff(unsigned long addr, unsigned long len,
> unsigned long prot, unsigned long flags,
> unsigned long fd, unsigned long pgoff);
> asmlinkage long sys_old_mmap(struct mmap_arg_struct __user *arg);
> -
> +asmlinkage long sys_multi_clock_gettime(struct __ptp_multi_clock_get __user * ptp_multi_clk_get);
> +asmlinkage long sys_multi_clock_gettime32(struct __ptp_multi_clock_get32 __user * ptp_multi_clk_get);
>
> /*
> * Not a real system call, but a placeholder for syscalls which are
> diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
> index 756b013fb832..3ebcaa052650 100644
> --- a/include/uapi/asm-generic/unistd.h
> +++ b/include/uapi/asm-generic/unistd.h
> @@ -829,8 +829,18 @@ __SYSCALL(__NR_futex_wait, sys_futex_wait)
> #define __NR_futex_requeue 456
> __SYSCALL(__NR_futex_requeue, sys_futex_requeue)
>
> +#if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32
> +#define __NR_multi_clock_gettime 457
> +__SC_3264(__NR_multi_clock_gettime, sys_multi_clock_gettime32, sys_multi_clock_gettime)
> +#endif
> +
> +#if defined(__SYSCALL_COMPAT) || __BITS_PER_LONG == 32
> +#define __NR_multi_clock_gettime64 458
> +__SYSCALL(__NR_multi_clock_gettime64, sys_multi_clock_gettime)
> +#endif
> +
> #undef __NR_syscalls
> -#define __NR_syscalls 457
> +#define __NR_syscalls 459
>
> /*
> * 32 bit systems traditionally used different
> diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
> index e1a6e3c675c0..8ed1c22f40ac 100644
> --- a/kernel/sys_ni.c
> +++ b/kernel/sys_ni.c
> @@ -335,6 +335,7 @@ COND_SYSCALL(ppoll_time32);
> COND_SYSCALL_COMPAT(ppoll_time32);
> COND_SYSCALL(utimensat_time32);
> COND_SYSCALL(clock_adjtime32);
> +COND_SYSCALL(multi_clock_gettime32);
>
> /*
> * The syscalls below are not found in include/uapi/asm-generic/unistd.h
> diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
> index b924f0f096fa..517558af2479 100644
> --- a/kernel/time/posix-timers.c
> +++ b/kernel/time/posix-timers.c
> @@ -1426,6 +1426,68 @@ SYSCALL_DEFINE4(clock_nanosleep_time32, clockid_t, which_clock, int, flags,
>
> #endif
>
> +SYSCALL_DEFINE1(multi_clock_gettime, struct __ptp_multi_clock_get __user *, ptp_multi_clk_get)
> +{
> + const struct k_clock *kc;
> + struct timespec64 kernel_tp;
> + struct __ptp_multi_clock_get multi_clk_get;
> + int error;
> + unsigned int i, j;
> +
> + if (copy_from_user(&multi_clk_get, ptp_multi_clk_get, sizeof(multi_clk_get)))
> + return -EFAULT;
> +
> + if (multi_clk_get.n_samples > MULTI_PTP_MAX_SAMPLES)
> + return -EINVAL;
> + if (multi_clk_get.n_clocks > MULTI_PTP_MAX_CLOCKS)
> + return -EINVAL;
> +
> + for (j = 0; j < multi_clk_get.n_samples; j++) {
> + for (i = 0; i < multi_clk_get.n_clocks; i++) {
> + kc = clockid_to_kclock(multi_clk_get.clkid_arr[i]);
> + if (!kc)
> + return -EINVAL;
> + error = kc->clock_get_timespec(multi_clk_get.clkid_arr[i], &kernel_tp);
> + if (!error && put_timespec64(&kernel_tp, (struct __kernel_timespec __user *)
> + &ptp_multi_clk_get->ts[j][i]))
> + error = -EFAULT;
> + }
> + }
> +
> + return error;
> +}
> +
> +SYSCALL_DEFINE1(multi_clock_gettime32, struct __ptp_multi_clock_get32 __user *, ptp_multi_clk_get)
> +{
> + const struct k_clock *kc;
> + struct timespec64 kernel_tp;
> + struct __ptp_multi_clock_get multi_clk_get;
> + int error;
> + unsigned int i, j;
> +
> + if (copy_from_user(&multi_clk_get, ptp_multi_clk_get, sizeof(multi_clk_get)))
> + return -EFAULT;
> +
> + if (multi_clk_get.n_samples > MULTI_PTP_MAX_SAMPLES)
> + return -EINVAL;
> + if (multi_clk_get.n_clocks > MULTI_PTP_MAX_CLOCKS)
> + return -EINVAL;
> +
> + for (j = 0; j < multi_clk_get.n_samples; j++) {
> + for (i = 0; i < multi_clk_get.n_clocks; i++) {
> + kc = clockid_to_kclock(multi_clk_get.clkid_arr[i]);
> + if (!kc)
> + return -EINVAL;
> + error = kc->clock_get_timespec(multi_clk_get.clkid_arr[i], &kernel_tp);
> + if (!error && put_old_timespec32(&kernel_tp, (struct old_timespec32 __user *)
> + &ptp_multi_clk_get->ts[j][i]))
> + error = -EFAULT;
> + }
> + }
> +
> + return error;
> +}
> +
> static const struct k_clock clock_realtime = {
> .clock_getres = posix_get_hrtimer_res,
> .clock_get_timespec = posix_get_realtime_timespec,
> --
> 2.26.3
>