diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 7d9bef6f870378..cf6df51641031a 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -36,6 +36,7 @@ #include #include #include +#include /* `bindgen` gets confused at certain things. */ const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL; diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 7adbcba68bf465..e49c2170601298 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -76,6 +76,8 @@ pub mod task; pub mod workqueue; pub mod linked_list; +#[cfg(CONFIG_MLX4_EN)] +pub mod mlx4; mod raw_list; pub mod rbtree; pub mod unsafe_list; diff --git a/rust/kernel/mlx4.rs b/rust/kernel/mlx4.rs new file mode 100644 index 00000000000000..7f53e0e9e670bc --- /dev/null +++ b/rust/kernel/mlx4.rs @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Infiniband mlx4 devices. +//! + +use alloc::boxed::Box; +use core::pin::Pin; +use core::{marker, ptr}; +use macros::vtable; + +use crate::bindings; +use crate::error::{code::*, Result}; +use crate::str::CStr; +use crate::workqueue::{BoxedQueue, Queue}; + +/// Infiband mlx4 device registration. +/// +pub struct Registration { + registered: bool, + #[allow(dead_code)] + name: &'static CStr, + wq: Mlx4WorkQueue, + cm_wq: CmWorkQueue, + qp_wq: QpWorkQueue, + mcg_wq: McgWorkQueue, + phantom: marker::PhantomData, +} + +impl Registration { + /// Creates a new [`Registration`] but does not register it yet. + /// + /// It is allowed to move. + pub fn new(name: &'static CStr) -> Self { + // INVARIANT: `registered` is `false` + Self { + registered: false, + name, + wq: Mlx4WorkQueue::new(), + cm_wq: CmWorkQueue::new(), + qp_wq: QpWorkQueue::new(), + mcg_wq: McgWorkQueue::new(), + phantom: marker::PhantomData, + } + } + + /// Registers a infiband mlx4 device. + /// + /// Returns a pinned heap-allocated representation of the registration. + pub fn new_pinned(name: &'static CStr) -> Result>> { + let mut r = Pin::from(Box::try_new(Self::new(name))?); + r.as_mut().register()?; + Ok(r) + } + + // Registers a infiband mlx4 device with the rest of the kernel. + /// + /// It must be pinned because the memory block that represents the registration is + /// self-referential. + pub fn register(self: Pin<&mut Self>) -> Result { + // SAFETY: We must ensure that we never move out of `this`. + let this = unsafe { self.get_unchecked_mut() }; + if this.registered { + // Already registered. + return Err(EINVAL); + } + + match this.wq.init() { + Ok(()) => {} + Err(e) => return Err(e), + } + + match this.qp_wq.init() { + Ok(()) => {} + Err(e) => { + this.wq.clean(); + return Err(e); + } + } + + match this.cm_wq.init() { + Ok(()) => {} + Err(e) => { + this.wq.clean(); + this.qp_wq.clean(); + return Err(e); + } + } + + match this.mcg_wq.init() { + Ok(()) => {} + Err(e) => { + this.wq.clean(); + this.cm_wq.clean(); + this.qp_wq.clean(); + return Err(e); + } + } + + // SAFETY: The adapter is compatible with the mlx4 register + unsafe { + bindings::mlx4_register_interface(Mlx4OperationTable::::build()); + } + + this.registered = true; + Ok(()) + } +} + +impl Drop for Registration { + /// Removes the registration from the kernel if it has completed successfully before. + fn drop(&mut self) { + if self.registered { + self.mcg_wq.clean(); + self.cm_wq.clean(); + self.qp_wq.clean(); + self.wq.clean(); + } + } +} + +/// Build kernel's `struct mlx4_interface` type with mlx4 device operation. +pub struct Mlx4OperationTable(marker::PhantomData); + +impl Mlx4OperationTable { + /// Builds an instance of [`struct mlx4_interface`]. + /// + /// # Safety + /// + /// The caller must ensure that the adapter is compatible with the way the device is registered. + pub fn build() -> *mut bindings::mlx4_interface { + return &mut bindings::mlx4_interface { + add: Some(Self::add_callback), + remove: Some(Self::remove_callback), + event: Some(Self::event_callback), + get_dev: None, + activate: None, + list: bindings::list_head { + next: ptr::null_mut(), + prev: ptr::null_mut(), + }, + // MLX4_PROT_IB_IPV6 + protocol: 0, + // MLX4_INTFF_BONDING + flags: 1, + }; + } + + unsafe extern "C" fn add_callback(_dev: *mut bindings::mlx4_dev) -> *mut core::ffi::c_void { + let _ = T::add(); + return ptr::null_mut(); + } + + unsafe extern "C" fn remove_callback( + _dev: *mut bindings::mlx4_dev, + _context: *mut core::ffi::c_void, + ) { + let _ = T::remove(); + } + + unsafe extern "C" fn event_callback( + _dev: *mut bindings::mlx4_dev, + _context: *mut core::ffi::c_void, + _event: bindings::mlx4_dev_event, + _param: core::ffi::c_ulong, + ) { + let _ = T::event(); + } +} + +/// Corresponds to the kernel's `struct mlx4_interface`. +/// +/// You implement this trait whenever you would create a `struct mlx4_interface`. +#[vtable] +pub trait Mlx4Operation { + /// Add a new mlx4 ib device. + fn add() -> Result; + /// Remove mlx4 ib device. + fn remove() -> Result; + /// Respond to specific mlx4 ib device event + fn event() -> Result; +} + +pub(crate) struct Mlx4WorkQueue { + wq: Option, +} + +impl Mlx4WorkQueue { + pub(crate) fn new() -> Self { + Self { wq: None } + } + + pub(crate) fn init(&mut self) -> Result { + let wq_tmp = Queue::try_new(format_args!("mlx4_ib"), 655369, 1); + self.wq = match wq_tmp { + Ok(wq) => Some(wq), + Err(e) => return Err(e), + }; + + Ok(()) + } + + pub(crate) fn clean(&mut self) { + if self.wq.is_some() { + drop(self.wq.take().unwrap()); + } + } +} + +pub(crate) struct CmWorkQueue { + cm_wq: Option, +} + +impl CmWorkQueue { + pub(crate) fn new() -> Self { + Self { cm_wq: None } + } + + pub(crate) fn init(&mut self) -> Result { + let cm_wq_tmp = Queue::try_new(format_args!("mlx4_ib_cm"), 0, 0); + self.cm_wq = match cm_wq_tmp { + Ok(cm_wq) => Some(cm_wq), + Err(e) => return Err(e), + }; + + Ok(()) + } + + pub(crate) fn clean(&mut self) { + if self.cm_wq.is_some() { + drop(self.cm_wq.take().unwrap()); + } + } +} + +pub(crate) struct McgWorkQueue { + clean_wq: Option, +} + +impl McgWorkQueue { + pub(crate) fn new() -> Self { + Self { clean_wq: None } + } + + pub(crate) fn init(&mut self) -> Result { + let clean_wq_tmp = Queue::try_new(format_args!("mlx4_ib_mcg"), 655369, 1); + self.clean_wq = match clean_wq_tmp { + Ok(clean_wq) => Some(clean_wq), + Err(e) => return Err(e), + }; + + Ok(()) + } + + pub(crate) fn clean(&mut self) { + if self.clean_wq.is_some() { + drop(self.clean_wq.take().unwrap()); + } + } +} + +pub(crate) struct QpWorkQueue { + mlx4_ib_qp_event_wq: Option, +} + +impl QpWorkQueue { + pub(crate) fn new() -> Self { + Self { + mlx4_ib_qp_event_wq: None, + } + } + + pub(crate) fn init(&mut self) -> Result { + let mlx4_ib_qp_event_wq_tmp = + Queue::try_new(format_args!("mlx4_ib_qp_event_wq"), 655361, 1); + self.mlx4_ib_qp_event_wq = match mlx4_ib_qp_event_wq_tmp { + Ok(mlx4_ib_qp_event_wq) => Some(mlx4_ib_qp_event_wq), + Err(e) => return Err(e), + }; + + Ok(()) + } + + pub(crate) fn clean(&mut self) { + if self.mlx4_ib_qp_event_wq.is_some() { + drop(self.mlx4_ib_qp_event_wq.take().unwrap()); + } + } +} diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs index d87dfe41562148..fa289af097b1a7 100644 --- a/rust/kernel/workqueue.rs +++ b/rust/kernel/workqueue.rs @@ -209,14 +209,14 @@ impl Queue { /// /// Callers should first consider using one of the existing ones (e.g. [`system`]) before /// deciding to create a new one. - pub fn try_new(name: fmt::Arguments<'_>) -> Result { + pub fn try_new(name: fmt::Arguments<'_>, flags: u32, max_active: i32) -> Result { // SAFETY: We use a format string that requires an `fmt::Arguments` pointer as the first // and only argument. let ptr = unsafe { bindings::alloc_workqueue( c_str!("%pA").as_char_ptr(), - 0, - 0, + flags, + max_active, &name as *const _ as *const core::ffi::c_void, ) }; @@ -408,6 +408,9 @@ pub struct BoxedQueue { ptr: NonNull, } +// SAFETY: Kernel workqueues are usable from any thread. +unsafe impl Sync for BoxedQueue {} + impl BoxedQueue { /// Creates a new instance of [`BoxedQueue`]. /// diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index 189c10ced6d4f7..55828d089e7de9 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -163,4 +163,11 @@ config SAMPLE_RUST_SELFTESTS If unsure, say N. +config SAMPLE_RUST_MLX4 + tristate "infiniband mlx4" + help + This option builds the infiniband mlx4 driver cases for Rust. + + If unsure, say N. + endif # SAMPLES_RUST diff --git a/samples/rust/Makefile b/samples/rust/Makefile index 420bcefeb08255..d7cee929accf34 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -15,5 +15,6 @@ obj-$(CONFIG_SAMPLE_RUST_NETFILTER) += rust_netfilter.o obj-$(CONFIG_SAMPLE_RUST_ECHO_SERVER) += rust_echo_server.o obj-$(CONFIG_SAMPLE_RUST_FS) += rust_fs.o obj-$(CONFIG_SAMPLE_RUST_SELFTESTS) += rust_selftests.o +obj-$(CONFIG_SAMPLE_RUST_MLX4) += rust_mlx4.o subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs diff --git a/samples/rust/rust_mlx4.rs b/samples/rust/rust_mlx4.rs new file mode 100644 index 00000000000000..280838071eeee6 --- /dev/null +++ b/samples/rust/rust_mlx4.rs @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust infiniband mls4 device sample. + +use kernel::mlx4; +use kernel::prelude::*; + +module! { + type: RustMlx4, + name: "rust_mlx4", + author: "Rust for Linux Contributors", + description: "Rust infiniband mlx4 device sample", + license: "GPL", +} + +struct RustMlx4Ops; + +#[vtable] +impl mlx4::Mlx4Operation for RustMlx4Ops { + fn add() -> Result { + Ok(()) + } + fn remove() -> Result { + Ok(()) + } + fn event() -> Result { + Ok(()) + } +} + +struct RustMlx4 { + _dev: Pin>>, +} + +impl kernel::Module for RustMlx4 { + fn init(name: &'static CStr, _module: &'static ThisModule) -> Result { + pr_info!("Rust infiniband mlx4 driver sample (init)\n"); + + Ok(RustMlx4 { + _dev: mlx4::Registration::new_pinned(name)?, + }) + } +} + +impl Drop for RustMlx4 { + fn drop(&mut self) { + pr_info!("Rust infiniband mlx4 driver sample (exit)\n"); + } +}