net-timestamp: TCP timestamping
[cascardo/linux.git] / net / core / sock.c
index 026e01f..2714811 100644 (file)
@@ -491,7 +491,7 @@ int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested)
 
        skb->dev = NULL;
 
-       if (sk_rcvqueues_full(sk, skb, sk->sk_rcvbuf)) {
+       if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) {
                atomic_inc(&sk->sk_drops);
                goto discard_and_relse;
        }
@@ -848,24 +848,25 @@ set_rcvbuf:
                        ret = -EINVAL;
                        break;
                }
-               sock_valbool_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE,
-                                 val & SOF_TIMESTAMPING_TX_HARDWARE);
-               sock_valbool_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE,
-                                 val & SOF_TIMESTAMPING_TX_SOFTWARE);
-               sock_valbool_flag(sk, SOCK_TIMESTAMPING_RX_HARDWARE,
-                                 val & SOF_TIMESTAMPING_RX_HARDWARE);
+               if (val & SOF_TIMESTAMPING_OPT_ID &&
+                   !(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)) {
+                       if (sk->sk_protocol == IPPROTO_TCP) {
+                               if (sk->sk_state != TCP_ESTABLISHED) {
+                                       ret = -EINVAL;
+                                       break;
+                               }
+                               sk->sk_tskey = tcp_sk(sk)->snd_una;
+                       } else {
+                               sk->sk_tskey = 0;
+                       }
+               }
+               sk->sk_tsflags = val;
                if (val & SOF_TIMESTAMPING_RX_SOFTWARE)
                        sock_enable_timestamp(sk,
                                              SOCK_TIMESTAMPING_RX_SOFTWARE);
                else
                        sock_disable_timestamp(sk,
                                               (1UL << SOCK_TIMESTAMPING_RX_SOFTWARE));
-               sock_valbool_flag(sk, SOCK_TIMESTAMPING_SOFTWARE,
-                                 val & SOF_TIMESTAMPING_SOFTWARE);
-               sock_valbool_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE,
-                                 val & SOF_TIMESTAMPING_SYS_HARDWARE);
-               sock_valbool_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE,
-                                 val & SOF_TIMESTAMPING_RAW_HARDWARE);
                break;
 
        case SO_RCVLOWAT:
@@ -1091,21 +1092,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
                break;
 
        case SO_TIMESTAMPING:
-               v.val = 0;
-               if (sock_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE))
-                       v.val |= SOF_TIMESTAMPING_TX_HARDWARE;
-               if (sock_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE))
-                       v.val |= SOF_TIMESTAMPING_TX_SOFTWARE;
-               if (sock_flag(sk, SOCK_TIMESTAMPING_RX_HARDWARE))
-                       v.val |= SOF_TIMESTAMPING_RX_HARDWARE;
-               if (sock_flag(sk, SOCK_TIMESTAMPING_RX_SOFTWARE))
-                       v.val |= SOF_TIMESTAMPING_RX_SOFTWARE;
-               if (sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE))
-                       v.val |= SOF_TIMESTAMPING_SOFTWARE;
-               if (sock_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE))
-                       v.val |= SOF_TIMESTAMPING_SYS_HARDWARE;
-               if (sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE))
-                       v.val |= SOF_TIMESTAMPING_RAW_HARDWARE;
+               v.val = sk->sk_tsflags;
                break;
 
        case SO_RCVTIMEO:
@@ -1478,6 +1465,7 @@ static void sk_update_clone(const struct sock *sk, struct sock *newsk)
 struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
 {
        struct sock *newsk;
+       bool is_charged = true;
 
        newsk = sk_prot_alloc(sk->sk_prot, priority, sk->sk_family);
        if (newsk != NULL) {
@@ -1522,9 +1510,13 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
 
                filter = rcu_dereference_protected(newsk->sk_filter, 1);
                if (filter != NULL)
-                       sk_filter_charge(newsk, filter);
+                       /* though it's an empty new sock, the charging may fail
+                        * if sysctl_optmem_max was changed between creation of
+                        * original socket and cloning
+                        */
+                       is_charged = sk_filter_charge(newsk, filter);
 
-               if (unlikely(xfrm_sk_clone_policy(newsk))) {
+               if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk))) {
                        /* It is still raw copy of parent, so invalidate
                         * destructor and make plain sk_free() */
                        newsk->sk_destruct = NULL;