diff options
author | Jeff Layton <jlayton@poochiereds.net> | 2014-05-01 06:28:46 -0400 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@primarydata.com> | 2014-07-12 18:36:35 -0400 |
commit | 49a4bda22e186c4d0eb07f4a36b5b1a378f9398d (patch) | |
tree | d8b0fa0a46738cb02515b88542b94f6c6b1390a7 /fs | |
parent | 8003d3c4aaa5560400818e14ce5db49cdfd79865 (diff) |
nfs4: queue free_lock_state job submission to nfsiod
We got a report of the following warning in Fedora:
BUG: sleeping function called from invalid context at mm/slub.c:969
in_atomic(): 1, irqs_disabled(): 0, pid: 533, name: bash
3 locks held by bash/533:
#0: (&sp->so_delegreturn_mutex){+.+...}, at: [<ffffffffa033da62>] nfs4_proc_lock+0x262/0x910 [nfsv4]
#1: (&nfsi->rwsem){.+.+.+}, at: [<ffffffffa033da6a>] nfs4_proc_lock+0x26a/0x910 [nfsv4]
#2: (&sb->s_type->i_lock_key#23){+.+...}, at: [<ffffffff812998dc>] flock_lock_file_wait+0x8c/0x3a0
CPU: 0 PID: 533 Comm: bash Not tainted 3.15.0-0.rc1.git1.1.fc21.x86_64 #1
Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
0000000000000000 00000000d664ff3c ffff880078b69a70 ffffffff817e82e0
0000000000000000 ffff880078b69a98 ffffffff810cf1a4 0000000000000050
0000000000000050 ffff88007cc01a00 ffff880078b69ad8 ffffffff8121449e
Call Trace:
[<ffffffff817e82e0>] dump_stack+0x4d/0x66
[<ffffffff810cf1a4>] __might_sleep+0x184/0x240
[<ffffffff8121449e>] kmem_cache_alloc_trace+0x4e/0x330
[<ffffffffa0331124>] ? nfs4_release_lockowner+0x74/0x110 [nfsv4]
[<ffffffffa0331124>] nfs4_release_lockowner+0x74/0x110 [nfsv4]
[<ffffffffa0352340>] nfs4_put_lock_state+0x90/0xb0 [nfsv4]
[<ffffffffa0352375>] nfs4_fl_release_lock+0x15/0x20 [nfsv4]
[<ffffffff81297515>] locks_free_lock+0x45/0x90
[<ffffffff8129996c>] flock_lock_file_wait+0x11c/0x3a0
[<ffffffffa033da6a>] ? nfs4_proc_lock+0x26a/0x910 [nfsv4]
[<ffffffffa033301e>] do_vfs_lock+0x1e/0x30 [nfsv4]
[<ffffffffa033da79>] nfs4_proc_lock+0x279/0x910 [nfsv4]
[<ffffffff810dbb26>] ? local_clock+0x16/0x30
[<ffffffff810f5a3f>] ? lock_release_holdtime.part.28+0xf/0x200
[<ffffffffa02f820c>] do_unlk+0x8c/0xc0 [nfs]
[<ffffffffa02f85c5>] nfs_flock+0xa5/0xf0 [nfs]
[<ffffffff8129a6f6>] locks_remove_file+0xb6/0x1e0
[<ffffffff812159d8>] ? kfree+0xd8/0x2d0
[<ffffffff8123bc63>] __fput+0xd3/0x210
[<ffffffff8123bdee>] ____fput+0xe/0x10
[<ffffffff810bfb6d>] task_work_run+0xcd/0xf0
[<ffffffff81019cd1>] do_notify_resume+0x61/0x90
[<ffffffff817fbea2>] int_signal+0x12/0x17
The problem is that NFSv4 is trying to do an allocation from
fl_release_private (in order to send a RELEASE_LOCKOWNER call). That
function can be called while holding the inode->i_lock, and it's
currently set up to do __GFP_WAIT allocations. v4.1 code has a
similar problem.
This patch adds a work_struct to the nfs4_lock_state and has the code
queue the free_lock_state operation to nfsiod.
Reported-by: Josh Stone <jistone@redhat.com>
Signed-off-by: Jeff Layton <jlayton@poochiereds.net>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/nfs4_fs.h | 13 | ||||
-rw-r--r-- | fs/nfs/nfs4state.c | 24 |
2 files changed, 25 insertions, 12 deletions
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 19f567c39670..5e2a8afc72cb 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h | |||
@@ -130,15 +130,16 @@ enum { | |||
130 | */ | 130 | */ |
131 | 131 | ||
132 | struct nfs4_lock_state { | 132 | struct nfs4_lock_state { |
133 | struct list_head ls_locks; /* Other lock stateids */ | 133 | struct list_head ls_locks; /* Other lock stateids */ |
134 | struct nfs4_state * ls_state; /* Pointer to open state */ | 134 | struct nfs4_state * ls_state; /* Pointer to open state */ |
135 | #define NFS_LOCK_INITIALIZED 0 | 135 | #define NFS_LOCK_INITIALIZED 0 |
136 | #define NFS_LOCK_LOST 1 | 136 | #define NFS_LOCK_LOST 1 |
137 | unsigned long ls_flags; | 137 | unsigned long ls_flags; |
138 | struct nfs_seqid_counter ls_seqid; | 138 | struct nfs_seqid_counter ls_seqid; |
139 | nfs4_stateid ls_stateid; | 139 | nfs4_stateid ls_stateid; |
140 | atomic_t ls_count; | 140 | atomic_t ls_count; |
141 | fl_owner_t ls_owner; | 141 | fl_owner_t ls_owner; |
142 | struct work_struct ls_release; | ||
142 | }; | 143 | }; |
143 | 144 | ||
144 | /* bits for nfs4_state->flags */ | 145 | /* bits for nfs4_state->flags */ |
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 544040835482..a770c8e469a7 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c | |||
@@ -799,6 +799,18 @@ __nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) | |||
799 | return NULL; | 799 | return NULL; |
800 | } | 800 | } |
801 | 801 | ||
802 | static void | ||
803 | free_lock_state_work(struct work_struct *work) | ||
804 | { | ||
805 | struct nfs4_lock_state *lsp = container_of(work, | ||
806 | struct nfs4_lock_state, ls_release); | ||
807 | struct nfs4_state *state = lsp->ls_state; | ||
808 | struct nfs_server *server = state->owner->so_server; | ||
809 | struct nfs_client *clp = server->nfs_client; | ||
810 | |||
811 | clp->cl_mvops->free_lock_state(server, lsp); | ||
812 | } | ||
813 | |||
802 | /* | 814 | /* |
803 | * Return a compatible lock_state. If no initialized lock_state structure | 815 | * Return a compatible lock_state. If no initialized lock_state structure |
804 | * exists, return an uninitialized one. | 816 | * exists, return an uninitialized one. |
@@ -820,6 +832,7 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f | |||
820 | if (lsp->ls_seqid.owner_id < 0) | 832 | if (lsp->ls_seqid.owner_id < 0) |
821 | goto out_free; | 833 | goto out_free; |
822 | INIT_LIST_HEAD(&lsp->ls_locks); | 834 | INIT_LIST_HEAD(&lsp->ls_locks); |
835 | INIT_WORK(&lsp->ls_release, free_lock_state_work); | ||
823 | return lsp; | 836 | return lsp; |
824 | out_free: | 837 | out_free: |
825 | kfree(lsp); | 838 | kfree(lsp); |
@@ -883,13 +896,12 @@ void nfs4_put_lock_state(struct nfs4_lock_state *lsp) | |||
883 | if (list_empty(&state->lock_states)) | 896 | if (list_empty(&state->lock_states)) |
884 | clear_bit(LK_STATE_IN_USE, &state->flags); | 897 | clear_bit(LK_STATE_IN_USE, &state->flags); |
885 | spin_unlock(&state->state_lock); | 898 | spin_unlock(&state->state_lock); |
886 | server = state->owner->so_server; | 899 | if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) |
887 | if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) { | 900 | queue_work(nfsiod_workqueue, &lsp->ls_release); |
888 | struct nfs_client *clp = server->nfs_client; | 901 | else { |
889 | 902 | server = state->owner->so_server; | |
890 | clp->cl_mvops->free_lock_state(server, lsp); | ||
891 | } else | ||
892 | nfs4_free_lock_state(server, lsp); | 903 | nfs4_free_lock_state(server, lsp); |
904 | } | ||
893 | } | 905 | } |
894 | 906 | ||
895 | static void nfs4_fl_copy_lock(struct file_lock *dst, struct file_lock *src) | 907 | static void nfs4_fl_copy_lock(struct file_lock *dst, struct file_lock *src) |