]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net_tstamp: add SCM_TS_OPT_ID to provide OPT_ID in control message
authorVadim Fedorenko <vadfed@meta.com>
Tue, 1 Oct 2024 12:57:14 +0000 (05:57 -0700)
committerJakub Kicinski <kuba@kernel.org>
Fri, 4 Oct 2024 18:52:19 +0000 (11:52 -0700)
SOF_TIMESTAMPING_OPT_ID socket option flag gives a way to correlate TX
timestamps and packets sent via socket. Unfortunately, there is no way
to reliably predict socket timestamp ID value in case of error returned
by sendmsg. For UDP sockets it's impossible because of lockless
nature of UDP transmit, several threads may send packets in parallel. In
case of RAW sockets MSG_MORE option makes things complicated. More
details are in the conversation [1].
This patch adds new control message type to give user-space
software an opportunity to control the mapping between packets and
values by providing ID with each sendmsg for UDP sockets.
The documentation is also added in this patch.

[1] https://lore.kernel.org/netdev/CALCETrU0jB+kg0mhV6A8mrHfTE1D1pr1SD_B9Eaa9aDPfgHdtA@mail.gmail.com/

Reviewed-by: Willem de Bruijn <willemb@google.com>
Reviewed-by: Jason Xing <kerneljasonxing@gmail.com>
Signed-off-by: Vadim Fedorenko <vadfed@meta.com>
Link: https://patch.msgid.link/20241001125716.2832769-2-vadfed@meta.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Documentation/networking/timestamping.rst
arch/alpha/include/uapi/asm/socket.h
arch/mips/include/uapi/asm/socket.h
arch/parisc/include/uapi/asm/socket.h
arch/sparc/include/uapi/asm/socket.h
include/net/inet_sock.h
include/net/sock.h
include/uapi/asm-generic/socket.h
net/core/sock.c
net/ipv4/ip_output.c
net/ipv6/ip6_output.c

index 8199e691767189cb9493894737b4f0aa9bb1a49f..b37bfbfc7d7970cde4cb20bfafe9b44d2fbf8b3c 100644 (file)
@@ -194,6 +194,20 @@ SOF_TIMESTAMPING_OPT_ID:
   among all possibly concurrently outstanding timestamp requests for
   that socket.
 
+  The process can optionally override the default generated ID, by
+  passing a specific ID with control message SCM_TS_OPT_ID (not
+  supported for TCP sockets)::
+
+    struct msghdr *msg;
+    ...
+    cmsg                        = CMSG_FIRSTHDR(msg);
+    cmsg->cmsg_level            = SOL_SOCKET;
+    cmsg->cmsg_type             = SCM_TS_OPT_ID;
+    cmsg->cmsg_len              = CMSG_LEN(sizeof(__u32));
+    *((__u32 *) CMSG_DATA(cmsg)) = opt_id;
+    err = sendmsg(fd, msg, 0);
+
+
 SOF_TIMESTAMPING_OPT_ID_TCP:
   Pass this modifier along with SOF_TIMESTAMPING_OPT_ID for new TCP
   timestamping applications. SOF_TIMESTAMPING_OPT_ID defines how the
index 251b73c5481eaa669873ff551787e9c7c453c537..302507bf9b5ddb98421706f667ee8105c873a572 100644 (file)
 #define SCM_DEVMEM_DMABUF      SO_DEVMEM_DMABUF
 #define SO_DEVMEM_DONTNEED     80
 
+#define SCM_TS_OPT_ID          81
+
 #if !defined(__KERNEL__)
 
 #if __BITS_PER_LONG == 64
index 8ab7582291abf2cf82216abc3d3b340990f372bd..d118d47315801b61f68bb0cc2edce9f7662d6d0f 100644 (file)
 #define SCM_DEVMEM_DMABUF      SO_DEVMEM_DMABUF
 #define SO_DEVMEM_DONTNEED     80
 
+#define SCM_TS_OPT_ID          81
+
 #if !defined(__KERNEL__)
 
 #if __BITS_PER_LONG == 64
index 38fc0b188e08424054b50ddc4a661b8bef74042f..d268d69bfcd27e39dcae1ff6f65bca09b31609ad 100644 (file)
 #define SCM_DEVMEM_DMABUF      SO_DEVMEM_DMABUF
 #define SO_DEVMEM_DONTNEED     80
 
