Skip to content

net: add SO_TIMESTAMPING support for PKT sockets - #20161

Merged
xiaoxiang781216 merged 10 commits into
apache:masterfrom
wenquan2015:so-timestamping
Sep 18, 2026
Merged

xiaoxiang781216 merged 10 commits into
apache:masterfrom
wenquan2015:so-timestamping

Conversation

@wenquan2015

@wenquan2015 wenquan2015 commented Sep 16, 2026

Copy link
Copy Markdown

Summary

Add Linux-compatible SO_TIMESTAMPING socket option support to NuttX, enabling hardware and software TX/RX packet timestamping on PKT
sockets with MSG_ERRQUEUE delivery. This is the kernel infrastructure needed by PTP (IEEE 1588) daemons and network latency
measurement tools.

The series is organized as three foundation patches (from the Xiaomi Vela team) that modernize the existing SO_TIMESTAMP
infrastructure, followed by five patches that build SO_TIMESTAMPING on top.

What changed (8 commits)

  1. nuttx: modify "d_rxtime" and use "iob->io_time" to instead in newer netdev driver

    — Move RX timestamp storage from net_driver_s.d_rxtime into iob_s.io_time, so each IOB carries its own timestamp through the
    stack. Remove the old iob_trycopyin/iob_copyout timestamp packing in CAN/PKT/UDP paths. Fix iob_clone_partial to copy io_time from
    source before the source pointer advances to NULL.

  2. nuttx: create "cmsg_store_timestamp" function

    — Extract a common cmsg_store_timestamp() helper in net/utils/net_cmsg.c that checks SO_TIMESTAMP/SO_TIMESTAMPNS via s_options
    and appends the appropriate cmsg. Replaces per-protocol timestamp cmsg formatting in CAN, PKT, and UDP receive paths.

  3. nuttx: add NETDEV_RX_STAMP flag in d_features

    — Replace the compile-time CONFIG_ARCH_HAVE_NETDEV_TIMESTAMP Kconfig with a runtime NETDEV_RX_STAMP bit in
    net_driver_s.d_features. Drivers that provide hardware RX timestamps set the flag at probe time; the stack checks it at runtime to
    decide whether to fill software timestamps.

  4. net/pkt: support option SO_TIMESTAMPING and MSG_ERRQUEUE

    — Add SO_TIMESTAMPING TX path for PKT sockets: tagged TX packets loop back through the driver with io_conn set, are routed into
    conn->errahead, and delivered to userspace via recvmsg(MSG_ERRQUEUE) with SO_TIMESTAMPING cmsg containing struct timespec[3]. Add
    poll(POLLPRI) notification when errahead is non-empty.

  5. net/pkt: fix time schedule problem when receive MSG_ERRQUEUE

    — Fix a scheduling issue where MSG_ERRQUEUE readiness was not properly waking poll waiters in pkt_netpoll.c.

  6. net/socket: use s_options for SO_TIMESTAMP instead of per-conn field

    — Remove the redundant udp_conn_s.timestamp field. The socket-level setsockopt/getsockopt already manages
    SO_TIMESTAMP/SO_TIMESTAMPNS via the s_options bitmask; the protocol-level handlers in inet_sockif.c were shadowing this, causing
    SO_TIMESTAMPNS to never work. Remove the protocol-level intercepts so both options are handled uniformly at the socket layer.

  7. net/socket: merge CONFIG_NET_TIMESTAMPING into CONFIG_NET_TIMESTAMP

    — Consolidate the two Kconfig options (CONFIG_NET_TIMESTAMP and CONFIG_NET_TIMESTAMPING) into a single CONFIG_NET_TIMESTAMP that
    covers SO_TIMESTAMP, SO_TIMESTAMPNS, and SO_TIMESTAMPING.

  8. include/sys/socket.h: add SCM_TIMESTAMPNS and SCM_TIMESTAMPING macros

    — Add missing SCM_TIMESTAMPNS and SCM_TIMESTAMPING control message type definitions for use with recvmsg() cmsg parsing.

