Re: [PATCH v3] sock: Make sock->sk_stamp thread-safe

2019-01-01 Thread David Miller
From: Deepa Dinamani 
Date: Thu, 27 Dec 2018 18:55:09 -0800

> Al Viro mentioned (Message-ID
> <20170626041334.gz10...@zeniv.linux.org.uk>)
> that there is probably a race condition
> lurking in accesses of sk_stamp on 32-bit machines.
> 
> sock->sk_stamp is of type ktime_t which is always an s64.
> On a 32 bit architecture, we might run into situations of
> unsafe access as the access to the field becomes non atomic.
> 
> Use seqlocks for synchronization.
> This allows us to avoid using spinlocks for readers as
> readers do not need mutual exclusion.
> 
> Another approach to solve this is to require sk_lock for all
> modifications of the timestamps. The current approach allows
> for timestamps to have their own lock: sk_stamp_lock.
> This allows for the patch to not compete with already
> existing critical sections, and side effects are limited
> to the paths in the patch.
> 
> The addition of the new field maintains the data locality
> optimizations from
> commit 9115e8cd2a0c ("net: reorganize struct sock for better data
> locality")
> 
> Note that all the instances of the sk_stamp accesses
> are either through the ioctl or the syscall recvmsg.
> 
> Signed-off-by: Deepa Dinamani 

Ok, I'm fine with this, so applied and queued up for -stable.

I will note in passing that there are several 32-bit architectures
that have 64-bit loads.  Sparc is one such case.  And they would not
need these changes.

But I don't think it's practical or worthwhile to add that level of
consideration into your changes.  I'd rather the commit stay as simple
as possible.

Thanks!


[PATCH v3] sock: Make sock->sk_stamp thread-safe

2018-12-27 Thread Deepa Dinamani
Al Viro mentioned (Message-ID
<20170626041334.gz10...@zeniv.linux.org.uk>)
that there is probably a race condition
lurking in accesses of sk_stamp on 32-bit machines.

sock->sk_stamp is of type ktime_t which is always an s64.
On a 32 bit architecture, we might run into situations of
unsafe access as the access to the field becomes non atomic.

Use seqlocks for synchronization.
This allows us to avoid using spinlocks for readers as
readers do not need mutual exclusion.

Another approach to solve this is to require sk_lock for all
modifications of the timestamps. The current approach allows
for timestamps to have their own lock: sk_stamp_lock.
This allows for the patch to not compete with already
existing critical sections, and side effects are limited
to the paths in the patch.

The addition of the new field maintains the data locality
optimizations from
commit 9115e8cd2a0c ("net: reorganize struct sock for better data
locality")

Note that all the instances of the sk_stamp accesses
are either through the ioctl or the syscall recvmsg.

Signed-off-by: Deepa Dinamani 
---
Changes since v2:
* added ifdef as per eric's request
Changes since v1:
* fixed sunrpc sk_stamp update

 include/net/sock.h   | 38 +++---
 net/compat.c | 15 +--
 net/core/sock.c  | 15 ++-
 net/sunrpc/svcsock.c |  2 +-
 4 files changed, 55 insertions(+), 15 deletions(-)

diff --git a/include/net/sock.h b/include/net/sock.h
index f665d74ae509..e144c071c93f 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -298,6 +298,7 @@ struct sock_common {
   *@sk_filter: socket filtering instructions
   *@sk_timer: sock cleanup timer
   *@sk_stamp: time stamp of last packet received
+  *@sk_stamp_seq: lock for accessing sk_stamp on 32 bit architectures only
   *@sk_tsflags: SO_TIMESTAMPING socket options
   *@sk_tskey: counter to disambiguate concurrent tstamp requests
   *@sk_zckey: counter to order MSG_ZEROCOPY notifications
@@ -474,6 +475,9 @@ struct sock {
const struct cred   *sk_peer_cred;
longsk_rcvtimeo;
ktime_t sk_stamp;
+#if BITS_PER_LONG==32
+   seqlock_t   sk_stamp_seq;
+#endif
u16 sk_tsflags;
u8  sk_shutdown;
u32 sk_tskey;
@@ -2287,6 +2291,34 @@ static inline void sk_drops_add(struct sock *sk, const 
struct sk_buff *skb)
atomic_add(segs, >sk_drops);
 }
 
+static inline ktime_t sock_read_timestamp(struct sock *sk)
+{
+#if BITS_PER_LONG==32
+   unsigned int seq;
+   ktime_t kt;
+
+   do {
+   seq = read_seqbegin(>sk_stamp_seq);
+   kt = sk->sk_stamp;
+   } while (read_seqretry(>sk_stamp_seq, seq));
+
+   return kt;
+#else
+   return sk->sk_stamp;
+#endif
+}
+
+static inline void sock_write_timestamp(struct sock *sk, ktime_t kt)
+{
+#if BITS_PER_LONG==32
+   write_seqlock(>sk_stamp_seq);
+   sk->sk_stamp = kt;
+   write_sequnlock(>sk_stamp_seq);
+#else
+   sk->sk_stamp = kt;
+#endif
+}
+
 void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
   struct sk_buff *skb);
 void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk,
@@ -2311,7 +2343,7 @@ sock_recv_timestamp(struct msghdr *msg, struct sock *sk, 
struct sk_buff *skb)
 (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE)))
__sock_recv_timestamp(msg, sk, skb);
else
-   sk->sk_stamp = kt;
+   sock_write_timestamp(sk, kt);
 
if (sock_flag(sk, SOCK_WIFI_STATUS) && skb->wifi_acked_valid)
__sock_recv_wifi_status(msg, sk, skb);
@@ -2332,9 +2364,9 @@ static inline void sock_recv_ts_and_drops(struct msghdr 
*msg, struct sock *sk,
if (sk->sk_flags & FLAGS_TS_OR_DROPS || sk->sk_tsflags & TSFLAGS_ANY)
__sock_recv_ts_and_drops(msg, sk, skb);
else if (unlikely(sock_flag(sk, SOCK_TIMESTAMP)))
-   sk->sk_stamp = skb->tstamp;
+   sock_write_timestamp(sk, skb->tstamp);
else if (unlikely(sk->sk_stamp == SK_DEFAULT_STAMP))
-   sk->sk_stamp = 0;
+   sock_write_timestamp(sk, 0);
 }
 
 void __sock_tx_timestamp(__u16 tsflags, __u8 *tx_flags);
diff --git a/net/compat.c b/net/compat.c
index 47a614b370cd..d1f3a8a0b3ef 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -467,12 +467,14 @@ int compat_sock_get_timestamp(struct sock *sk, struct 
timeval __user *userstamp)
ctv = (struct compat_timeval __user *) userstamp;
err = -ENOENT;
sock_enable_timestamp(sk, SOCK_TIMESTAMP);
-   tv = ktime_to_timeval(sk->sk_stamp);
+   tv = ktime_to_timeval(sock_read_timestamp(sk));
+
if (tv.tv_sec == -1)
return err;
if (tv.tv_sec == 0) {
-   sk->sk_stamp = ktime_get_real();
-   tv =