Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,15 @@ netdevsim-dkms/
│ ├── psample.c # Packet sampling (optional)
│ └── macsec.c # MACsec offload (optional)
├── scripts/
│ ├── create-vrt-clocks.sh # Per-Kind-node stand-in mock PHCs (virtual RT)
│ ├── test-dpll.sh # DPLL unit tests (make test-dpll)
│ ├── test-phc.sh # Mock PHC unit tests (make test-phc)
│ ├── test-gnss-ubx.sh # GNSS/UBX unit tests (make test-gnss-ubx)
│ └── test-utm-ubuntu.sh # Local macOS testing via UTM
├── shim/ # CI LD_PRELOAD: phc2sys CLOCK_REALTIME → mock PHC
│ ├── ptp_vrt_shim.c
│ ├── phc2sys-wrapper
│ └── README.md
├── Makefile # Top-level: ordered build, tarball, RPM
├── dkms.conf # DKMS registration (4 modules)
├── install-udev-rule.sh # POST_INSTALL hook
Expand Down
47 changes: 43 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ VERSION := 6.9.5
CONTAINER_IMAGE ?= fedora:39
CONTAINER_CMD ?= podman

.PHONY: all clean modules_install tarball rpm rpm-container test-utm
.PHONY: all clean modules_install tarball rpm rpm-container test-utm \
test-dpll test-phc test-gnss-ubx test-all dkms-install dkms-uninstall

all:
$(MAKE) -C $(KDIR) M=$(PWD)/ptp DKMS_INCLUDE=$(DKMS_INCLUDE) modules
Expand All @@ -34,9 +35,12 @@ all:
modules

clean:
$(MAKE) -C $(KDIR) M=$(PWD)/ptp clean
$(MAKE) -C $(KDIR) M=$(PWD)/dpll clean
$(MAKE) -C $(KDIR) M=$(PWD)/netdevsim clean
find $(PWD)/ptp $(PWD)/dpll $(PWD)/netdevsim \
\( -name '*.o' -o -name '*.ko' -o -name '*.ko.*' \
-o -name '*.mod' -o -name '*.mod.c' -o -name '*.mod.o' \
-o -name '.*.cmd' -o -name 'modules.order' \
-o -name 'Module.symvers' -o -name '.tmp_versions' \) \
-exec rm -rf {} + 2>/dev/null || true
rm -rf $(PWD)/rpmbuild $(PWD)/$(NAME)-$(VERSION).tar.gz

tarball:
Expand Down Expand Up @@ -68,6 +72,41 @@ rpm-container: tarball
bash -c "dnf install -y rpm-build && rpmbuild -bb --define '_topdir /src/rpmbuild' $(NAME)-dkms.spec"
@echo "RPMs written to rpmbuild/RPMS/"

DKMS_SRC := /usr/src/$(NAME)-$(VERSION)

dkms-install: ## Install DKMS modules (requires root)
sudo mkdir -p $(DKMS_SRC)
sudo cp -a Makefile dkms.conf install-udev-rule.sh 99-nsim-ptp.rules \
include ptp dpll netdevsim $(DKMS_SRC)/
sudo dkms add $(NAME)/$(VERSION) 2>/dev/null || true
sudo dkms build --force $(NAME)/$(VERSION)
sudo dkms install --force $(NAME)/$(VERSION)
sudo cp $(DKMS_SRC)/99-nsim-ptp.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
@echo "Installed $(NAME)/$(VERSION) via DKMS"

dkms-uninstall: ## Uninstall DKMS modules (requires root)
-sudo rmmod netdevsim nsim_dpll nsim_ptp_mock nsim_ptp 2>/dev/null
-sudo dkms remove $(NAME)/$(VERSION) --all 2>/dev/null
-sudo rm -rf $(DKMS_SRC)
-sudo rm -f /etc/udev/rules.d/99-nsim-ptp.rules
-sudo udevadm control --reload-rules
@echo "Uninstalled $(NAME)/$(VERSION)"

