]> git.ipfire.org Git - thirdparty/linux.git/blobdiff - fs/locks.c
locks: convert posix locks to file_lock_context
[thirdparty/linux.git] / fs / locks.c
index 055df53f19de7476bf6bdff8501faffd0664d937..e50bb4d9e757c91d9da2488ed30c71d034dc39e4 100644 (file)
@@ -157,9 +157,6 @@ static int target_leasetype(struct file_lock *fl)
 int leases_enable = 1;
 int lease_break_time = 45;
 
-#define for_each_lock(inode, lockp) \
-       for (lockp = &inode->i_flock; *lockp != NULL; lockp = &(*lockp)->fl_next)
-
 /*
  * The global file_lock_list is only used for displaying /proc/locks, so we
  * keep a list on each CPU, with each list protected by its own spinlock via
@@ -218,6 +215,7 @@ locks_get_lock_context(struct inode *inode)
                goto out;
 
        INIT_LIST_HEAD(&new->flc_flock);
+       INIT_LIST_HEAD(&new->flc_posix);
 
        /*
         * Assign the pointer if it's not already assigned. If it is, then
@@ -241,6 +239,7 @@ locks_free_lock_context(struct file_lock_context *ctx)
 {
        if (ctx) {
                WARN_ON_ONCE(!list_empty(&ctx->flc_flock));
+               WARN_ON_ONCE(!list_empty(&ctx->flc_posix));
                kmem_cache_free(flctx_cache, ctx);
        }
 }
@@ -809,21 +808,26 @@ void
 posix_test_lock(struct file *filp, struct file_lock *fl)
 {
        struct file_lock *cfl;
+       struct file_lock_context *ctx;
        struct inode *inode = file_inode(filp);
 
+       ctx = inode->i_flctx;
+       if (!ctx || list_empty_careful(&ctx->flc_posix)) {
+               fl->fl_type = F_UNLCK;
+               return;
+       }
+
        spin_lock(&inode->i_lock);
-       for (cfl = file_inode(filp)->i_flock; cfl; cfl = cfl->fl_next) {
-               if (!IS_POSIX(cfl))
-                       continue;
-               if (posix_locks_conflict(fl, cfl))
-                       break;
+       list_for_each_entry(cfl, &ctx->flc_posix, fl_list) {
+               if (posix_locks_conflict(fl, cfl)) {
+                       locks_copy_conflock(fl, cfl);
+                       if (cfl->fl_nspid)
+                               fl->fl_pid = pid_vnr(cfl->fl_nspid);
+                       goto out;
+               }
        }
-       if (cfl) {
-               locks_copy_conflock(fl, cfl);
-               if (cfl->fl_nspid)
-                       fl->fl_pid = pid_vnr(cfl->fl_nspid);
-       } else
-               fl->fl_type = F_UNLCK;
+       fl->fl_type = F_UNLCK;
+out:
        spin_unlock(&inode->i_lock);
        return;
 }
@@ -983,16 +987,20 @@ out:
 
 static int __posix_lock_file(struct inode *inode, struct file_lock *request, struct file_lock *conflock)
 {
-       struct file_lock *fl;
+       struct file_lock *fl, *tmp;
        struct file_lock *new_fl = NULL;
        struct file_lock *new_fl2 = NULL;
        struct file_lock *left = NULL;
        struct file_lock *right = NULL;
-       struct file_lock **before;
+       struct file_lock_context *ctx;
        int error;
        bool added = false;
        LIST_HEAD(dispose);
 
+       ctx = locks_get_lock_context(inode);
+       if (!ctx)
+               return -ENOMEM;
+
        /*
         * We may need two file_lock structures for this operation,
         * so we get them in advance to avoid races.
@@ -1013,8 +1021,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
         * blocker's list of waiters and the global blocked_hash.
         */
        if (request->fl_type != F_UNLCK) {
-               for_each_lock(inode, before) {
-                       fl = *before;
+               list_for_each_entry(fl, &ctx->flc_posix, fl_list) {
                        if (!IS_POSIX(fl))
                                continue;
                        if (!posix_locks_conflict(request, fl))
@@ -1044,29 +1051,25 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
        if (request->fl_flags & FL_ACCESS)
                goto out;
 
-       /*
-        * Find the first old lock with the same owner as the new lock.
-        */
-       
-       before = &inode->i_flock;
-
-       /* First skip locks owned by other processes.  */
-       while ((fl = *before) && (!IS_POSIX(fl) ||
-                                 !posix_same_owner(request, fl))) {
-               before = &fl->fl_next;
+       /* Find the first old lock with the same owner as the new lock */
+       list_for_each_entry(fl, &ctx->flc_posix, fl_list) {
+               if (posix_same_owner(request, fl))
+                       break;
        }
 
        /* Process locks with this owner. */
-       while ((fl = *before) && posix_same_owner(request, fl)) {
-               /* Detect adjacent or overlapping regions (if same lock type)
-                */
+       list_for_each_entry_safe_from(fl, tmp, &ctx->flc_posix, fl_list) {
+               if (!posix_same_owner(request, fl))
+                       break;
+
+               /* Detect adjacent or overlapping regions (if same lock type) */
                if (request->fl_type == fl->fl_type) {
                        /* In all comparisons of start vs end, use
                         * "start - 1" rather than "end + 1". If end
                         * is OFFSET_MAX, end + 1 will become negative.
                         */
                        if (fl->fl_end < request->fl_start - 1)
-                               goto next_lock;
+                               continue;
                        /* If the next lock in the list has entirely bigger
                         * addresses than the new one, insert the lock here.
                         */
@@ -1087,18 +1090,17 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
                        else
                                request->fl_end = fl->fl_end;
                        if (added) {
-                               locks_delete_lock(before, &dispose);
+                               locks_delete_lock_ctx(fl, &dispose);
                                continue;
                        }
                        request = fl;
                        added = true;
-               }
-               else {
+               } else {
                        /* Processing for different lock types is a bit
                         * more complex.
                         */
                        if (fl->fl_end < request->fl_start)
-                               goto next_lock;
+                               continue;
                        if (fl->fl_start > request->fl_end)
                                break;
                        if (request->fl_type == F_UNLCK)
@@ -1117,7 +1119,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
                                 * one (This may happen several times).
                                 */
                                if (added) {
-                                       locks_delete_lock(before, &dispose);
+                                       locks_delete_lock_ctx(fl, &dispose);
                                        continue;
                                }
                                /*
@@ -1133,15 +1135,11 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
                                locks_copy_lock(new_fl, request);
                                request = new_fl;
                                new_fl = NULL;
-                               locks_delete_lock(before, &dispose);
-                               locks_insert_lock(before, request);
+                               locks_insert_lock_ctx(request, &fl->fl_list);
+                               locks_delete_lock_ctx(fl, &dispose);
                                added = true;
                        }
                }
-               /* Go on to next lock.
-                */
-       next_lock:
-               before = &fl->fl_next;
        }
 
        /*
@@ -1166,7 +1164,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
                        goto out;
                }
                locks_copy_lock(new_fl, request);
-               locks_insert_lock(before, new_fl);
+               locks_insert_lock_ctx(new_fl, &fl->fl_list);
                new_fl = NULL;
        }
        if (right) {
@@ -1177,7 +1175,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
                        left = new_fl2;
                        new_fl2 = NULL;
                        locks_copy_lock(left, right);
-                       locks_insert_lock(before, left);
+                       locks_insert_lock_ctx(left, &fl->fl_list);
                }
                right->fl_start = request->fl_end + 1;
                locks_wake_up_blocks(right);
@@ -1257,22 +1255,29 @@ EXPORT_SYMBOL(posix_lock_file_wait);
  */
 int locks_mandatory_locked(struct file *file)
 {
+       int ret;
        struct inode *inode = file_inode(file);
+       struct file_lock_context *ctx;
        struct file_lock *fl;
 
+       ctx = inode->i_flctx;
+       if (!ctx || list_empty_careful(&ctx->flc_posix))
+               return 0;
+
        /*
         * Search the lock list for this inode for any POSIX locks.
         */
        spin_lock(&inode->i_lock);
-       for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
-               if (!IS_POSIX(fl))
-                       continue;
+       ret = 0;
+       list_for_each_entry(fl, &ctx->flc_posix, fl_list) {
                if (fl->fl_owner != current->files &&
-                   fl->fl_owner != file)
+                   fl->fl_owner != file) {
+                       ret = -EAGAIN;
                        break;
+               }
        }
        spin_unlock(&inode->i_lock);
-       return fl ? -EAGAIN : 0;
+       return ret;
 }
 
 /**
@@ -2389,13 +2394,14 @@ out:
 void locks_remove_posix(struct file *filp, fl_owner_t owner)
 {
        struct file_lock lock;
+       struct file_lock_context *ctx = file_inode(filp)->i_flctx;
 
        /*
         * If there are no locks held on this file, we don't need to call
         * posix_lock_file().  Another process could be setting a lock on this
         * file at the same time, but we wouldn't remove that lock anyway.
         */
-       if (!file_inode(filp)->i_flock)
+       if (!ctx || list_empty(&ctx->flc_posix))
                return;
 
        lock.fl_type = F_UNLCK;