Skip to content

Commit 0aa7317

Browse files
Ming Leiaxboe
authored andcommitted
ublk_drv: add SET_PARAMS/GET_PARAMS control command
Add two commands to set/get parameters generically. One important goal of ublk is to provide generic framework for making block device by userspace flexibly. As one generic block device, there are still lots of block parameters, such as max_sectors, write_cache/fua, discard related limits, zoned parameters, ...., so this patch starts to add generic mechanism for set/get device parameters. Both generic block parameters(all kinds of queue settings) and ublk feature parameters can be covered with this way, then it becomes quite easy to extend in future. Add two parameter types are used so far: basic(covers basic queue setting and misc settings which can't be grouped easily) and discard, basic type must be set, and discard type becomes optional now This way provides mechanism to simulate any kind of generic block device from userspace easily, from both block queue setting viewpoint or ublk feature viewpoint. The style of putting all parameters together is suggested by Christoph. Suggested-by: Christoph Hellwig <[email protected]> Signed-off-by: Ming Lei <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jens Axboe <[email protected]>
1 parent 93d71ec commit 0aa7317

File tree

2 files changed

+234
-18
lines changed

2 files changed

+234
-18
lines changed

drivers/block/ublk_drv.c

Lines changed: 187 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@
4949
/* All UBLK_F_* have to be included into UBLK_F_ALL */
5050
#define UBLK_F_ALL (UBLK_F_SUPPORT_ZERO_COPY | UBLK_F_URING_CMD_COMP_IN_TASK)
5151

52+
/* All UBLK_PARAM_TYPE_* should be included here */
53+
#define UBLK_PARAM_TYPE_ALL (UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DISCARD)
54+
5255
struct ublk_rq_data {
5356
struct callback_head work;
5457
};
@@ -137,6 +140,8 @@ struct ublk_device {
137140
spinlock_t mm_lock;
138141
struct mm_struct *mm;
139142

143+
struct ublk_params params;
144+
140145
struct completion completion;
141146
unsigned int nr_queues_ready;
142147
atomic_t nr_aborted_queues;
@@ -149,6 +154,12 @@ struct ublk_device {
149154
struct work_struct stop_work;
150155
};
151156

157+
/* header of ublk_params */
158+
struct ublk_params_header {
159+
__u32 len;
160+
__u32 types;
161+
};
162+
152163
static dev_t ublk_chr_devt;
153164
static struct class *ublk_chr_class;
154165

@@ -160,6 +171,91 @@ static DEFINE_MUTEX(ublk_ctl_mutex);
160171

161172
static struct miscdevice ublk_misc;
162173

174+
static void ublk_dev_param_basic_apply(struct ublk_device *ub)
175+
{
176+
struct request_queue *q = ub->ub_disk->queue;
177+
const struct ublk_param_basic *p = &ub->params.basic;
178+
179+
blk_queue_logical_block_size(q, 1 << p->logical_bs_shift);
180+
blk_queue_physical_block_size(q, 1 << p->physical_bs_shift);
181+
blk_queue_io_min(q, 1 << p->io_min_shift);
182+
blk_queue_io_opt(q, 1 << p->io_opt_shift);
183+
184+
blk_queue_write_cache(q, p->attrs & UBLK_ATTR_VOLATILE_CACHE,
185+
p->attrs & UBLK_ATTR_FUA);
186+
if (p->attrs & UBLK_ATTR_ROTATIONAL)
187+
blk_queue_flag_clear(QUEUE_FLAG_NONROT, q);
188+
else
189+
blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
190+
191+
blk_queue_max_hw_sectors(q, p->max_sectors);
192+
blk_queue_chunk_sectors(q, p->chunk_sectors);
193+
blk_queue_virt_boundary(q, p->virt_boundary_mask);
194+
195+
if (p->attrs & UBLK_ATTR_READ_ONLY)
196+
set_disk_ro(ub->ub_disk, true);
197+
198+
set_capacity(ub->ub_disk, p->dev_sectors);
199+
}
200+
201+
static void ublk_dev_param_discard_apply(struct ublk_device *ub)
202+
{
203+
struct request_queue *q = ub->ub_disk->queue;
204+
const struct ublk_param_discard *p = &ub->params.discard;
205+
206+
q->limits.discard_alignment = p->discard_alignment;
207+
q->limits.discard_granularity = p->discard_granularity;
208+
blk_queue_max_discard_sectors(q, p->max_discard_sectors);
209+
blk_queue_max_write_zeroes_sectors(q,
210+
p->max_write_zeroes_sectors);
211+
blk_queue_max_discard_segments(q, p->max_discard_segments);
212+
}
213+
214+
static int ublk_validate_params(const struct ublk_device *ub)
215+
{
216+
/* basic param is the only one which must be set */
217+
if (ub->params.types & UBLK_PARAM_TYPE_BASIC) {
218+
const struct ublk_param_basic *p = &ub->params.basic;
219+
220+
if (p->logical_bs_shift > PAGE_SHIFT)
221+
return -EINVAL;
222+
223+
if (p->logical_bs_shift > p->physical_bs_shift)
224+
return -EINVAL;
225+
226+
if (p->max_sectors > (ub->dev_info.rq_max_blocks <<
227+
(ub->bs_shift - 9)))
228+
return -EINVAL;
229+
} else
230+
return -EINVAL;
231+
232+
if (ub->params.types & UBLK_PARAM_TYPE_DISCARD) {
233+
const struct ublk_param_discard *p = &ub->params.discard;
234+
235+
/* So far, only support single segment discard */
236+
if (p->max_discard_sectors && p->max_discard_segments != 1)
237+
return -EINVAL;
238+
239+
if (!p->discard_granularity)
240+
return -EINVAL;
241+
}
242+
243+
return 0;
244+
}
245+
246+
static int ublk_apply_params(struct ublk_device *ub)
247+
{
248+
if (!(ub->params.types & UBLK_PARAM_TYPE_BASIC))
249+
return -EINVAL;
250+
251+
ublk_dev_param_basic_apply(ub);
252+
253+
if (ub->params.types & UBLK_PARAM_TYPE_DISCARD)
254+
ublk_dev_param_discard_apply(ub);
255+
256+
return 0;
257+
}
258+
163259
static inline bool ublk_can_use_task_work(const struct ublk_queue *ubq)
164260
{
165261
if (IS_BUILTIN(CONFIG_BLK_DEV_UBLK) &&
@@ -1138,7 +1234,6 @@ static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd)
11381234
{
11391235
struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
11401236
int ublksrv_pid = (int)header->data[0];
1141-
unsigned long dev_blocks = header->data[1];
11421237
struct ublk_device *ub;
11431238
struct gendisk *disk;
11441239
int ret = -EINVAL;
@@ -1161,10 +1256,6 @@ static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd)
11611256
goto out_unlock;
11621257
}
11631258

1164-
/* We may get disk size updated */
1165-
if (dev_blocks)
1166-
ub->dev_info.dev_blocks = dev_blocks;
1167-
11681259
disk = blk_mq_alloc_disk(&ub->tag_set, ub);
11691260
if (IS_ERR(disk)) {
11701261
ret = PTR_ERR(disk);
@@ -1174,19 +1265,13 @@ static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd)
11741265
disk->fops = &ub_fops;
11751266
disk->private_data = ub;
11761267

1177-
blk_queue_logical_block_size(disk->queue, ub->dev_info.block_size);
1178-
blk_queue_physical_block_size(disk->queue, ub->dev_info.block_size);
1179-
blk_queue_io_min(disk->queue, ub->dev_info.block_size);
1180-
blk_queue_max_hw_sectors(disk->queue,
1181-
ub->dev_info.rq_max_blocks << (ub->bs_shift - 9));
1182-
disk->queue->limits.discard_granularity = PAGE_SIZE;
1183-
blk_queue_max_discard_sectors(disk->queue, UINT_MAX >> 9);
1184-
blk_queue_max_write_zeroes_sectors(disk->queue, UINT_MAX >> 9);
1185-
1186-
set_capacity(disk, ub->dev_info.dev_blocks << (ub->bs_shift - 9));
1187-
11881268
ub->dev_info.ublksrv_pid = ublksrv_pid;
11891269
ub->ub_disk = disk;
1270+
1271+
ret = ublk_apply_params(ub);
1272+
if (ret)
1273+
goto out_put_disk;
1274+
11901275
get_device(&ub->cdev_dev);
11911276
ret = add_disk(disk);
11921277
if (ret) {
@@ -1195,11 +1280,13 @@ static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd)
11951280
* called in case of add_disk failure.
11961281
*/
11971282
ublk_put_device(ub);
1198-
put_disk(disk);
1199-
goto out_unlock;
1283+
goto out_put_disk;
12001284
}
12011285
set_bit(UB_STATE_USED, &ub->state);
12021286
ub->dev_info.state = UBLK_S_DEV_LIVE;
1287+
out_put_disk:
1288+
if (ret)
1289+
put_disk(disk);
12031290
out_unlock:
12041291
mutex_unlock(&ub->mutex);
12051292
ublk_put_device(ub);
@@ -1447,6 +1534,82 @@ static int ublk_ctrl_get_dev_info(struct io_uring_cmd *cmd)
14471534
return ret;
14481535
}
14491536

