diff options
author | Jeff Layton <jlayton@redhat.com> | 2012-07-23 13:58:51 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-08-09 11:27:51 -0400 |
commit | eb65b85e1bce06b665b3568c19a249cd886fa6ff (patch) | |
tree | e18a826bcd0f37a492245735582bfe4119ecede4 | |
parent | 9d0ed6ec043bb8cbb895d13ce780878e2e50df80 (diff) |
nfs: skip commit in releasepage if we're freeing memory for fs-related reasons
commit 5cf02d09b50b1ee1c2d536c9cf64af5a7d433f56 upstream.
We've had some reports of a deadlock where rpciod ends up with a stack
trace like this:
PID: 2507 TASK: ffff88103691ab40 CPU: 14 COMMAND: "rpciod/14"
#0 [ffff8810343bf2f0] schedule at ffffffff814dabd9
#1 [ffff8810343bf3b8] nfs_wait_bit_killable at ffffffffa038fc04 [nfs]
#2 [ffff8810343bf3c8] __wait_on_bit at ffffffff814dbc2f
#3 [ffff8810343bf418] out_of_line_wait_on_bit at ffffffff814dbcd8
#4 [ffff8810343bf488] nfs_commit_inode at ffffffffa039e0c1 [nfs]
#5 [ffff8810343bf4f8] nfs_release_page at ffffffffa038bef6 [nfs]
#6 [ffff8810343bf528] try_to_release_page at ffffffff8110c670
#7 [ffff8810343bf538] shrink_page_list.clone.0 at ffffffff81126271
#8 [ffff8810343bf668] shrink_inactive_list at ffffffff81126638
#9 [ffff8810343bf818] shrink_zone at ffffffff8112788f
#10 [ffff8810343bf8c8] do_try_to_free_pages at ffffffff81127b1e
#11 [ffff8810343bf958] try_to_free_pages at ffffffff8112812f
#12 [ffff8810343bfa08] __alloc_pages_nodemask at ffffffff8111fdad
#13 [ffff8810343bfb28] kmem_getpages at ffffffff81159942
#14 [ffff8810343bfb58] fallback_alloc at ffffffff8115a55a
#15 [ffff8810343bfbd8] ____cache_alloc_node at ffffffff8115a2d9
#16 [ffff8810343bfc38] kmem_cache_alloc at ffffffff8115b09b
#17 [ffff8810343bfc78] sk_prot_alloc at ffffffff81411808
#18 [ffff8810343bfcb8] sk_alloc at ffffffff8141197c
#19 [ffff8810343bfce8] inet_create at ffffffff81483ba6
#20 [ffff8810343bfd38] __sock_create at ffffffff8140b4a7
#21 [ffff8810343bfd98] xs_create_sock at ffffffffa01f649b [sunrpc]
#22 [ffff8810343bfdd8] xs_tcp_setup_socket at ffffffffa01f6965 [sunrpc]
#23 [ffff8810343bfe38] worker_thread at ffffffff810887d0
#24 [ffff8810343bfee8] kthread at ffffffff8108dd96
#25 [ffff8810343bff48] kernel_thread at ffffffff8100c1ca
rpciod is trying to allocate memory for a new socket to talk to the
server. The VM ends up calling ->releasepage to get more memory, and it
tries to do a blocking commit. That commit can't succeed however without
a connected socket, so we deadlock.
Fix this by setting PF_FSTRANS on the workqueue task prior to doing the
socket allocation, and having nfs_release_page check for that flag when
deciding whether to do a commit call. Also, set PF_FSTRANS
unconditionally in rpc_async_schedule since that function can also do
allocations sometimes.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | fs/nfs/file.c | 7 | ||||
-rw-r--r-- | net/sunrpc/sched.c | 2 | ||||
-rw-r--r-- | net/sunrpc/xprtrdma/transport.c | 3 | ||||
-rw-r--r-- | net/sunrpc/xprtsock.c | 10 |
4 files changed, 19 insertions, 3 deletions
diff --git a/fs/nfs/file.c b/fs/nfs/file.c index dd2f13077be..6c6e2c46122 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c | |||
@@ -493,8 +493,11 @@ static int nfs_release_page(struct page *page, gfp_t gfp) | |||
493 | 493 | ||
494 | dfprintk(PAGECACHE, "NFS: release_page(%p)\n", page); | 494 | dfprintk(PAGECACHE, "NFS: release_page(%p)\n", page); |
495 | 495 | ||
496 | /* Only do I/O if gfp is a superset of GFP_KERNEL */ | 496 | /* Only do I/O if gfp is a superset of GFP_KERNEL, and we're not |
497 | if (mapping && (gfp & GFP_KERNEL) == GFP_KERNEL) { | 497 | * doing this memory reclaim for a fs-related allocation. |
498 | */ | ||
499 | if (mapping && (gfp & GFP_KERNEL) == GFP_KERNEL && | ||
500 | !(current->flags & PF_FSTRANS)) { | ||
498 | int how = FLUSH_SYNC; | 501 | int how = FLUSH_SYNC; |
499 | 502 | ||
500 | /* Don't let kswapd deadlock waiting for OOM RPC calls */ | 503 | /* Don't let kswapd deadlock waiting for OOM RPC calls */ |
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index b6bb22571c5..c57f97f44e6 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c | |||
@@ -713,7 +713,9 @@ void rpc_execute(struct rpc_task *task) | |||
713 | 713 | ||
714 | static void rpc_async_schedule(struct work_struct *work) | 714 | static void rpc_async_schedule(struct work_struct *work) |
715 | { | 715 | { |
716 | current->flags |= PF_FSTRANS; | ||
716 | __rpc_execute(container_of(work, struct rpc_task, u.tk_work)); | 717 | __rpc_execute(container_of(work, struct rpc_task, u.tk_work)); |
718 | current->flags &= ~PF_FSTRANS; | ||
717 | } | 719 | } |
718 | 720 | ||
719 | /** | 721 | /** |
diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 0867070bb5c..d0b5210d981 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c | |||
@@ -200,6 +200,7 @@ xprt_rdma_connect_worker(struct work_struct *work) | |||
200 | int rc = 0; | 200 | int rc = 0; |
201 | 201 | ||
202 | if (!xprt->shutdown) { | 202 | if (!xprt->shutdown) { |
203 | current->flags |= PF_FSTRANS; | ||
203 | xprt_clear_connected(xprt); | 204 | xprt_clear_connected(xprt); |
204 | 205 | ||
205 | dprintk("RPC: %s: %sconnect\n", __func__, | 206 | dprintk("RPC: %s: %sconnect\n", __func__, |
@@ -212,10 +213,10 @@ xprt_rdma_connect_worker(struct work_struct *work) | |||
212 | 213 | ||
213 | out: | 214 | out: |
214 | xprt_wake_pending_tasks(xprt, rc); | 215 | xprt_wake_pending_tasks(xprt, rc); |
215 | |||
216 | out_clear: | 216 | out_clear: |
217 | dprintk("RPC: %s: exit\n", __func__); | 217 | dprintk("RPC: %s: exit\n", __func__); |
218 | xprt_clear_connecting(xprt); | 218 | xprt_clear_connecting(xprt); |
219 | current->flags &= ~PF_FSTRANS; | ||
219 | } | 220 | } |
220 | 221 | ||
221 | /* | 222 | /* |
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index ea7507979b0..554111f42b0 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c | |||
@@ -1882,6 +1882,8 @@ static void xs_local_setup_socket(struct work_struct *work) | |||
1882 | if (xprt->shutdown) | 1882 | if (xprt->shutdown) |
1883 | goto out; | 1883 | goto out; |
1884 | 1884 | ||
1885 | current->flags |= PF_FSTRANS; | ||
1886 | |||
1885 | clear_bit(XPRT_CONNECTION_ABORT, &xprt->state); | 1887 | clear_bit(XPRT_CONNECTION_ABORT, &xprt->state); |
1886 | status = __sock_create(xprt->xprt_net, AF_LOCAL, | 1888 | status = __sock_create(xprt->xprt_net, AF_LOCAL, |
1887 | SOCK_STREAM, 0, &sock, 1); | 1889 | SOCK_STREAM, 0, &sock, 1); |
@@ -1915,6 +1917,7 @@ static void xs_local_setup_socket(struct work_struct *work) | |||
1915 | out: | 1917 | out: |
1916 | xprt_clear_connecting(xprt); | 1918 | xprt_clear_connecting(xprt); |
1917 | xprt_wake_pending_tasks(xprt, status); | 1919 | xprt_wake_pending_tasks(xprt, status); |
1920 | current->flags &= ~PF_FSTRANS; | ||
1918 | } | 1921 | } |
1919 | 1922 | ||
1920 | static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) | 1923 | static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) |
@@ -1957,6 +1960,8 @@ static void xs_udp_setup_socket(struct work_struct *work) | |||
1957 | if (xprt->shutdown) | 1960 | if (xprt->shutdown) |
1958 | goto out; | 1961 | goto out; |
1959 | 1962 | ||
1963 | current->flags |= PF_FSTRANS; | ||
1964 | |||
1960 | /* Start by resetting any existing state */ | 1965 | /* Start by resetting any existing state */ |
1961 | xs_reset_transport(transport); | 1966 | xs_reset_transport(transport); |
1962 | sock = xs_create_sock(xprt, transport, | 1967 | sock = xs_create_sock(xprt, transport, |
@@ -1975,6 +1980,7 @@ static void xs_udp_setup_socket(struct work_struct *work) | |||
1975 | out: | 1980 | out: |
1976 | xprt_clear_connecting(xprt); | 1981 | xprt_clear_connecting(xprt); |
1977 | xprt_wake_pending_tasks(xprt, status); | 1982 | xprt_wake_pending_tasks(xprt, status); |
1983 | current->flags &= ~PF_FSTRANS; | ||
1978 | } | 1984 | } |
1979 | 1985 | ||
1980 | /* | 1986 | /* |
@@ -2100,6 +2106,8 @@ static void xs_tcp_setup_socket(struct work_struct *work) | |||
2100 | if (xprt->shutdown) | 2106 | if (xprt->shutdown) |
2101 | goto out; | 2107 | goto out; |
2102 | 2108 | ||
2109 | current->flags |= PF_FSTRANS; | ||
2110 | |||
2103 | if (!sock) { | 2111 | if (!sock) { |
2104 | clear_bit(XPRT_CONNECTION_ABORT, &xprt->state); | 2112 | clear_bit(XPRT_CONNECTION_ABORT, &xprt->state); |
2105 | sock = xs_create_sock(xprt, transport, | 2113 | sock = xs_create_sock(xprt, transport, |
@@ -2149,6 +2157,7 @@ static void xs_tcp_setup_socket(struct work_struct *work) | |||
2149 | case -EINPROGRESS: | 2157 | case -EINPROGRESS: |
2150 | case -EALREADY: | 2158 | case -EALREADY: |
2151 | xprt_clear_connecting(xprt); | 2159 | xprt_clear_connecting(xprt); |
2160 | current->flags &= ~PF_FSTRANS; | ||
2152 | return; | 2161 | return; |
2153 | case -EINVAL: | 2162 | case -EINVAL: |
2154 | /* Happens, for instance, if the user specified a link | 2163 | /* Happens, for instance, if the user specified a link |
@@ -2161,6 +2170,7 @@ out_eagain: | |||
2161 | out: | 2170 | out: |
2162 | xprt_clear_connecting(xprt); | 2171 | xprt_clear_connecting(xprt); |
2163 | xprt_wake_pending_tasks(xprt, status); | 2172 | xprt_wake_pending_tasks(xprt, status); |
2173 | current->flags &= ~PF_FSTRANS; | ||
2164 | } | 2174 | } |
2165 | 2175 | ||
2166 | /** | 2176 | /** |