#block-device #userspace #block #io-uring #ublk #linux-kernel #storage

bin+lib libublk

用于在用户空间构建 Linux 块设备的库

4 个版本 (2 个重大更改)

0.3.0 2024 年 3 月 5 日
0.2.2 2024 年 1 月 8 日
0.2.1 2023 年 10 月 29 日
0.1.2 2023 年 8 月 26 日

#655 in 异步

Download history 35/week @ 2024-03-11 2/week @ 2024-03-18 25/week @ 2024-04-01 7/week @ 2024-05-20 8/week @ 2024-05-27 9/week @ 2024-06-03 28/week @ 2024-06-10 12/week @ 2024-06-17 6/week @ 2024-06-24

每月 56 次下载
用于 rublk

MIT/Apache

120KB
2.5K SLoC

Libublk

license license

用于构建 Linux ublk 目标设备的 Rust 库,它与 Linux ublk 驱动^1 通信以暴露标准 Linux 块设备,同时所有目标 I/O 逻辑都在用户空间实现。

Linux 内核 6.0 开始支持通过 CONFIG_BLK_DEV_UBLK 配置选项覆盖的 ublk。

文档

ublk 文档链接

ublk 介绍

快速开始

遵循一个基于 libublk 构建的 2-队列 ublk-null 目标,运行代码后在 /dev/ublkbN 创建块设备。通过按 Ctrl+C 终止此进程后,设备将被删除。

use libublk::{ctrl::UblkCtrlBuilder, io::UblkDev, io::UblkQueue};

// async/.await IO handling
async fn handle_io_cmd(q: &UblkQueue<'_>, tag: u16) -> i32 {
    (q.get_iod(tag).nr_sectors << 9) as i32
}

// implement whole ublk IO level protocol
async fn io_task(q: &UblkQueue<'_>, tag: u16) {
    // IO buffer for exchange data with /dev/ublkbN
    let buf_bytes = q.dev.dev_info.max_io_buf_bytes as usize;
    let buf = libublk::helpers::IoBuf::<u8>::new(buf_bytes);
    let mut cmd_op = libublk::sys::UBLK_U_IO_FETCH_REQ;
    let mut res = 0;

    // Register IO buffer, so that buffer pages can be discarded
    // when queue becomes idle
    q.register_io_buf(tag, &buf);
    loop {
        // Complete previous command with result and re-submit
        // IO command for fetching new IO request from /dev/ublkbN
        res = q.submit_io_cmd(tag, cmd_op, buf.as_mut_ptr(), res).await;
        if res == libublk::sys::UBLK_IO_RES_ABORT {
            break;
        }

        // Handle this incoming IO command
        res = handle_io_cmd(&q, tag).await;
        cmd_op = libublk::sys::UBLK_U_IO_COMMIT_AND_FETCH_REQ;
    }
}

fn q_fn(qid: u16, dev: &UblkDev) {
    let q_rc = std::rc::Rc::new(UblkQueue::new(qid as u16, &dev).unwrap());
    let exe = smol::LocalExecutor::new();
    let mut f_vec = Vec::new();

    for tag in 0..dev.dev_info.queue_depth {
        let q = q_rc.clone();

        f_vec.push(exe.spawn(async move { io_task(&q, tag).await }));
    }

    // Drive smol executor, won't exit until queue is dead
    libublk::uring_async::ublk_wait_and_handle_ios(&exe, &q_rc);
    smol::block_on(async { futures::future::join_all(f_vec).await });
}

fn main() {
    // Create ublk device
    let ctrl = std::sync::Arc::new(
        UblkCtrlBuilder::default()
            .name("async_null")
            .nr_queues(2)
            .dev_flags(libublk::UblkFlags::UBLK_DEV_F_ADD_DEV)
            .build()
            .unwrap(),
    );
    // Kill ublk device by handling "Ctrl + C"
    let ctrl_sig = ctrl.clone();
    let _ = ctrlc::set_handler(move || {
        ctrl_sig.kill_dev().unwrap();
    });

    // Now start this ublk target
    ctrl.run_target(
        // target initialization
        |dev| {
            dev.set_default_params(250_u64 << 30);
            Ok(())
        },
        // queue IO logic
        |tag, dev| q_fn(tag, dev),
        // dump device after it is started
        |dev| dev.dump(),
    )
    .unwrap();

    // Usually device is deleted automatically when `ctrl` drops, but
    // here `ctrl` is leaked by the global sig handler closure actually,
    // so we have to delete it explicitly
    ctrl.del_dev().unwrap();
}
  • examples/loop.rs:使用 async/await & io_uring 的真实示例。

  • examples/ramdisk.rs:单线程 & async/.await 用于控制和 IO,此技术将在未来扩展以从单线程创建多个设备。

rublk^4 基于 libublk,并支持 null、loop、zoned & qcow2 目标。

非特权 ublk 支持

在非特权模式(UBLK_F_UNPRIVILEGED_DEV)下,ublk 设备可以在非管理员用户会话中创建。为了支持此功能

  • 安装 udev 规则
KERNEL=="ublk-control", MODE="0666", OPTIONS+="static_node=ublk-control"
ACTION=="add",KERNEL=="ublk[bc]*",RUN+="/usr/local/sbin/ublk_chown.sh %k 'add' '%M' '%m'"
ACTION=="remove",KERNEL=="ublk[bc]*",RUN+="/usr/local/sbin/ublk_chown.sh %k 'remove' '%M' '%m'"
  • 安装实用程序和脚本

utils/ublk_chown.shutils/ublk_user_id.rs 的二进制文件需要安装在 /usr/local/sbin 或其他与 udev 规则匹配的目录下。

测试

您可以使用 cargo test 运行库的测试。

性能

当运行fio t/io_uring /dev/ublkb0^2时,IOPS基本上与在由blktests创建的ublk设备上运行相同测试的结果相同miniublk^3,该测试是用纯C编写的。ublk设备为空,有2个队列,每个队列的深度为64。

示例

循环

cargo run --example loop help

cargo run --example null help

许可证

该项目可以选择Apache License,Version 2.0或MIT许可证。

贡献

欢迎各种贡献!

参考文献

依赖项

~10–22MB
~320K SLoC