1537+
static int ublk_ctrl_get_params(struct io_uring_cmd *cmd)
1538+
{
1539+
struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
1540+
void __user *argp = (void __user *)(unsigned long)header->addr;
1541+
struct ublk_params_header ph;
1542+
struct ublk_device *ub;
1543+
int ret;
1544+
1545+
if (header->len <= sizeof(ph) || !header->addr)
1546+
return -EINVAL;
1547+
1548+
if (copy_from_user(&ph, argp, sizeof(ph)))
1549+
return -EFAULT;
1550+
1551+
if (ph.len > header->len || !ph.len)
1552+
return -EINVAL;
1553+
1554+
if (ph.len > sizeof(struct ublk_params))
1555+
ph.len = sizeof(struct ublk_params);
1556+
1557+
ub = ublk_get_device_from_id(header->dev_id);
1558+
if (!ub)
1559+
return -EINVAL;
1560+
1561+
mutex_lock(&ub->mutex);
1562+
if (copy_to_user(argp, &ub->params, ph.len))
1563+
ret = -EFAULT;
1564+
else
1565+
ret = 0;
1566+
mutex_unlock(&ub->mutex);
1567+
1568+
ublk_put_device(ub);
1569+
return ret;
1570+
}
1571+
1572+
static int ublk_ctrl_set_params(struct io_uring_cmd *cmd)
1573+
{
1574+
struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
1575+
void __user *argp = (void __user *)(unsigned long)header->addr;
1576+
struct ublk_params_header ph;
1577+
struct ublk_device *ub;
1578+
int ret = -EFAULT;
1579+
1580+
if (header->len <= sizeof(ph) || !header->addr)
1581+
return -EINVAL;
1582+
1583+
if (copy_from_user(&ph, argp, sizeof(ph)))
1584+
return -EFAULT;
1585+
1586+
if (ph.len > header->len || !ph.len || !ph.types)
1587+
return -EINVAL;
1588+
1589+
if (ph.len > sizeof(struct ublk_params))
1590+
ph.len = sizeof(struct ublk_params);
1591+
1592+
ub = ublk_get_device_from_id(header->dev_id);
1593+
if (!ub)
1594+
return -EINVAL;
1595+
1596+
/* parameters can only be changed when device isn't live */
1597+
mutex_lock(&ub->mutex);
1598+
if (ub->dev_info.state == UBLK_S_DEV_LIVE) {
1599+
ret = -EACCES;
1600+
} else if (copy_from_user(&ub->params, argp, ph.len)) {
1601+
ret = -EFAULT;
1602+
} else {
1603+
/* clear all we don't support yet */
1604+
ub->params.types &= UBLK_PARAM_TYPE_ALL;
1605+
ret = ublk_validate_params(ub);
1606+
}
1607+
mutex_unlock(&ub->mutex);
1608+
ublk_put_device(ub);
1609+
1610+
return ret;
1611+
}
1612+
14501613
static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
14511614
unsigned int issue_flags)
14521615
{
@@ -1482,6 +1645,12 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
14821645
case UBLK_CMD_GET_QUEUE_AFFINITY:
14831646
ret = ublk_ctrl_get_queue_affinity(cmd);
14841647
break;
1648+
case UBLK_CMD_GET_PARAMS:
1649+
ret = ublk_ctrl_get_params(cmd);
1650+
break;
1651+
case UBLK_CMD_SET_PARAMS:
1652+
ret = ublk_ctrl_set_params(cmd);
1653+
break;
14851654
default:
14861655
break;
14871656
}

