Commit 22a1ffea authored by Stefan Hajnoczi's avatar Stefan Hajnoczi Committed by Jens Axboe
Browse files

block: add IOC_PR_READ_KEYS ioctl



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 c787f1ba ("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: default avatarStefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 38ec8469
Loading
Loading
Loading
Loading
+56 −0
Original line number Diff line number Diff line
@@ -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);
	}
+7 −0
Original line number Diff line number Diff line
@@ -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 */