Re: [PATCH 1/2] ublk_drv: store device parameters

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Jul 25, 2022 at 03:06:50PM +0800, Ming Lei wrote:
> There could be more parameters than the two types(), such as segments,
> zoned, ..., also in future, feature related parameters can be added
> in this way too, and most of them are optional.

Yes.  But just having a struct that grows is much cleaner and simpler
than those indirections.  e.g something like this patch on top of this
series.  With this new fields can just be added to the end of
struct ublk_params.  Old kernels will ignore them, but due to the copy
back of the parsed structure userspace can detect that if it cares:

 drivers/block/ublk_drv.c      |  320 +++++++++++++++------------------------------------------------------------------
 include/uapi/linux/ublk_cmd.h |   67 ++++++----------
 2 files changed, 85 insertions(+), 302 deletions(-)

diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 83fd65d8a2051..f9db59af12752 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -137,7 +137,7 @@ struct ublk_device {
 	spinlock_t		mm_lock;
 	struct mm_struct	*mm;
 
-	struct xarray		paras;
+	struct ublk_params	params;
 
 	struct completion	completion;
 	unsigned int		nr_queues_ready;
@@ -151,16 +151,6 @@ struct ublk_device {
 	struct work_struct	stop_work;
 };
 
-typedef int (ublk_para_validate)(const struct ublk_device *,
-		const struct ublk_para_header *);
-typedef void (ublk_para_apply)(struct ublk_device *ub,
-		const struct ublk_para_header *);
-
-struct ublk_para_ops {
-	ublk_para_validate *validate_fn;
-	ublk_para_apply *apply_fn;
-};
-
 static dev_t ublk_chr_devt;
 static struct class *ublk_chr_class;
 
@@ -172,231 +162,69 @@ static DEFINE_MUTEX(ublk_ctl_mutex);
 
 static struct miscdevice ublk_misc;
 
-static int ublk_dev_para_basic_validate(const struct ublk_device *ub,
-		const struct ublk_para_header *header)
+static void ublk_init_default_params(struct ublk_device *ub)
 {
-	const struct ublk_basic_para *p = (struct ublk_basic_para *)header;
+	struct ublk_params *p = &ub->params;
+	struct ublksrv_ctrl_dev_info *info = &ub->dev_info;
 
-	if (p->logical_bs_shift > PAGE_SHIFT)
-		return -EINVAL;
+	p->logical_bs_shift = ilog2(info->block_size);
+	p->physical_bs_shift = ilog2(info->block_size);
+	p->io_opt_shift = ilog2(info->block_size);
+	p->io_min_shift = ilog2(info->block_size);
 
-	if (p->logical_bs_shift > p->physical_bs_shift)
-		return -EINVAL;
+	p->max_sectors = info->rq_max_blocks << (ub->bs_shift - 9);
+	p->dev_sectors = info->dev_blocks << (ub->bs_shift - 9);
 
-	return 0;
+	p->discard_granularity = PAGE_SIZE;
+	p->max_discard_sectors = UINT_MAX >> 9;
+	p->max_write_zeroes_sectors = UINT_MAX >> 9;
+	p->max_discard_segments = 1;
 }
 
-static void ublk_dev_para_basic_apply(struct ublk_device *ub,
-		const struct ublk_para_header *header)
+static int ublk_params_apply(const struct ublk_device *ub,
+		struct ublk_params *p)
 {
 	struct request_queue *q = ub->ub_disk->queue;
-	const struct ublk_basic_para *p = (struct ublk_basic_para *)header;
+
+	if (p->logical_bs_shift > PAGE_SHIFT)
+		return -EINVAL;
+	if (p->logical_bs_shift > p->physical_bs_shift)
+		return -EINVAL;
+
+	/* For now only single segment discards are supported */
+	if (p->max_discard_sectors && p->max_discard_segments != 1)
+		return -EINVAL;
 
 	blk_queue_logical_block_size(q, 1 << p->logical_bs_shift);
 	blk_queue_physical_block_size(q, 1 << p->physical_bs_shift);
 	blk_queue_io_min(q, 1 << p->io_min_shift);
 	blk_queue_io_opt(q, 1 << p->io_opt_shift);
 
-	blk_queue_write_cache(q, p->write_back_cache, p->fua);
-	if (!p->rotational)
-		blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
-	else
+	blk_queue_write_cache(q, p->attrs & UBLK_ATTR_VOLATILE_CACHE,
+			p->attrs & UBLK_ATTR_FUA);
+	if (p->attrs & UBLK_ATTR_ROTATIONAL)
 		blk_queue_flag_clear(QUEUE_FLAG_NONROT, q);
+	else
+		blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
 
 	blk_queue_max_hw_sectors(q, p->max_sectors);
 	blk_queue_chunk_sectors(q, p->chunk_sectors);
 	blk_queue_virt_boundary(q, p->virt_boundary_mask);
 
-	if (p->read_only)
+	if (p->attrs & UBLK_ATTR_READ_ONLY)
 		set_disk_ro(ub->ub_disk, true);
 
 	set_capacity(ub->ub_disk, p->dev_sectors);
-}
-
-static int ublk_dev_para_discard_validate(const struct ublk_device *ub,
-		const struct ublk_para_header *header)
-{
-	const struct ublk_discard_para *p = (struct ublk_discard_para *)header;
-
-	/* So far, only support single segment discard */
-	if (p->max_discard_sectors && p->max_discard_segments != 1)
-		return -EINVAL;
-	return 0;
-}
-
-static void ublk_dev_para_discard_apply(struct ublk_device *ub,
-		const struct ublk_para_header *header)
-{
-	struct request_queue *q = ub->ub_disk->queue;
-	const struct ublk_discard_para *p = (struct ublk_discard_para *)
-		header;
 
-	q->limits.discard_alignment = p->discard_alignment;
+	q->limits.discard_alignment = p->discard_alignment_offset;
 	q->limits.discard_granularity = p->discard_granularity;
 	blk_queue_max_discard_sectors(q, p->max_discard_sectors);
 	blk_queue_max_write_zeroes_sectors(q,
 			p->max_write_zeroes_sectors);
 	blk_queue_max_discard_segments(q, p->max_discard_segments);
-}
-
-static const unsigned int para_len[] = {
-	[UBLK_PARA_TYPE_BASIC] = sizeof(struct ublk_basic_para),
-	[UBLK_PARA_TYPE_DISCARD] = sizeof(struct ublk_discard_para),
-};
-
-static const struct ublk_para_ops para_ops[] = {
-	[UBLK_PARA_TYPE_BASIC] = {
-		.validate_fn = ublk_dev_para_basic_validate,
-		.apply_fn = ublk_dev_para_basic_apply,
-	},
-	[UBLK_PARA_TYPE_DISCARD] = {
-		.validate_fn = ublk_dev_para_discard_validate,
-		.apply_fn = ublk_dev_para_discard_apply,
-	},
-};
-
-static void ublk_update_dev_sectors(struct ublk_device *ub,
-		sector_t sectors)
-{
-	struct ublk_basic_para  *p = xa_load(&ub->paras,
-			UBLK_PARA_TYPE_BASIC);
-
-	if (p)
-		p->dev_sectors = (__u64)sectors;
-}
-
-static struct ublk_para_header *ublk_alloc_def_basic_para(
-		struct ublk_device *ub)
-{
-	struct ublk_basic_para  *p = kzalloc(sizeof(*p), GFP_KERNEL);
-	struct ublksrv_ctrl_dev_info *info = &ub->dev_info;
-
-	if (!p)
-		return NULL;
-
-	p->header.type = UBLK_PARA_TYPE_BASIC;
-	p->header.len = sizeof(*p);
-	p->logical_bs_shift = ilog2(info->block_size);
-	p->physical_bs_shift = ilog2(info->block_size);
-	p->io_opt_shift = ilog2(info->block_size);
-	p->io_min_shift = ilog2(info->block_size);
-	p->rotational = 0;
-	p->write_back_cache = 0;
-	p->fua = 0;
-	p->read_only = 0;
-
-	p->max_sectors = info->rq_max_blocks << (ub->bs_shift - 9);
-	p->chunk_sectors = 0;
-	p->virt_boundary_mask = 0;
-	p->dev_sectors = info->dev_blocks << (ub->bs_shift - 9);
-
-	return (struct ublk_para_header *)p;
-}
-
-static struct ublk_para_header *ublk_alloc_def_discard_para(
-		struct ublk_device *ub)
-{
-	struct ublk_discard_para  *p = kzalloc(sizeof(*p), GFP_KERNEL);
-
-	if (!p)
-		return NULL;
-
-	p->header.type = UBLK_PARA_TYPE_DISCARD;
-	p->header.len = sizeof(*p);
-
-	p->discard_alignment =	0;
-	p->discard_granularity = PAGE_SIZE;
-	p->max_discard_sectors = UINT_MAX >> 9;
-	p->max_write_zeroes_sectors = UINT_MAX >> 9;
-	p->max_discard_segments = 1;
-
-	return (struct ublk_para_header *)p;
-}
-
-static int ublk_validate_para_header(const struct ublk_device *ub,
-		const struct ublk_para_header *h)
-{
-	if (h->type >= UBLK_PARA_TYPE_LAST)
-		return -EINVAL;
-
-	if (h->len != para_len[h->type])
-		return -EINVAL;
-
 	return 0;
 }
 
-
-static int ublk_validate_para(const struct ublk_device *ub,
-		const struct ublk_para_header *h)
-{
-	int ret = ublk_validate_para_header(ub, h);
-
-	if (ret)
-		return ret;
-
-	if (para_ops[h->type].validate_fn)
-		return para_ops[h->type].validate_fn(ub, h);
-
-	return 0;
-}
-
-/* Old parameter with same type will be overridden */
-static int ublk_install_para(struct ublk_device *ub,
-		struct ublk_para_header *h)
-{
-	void *old;
-	int ret;
-
-	ret = ublk_validate_para(ub, h);
-	if (ret)
-		return ret;
-
-	old = xa_store(&ub->paras, h->type, h, GFP_KERNEL);
-	if (xa_is_err(old))
-		return xa_err(old);
-	kfree(old);
-	return 0;
-}
-
-static void ublk_apply_para(struct ublk_device *ub,
-		const struct ublk_para_header *h)
-{
-	if (para_ops[h->type].apply_fn)
-		para_ops[h->type].apply_fn(ub, h);
-}
-
-/* default parameters are allocated/installed before disk is allocated */
-static void ublk_install_def_paras(struct ublk_device *ub)
-{
-	struct ublk_para_header *h;
-
-	h = ublk_alloc_def_basic_para(ub);
-	if (h && ublk_install_para(ub, h))
-		kfree(h);
-
-	h = ublk_alloc_def_discard_para(ub);
-	if (h && ublk_install_para(ub, h))
-		kfree(h);
-}
-
-static void ublk_apply_paras(struct ublk_device *ub)
-{
-	struct ublk_para_header *h;
-	unsigned long type;
-
-	xa_for_each(&ub->paras, type, h)
-		ublk_apply_para(ub, h);
-}
-
-static void ublk_uninstall_paras(struct ublk_device *ub)
-{
-	unsigned long type;
-	void *p;
-
-	xa_for_each(&ub->paras, type, p)
-		kfree(p);
-}
-
 static inline bool ublk_can_use_task_work(const struct ublk_queue *ubq)
 {
 	if (IS_BUILTIN(CONFIG_BLK_DEV_UBLK) &&
@@ -1281,10 +1109,8 @@ static void ublk_cdev_rel(struct device *dev)
 
 	blk_mq_free_tag_set(&ub->tag_set);
 	ublk_deinit_queues(ub);
-	ublk_uninstall_paras(ub);
 	ublk_free_dev_number(ub);
 	mutex_destroy(&ub->mutex);
-	xa_destroy(&ub->paras);
 	kfree(ub);
 }
 
@@ -1397,8 +1223,7 @@ static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd)
 	/* We may get disk size updated */
 	if (dev_blocks) {
 		ub->dev_info.dev_blocks = dev_blocks;
-		ublk_update_dev_sectors(ub,
-				dev_blocks << (ub->bs_shift - 9));
+		ub->params.dev_sectors = dev_blocks << (ub->bs_shift - 9);
 	}
 
 	disk = blk_mq_alloc_disk(&ub->tag_set, ub);
@@ -1413,8 +1238,6 @@ static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd)
 	ub->dev_info.ublksrv_pid = ublksrv_pid;
 	ub->ub_disk = disk;
 
