diff --git a/.reuse/dep5 b/.reuse/dep5 index ee692170..553e1b8a 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -28,3 +28,7 @@ License: LGPL-2.1-or-later Files: rpm/external/libchrony.patch Copyright: 2023 © Miroslav Lichvar License: LGPL-2.1-or-later + +Files: clkmgr/tool/clknetsim/* +Copyright: 2024 © Erez Geva +License: GPL-2.0-or-later diff --git a/Makefile b/Makefile index e02a882e..8e1ae26d 100644 --- a/Makefile +++ b/Makefile @@ -284,7 +284,7 @@ SRC_FILES_DIR:=$(wildcard README.md t*/*.pl */*/*.m4 .reuse/* */gitlab*\ */github* */*.opt configure.ac src/*.m4 */*.md t*/*.sh */*/*.sh swig/*/*\ */*.i */*/msgCall.i */*/warn.i $(CLKMGR_DIR)/*/*.i man/*\ $(PMC_DIR)/phc_ctl $(PMC_DIR)/*.[ch]* */Makefile [wc]*/*/Makefile\ - $(CLKMGR_DIR)/sample/*.c* $(CLKMGR_DIR)/image/*\ + $(CLKMGR_DIR)/sample/*.c* $(CLKMGR_DIR)/image/* $(CLKMGR_DIR)/tool/clknetsim/*\ */*/*test*/*.go LICENSES/* *.in tools/*.in $(HMAC_SRC)/*.cpp)\ src/ver.h.in src/name.h.in $(SRCS) $(HEADERS_SRCS) LICENSE\ $(MAKEFILE_LIST) credits $(SRCS_CLKMGR) @@ -857,7 +857,7 @@ CLEAN_DIRS:=$(filter %/, $(wildcard wrappers/lua/*/ wrappers/python/*/ rpm/[BRS] wrappers/go/$(SWIG_LNAME) $(filter-out %.md,$(wildcard doc/*)) DISTCLEAN:=configure configure~ defs.mk aclocal.m4 libtool install-sh\ ltmain.sh $(wildcard src/config.h* config.*) -DISTCLEAN_DIRS:=autom4te.cache m4 +DISTCLEAN_DIRS:=autom4te.cache m4 $(CLKMGR_DIR)/sim clean: deb_clean $(Q_CLEAN)$(RM) $(CLEAN) diff --git a/clkmgr/.gitignore b/clkmgr/.gitignore index 0828519e..6c69ee2c 100644 --- a/clkmgr/.gitignore +++ b/clkmgr/.gitignore @@ -12,3 +12,4 @@ /doc/ /utest/utest_* /pub/clkmgr/types*.h +/sim diff --git a/clkmgr/proxy/connect_chrony.cpp b/clkmgr/proxy/connect_chrony.cpp index 20ee10a4..3942d680 100644 --- a/clkmgr/proxy/connect_chrony.cpp +++ b/clkmgr/proxy/connect_chrony.cpp @@ -21,6 +21,7 @@ #include #include #include +#include __CLKMGR_NAMESPACE_USE; @@ -35,6 +36,14 @@ struct ThreadArgs { int report_index; }; +static inline bool isFileSock(const char *file) +{ + struct stat sb; + if(stat(file, &sb) != 0) + return false; + return (sb.st_mode & S_IFMT) == S_IFSOCK; +} + void chrony_notify_client() { PrintDebug("[clkmgr]::notify_client"); @@ -148,8 +157,13 @@ void start_monitor_thread(chrony_session *s, int report_index) void ConnectChrony::connect_chrony() { + const char *chronyd_addr = "/var/run/chrony/chronyd.sock"; + if(!isFileSock(chronyd_addr)) { + PrintInfo("Skip chrony."); + return; + } /* connect to chronyd unix socket*/ - fd = chrony_open_socket("/var/run/chrony/chronyd.sock"); + fd = chrony_open_socket(chronyd_addr); chrony_session *s; if(chrony_init_session(&s, fd) == CHRONY_OK) { start_monitor_thread(s, report_index); diff --git a/clkmgr/proxy/connect_ptp4l.cpp b/clkmgr/proxy/connect_ptp4l.cpp index 074e92d5..18287138 100644 --- a/clkmgr/proxy/connect_ptp4l.cpp +++ b/clkmgr/proxy/connect_ptp4l.cpp @@ -18,10 +18,13 @@ /* libptpmgmt */ #include "msg.h" #include "sock.h" +#include "err.h" #include #include +#include #include +#include __CLKMGR_NAMESPACE_USE; @@ -31,12 +34,22 @@ using namespace ptpmgmt; static const size_t bufSize = 2000; static char buf[bufSize]; static ptpmgmt::Message msg; -static ptpmgmt::Message msgu; static ptpmgmt::SockUnix sku; static ptpmgmt::SockBase *sk = &sku; static ptpmgmt::SUBSCRIBE_EVENTS_NP_t events_tlv; +static atomic sk_init(false); ptp_event clockEvent = { 0 }; +static inline bool connect_uds() +{ + const char *uds_address = getenv("CLKMGR_UDS_ADDRESS"); + if(uds_address == nullptr || *uds_address == 0) + uds_address = "/var/run/ptp4l"; + bool ret = sku.setDefSelfAddress() && sku.init() && + sku.setPeerAddress(uds_address); + sk_init.store(ret); + return ret; +} void notify_client() { PrintDebug("[clkmgr]::notify_client"); @@ -65,6 +78,9 @@ void notify_client() void event_handle() { const BaseMngTlv *data = msg.getData(); + #if 0 + PrintDebug(string("ptp4l handle: ") + msg.mng2str_c(msg.getTlvId())); + #endif switch(msg.getTlvId()) { case TIME_STATUS_NP: { const auto *timeStatus = static_cast(data); @@ -124,43 +140,29 @@ void event_handle() notify_client(); } -static inline bool msg_send(bool local) +static inline bool msg_send(mng_vals_e id) { static int seq = 0; - ptpmgmt::Message *m; - if(local) - m = &msgu; - else - m = &msg; - MNG_PARSE_ERROR_e err = m->build(buf, bufSize, seq); + MNG_PARSE_ERROR_e err = msg.build(buf, bufSize, seq); if(err != MNG_PARSE_ERROR_OK) { - PrintError(string("build error ") + msg.err2str_c(err)); + PrintError(string("ptp4l msg build error ") + msg.err2str_c(err)); return false; } - bool ret; - ret = sk->send(buf, m->getMsgLen()); - if(!ret) { - #if 0 - PrintError("send failed"); - #endif - return false; + bool ret = sk->send(buf, msg.getMsgLen()); + if(ret) { + seq++; + return true; } - seq++; - return true; + #if 0 + PrintError("ptp4l send failed: " + Error::getError() + ", ID: " + + msg.mng2str_c(id)); + #endif + return false; } -static inline bool msg_set_action(bool local, mng_vals_e id) +static inline bool msg_send_id(mng_vals_e id) { - bool ret; - if(local) - ret = msgu.setAction(GET, id); - else - ret = msg.setAction(GET, id); - if(!ret) { - PrintError(string("Fail get ") + msg.mng2str_c(id)); - return false; - } - return msg_send(local); + return msg.setAction(GET, id) && msg_send(id); } /** @@ -185,19 +187,14 @@ static inline bool msg_set_action(bool local, mng_vals_e id) * sent, false otherwise. * */ -bool event_subscription(clkmgr_handle **handle) +bool event_subscription(clkmgr_handle **handle = nullptr) { - memset(events_tlv.bitmask, 0, sizeof events_tlv.bitmask); - events_tlv.duration = UINT16_MAX; - events_tlv.setEvent(NOTIFY_TIME_SYNC); - events_tlv.setEvent(NOTIFY_PORT_STATE); - events_tlv.setEvent(NOTIFY_CMLDS); - if(!msg.setAction(SET, SUBSCRIBE_EVENTS_NP, &events_tlv)) { + const mng_vals_e id = SUBSCRIBE_EVENTS_NP; + if(!msg.setAction(SET, id, &events_tlv)) { PrintError("Fail set SUBSCRIBE_EVENTS_NP"); return false; } - bool ret = msg_send(false); - return ret; + return msg_send(id); } /** @@ -223,33 +220,44 @@ void *ptp4l_event_loop(void *arg) { const uint64_t timeout_ms = 1000; bool lost_connection = false; + // Ensure socket is initilized before we start + while(!sk_init.load()) + sleep(1); for(;;) { - if(!msg_set_action(true, PORT_PROPERTIES_NP)) + if(!msg_send_id(PORT_PROPERTIES_NP)) break; ssize_t cnt; - sk->poll(timeout_ms); - cnt = sk->rcv(buf, bufSize); - if(cnt > 0) { - MNG_PARSE_ERROR_e err = msg.parse(buf, cnt); - if(err == MNG_PARSE_ERROR_OK) { - event_handle(); - break; + if(sk->poll(timeout_ms)) { + cnt = sk->rcv(buf, bufSize); + if(cnt > 0) { + MNG_PARSE_ERROR_e err = msg.parse(buf, cnt); + if(err == MNG_PARSE_ERROR_OK) { + event_handle(); + break; + } } } + #if 0 + else + PrintError("ptp4l first poll fail: " + Error::getError()); + #endif } - msg_set_action(false, PORT_PROPERTIES_NP); - event_subscription(nullptr); + msg_send_id(PORT_PROPERTIES_NP); + event_subscription(); for(;;) { - if(sk->poll(timeout_ms) > 0) { - const auto cnt = sk->rcv(buf, bufSize); + if(sk->poll(timeout_ms)) { + const ssize_t cnt = sk->rcv(buf, bufSize); if(cnt > 0) { MNG_PARSE_ERROR_e err = msg.parse(buf, cnt); if(err == MNG_PARSE_ERROR_OK) event_handle(); } } else { + #if 0 + PrintError("ptp4l poll fail: " + Error::getError()); + #endif for(;;) { - if(event_subscription(nullptr)) + if(event_subscription()) break; if(!lost_connection) { PrintError("Lost connection to ptp4l."); @@ -290,14 +298,17 @@ void *ptp4l_event_loop(void *arg) int Connect::connect(uint8_t transport_specific) { clockEvent.ptp4l_id = 1; - const char *uds_address = "/var/run/ptp4l"; - if(!sku.setDefSelfAddress() || !sku.init() || - !sku.setPeerAddress(uds_address)) + if(!connect_uds()) return -1; /* Set Transport Specific */ MsgParams prms = msg.getParams(); prms.transportSpecific = transport_specific; msg.updateParams(prms); + // Prepare Events + events_tlv.duration = UINT16_MAX; + events_tlv.setEvent(NOTIFY_TIME_SYNC); + events_tlv.setEvent(NOTIFY_PORT_STATE); + events_tlv.setEvent(NOTIFY_CMLDS); handle_connect(); return 0; } diff --git a/clkmgr/tool/clknetsim/futex.patch b/clkmgr/tool/clknetsim/futex.patch new file mode 100644 index 00000000..756b7573 --- /dev/null +++ b/clkmgr/tool/clknetsim/futex.patch @@ -0,0 +1,59 @@ +From fd356c3cf075fd5b89cc98e8f8f1a971a699e99c Mon Sep 17 00:00:00 2001 +From: Erez Geva +Date: Wed, 5 Feb 2025 09:45:56 +0100 +Subject: [PATCH] Add futex system call. + +Signed-off-by: Erez Geva +--- + client.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/client.c b/client.c +index 12923de..7968548 100644 +--- a/client.c ++++ b/client.c +@@ -63,6 +63,7 @@ + #ifdef SO_TIMESTAMPING + #include + #include ++#include + #endif + + #include "protocol.h" +@@ -129,6 +130,7 @@ static int (*_usleep)(useconds_t usec); + static void (*_srandom)(unsigned int seed); + static int (*_shmget)(key_t key, size_t size, int shmflg); + static void *(*_shmat)(int shmid, const void *shmaddr, int shmflg); ++static long *(*_syscall)(long number, ...); + + static unsigned int node; + static int initializing = 0; +@@ -280,6 +282,7 @@ static void init_symbols(void) { + _srandom = (void (*)(unsigned int seed))dlsym(RTLD_NEXT, "srandom"); + _shmget = (int (*)(key_t key, size_t size, int shmflg))dlsym(RTLD_NEXT, "shmget"); + _shmat = (void *(*)(int shmid, const void *shmaddr, int shmflg))dlsym(RTLD_NEXT, "shmat"); ++ _syscall = (long *(*)(long number, ...))dlsym(RTLD_NEXT, "syscall"); + + initialized_symbols = 1; + } +@@ -2920,6 +2923,17 @@ long syscall(long number, ...) { + } + break; + #endif ++ case __NR_futex: ++ { ++ uint32_t *uaddr = va_arg(ap, uint32_t *); ++ int futex_op = va_arg(ap, int); ++ uint32_t val = va_arg(ap, uint32_t); ++ struct timespec *timeout = va_arg(ap, struct timespec *); ++ uint32_t *uaddr2 = va_arg(ap, uint32_t *); ++ uint32_t val3 = va_arg(ap, uint32_t); ++ r = syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3); ++ } ++ break; + default: + assert(0); + } +-- +2.39.5 + diff --git a/clkmgr/tool/clknetsim/gettid.patch b/clkmgr/tool/clknetsim/gettid.patch new file mode 100644 index 00000000..7c594062 --- /dev/null +++ b/clkmgr/tool/clknetsim/gettid.patch @@ -0,0 +1,27 @@ +From c2f41da8111e8ec0b1ee1f213a7e454fb3f10cb3 Mon Sep 17 00:00:00 2001 +From: Erez Geva +Date: Sun, 9 Feb 2025 01:25:44 +0100 +Subject: [PATCH] Add gettid system call. + +Signed-off-by: Erez Geva +--- + client.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/client.c b/client.c +index 7968548..af11b63 100644 +--- a/client.c ++++ b/client.c +@@ -2934,6 +2934,9 @@ long syscall(long number, ...) { + r = syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3); + } + break; ++ case __NR_gettid: ++ r = syscall(SYS_gettid); ++ break; + default: + assert(0); + } +-- +2.39.5 + diff --git a/clkmgr/tool/clknetsim/series b/clkmgr/tool/clknetsim/series new file mode 100644 index 00000000..37445a25 --- /dev/null +++ b/clkmgr/tool/clknetsim/series @@ -0,0 +1,2 @@ +futex.patch +gettid.patch diff --git a/clkmgr/tool/sim.sh b/clkmgr/tool/sim.sh new file mode 100755 index 00000000..6fd5665a --- /dev/null +++ b/clkmgr/tool/sim.sh @@ -0,0 +1,193 @@ +#!/bin/bash -e +# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-FileCopyrightText: Copyright © 2024 Intel Corporation. +# +# @author Erez Geva +# @copyright © 2024 Intel Corporation. +# +# Simulation using clknetsim +############################################################################### +equit() +{ + echo "$@" + exit 1 +} +s_cp() +{ + local s="$1" + [[ -f "$s" ]] || return + if [[ -f "$sim_tgt/$s" ]]; then + if ! cmp -s "$s" "$sim_tgt/$s"; then + cp "$s" "$sim_tgt/$s" + build=true + fi + else + cp "$s" "$sim_tgt/" + build=true + fi +} +clk_client() +{ + LD_PRELOAD=$CLKNETSIM_PATH/clknetsim.so CLKNETSIM_NODE=$c_node\ + CLKNETSIM_SOCKET=$CLKNETSIM_TMPDIR/sock LD_LIBRARY_PATH=.libs\ + $@ &> $CLKNETSIM_TMPDIR/log.$c_node & + local lastpid=$! + disown $lastpid + client_pids+=" $lastpid" +} +clk_proxy() +{ + local uds_srv=$uds_base/$1 + LD_PRELOAD=$CLKNETSIM_PATH/clknetsim.so CLKNETSIM_NODE=$c_node\ + CLKNETSIM_SOCKET=$CLKNETSIM_TMPDIR/sock LD_LIBRARY_PATH=.libs\ + CLKMGR_UDS_ADDRESS=$uds_srv\ + clkmgr/proxy/clkmgr_proxy &> $CLKNETSIM_TMPDIR/log.$c_node & + local lastpid=$! + disown $lastpid + client_pids+=" $lastpid" +} +c_sig() +{ + if [[ -n "$client_pids" ]]; then + echo "Check $client_pids" + set +e + local p + for p in $client_pids; do + if pgrep $p; then + echo "kill $p" + kill $p + fi + done + client_pids='' + # Ensure server is killed + if pgrep clknetsim; then + echo 'kill clknetsim' + killall clknetsim + fi + fi +} +c_ctrl() +{ + echo '' + c_sig +} +prepare_clknetsim() +{ + # Clock simulation comes from: + # https://gitlab.com/chrony/clknetsim + # https://github.com/mlichvar/clknetsim + [[ -f "$base/$CLKNETSIM_PATH/clknetsim.bash" ]] || equit 'clknetsim is missing' + # Build clknetsim + local sim_tgt=$base/$CLKNETSIM_PATH/patches + mkdir -p $sim_tgt + local n build=false + # Copy patches + cd $base/clkmgr/tool/clknetsim + for n in * + do s_cp "$n";done + cd "$base/$CLKNETSIM_PATH" + if $build || ! [[ -f .pc/applied-patches ]] || + ! cmp patches/series .pc/applied-patches; then + quilt pop -af || true + quilt push -a + build=true + fi + if $build || ! [[ -x clknetsim ]] || ! [[ -f clknetsim.so ]]; then + make + fi +} +main() +{ + local -r base="$(realpath "$(dirname "$0")/../..")" + local -r CLKNETSIM_PATH=../clknetsim + local -r CLKNETSIM_TMPDIR=clkmgr/sim + local -r uds_base=/clknetsim/unix + prepare_clknetsim + cd "$base" + local a_patch + # Probe linuxptp + if [[ -f ../linuxptp/ptp4l.c ]]; then + [[ -x ../linuxptp/ptp4l ]] || make -C ../linuxptp --no-print-directory + a_patch=:../linuxptp + elif [[ -z "$(which ptp4l)" ]]; then + equit 'ptp4l is missing' + fi + # Probe chrony + if [[ -z "$(which chronyd)" ]]; then + [[ -f '../chrony/ntp_io_linux.c' ]] || equit 'chronyd is missing' + if ! [[ -x ../chrony/chronyd ]]; then + cd ../chrony + ./configure + make + cd "$base" + fi + a_patch+=:../chrony + fi + # Add path to linuxptp and chrony + [[ -z "$a_patch" ]] || local PATH=$PATH$a_patch + + # Make local and sample + if ! [[ clkmgr/proxy/clkmgr_proxy ]]; then + make + fi + if ! [[ clkmgr/sample/clkmgr_test ]]; then + make -C clkmgr/sample --no-print-directory + fi + + # Prepare simulation envirounment + local client_pids + local -i c_node=0 + export CLKNETSIM_UNIX_SUBNET=2 + rm -f $CLKNETSIM_TMPDIR/log.[0-9]* $CLKNETSIM_TMPDIR/conf.[0-9]* + . $CLKNETSIM_PATH/clknetsim.bash + + # Test configuraton + generate_config4 '1' '1 2 | 2 4 | 3 4 | 4 5' 0.01\ + '(sum (* 1e-9 (normal)))'\ + '(* 1e-8 (exponential))' + echo 'node5_start = 100' >> $CLKNETSIM_TMPDIR/conf + +#generate_config4 '1' '1 2' 0.01\ +# '(sum (* 1e-9 (normal)))'\ +# '(* 1e-8 (exponential))' + + local -r c_if=eth0 + local -ir run_time='60 * 5' # time limit in seconds for clknetsim server + + # Trap signals + trap c_ctrl INT # Ctrl^C + trap c_sig TERM # Normal Termination + trap c_sig QUIT # Quit from keyboard + trap c_sig HUP # Hangup detected + trap c_sig EXIT # on exit + trap c_sig ERR # + + # UDS = $uds_base/: + + # Start clients + c_node='c_node + 1' + start_client $c_node ptp4l '' '' "-i $c_if" + + c_node='c_node + 1' + start_client $c_node ptp4l '' '' "-i $c_if" +# "uds_address $uds_base/$c_node:1" + + c_node='c_node + 1' + start_client $c_node chronyd +# "refclock PHC /dev/ptp0 poll -6 dpoll -1" + + c_node='c_node + 1' + clk_proxy 2:1 + +# TODO fix crash +#c_node='c_node + 1' +#clk_client clkmgr/sample/clkmgr_test + + # Run test with clknetsim server + set +e + start_server $c_node -l $run_time -n 2 + set -e + client_pids='' + cat $CLKNETSIM_TMPDIR/log.4 +} +main "$@"