the timestamp even if sysctl net.core.tstamp_allow_data is 0.
   This option disables SOF_TIMESTAMPING_OPT_CMSG.
 
+SOF_TIMESTAMPING_OPT_STATS:
+
+  Optional stats that are obtained along with the transmit timestamps.
+  It must be used together with SOF_TIMESTAMPING_OPT_TSONLY. When the
+  transmit timestamp is available, the stats are available in a
+  separate control message of type SCM_TIMESTAMPING_OPT_STATS, as a
+  list of TLVs (struct nlattr) of types. These stats allow the
+  application to associate various transport layer stats with
+  the transmit timestamps, such as how long a certain block of
+  data was limited by peer's receiver window.
 
 New applications are encouraged to pass SOF_TIMESTAMPING_OPT_ID to
 disambiguate timestamps and SOF_TIMESTAMPING_OPT_TSONLY to operate
 
 
 #define SO_CNX_ADVICE          53
 
+#define SCM_TIMESTAMPING_OPT_STATS     54
+
 #endif /* _UAPI_ASM_SOCKET_H */
 
 
 #define SO_CNX_ADVICE          53
 
+#define SCM_TIMESTAMPING_OPT_STATS     54
+
 #endif /* _ASM_SOCKET_H */
 
 
 
 #define SO_CNX_ADVICE          53
 
+#define SCM_TIMESTAMPING_OPT_STATS     54
+
 #endif /* _ASM_IA64_SOCKET_H */
 
 
 #define SO_CNX_ADVICE          53
 
+#define SCM_TIMESTAMPING_OPT_STATS     54
+
 #endif /* _ASM_M32R_SOCKET_H */
 
 
 #define SO_CNX_ADVICE          53
 
+#define SCM_TIMESTAMPING_OPT_STATS     54
+
 #endif /* _UAPI_ASM_SOCKET_H */
 
 
 #define SO_CNX_ADVICE          53
 
+#define SCM_TIMESTAMPING_OPT_STATS     54
+
 #endif /* _ASM_SOCKET_H */
 
 
 #define SO_CNX_ADVICE          0x402E
 
+#define SCM_TIMESTAMPING_OPT_STATS     0x402F
+
 #endif /* _UAPI_ASM_SOCKET_H */
 
 
 #define SO_CNX_ADVICE          53
 
+#define SCM_TIMESTAMPING_OPT_STATS     54
+
 #endif /* _ASM_POWERPC_SOCKET_H */
 
 
 #define SO_CNX_ADVICE          53
 
+#define SCM_TIMESTAMPING_OPT_STATS     54
+
 #endif /* _ASM_SOCKET_H */
 
 
 #define SO_CNX_ADVICE          0x0037
 
+#define SCM_TIMESTAMPING_OPT_STATS     0x0038
+
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION             0x5001
 #define SO_SECURITY_ENCRYPTION_TRANSPORT       0x5002
 
 
 #define SO_CNX_ADVICE          53
 
+#define SCM_TIMESTAMPING_OPT_STATS     54
+
 #endif /* _XTENSA_SOCKET_H */
 
        tp->saved_syn = NULL;
 }
 
+struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk);
+
 #endif /* _LINUX_TCP_H */
 
 
 #define SO_CNX_ADVICE          53
 
+#define SCM_TIMESTAMPING_OPT_STATS     54
+
 #endif /* __ASM_GENERIC_SOCKET_H */
 
        SOF_TIMESTAMPING_TX_ACK = (1<<9),
        SOF_TIMESTAMPING_OPT_CMSG = (1<<10),
        SOF_TIMESTAMPING_OPT_TSONLY = (1<<11),
+       SOF_TIMESTAMPING_OPT_STATS = (1<<12),
 
-       SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_OPT_TSONLY,
+       SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_OPT_STATS,
        SOF_TIMESTAMPING_MASK = (SOF_TIMESTAMPING_LAST - 1) |
                                 SOF_TIMESTAMPING_LAST
 };
 
        __u64   tcpi_sndbuf_limited; /* Time (usec) limited by send buffer */
 };
 
+/* netlink attributes types for SCM_TIMESTAMPING_OPT_STATS */
+enum {
+       TCP_NLA_PAD,
+       TCP_NLA_BUSY,           /* Time (usec) busy sending data */
+       TCP_NLA_RWND_LIMITED,   /* Time (usec) limited by receive window */
+       TCP_NLA_SNDBUF_LIMITED, /* Time (usec) limited by send buffer */
+};
+
 /* for TCP_MD5SIG socket option */
 #define TCP_MD5SIG_MAXKEYLEN   80
 
 
        if (!skb_may_tx_timestamp(sk, tsonly))
                return;
 
-       if (tsonly)
-               skb = alloc_skb(0, GFP_ATOMIC);
-       else
+       if (tsonly) {
+#ifdef CONFIG_INET
+               if ((sk->sk_tsflags & SOF_TIMESTAMPING_OPT_STATS) &&
+                   sk->sk_protocol == IPPROTO_TCP &&
+                   sk->sk_type == SOCK_STREAM)
+                       skb = tcp_get_timestamping_opt_stats(sk);
+               else
+#endif
+                       skb = alloc_skb(0, GFP_ATOMIC);
+       } else {
                skb = skb_clone(orig_skb, GFP_ATOMIC);
+       }
        if (!skb)
                return;
 
 
                                sk->sk_tskey = 0;
                        }
                }
+
+               if (val & SOF_TIMESTAMPING_OPT_STATS &&
+                   !(val & SOF_TIMESTAMPING_OPT_TSONLY)) {
+                       ret = -EINVAL;
+                       break;
+               }
+
                sk->sk_tsflags = val;
                if (val & SOF_TIMESTAMPING_RX_SOFTWARE)
                        sock_enable_timestamp(sk,
 
 }
 EXPORT_SYMBOL_GPL(tcp_get_info);
 
+struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk)
+{
+       const struct tcp_sock *tp = tcp_sk(sk);
+       struct sk_buff *stats;
+       struct tcp_info info;
+
+       stats = alloc_skb(3 * nla_total_size_64bit(sizeof(u64)), GFP_ATOMIC);
+       if (!stats)
+               return NULL;
+
+       tcp_get_info_chrono_stats(tp, &info);
+       nla_put_u64_64bit(stats, TCP_NLA_BUSY,
+                         info.tcpi_busy_time, TCP_NLA_PAD);
+       nla_put_u64_64bit(stats, TCP_NLA_RWND_LIMITED,
+                         info.tcpi_rwnd_limited, TCP_NLA_PAD);
+       nla_put_u64_64bit(stats, TCP_NLA_SNDBUF_LIMITED,
+                         info.tcpi_sndbuf_limited, TCP_NLA_PAD);
+       return stats;
+}
+
 static int do_tcp_getsockopt(struct sock *sk, int level,
                int optname, char __user *optval, int __user *optlen)
 {
 
            (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE) &&
            ktime_to_timespec_cond(shhwtstamps->hwtstamp, tss.ts + 2))
                empty = 0;
-       if (!empty)
+       if (!empty) {
                put_cmsg(msg, SOL_SOCKET,
                         SCM_TIMESTAMPING, sizeof(tss), &tss);
+
+               if (skb->len && (sk->sk_tsflags & SOF_TIMESTAMPING_OPT_STATS))
+                       put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPING_OPT_STATS,
+                                skb->len, skb->data);
+       }
 }
 EXPORT_SYMBOL_GPL(__sock_recv_timestamp);