From: Cyrill Gorcunov <gorcu...@virtuozzo.com>

It looks like plain splinlock guards are not suffice enough to prevent
reuse the same lock entries when several clients issues an operation
simultaneously and one of them are in error path which could possibly
lead to the list corruption. We've been said that this is impossible
but taking into account the backtrace we are receiving it seems such
scenario is still possible.

Thus lets do a trick - add a reference to the blocked lock entry and
when we're in potential race phase lets increment it thus if another
client will try to reuse it before we finished processing the number
of references will be more than one, and finally print a warning if
it happens.

The patch is done on top

branch branch-rh7-3.10.0-1160.41.1.vz7.183.x-ovz
commit 98c614598df6a61ba38af34c370ee44690e785f9
issue https://jira.sw.ru/browse/PSBM-133274

CC: Vasily Averin <v...@virtuozzo.com>
CC: Kirill Tkhai <ktk...@virtuozzo.com>
Signed-off-by: Cyrill Gorcunov <gorcu...@gmail.com>
---
 fs/nfsd/nfs4state.c |   38 ++++++++++++++++++++++++++++++++++++++
 fs/nfsd/state.h     |    1 +
 2 files changed, 39 insertions(+)

--- vzkernel7.orig/fs/nfsd/nfs4state.c
+++ vzkernel7/fs/nfsd/nfs4state.c
@@ -211,6 +211,33 @@ static void nfsd4_put_session(struct nfs
        spin_unlock(&nn->client_lock);
 }
 
+static void nbl_ref_init(struct nfsd4_blocked_lock *nbl)
+{
+       kref_init(&nbl->kref);
+}
+
+static void nbl_ref_release(struct kref *kref)
+{
+       /* We don't need to release anything, the caller will */
+}
+
+static void nbl_ref_put(struct nfsd4_blocked_lock *nbl)
+{
+       kref_put(&nbl->kref, nbl_ref_release);
+}
+
+static void nbl_ref_get(struct nfsd4_blocked_lock *nbl)
+{
+       kref_get(&nbl->kref);
+}
+
+static void nbl_ref_verify(struct nfsd4_blocked_lock *nbl)
+{
+       int c = kref_read(&nbl->kref);
+       if (c > 1)
+               pr_warn_once("nfsd: infly reuse of block %d\n", c);
+}
+
 static struct nfsd4_blocked_lock *
 find_blocked_lock(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
                        struct nfsd_net *nn)
@@ -220,6 +247,7 @@ find_blocked_lock(struct nfs4_lockowner
        spin_lock(&nn->blocked_locks_lock);
        list_for_each_entry(cur, &lo->lo_blocked, nbl_list) {
                if (fh_match(fh, &cur->nbl_fh)) {
+                       nbl_ref_verify(cur);
                        list_del_init(&cur->nbl_list);
                        list_del_init(&cur->nbl_lru);
                        found = cur;
@@ -244,6 +272,7 @@ find_or_allocate_block(struct nfs4_locko
                if (nbl) {
                        INIT_LIST_HEAD(&nbl->nbl_list);
                        INIT_LIST_HEAD(&nbl->nbl_lru);
+                       nbl_ref_init(nbl);
                        fh_copy_shallow(&nbl->nbl_fh, fh);
                        locks_init_lock(&nbl->nbl_lock);
                        nfsd4_init_cb(&nbl->nbl_cb, lo->lo_owner.so_client,
@@ -258,6 +287,8 @@ static void
 free_blocked_lock(struct nfsd4_blocked_lock *nbl)
 {
        locks_release_private(&nbl->nbl_lock);
+       nbl_ref_verify(nbl);
+       nbl_ref_put(nbl);
        kfree(nbl);
 }
 
@@ -277,6 +308,7 @@ remove_blocked_locks(struct nfs4_lockown
                                        nbl_list);
                list_del_init(&nbl->nbl_list);
                list_move(&nbl->nbl_lru, &reaplist);
+               nbl_ref_verify(nbl);
        }
        spin_unlock(&nn->blocked_locks_lock);
 
@@ -4766,6 +4798,7 @@ nfs4_laundromat(struct nfsd_net *nn)
                }
                list_move(&nbl->nbl_lru, &reaplist);
                list_del_init(&nbl->nbl_list);
+               nbl_ref_verify(nbl);
        }
        spin_unlock(&nn->blocked_locks_lock);
 
@@ -5557,6 +5590,7 @@ nfsd4_lm_notify(struct file_lock *fl)
        if (!list_empty(&nbl->nbl_list)) {
                list_del_init(&nbl->nbl_list);
                list_del_init(&nbl->nbl_lru);
+               nbl_ref_verify(nbl);
                queue = true;
        }
        spin_unlock(&nn->blocked_locks_lock);
@@ -5993,6 +6027,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struc
        if (fl_flags & FL_SLEEP) {
                nbl->nbl_time = jiffies;
                spin_lock(&nn->blocked_locks_lock);
+               nbl_ref_get(nbl);
                list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked);
                list_add_tail(&nbl->nbl_lru, &nn->blocked_locks_lru);
                spin_unlock(&nn->blocked_locks_lock);
@@ -6005,6 +6040,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struc
                status = 0;
                break;
        case FILE_LOCK_DEFERRED:
+               if (fl_flags & FL_SLEEP)
+                       nbl_ref_put(nbl);
                nbl = NULL;
                /* Fallthrough */
        case -EAGAIN:           /* conflock holds conflicting lock */
@@ -6025,6 +6062,7 @@ out:
                /* dequeue it if we queued it before */
                if (fl_flags & FL_SLEEP) {
                        spin_lock(&nn->blocked_locks_lock);
+                       nbl_ref_put(nbl);
                        list_del_init(&nbl->nbl_list);
                        list_del_init(&nbl->nbl_lru);
                        spin_unlock(&nn->blocked_locks_lock);
--- vzkernel7.orig/fs/nfsd/state.h
+++ vzkernel7/fs/nfsd/state.h
@@ -583,6 +583,7 @@ enum nfsd4_cb_op {
 struct nfsd4_blocked_lock {
        struct list_head        nbl_list;
        struct list_head        nbl_lru;
+       struct kref             kref;
        unsigned long           nbl_time;
        struct file_lock        nbl_lock;
        struct knfsd_fh         nbl_fh;
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to