-	ublk_apply_paras(ub);
-
 	get_device(&ub->cdev_dev);
 	ret = add_disk(disk);
 	if (ret) {
@@ -1525,7 +1348,6 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd)
 	spin_lock_init(&ub->mm_lock);
 	INIT_WORK(&ub->stop_work, ublk_stop_work_fn);
 	INIT_DELAYED_WORK(&ub->monitor_work, ublk_daemon_monitor_work);
-	xa_init(&ub->paras);
 
 	ret = ublk_alloc_dev_number(ub, header->dev_id);
 	if (ret < 0)
@@ -1552,6 +1374,8 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd)
 			ub->dev_info.nr_hw_queues, nr_cpu_ids);
 	ublk_align_max_io_size(ub);
 
+	ublk_init_default_params(ub);
+
 	ret = ublk_init_queues(ub);
 	if (ret)
 		goto out_free_dev_number;
@@ -1569,9 +1393,6 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd)
 	 * ublk_add_chdev() will cleanup everything if it fails.
 	 */
 	ret = ublk_add_chdev(ub);
-	if (!ret)
-		ublk_install_def_paras(ub);
-
 	goto out_unlock;
 
 out_free_tag_set:
@@ -1674,84 +1495,65 @@ static int ublk_ctrl_get_dev_info(struct io_uring_cmd *cmd)
 	return ret;
 }
 