Impact

  • New functionality: SO_TIMESTAMPING socket option with TX software timestamping and MSG_ERRQUEUE delivery on PKT sockets.
    poll(POLLPRI) notification for errqueue readiness.
  • Bug fix: SO_TIMESTAMPNS was broken on UDP sockets because inet_sockif.c intercepted SO_TIMESTAMP before the socket layer,
    preventing s_options from being set. Now both SO_TIMESTAMP and SO_TIMESTAMPNS work correctly.
  • Bug fix: iob_clone_partial could dereference NULL when copying io_time after the source IOB chain was fully consumed.
  • Refactoring: Timestamp storage moved from d_rxtime to iob_s.io_time; per-protocol cmsg formatting replaced with common
    cmsg_store_timestamp() helper; compile-time ARCH_HAVE_NETDEV_TIMESTAMP replaced with runtime NETDEV_RX_STAMP flag.
  • No behavioral change for existing callers when CONFIG_NET_TIMESTAMP is disabled.

Testing

 #include <nuttx/config.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <unistd.h>
  #include <errno.h>
  #include <arpa/inet.h>
  #include <net/if.h>
  #include <netpacket/packet.h>
  #include <sys/socket.h>
  #include <sys/time.h>
  #include <poll.h>

  /* Test 1: Verify SO_TIMESTAMPING setsockopt/getsockopt */

  static int test_setsockopt(void)
  {
    int fd, val, ret = 0;
    socklen_t len;

    printf("=== Test 1: SO_TIMESTAMPING setsockopt/getsockopt ===\n");
    fd = socket(AF_PACKET, SOCK_RAW, htons(0x0003));
    if (fd < 0) { printf("FAIL: socket: %d\n", errno); return -1; }

    val = SOF_TIMESTAMPING_TX_SOFTWARE;
    if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val)) < 0)
      { printf("FAIL: setsockopt: %d\n", errno); ret = -1; goto out; }
    printf("  setsockopt SO_TIMESTAMPING TX: OK\n");

    len = sizeof(val); val = 0;
    if (getsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0)
      { printf("FAIL: getsockopt: %d\n", errno); ret = -1; goto out; }
    printf("  getsockopt: flags=0x%x %s\n", val, val ? "OK" : "FAIL");
    if (!val) ret = -1;
  out:
    close(fd); return ret;
  }

  /* Test 2: SO_TIMESTAMP backward compatibility */

  static int test_so_timestamp(void)
  {
    int fd, val, ret = 0;
    printf("\n=== Test 2: SO_TIMESTAMP backward compatibility ===\n");
    fd = socket(AF_PACKET, SOCK_RAW, htons(0x0003));
    if (fd < 0) { printf("FAIL: socket: %d\n", errno); return -1; }
    val = 1;
    if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &val, sizeof(val)) < 0)
      { printf("FAIL: SO_TIMESTAMP: %d\n", errno); ret = -1; goto out; }
    printf("  setsockopt SO_TIMESTAMP: OK\n");
    if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPNS, &val, sizeof(val)) < 0)
      { printf("FAIL: SO_TIMESTAMPNS: %d\n", errno); ret = -1; goto out; }
    printf("  setsockopt SO_TIMESTAMPNS: OK\n");
  out:
    close(fd); return ret;
  }

  /* Test 3: TX timestamping + MSG_ERRQUEUE */

  static int test_tx_timestamp(void)
  {
    int fd, val, ret = 0;
    unsigned int ifindex;
    struct sockaddr_ll addr;
    char buf[64], cmsgbuf[256];
    struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg;
    ssize_t n;

    printf("\n=== Test 3: TX timestamping + MSG_ERRQUEUE ===\n");
    fd = socket(AF_PACKET, SOCK_DGRAM, htons(0x0800));
    if (fd < 0) { printf("FAIL: socket: %d\n", errno); return -1; }

    val = SOF_TIMESTAMPING_TX_SOFTWARE;
    setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val));

    ifindex = if_nametoindex("eth0");
    if (!ifindex) { printf("  eth0 not found, skip\n"); goto out; }

    memset(&addr, 0, sizeof(addr));
    addr.sll_family = AF_PACKET; addr.sll_protocol = htons(0x0003);
    addr.sll_ifindex = ifindex;
    bind(fd, (struct sockaddr *)&addr, sizeof(addr));

    memset(buf, 0xaa, 46);
    addr.sll_halen = 6; memset(addr.sll_addr, 0xff, 6);
    n = sendto(fd, buf, 46, 0, (struct sockaddr *)&addr, sizeof(addr));
    if (n < 0) { printf("FAIL: send: %d\n", errno); ret = -1; goto out; }
    printf("  sent %zd bytes\n", n);

    usleep(10000);
    memset(&msg, 0, sizeof(msg));
    iov.iov_base = buf; iov.iov_len = sizeof(buf);
    msg.msg_iov = &iov; msg.msg_iovlen = 1;
    msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf);

    n = recvmsg(fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT);
    if (n < 0) { printf("  MSG_ERRQUEUE: %d\n", errno); goto out; }
    printf("  recvmsg MSG_ERRQUEUE: %zd bytes, flags=0x%x\n", n, msg.msg_flags);

    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
      if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMPING)
        { struct timespec *ts = (struct timespec *)CMSG_DATA(cmsg);
          printf("  TX timestamp: %lld.%09ld\n  PASS!\n",
                 (long long)ts[0].tv_sec, ts[0].tv_nsec); goto out; }
    printf("  WARN: no SO_TIMESTAMPING cmsg\n");
  out:
    close(fd); return ret;
  }

  /* Test 4: RX timestamp via SO_TIMESTAMP */

  static int test_rx_timestamp(void)
  {
    int fd, val, ret = 0, retry;
    unsigned int ifindex;
    struct sockaddr_ll addr;
    char buf[128], cmsgbuf[256];
    struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg;
    ssize_t n;

    printf("\n=== Test 4: RX timestamp via SO_TIMESTAMP ===\n");
    fd = socket(AF_PACKET, SOCK_RAW, htons(0x0003));
    if (fd < 0) { printf("FAIL: socket: %d\n", errno); return -1; }

    val = 1;
    setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &val, sizeof(val));

    ifindex = if_nametoindex("eth0");
    if (!ifindex) { printf("  eth0 not found, skip\n"); goto out; }

    memset(&addr, 0, sizeof(addr));
    addr.sll_family = AF_PACKET; addr.sll_protocol = htons(0x0003);
    addr.sll_ifindex = ifindex;
    bind(fd, (struct sockaddr *)&addr, sizeof(addr));

    /* Send broadcast via separate SOCK_DGRAM to trigger RX on RAW socket */
    { int sfd = socket(AF_PACKET, SOCK_DGRAM, htons(0x0800));
      if (sfd >= 0) {
        struct sockaddr_ll sa = {0};
        sa.sll_family = AF_PACKET; sa.sll_protocol = htons(0x0800);
        sa.sll_ifindex = ifindex; sa.sll_halen = 6;
        memset(sa.sll_addr, 0xff, 6); memset(buf, 0xcc, 46);
        sendto(sfd, buf, 46, 0, (struct sockaddr *)&sa, sizeof(sa));
        close(sfd); printf("  sent broadcast to trigger RX\n");
    } }

    memset(&msg, 0, sizeof(msg));
    iov.iov_base = buf; iov.iov_len = sizeof(buf);
    msg.msg_iov = &iov; msg.msg_iovlen = 1;
    msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf);

    for (retry = 0; retry < 10; retry++)
      { usleep(50000); n = recvmsg(fd, &msg, MSG_DONTWAIT); if (n > 0) break; }

    if (n <= 0) { printf("  no RX packet\n"); goto out; }
    printf("  recvmsg: %zd bytes\n", n);

    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
      if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMP)
        { struct timeval *tv = (struct timeval *)CMSG_DATA(cmsg);
          printf("  RX timestamp: %lld.%06ld\n  PASS!\n",
                 (long long)tv->tv_sec, (long)tv->tv_usec); goto out; }
    printf("  WARN: no timestamp cmsg\n");
  out:
    close(fd); return ret;
  }

  /* Test 5: TX timestamping with poll(POLLPRI) */

  static int test_tx_timestamp_poll(void)
  {
    int fd, val, ret = 0;
    unsigned int ifindex;
    struct sockaddr_ll addr;
    char buf[64], cmsgbuf[256];
    struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg;
    struct pollfd pfd;
    ssize_t n;

    printf("\n=== Test 5: TX timestamping with poll() ===\n");
    fd = socket(AF_PACKET, SOCK_DGRAM, htons(0x0800));
    if (fd < 0) { printf("FAIL: socket: %d\n", errno); return -1; }

    val = SOF_TIMESTAMPING_TX_SOFTWARE;
    setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val));

    ifindex = if_nametoindex("eth0");

    memset(buf, 0xaa, 46);
    n = sendto(fd, buf, 46, 0, (struct sockaddr *)&addr, sizeof(addr));
    if (n < 0) { printf("FAIL: send: %d\n", errno); ret = -1; goto out; }
    printf("  sent %zd bytes\n", n);

    pfd.fd = fd; pfd.events = POLLPRI; pfd.revents = 0;
    n = poll(&pfd, 1, 1000);
    if (n <= 0) { printf("  poll timeout/error\n"); ret = -1; goto out; }
    printf("  poll: revents=0x%x %s\n", pfd.revents,
           (pfd.revents & POLLPRI) ? "(POLLPRI)" : "(unexpected)");
    if (!(pfd.revents & POLLPRI)) { ret = -1; goto out; }

    memset(&msg, 0, sizeof(msg));
    iov.iov_base = buf; iov.iov_len = sizeof(buf);
    msg.msg_iov = &iov; msg.msg_iovlen = 1;
    msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf);

    n = recvmsg(fd, &msg, MSG_ERRQUEUE);
    if (n < 0) { printf("FAIL: recvmsg: %d\n", errno); ret = -1; goto out; }
    printf("  recvmsg: %zd bytes, flags=0x%x\n", n, msg.msg_flags);

    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
      if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMPING)
        { struct timespec *ts = (struct timespec *)CMSG_DATA(cmsg);
          printf("  TX timestamp: %lld.%09ld\n", (long long)ts[0].tv_sec,
                 ts[0].tv_nsec);
          printf("  PASS: poll + MSG_ERRQUEUE works!\n"); goto out; }
    printf("  WARN: no SO_TIMESTAMPING cmsg\n");
  out:
    close(fd); return ret;
  }

  int main(int argc, FAR char *argv[])
  {
    int fail = 0;
    printf("SO_TIMESTAMPING Test Suite\n=========================\n\n");
    fail += (test_setsockopt() < 0);
    fail += (test_so_timestamp() < 0);
    fail += (test_tx_timestamp() < 0);
    fail += (test_rx_timestamp() < 0);
    fail += (test_tx_timestamp_poll() < 0);
    printf("\n=========================\n");
    printf(fail ? "%d test(s) FAILED\n" : "All tests PASSED\n", fail);
    return fail;
  }