+#define SCM_TS_OPT_ID          0x404C
+
 #if !defined(__KERNEL__)
 
 #if __BITS_PER_LONG == 64
index 57084ed2f3c4ec1325397cfe8ea2356bb67b0ffd..113cd9f353e35f07d925550054935f3f7102974a 100644 (file)
 #define SCM_DEVMEM_DMABUF        SO_DEVMEM_DMABUF
 #define SO_DEVMEM_DONTNEED       0x0059
 
+#define SCM_TS_OPT_ID            0x005a
+
 #if !defined(__KERNEL__)
 
 
index 394c3b66065e20d34594d6e2a2010c55bb457810..f01dd273bea69d2eaf7a1d28274d7f980942b78a 100644 (file)
@@ -174,6 +174,7 @@ struct inet_cork {
        __s16                   tos;
        char                    priority;
        __u16                   gso_size;
+       u32                     ts_opt_id;
        u64                     transmit_time;
        u32                     mark;
 };
@@ -241,7 +242,8 @@ struct inet_sock {
        struct inet_cork_full   cork;
 };
 
-#define IPCORK_OPT     1       /* ip-options has been held in ipcork.opt */
+#define IPCORK_OPT             1       /* ip-options has been held in ipcork.opt */
+#define IPCORK_TS_OPT_ID       2       /* ts_opt_id field is valid, overriding sk_tskey */
 
 enum {
        INET_FLAGS_PKTINFO      = 0,
index c58ca8dd561b7312ffc0836585c04d9fe917a124..ccf28c2b70b1e353ceeba5e485233d70b3a89190 100644 (file)
@@ -954,6 +954,12 @@ enum sock_flags {
 };
 
 #define SK_FLAGS_TIMESTAMP ((1UL << SOCK_TIMESTAMP) | (1UL << SOCK_TIMESTAMPING_RX_SOFTWARE))
+/*
+ * The highest bit of sk_tsflags is reserved for kernel-internal
+ * SOCKCM_FLAG_TS_OPT_ID. There is a check in core/sock.c to control that
+ * SOF_TIMESTAMPING* values do not reach this reserved area
+ */
+#define SOCKCM_FLAG_TS_OPT_ID  BIT(31)
 
 static inline void sock_copy_flags(struct sock *nsk, const struct sock *osk)
 {
@@ -1796,6 +1802,7 @@ struct sockcm_cookie {
        u64 transmit_time;
        u32 mark;
        u32 tsflags;
+       u32 ts_opt_id;
 };
 
 static inline void sockcm_init(struct sockcm_cookie *sockc,
index 3b4e3e8156025f305fea4639bb108264b5316bfa..deacfd6dd197fb6ae96a630ad07f40f6f7a822f8 100644 (file)
 #define SCM_DEVMEM_DMABUF      SO_DEVMEM_DMABUF
 #define SO_DEVMEM_DONTNEED     80
 
+#define SCM_TS_OPT_ID          81
+
 #if !defined(__KERNEL__)
 
 #if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
index 039be95c40cf6fa429d33e0f42ee606188045992..846f494a17cf9614bb96505eec743df17574c138 100644 (file)
@@ -2899,6 +2899,8 @@ int __sock_cmsg_send(struct sock *sk, struct cmsghdr *cmsg,
 {
        u32 tsflags;
 
+       BUILD_BUG_ON(SOF_TIMESTAMPING_LAST == (1 << 31));
+
        switch (cmsg->cmsg_type) {
        case SO_MARK:
                if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) &&
@@ -2927,6 +2929,17 @@ int __sock_cmsg_send(struct sock *sk, struct cmsghdr *cmsg,
                        return -EINVAL;
                sockc->transmit_time = get_unaligned((u64 *)CMSG_DATA(cmsg));
                break;
+       case SCM_TS_OPT_ID:
+               if (sk_is_tcp(sk))
+                       return -EINVAL;
+               tsflags = READ_ONCE(sk->sk_tsflags);
+               if (!(tsflags & SOF_TIMESTAMPING_OPT_ID))
+                       return -EINVAL;
+               if (cmsg->cmsg_len != CMSG_LEN(sizeof(u32)))
+                       return -EINVAL;
+               sockc->ts_opt_id = *(u32 *)CMSG_DATA(cmsg);
+               sockc->tsflags |= SOCKCM_FLAG_TS_OPT_ID;
+               break;
        /* SCM_RIGHTS and SCM_CREDENTIALS are semantically in SOL_UNIX. */
        case SCM_RIGHTS:
        case SCM_CREDENTIALS:
index 49811c9281d424bc4b43b8e9075da9fe724773cb..0c7049f5036920933cf97953235c166a4090ba50 100644 (file)
@@ -973,7 +973,7 @@ static int __ip_append_data(struct sock *sk,
        unsigned int maxfraglen, fragheaderlen, maxnonfragsize;
        int csummode = CHECKSUM_NONE;
        struct rtable *rt = dst_rtable(cork->dst);
-       bool paged, hold_tskey, extra_uref = false;
+       bool paged, hold_tskey = false, extra_uref = false;
        unsigned int wmem_alloc_delta = 0;
        u32 tskey = 0;
 
@@ -1049,10 +1049,15 @@ static int __ip_append_data(struct sock *sk,
 
        cork->length += length;
 
-       hold_tskey = cork->tx_flags & SKBTX_ANY_TSTAMP &&
-                    READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID;
-       if (hold_tskey)
-               tskey = atomic_inc_return(&sk->sk_tskey) - 1;
+       if (cork->tx_flags & SKBTX_ANY_TSTAMP &&
+           READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID) {
+               if (cork->flags & IPCORK_TS_OPT_ID) {
+                       tskey = cork->ts_opt_id;
+               } else {
+                       tskey = atomic_inc_return(&sk->sk_tskey) - 1;
+                       hold_tskey = true;
+               }
+       }
 
        /* So, what's going on in the loop below?
         *
@@ -1327,6 +1332,10 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork,
        cork->transmit_time = ipc->sockc.transmit_time;
        cork->tx_flags = 0;
        sock_tx_timestamp(sk, ipc->sockc.tsflags, &cork->tx_flags);
+       if (ipc->sockc.tsflags & SOCKCM_FLAG_TS_OPT_ID) {
+               cork->flags |= IPCORK_TS_OPT_ID;
+               cork->ts_opt_id = ipc->sockc.ts_opt_id;
+       }
 
        return 0;
 }
index f26841f1490f5c31e3b09bce5773391e779128e1..ff6bd8d85e9a8c83468629a3b910cf8936b4b7d9 100644 (file)
@@ -1402,7 +1402,10 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork,
        cork->base.tx_flags = 0;
        cork->base.mark = ipc6->sockc.mark;
        sock_tx_timestamp(sk, ipc6->sockc.tsflags, &cork->base.tx_flags);
-
+       if (ipc6->sockc.tsflags & SOCKCM_FLAG_TS_OPT_ID) {
+               cork->base.flags |= IPCORK_TS_OPT_ID;
+               cork->base.ts_opt_id = ipc6->sockc.ts_opt_id;
+       }
        cork->base.length = 0;
        cork->base.transmit_time = ipc6->sockc.transmit_time;
 
@@ -1433,7 +1436,7 @@ static int __ip6_append_data(struct sock *sk,
        bool zc = false;
        u32 tskey = 0;
        struct rt6_info *rt = dst_rt6_info(cork->dst);
-       bool paged, hold_tskey, extra_uref = false;
+       bool paged, hold_tskey = false, extra_uref = false;
        struct ipv6_txoptions *opt = v6_cork->opt;
        int csummode = CHECKSUM_NONE;
        unsigned int maxnonfragsize, headersize;
@@ -1543,10 +1546,15 @@ emsgsize:
                        flags &= ~MSG_SPLICE_PAGES;
        }
 
-       hold_tskey = cork->tx_flags & SKBTX_ANY_TSTAMP &&
-                    READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID;
-       if (hold_tskey)
-               tskey = atomic_inc_return(&sk->sk_tskey) - 1;
+       if (cork->tx_flags & SKBTX_ANY_TSTAMP &&
+           READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID) {
+               if (cork->flags & IPCORK_TS_OPT_ID) {
+                       tskey = cork->ts_opt_id;
+               } else {
+                       tskey = atomic_inc_return(&sk->sk_tskey) - 1;
+                       hold_tskey = true;
+               }
+       }
 
        /*
         * Let's try using as much space as possible.