### Run loop example Source: https://github.com/ublk-org/libublk-rs/blob/main/README.md Executes the loop example with the help flag to display usage information. ```bash cargo run --example loop help ``` -------------------------------- ### Run ramdisk example Source: https://github.com/ublk-org/libublk-rs/blob/main/CLAUDE.md Execute the ramdisk example. ```bash cargo run --example ramdisk ``` -------------------------------- ### Setup Async ublk Queue Source: https://context7.com/ublk-org/libublk-rs/llms.txt Demonstrates the complete setup for an asynchronous ublk queue, including creating the queue, initializing the executor, spawning I/O tasks, and running the simplified event loop. ```rust // Complete async queue setup example fn setup_async_queue(qid: u16, dev: &libublk::io::UblkDev) { let q_rc = Rc::new(UblkQueue::new(qid, dev).unwrap()); let exe_rc = Rc::new(smol::LocalExecutor::new()); let exe = exe_rc.clone(); let mut tasks = Vec::new(); // Spawn I/O tasks for each tag for tag in 0..dev.dev_info.queue_depth as u16 { let q = q_rc.clone(); tasks.push(exe.spawn(async move { // I/O task implementation... })); } // Run event loop let q = q_rc.clone(); smol::block_on(exe_rc.run(async move { simple_event_loop(&q, &exe, tasks).await.unwrap(); })); } ``` -------------------------------- ### Run null example Source: https://github.com/ublk-org/libublk-rs/blob/main/README.md Executes the null example with the help flag to display usage information. ```bash cargo run --example null help ``` -------------------------------- ### Open ublk control device and initialize command structure Source: https://github.com/ublk-org/libublk-rs/blob/main/libublk-rs-sys/README.md Demonstrates opening the /dev/ublk-control device and initializing a ublksrv_ctrl_cmd structure for kernel interaction. ```rust use libublk_rs_sys::*; use std::os::fd::AsRawFd; // Open the ublk control device let ctrl_fd = unsafe { libc::open( b"/dev/ublk-control\0".as_ptr() as *const i8, libc::O_RDWR, ) }; if ctrl_fd < 0 { panic!("Failed to open /dev/ublk-control"); } // Create a control command structure let mut cmd = ublksrv_ctrl_cmd { dev_id: 0, queue_id: !0u16, // -1 for device-level commands len: 0, addr: 0, data: [0; 1], dev_path_len: 0, pad: 0, reserved: 0, }; // Use with your own io_uring instance for async operations... ``` -------------------------------- ### Create and Configure Ublk Control Device Source: https://context7.com/ublk-org/libublk-rs/llms.txt Demonstrates both synchronous and asynchronous patterns for initializing a ublk device with custom queue, buffer, and feature configurations. ```rust use libublk::ctrl::UblkCtrlBuilder; use libublk::{UblkFlags, UblkError}; fn create_null_device() -> Result<(), UblkError> { // Build a ublk control device with custom configuration let ctrl = UblkCtrlBuilder::default() .name("example_null") // Device name for identification .id(-1) // -1 for auto-allocation, or specific ID .nr_queues(2_u16) // Number of hardware queues .depth(128_u16) // Queue depth (max in-flight I/O commands) .io_buf_bytes(524288) // I/O buffer size per request (512KB) .ctrl_flags(libublk::sys::UBLK_F_USER_COPY as u64) // Enable user_copy mode .dev_flags(UblkFlags::UBLK_DEV_F_ADD_DEV) // Add device flag .build()?; // Device is now created - get device info println!("Created device ID: {}", ctrl.dev_info().dev_id); println!("Device path: {}", ctrl.get_bdev_path()); // Dump device information ctrl.dump(); // Clean up - delete the device ctrl.del_dev()?; Ok(()) } // Async version for non-blocking device creation async fn create_device_async() -> Result<(), UblkError> { let ctrl = UblkCtrlBuilder::default() .name("async_ramdisk") .id(-1) .nr_queues(1_u16) .depth(128_u16) .dev_flags(UblkFlags::UBLK_DEV_F_ADD_DEV) .ctrl_flags(libublk::sys::UBLK_F_USER_RECOVERY as u64) // Enable recovery support .build_async() .await?; println!("Async device created: {}", ctrl.dev_info().dev_id); ctrl.del_dev_async_await().await?; Ok(()) ``` -------------------------------- ### Implement a 2-queue ublk-null target Source: https://github.com/ublk-org/libublk-rs/blob/main/README.md Demonstrates setting up a ublk device with async IO handling using the smol executor. ```rust 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 } async fn io_task(q: &UblkQueue<'_>, tag: u16) -> Result<(), libublk::UblkError> { // 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::::new(buf_bytes); // Submit initial prep command for setup IO forward q.submit_io_prep_cmd(tag, BufDesc::Slice(buf.as_slice()), 0, Some(&buf)).await?; loop { // Handle this incoming IO command, whole IO logic let res = handle_io_cmd(&q, tag).await; // Commit result and fetch next IO request q.submit_io_commit_cmd(tag, BufDesc::Slice(buf.as_slice()), res).await?; } } fn q_fn(qid: u16, dev: &UblkDev) { let q_rc = std::rc::Rc::new(UblkQueue::new(qid as u16, &dev).unwrap()); let exe_rc = std::rc::Rc::new(smol::LocalExecutor::new()); let exe = exe_rc.clone(); 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 smol::block_on(exe_rc.run(async move { let run_ops = || while exe.try_tick() {}; let done = || f_vec.iter().all(|task| task.is_finished()); if let Err(e) = libublk::wait_and_handle_io_events(&q_rc, Some(20), run_ops, done).await { log::error!("handle_uring_events failed: {}", e); } })); } 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(); } ``` -------------------------------- ### Build libublk-rs Source: https://github.com/ublk-org/libublk-rs/blob/main/CLAUDE.md Standard command to build the library. ```bash cargo build ``` -------------------------------- ### Run a Null Target with UblkCtrl Source: https://context7.com/ublk-org/libublk-rs/llms.txt Demonstrates the full lifecycle management of a null device using UblkCtrlBuilder and run_target. Requires defining closures for device initialization, queue I/O handling, and post-start actions. ```rust use libublk::ctrl::UblkCtrlBuilder; use libublk::io::{UblkDev, UblkQueue, UblkIOCtx, BufDescList}; use libublk::{UblkFlags, UblkError, UblkIORes, BufDesc}; use std::rc::Rc; fn run_null_target() -> Result<(), UblkError> { let ctrl = UblkCtrlBuilder::default() .name("null_target") .id(-1) .nr_queues(1_u16) .depth(64_u16) .io_buf_bytes(524288) .dev_flags(UblkFlags::UBLK_DEV_F_ADD_DEV) .build()?; // Target initialization closure - called once per device let tgt_init = |dev: &mut UblkDev| { // Set device size to 250GB dev.set_default_params(250_u64 << 30); Ok(()) }; // Queue handler closure - called for each queue (runs in separate thread) let queue_handler = |qid: u16, dev: &UblkDev| { let bufs_rc = Rc::new(dev.alloc_queue_io_bufs()); let bufs = bufs_rc.clone(); // I/O handler for each incoming command let io_handler = move |q: &UblkQueue, tag: u16, _io: &UblkIOCtx| { let iod = q.get_iod(tag); let bytes = (iod.nr_sectors << 9) as i32; // For null device, just complete with success let buf_desc = BufDesc::Slice(bufs[tag as usize].as_slice()); q.complete_io_cmd_unified(tag, buf_desc, Ok(UblkIORes::Result(bytes))) .unwrap(); }; // Create queue and submit initial fetch commands let queue = UblkQueue::new(qid, dev) .unwrap() .submit_fetch_commands_unified(BufDescList::Slices(Some(&bufs_rc))) .unwrap(); // Wait and handle I/O commands until device is stopped queue.wait_and_handle_io(io_handler); }; // Post-start work closure - called after device is started let on_start = |ctrl: &libublk::ctrl::UblkCtrl| { ctrl.dump(); println!("Device started successfully!"); // For demo, kill the device immediately ctrl.kill_dev().unwrap(); }; // Run the target - blocks until device is stopped ctrl.run_target(tgt_init, queue_handler, on_start)?; Ok(()) } ``` -------------------------------- ### Manage ublk devices with UblkCtrl Source: https://context7.com/ublk-org/libublk-rs/llms.txt Demonstrates listing devices, retrieving device information, and performing synchronous deletion. Requires the libublk crate. ```rust use libublk::ctrl::UblkCtrl; use libublk::UblkError; fn device_management() -> Result<(), UblkError> { // List all ublk devices println!("Listing all ublk devices:"); UblkCtrl::for_each_dev_id(|dev_id| { if let Ok(ctrl) = UblkCtrl::new_simple(dev_id as i32) { ctrl.dump(); } }); // Get info for a specific device let dev_id = 0; let ctrl = UblkCtrl::new_simple(dev_id)?; // Read device info from driver println!("\nDevice {} info:", dev_id); println!(" Device path: {}", ctrl.get_bdev_path()); println!(" Char device: {}", ctrl.get_cdev_path()); println!(" Queues: {}", ctrl.dev_info().nr_hw_queues); println!(" Queue depth: {}", ctrl.dev_info().queue_depth); // Get target information from exported JSON if let Ok(target) = ctrl.get_target_from_json() { println!(" Target size: {} bytes", target.dev_size); } if let Ok(tgt_type) = ctrl.get_target_type_from_json() { println!(" Target type: {}", tgt_type); } // Delete a device synchronously ctrl.del_dev()?; println!("Device {} deleted", dev_id); // Or delete asynchronously (non-blocking) // ctrl.del_dev_async()?; Ok(()) } ``` -------------------------------- ### Implement Synchronous I/O Queue Handler in Rust Source: https://context7.com/ublk-org/libublk-rs/llms.txt Demonstrates setting up a synchronous I/O handler with buffer allocation and command processing for read, write, and flush operations. ```rust use libublk::io::{UblkQueue, UblkDev, UblkIOCtx, BufDescList}; use libublk::{UblkError, UblkIORes, BufDesc}; use std::rc::Rc; // Synchronous queue handler with buffer management fn sync_queue_handler(qid: u16, dev: &UblkDev) { // Allocate I/O buffers for all tags in the queue let bufs_rc = Rc::new(dev.alloc_queue_io_bufs()); let bufs = bufs_rc.clone(); // Define I/O handler closure let io_handler = move |q: &UblkQueue, tag: u16, io: &UblkIOCtx| { let iod = q.get_iod(tag); let op = iod.op_flags & 0xff; let bytes = (iod.nr_sectors << 9) as i32; // Handle different operation types match op { libublk::sys::UBLK_IO_OP_READ => { // Read operation - data would be filled into buffer let buf_slice = bufs[tag as usize].as_slice(); q.complete_io_cmd_unified( tag, BufDesc::Slice(buf_slice), Ok(UblkIORes::Result(bytes)) ).unwrap(); } libublk::sys::UBLK_IO_OP_WRITE => { // Write operation - data is in buffer let buf_slice = bufs[tag as usize].as_slice(); q.complete_io_cmd_unified( tag, BufDesc::Slice(buf_slice), Ok(UblkIORes::Result(bytes)) ).unwrap(); } libublk::sys::UBLK_IO_OP_FLUSH => { // Flush operation - no data transfer q.complete_io_cmd_unified( tag, BufDesc::Slice(&[]), Ok(UblkIORes::Result(0)) ).unwrap(); } _ => { // Unknown operation - return error q.complete_io_cmd_unified( tag, BufDesc::Slice(&[]), Ok(UblkIORes::Result(-libc::EINVAL)) ).unwrap(); } } }; // Create queue and submit initial fetch commands let queue = match UblkQueue::new(qid, dev) .unwrap() .submit_fetch_commands_unified(BufDescList::Slices(Some(&bufs_rc))) { Ok(q) => q, Err(e) => { log::error!("Failed to submit fetch commands: {}", e); return; } }; // Main I/O loop - waits for and handles I/O commands queue.wait_and_handle_io(io_handler); } ``` -------------------------------- ### Build libublk-rs with fat_complete feature Source: https://github.com/ublk-org/libublk-rs/blob/main/CLAUDE.md Build the library with the fat completion feature enabled. ```bash cargo build --features=fat_complete ``` -------------------------------- ### Run libublk-rs tests Source: https://github.com/ublk-org/libublk-rs/blob/main/CLAUDE.md Command to execute the test suite for the library. ```bash cargo test ``` -------------------------------- ### Implement Zero-Copy I/O with Auto Buffer Registration Source: https://context7.com/ublk-org/libublk-rs/llms.txt Configures a UblkCtrl with UBLK_F_AUTO_BUF_REG and demonstrates handling I/O using auto-registered buffer descriptors. ```rust use libublk::ctrl::UblkCtrlBuilder; use libublk::io::{UblkDev, UblkQueue, UblkIOCtx, BufDescList}; use libublk::{UblkFlags, UblkError, UblkIORes, BufDesc}; fn zero_copy_target() -> Result<(), UblkError> { // Enable AUTO_BUF_REG for zero-copy support let ctrl_flags = libublk::sys::UBLK_F_AUTO_BUF_REG as u64; let ctrl = UblkCtrlBuilder::default() .name("zero_copy_null") .id(-1) .nr_queues(1_u16) .depth(128_u16) .ctrl_flags(ctrl_flags) .dev_flags(UblkFlags::UBLK_DEV_F_ADD_DEV) .build()?; let tgt_init = |dev: &mut UblkDev| { dev.set_default_params(250_u64 << 30); Ok(()) }; let queue_handler = |qid: u16, dev: &UblkDev| { // Create auto buffer registration list for all tags let auto_buf_reg_list: Vec = (0..dev.dev_info.queue_depth) .map(|tag| libublk::sys::ublk_auto_buf_reg { index: tag, flags: libublk::sys::UBLK_AUTO_BUF_REG_FALLBACK as u8, ..Default::default() }) .collect(); let auto_buf_rc = std::rc::Rc::new(auto_buf_reg_list); let auto_buf = auto_buf_rc.clone(); // Zero-copy I/O handler let io_handler = move |q: &UblkQueue, tag: u16, _io: &UblkIOCtx| { let iod = q.get_iod(tag); let bytes = (iod.nr_sectors << 9) as i32; // Use auto-registered buffer descriptor for zero-copy let buf_desc = BufDesc::AutoReg(auto_buf[tag as usize]); q.complete_io_cmd_unified(tag, buf_desc, Ok(UblkIORes::Result(bytes))) .unwrap(); }; // Submit fetch commands with auto buffer registration let queue = UblkQueue::new(qid, dev) .unwrap() .submit_fetch_commands_unified(BufDescList::AutoRegs(&auto_buf_rc)) .unwrap(); queue.wait_and_handle_io(io_handler); }; let on_start = |ctrl: &libublk::ctrl::UblkCtrl| { ctrl.dump(); ctrl.kill_dev().unwrap(); }; ctrl.run_target(tgt_init, queue_handler, on_start)?; Ok(()) } // Check if queue supports zero-copy auto buffer registration fn check_zero_copy_support(q: &UblkQueue) -> bool { q.support_auto_buf_zc() } ``` -------------------------------- ### Async I/O Task with submit_io_prep_cmd and submit_io_commit_cmd Source: https://context7.com/ublk-org/libublk-rs/llms.txt Handles individual asynchronous I/O operations for a given tag. It prepares a buffer, submits it for I/O, and commits the result. Use this for managing single I/O requests within an async executor. ```rust use libublk::io::{UblkQueue, UblkDev}; use libublk::helpers::IoBuf; use libublk::{UblkError, BufDesc}; use std::rc::Rc; // Async I/O task for a single tag async fn io_task(q: &UblkQueue<'_>, tag: u16, backing_storage: &mut [u8]) -> Result<(), UblkError> { let buf_size = q.dev.dev_info.max_io_buf_bytes as usize; // Allocate aligned I/O buffer let mut buffer = IoBuf::::new(buf_size); // Submit initial prep command - registers buffer and waits for first I/O q.submit_io_prep_cmd(tag, BufDesc::Slice(buffer.as_slice()), 0, Some(&buffer)) .await?; loop { let iod = q.get_iod(tag); let off = (iod.start_sector << 9) as usize; let bytes = (iod.nr_sectors << 9) as usize; let op = iod.op_flags & 0xff; // Handle I/O operation using safe slice operations let result = match op { libublk::sys::UBLK_IO_OP_READ => { // Copy from backing storage to I/O buffer let src = &backing_storage[off..off + bytes]; let dst = &mut buffer.as_mut_slice()[..bytes]; dst.copy_from_slice(src); bytes as i32 } libublk::sys::UBLK_IO_OP_WRITE => { // Copy from I/O buffer to backing storage let src = &buffer.as_slice()[..bytes]; let dst = &mut backing_storage[off..off + bytes]; dst.copy_from_slice(src); bytes as i32 } libublk::sys::UBLK_IO_OP_FLUSH => 0, _ => -libc::EINVAL, }; // Submit commit command with result - waits for next I/O // Returns Err(QueueIsDown) when device is stopped q.submit_io_commit_cmd(tag, BufDesc::Slice(buffer.as_slice()), result) .await?; } } ``` -------------------------------- ### Configure udev rules for unprivileged ublk Source: https://github.com/ublk-org/libublk-rs/blob/main/README.md Rules required to allow non-admin users to create ublk devices. ```text 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'" ``` -------------------------------- ### Check executable capabilities Source: https://github.com/ublk-org/libublk-rs/blob/main/CLAUDE.md Verify the current capabilities of an executable. ```bash getcap /path/to/your/ublk_executable ``` -------------------------------- ### Run executable with elevated privileges Source: https://github.com/ublk-org/libublk-rs/blob/main/CLAUDE.md Run the ublk executable with root privileges, which can satisfy capability requirements like CAP_IPC_LOCK. ```bash sudo ./your_ublk_executable ``` -------------------------------- ### Simplified Async Event Loop with wait_and_handle_io_events Source: https://context7.com/ublk-org/libublk-rs/llms.txt Uses the `libublk::wait_and_handle_io_events` helper for a simpler event loop implementation. It requires executor ticking and a completion check function, and supports an optional idle timeout. ```rust use libublk::io::UblkQueue; // Using the built-in wait_and_handle_io_events helper async fn simple_event_loop( q: &UblkQueue<'_>, exe: &smol::LocalExecutor<'_>, tasks: Vec>, ) -> Result<(), UblkError> { let run_ops = || while exe.try_tick() {}; let is_done = || tasks.iter().all(|task| task.is_finished()); // 20 second idle timeout for queue idle management libublk::wait_and_handle_io_events(q, Some(20), run_ops, is_done).await } ``` -------------------------------- ### Perform graceful device shutdown Source: https://context7.com/ublk-org/libublk-rs/llms.txt Uses kill_dev to safely signal a device stop, which is preferred for target code to avoid potential deadlocks. ```rust // Graceful device shutdown fn graceful_shutdown(dev_id: i32) -> Result<(), UblkError> { let ctrl = UblkCtrl::new_simple(dev_id)?; // kill_dev is preferred for target code - safe and avoids deadlock // It signals stop but may not complete deletion immediately ctrl.kill_dev()?; // For complete removal, use del_dev after kill_dev // ctrl.del_dev()?; Ok(()) } ``` -------------------------------- ### Run Async Queue with Smol Executor Source: https://context7.com/ublk-org/libublk-rs/llms.txt Manages and runs multiple asynchronous I/O tasks concurrently using the smol executor. This function spawns an `io_task` for each queue depth and handles I/O events until the queue is stopped. ```rust // Run async queue with smol executor fn async_queue_handler(qid: u16, dev: &UblkDev, storage: &'static mut [u8]) { let q_rc = Rc::new(UblkQueue::new(qid, dev).unwrap()); let exe_rc = Rc::new(smol::LocalExecutor::new()); let exe = exe_rc.clone(); let mut tasks = Vec::new(); // Spawn async task for each tag for tag in 0..dev.dev_info.queue_depth as u16 { let q = q_rc.clone(); tasks.push(exe.spawn(async move { match io_task(&q, tag, storage).await { Err(UblkError::QueueIsDown) | Ok(_) => {} // Ignore QueueIsDown, handle other errors Err(e) => log::error!("io_task failed for tag {}: {}", tag, e), } })); } // Run executor with I/O event handling let q = q_rc.clone(); smol::block_on(exe_rc.run(async move { let run_ops = || while exe.try_tick() {}; let is_done = || tasks.iter().all(|task| task.is_finished()); libublk::wait_and_handle_io_events(&q, Some(20), run_ops, is_done) .await .unwrap(); })); } ``` -------------------------------- ### Custom Async Event Loop with run_uring_tasks Source: https://context7.com/ublk-org/libublk-rs/llms.txt Implements a custom event loop using `run_uring_tasks` by defining specific polling, event reaping, executor ticking, and completion check functions. Requires `libublk` and `smol` runtime. ```rust use libublk::uring_async::{run_uring_tasks, ublk_wake_task, ublk_reap_events_with_handler}; use libublk::io::{UblkQueue, with_queue_ring_mut}; use libublk::UblkError; use std::rc::Rc; async fn custom_event_loop( q: &UblkQueue<'_>, exe: &smol::LocalExecutor<'_>, tasks: Vec>, ) -> Result<(), UblkError> { // Define polling function for io_uring let poll_uring = || async { // Submit pending operations and wait with_queue_ring_mut(q, |ring| ring.submit_and_wait(0))?; Ok(false) // No timeout }; // Define event reaping function let reap_events = |_poll_timeout| { with_queue_ring_mut(q, |ring| { ublk_reap_events_with_handler(ring, |cqe| { // Wake the async task waiting for this completion ublk_wake_task(cqe.user_data(), cqe); }) }) }; // Define executor tick function let run_ops = || { while exe.try_tick() {} }; // Define completion check function let is_done = || tasks.iter().all(|task| task.is_finished()); // Run the event loop run_uring_tasks(poll_uring, reap_events, run_ops, is_done).await } ``` -------------------------------- ### Submit Asynchronous io_uring Operations Source: https://context7.com/ublk-org/libublk-rs/llms.txt Utilize ublk_submit_sqe_async to perform non-blocking I/O operations like read, write, and sync within the ublk command loop. ```rust use libublk::uring_async::ublk_submit_sqe_async; use libublk::{UblkError, UblkUringData}; use io_uring::{opcode, types}; // Submit a read operation to backing storage asynchronously async fn read_from_backing_file( fd: i32, buf: &mut [u8], offset: u64, ) -> Result { let sqe = opcode::Read::new(types::Fixed(fd as u32), buf.as_mut_ptr(), buf.len() as u32) .offset(offset) .build() .flags(io_uring::squeue::Flags::FIXED_FILE); // Mark as target I/O using UblkUringData::Target let result = ublk_submit_sqe_async(sqe, UblkUringData::Target as u64).await?; Ok(result) } // Submit a write operation asynchronously async fn write_to_backing_file( fd: i32, buf: &[u8], offset: u64, ) -> Result { let sqe = opcode::Write::new(types::Fixed(fd as u32), buf.as_ptr(), buf.len() as u32) .offset(offset) .build() .flags(io_uring::squeue::Flags::FIXED_FILE); let result = ublk_submit_sqe_async(sqe, UblkUringData::Target as u64).await?; Ok(result) } // Submit a flush/sync operation async fn sync_backing_file(fd: i32, offset: u64, len: u32) -> Result { let sqe = opcode::SyncFileRange::new(types::Fixed(fd as u32), len) .offset(offset) .build() .flags(io_uring::squeue::Flags::FIXED_FILE); let result = ublk_submit_sqe_async(sqe, UblkUringData::Target as u64).await?; Ok(result) } // Complete loop device I/O handler using async operations async fn loop_io_handler( q: &libublk::io::UblkQueue<'_>, tag: u16, io_buf: &mut [u8], ) -> i32 { let iod = q.get_iod(tag); let op = iod.op_flags & 0xff; let offset = (iod.start_sector << 9) as u64; let bytes = (iod.nr_sectors << 9) as u32; // Retry loop for handling EAGAIN for _ in 0..4 { let result = match op { libublk::sys::UBLK_IO_OP_READ => { read_from_backing_file(1, &mut io_buf[..bytes as usize], offset).await } libublk::sys::UBLK_IO_OP_WRITE => { write_to_backing_file(1, &io_buf[..bytes as usize], offset).await } libublk::sys::UBLK_IO_OP_FLUSH => { sync_backing_file(1, offset, bytes).await } _ => return -libc::EINVAL, }; match result { Ok(res) if res != -libc::EAGAIN => return res, Err(_) => return -libc::EIO, _ => continue, // Retry on EAGAIN } } -libc::EAGAIN } ``` -------------------------------- ### Manage Aligned I/O Buffers with IoBuf Source: https://context7.com/ublk-org/libublk-rs/llms.txt Use IoBuf for page-aligned memory allocation, safe slice access, and memory locking. Buffers are automatically deallocated when they go out of scope. ```rust use libublk::helpers::IoBuf; fn buffer_operations() { // Create a 512KB aligned buffer let mut buf: IoBuf = IoBuf::new(512 * 1024); // Zero-initialize the buffer buf.zero_buf(); // Safe slice access for reading let slice: &[u8] = buf.as_slice(); println!("Buffer length: {} bytes", slice.len()); // Safe mutable slice access for writing let mut_slice: &mut [u8] = buf.as_mut_slice(); mut_slice[0..4].copy_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]); // Get subslice with bounds checking let header = buf.subslice(0..512); println!("Header bytes: {:?}", &header[0..4]); // Mutable subslice access let data_section = buf.subslice_mut(512..1024); data_section.fill(0xFF); // Lock buffer in physical memory (prevents swapping) if buf.mlock() { println!("Buffer locked in memory"); assert!(buf.is_mlocked()); // Perform critical I/O operations... // Unlock when done buf.munlock(); } // Raw pointer access (when needed for FFI) let ptr: *const u8 = buf.as_ptr(); let mut_ptr: *mut u8 = buf.as_mut_ptr(); // Buffer is automatically freed when dropped } // Use with ublk device for I/O buffer allocation fn allocate_queue_buffers(dev: &libublk::io::UblkDev) { // Allocate I/O buffers for all tags in a queue let bufs: Vec> = dev.alloc_queue_io_bufs(); println!("Allocated {} buffers", bufs.len()); for (tag, buf) in bufs.iter().enumerate() { println!("Tag {}: {} bytes at {:?}", tag, buf.len(), buf.as_ptr()); } } ``` -------------------------------- ### Recover ublk device after crash Source: https://context7.com/ublk-org/libublk-rs/llms.txt Initiates a recovery process for a device that was created with the UBLK_F_USER_RECOVERY flag. This signals the kernel that the user-space process is resuming control. ```rust // Device recovery after daemon crash fn recover_device(dev_id: i32) -> Result<(), UblkError> { // Device must have been created with UBLK_F_USER_RECOVERY flag let ctrl = UblkCtrl::new_simple(dev_id)?; // Start recovery process - this signals the kernel we're taking over ctrl.start_user_recover()?; // Get device size from exported JSON let size = if let Ok(tgt) = ctrl.get_target_from_json() { tgt.dev_size } else { return Err(UblkError::OtherError(-libc::EINVAL)); }; println!("Recovering device {} with size {} bytes", dev_id, size); // Now reinitialize queues and start handling I/O again... // (Similar to normal device setup, but with UBLK_DEV_F_RECOVER_DEV flag) Ok(()) } ``` -------------------------------- ### Grant CAP_IPC_LOCK capability Source: https://github.com/ublk-org/libublk-rs/blob/main/CLAUDE.md Grant the CAP_IPC_LOCK capability to an executable for memory locking support. This is required when using the UBLK_DEV_F_MLOCK_IO_BUFFER flag. ```bash sudo setcap cap_ipc_lock=eip /path/to/your/ublk_executable ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.