/
githubmirror
/
libblkio
Обзор
Документация
Войти
/
githubmirror
/
libblkio
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v1.0.0
src/lib.rs
547 строк
14 KB
Stefan Hajnoczi
Merge branch 'poll-queues' into 'main'
09 сен 2022, 22:46
09 сен 2022, 22:46
d0dfaab
Код
Авторство
О чём код?
// SPDX-License-Identifier: (MIT OR Apache-2.0) #![cfg_attr(feature = "_unsafe-op-in-unsafe-fn", deny(unsafe_op_in_unsafe_fn))] #![cfg_attr(not(feature = "_unsafe-op-in-unsafe-fn"), allow(unused_unsafe))] use libc::{iovec, sigset_t, size_t, timespec, EINTR}; use nix::errno::Errno; use std::convert::TryInto; use std::ffi::CStr; use std::os::raw::{c_char, c_int, c_void}; use std::ptr; use std::time::Duration; use blkio::{Blkio, Blkioq, Completion, Error, MemoryRegion, ReqFlags, Result}; /// If `f()` evaluates to `Ok`, this returns `0`. Otherwise, this sets the thread-local error /// message from the `Err` and returns its associated _negated_ errno value. fn handling_result<F>(f: F) -> c_int where F: FnOnce() -> Result<()>, { handling_result_with_value(|| { f()?; Ok(0) }) } /// If `f()` evaluates to `Ok`, this returns its value. Otherwise, this sets the thread-local error /// message from the `Err` and returns its associated _negated_ errno value. fn handling_result_with_value<F>(f: F) -> c_int where F: FnOnce() -> Result<c_int>, { match f() { Ok(value) => value, Err(e) => { let msg = e.message(); unsafe { blkio_set_error_msg_(msg.as_ptr() as *const c_char, msg.len()) }; -(e.errno()) } } } extern "C" { fn blkio_get_error_msg_() -> *const c_char; fn blkio_set_error_msg_(msg: *const c_char, msg_len: size_t); } // Can't reexport directly from error-msg.c, so we create a wrapper here. See: // https://github.com/rust-lang/rfcs/issues/2771 #[no_mangle] pub extern "C" fn blkio_get_error_msg() -> *const c_char { unsafe { blkio_get_error_msg_() } } /// # Safety /// /// The C caller must ensure that the pointers are valid #[no_mangle] pub unsafe extern "C" fn blkio_create(cdriver: *const c_char, bp: *mut *mut c_void) -> c_int { handling_result(|| { if bp.is_null() { return Err(Error::new(Errno::EINVAL, "bp must be non-NULL")); } unsafe { *bp = ptr::null_mut() }; let driver_name = unsafe { CStr::from_ptr(cdriver) } .to_str() .map_err(|_| Error::new(Errno::EINVAL, "Invalid driver name"))?; let b = Blkio::new(driver_name)?; unsafe { *bp = Box::into_raw(Box::new(b)) as *mut c_void }; Ok(()) }) } #[no_mangle] pub extern "C" fn blkio_connect(b: &mut Blkio) -> c_int { handling_result(|| b.connect()) } #[no_mangle] pub extern "C" fn blkio_start(b: &mut Blkio) -> c_int { handling_result(|| b.start()) } /// # Safety /// /// The C caller must ensure that the pointers are valid /// #[no_mangle] pub unsafe extern "C" fn blkio_destroy(bp: *mut *mut Blkio) { if !bp.is_null() { unsafe { Box::from_raw(*bp); // reconstruct then drop the Box *bp = ptr::null_mut(); } } } fn get_property<T, G>(cname: *const c_char, value: *mut T, default: T, getter: G) -> c_int where G: FnOnce(&str) -> Result<T>, { handling_result(|| { let name = unsafe { CStr::from_ptr(cname) } .to_str() .map_err(|_| Error::new(Errno::EINVAL, "Invalid property name"))?; match getter(name) { Ok(v) => { unsafe { *value = v }; Ok(()) } Err(e) => { unsafe { *value = default }; Err(e) } } }) } fn set_property<S>(cname: *const c_char, setter: S) -> c_int where S: FnOnce(&str) -> Result<()>, { handling_result(|| { let name = unsafe { CStr::from_ptr(cname) } .to_str() .map_err(|_| Error::new(Errno::EINVAL, "Invalid property name"))?; setter(name) }) } /// # Safety /// /// The C caller must ensure that the pointers are valid /// #[no_mangle] pub unsafe extern "C" fn blkio_get_bool( b: &Blkio, cname: *const c_char, value: *mut bool, ) -> c_int { get_property(cname, value, false, |name| b.get_bool(name)) } /// # Safety /// /// The C caller must ensure that the pointers are valid /// #[no_mangle] pub unsafe extern "C" fn blkio_get_int( b: &Blkio, cname: *const c_char, value: *mut c_int, ) -> c_int { get_property(cname, value, 0, |name| b.get_i32(name)) } /// # Safety /// /// The C caller must ensure that the pointers are valid /// #[no_mangle] pub unsafe extern "C" fn blkio_get_str( b: &Blkio, cname: *const c_char, value: *mut *mut c_char, ) -> c_int { get_property(cname, value, ptr::null_mut(), |name| { let value_str = b.get_str(name)?; let value_copy = unsafe { libc::strndup(value_str.as_ptr() as *const c_char, value_str.len()) }; if value_copy.is_null() { Err(Error::new(Errno::ENOMEM, "Out of memory")) } else { Ok(value_copy) } }) } /// # Safety /// /// The C caller must ensure that the pointers are valid /// #[no_mangle] pub unsafe extern "C" fn blkio_get_uint64( b: &Blkio, cname: *const c_char, value: *mut u64, ) -> c_int { get_property(cname, value, 0, |name| b.get_u64(name)) } /// # Safety /// /// The C caller must ensure that the pointers are valid /// #[no_mangle] pub unsafe extern "C" fn blkio_set_bool(b: &mut Blkio, cname: *const c_char, value: bool) -> c_int { set_property(cname, |name| b.set_bool(name, value)) } /// # Safety /// /// The C caller must ensure that the pointers are valid /// #[no_mangle] pub unsafe extern "C" fn blkio_set_int(b: &mut Blkio, cname: *const c_char, value: c_int) -> c_int { set_property(cname, |name| b.set_i32(name, value)) } /// # Safety /// /// The C caller must ensure that the pointers are valid /// #[no_mangle] pub unsafe extern "C" fn blkio_set_str( b: &mut Blkio, cname: *const c_char, cvalue: *const c_char, ) -> c_int { set_property(cname, |name| { let value = unsafe { CStr::from_ptr(cvalue) } .to_str() .map_err(|_| Error::new(Errno::EINVAL, "Invalid value string"))?; b.set_str(name, value) }) } /// # Safety /// /// The C caller must ensure that the pointers are valid /// #[no_mangle] pub unsafe extern "C" fn blkio_set_uint64( b: &mut Blkio, cname: *const c_char, value: u64, ) -> c_int { set_property(cname, |name| b.set_u64(name, value)) } #[derive(Clone, Copy)] #[repr(C)] pub struct MemRegion { addr: *mut c_void, len: size_t, iova: u64, fd_offset: i64, fd: c_int, flags: u32, } impl From<MemRegion> for MemoryRegion { fn from(region: MemRegion) -> MemoryRegion { MemoryRegion { addr: region.addr as usize, iova: region.iova, len: region.len, fd: region.fd, fd_offset: region.fd_offset, flags: region.flags, } } } impl From<MemoryRegion> for MemRegion { fn from(region: MemoryRegion) -> MemRegion { MemRegion { addr: region.addr as *mut c_void, iova: region.iova, len: region.len, fd_offset: region.fd_offset, fd: region.fd, flags: region.flags, } } } #[no_mangle] pub extern "C" fn blkio_alloc_mem_region( b: &mut Blkio, region: &mut MemRegion, len: size_t, ) -> c_int { handling_result(|| { let mem_region = b.alloc_mem_region(len)?; *region = mem_region.into(); Ok(()) }) } #[no_mangle] pub extern "C" fn blkio_free_mem_region(b: &mut Blkio, region: &MemRegion) { b.free_mem_region(&(*region).into()); } #[no_mangle] pub extern "C" fn blkio_map_mem_region(b: &mut Blkio, region: &MemRegion) -> c_int { handling_result(|| b.map_mem_region(&(*region).into())) } #[no_mangle] pub extern "C" fn blkio_unmap_mem_region(b: &mut Blkio, region: &MemRegion) { b.unmap_mem_region(&(*region).into()); } #[no_mangle] pub extern "C" fn blkio_get_queue(b: &mut Blkio, index: c_int) -> *mut Blkioq { if index < 0 { return ptr::null_mut(); } b.get_queue(index as usize) .map(|q| q as *mut Blkioq) .unwrap_or(ptr::null_mut()) } #[no_mangle] pub extern "C" fn blkio_get_poll_queue(b: &mut Blkio, index: c_int) -> *mut Blkioq { if index < 0 { return ptr::null_mut(); } b.get_poll_queue(index as usize) .map(|q| q as *mut Blkioq) .unwrap_or(ptr::null_mut()) } #[no_mangle] pub extern "C" fn blkioq_read( q: &mut Blkioq, start: u64, buf: *mut c_void, len: size_t, user_data: *mut c_void, flags: ReqFlags, ) { q.read(start, buf as *mut u8, len, user_data as usize, flags) } #[no_mangle] pub extern "C" fn blkioq_write( q: &mut Blkioq, start: u64, buf: *const c_void, len: size_t, user_data: *mut c_void, flags: ReqFlags, ) { q.write(start, buf as *const u8, len, user_data as usize, flags) } #[no_mangle] pub extern "C" fn blkioq_readv( q: &mut Blkioq, start: u64, iovec: *const iovec, iovcnt: c_int, user_data: *mut c_void, flags: ReqFlags, ) { q.readv(start, iovec, iovcnt as u32, user_data as usize, flags) } #[no_mangle] pub extern "C" fn blkioq_writev( q: &mut Blkioq, start: u64, iovec: *const iovec, iovcnt: c_int, user_data: *mut c_void, flags: ReqFlags, ) { q.writev(start, iovec, iovcnt as u32, user_data as usize, flags) } #[no_mangle] pub extern "C" fn blkioq_write_zeroes( q: &mut Blkioq, start: u64, len: u64, user_data: *mut c_void, flags: ReqFlags, ) { q.write_zeroes(start, len, user_data as usize, flags) } #[no_mangle] pub extern "C" fn blkioq_discard( q: &mut Blkioq, start: u64, len: u64, user_data: *mut c_void, flags: ReqFlags, ) { q.discard(start, len, user_data as usize, flags) } #[no_mangle] pub extern "C" fn blkioq_flush(q: &mut Blkioq, user_data: *mut c_void, flags: ReqFlags) { q.flush(user_data as usize, flags) } fn duration_from_timespec(t: ×pec) -> Result<Duration> { // The Linux kernel checks these timespec fields in the same way. Check now since the same // preconditions are necessary for converting to Duration. if t.tv_sec < 0 { return Err(Error::new(Errno::EINVAL, "tv_sec cannot be negative")); } if t.tv_nsec as u64 >= 1000000000 { return Err(Error::new( Errno::EINVAL, "tv_nsec must be less than one second", )); } Ok(Duration::new(t.tv_sec as u64, t.tv_nsec as u32)) } fn timespec_from_duration(d: Duration) -> timespec { timespec { tv_sec: d.as_secs() as _, tv_nsec: d.subsec_nanos() as _, } } fn completions_to_slice<'a>( completions: *mut std::mem::MaybeUninit<Completion>, min_completions: c_int, max_completions: c_int, ) -> Result<&'a mut [std::mem::MaybeUninit<Completion>]> { if min_completions < 0 { return Err(Error::new( Errno::EINVAL, "min_completions cannot be negative", )); } if max_completions < 0 { return Err(Error::new( Errno::EINVAL, "max_completions cannot be negative", )); } if min_completions > max_completions { return Err(Error::new( Errno::EINVAL, "min_completions must be less than or equal to max_completions", )); } if completions.is_null() { if max_completions > 0 { return Err(Error::new( Errno::EINVAL, "max_completions must be 0 when completions is NULL", )); } return Ok(&mut []); } Ok(unsafe { std::slice::from_raw_parts_mut(completions, max_completions as usize) }) } #[no_mangle] pub extern "C" fn blkioq_do_io( q: &mut Blkioq, completions: *mut std::mem::MaybeUninit<Completion>, min_completions: c_int, max_completions: c_int, timeout: Option<&mut timespec>, ) -> c_int { handling_result_with_value(|| { let mut duration = timeout.as_deref().map(duration_from_timespec).transpose()?; let completions_slice = completions_to_slice(completions, min_completions, max_completions)?; let result = loop { match q .do_io( completions_slice, min_completions as usize, duration.as_mut(), None, ) .map(|n| n.try_into().unwrap()) { Err(err) if err.errno() == EINTR => {} result => break result, } }; if let Some(t) = timeout { *t = timespec_from_duration(duration.unwrap()); } result }) } #[no_mangle] pub extern "C" fn blkioq_do_io_interruptible( q: &mut Blkioq, completions: *mut std::mem::MaybeUninit<Completion>, min_completions: c_int, max_completions: c_int, timeout: Option<&mut timespec>, sig: &sigset_t, ) -> c_int { handling_result_with_value(|| { let mut duration = timeout.as_deref().map(duration_from_timespec).transpose()?; let completions_slice = completions_to_slice(completions, min_completions, max_completions)?; let result = q .do_io( completions_slice, min_completions as usize, duration.as_mut(), Some(sig), ) .map(|n| n.try_into().unwrap()); if let Some(t) = timeout { *t = timespec_from_duration(duration.unwrap()); } result }) } #[no_mangle] pub extern "C" fn blkioq_get_completion_fd(q: &mut Blkioq) -> c_int { q.get_completion_fd().unwrap_or(-1) } #[no_mangle] pub extern "C" fn blkioq_set_completion_fd_enabled(q: &mut Blkioq, enable: bool) { q.set_completion_fd_enabled(enable); }