]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
fs/lock: add 2 callbacks to lock_manager_operations to resolve conflict
authorDai Ngo <dai.ngo@oracle.com>
Mon, 2 May 2022 21:19:25 +0000 (14:19 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 10 Apr 2024 14:19:04 +0000 (16:19 +0200)
[ Upstream commit 2443da2259e97688f93d64d17ab69b15f466078a ]

Add 2 new callbacks, lm_lock_expirable and lm_expire_lock, to
lock_manager_operations to allow the lock manager to take appropriate
action to resolve the lock conflict if possible.

A new field, lm_mod_owner, is also added to lock_manager_operations.
The lm_mod_owner is used by the fs/lock code to make sure the lock
manager module such as nfsd, is not freed while lock conflict is being
resolved.

lm_lock_expirable checks and returns true to indicate that the lock
conflict can be resolved else return false. This callback must be
called with the flc_lock held so it can not block.

lm_expire_lock is called to resolve the lock conflict if the returned
value from lm_lock_expirable is true. This callback is called without
the flc_lock held since it's allowed to block. Upon returning from
this callback, the lock conflict should be resolved and the caller is
expected to restart the conflict check from the beginnning of the list.

Lock manager, such as NFSv4 courteous server, uses this callback to
resolve conflict by destroying lock owner, or the NFSv4 courtesy client
(client that has expired but allowed to maintains its states) that owns
the lock.

Reviewed-by: J. Bruce Fields <bfields@fieldses.org>
Signed-off-by: Dai Ngo <dai.ngo@oracle.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Documentation/filesystems/locking.rst
fs/locks.c
include/linux/fs.h

index e2a27bb5dc4115e308d0c4cd1afb31fdcf50c5da..dabf4f7c755ed2c61f804dd849445c0a4cdc268f 100644 (file)
@@ -442,6 +442,8 @@ prototypes::
        void (*lm_break)(struct file_lock *); /* break_lease callback */
        int (*lm_change)(struct file_lock **, int);
        bool (*lm_breaker_owns_lease)(struct file_lock *);
+        bool (*lm_lock_expirable)(struct file_lock *);
+        void (*lm_expire_lock)(void);
 
 locking rules:
 
@@ -453,6 +455,8 @@ lm_grant:           no              no                      no
 lm_break:              yes             no                      no
 lm_change              yes             no                      no
 lm_breaker_owns_lease: yes             no                      no
+lm_lock_expirable      yes             no                      no
+lm_expire_lock         no              no                      yes
 ====================== =============   =================       =========
 
 buffer_head
index f8c4844ebcce4bd94596a22f7efa295f3910adb2..317c2ec17b943ec97e9ae55f48ef5879bb8b27eb 100644 (file)
@@ -982,6 +982,8 @@ posix_test_lock(struct file *filp, struct file_lock *fl)
        struct file_lock *cfl;
        struct file_lock_context *ctx;
        struct inode *inode = locks_inode(filp);
+       void *owner;
+       void (*func)(void);
 
        ctx = smp_load_acquire(&inode->i_flctx);
        if (!ctx || list_empty_careful(&ctx->flc_posix)) {
@@ -989,12 +991,23 @@ posix_test_lock(struct file *filp, struct file_lock *fl)
                return;
        }
 
+retry:
        spin_lock(&ctx->flc_lock);
        list_for_each_entry(cfl, &ctx->flc_posix, fl_list) {
-               if (posix_locks_conflict(fl, cfl)) {
-                       locks_copy_conflock(fl, cfl);
-                       goto out;
+               if (!posix_locks_conflict(fl, cfl))
+                       continue;
+               if (cfl->fl_lmops && cfl->fl_lmops->lm_lock_expirable
+                       && (*cfl->fl_lmops->lm_lock_expirable)(cfl)) {
+                       owner = cfl->fl_lmops->lm_mod_owner;
+                       func = cfl->fl_lmops->lm_expire_lock;
+                       __module_get(owner);
+                       spin_unlock(&ctx->flc_lock);
+                       (*func)();
+                       module_put(owner);
+                       goto retry;
                }
+               locks_copy_conflock(fl, cfl);
+               goto out;
        }
        fl->fl_type = F_UNLCK;
 out:
@@ -1168,6 +1181,8 @@ static int posix_lock_inode(struct inode *inode, struct file_lock *request,
        int error;
        bool added = false;
        LIST_HEAD(dispose);
+       void *owner;
+       void (*func)(void);
 
        ctx = locks_get_lock_context(inode, request->fl_type);
        if (!ctx)
@@ -1186,6 +1201,7 @@ static int posix_lock_inode(struct inode *inode, struct file_lock *request,
                new_fl2 = locks_alloc_lock();
        }
 
+retry:
        percpu_down_read(&file_rwsem);
        spin_lock(&ctx->flc_lock);
        /*
@@ -1197,6 +1213,17 @@ static int posix_lock_inode(struct inode *inode, struct file_lock *request,
                list_for_each_entry(fl, &ctx->flc_posix, fl_list) {
                        if (!posix_locks_conflict(request, fl))
                                continue;
+                       if (fl->fl_lmops && fl->fl_lmops->lm_lock_expirable
+                               && (*fl->fl_lmops->lm_lock_expirable)(fl)) {
+                               owner = fl->fl_lmops->lm_mod_owner;
+                               func = fl->fl_lmops->lm_expire_lock;
+                               __module_get(owner);
+                               spin_unlock(&ctx->flc_lock);
+                               percpu_up_read(&file_rwsem);
+                               (*func)();
+                               module_put(owner);
+                               goto retry;
+                       }
                        if (conflock)
                                locks_copy_conflock(conflock, fl);
                        error = -EAGAIN;
index 9b7ce642d4f087b41d7ee6064968ac9b25fe3880..371d67c9221c5e1b64f6a67f98910bbd998cc269 100644 (file)
@@ -1066,6 +1066,7 @@ struct file_lock_operations {
 };
 
 struct lock_manager_operations {
+       void *lm_mod_owner;
        fl_owner_t (*lm_get_owner)(fl_owner_t);
        void (*lm_put_owner)(fl_owner_t);
        void (*lm_notify)(struct file_lock *);  /* unblock callback */
@@ -1074,6 +1075,8 @@ struct lock_manager_operations {
        int (*lm_change)(struct file_lock *, int, struct list_head *);
        void (*lm_setup)(struct file_lock *, void **);
        bool (*lm_breaker_owns_lease)(struct file_lock *);
+       bool (*lm_lock_expirable)(struct file_lock *cfl);
+       void (*lm_expire_lock)(void);
 };
 
 struct lock_manager {