| From foo@baz Thu Dec 8 07:19:12 CET 2016 |
| From: WANG Cong <xiyou.wangcong@gmail.com> |
| Date: Thu, 17 Nov 2016 15:55:26 -0800 |
| Subject: af_unix: conditionally use freezable blocking calls in read |
| |
| From: WANG Cong <xiyou.wangcong@gmail.com> |
| |
| |
| [ Upstream commit 06a77b07e3b44aea2b3c0e64de420ea2cfdcbaa9 ] |
| |
| 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> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/unix/af_unix.c | 17 +++++++++++------ |
| 1 file changed, 11 insertions(+), 6 deletions(-) |
| |
| --- a/net/unix/af_unix.c |
| +++ b/net/unix/af_unix.c |
| @@ -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 |
| |
| 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 so |
| .flags = flags |
| }; |
| |
| - return unix_stream_read_generic(&state); |
| + return unix_stream_read_generic(&state, true); |
| } |
| |
| static ssize_t skb_unix_socket_splice(struct sock *sk, |
| @@ -2518,7 +2523,7 @@ static ssize_t unix_stream_splice_read(s |
| 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) |