af_unix: conditionally use freezable blocking calls in read
authorWANG Cong <xiyou.wangcong@gmail.com>
Thu, 17 Nov 2016 23:55:26 +0000 (15:55 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 18 Nov 2016 18:58:39 +0000 (13:58 -0500)
Commit 2b15af6f95 ("af_unix: use freezable blocking calls in read")
converts schedule_timeout() to its freezable version, it was probably
correct at that time, but later, commit 2b514574f7e8
("net: af_unix: implement splice for stream af_unix sockets") breaks
the strong requirement for a freezable sleep, according to
commit 0f9548ca1091:

    We shouldn't try_to_freeze if locks are held.  Holding a lock can cause a
    deadlock if the lock is later acquired in the suspend or hibernate path
    (e.g.  by dpm).  Holding a lock can also cause a deadlock in the case of
    cgroup_freezer if a lock is held inside a frozen cgroup that is later
    acquired by a process outside that group.

The pipe_lock is still held at that point.

So use freezable version only for the recvmsg call path, avoid impact for
Android.

Fixes: 2b514574f7e8 ("net: af_unix: implement splice for stream af_unix sockets")
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Colin Cross <ccross@android.com>
Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Cc: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/unix/af_unix.c

index 5d1c14a2f26823f403d2677e27a34b9a2e5a3d6d..2358f2690ec58c20aeb77fb60ac45b3db27ecac6 100644 (file)
@@ -2199,7 +2199,8 @@ out:
  *     Sleep until more data has arrived. But check for races..
  */
 static long unix_stream_data_wait(struct sock *sk, long timeo,
-                                 struct sk_buff *last, unsigned int last_len)
+                                 struct sk_buff *last, unsigned int last_len,
+                                 bool freezable)
 {
        struct sk_buff *tail;
        DEFINE_WAIT(wait);
@@ -2220,7 +2221,10 @@ static long unix_stream_data_wait(struct sock *sk, long timeo,
 
                sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
                unix_state_unlock(sk);
-               timeo = freezable_schedule_timeout(timeo);
+               if (freezable)
+                       timeo = freezable_schedule_timeout(timeo);
+               else
+                       timeo = schedule_timeout(timeo);
                unix_state_lock(sk);
 
                if (sock_flag(sk, SOCK_DEAD))
@@ -2250,7 +2254,8 @@ struct unix_stream_read_state {
        unsigned int splice_flags;
 };
 
-static int unix_stream_read_generic(struct unix_stream_read_state *state)
+static int unix_stream_read_generic(struct unix_stream_read_state *state,
+                                   bool freezable)
 {
        struct scm_cookie scm;
        struct socket *sock = state->socket;
@@ -2330,7 +2335,7 @@ again:
                        mutex_unlock(&u->iolock);
 
                        timeo = unix_stream_data_wait(sk, timeo, last,
-                                                     last_len);
+                                                     last_len, freezable);
 
                        if (signal_pending(current)) {
                                err = sock_intr_errno(timeo);
@@ -2472,7 +2477,7 @@ static int unix_stream_recvmsg(struct socket *sock, struct msghdr *msg,
                .flags = flags
        };
 
-       return unix_stream_read_generic(&state);
+       return unix_stream_read_generic(&state, true);
 }
 
 static int unix_stream_splice_actor(struct sk_buff *skb,
@@ -2503,7 +2508,7 @@ static ssize_t unix_stream_splice_read(struct socket *sock,  loff_t *ppos,
            flags & SPLICE_F_NONBLOCK)
                state.flags = MSG_DONTWAIT;
 
-       return unix_stream_read_generic(&state);
+       return unix_stream_read_generic(&state, false);
 }
 
 static int unix_shutdown(struct socket *sock, int mode)