]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
dlm: fix user space lock decision to copy lvb
authorAlexander Aring <aahringo@redhat.com>
Thu, 28 Mar 2024 15:48:33 +0000 (11:48 -0400)
committerDavid Teigland <teigland@redhat.com>
Mon, 1 Apr 2024 18:31:12 +0000 (13:31 -0500)
This patch fixes the copy lvb decision for user space lock requests.
Checking dlm_lvb_operations is done earlier, where granted/requested
lock modes are available to use in the matrix.

The decision had been moved to the wrong location, where granted mode
and requested mode where the same, which causes the dlm_lvb_operations
matix to produce the wrong copy decision. For PW or EX requests, the
caller could get invalid lvb data.

Fixes: 61bed0baa4db ("fs: dlm: use a non-static queue for callbacks")
Signed-off-by: Alexander Aring <aahringo@redhat.com>
Signed-off-by: David Teigland <teigland@redhat.com>
fs/dlm/ast.c
fs/dlm/dlm_internal.h
fs/dlm/user.c

index 1f2f70a1b824eb3dcc6ae91e4cc1d41c2643b560..decedc4ee15f6d365d9dd54068bc2160bd8b44cf 100644 (file)
@@ -12,6 +12,7 @@
 #include <trace/events/dlm.h>
 
 #include "dlm_internal.h"
+#include "lvb_table.h"
 #include "memory.h"
 #include "lock.h"
 #include "user.h"
@@ -42,6 +43,7 @@ int dlm_enqueue_lkb_callback(struct dlm_lkb *lkb, uint32_t flags, int mode,
        struct dlm_ls *ls = lkb->lkb_resource->res_ls;
        int rv = DLM_ENQUEUE_CALLBACK_SUCCESS;
        struct dlm_callback *cb;
+       int copy_lvb = 0;
        int prev_mode;
 
        if (flags & DLM_CB_BAST) {
@@ -73,6 +75,17 @@ int dlm_enqueue_lkb_callback(struct dlm_lkb *lkb, uint32_t flags, int mode,
                                goto out;
                        }
                }
+       } else if (flags & DLM_CB_CAST) {
+               if (test_bit(DLM_DFL_USER_BIT, &lkb->lkb_dflags)) {
+                       if (lkb->lkb_last_cast)
+                               prev_mode = lkb->lkb_last_cb->mode;
+                       else
+                               prev_mode = -1;
+
+                       if (!status && lkb->lkb_lksb->sb_lvbptr &&
+                           dlm_lvb_operations[prev_mode + 1][mode + 1])
+                               copy_lvb = 1;
+               }
        }
 
        cb = dlm_allocate_cb();
@@ -85,6 +98,7 @@ int dlm_enqueue_lkb_callback(struct dlm_lkb *lkb, uint32_t flags, int mode,
        cb->mode = mode;
        cb->sb_status = status;
        cb->sb_flags = (sbflags & 0x000000FF);
+       cb->copy_lvb = copy_lvb;
        kref_init(&cb->ref);
        if (!test_and_set_bit(DLM_IFL_CB_PENDING_BIT, &lkb->lkb_iflags))
                rv = DLM_ENQUEUE_CALLBACK_NEED_SCHED;
index 3b4dbce849f0f268d8b6876f28d90a32e0d9c119..a9137c90f34831eaa9d1aea320a4286c1d450592 100644 (file)
@@ -222,6 +222,7 @@ struct dlm_callback {
        int                     sb_status;      /* copy to lksb status */
        uint8_t                 sb_flags;       /* copy to lksb flags */
        int8_t                  mode; /* rq mode of bast, gr mode of cast */
+       int                     copy_lvb;
 
        struct list_head        list;
        struct kref             ref;
index 9f9b68448830ece12fd342e79ef1f2ccf6f9f1ae..12a483deeef5ef7edfb7578074bf975e4cdd9045 100644 (file)
@@ -21,7 +21,6 @@
 #include "dlm_internal.h"
 #include "lockspace.h"
 #include "lock.h"
-#include "lvb_table.h"
 #include "user.h"
 #include "ast.h"
 #include "config.h"
@@ -806,8 +805,7 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count,
        struct dlm_lkb *lkb;
        DECLARE_WAITQUEUE(wait, current);
        struct dlm_callback *cb;
-       int rv, ret, copy_lvb = 0;
-       int old_mode, new_mode;
+       int rv, ret;
 
        if (count == sizeof(struct dlm_device_version)) {
                rv = copy_version_to_user(buf, count);
@@ -864,9 +862,6 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count,
 
        lkb = list_first_entry(&proc->asts, struct dlm_lkb, lkb_cb_list);
 
-       /* rem_lkb_callback sets a new lkb_last_cast */
-       old_mode = lkb->lkb_last_cast->mode;
-
        rv = dlm_dequeue_lkb_callback(lkb, &cb);
        switch (rv) {
        case DLM_DEQUEUE_CALLBACK_EMPTY:
@@ -895,12 +890,6 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count,
        if (cb->flags & DLM_CB_BAST) {
                trace_dlm_bast(lkb->lkb_resource->res_ls, lkb, cb->mode);
        } else if (cb->flags & DLM_CB_CAST) {
-               new_mode = cb->mode;
-
-               if (!cb->sb_status && lkb->lkb_lksb->sb_lvbptr &&
-                   dlm_lvb_operations[old_mode + 1][new_mode + 1])
-                       copy_lvb = 1;
-
                lkb->lkb_lksb->sb_status = cb->sb_status;
                lkb->lkb_lksb->sb_flags = cb->sb_flags;
                trace_dlm_ast(lkb->lkb_resource->res_ls, lkb);
@@ -908,7 +897,7 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count,
 
        ret = copy_result_to_user(lkb->lkb_ua,
                                  test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags),
-                                 cb->flags, cb->mode, copy_lvb, buf, count);
+                                 cb->flags, cb->mode, cb->copy_lvb, buf, count);
 
        kref_put(&cb->ref, dlm_release_callback);