diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 255b2de46a24..e185bdb165de 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -137,6 +137,8 @@ struct ublk_device {
 	spinlock_t		mm_lock;
 	struct mm_struct	*mm;
 
+	struct xarray		paras;
+
 	struct completion	completion;
 	unsigned int		nr_queues_ready;
 	atomic_t		nr_aborted_queues;
@@ -149,6 +151,16 @@ 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;
 
@@ -160,6 +172,231 @@ 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)
+{
+	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;
+
+	return 0;
+}
+
+static void ublk_dev_para_basic_apply(struct ublk_device *ub,
+		const struct ublk_para_header *header)
+{
+	struct request_queue *q = ub->ub_disk->queue;
+	const struct ublk_basic_para *p = (struct ublk_basic_para *)header;
+
+	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_flag_clear(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)
+		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_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) &&
@@ -1044,8 +1281,10 @@ 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);
 }
 
@@ -1156,8 +1395,11 @@ static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd)
 	}
 
 	/* We may get disk size updated */
-	if (dev_blocks)
+	if (dev_blocks) {
 		ub->dev_info.dev_blocks = dev_blocks;
+		ublk_update_dev_sectors(ub,
+				dev_blocks << (ub->bs_shift - 9));
+	}
 
 	disk = blk_mq_alloc_disk(&ub->tag_set, ub);
 	if (IS_ERR(disk)) {
@@ -1168,19 +1410,11 @@ static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd)
 	disk->fops = &ub_fops;
 	disk->private_data = ub;
 
-	blk_queue_logical_block_size(disk->queue, ub->dev_info.block_size);
-	blk_queue_physical_block_size(disk->queue, ub->dev_info.block_size);
-	blk_queue_io_min(disk->queue, ub->dev_info.block_size);
-	blk_queue_max_hw_sectors(disk->queue,
-		ub->dev_info.rq_max_blocks << (ub->bs_shift - 9));
-	disk->queue->limits.discard_granularity = PAGE_SIZE;
-	blk_queue_max_discard_sectors(disk->queue, UINT_MAX >> 9);
-	blk_queue_max_write_zeroes_sectors(disk->queue, UINT_MAX >> 9);
-
-	set_capacity(disk, ub->dev_info.dev_blocks << (ub->bs_shift - 9));
-
 	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) {
@@ -1291,6 +1525,7 @@ 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)
@@ -1334,6 +1569,9 @@ 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:
diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h
index ca33092354ab..99f81a1e9a95 100644
--- a/include/uapi/linux/ublk_cmd.h
+++ b/include/uapi/linux/ublk_cmd.h
@@ -158,4 +158,47 @@ 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;
+};
+
 #endif