-static int ublk_ctrl_get_para(struct io_uring_cmd *cmd)
+static int ublk_ctrl_get_params(struct io_uring_cmd *cmd)
 {
 	struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
+	size_t psize = min_t(size_t, sizeof(struct ublk_params), header->len);
 	void __user *argp = (void __user *)(unsigned long)header->addr;
 	struct ublk_device *ub;
-	struct ublk_para_header ph;
-	struct ublk_para_header *para = NULL;
 	int ret = 0;
 
-	if (header->len <= sizeof(ph) || !header->addr)
+	if (!header->addr)
 		return -EINVAL;
 
 	ub = ublk_get_device_from_id(header->dev_id);
 	if (!ub)
 		return -EINVAL;
 
-	ret = -EFAULT;
-	if (copy_from_user(&ph, argp, sizeof(ph)))
-		goto out_put;
-
-	ret = ublk_validate_para_header(ub, &ph);
-	if (ret)
-		goto out_put;
-
 	mutex_lock(&ub->mutex);
-	para = xa_load(&ub->paras, ph.type);
-	mutex_unlock(&ub->mutex);
-	if (!para)
-		ret = -EINVAL;
-	else if (copy_to_user(argp, para, ph.len))
+	if (copy_to_user(argp, &ub->params, psize))
 		ret = -EFAULT;
-out_put:
+	mutex_unlock(&ub->mutex);
 	ublk_put_device(ub);
 	return ret;
 }
 
