Skip to content

Add infiniband mlx4 device driver basic interface #1033

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Documentation/process/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ you probably needn't concern yourself with pcmciautils.
GNU C 5.1 gcc --version
Clang/LLVM (optional) 11.0.0 clang --version
Rust (optional) 1.71.0 rustc --version
bindgen (optional) 0.56.0 bindgen --version
bindgen (optional) 0.65.1 bindgen --version
GNU make 3.82 make --version
bash 4.2 bash --version
binutils 2.25 ld -v
Expand Down
6 changes: 3 additions & 3 deletions rust/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ quiet_cmd_bindgen = BINDGEN $@
$(BINDGEN) $< $(bindgen_target_flags) \
--use-core --with-derive-default --ctypes-prefix core::ffi --no-layout-tests \
--no-debug '.*' \
--size_t-is-usize -o $@ -- $(bindgen_c_flags_final) -DMODULE \
-o $@ -- $(bindgen_c_flags_final) -DMODULE \
$(bindgen_target_cflags) $(bindgen_target_extra)

$(obj)/bindings/bindings_generated.rs: private bindgen_target_flags = \
Expand All @@ -339,8 +339,8 @@ $(obj)/bindings/bindings_generated.rs: $(src)/bindings/bindings_helper.h \
# given it is `libclang`; but for consistency, future Clang changes and/or
# a potential future GCC backend for `bindgen`, we disable it too.
$(obj)/bindings/bindings_helpers_generated.rs: private bindgen_target_flags = \
--blacklist-type '.*' --whitelist-var '' \
--whitelist-function 'rust_helper_.*'
--blocklist-type '.*' --allowlist-var '' \
--allowlist-function 'rust_helper_.*'
$(obj)/bindings/bindings_helpers_generated.rs: private bindgen_target_cflags = \
-I$(objtree)/$(obj) -Wno-missing-prototypes -Wno-missing-declarations
$(obj)/bindings/bindings_helpers_generated.rs: private bindgen_target_extra = ; \
Expand Down
1 change: 1 addition & 0 deletions rust/bindings/bindings_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <linux/sysctl.h>
#include <linux/uaccess.h>
#include <linux/uio.h>
#include <linux/mlx4/driver.h>

/* `bindgen` gets confused at certain things. */
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
Expand Down
13 changes: 6 additions & 7 deletions rust/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -657,19 +657,18 @@ int rust_helper_fs_parse(struct fs_context *fc,
EXPORT_SYMBOL_GPL(rust_helper_fs_parse);

/*
* We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
* as the Rust `usize` type, so we can use it in contexts where Rust
* expects a `usize` like slice (array) indices. `usize` is defined to be
* the same as C's `uintptr_t` type (can hold any pointer) but not
* necessarily the same as `size_t` (can hold the size of any single
* `bindgen` binds the C `size_t` type the Rust `usize` type, so we can
* use it in contexts where Rust expects a `usize` like slice (array) indices.
* `usize` is defined to be the same as C's `uintptr_t` type (can hold any pointer)
* but not necessarily the same as `size_t` (can hold the size of any single
* object). Most modern platforms use the same concrete integer type for
* both of them, but in case we find ourselves on a platform where
* that's not true, fail early instead of risking ABI or
* integer-overflow issues.
*
* If your platform fails this assertion, it means that you are in
* danger of integer-overflow bugs (even if you attempt to remove
* `--size_t-is-usize`). It may be easiest to change the kernel ABI on
* danger of integer-overflow bugs (even if you attempt to add
* `--no-size_t-is-usize`). It may be easiest to change the kernel ABI on
* your platform such that `size_t` matches `uintptr_t` (i.e., to increase
* `size_t`, because `uintptr_t` has to be at least as big as `size_t`).
*/
Expand Down
5 changes: 2 additions & 3 deletions rust/kernel/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -257,7 +259,4 @@ fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
pr_emerg!("{}\n", info);
// SAFETY: FFI call.
unsafe { bindings::BUG() };
// Bindgen currently does not recognize `__noreturn` so `BUG` returns `()`
// instead of `!`. See <https://github.com/rust-lang/rust-bindgen/issues/2094>.
loop {}
}
288 changes: 288 additions & 0 deletions rust/kernel/mlx4.rs
Original file line number Diff line number Diff line change
@@ -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<T: Mlx4Operation> {
registered: bool,
#[allow(dead_code)]
name: &'static CStr,
wq: Mlx4WorkQueue,
cm_wq: CmWorkQueue,
qp_wq: QpWorkQueue,
mcg_wq: McgWorkQueue,
phantom: marker::PhantomData<T>,
}

impl<T: Mlx4Operation> Registration<T> {
/// 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<Pin<Box<Self>>> {
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::<T>::build());
}

this.registered = true;
Ok(())
}
}

impl<T: Mlx4Operation> Drop for Registration<T> {
/// 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<T>(marker::PhantomData<T>);

impl<T: Mlx4Operation> Mlx4OperationTable<T> {
/// 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<BoxedQueue>,
}

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<BoxedQueue>,
}

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<BoxedQueue>,
}

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<BoxedQueue>,
}

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());
}
}
}
9 changes: 6 additions & 3 deletions rust/kernel/workqueue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<BoxedQueue> {
pub fn try_new(name: fmt::Arguments<'_>, flags: u32, max_active: i32) -> Result<BoxedQueue> {
// 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,
)
};
Expand Down Expand Up @@ -408,6 +408,9 @@ pub struct BoxedQueue {
ptr: NonNull<Queue>,
}

// SAFETY: Kernel workqueues are usable from any thread.
unsafe impl Sync for BoxedQueue {}

impl BoxedQueue {
/// Creates a new instance of [`BoxedQueue`].
///
Expand Down
Loading