This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 81f42cb2cfc475f6ba07e2b35ed3362b62cff8d9 Author: gaohedong <[email protected]> AuthorDate: Sun Feb 16 17:35:04 2025 +0800 net/ethernet: add timestamp for socket packet add timestamp for socket packet Signed-off-by: gaohedong <[email protected]> --- include/sys/socket.h | 5 +- net/pkt/pkt_input.c | 27 +++++++++ net/pkt/pkt_recvmsg.c | 144 +++++++++++++++++++++++++++++++++--------------- net/socket/getsockopt.c | 17 +++--- net/socket/setsockopt.c | 17 +++--- net/socket/socket.h | 1 + 6 files changed, 151 insertions(+), 60 deletions(-) diff --git a/include/sys/socket.h b/include/sys/socket.h index bd477422280..ead113a62ef 100644 --- a/include/sys/socket.h +++ b/include/sys/socket.h @@ -212,7 +212,7 @@ #define SO_TYPE 15 /* Reports the socket type (get only). * return: int */ -#define SO_TIMESTAMP 16 /* Generates a timestamp for each incoming packet +#define SO_TIMESTAMP 16 /* Generates a timestamp in us for each incoming packet * arg: integer value */ #define SO_BINDTODEVICE 17 /* Bind this socket to a specific network device. @@ -220,6 +220,9 @@ #define SO_PEERCRED 18 /* Return the credentials of the peer process * connected to this socket. */ +#define SO_TIMESTAMPNS 20 /* Generates a timestamp in ns for each incoming packet + * arg: integer value + */ /* The options are unsupported but included for compatibility * and portability diff --git a/net/pkt/pkt_input.c b/net/pkt/pkt_input.c index 321d9eb8f5d..230aaf03695 100644 --- a/net/pkt/pkt_input.c +++ b/net/pkt/pkt_input.c @@ -36,6 +36,7 @@ #include "devif/devif.h" #include "pkt/pkt.h" +#include "socket/socket.h" /**************************************************************************** * Private Functions @@ -68,6 +69,22 @@ static uint16_t pkt_datahandler(FAR struct net_driver_s *dev, return 0; } +#ifdef CONFIG_NET_TIMESTAMP + if (_SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMP) || + _SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMPNS)) + { + ret = iob_trycopyin(iob, (FAR const uint8_t *)&dev->d_rxtime, + sizeof(struct timespec), 0, true); + if (ret != sizeof(struct timespec)) + { + nerr("ERROR: Failed to write timestamp: %d\n", ret); + goto errout; + } + + iob_reserve(iob, sizeof(struct timespec)); + } +#endif + /* Clone an I/O buffer chain of the L2 data, use throttled IOB to avoid * overconsumption. * TODO: Optimize IOB clone after we support shared IOB. @@ -139,6 +156,16 @@ static int pkt_in(FAR struct net_driver_s *dev) { uint16_t flags; +#if defined(CONFIG_NET_TIMESTAMP) && !defined(CONFIG_ARCH_HAVE_NETDEV_TIMESTAMP) + /* Get system as timestamp if no hardware timestamp */ + + if (_SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMP) || + _SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMPNS)) + { + clock_gettime(CLOCK_REALTIME, &dev->d_rxtime); + } +#endif /* CONFIG_NET_TIMESTAMP */ + /* Setup for the application callback */ dev->d_appdata = dev->d_buf; diff --git a/net/pkt/pkt_recvmsg.c b/net/pkt/pkt_recvmsg.c index 79919516af8..08b93092de9 100644 --- a/net/pkt/pkt_recvmsg.c +++ b/net/pkt/pkt_recvmsg.c @@ -30,6 +30,7 @@ #include <sys/types.h> #include <sys/socket.h> +#include <sys/time.h> #include <stdint.h> #include <string.h> #include <errno.h> @@ -54,18 +55,54 @@ struct pkt_recvfrom_s { - FAR struct devif_callback_s *pr_cb; /* Reference to callback instance */ - sem_t pr_sem; /* Semaphore signals recv completion */ - size_t pr_buflen; /* Length of receive buffer */ - FAR uint8_t *pr_buffer; /* Pointer to receive buffer */ - ssize_t pr_recvlen; /* The received length */ - int pr_result; /* Success:OK, failure:negated errno */ + FAR struct pkt_conn_s *pr_conn; /* Connection associated with the socket */ + FAR struct devif_callback_s *pr_cb; /* Reference to callback instance */ + FAR struct msghdr *pr_msg; /* Receive info and buffer */ + sem_t pr_sem; /* Semaphore signals recv completion */ + ssize_t pr_recvlen; /* The received length */ + int pr_result; /* Success:OK, failure:negated errno */ }; /**************************************************************************** * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: pkt_store_cmsg_timestamp + * + * Description: + * Store the timestamp in the cmsg + * + * Input Parameters: + * pstate Recicve state information + * timestamp Timestamp information + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_NET_TIMESTAMP +static void pkt_store_cmsg_timestamp(FAR struct pkt_recvfrom_s *pstate, + FAR struct timespec *timestamp) +{ + FAR struct msghdr *msg = pstate->pr_msg; + struct timeval tv; + + if (_SO_GETOPT(pstate->pr_conn->sconn.s_options, SO_TIMESTAMPNS)) + { + cmsg_append(msg, SOL_SOCKET, SO_TIMESTAMPNS, timestamp, + sizeof(struct timespec)); + } + else + { + TIMESPEC_TO_TIMEVAL(&tv, timestamp); + cmsg_append(msg, SOL_SOCKET, SO_TIMESTAMP, &tv, + sizeof(struct timeval)); + } +} +#endif + /**************************************************************************** * Name: pkt_add_recvlen * @@ -92,8 +129,6 @@ static inline void pkt_add_recvlen(FAR struct pkt_recvfrom_s *pstate, } pstate->pr_recvlen += recvlen; - pstate->pr_buffer += recvlen; - pstate->pr_buflen -= recvlen; } /**************************************************************************** @@ -120,20 +155,24 @@ static void pkt_recvfrom_newdata(FAR struct net_driver_s *dev, unsigned int offset; size_t recvlen; - if (dev->d_len > pstate->pr_buflen) - { - recvlen = pstate->pr_buflen; - } - else +#ifdef CONFIG_NET_TIMESTAMP + /* Unpack stored timestamp if SO_TIMESTAMP socket option is enabled */ + + if (_SO_GETOPT(pstate->pr_conn->sconn.s_options, SO_TIMESTAMP) || + _SO_GETOPT(pstate->pr_conn->sconn.s_options, SO_TIMESTAMPNS)) { - recvlen = dev->d_len; + pkt_store_cmsg_timestamp(pstate, &dev->d_rxtime); } +#endif + + recvlen = MIN(pstate->pr_msg->msg_iov->iov_len, dev->d_len); /* Copy the new packet data into the user buffer */ offset = (dev->d_appdata - dev->d_iob->io_data) - dev->d_iob->io_offset; - recvlen = iob_copyout(pstate->pr_buffer, dev->d_iob, recvlen, offset); + recvlen = iob_copyout(pstate->pr_msg->msg_iov->iov_base, + dev->d_iob, recvlen, offset); ninfo("Received %d bytes (of %d)\n", (int)recvlen, (int)dev->d_len); @@ -228,9 +267,8 @@ static uint16_t pkt_recvfrom_eventhandler(FAR struct net_driver_s *dev, * Initialize the state structure * * Input Parameters: - * psock Pointer to the socket structure for the socket - * buf Buffer to receive data - * len Length of buffer + * conn The PKT connection of interest + * msg Receive info and buffer for receive data * pstate A pointer to the state structure to be initialized * * Returned Value: @@ -240,9 +278,8 @@ static uint16_t pkt_recvfrom_eventhandler(FAR struct net_driver_s *dev, * ****************************************************************************/ -static void pkt_recvfrom_initialize(FAR struct socket *psock, FAR void *buf, - size_t len, FAR struct sockaddr *infrom, - FAR socklen_t *fromlen, +static void pkt_recvfrom_initialize(FAR struct pkt_conn_s *conn, + FAR struct msghdr *msg, FAR struct pkt_recvfrom_s *pstate) { /* Initialize the state structure. */ @@ -250,8 +287,8 @@ static void pkt_recvfrom_initialize(FAR struct socket *psock, FAR void *buf, memset(pstate, 0, sizeof(struct pkt_recvfrom_s)); nxsem_init(&pstate->pr_sem, 0, 0); /* Doesn't really fail */ - pstate->pr_buflen = len; - pstate->pr_buffer = buf; + pstate->pr_conn = conn; + pstate->pr_msg = msg; } /* The only un-initialization that has to be performed is destroying the @@ -313,35 +350,58 @@ static ssize_t pkt_recvfrom_result(int result, * Copy the buffered read-ahead data to the user buffer. * * Input Parameters: - * conn - PKT socket connection structure containing the read- - * ahead data. - * buf target buffer. + * pstate The state structure of the recv operation * * Returned Value: - * Number of bytes copied to the user buffer + * None * * Assumptions: * The network is locked. * ****************************************************************************/ -static inline ssize_t pkt_readahead(FAR struct pkt_conn_s *conn, - FAR void *buf, size_t buflen) +static inline void pkt_readahead(FAR struct pkt_recvfrom_s *pstate) { + FAR struct pkt_conn_s *conn = pstate->pr_conn; FAR struct iob_s *iob; - ssize_t ret = -ENODATA; + int recvlen; /* Check there is any packets already buffered in a read-ahead buffer. */ + pstate->pr_recvlen = -ENODATA; + if ((iob = iob_peek_queue(&conn->readahead)) != NULL) { DEBUGASSERT(iob->io_pktlen > 0); +#ifdef CONFIG_NET_TIMESTAMP + /* Unpack stored timestamp if SO_TIMESTAMP/SO_TIMESTAMPNS socket option + * is enabled + */ + + if (_SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMP) || + _SO_GETOPT(conn->sconn.s_options, SO_TIMESTAMPNS)) + { + struct timespec ts; + recvlen = iob_copyout((FAR uint8_t *)&ts, iob, + sizeof(struct timespec), + -sizeof(struct timespec)); + DEBUGASSERT(recvlen == sizeof(struct timespec)); + + pkt_store_cmsg_timestamp(pstate, &ts); + } +#endif + /* Copy to user */ - ret = iob_copyout(buf, iob, buflen, 0); + recvlen = iob_copyout(pstate->pr_msg->msg_iov->iov_base, iob, + pstate->pr_msg->msg_iov->iov_len, 0); - ninfo("Received %zd bytes (of %u)\n", ret, iob->io_pktlen); + /* Update the accumulated size of the data read */ + + pstate->pr_recvlen = recvlen; + + ninfo("Received %d bytes (of %u)\n", recvlen, iob->io_pktlen); /* Remove the I/O buffer chain from the head of the read-ahead * buffer queue. @@ -353,8 +413,6 @@ static inline ssize_t pkt_readahead(FAR struct pkt_conn_s *conn, iob_free_chain(iob); } - - return ret; } /**************************************************************************** @@ -392,14 +450,12 @@ static inline ssize_t pkt_readahead(FAR struct pkt_conn_s *conn, ssize_t pkt_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, int flags) { - FAR void *buf = msg->msg_iov->iov_base; - size_t len = msg->msg_iov->iov_len; FAR struct sockaddr *from = msg->msg_name; FAR socklen_t *fromlen = &msg->msg_namelen; FAR struct pkt_conn_s *conn = psock->s_conn; FAR struct net_driver_s *dev; struct pkt_recvfrom_s state; - ssize_t ret; + ssize_t ret = 0; /* If a 'from' address has been provided, verify that it is large * enough to hold this address family. @@ -427,6 +483,8 @@ ssize_t pkt_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, * locked because we don't want anything to happen until we are ready. */ + pkt_recvfrom_initialize(conn, msg, &state); + net_lock(); /* Check if there is buffered read-ahead data for this socket. We may have @@ -435,7 +493,8 @@ ssize_t pkt_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, if (!IOB_QEMPTY(&conn->readahead)) { - ret = pkt_readahead(conn, buf, len); + pkt_readahead(&state); + ret = pkt_recvfrom_result(ret, &state); } else if (_SS_ISNONBLOCK(conn->sconn.s_flags) || (flags & MSG_DONTWAIT) != 0) @@ -446,8 +505,6 @@ ssize_t pkt_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, } else { - pkt_recvfrom_initialize(psock, buf, len, from, fromlen, &state); - /* Get the device driver that will service this transfer */ dev = pkt_find_device(conn); @@ -496,12 +553,13 @@ ssize_t pkt_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, { ret = -EBUSY; } - -errout_with_state: - pkt_recvfrom_uninitialize(&state); } net_unlock(); + +errout_with_state: + pkt_recvfrom_uninitialize(&state); + return ret; } diff --git a/net/socket/getsockopt.c b/net/socket/getsockopt.c index 2f01f39e485..d4acc4becbd 100644 --- a/net/socket/getsockopt.c +++ b/net/socket/getsockopt.c @@ -140,15 +140,16 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option, * is outside of the scope of getsockopt. */ - case SO_BROADCAST: /* Permits sending of broadcast messages */ - case SO_DEBUG: /* Enables recording of debugging information */ - case SO_DONTROUTE: /* Requests outgoing messages bypass standard routing */ - case SO_KEEPALIVE: /* Verifies TCP connections active by enabling the - * periodic transmission of probes */ - case SO_OOBINLINE: /* Leaves received out-of-band data inline */ - case SO_REUSEADDR: /* Allow reuse of local addresses */ + case SO_BROADCAST: /* Permits sending of broadcast messages */ + case SO_DEBUG: /* Enables recording of debugging information */ + case SO_DONTROUTE: /* Requests outgoing messages bypass standard routing */ + case SO_KEEPALIVE: /* Verifies TCP connections active by enabling the + * periodic transmission of probes */ + case SO_OOBINLINE: /* Leaves received out-of-band data inline */ + case SO_REUSEADDR: /* Allow reuse of local addresses */ #ifdef CONFIG_NET_TIMESTAMP - case SO_TIMESTAMP: /* Generates a timestamp for each incoming packet */ + case SO_TIMESTAMP: /* Generates a timestamp in us for each incoming packet */ + case SO_TIMESTAMPNS: /* Generates a timestamp in ns for each incoming packet */ #endif { sockopt_t optionset; diff --git a/net/socket/setsockopt.c b/net/socket/setsockopt.c index 5e3a59157a8..97b039a6ede 100644 --- a/net/socket/setsockopt.c +++ b/net/socket/setsockopt.c @@ -130,15 +130,16 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option, return OK; } - case SO_BROADCAST: /* Permits sending of broadcast messages */ - case SO_DEBUG: /* Enables recording of debugging information */ - case SO_DONTROUTE: /* Requests outgoing messages bypass standard routing */ - case SO_KEEPALIVE: /* Verifies TCP connections active by enabling the - * periodic transmission of probes */ - case SO_OOBINLINE: /* Leaves received out-of-band data inline */ - case SO_REUSEADDR: /* Allow reuse of local addresses */ + case SO_BROADCAST: /* Permits sending of broadcast messages */ + case SO_DEBUG: /* Enables recording of debugging information */ + case SO_DONTROUTE: /* Requests outgoing messages bypass standard routing */ + case SO_KEEPALIVE: /* Verifies TCP connections active by enabling the + * periodic transmission of probes */ + case SO_OOBINLINE: /* Leaves received out-of-band data inline */ + case SO_REUSEADDR: /* Allow reuse of local addresses */ #ifdef CONFIG_NET_TIMESTAMP - case SO_TIMESTAMP: /* Generates a timestamp for each incoming packet */ + case SO_TIMESTAMP: /* Generates a timestamp in us for each incoming packet */ + case SO_TIMESTAMPNS: /* Generates a timestamp in ns for each incoming packet */ #endif { int setting; diff --git a/net/socket/socket.h b/net/socket/socket.h index d05c66c83be..75362f06609 100644 --- a/net/socket/socket.h +++ b/net/socket/socket.h @@ -65,6 +65,7 @@ #define _SO_SNDTIMEO _SO_BIT(SO_SNDTIMEO) #define _SO_TYPE _SO_BIT(SO_TYPE) #define _SO_TIMESTAMP _SO_BIT(SO_TIMESTAMP) +#define _SO_TIMESTAMPNS _SO_BIT(SO_TIMESTAMPNS) #define _SO_BINDTODEVICE _SO_BIT(SO_BINDTODEVICE) /* This is the largest option value. REVISIT: belongs in sys/socket.h */
