/* All UBLK_F_* have to be included into UBLK_F_ALL */
 #define UBLK_F_ALL (UBLK_F_SUPPORT_ZERO_COPY | UBLK_F_URING_CMD_COMP_IN_TASK)
 
+/* All UBLK_PARAM_TYPE_* should be included here */
+#define UBLK_PARAM_TYPE_ALL (UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DISCARD)
+
 struct ublk_rq_data {
        struct callback_head work;
 };
        spinlock_t              mm_lock;
        struct mm_struct        *mm;
 
+       struct ublk_params      params;
+
        struct completion       completion;
        unsigned int            nr_queues_ready;
        atomic_t                nr_aborted_queues;
        struct work_struct      stop_work;
 };
 
+/* header of ublk_params */
+struct ublk_params_header {
+       __u32   len;
+       __u32   types;
+};
+
 static dev_t ublk_chr_devt;
 static struct class *ublk_chr_class;
 
 
 static struct miscdevice ublk_misc;
 
+static void ublk_dev_param_basic_apply(struct ublk_device *ub)
+{
+       struct request_queue *q = ub->ub_disk->queue;
+       const struct ublk_param_basic *p = &ub->params.basic;
+
+       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->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->attrs & UBLK_ATTR_READ_ONLY)
+               set_disk_ro(ub->ub_disk, true);
+
+       set_capacity(ub->ub_disk, p->dev_sectors);
+}
+
+static void ublk_dev_param_discard_apply(struct ublk_device *ub)
+{
+       struct request_queue *q = ub->ub_disk->queue;
+       const struct ublk_param_discard *p = &ub->params.discard;
+
+       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 int ublk_validate_params(const struct ublk_device *ub)
+{
+       /* basic param is the only one which must be set */
+       if (ub->params.types & UBLK_PARAM_TYPE_BASIC) {
+               const struct ublk_param_basic *p = &ub->params.basic;
+
+               if (p->logical_bs_shift > PAGE_SHIFT)
+                       return -EINVAL;
+
+               if (p->logical_bs_shift > p->physical_bs_shift)
+                       return -EINVAL;
+
+               if (p->max_sectors > (ub->dev_info.rq_max_blocks <<
+                                       (ub->bs_shift - 9)))
+                       return -EINVAL;
+       } else
+               return -EINVAL;
+
+       if (ub->params.types & UBLK_PARAM_TYPE_DISCARD) {
+               const struct ublk_param_discard *p = &ub->params.discard;
+
+               /* So far, only support single segment discard */
+               if (p->max_discard_sectors && p->max_discard_segments != 1)
+                       return -EINVAL;
+
+               if (!p->discard_granularity)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ublk_apply_params(struct ublk_device *ub)
+{
+       if (!(ub->params.types & UBLK_PARAM_TYPE_BASIC))
+               return -EINVAL;
+
+       ublk_dev_param_basic_apply(ub);
+
+       if (ub->params.types & UBLK_PARAM_TYPE_DISCARD)
+               ublk_dev_param_discard_apply(ub);
+
+       return 0;
+}
+
 static inline bool ublk_can_use_task_work(const struct ublk_queue *ubq)
 {
        if (IS_BUILTIN(CONFIG_BLK_DEV_UBLK) &&
 {
        struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
        int ublksrv_pid = (int)header->data[0];
-       unsigned long dev_blocks = header->data[1];
        struct ublk_device *ub;
        struct gendisk *disk;
        int ret = -EINVAL;
                goto out_unlock;
        }
 
-       /* We may get disk size updated */
-       if (dev_blocks)
-               ub->dev_info.dev_blocks = dev_blocks;
-
        disk = blk_mq_alloc_disk(&ub->tag_set, ub);
        if (IS_ERR(disk)) {
                ret = PTR_ERR(disk);
        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;
+
+       ret = ublk_apply_params(ub);
+       if (ret)
+               goto out_put_disk;
+
        get_device(&ub->cdev_dev);
        ret = add_disk(disk);
        if (ret) {
                 * called in case of add_disk failure.
                 */
                ublk_put_device(ub);
-               put_disk(disk);
-               goto out_unlock;
+               goto out_put_disk;
        }
        set_bit(UB_STATE_USED, &ub->state);
        ub->dev_info.state = UBLK_S_DEV_LIVE;
+out_put_disk:
+       if (ret)
+               put_disk(disk);
 out_unlock:
        mutex_unlock(&ub->mutex);
        ublk_put_device(ub);
        return ret;
 }
 
+static int ublk_ctrl_get_params(struct io_uring_cmd *cmd)
+{
+       struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
+       void __user *argp = (void __user *)(unsigned long)header->addr;
+       struct ublk_params_header ph;
+       struct ublk_device *ub;
+       int ret;
+
+       if (header->len <= sizeof(ph) || !header->addr)
+               return -EINVAL;
+
+       if (copy_from_user(&ph, argp, sizeof(ph)))
+               return -EFAULT;
+
+       if (ph.len > header->len || !ph.len)
+               return -EINVAL;
+
+       if (ph.len > sizeof(struct ublk_params))
+               ph.len = sizeof(struct ublk_params);
+
+       ub = ublk_get_device_from_id(header->dev_id);
+       if (!ub)
+               return -EINVAL;
+
+       mutex_lock(&ub->mutex);
+       if (copy_to_user(argp, &ub->params, ph.len))
+               ret = -EFAULT;
+       else
+               ret = 0;
+       mutex_unlock(&ub->mutex);
+
+       ublk_put_device(ub);
+       return ret;
+}
+
+static int ublk_ctrl_set_params(struct io_uring_cmd *cmd)
+{
+       struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
+       void __user *argp = (void __user *)(unsigned long)header->addr;
+       struct ublk_params_header ph;
+       struct ublk_device *ub;
+       int ret = -EFAULT;
+
+       if (header->len <= sizeof(ph) || !header->addr)
+               return -EINVAL;
+
+       if (copy_from_user(&ph, argp, sizeof(ph)))
+               return -EFAULT;
+
+       if (ph.len > header->len || !ph.len || !ph.types)
+               return -EINVAL;
+
+       if (ph.len > sizeof(struct ublk_params))
+               ph.len = sizeof(struct ublk_params);
+
+       ub = ublk_get_device_from_id(header->dev_id);
+       if (!ub)
+               return -EINVAL;
+
+       /* 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;
+       } else if (copy_from_user(&ub->params, argp, ph.len)) {
+               ret = -EFAULT;
+       } else {
+               /* clear all we don't support yet */
+               ub->params.types &= UBLK_PARAM_TYPE_ALL;
+               ret = ublk_validate_params(ub);
+       }
+       mutex_unlock(&ub->mutex);
+       ublk_put_device(ub);
+
+       return ret;
+}
+
 static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
                unsigned int issue_flags)
 {
        case UBLK_CMD_GET_QUEUE_AFFINITY:
                ret = ublk_ctrl_get_queue_affinity(cmd);
                break;
+       case UBLK_CMD_GET_PARAMS:
+               ret = ublk_ctrl_get_params(cmd);
+               break;
+       case UBLK_CMD_SET_PARAMS:
+               ret = ublk_ctrl_set_params(cmd);
+               break;
        default:
                break;
        }
 
 #define        UBLK_CMD_DEL_DEV                0x05
 #define        UBLK_CMD_START_DEV      0x06
 #define        UBLK_CMD_STOP_DEV       0x07
+#define        UBLK_CMD_SET_PARAMS     0x08
+#define        UBLK_CMD_GET_PARAMS     0x09
 
 /*
  * IO commands, issued by ublk server, and handled by ublk driver.
        __u64   addr;
 };
 
+struct ublk_param_basic {
+#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)
+       __u32   attrs;
+       __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;
+};
+
+struct ublk_param_discard {
+       __u32   discard_alignment;
+
+       __u32   discard_granularity;
+       __u32   max_discard_sectors;
+
+       __u32   max_write_zeroes_sectors;
+       __u16   max_discard_segments;
+       __u16   reserved0;
+};
+
+struct ublk_params {
+       /*
+        * Total length of parameters, userspace has to set 'len' for both
+        * SET_PARAMS and GET_PARAMS command, and driver may update len
+        * if two sides use different version of 'ublk_params', same with
+        * 'types' fields.
+        */
+       __u32   len;
+#define UBLK_PARAM_TYPE_BASIC           (1 << 0)
+#define UBLK_PARAM_TYPE_DISCARD         (1 << 1)
+       __u32   types;                  /* types of parameter included */
+
+       struct ublk_param_basic         basic;
+       struct ublk_param_discard       discard;
+};
+
 #endif