]> git.ipfire.org Git - thirdparty/kernel/stable.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)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 30 May 2024 07:49:08 +0000 (09:49 +0200)
[ Upstream commit ad191e0eeebf64a60ca2d16ca01a223d2b1dd25e ]

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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
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 dfc444dad3298aeb361cc29533278496a81f85da..511d0b984f580e09d959f27017b34962987f1193 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);