Tested on NuttX sim (sim:dynconns with CONFIG_NET_TIMESTAMP=y, CONFIG_NET_PKT=y) using TAP networking:

  1. SO_TIMESTAMPING setsockopt/getsockopt — set TX flags, read back, verified flag value ✅
  2. SO_TIMESTAMP/SO_TIMESTAMPNS backward compatibility — setsockopt both options on PKT socket ✅
  3. TX timestamping + MSG_ERRQUEUE — send packet, recvmsg(MSG_ERRQUEUE) returns SO_TIMESTAMPING cmsg with valid CLOCK_REALTIME
    timestamp ✅
  4. RX timestamp via SO_TIMESTAMP — receive broadcast packet, cmsg contains valid struct timeval timestamp ✅
  5. TX timestamping with poll(POLLPRI) — send packet, poll() returns POLLPRI, then recvmsg(MSG_ERRQUEUE) returns valid TX timestamp

    SO_TIMESTAMPING Test Suite
    === Test 1: SO_TIMESTAMPING setsockopt/getsockopt ===
    setsockopt SO_TIMESTAMPING TX: OK
    getsockopt SO_TIMESTAMPING: flags=0x1
    TX flag set: OK (val=0x1)
    === Test 2: SO_TIMESTAMP backward compatibility ===
    setsockopt SO_TIMESTAMP: OK
    setsockopt SO_TIMESTAMPNS: OK
    === Test 3: TX timestamping + MSG_ERRQUEUE ===
    sent 46 bytes
    recvmsg MSG_ERRQUEUE: got 46 bytes, flags=0x2000
    TX timestamp: 1789550304.500344702
    PASS: SO_TIMESTAMPING TX works!
    === Test 4: RX timestamp via SO_TIMESTAMP ===
    recvmsg: got 60 bytes
    RX timestamp: 1789550304.519840
    PASS: SO_TIMESTAMP RX works!
    === Test 5: TX timestamping with poll() ===
    poll returned: revents=0xa (POLLPRI)
    recvmsg: got 46 bytes, flags=0x2000
    TX timestamp: 1789550304.579824479
    PASS: poll + MSG_ERRQUEUE works!
    All tests PASSED