-static int ublk_ctrl_set_para(struct io_uring_cmd *cmd)
+static int ublk_ctrl_set_params(struct io_uring_cmd *cmd)
 {
 	struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
+	size_t psize = min_t(size_t, sizeof(struct ublk_params), header->len);
 	void __user *argp = (void __user *)(unsigned long)header->addr;
 	struct ublk_device *ub;
-	struct ublk_para_header ph;
-	struct ublk_para_header *para = NULL;
+	struct ublk_params params = {};
 	int ret = -EFAULT;
 
-	if (header->len <= sizeof(ph) || !header->addr)
+	if (!header->addr)
 		return -EINVAL;
 
 	ub = ublk_get_device_from_id(header->dev_id);
 	if (!ub)
 		return -EINVAL;
 
-	if (copy_from_user(&ph, argp, sizeof(ph)))
+	if (copy_from_user(&params, argp, psize))
 		goto out_put;
 
-	ret = ublk_validate_para_header(ub, &ph);
+	/* parameters can only be changed when device isn't live */
+	mutex_lock(&ub->mutex);
+	if (ub->dev_info.state == UBLK_S_DEV_LIVE) {
+		ret = -EACCES;
+		goto out_unlock;
+	}
+	ret = ublk_params_apply(ub, &params);
 	if (ret)
-		goto out_put;
+		goto out_unlock;
 
-	para = kmalloc(ph.len, GFP_KERNEL);
-	if (!para) {
-		ret = -ENOMEM;
-	} else if (copy_from_user(para, argp, ph.len)) {
+	/* copy back the paramters that were actually applied */
+	if (copy_to_user(argp, &ub->params, psize))
 		ret = -EFAULT;
-	} else {
-		/* parameters can only be changed when device isn't live */
-		mutex_lock(&ub->mutex);
-		if (ub->dev_info.state != UBLK_S_DEV_LIVE)
-			ret = ublk_install_para(ub, para);
-		else
-			ret = -EACCES;
-		mutex_unlock(&ub->mutex);
-	}
+out_unlock:
+	mutex_unlock(&ub->mutex);
 out_put:
 	ublk_put_device(ub);
-	if (ret)
-		kfree(para);
-
 	return ret;
 }
 
