Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ublk: add segment parameter

IO split is usually bad in io_uring world, since -EAGAIN is caused and
IO handling may have to fallback to io-wq, this way does hurt performance.

ublk starts to support zero copy recently, for avoiding unnecessary IO
split, ublk driver's segment limit should be aligned with backend
device's segment limit.

Another reason is that io_buffer_register_bvec() needs to allocate bvecs,
which number is aligned with ublk request segment number, so that big
memory allocation can be avoided by setting reasonable max_segments limit.

So add segment parameter for providing ublk server chance to align
segment limit with backend, and keep it reasonable from implementation
viewpoint.

Signed-off-by: Ming Lei <ming.lei@redhat.com>
Link: https://lore.kernel.org/r/20250327095123.179113-7-ming.lei@redhat.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Ming Lei and committed by
Jens Axboe
ebf695f1 b460f328

+44 -1
+19 -1
drivers/block/ublk_drv.c
··· 74 74 #define UBLK_PARAM_TYPE_ALL \ 75 75 (UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DISCARD | \ 76 76 UBLK_PARAM_TYPE_DEVT | UBLK_PARAM_TYPE_ZONED | \ 77 - UBLK_PARAM_TYPE_DMA_ALIGN) 77 + UBLK_PARAM_TYPE_DMA_ALIGN | UBLK_PARAM_TYPE_SEGMENT) 78 78 79 79 struct ublk_rq_data { 80 80 struct kref ref; ··· 577 577 return -EINVAL; 578 578 579 579 if (!is_power_of_2(p->alignment + 1)) 580 + return -EINVAL; 581 + } 582 + 583 + if (ub->params.types & UBLK_PARAM_TYPE_SEGMENT) { 584 + const struct ublk_param_segment *p = &ub->params.seg; 585 + 586 + if (!is_power_of_2(p->seg_boundary_mask + 1)) 587 + return -EINVAL; 588 + 589 + if (p->seg_boundary_mask + 1 < UBLK_MIN_SEGMENT_SIZE) 590 + return -EINVAL; 591 + if (p->max_segment_size < UBLK_MIN_SEGMENT_SIZE) 580 592 return -EINVAL; 581 593 } 582 594 ··· 2381 2369 2382 2370 if (ub->params.types & UBLK_PARAM_TYPE_DMA_ALIGN) 2383 2371 lim.dma_alignment = ub->params.dma.alignment; 2372 + 2373 + if (ub->params.types & UBLK_PARAM_TYPE_SEGMENT) { 2374 + lim.seg_boundary_mask = ub->params.seg.seg_boundary_mask; 2375 + lim.max_segment_size = ub->params.seg.max_segment_size; 2376 + lim.max_segments = ub->params.seg.max_segments; 2377 + } 2384 2378 2385 2379 if (wait_for_completion_interruptible(&ub->completion) != 0) 2386 2380 return -EINTR;
+25
include/uapi/linux/ublk_cmd.h
··· 410 410 __u8 pad[4]; 411 411 }; 412 412 413 + #define UBLK_MIN_SEGMENT_SIZE 4096 414 + /* 415 + * If any one of the three segment parameter is set as 0, the behavior is 416 + * undefined. 417 + */ 418 + struct ublk_param_segment { 419 + /* 420 + * seg_boundary_mask + 1 needs to be power_of_2(), and the sum has 421 + * to be >= UBLK_MIN_SEGMENT_SIZE(4096) 422 + */ 423 + __u64 seg_boundary_mask; 424 + 425 + /* 426 + * max_segment_size could be override by virt_boundary_mask, so be 427 + * careful when setting both. 428 + * 429 + * max_segment_size has to be >= UBLK_MIN_SEGMENT_SIZE(4096) 430 + */ 431 + __u32 max_segment_size; 432 + __u16 max_segments; 433 + __u8 pad[2]; 434 + }; 435 + 413 436 struct ublk_params { 414 437 /* 415 438 * Total length of parameters, userspace has to set 'len' for both ··· 446 423 #define UBLK_PARAM_TYPE_DEVT (1 << 2) 447 424 #define UBLK_PARAM_TYPE_ZONED (1 << 3) 448 425 #define UBLK_PARAM_TYPE_DMA_ALIGN (1 << 4) 426 + #define UBLK_PARAM_TYPE_SEGMENT (1 << 5) 449 427 __u32 types; /* types of parameter included */ 450 428 451 429 struct ublk_param_basic basic; ··· 454 430 struct ublk_param_devt devt; 455 431 struct ublk_param_zoned zoned; 456 432 struct ublk_param_dma_align dma; 433 + struct ublk_param_segment seg; 457 434 }; 458 435 459 436 #endif