depends-on: apache/nuttx-apps/pull/3788

@github-actions github-actions Bot added Arch: arm Issues related to ARM (32-bit) architecture Arch: simulator Issues related to the SIMulator Area: Memory Management Memory Management issues Size: L The size of the change in this PR is large labels Sep 16, 2026
@xiaoxiang781216

Copy link
Copy Markdown
Contributor

@daniel-p-carvalho please review and try this pr for hw timestamp.

@github-actions

github-actions Bot commented Sep 16, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

No memory changes detected for:

@acassis

acassis commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

@wenquan2015 @xiaoxiang781216 this modification is causing an error on CI

@acassis

acassis commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

@wenquan2015 suggestion: create testing with this sample code at apps/testing/nettest/timestamp/

@wenquan2015

Copy link
Copy Markdown
Author

@wenquan2015 suggestion: create testing with this sample code at apps/testing/nettest/timestamp/

I'll create a separate nuttx-apps PR to add the SO_TIMESTAMPING test program under
apps/testing/nettest/timestamp/

@wenquan2015
wenquan2015 force-pushed the so-timestamping branch 3 times, most recently from 10b32ca to 699f7b4 Compare September 16, 2026 12:58
@github-actions github-actions Bot added Size: XL The size of the change in this PR is very large. Consider breaking down the PR into smaller pieces. and removed Size: L The size of the change in this PR is large labels Sep 16, 2026
@daniel-p-carvalho