test-dpll: ## Run DPLL unit tests (requires root, modules loaded)
sudo ./scripts/test-dpll.sh

test-phc: ## Run mock PHC unit tests (requires root, modules loaded)
sudo ./scripts/test-phc.sh

test-gnss-ubx: ## Run GNSS/UBX protocol unit tests (requires root, modules loaded)
sudo ./scripts/test-gnss-ubx.sh

test-all: ## Run all unit tests (dpll + phc + gnss-ubx)
sudo ./scripts/test-dpll.sh
sudo ./scripts/test-phc.sh
sudo ./scripts/test-gnss-ubx.sh

test-utm: rpm-container
./scripts/test-utm.sh \
--rpm $$(ls rpmbuild/RPMS/noarch/$(NAME)-dkms-*.noarch.rpm | head -1) \
Expand Down
49 changes: 40 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,14 @@ substantially different kernel version will likely require source modifications.
### Using DKMS (recommended)

```bash
# Copy source tree to the DKMS source directory
sudo cp -r . /usr/src/netdevsim-6.9.5

# Register and build
sudo dkms add netdevsim/6.9.5
sudo dkms build netdevsim/6.9.5
sudo dkms install netdevsim/6.9.5
# One-shot install (copy sources, dkms add/build/install, udev rules)
make dkms-install

# Or manually:
# sudo cp -r . /usr/src/netdevsim-6.9.5
# sudo dkms add netdevsim/6.9.5
# sudo dkms build netdevsim/6.9.5
# sudo dkms install netdevsim/6.9.5
```

### Manual build (without DKMS)
Expand All @@ -54,8 +55,11 @@ sudo make modules_install
## Uninstallation

```bash
sudo dkms remove netdevsim/6.9.5 --all
sudo rm -rf /usr/src/netdevsim-6.9.5
make dkms-uninstall

# Or manually:
# sudo dkms remove netdevsim/6.9.5 --all
# sudo rm -rf /usr/src/netdevsim-6.9.5
```

## Loading the modules
Expand Down Expand Up @@ -125,6 +129,33 @@ When using netdevsim inside containers or Kubernetes pods:
- **Kubernetes pods:** The udev rule creates real `/dev/ptpN` device
nodes (not symlinks) so they propagate into containers.

- **Virtual CLOCK_REALTIME (CI):** Kind workers share one host
`CLOCK_REALTIME`. Use `scripts/create-vrt-clocks.sh` plus the
`shim/` LD_PRELOAD (packaged in the linuxptp-daemon image) so each
node's `phc2sys -a -r` disciplines a dedicated stand-in mock PHC.
See [shim/README.md](shim/README.md). This is not a second kernel
realtime clock.

## Unit Tests

Requires root and DKMS modules installed (`make dkms-install`). Scripts load
modules unless `--no-load` is passed.

```bash
make test-dpll # DPLL sysfs / netlink / lock_status
make test-phc # mock PHC get/set/adj/EXTTS / sharing
make test-gnss-ubx # GNSS UBX + NMEA signal block/restore
make test-all # all of the above
```

Or run the scripts directly:

```bash
sudo ./scripts/test-dpll.sh [--no-load] [--verbose]
sudo ./scripts/test-phc.sh [--no-load] [--verbose]
sudo ./scripts/test-gnss-ubx.sh [--no-load] [--verbose]
```

## Local libvirt/KVM VMs (Linux x86_64)

### Prerequisites
Expand Down
106 changes: 106 additions & 0 deletions scripts/create-vrt-clocks.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/bin/bash
# Create per-node stand-in mock PHCs that act as virtual CLOCK_REALTIME targets
# for Kind/netdevsim CI (used with the phc2sys LD_PRELOAD shim).
#
# Usage:
# ./create-vrt-clocks.sh [node1] [node2] [node3]
# Defaults: kind-netdevsim-worker kind-netdevsim-worker2 kind-netdevsim-worker3
#
# For each node, creates a 1-port netdevsim device with a unique logical clock,
# writes /var/run/ptp/vrt/device inside that Kind node (seen by linuxptp pods as
# /var/run/vrt/device via the existing hostPath mount).