@@ -1790,11 +1592,11 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
 	case UBLK_CMD_GET_QUEUE_AFFINITY:
 		ret = ublk_ctrl_get_queue_affinity(cmd);
 		break;
-	case UBLK_CMD_GET_PARA:
-		ret = ublk_ctrl_get_para(cmd);
+	case UBLK_CMD_GET_PARAMS:
+		ret = ublk_ctrl_get_params(cmd);
 		break;
-	case UBLK_CMD_SET_PARA:
-		ret = ublk_ctrl_set_para(cmd);
+	case UBLK_CMD_SET_PARAMS:
+		ret = ublk_ctrl_set_params(cmd);
 		break;
 	default:
 		break;
diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h
index 0f7be73987551..ce406bbede082 100644
--- a/include/uapi/linux/ublk_cmd.h
+++ b/include/uapi/linux/ublk_cmd.h
@@ -15,8 +15,8 @@
 #define	UBLK_CMD_DEL_DEV		0x05
 #define	UBLK_CMD_START_DEV	0x06
 #define	UBLK_CMD_STOP_DEV	0x07
-#define	UBLK_CMD_SET_PARA	0x08
-#define	UBLK_CMD_GET_PARA	0x09
+#define	UBLK_CMD_SET_PARAMS	0x08
+#define	UBLK_CMD_GET_PARAMS	0x09
 
 /*
  * IO commands, issued by ublk server, and handled by ublk driver.
@@ -160,47 +160,28 @@ struct ublksrv_io_cmd {
 	__u64	addr;
 };
 
-/* ublk device parameter definition */
-enum {
-	UBLK_PARA_TYPE_BASIC,
-	UBLK_PARA_TYPE_DISCARD,
-	UBLK_PARA_TYPE_LAST,
-};
-
-struct ublk_para_header {
-	__u16 type;
-	__u16 len;
-} __attribute__ ((__packed__));
-
-struct ublk_basic_para {
-	struct ublk_para_header  header;
-	__u32   logical_bs_shift:6;
-	__u32   physical_bs_shift:6;
-	__u32	io_opt_shift:6;
-	__u32	io_min_shift:6;
-	__u32	rotational:1;
-	__u32	write_back_cache:1;
-	__u32	fua:1;
-	__u32	read_only:1;
-	__u32	unused:4;
-
-	__u32	max_sectors;
-	__u32	chunk_sectors;
-
-	__u64   dev_sectors;
-	__u64   virt_boundary_mask;
-};
-
-struct ublk_discard_para {
-	struct ublk_para_header  header;
-	__u32	discard_alignment;
-
-	__u32	discard_granularity;
-	__u32	max_discard_sectors;
-
-	__u32	max_write_zeroes_sectors;
-	__u16	max_discard_segments;
-	__u16	reserved0;
+struct ublk_params {
+	__u32		attrs;
+#define UBLK_ATTR_READ_ONLY		(1 << 0)
+#define UBLK_ATTR_ROTATIONAL		(1 << 1)
+#define UBLK_ATTR_VOLATILE_CACHE	(1 << 2)
+#define UBLK_ATTR_FUA			(1 << 3)
+	__u8		logical_bs_shift;
+	__u8		physical_bs_shift;
+	__u8		io_opt_shift;
+	__u8		io_min_shift;
+	__u32		max_sectors;
+	__u32		chunk_sectors;
+	__u64		dev_sectors;
+	__u64		virt_boundary_mask;
+
+	__u32		discard_alignment_offset;
+	__u32		discard_granularity;
+	__u32		max_discard_sectors;
+	__u32		max_write_zeroes_sectors;
+	__u16		max_discard_segments;
+	__u8		reserved0[6];
+	/* keep this 8 byte aligned */
 };
 
 #endif




[Index of Archives]     [Linux RAID]     [Linux SCSI]     [Linux ATA RAID]     [IDE]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Device Mapper]

  Powered by Linux