Copy link
Copy Markdown
Contributor

@daniel-p-carvalho please review and try this pr for hw timestamp.

Tried this on real hardware (RMII Ethernet, STM32H743BI). The SO_TIMESTAMPING infrastructure itself is all correct - setsockopt/getsockopt, cmsg, the error queue, poll(POLLPRI), everything works exactly as expected.

What I found: on real hardware, TX timestamp delivery depends on the transmitted frame looping back through the receive path (io_conn match in pkt_in()). That works fine in the simulator (which already has that loopback simulated), but on a real Ethernet board, full-duplex, point-to-point, the transmitted frame never shows up on RX again - so poll/recvmsg never fire. Probably just wasn't caught because testing this needs real physical hardware with a second point on the network, not just the simulator.

I put together a prototype to close that last gap: a new function (pkt_tx_timestamp_complete()) that lets the driver deliver the timestamp directly from the TX-complete interrupt, without depending on any loopback - which is how hardware TX timestamping normally works (read the completed descriptor). Wired it into the STM32H7 driver as a reference and validated it end to end: poll/recvmsg now deliver correctly.

Left the branches available in case they're useful:

daniel-p-carvalho/apache-nuttx#test/pr20161-h7-tx-timestamp (the new delivery path + driver wiring)
daniel-p-carvalho/nuttx-apps#test/pr20161-h7-tx-timestamp (the test I used to validate it)
Happy to help if there's anything I can do.

@github-actions

Copy link
Copy Markdown

🔗 Cross-repo PR dependencies

The read-only Build run reported the following dependent PR(s) and fetched head SHA(s):

CI run: https://github.com/apache/nuttx/actions/runs/35173950789

@wenquan2015

Copy link
Copy Markdown
Author

@daniel-p-carvalho please review and try this pr for hw timestamp.

Tried this on real hardware (RMII Ethernet, STM32H743BI). The SO_TIMESTAMPING infrastructure itself is all correct - setsockopt/getsockopt, cmsg, the error queue, poll(POLLPRI), everything works exactly as expected.

What I found: on real hardware, TX timestamp delivery depends on the transmitted frame looping back through the receive path (io_conn match in pkt_in()). That works fine in the simulator (which already has that loopback simulated), but on a real Ethernet board, full-duplex, point-to-point, the transmitted frame never shows up on RX again - so poll/recvmsg never fire. Probably just wasn't caught because testing this needs real physical hardware with a second point on the network, not just the simulator.