include/uapi/linux/ublk_cmd.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#define UBLK_CMD_DEL_DEV 0x05
1616
#define UBLK_CMD_START_DEV 0x06
1717
#define UBLK_CMD_STOP_DEV 0x07
18+
#define UBLK_CMD_SET_PARAMS 0x08
19+
#define UBLK_CMD_GET_PARAMS 0x09
1820

1921
/*
2022
* IO commands, issued by ublk server, and handled by ublk driver.
@@ -158,4 +160,49 @@ struct ublksrv_io_cmd {
158160
__u64 addr;
159161
};
160162

163+
struct ublk_param_basic {
164+
#define UBLK_ATTR_READ_ONLY (1 << 0)
165+
#define UBLK_ATTR_ROTATIONAL (1 << 1)
166+
#define UBLK_ATTR_VOLATILE_CACHE (1 << 2)
167+
#define UBLK_ATTR_FUA (1 << 3)
168+
__u32 attrs;
169+
__u8 logical_bs_shift;
170+
__u8 physical_bs_shift;
171+
__u8 io_opt_shift;
172+
__u8 io_min_shift;
173+
174+
__u32 max_sectors;
175+
__u32 chunk_sectors;
176+
177+
__u64 dev_sectors;
178+
__u64 virt_boundary_mask;
179+
};
180+
181+
struct ublk_param_discard {
182+
__u32 discard_alignment;
183+
184+
__u32 discard_granularity;
185+
__u32 max_discard_sectors;
186+
187+
__u32 max_write_zeroes_sectors;
188+
__u16 max_discard_segments;
189+
__u16 reserved0;
190+
};
191+
192+
struct ublk_params {
193+
/*
194+
* Total length of parameters, userspace has to set 'len' for both
195+
* SET_PARAMS and GET_PARAMS command, and driver may update len
196+
* if two sides use different version of 'ublk_params', same with
197+
* 'types' fields.
198+
*/
199+
__u32 len;
200+
#define UBLK_PARAM_TYPE_BASIC (1 << 0)
201+
#define UBLK_PARAM_TYPE_DISCARD (1 << 1)
202+
__u32 types; /* types of parameter included */
203+
204+
struct ublk_param_basic basic;
205+
struct ublk_param_discard discard;
206+
};
207+
161208
#endif

0 commit comments

Comments
 (0)