diff options
author | Patrick Ohly <patrick.ohly@intel.com> | 2009-02-12 00:03:38 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-02-16 01:43:35 -0500 |
commit | 20d4947353be60e909e6b1a79d241457edd6833f (patch) | |
tree | 939ced518fc52e57df9ee9efb0cd14b6e26a3bc4 | |
parent | ac45f602ee3d1b6f326f68bc0c2591ceebf05ba4 (diff) |
net: socket infrastructure for SO_TIMESTAMPING
The overlap with the old SO_TIMESTAMP[NS] options is handled so
that time stamping in software (net_enable_timestamp()) is
enabled when SO_TIMESTAMP[NS] and/or SO_TIMESTAMPING_RX_SOFTWARE
is set. It's disabled if all of these are off.
Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/sock.h | 44 | ||||
-rw-r--r-- | net/compat.c | 19 | ||||
-rw-r--r-- | net/core/sock.c | 81 | ||||
-rw-r--r-- | net/socket.c | 84 |
4 files changed, 186 insertions, 42 deletions
diff --git a/include/net/sock.h b/include/net/sock.h index ded6854e3e4a..cc9d5bcb06f7 100644 --- a/include/net/sock.h +++ b/include/net/sock.h | |||
@@ -158,7 +158,7 @@ struct sock_common { | |||
158 | * @sk_allocation: allocation mode | 158 | * @sk_allocation: allocation mode |
159 | * @sk_sndbuf: size of send buffer in bytes | 159 | * @sk_sndbuf: size of send buffer in bytes |
160 | * @sk_flags: %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE, | 160 | * @sk_flags: %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE, |
161 | * %SO_OOBINLINE settings | 161 | * %SO_OOBINLINE settings, %SO_TIMESTAMPING settings |
162 | * @sk_no_check: %SO_NO_CHECK setting, wether or not checkup packets | 162 | * @sk_no_check: %SO_NO_CHECK setting, wether or not checkup packets |
163 | * @sk_route_caps: route capabilities (e.g. %NETIF_F_TSO) | 163 | * @sk_route_caps: route capabilities (e.g. %NETIF_F_TSO) |
164 | * @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4) | 164 | * @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4) |
@@ -488,6 +488,13 @@ enum sock_flags { | |||
488 | SOCK_RCVTSTAMPNS, /* %SO_TIMESTAMPNS setting */ | 488 | SOCK_RCVTSTAMPNS, /* %SO_TIMESTAMPNS setting */ |
489 | SOCK_LOCALROUTE, /* route locally only, %SO_DONTROUTE setting */ | 489 | SOCK_LOCALROUTE, /* route locally only, %SO_DONTROUTE setting */ |
490 | SOCK_QUEUE_SHRUNK, /* write queue has been shrunk recently */ | 490 | SOCK_QUEUE_SHRUNK, /* write queue has been shrunk recently */ |
491 | SOCK_TIMESTAMPING_TX_HARDWARE, /* %SOF_TIMESTAMPING_TX_HARDWARE */ | ||
492 | SOCK_TIMESTAMPING_TX_SOFTWARE, /* %SOF_TIMESTAMPING_TX_SOFTWARE */ | ||
493 | SOCK_TIMESTAMPING_RX_HARDWARE, /* %SOF_TIMESTAMPING_RX_HARDWARE */ | ||
494 | SOCK_TIMESTAMPING_RX_SOFTWARE, /* %SOF_TIMESTAMPING_RX_SOFTWARE */ | ||
495 | SOCK_TIMESTAMPING_SOFTWARE, /* %SOF_TIMESTAMPING_SOFTWARE */ | ||
496 | SOCK_TIMESTAMPING_RAW_HARDWARE, /* %SOF_TIMESTAMPING_RAW_HARDWARE */ | ||
497 | SOCK_TIMESTAMPING_SYS_HARDWARE, /* %SOF_TIMESTAMPING_SYS_HARDWARE */ | ||
491 | }; | 498 | }; |
492 | 499 | ||
493 | static inline void sock_copy_flags(struct sock *nsk, struct sock *osk) | 500 | static inline void sock_copy_flags(struct sock *nsk, struct sock *osk) |
@@ -1346,14 +1353,45 @@ static __inline__ void | |||
1346 | sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) | 1353 | sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) |
1347 | { | 1354 | { |
1348 | ktime_t kt = skb->tstamp; | 1355 | ktime_t kt = skb->tstamp; |
1356 | struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb); | ||
1349 | 1357 | ||
1350 | if (sock_flag(sk, SOCK_RCVTSTAMP)) | 1358 | /* |
1359 | * generate control messages if | ||
1360 | * - receive time stamping in software requested (SOCK_RCVTSTAMP | ||
1361 | * or SOCK_TIMESTAMPING_RX_SOFTWARE) | ||
1362 | * - software time stamp available and wanted | ||
1363 | * (SOCK_TIMESTAMPING_SOFTWARE) | ||
1364 | * - hardware time stamps available and wanted | ||
1365 | * (SOCK_TIMESTAMPING_SYS_HARDWARE or | ||
1366 | * SOCK_TIMESTAMPING_RAW_HARDWARE) | ||
1367 | */ | ||
1368 | if (sock_flag(sk, SOCK_RCVTSTAMP) || | ||
1369 | sock_flag(sk, SOCK_TIMESTAMPING_RX_SOFTWARE) || | ||
1370 | (kt.tv64 && sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE)) || | ||
1371 | (hwtstamps->hwtstamp.tv64 && | ||
1372 | sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE)) || | ||
1373 | (hwtstamps->syststamp.tv64 && | ||
1374 | sock_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE))) | ||
1351 | __sock_recv_timestamp(msg, sk, skb); | 1375 | __sock_recv_timestamp(msg, sk, skb); |
1352 | else | 1376 | else |
1353 | sk->sk_stamp = kt; | 1377 | sk->sk_stamp = kt; |
1354 | } | 1378 | } |
1355 | 1379 | ||
1356 | /** | 1380 | /** |
1381 | * sock_tx_timestamp - checks whether the outgoing packet is to be time stamped | ||
1382 | * @msg: outgoing packet | ||
1383 | * @sk: socket sending this packet | ||
1384 | * @shtx: filled with instructions for time stamping | ||
1385 | * | ||
1386 | * Currently only depends on SOCK_TIMESTAMPING* flags. Returns error code if | ||
1387 | * parameters are invalid. | ||
1388 | */ | ||
1389 | extern int sock_tx_timestamp(struct msghdr *msg, | ||
1390 | struct sock *sk, | ||
1391 | union skb_shared_tx *shtx); | ||
1392 | |||
1393 | |||
1394 | /** | ||
1357 | * sk_eat_skb - Release a skb if it is no longer needed | 1395 | * sk_eat_skb - Release a skb if it is no longer needed |
1358 | * @sk: socket to eat this skb from | 1396 | * @sk: socket to eat this skb from |
1359 | * @skb: socket buffer to eat | 1397 | * @skb: socket buffer to eat |
@@ -1421,7 +1459,7 @@ static inline struct sock *skb_steal_sock(struct sk_buff *skb) | |||
1421 | return NULL; | 1459 | return NULL; |
1422 | } | 1460 | } |
1423 | 1461 | ||
1424 | extern void sock_enable_timestamp(struct sock *sk); | 1462 | extern void sock_enable_timestamp(struct sock *sk, int flag); |
1425 | extern int sock_get_timestamp(struct sock *, struct timeval __user *); | 1463 | extern int sock_get_timestamp(struct sock *, struct timeval __user *); |
1426 | extern int sock_get_timestampns(struct sock *, struct timespec __user *); | 1464 | extern int sock_get_timestampns(struct sock *, struct timespec __user *); |
1427 | 1465 | ||
diff --git a/net/compat.c b/net/compat.c index a3a2ba0fac08..8d739053afe4 100644 --- a/net/compat.c +++ b/net/compat.c | |||
@@ -216,7 +216,7 @@ Efault: | |||
216 | int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data) | 216 | int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data) |
217 | { | 217 | { |
218 | struct compat_timeval ctv; | 218 | struct compat_timeval ctv; |
219 | struct compat_timespec cts; | 219 | struct compat_timespec cts[3]; |
220 | struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control; | 220 | struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control; |
221 | struct compat_cmsghdr cmhdr; | 221 | struct compat_cmsghdr cmhdr; |
222 | int cmlen; | 222 | int cmlen; |
@@ -233,12 +233,17 @@ int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *dat | |||
233 | data = &ctv; | 233 | data = &ctv; |
234 | len = sizeof(ctv); | 234 | len = sizeof(ctv); |
235 | } | 235 | } |
236 | if (level == SOL_SOCKET && type == SCM_TIMESTAMPNS) { | 236 | if (level == SOL_SOCKET && |
237 | (type == SCM_TIMESTAMPNS || type == SCM_TIMESTAMPING)) { | ||
238 | int count = type == SCM_TIMESTAMPNS ? 1 : 3; | ||
239 | int i; | ||
237 | struct timespec *ts = (struct timespec *)data; | 240 | struct timespec *ts = (struct timespec *)data; |
238 | cts.tv_sec = ts->tv_sec; | 241 | for (i = 0; i < count; i++) { |
239 | cts.tv_nsec = ts->tv_nsec; | 242 | cts[i].tv_sec = ts[i].tv_sec; |
243 | cts[i].tv_nsec = ts[i].tv_nsec; | ||
244 | } | ||
240 | data = &cts; | 245 | data = &cts; |
241 | len = sizeof(cts); | 246 | len = sizeof(cts[0]) * count; |
242 | } | 247 | } |
243 | 248 | ||
244 | cmlen = CMSG_COMPAT_LEN(len); | 249 | cmlen = CMSG_COMPAT_LEN(len); |
@@ -455,7 +460,7 @@ int compat_sock_get_timestamp(struct sock *sk, struct timeval __user *userstamp) | |||
455 | struct timeval tv; | 460 | struct timeval tv; |
456 | 461 | ||
457 | if (!sock_flag(sk, SOCK_TIMESTAMP)) | 462 | if (!sock_flag(sk, SOCK_TIMESTAMP)) |
458 | sock_enable_timestamp(sk); | 463 | sock_enable_timestamp(sk, SOCK_TIMESTAMP); |
459 | tv = ktime_to_timeval(sk->sk_stamp); | 464 | tv = ktime_to_timeval(sk->sk_stamp); |
460 | if (tv.tv_sec == -1) | 465 | if (tv.tv_sec == -1) |
461 | return err; | 466 | return err; |
@@ -479,7 +484,7 @@ int compat_sock_get_timestampns(struct sock *sk, struct timespec __user *usersta | |||
479 | struct timespec ts; | 484 | struct timespec ts; |
480 | 485 | ||
481 | if (!sock_flag(sk, SOCK_TIMESTAMP)) | 486 | if (!sock_flag(sk, SOCK_TIMESTAMP)) |
482 | sock_enable_timestamp(sk); | 487 | sock_enable_timestamp(sk, SOCK_TIMESTAMP); |
483 | ts = ktime_to_timespec(sk->sk_stamp); | 488 | ts = ktime_to_timespec(sk->sk_stamp); |
484 | if (ts.tv_sec == -1) | 489 | if (ts.tv_sec == -1) |
485 | return err; | 490 | return err; |
diff --git a/net/core/sock.c b/net/core/sock.c index 4c64be4f8765..40887e76652c 100644 --- a/net/core/sock.c +++ b/net/core/sock.c | |||
@@ -120,6 +120,7 @@ | |||
120 | #include <net/net_namespace.h> | 120 | #include <net/net_namespace.h> |
121 | #include <net/request_sock.h> | 121 | #include <net/request_sock.h> |
122 | #include <net/sock.h> | 122 | #include <net/sock.h> |
123 | #include <linux/net_tstamp.h> | ||
123 | #include <net/xfrm.h> | 124 | #include <net/xfrm.h> |
124 | #include <linux/ipsec.h> | 125 | #include <linux/ipsec.h> |
125 | 126 | ||
@@ -255,11 +256,14 @@ static void sock_warn_obsolete_bsdism(const char *name) | |||
255 | } | 256 | } |
256 | } | 257 | } |
257 | 258 | ||
258 | static void sock_disable_timestamp(struct sock *sk) | 259 | static void sock_disable_timestamp(struct sock *sk, int flag) |
259 | { | 260 | { |
260 | if (sock_flag(sk, SOCK_TIMESTAMP)) { | 261 | if (sock_flag(sk, flag)) { |
261 | sock_reset_flag(sk, SOCK_TIMESTAMP); | 262 | sock_reset_flag(sk, flag); |
262 | net_disable_timestamp(); | 263 | if (!sock_flag(sk, SOCK_TIMESTAMP) && |
264 | !sock_flag(sk, SOCK_TIMESTAMPING_RX_SOFTWARE)) { | ||
265 | net_disable_timestamp(); | ||
266 | } | ||
263 | } | 267 | } |
264 | } | 268 | } |
265 | 269 | ||
@@ -614,13 +618,38 @@ set_rcvbuf: | |||
614 | else | 618 | else |
615 | sock_set_flag(sk, SOCK_RCVTSTAMPNS); | 619 | sock_set_flag(sk, SOCK_RCVTSTAMPNS); |
616 | sock_set_flag(sk, SOCK_RCVTSTAMP); | 620 | sock_set_flag(sk, SOCK_RCVTSTAMP); |
617 | sock_enable_timestamp(sk); | 621 | sock_enable_timestamp(sk, SOCK_TIMESTAMP); |
618 | } else { | 622 | } else { |
619 | sock_reset_flag(sk, SOCK_RCVTSTAMP); | 623 | sock_reset_flag(sk, SOCK_RCVTSTAMP); |
620 | sock_reset_flag(sk, SOCK_RCVTSTAMPNS); | 624 | sock_reset_flag(sk, SOCK_RCVTSTAMPNS); |
621 | } | 625 | } |
622 | break; | 626 | break; |
623 | 627 | ||
628 | case SO_TIMESTAMPING: | ||
629 | if (val & ~SOF_TIMESTAMPING_MASK) { | ||
630 | ret = EINVAL; | ||
631 | break; | ||
632 | } | ||
633 | sock_valbool_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE, | ||
634 | val & SOF_TIMESTAMPING_TX_HARDWARE); | ||
635 | sock_valbool_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE, | ||
636 | val & SOF_TIMESTAMPING_TX_SOFTWARE); | ||
637 | sock_valbool_flag(sk, SOCK_TIMESTAMPING_RX_HARDWARE, | ||
638 | val & SOF_TIMESTAMPING_RX_HARDWARE); | ||
639 | if (val & SOF_TIMESTAMPING_RX_SOFTWARE) | ||
640 | sock_enable_timestamp(sk, | ||
641 | SOCK_TIMESTAMPING_RX_SOFTWARE); | ||
642 | else | ||
643 | sock_disable_timestamp(sk, | ||
644 | SOCK_TIMESTAMPING_RX_SOFTWARE); | ||
645 | sock_valbool_flag(sk, SOCK_TIMESTAMPING_SOFTWARE, | ||
646 | val & SOF_TIMESTAMPING_SOFTWARE); | ||
647 | sock_valbool_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE, | ||
648 | val & SOF_TIMESTAMPING_SYS_HARDWARE); | ||
649 | sock_valbool_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE, | ||
650 | val & SOF_TIMESTAMPING_RAW_HARDWARE); | ||
651 | break; | ||
652 | |||
624 | case SO_RCVLOWAT: | 653 | case SO_RCVLOWAT: |
625 | if (val < 0) | 654 | if (val < 0) |
626 | val = INT_MAX; | 655 | val = INT_MAX; |
@@ -768,6 +797,24 @@ int sock_getsockopt(struct socket *sock, int level, int optname, | |||
768 | v.val = sock_flag(sk, SOCK_RCVTSTAMPNS); | 797 | v.val = sock_flag(sk, SOCK_RCVTSTAMPNS); |
769 | break; | 798 | break; |
770 | 799 | ||
800 | case SO_TIMESTAMPING: | ||
801 | v.val = 0; | ||
802 | if (sock_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE)) | ||
803 | v.val |= SOF_TIMESTAMPING_TX_HARDWARE; | ||
804 | if (sock_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE)) | ||
805 | v.val |= SOF_TIMESTAMPING_TX_SOFTWARE; | ||
806 | if (sock_flag(sk, SOCK_TIMESTAMPING_RX_HARDWARE)) | ||
807 | v.val |= SOF_TIMESTAMPING_RX_HARDWARE; | ||
808 | if (sock_flag(sk, SOCK_TIMESTAMPING_RX_SOFTWARE)) | ||
809 | v.val |= SOF_TIMESTAMPING_RX_SOFTWARE; | ||
810 | if (sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE)) | ||
811 | v.val |= SOF_TIMESTAMPING_SOFTWARE; | ||
812 | if (sock_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE)) | ||
813 | v.val |= SOF_TIMESTAMPING_SYS_HARDWARE; | ||
814 | if (sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE)) | ||
815 | v.val |= SOF_TIMESTAMPING_RAW_HARDWARE; | ||
816 | break; | ||
817 | |||
771 | case SO_RCVTIMEO: | 818 | case SO_RCVTIMEO: |
772 | lv=sizeof(struct timeval); | 819 | lv=sizeof(struct timeval); |
773 | if (sk->sk_rcvtimeo == MAX_SCHEDULE_TIMEOUT) { | 820 | if (sk->sk_rcvtimeo == MAX_SCHEDULE_TIMEOUT) { |
@@ -969,7 +1016,8 @@ void sk_free(struct sock *sk) | |||
969 | rcu_assign_pointer(sk->sk_filter, NULL); | 1016 | rcu_assign_pointer(sk->sk_filter, NULL); |
970 | } | 1017 | } |
971 | 1018 | ||
972 | sock_disable_timestamp(sk); | 1019 | sock_disable_timestamp(sk, SOCK_TIMESTAMP); |
1020 | sock_disable_timestamp(sk, SOCK_TIMESTAMPING_RX_SOFTWARE); | ||
973 | 1021 | ||
974 | if (atomic_read(&sk->sk_omem_alloc)) | 1022 | if (atomic_read(&sk->sk_omem_alloc)) |
975 | printk(KERN_DEBUG "%s: optmem leakage (%d bytes) detected.\n", | 1023 | printk(KERN_DEBUG "%s: optmem leakage (%d bytes) detected.\n", |
@@ -1787,7 +1835,7 @@ int sock_get_timestamp(struct sock *sk, struct timeval __user *userstamp) | |||
1787 | { | 1835 | { |
1788 | struct timeval tv; | 1836 | struct timeval tv; |
1789 | if (!sock_flag(sk, SOCK_TIMESTAMP)) | 1837 | if (!sock_flag(sk, SOCK_TIMESTAMP)) |
1790 | sock_enable_timestamp(sk); | 1838 | sock_enable_timestamp(sk, SOCK_TIMESTAMP); |
1791 | tv = ktime_to_timeval(sk->sk_stamp); | 1839 | tv = ktime_to_timeval(sk->sk_stamp); |
1792 | if (tv.tv_sec == -1) | 1840 | if (tv.tv_sec == -1) |
1793 | return -ENOENT; | 1841 | return -ENOENT; |
@@ -1803,7 +1851,7 @@ int sock_get_timestampns(struct sock *sk, struct timespec __user *userstamp) | |||
1803 | { | 1851 | { |
1804 | struct timespec ts; | 1852 | struct timespec ts; |
1805 | if (!sock_flag(sk, SOCK_TIMESTAMP)) | 1853 | if (!sock_flag(sk, SOCK_TIMESTAMP)) |
1806 | sock_enable_timestamp(sk); | 1854 | sock_enable_timestamp(sk, SOCK_TIMESTAMP); |
1807 | ts = ktime_to_timespec(sk->sk_stamp); | 1855 | ts = ktime_to_timespec(sk->sk_stamp); |
1808 | if (ts.tv_sec == -1) | 1856 | if (ts.tv_sec == -1) |
1809 | return -ENOENT; | 1857 | return -ENOENT; |
@@ -1815,11 +1863,20 @@ int sock_get_timestampns(struct sock *sk, struct timespec __user *userstamp) | |||
1815 | } | 1863 | } |
1816 | EXPORT_SYMBOL(sock_get_timestampns); | 1864 | EXPORT_SYMBOL(sock_get_timestampns); |
1817 | 1865 | ||
1818 | void sock_enable_timestamp(struct sock *sk) | 1866 | void sock_enable_timestamp(struct sock *sk, int flag) |
1819 | { | 1867 | { |
1820 | if (!sock_flag(sk, SOCK_TIMESTAMP)) { | 1868 | if (!sock_flag(sk, flag)) { |
1821 | sock_set_flag(sk, SOCK_TIMESTAMP); | 1869 | sock_set_flag(sk, flag); |
1822 | net_enable_timestamp(); | 1870 | /* |
1871 | * we just set one of the two flags which require net | ||
1872 | * time stamping, but time stamping might have been on | ||
1873 | * already because of the other one | ||
1874 | */ | ||
1875 | if (!sock_flag(sk, | ||
1876 | flag == SOCK_TIMESTAMP ? | ||
1877 | SOCK_TIMESTAMPING_RX_SOFTWARE : | ||
1878 | SOCK_TIMESTAMP)) | ||
1879 | net_enable_timestamp(); | ||
1823 | } | 1880 | } |
1824 | } | 1881 | } |
1825 | 1882 | ||
diff --git a/net/socket.c b/net/socket.c index 35dd7371752a..47a3dc074eb0 100644 --- a/net/socket.c +++ b/net/socket.c | |||
@@ -545,6 +545,18 @@ void sock_release(struct socket *sock) | |||
545 | sock->file = NULL; | 545 | sock->file = NULL; |
546 | } | 546 | } |
547 | 547 | ||
548 | int sock_tx_timestamp(struct msghdr *msg, struct sock *sk, | ||
549 | union skb_shared_tx *shtx) | ||
550 | { | ||
551 | shtx->flags = 0; | ||
552 | if (sock_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE)) | ||
553 | shtx->hardware = 1; | ||
554 | if (sock_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE)) | ||
555 | shtx->software = 1; | ||
556 | return 0; | ||
557 | } | ||
558 | EXPORT_SYMBOL(sock_tx_timestamp); | ||
559 | |||
548 | static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock, | 560 | static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock, |
549 | struct msghdr *msg, size_t size) | 561 | struct msghdr *msg, size_t size) |
550 | { | 562 | { |
@@ -595,33 +607,65 @@ int kernel_sendmsg(struct socket *sock, struct msghdr *msg, | |||
595 | return result; | 607 | return result; |
596 | } | 608 | } |
597 | 609 | ||
610 | static int ktime2ts(ktime_t kt, struct timespec *ts) | ||
611 | { | ||
612 | if (kt.tv64) { | ||
613 | *ts = ktime_to_timespec(kt); | ||
614 | return 1; | ||
615 | } else { | ||
616 | return 0; | ||
617 | } | ||
618 | } | ||
619 | |||
598 | /* | 620 | /* |
599 | * called from sock_recv_timestamp() if sock_flag(sk, SOCK_RCVTSTAMP) | 621 | * called from sock_recv_timestamp() if sock_flag(sk, SOCK_RCVTSTAMP) |
600 | */ | 622 | */ |
601 | void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, | 623 | void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, |
602 | struct sk_buff *skb) | 624 | struct sk_buff *skb) |
603 | { | 625 | { |
604 | ktime_t kt = skb->tstamp; | 626 | int need_software_tstamp = sock_flag(sk, SOCK_RCVTSTAMP); |
605 | 627 | struct timespec ts[3]; | |
606 | if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) { | 628 | int empty = 1; |
607 | struct timeval tv; | 629 | struct skb_shared_hwtstamps *shhwtstamps = |
608 | /* Race occurred between timestamp enabling and packet | 630 | skb_hwtstamps(skb); |
609 | receiving. Fill in the current time for now. */ | 631 | |
610 | if (kt.tv64 == 0) | 632 | /* Race occurred between timestamp enabling and packet |
611 | kt = ktime_get_real(); | 633 | receiving. Fill in the current time for now. */ |
612 | skb->tstamp = kt; | 634 | if (need_software_tstamp && skb->tstamp.tv64 == 0) |
613 | tv = ktime_to_timeval(kt); | 635 | __net_timestamp(skb); |
614 | put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMP, sizeof(tv), &tv); | 636 | |
615 | } else { | 637 | if (need_software_tstamp) { |
616 | struct timespec ts; | 638 | if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) { |
617 | /* Race occurred between timestamp enabling and packet | 639 | struct timeval tv; |
618 | receiving. Fill in the current time for now. */ | 640 | skb_get_timestamp(skb, &tv); |
619 | if (kt.tv64 == 0) | 641 | put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMP, |
620 | kt = ktime_get_real(); | 642 | sizeof(tv), &tv); |
621 | skb->tstamp = kt; | 643 | } else { |
622 | ts = ktime_to_timespec(kt); | 644 | struct timespec ts; |
623 | put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPNS, sizeof(ts), &ts); | 645 | skb_get_timestampns(skb, &ts); |
646 | put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPNS, | ||
647 | sizeof(ts), &ts); | ||
648 | } | ||
649 | } | ||
650 | |||
651 | |||
652 | memset(ts, 0, sizeof(ts)); | ||
653 | if (skb->tstamp.tv64 && | ||
654 | sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE)) { | ||
655 | skb_get_timestampns(skb, ts + 0); | ||
656 | empty = 0; | ||
657 | } | ||
658 | if (shhwtstamps) { | ||
659 | if (sock_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE) && | ||
660 | ktime2ts(shhwtstamps->syststamp, ts + 1)) | ||
661 | empty = 0; | ||
662 | if (sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE) && | ||
663 | ktime2ts(shhwtstamps->hwtstamp, ts + 2)) | ||
664 | empty = 0; | ||
624 | } | 665 | } |
666 | if (!empty) | ||
667 | put_cmsg(msg, SOL_SOCKET, | ||
668 | SCM_TIMESTAMPING, sizeof(ts), &ts); | ||
625 | } | 669 | } |
626 | 670 | ||
627 | EXPORT_SYMBOL_GPL(__sock_recv_timestamp); | 671 | EXPORT_SYMBOL_GPL(__sock_recv_timestamp); |