]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
block: add IOC_PR_READ_KEYS ioctl
authorStefan Hajnoczi <stefanha@redhat.com>
Mon, 1 Dec 2025 21:43:28 +0000 (16:43 -0500)
committerJens Axboe <axboe@kernel.dk>
Thu, 4 Dec 2025 14:19:26 +0000 (07:19 -0700)
Add a Persistent Reservations ioctl to read the list of currently
registered reservation keys. This calls the pr_ops->read_keys() function
that was previously added in commit c787f1baa503 ("block: Add PR
callouts for read keys and reservation") but was only used by the
in-kernel SCSI target so far.

The IOC_PR_READ_KEYS ioctl is necessary so that userspace applications
that rely on Persistent Reservations ioctls have a way of inspecting the
current state. Cluster managers and validation tests need this
functionality.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
block/ioctl.c
include/uapi/linux/pr.h

index 2b3ab9bfc413da8eeed06dc798c628a1728f8c33..c0802ebf54a635bd2f9f6c9096d2e23d3bc5e647 100644 (file)
@@ -423,6 +423,60 @@ static int blkdev_pr_clear(struct block_device *bdev, blk_mode_t mode,
        return ops->pr_clear(bdev, c.key);
 }
 
+static int blkdev_pr_read_keys(struct block_device *bdev, blk_mode_t mode,
+               struct pr_read_keys __user *arg)
+{
+       const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
+       struct pr_keys *keys_info;
+       struct pr_read_keys read_keys;
+       u64 __user *keys_ptr;
+       size_t keys_info_len;
+       size_t keys_copy_len;
+       int ret;
+
+       if (!blkdev_pr_allowed(bdev, mode))
+               return -EPERM;
+       if (!ops || !ops->pr_read_keys)
+               return -EOPNOTSUPP;
+
+       if (copy_from_user(&read_keys, arg, sizeof(read_keys)))
+               return -EFAULT;
+
+       keys_info_len = struct_size(keys_info, keys, read_keys.num_keys);
+       if (keys_info_len == SIZE_MAX)
+               return -EINVAL;
+
+       keys_info = kzalloc(keys_info_len, GFP_KERNEL);
+       if (!keys_info)
+               return -ENOMEM;
+
+       keys_info->num_keys = read_keys.num_keys;
+
+       ret = ops->pr_read_keys(bdev, keys_info);
+       if (ret)
+               goto out;
+
+       /* Copy out individual keys */
+       keys_ptr = u64_to_user_ptr(read_keys.keys_ptr);
+       keys_copy_len = min(read_keys.num_keys, keys_info->num_keys) *
+                       sizeof(keys_info->keys[0]);
+
+       if (copy_to_user(keys_ptr, keys_info->keys, keys_copy_len)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       /* Copy out the arg struct */
+       read_keys.generation = keys_info->generation;
+       read_keys.num_keys = keys_info->num_keys;
+
+       if (copy_to_user(arg, &read_keys, sizeof(read_keys)))
+               ret = -EFAULT;
+out:
+       kfree(keys_info);
+       return ret;
+}
+
 static int blkdev_flushbuf(struct block_device *bdev, unsigned cmd,
                unsigned long arg)
 {
@@ -645,6 +699,8 @@ static int blkdev_common_ioctl(struct block_device *bdev, blk_mode_t mode,
                return blkdev_pr_preempt(bdev, mode, argp, true);
        case IOC_PR_CLEAR:
                return blkdev_pr_clear(bdev, mode, argp);
+       case IOC_PR_READ_KEYS:
+               return blkdev_pr_read_keys(bdev, mode, argp);
        default:
                return blk_get_meta_cap(bdev, cmd, argp);
        }
index d8126415966f39978e7be95ba65bf0229c71f638..fcb74eab92c80c2a4603fb01120359de64ddc52b 100644 (file)
@@ -56,6 +56,12 @@ struct pr_clear {
        __u32   __pad;
 };
 
+struct pr_read_keys {
+       __u32   generation;
+       __u32   num_keys;
+       __u64   keys_ptr;
+};
+
 #define PR_FL_IGNORE_KEY       (1 << 0)        /* ignore existing key */
 
 #define IOC_PR_REGISTER                _IOW('p', 200, struct pr_registration)
@@ -64,5 +70,6 @@ struct pr_clear {
 #define IOC_PR_PREEMPT         _IOW('p', 203, struct pr_preempt)
 #define IOC_PR_PREEMPT_ABORT   _IOW('p', 204, struct pr_preempt)
 #define IOC_PR_CLEAR           _IOW('p', 205, struct pr_clear)
+#define IOC_PR_READ_KEYS       _IOWR('p', 206, struct pr_read_keys)
 
 #endif /* _UAPI_PR_H */