I put together a prototype to close that last gap: a new function (pkt_tx_timestamp_complete()) that lets the driver deliver the timestamp directly from the TX-complete interrupt, without depending on any loopback - which is how hardware TX timestamping normally works (read the completed descriptor). Wired it into the STM32H7 driver as a reference and validated it end to end: poll/recvmsg now deliver correctly.

Left the branches available in case they're useful:

daniel-p-carvalho/apache-nuttx#test/pr20161-h7-tx-timestamp (the new delivery path + driver wiring) daniel-p-carvalho/nuttx-apps#test/pr20161-h7-tx-timestamp (the test I used to validate it) Happy to help if there's anything I can do.

SO_TIMESTAMPING_TX_HARDWARE depends on driver-level implementation. When transmitting, the driver checks if the IOB is tagged for
hardware TX timestamping, clones the packet, and after the transmission completes successfully, stamps the hardware timestamp onto
the cloned copy and loops it back to the RX path for delivery via MSG_ERRQUEUE.
TX Hardware Timestamp: Generic Driver Implementation Pattern
Transmit path (driver_transmit):
1. Check pkt->io_conn != NULL → TX hardware timestamp requested
2. Clone the pkt (iob_clone) and hold the clone in driver private structure
3. Start timeout timer (guard against missing timestamp / pkt leak)
4. Configure hardware: enable timestamp capture on TX descriptor
5. Submit original pkt to DMA and free it after transmission as normal

TX completion interrupt (driver_txisr):
1. Read TX timestamp from hardware registers (or poll for it)
2. Write timestamp into held clone's iob->io_time
3. Store clone into driver's pending queue
4. Notify upper layer via netdev_lower_rxready()

Receive path (driver_receive):
1. First check if a TX timestamp clone is pending delivery
2. If yes: return the clone
(upper layer routes it via pkt_in → io_conn match
→ errahead queue → userspace MSG_ERRQUEUE)
3. Otherwise: process normal RX packets

acassis
acassis previously approved these changes Sep 17, 2026
@xiaoxiang781216

Copy link
Copy Markdown
Contributor

@wenquan2015 please fix ci error.

@github-actions

Copy link
Copy Markdown

🔗 Cross-repo PR dependencies

The read-only Build run reported the following dependent PR(s) and fetched head SHA(s):

CI run: https://github.com/apache/nuttx/actions/runs/35297281971

OceanfromXiaomi and others added 10 commits September 18, 2026 13:54
Move RX timestamp storage from net_driver_s.d_rxtime into
iob_s.io_time so each IOB carries its own timestamp through
the stack. Remove old iob_trycopyin/iob_copyout timestamp
packing in CAN/PKT/UDP paths. Fix iob_clone_partial to copy
io_time before source pointer advances to NULL.

Signed-off-by: OceanfromXiaomi <zhaohaiyang1@xiaomi.com>
Signed-off-by: wenquan1 <wenquan1@xiaomi.com>
Extract a common cmsg_store_timestamp() helper that checks
SO_TIMESTAMP/SO_TIMESTAMPNS via s_options and appends the
appropriate cmsg. Replaces per-protocol timestamp formatting
in CAN, PKT, and UDP receive paths.


Signed-off-by: OceanfromXiaomi <zhaohaiyang1@xiaomi.com>
Replace compile-time CONFIG_ARCH_HAVE_NETDEV_TIMESTAMP with
a runtime NETDEV_RX_STAMP bit in net_driver_s.d_features.
Drivers providing hardware RX timestamps set the flag at
probe time; the stack checks it at runtime.


Signed-off-by: OceanfromXiaomi <zhaohaiyang1@xiaomi.com>
Add SO_TIMESTAMPING TX path for PKT sockets. Tagged TX
packets loop back through the driver with io_conn set,
are routed into conn->errahead, and delivered to userspace
via recvmsg(MSG_ERRQUEUE) with SO_TIMESTAMPING cmsg.
Add poll(POLLPRI) notification when errahead is non-empty.


Signed-off-by: wenquan1 <wenquan1@xiaomi.com>
Fix a scheduling issue where MSG_ERRQUEUE readiness was
not properly waking poll waiters in pkt_netpoll.c.


Signed-off-by: wenquan1 <wenquan1@xiaomi.com>
Remove the redundant `timestamp` field from `udp_conn_s` and use the
existing `s_options` bitmask to track SO_TIMESTAMP/SO_TIMESTAMPNS state.

