Merge tag 'drm-misc-next-2022-07-07' of git://anongit.freedesktop.org/drm/drm-misc...
[linux-2.6-microblaze.git] / fs / locks.c
index 8c6df10..ca28e0e 100644 (file)
@@ -300,6 +300,34 @@ void locks_release_private(struct file_lock *fl)
 }
 EXPORT_SYMBOL_GPL(locks_release_private);
 
+/**
+ * locks_owner_has_blockers - Check for blocking lock requests
+ * @flctx: file lock context
+ * @owner: lock owner
+ *
+ * Return values:
+ *   %true: @owner has at least one blocker
+ *   %false: @owner has no blockers
+ */
+bool locks_owner_has_blockers(struct file_lock_context *flctx,
+               fl_owner_t owner)
+{
+       struct file_lock *fl;
+
+       spin_lock(&flctx->flc_lock);
+       list_for_each_entry(fl, &flctx->flc_posix, fl_list) {
+               if (fl->fl_owner != owner)
+                       continue;
+               if (!list_empty(&fl->fl_blocked_requests)) {
+                       spin_unlock(&flctx->flc_lock);
+                       return true;
+               }
+       }
+       spin_unlock(&flctx->flc_lock);
+       return false;
+}
+EXPORT_SYMBOL_GPL(locks_owner_has_blockers);
+
 /* Free a lock which is not in use. */
 void locks_free_lock(struct file_lock *fl)
 {
@@ -874,6 +902,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)) {
@@ -881,12 +911,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:
@@ -1060,6 +1101,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)
@@ -1078,6 +1121,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);
        /*
@@ -1089,6 +1133,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;