set -euo pipefail

if [[ $# -eq 0 ]]; then
NODES=(kind-netdevsim-worker kind-netdevsim-worker2 kind-netdevsim-worker3)
else
NODES=("$@")
fi

ARCH=$(uname -m)
if [[ "$ARCH" == "x86_64" ]]; then
PCI_PREFIX="0000:1f"
elif [[ "$ARCH" == "aarch64" ]]; then
PCI_PREFIX="0001:1f"
else
echo "Unsupported architecture: $ARCH" >&2
exit 1
fi

NSIM_NEW=/sys/bus/netdevsim/new_device
NSIM_DEL=/sys/bus/netdevsim/del_device

# Reserved IDs well above the topology devices (1..18) in configSwitch2.sh.
BASE_ID=90
# PCI function slot 0x10+ also above topology (0x01..0x0f).
BASE_FUNC=0x10

runtime_exec() {
local name=$1
shift
if podman inspect "$name" &>/dev/null; then
podman exec "$name" "$@"
elif docker inspect "$name" &>/dev/null; then
docker exec "$name" "$@"
else
echo "Error: Kind node container '$name' not found" >&2
return 1
fi
}

phc_index_for_nsim() {
local nsim_id=$1
local netdir iface idx
netdir="/sys/bus/netdevsim/devices/netdevsim${nsim_id}/net"
iface=$(find "$netdir" -maxdepth 1 -mindepth 1 -type d -printf '%f\n' | head -1)
if [[ -z "$iface" ]]; then
echo "Error: no netdev for netdevsim${nsim_id}" >&2
return 1
fi
idx=$(ethtool -T "$iface" 2>/dev/null | awk '/PTP Hardware Clock:/ {print $4}')
if [[ -z "$idx" || "$idx" == "none" ]]; then
echo "Error: no PHC on $iface (netdevsim${nsim_id})" >&2
return 1
fi
echo "$idx"
}

i=0
for node in "${NODES[@]}"; do
id=$((BASE_ID + i))
clk=$id
func=$((BASE_FUNC + i))
pci=$(printf '%s:%02x.0' "$PCI_PREFIX" "$func")

# Replace any previous VRT device with this id.
echo "$id" >"$NSIM_DEL" 2>/dev/null || true
if ! echo "$id $pci $clk 1" >"$NSIM_NEW"; then
echo "Error: failed to create netdevsim id=$id pci=$pci" >&2
exit 1
fi
udevadm settle 2>/dev/null || sleep 0.5
chmod 666 /dev/nsim_ptp* 2>/dev/null || true

phc=$(phc_index_for_nsim "$id")
dev="/dev/ptp${phc}"
if [[ ! -e "$dev" ]]; then
echo "Error: $dev does not exist after creating netdevsim${id}" >&2
exit 1
fi

# Keep the unused netdev down; only the PHC chardev is needed.
netdir="/sys/bus/netdevsim/devices/netdevsim${id}/net"
iface=$(find "$netdir" -maxdepth 1 -mindepth 1 -type d -printf '%f\n' | head -1)
ip link set dev "$iface" down 2>/dev/null || true

# Node-local path under the hostPath root used by linuxptp-daemon (/var/run/ptp).
runtime_exec "$node" mkdir -p /var/run/ptp/vrt
runtime_exec "$node" bash -c "echo -n '$dev' > /var/run/ptp/vrt/device"
runtime_exec "$node" bash -c "ln -sfn '$dev' /var/run/ptp/vrt/ptpRT"

echo "VRT: node=$node nsim=$id pci=$pci clk=$clk phc=$dev"
i=$((i + 1))
done

echo "Created ${i} virtual RT stand-in PHC(s)"
Loading
Loading