Skip to content

Commit 47446d7

Browse files
vaverinchucklever
authored andcommitted
nfsd4: add refcount for nfsd4_blocked_lock
nbl allocated in nfsd4_lock can be released by a several ways: directly in nfsd4_lock(), via nfs4_laundromat(), via another nfs command RELEASE_LOCKOWNER or via nfsd4_callback. This structure should be refcounted to be used and released correctly in all these cases. Refcount is initialized to 1 during allocation and is incremented when nbl is added into nbl_list/nbl_lru lists. Usually nbl is linked into both lists together, so only one refcount is used for both lists. However nfsd4_lock() should keep in mind that nbl can be present in one of lists only. This can happen if nbl was handled already by nfs4_laundromat/nfsd4_callback/etc. Refcount is decremented if vfs_lock_file() returns FILE_LOCK_DEFERRED, because nbl can be handled already by nfs4_laundromat/nfsd4_callback/etc. Refcount is not changed in find_blocked_lock() because of it reuses counter released after removing nbl from lists. Signed-off-by: Vasily Averin <[email protected]> Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent 40595cd commit 47446d7

File tree

2 files changed

+23
-3
lines changed

2 files changed

+23
-3
lines changed

fs/nfsd/nfs4state.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ find_blocked_lock(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
246246
list_for_each_entry(cur, &lo->lo_blocked, nbl_list) {
247247
if (fh_match(fh, &cur->nbl_fh)) {
248248
list_del_init(&cur->nbl_list);
249+
WARN_ON(list_empty(&cur->nbl_lru));
249250
list_del_init(&cur->nbl_lru);
250251
found = cur;
251252
break;
@@ -271,6 +272,7 @@ find_or_allocate_block(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
271272
INIT_LIST_HEAD(&nbl->nbl_lru);
272273
fh_copy_shallow(&nbl->nbl_fh, fh);
273274
locks_init_lock(&nbl->nbl_lock);
275+
kref_init(&nbl->nbl_kref);
274276
nfsd4_init_cb(&nbl->nbl_cb, lo->lo_owner.so_client,
275277
&nfsd4_cb_notify_lock_ops,
276278
NFSPROC4_CLNT_CB_NOTIFY_LOCK);
@@ -279,12 +281,21 @@ find_or_allocate_block(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
279281
return nbl;
280282
}
281283

284+
static void
285+
free_nbl(struct kref *kref)
286+
{
287+
struct nfsd4_blocked_lock *nbl;
288+
289+
nbl = container_of(kref, struct nfsd4_blocked_lock, nbl_kref);
290+
kfree(nbl);
291+
}
292+
282293
static void
283294
free_blocked_lock(struct nfsd4_blocked_lock *nbl)
284295
{
285296
locks_delete_block(&nbl->nbl_lock);
286297
locks_release_private(&nbl->nbl_lock);
287-
kfree(nbl);
298+
kref_put(&nbl->nbl_kref, free_nbl);
288299
}
289300

290301
static void
@@ -302,6 +313,7 @@ remove_blocked_locks(struct nfs4_lockowner *lo)
302313
struct nfsd4_blocked_lock,
303314
nbl_list);
304315
list_del_init(&nbl->nbl_list);
316+
WARN_ON(list_empty(&nbl->nbl_lru));
305317
list_move(&nbl->nbl_lru, &reaplist);
306318
}
307319
spin_unlock(&nn->blocked_locks_lock);
@@ -6987,6 +6999,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
69876999
spin_lock(&nn->blocked_locks_lock);
69887000
list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked);
69897001
list_add_tail(&nbl->nbl_lru, &nn->blocked_locks_lru);
7002+
kref_get(&nbl->nbl_kref);
69907003
spin_unlock(&nn->blocked_locks_lock);
69917004
}
69927005

@@ -6999,6 +7012,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
69997012
nn->somebody_reclaimed = true;
70007013
break;
70017014
case FILE_LOCK_DEFERRED:
7015+
kref_put(&nbl->nbl_kref, free_nbl);
70027016
nbl = NULL;
70037017
fallthrough;
70047018
case -EAGAIN: /* conflock holds conflicting lock */
@@ -7019,8 +7033,13 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
70197033
/* dequeue it if we queued it before */
70207034
if (fl_flags & FL_SLEEP) {
70217035
spin_lock(&nn->blocked_locks_lock);
7022-
list_del_init(&nbl->nbl_list);
7023-
list_del_init(&nbl->nbl_lru);
7036+
if (!list_empty(&nbl->nbl_list) &&
7037+
!list_empty(&nbl->nbl_lru)) {
7038+
list_del_init(&nbl->nbl_list);
7039+
list_del_init(&nbl->nbl_lru);
7040+
kref_put(&nbl->nbl_kref, free_nbl);
7041+
}
7042+
/* nbl can use one of lists to be linked to reaplist */
70247043
spin_unlock(&nn->blocked_locks_lock);
70257044
}
70267045
free_blocked_lock(nbl);

fs/nfsd/state.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,7 @@ struct nfsd4_blocked_lock {
633633
struct file_lock nbl_lock;
634634
struct knfsd_fh nbl_fh;
635635
struct nfsd4_callback nbl_cb;
636+
struct kref nbl_kref;
636637
};
637638

638639
struct nfsd4_compound_state;

0 commit comments

Comments
 (0)