The socket-level setsockopt/getsockopt already handles SO_TIMESTAMP via
_SO_SETOPT/_SO_GETOPT on s_options. The protocol-level handlers in
inet_sockif.c were intercepting the option before the socket layer,
causing s_options to never be set. This also meant SO_TIMESTAMPNS was
broken since inet_sockif.c only handled SO_TIMESTAMP.

Changes:
- Remove udp_conn_s.timestamp field from udp.h
- Remove SO_TIMESTAMP get/set handlers from inet_sockif.c, letting
  them fall through to the socket-level handler
- Simplify udp_recvfrom.c to call cmsg_store_timestamp() directly,
  which already checks s_options internally
- Align pkt_input.c software timestamp generation with ipv4/can by
  removing per-socket SO_TIMESTAMP option check, only checking
  hardware timestamp capability

Signed-off-by: wenquan1 <wenquan1@xiaomi.com>
Consolidate the two separate timestamp Kconfig options into a single
CONFIG_NET_TIMESTAMP option that covers SO_TIMESTAMP, SO_TIMESTAMPNS
and SO_TIMESTAMPING socket options.

Previously CONFIG_NET_TIMESTAMPING was a separate option only used by
PKT sockets for hardware TX/RX timestamps and error queue support.
Since both options guard the same io_time field in iob_s and share
the s_options bitmask, merging them simplifies configuration without
functional impact.

Changes:
- Replace all CONFIG_NET_TIMESTAMPING with CONFIG_NET_TIMESTAMP in
  pkt_input.c, pkt_recvmsg.c, pkt_sendmsg_buffered.c,
  pkt_sendmsg_unbuffered.c, pkt_sockif.c, pkt_netpoll.c, pkt.h,
  setsockopt.c, getsockopt.c
- Simplify iob.h conditional from OR of both to single option
- Remove NET_TIMESTAMPING Kconfig entry, update NET_TIMESTAMP
  description to cover all three socket options

Signed-off-by: wenquan1 <wenquan1@xiaomi.com>
Add missing SCM_TIMESTAMPNS and SCM_TIMESTAMPING control message type
definitions mapped to their corresponding SO_TIMESTAMPNS and
SO_TIMESTAMPING socket options. Also align whitespace of existing
SCM_* definitions for consistency.

Signed-off-by: wenquan1 <wenquan1@xiaomi.com>
Add TX timestamp loopback support to the simulator network driver.
When a packet tagged with io_conn (SO_TIMESTAMPING TX) is sent,
the driver clones the packet, generates a software timestamp, and
queues it for loopback through the RX path. The protocol layer
(UDP/PKT) then delivers the timestamp via MSG_ERRQUEUE.

- Add tstampq IOB queue to sim_netdev_s for loopback packets.
- In netdriver_send(), clone timestamped packets with realtime
  clock and notify RX ready.
- In netdriver_recv(), return loopback packets before reading
  from the tap device.

Signed-off-by: wenquan1 <wenquan1@xiaomi.com>
Fix coding style issues flagged by nxstyle in files touched by the
SO_TIMESTAMPING series. These are pre-existing issues, not introduced
by the SO_TIMESTAMPING patches:

- inet_sockif.c: missing blank lines after declarations
- ipv4_input.c: missing blank line after declaration, bad comment alignment
- can_input.c: bad indentation inside #ifdef block
- getsockopt.c: bad comment block alignment, bad brace alignment
- setsockopt.c: wrong column position of comment
- sim_netdriver.c: missing blank lines after declarations

Signed-off-by: wenquan1 <wenquan1@xiaomi.com>
@github-actions

Copy link
Copy Markdown

🔗 Cross-repo PR dependencies

The read-only Build run reported the following dependent PR(s) and fetched head SHA(s):

CI run: https://github.com/apache/nuttx/actions/runs/35312719356

@xiaoxiang781216
xiaoxiang781216 merged commit 1532596 into apache:master Sep 18, 2026
53 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Arch: arm Issues related to ARM (32-bit) architecture Arch: simulator Issues related to the SIMulator Area: Memory Management Memory Management issues Board: arm Size: XL The size of the change in this PR is very large. Consider breaking down the PR into smaller pieces.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants