From nobody Fri Oct 3 07:42:39 2025 Received: from forward205a.mail.yandex.net (forward205a.mail.yandex.net [178.154.239.88]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B4B2B302CB6; Wed, 3 Sep 2025 13:19:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.154.239.88 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756905583; cv=none; b=J3SVkSHzljPLsGw/Pah2LaVLNfSbY1WBcK5A5QW52zYEEmGFwX22CdhFBcqeyMO94c70+S1tsPb81AazUYlbCnJosIC8XlElA1vJ+ECQWw+btmya/kXJCd6B2FyLOWgOUEc7wQHf8b2smHsNrIfPLS2TG1dMIDN4aGtwWWFZlpg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756905583; c=relaxed/simple; bh=pyzxwGx7+PSkmBG1WocWAcb5j3Uj2h0oOnLjIjc2jd4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=e+kOQCYpucq01K5g5gC4mK+mDXADxPs55vkscKOEDKcMLoBJ0g+g1ZwLss+izksu2zPhVKE4Y1kZ0sWNiHTBMbmAOuuTuGiczwjXzN/IwOALynZORdaZ2hFqmBjyzIYrdaeHrAM8hqGtowWLcMPfPT5s791Q48jHEyAXqYbCuSY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=onurozkan.dev; spf=pass smtp.mailfrom=onurozkan.dev; dkim=pass (1024-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b=OJXBnqr/; arc=none smtp.client-ip=178.154.239.88 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b="OJXBnqr/" Received: from forward102a.mail.yandex.net (forward102a.mail.yandex.net [IPv6:2a02:6b8:c0e:500:1:45:d181:d102]) by forward205a.mail.yandex.net (Yandex) with ESMTPS id E24BBC751F; Wed, 03 Sep 2025 16:13:43 +0300 (MSK) Received: from mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net (mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net [IPv6:2a02:6b8:c1d:3e17:0:640:f9aa:0]) by forward102a.mail.yandex.net (Yandex) with ESMTPS id 554AEC0054; Wed, 03 Sep 2025 16:13:36 +0300 (MSK) Received: by mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net (smtp/Yandex) with ESMTPSA id GDgKkBDMquQ0-trGI2GpZ; Wed, 03 Sep 2025 16:13:35 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=onurozkan.dev; s=mail; t=1756905215; bh=PCoOmkaZAX9QwwZz2KVS30/FrEGaOVtw0GvyVsUp+oI=; h=Cc:Message-ID:References:Date:In-Reply-To:Subject:To:From; b=OJXBnqr/Oacta5ihlRRrx8ApyLEHfYwx18VHIj1ELZMYIQ3Ght4iKAHALyKslPGLB GXkISFehVsSnD/ndhqg5dajGgDVr77FcAUhtzlls3j3Bd3PYtglDg8zlDNp+wBdcGj 7YoAed1ZUJr23hNcU11On2A562GOdiH80iz/zRMw= Authentication-Results: mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net; dkim=pass header.i=@onurozkan.dev From: =?UTF-8?q?Onur=20=C3=96zkan?= To: rust-for-linux@vger.kernel.org Cc: linux-kernel@vger.kernel.org, lossin@kernel.org, lyude@redhat.com, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, dakr@kernel.org, peterz@infradead.org, mingo@redhat.com, will@kernel.org, longman@redhat.com, felipe_life@live.com, daniel@sedlak.dev, bjorn3_gh@protonmail.com, daniel.almeida@collabora.com, =?UTF-8?q?Onur=20=C3=96zkan?= Subject: [PATCH v6 1/7] rust: add C wrappers for ww_mutex inline functions Date: Wed, 3 Sep 2025 16:13:07 +0300 Message-ID: <20250903131313.4365-2-work@onurozkan.dev> X-Mailer: git-send-email 2.50.0 In-Reply-To: <20250903131313.4365-1-work@onurozkan.dev> References: <20250903131313.4365-1-work@onurozkan.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Some of the kernel's `ww_mutex` functions are implemented as `static inline`, so they are inaccessible from Rust as bindgen can't generate code on them. This patch provides C function wrappers around these inline implementations, so bindgen can see them and generate the corresponding Rust code. Signed-off-by: Onur =C3=96zkan Reviewed-by: Daniel Almeida --- rust/helpers/helpers.c | 1 + rust/helpers/ww_mutex.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 rust/helpers/ww_mutex.c diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 7cf7fe95e41d..4c789c5537b1 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -50,4 +50,5 @@ #include "vmalloc.c" #include "wait.c" #include "workqueue.c" +#include "ww_mutex.c" #include "xarray.c" diff --git a/rust/helpers/ww_mutex.c b/rust/helpers/ww_mutex.c new file mode 100644 index 000000000000..61a487653394 --- /dev/null +++ b/rust/helpers/ww_mutex.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +void rust_helper_ww_mutex_init(struct ww_mutex *lock, struct ww_class *ww_= class) +{ + ww_mutex_init(lock, ww_class); +} + +void rust_helper_ww_acquire_init(struct ww_acquire_ctx *ctx, struct ww_cla= ss *ww_class) +{ + ww_acquire_init(ctx, ww_class); +} + +void rust_helper_ww_acquire_done(struct ww_acquire_ctx *ctx) +{ + ww_acquire_done(ctx); +} + +void rust_helper_ww_acquire_fini(struct ww_acquire_ctx *ctx) +{ + ww_acquire_fini(ctx); +} + +void rust_helper_ww_mutex_lock_slow(struct ww_mutex *lock, struct ww_acqui= re_ctx *ctx) +{ + ww_mutex_lock_slow(lock, ctx); +} + +int rust_helper_ww_mutex_lock_slow_interruptible(struct ww_mutex *lock, st= ruct ww_acquire_ctx *ctx) +{ + return ww_mutex_lock_slow_interruptible(lock, ctx); +} + +bool rust_helper_ww_mutex_is_locked(struct ww_mutex *lock) +{ + return ww_mutex_is_locked(lock); +} + -- 2.50.0 From nobody Fri Oct 3 07:42:39 2025 Received: from forward206a.mail.yandex.net (forward206a.mail.yandex.net [178.154.239.87]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A86222F7449; Wed, 3 Sep 2025 13:21:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.154.239.87 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756905692; cv=none; b=ihHQWDLpnM0IdVHt0s2vTrvUmP8xz7LdTy+zrCRbLt8gWJhNxYgCCNHBJE5UaS1JxWVnIBqjIflcNz2r3Al4DlJoAAXsvJ1NkS7emLFxPaRdYW1k2du8rGqAsqnD6wg6sdF1NkkL0fj2tVHRjrYbn2wOrm6DhNc3D+87SQYjFkI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756905692; c=relaxed/simple; bh=89TA99TzngR1DPri1zBaKB6jSqwWX+ssbHxAwLZGtQM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=ChOAsIbcYrQLa0R4gxAtQUPOjCNi9yg0ig0Qo8WecTr/V3rqN2VNVe4vIrx05/LFP2+/rK5JJolJPp3OHqSR7tkbL+I8S08dzMQCAcJVptErK6sJIuZannMaaZmFgwChmpo+mvfAkQP9015hVUPrZNRpxIkbZaQ+h79JqfPNYPQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=onurozkan.dev; spf=pass smtp.mailfrom=onurozkan.dev; dkim=pass (1024-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b=HW3EIXvM; arc=none smtp.client-ip=178.154.239.87 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b="HW3EIXvM" Received: from forward100a.mail.yandex.net (forward100a.mail.yandex.net [IPv6:2a02:6b8:c0e:500:1:45:d181:d100]) by forward206a.mail.yandex.net (Yandex) with ESMTPS id D5F14826D2; Wed, 03 Sep 2025 16:13:48 +0300 (MSK) Received: from mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net (mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net [IPv6:2a02:6b8:c1d:3e17:0:640:f9aa:0]) by forward100a.mail.yandex.net (Yandex) with ESMTPS id D74E8C0036; Wed, 03 Sep 2025 16:13:40 +0300 (MSK) Received: by mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net (smtp/Yandex) with ESMTPSA id GDgKkBDMquQ0-0DBmNgtu; Wed, 03 Sep 2025 16:13:39 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=onurozkan.dev; s=mail; t=1756905220; bh=nnsW47jRpu2o9uucmiKj09c8TsfRK6GusYy4hDbXhkY=; h=Cc:Message-ID:References:Date:In-Reply-To:Subject:To:From; b=HW3EIXvMT33sPsjj08LGs3vgRvzAuttbM/2RJ57tUPq4zoZ/T3Y1c4pA1YDUkIxu2 Gas5FNNXn+cFQ/v1eOlBq+vAcynOfPiuIm/rClR2Az9qkOF8JZ3NRUv8o/b+L2KEQO r3DYr13giwGpOaSbMtyVMAthtiDFpyEUoHN5hHfI= Authentication-Results: mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net; dkim=pass header.i=@onurozkan.dev From: =?UTF-8?q?Onur=20=C3=96zkan?= To: rust-for-linux@vger.kernel.org Cc: linux-kernel@vger.kernel.org, lossin@kernel.org, lyude@redhat.com, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, dakr@kernel.org, peterz@infradead.org, mingo@redhat.com, will@kernel.org, longman@redhat.com, felipe_life@live.com, daniel@sedlak.dev, bjorn3_gh@protonmail.com, daniel.almeida@collabora.com, =?UTF-8?q?Onur=20=C3=96zkan?= Subject: [PATCH v6 2/7] rust: implement `WwClass` for ww_mutex support Date: Wed, 3 Sep 2025 16:13:08 +0300 Message-ID: <20250903131313.4365-3-work@onurozkan.dev> X-Mailer: git-send-email 2.50.0 In-Reply-To: <20250903131313.4365-1-work@onurozkan.dev> References: <20250903131313.4365-1-work@onurozkan.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Adds the `WwClass` type, the first step in supporting `ww_mutex` in Rust. `WwClass` represents a class of ww mutexes used for deadlock avoidance for supporting both wait-die and wound-wait semantics. Also adds the `define_ww_class!` macro for safely declaring static instances. Signed-off-by: Onur =C3=96zkan --- rust/kernel/sync/lock.rs | 1 + rust/kernel/sync/lock/ww_mutex.rs | 136 ++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 rust/kernel/sync/lock/ww_mutex.rs diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs index 27202beef90c..5b320c2b28c1 100644 --- a/rust/kernel/sync/lock.rs +++ b/rust/kernel/sync/lock.rs @@ -15,6 +15,7 @@ pub mod mutex; pub mod spinlock; +pub mod ww_mutex; pub(super) mod global; pub use global::{GlobalGuard, GlobalLock, GlobalLockBackend, GlobalLockedB= y}; diff --git a/rust/kernel/sync/lock/ww_mutex.rs b/rust/kernel/sync/lock/ww_m= utex.rs new file mode 100644 index 000000000000..ca5b4525ace6 --- /dev/null +++ b/rust/kernel/sync/lock/ww_mutex.rs @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! A kernel Wound/Wait Mutex. +//! +//! This module provides Rust abstractions for the Linux kernel's `ww_mute= x` implementation, +//! which provides deadlock avoidance through a wait-wound or wait-die alg= orithm. +//! +//! C header: [`include/linux/ww_mutex.h`](srctree/include/linux/ww_mutex.= h) +//! +//! For more information: + +use crate::bindings; +use crate::prelude::*; +use crate::types::Opaque; + +/// Create static [`WwClass`] instances. +/// +/// # Examples +/// +/// ``` +/// use kernel::{c_str, define_ww_class}; +/// +/// define_ww_class!(WOUND_WAIT_GLOBAL_CLASS, wound_wait, c_str!("wound_wa= it_global_class")); +/// define_ww_class!(WAIT_DIE_GLOBAL_CLASS, wait_die, c_str!("wait_die_glo= bal_class")); +/// ``` +#[macro_export] +macro_rules! define_ww_class { + ($name:ident, wound_wait, $class_name:expr) =3D> { + static $name: $crate::sync::lock::ww_mutex::WwClass =3D + // SAFETY: This is `static`, so address is fixed and won't mov= e. + unsafe { $crate::sync::lock::ww_mutex::WwClass::unpinned_new($= class_name, false) }; + }; + ($name:ident, wait_die, $class_name:expr) =3D> { + static $name: $crate::sync::lock::ww_mutex::WwClass =3D + // SAFETY: This is `static`, so address is fixed and won't mov= e. + unsafe { $crate::sync::lock::ww_mutex::WwClass::unpinned_new($= class_name, true) }; + }; +} + +/// A class used to group mutexes together for deadlock avoidance. +/// +/// All mutexes that might be acquired together should use the same class. +/// +/// # Examples +/// +/// ``` +/// use kernel::sync::lock::ww_mutex::WwClass; +/// use kernel::c_str; +/// use pin_init::stack_pin_init; +/// +/// stack_pin_init!(let _wait_die_class =3D WwClass::new_wait_die(c_str!("= graphics_buffers"))); +/// stack_pin_init!(let _wound_wait_class =3D WwClass::new_wound_wait(c_st= r!("memory_pools"))); +/// +/// # Ok::<(), Error>(()) +/// ``` +#[pin_data] +pub struct WwClass { + #[pin] + inner: Opaque, +} + +// SAFETY: [`WwClass`] is set up once and never modified. It's fine to sha= re it across threads. +unsafe impl Sync for WwClass {} +// SAFETY: Doesn't hold anything thread-specific. It's safe to send to oth= er threads. +unsafe impl Send for WwClass {} + +impl WwClass { + /// Creates an unpinned [`WwClass`]. + /// + /// # Safety + /// + /// Caller must guarantee that the returned value is not moved after c= reation. + pub const unsafe fn unpinned_new(name: &'static CStr, is_wait_die: boo= l) -> Self { + WwClass { + inner: Opaque::new(bindings::ww_class { + stamp: bindings::atomic_long_t { counter: 0 }, + acquire_name: name.as_char_ptr(), + mutex_name: name.as_char_ptr(), + is_wait_die: is_wait_die as u32, + // TODO: Replace with `bindings::lock_class_key::default()= ` once stabilized for `const`. + // + // SAFETY: This is always zero-initialized when defined wi= th `DEFINE_WD_CLASS` + // globally on C side. + // + // Ref: + acquire_key: unsafe { core::mem::zeroed() }, + // TODO: Replace with `bindings::lock_class_key::default()= ` once stabilized for `const`. + // + // SAFETY: This is always zero-initialized when defined wi= th `DEFINE_WD_CLASS` + // globally on C side. + // + // Ref: + mutex_key: unsafe { core::mem::zeroed() }, + }), + } + } + + /// Creates a [`WwClass`]. + /// + /// You should not use this function directly. Use the [`define_ww_cla= ss!`] + /// macro or call [`WwClass::new_wait_die`] or [`WwClass::new_wound_wa= it`] instead. + const fn new(name: &'static CStr, is_wait_die: bool) -> Self { + WwClass { + inner: Opaque::new(bindings::ww_class { + stamp: bindings::atomic_long_t { counter: 0 }, + acquire_name: name.as_char_ptr(), + mutex_name: name.as_char_ptr(), + is_wait_die: is_wait_die as u32, + // TODO: Replace with `bindings::lock_class_key::default()= ` once stabilized for `const`. + // + // SAFETY: This is always zero-initialized when defined wi= th `DEFINE_WD_CLASS` + // globally on C side. + // + // Ref: + acquire_key: unsafe { core::mem::zeroed() }, + // TODO: Replace with `bindings::lock_class_key::default()= ` once stabilized for `const`. + // + // SAFETY: This is always zero-initialized when defined wi= th `DEFINE_WD_CLASS` + // globally on C side. + // + // Ref: + mutex_key: unsafe { core::mem::zeroed() }, + }), + } + } + + /// Creates wait-die [`WwClass`]. + pub fn new_wait_die(name: &'static CStr) -> impl PinInit { + Self::new(name, true) + } + + /// Creates wound-wait [`WwClass`]. + pub fn new_wound_wait(name: &'static CStr) -> impl PinInit { + Self::new(name, false) + } +} -- 2.50.0 From nobody Fri Oct 3 07:42:39 2025 Received: from forward201a.mail.yandex.net (forward201a.mail.yandex.net [178.154.239.92]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5F8362FFDCA; Wed, 3 Sep 2025 13:20:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.154.239.92 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756905616; cv=none; b=WUjlBO0Y07Au3pT1N8HaValSUkN9MKFCBWu2Sqh0dfPJ2fiUBWYxZy5nOcxcvDu4NRmsLoJLRKLvrwKiXYjq3s7RlOzUZWEyW03jzORdYvW+YrOf19FKh4UzaJgQ/4+wcAgRo4sOLja1VL2pbNHlY3UJfB+R19IKZ4/VKVxnI3o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756905616; c=relaxed/simple; bh=wPLs9MaBuHrrFvs1AV/wqQc7ExYa69dkZnKTlSk9fRU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=YuCl3O+7nc+0iRHEiwQ7j9NgfFDH8Pqn+BhrGrlSWSIOOLIZcKTU9AlvRm4mUSYcK9j0QnjBSv09j9ulmgs7lAd9tm46APPzE1ETObscJ0vJnvPOMzPEPpsTR23CE7mO0z30jsreZP8YiKkB13OqwT2wWdcQlh1VKur95z2Xi4E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=onurozkan.dev; spf=pass smtp.mailfrom=onurozkan.dev; dkim=pass (1024-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b=XPxrepaM; arc=none smtp.client-ip=178.154.239.92 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b="XPxrepaM" Received: from forward100a.mail.yandex.net (forward100a.mail.yandex.net [IPv6:2a02:6b8:c0e:500:1:45:d181:d100]) by forward201a.mail.yandex.net (Yandex) with ESMTPS id EFEDA87B3E; Wed, 03 Sep 2025 16:13:53 +0300 (MSK) Received: from mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net (mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net [IPv6:2a02:6b8:c1d:3e17:0:640:f9aa:0]) by forward100a.mail.yandex.net (Yandex) with ESMTPS id 7D475C0072; Wed, 03 Sep 2025 16:13:46 +0300 (MSK) Received: by mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net (smtp/Yandex) with ESMTPSA id GDgKkBDMquQ0-MqKpFOhl; Wed, 03 Sep 2025 16:13:45 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=onurozkan.dev; s=mail; t=1756905225; bh=iXnvkLKoz5DPGCrAQJ3fuzmev6s3f2CTHUPCTUNUIkY=; h=Cc:Message-ID:References:Date:In-Reply-To:Subject:To:From; b=XPxrepaMdCHUvTzy1AZApYPzymxbbpRMT2czG5cYIqzs6mFZILrvmMblLzJdWCVkx MeFEp/TxoT8qHwwY/UMAGuYzvmYgUJHbFXu/9eyiKTacjMDae40cAnxDx1hnB2rzMf lYJtXYAfCHuOyxi0WWbX+H5mDcy382CLawBUICcQ= Authentication-Results: mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net; dkim=pass header.i=@onurozkan.dev From: =?UTF-8?q?Onur=20=C3=96zkan?= To: rust-for-linux@vger.kernel.org Cc: linux-kernel@vger.kernel.org, lossin@kernel.org, lyude@redhat.com, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, dakr@kernel.org, peterz@infradead.org, mingo@redhat.com, will@kernel.org, longman@redhat.com, felipe_life@live.com, daniel@sedlak.dev, bjorn3_gh@protonmail.com, daniel.almeida@collabora.com, =?UTF-8?q?Onur=20=C3=96zkan?= Subject: [PATCH v6 3/7] rust: implement `WwMutex`, `WwAcquireCtx` and `WwMutexGuard` Date: Wed, 3 Sep 2025 16:13:09 +0300 Message-ID: <20250903131313.4365-4-work@onurozkan.dev> X-Mailer: git-send-email 2.50.0 In-Reply-To: <20250903131313.4365-1-work@onurozkan.dev> References: <20250903131313.4365-1-work@onurozkan.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Includes full locking API (lock, try_lock, slow path, interruptible variant= s) and integration with kernel bindings. Also adds the `EDEADLK` error code to support deadlock detection. Signed-off-by: Onur =C3=96zkan --- rust/kernel/error.rs | 1 + rust/kernel/sync/lock/ww_mutex.rs | 289 +++++++++++++++++++++++++++++- 2 files changed, 289 insertions(+), 1 deletion(-) diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index a41de293dcd1..560de6117094 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -64,6 +64,7 @@ macro_rules! declare_err { declare_err!(EPIPE, "Broken pipe."); declare_err!(EDOM, "Math argument out of domain of func."); declare_err!(ERANGE, "Math result not representable."); + declare_err!(EDEADLK, "Resource deadlock avoided."); declare_err!(EOVERFLOW, "Value too large for defined data type."); declare_err!(ETIMEDOUT, "Connection timed out."); declare_err!(ERESTARTSYS, "Restart the system call."); diff --git a/rust/kernel/sync/lock/ww_mutex.rs b/rust/kernel/sync/lock/ww_m= utex.rs index ca5b4525ace6..314360632953 100644 --- a/rust/kernel/sync/lock/ww_mutex.rs +++ b/rust/kernel/sync/lock/ww_mutex.rs @@ -10,8 +10,11 @@ //! For more information: use crate::bindings; +use crate::error::to_result; use crate::prelude::*; -use crate::types::Opaque; +use crate::types::{NotThreadSafe, Opaque}; +use core::cell::UnsafeCell; +use core::marker::PhantomData; /// Create static [`WwClass`] instances. /// @@ -134,3 +137,287 @@ pub fn new_wound_wait(name: &'static CStr) -> impl Pi= nInit { Self::new(name, false) } } + +/// Groups multiple mutex acquisitions together for deadlock avoidance. +/// +/// Must be used when acquiring multiple mutexes of the same class. +/// +/// # Examples +/// +/// ``` +/// use kernel::sync::lock::ww_mutex::{WwClass, WwAcquireCtx, WwMutex}; +/// use kernel::c_str; +/// use kernel::sync::Arc; +/// use pin_init::stack_pin_init; +/// +/// stack_pin_init!(let class =3D WwClass::new_wound_wait(c_str!("my_class= "))); +/// +/// // Create mutexes. +/// let mutex1 =3D Arc::pin_init(WwMutex::new(1, &class), GFP_KERNEL)?; +/// let mutex2 =3D Arc::pin_init(WwMutex::new(2, &class), GFP_KERNEL)?; +/// +/// // Create acquire context for deadlock avoidance. +/// let ctx =3D KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?; +/// +/// // Acquire multiple locks safely. +/// let guard1 =3D ctx.lock(&mutex1)?; +/// let guard2 =3D ctx.lock(&mutex2)?; +/// +/// // Mark acquisition phase as complete. +/// ctx.done(); +/// +/// # Ok::<(), Error>(()) +/// ``` +#[pin_data(PinnedDrop)] +pub struct WwAcquireCtx<'a> { + #[pin] + inner: Opaque, + _p: PhantomData<&'a WwClass>, +} + +impl<'ww_class> WwAcquireCtx<'ww_class> { + /// Initializes `Self` with calling C side `ww_acquire_init` inside. + pub fn new(ww_class: &'ww_class WwClass) -> impl PinInit { + let class =3D ww_class.inner.get(); + pin_init!(WwAcquireCtx { + inner <- Opaque::ffi_init(|slot: *mut bindings::ww_acquire_ctx= | { + // SAFETY: `ww_class` is valid for the lifetime `'ww_class= ` captured by `Self`. + unsafe { bindings::ww_acquire_init(slot, class) } + }), + _p: PhantomData + }) + } + + /// Marks the end of the acquire phase. + /// + /// After calling this function, no more mutexes can be acquired with = this context. + pub fn done(&self) { + // SAFETY: The context is pinned and valid. + unsafe { bindings::ww_acquire_done(self.inner.get()) }; + } + + /// Locks the given mutex. + pub fn lock<'a, T>(&'a self, ww_mutex: &'a WwMutex<'a, T>) -> Result> { + // SAFETY: The mutex is pinned and valid. + let ret =3D unsafe { bindings::ww_mutex_lock(ww_mutex.mutex.get(),= self.inner.get()) }; + + to_result(ret)?; + + Ok(WwMutexGuard::new(ww_mutex)) + } + + /// Similar to `lock`, but can be interrupted by signals. + pub fn lock_interruptible<'a, T>( + &'a self, + ww_mutex: &'a WwMutex<'a, T>, + ) -> Result> { + // SAFETY: The mutex is pinned and valid. + let ret =3D unsafe { + bindings::ww_mutex_lock_interruptible(ww_mutex.mutex.get(), se= lf.inner.get()) + }; + + to_result(ret)?; + + Ok(WwMutexGuard::new(ww_mutex)) + } + + /// Locks the given mutex using the slow path. + /// + /// This function should be used when `lock` fails (typically due to a= potential deadlock). + pub fn lock_slow<'a, T>(&'a self, ww_mutex: &'a WwMutex<'a, T>) -> Res= ult> { + // SAFETY: The mutex is pinned and valid, and we're in the slow pa= th. + unsafe { bindings::ww_mutex_lock_slow(ww_mutex.mutex.get(), self.i= nner.get()) }; + + Ok(WwMutexGuard::new(ww_mutex)) + } + + /// Similar to `lock_slow`, but can be interrupted by signals. + pub fn lock_slow_interruptible<'a, T>( + &'a self, + ww_mutex: &'a WwMutex<'a, T>, + ) -> Result> { + // SAFETY: The mutex is pinned and valid, and we are in the slow p= ath. + let ret =3D unsafe { + bindings::ww_mutex_lock_slow_interruptible(ww_mutex.mutex.get(= ), self.inner.get()) + }; + + to_result(ret)?; + + Ok(WwMutexGuard::new(ww_mutex)) + } + + /// Tries to lock the mutex without blocking. + /// + /// Unlike `lock`, no deadlock handling is performed. + pub fn try_lock<'a, T>(&'a self, ww_mutex: &'a WwMutex<'a, T>) -> Resu= lt> { + // SAFETY: The mutex is pinned and valid. + let ret =3D unsafe { bindings::ww_mutex_trylock(ww_mutex.mutex.get= (), self.inner.get()) }; + + if ret =3D=3D 0 { + return Err(EBUSY); + } else { + to_result(ret)?; + } + + Ok(WwMutexGuard::new(ww_mutex)) + } +} + +#[pinned_drop] +impl PinnedDrop for WwAcquireCtx<'_> { + fn drop(self: Pin<&mut Self>) { + // SAFETY: The context is being dropped and is pinned. + unsafe { bindings::ww_acquire_fini(self.inner.get()) }; + } +} + +/// A wound/wait mutex backed with C side `ww_mutex`. +/// +/// This is a mutual exclusion primitive that provides deadlock avoidance = when +/// acquiring multiple locks of the same class. +/// +/// # Examples +/// +/// ## Basic Usage +/// +/// ``` +/// use kernel::c_str; +/// use kernel::sync::Arc; +/// use kernel::sync::lock::ww_mutex::{WwClass, WwAcquireCtx, WwMutex }; +/// use pin_init::stack_pin_init; +/// +/// stack_pin_init!(let class =3D WwClass::new_wound_wait(c_str!("buffer_c= lass"))); +/// let mutex =3D Arc::pin_init(WwMutex::new(42, &class), GFP_KERNEL)?; +/// +/// let ctx =3D KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?; +/// +/// let guard =3D ctx.lock(&mutex)?; +/// assert_eq!(*guard, 42); +/// +/// # Ok::<(), Error>(()) +/// ``` +/// +/// ## Multiple Locks +/// +/// ``` +/// use kernel::c_str; +/// use kernel::prelude::*; +/// use kernel::sync::Arc; +/// use kernel::sync::lock::ww_mutex::{WwClass, WwAcquireCtx, WwMutex}; +/// use pin_init::stack_pin_init; +/// +/// stack_pin_init!(let class =3D WwClass::new_wait_die(c_str!("resource_c= lass"))); +/// let mutex_a =3D Arc::pin_init(WwMutex::new("Resource A", &class), GFP_= KERNEL)?; +/// let mutex_b =3D Arc::pin_init(WwMutex::new("Resource B", &class), GFP_= KERNEL)?; +/// +/// let ctx =3D KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?; +/// +/// // Try to acquire both locks. +/// let guard_a =3D match ctx.lock(&mutex_a) { +/// Ok(guard) =3D> guard, +/// Err(e) if e =3D=3D EDEADLK =3D> { +/// // Deadlock detected, use slow path. +/// ctx.lock_slow(&mutex_a)? +/// } +/// Err(e) =3D> return Err(e), +/// }; +/// +/// let guard_b =3D ctx.lock(&mutex_b)?; +/// ctx.done(); +/// +/// # Ok::<(), Error>(()) +/// ``` +#[pin_data] +pub struct WwMutex<'a, T: ?Sized> { + _p: PhantomData<&'a WwClass>, + #[pin] + mutex: Opaque, + data: UnsafeCell, +} + +// SAFETY: [`WwMutex`] can be shared between threads. +unsafe impl Send for WwMutex<'_, T> {} +// SAFETY: [`WwMutex`] can be safely accessed from multiple threads concur= rently. +unsafe impl Sync for WwMutex<'_, T> {} + +impl<'ww_class, T> WwMutex<'ww_class, T> { + /// Creates `Self` with calling `ww_mutex_init` inside. + pub fn new(t: T, ww_class: &'ww_class WwClass) -> impl PinInit { + let class =3D ww_class.inner.get(); + pin_init!(WwMutex { + mutex <- Opaque::ffi_init(|slot: *mut bindings::ww_mutex| { + // SAFETY: `ww_class` is valid for the lifetime `'ww_class= ` captured by `Self`. + unsafe { bindings::ww_mutex_init(slot, class) } + }), + data: UnsafeCell::new(t), + _p: PhantomData, + }) + } +} + +impl WwMutex<'_, T> { + /// Returns a raw pointer to the inner mutex. + fn as_ptr(&self) -> *mut bindings::ww_mutex { + self.mutex.get() + } + + /// Checks if the mutex is currently locked. + /// + /// Intended for internal tests only and should not be used + /// anywhere else. + #[cfg(CONFIG_KUNIT)] + fn is_locked(&self) -> bool { + // SAFETY: The mutex is pinned and valid. + unsafe { bindings::ww_mutex_is_locked(self.mutex.get()) } + } +} + +/// A guard that provides exclusive access to the data protected +/// by a [`WwMutex`]. +/// +/// # Invariants +/// +/// The guard holds an exclusive lock on the associated [`WwMutex`]. The l= ock is held +/// for the entire lifetime of this guard and is automatically released wh= en the +/// guard is dropped. +#[must_use =3D "the lock unlocks immediately when the guard is unused"] +pub struct WwMutexGuard<'a, T: ?Sized> { + mutex: &'a WwMutex<'a, T>, + _not_send: NotThreadSafe, +} + +// SAFETY: [`WwMutexGuard`] can be shared between threads if the data can. +unsafe impl Sync for WwMutexGuard<'_, T> {} + +impl<'a, T: ?Sized> WwMutexGuard<'a, T> { + /// Creates a new guard for a locked mutex. + fn new(mutex: &'a WwMutex<'a, T>) -> Self { + Self { + mutex, + _not_send: NotThreadSafe, + } + } +} + +impl core::ops::Deref for WwMutexGuard<'_, T> { + type Target =3D T; + + fn deref(&self) -> &Self::Target { + // SAFETY: We hold the lock, so we have exclusive access. + unsafe { &*self.mutex.data.get() } + } +} + +impl core::ops::DerefMut for WwMutexGuard<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: We hold the lock, so we have exclusive access. + unsafe { &mut *self.mutex.data.get() } + } +} + +impl Drop for WwMutexGuard<'_, T> { + fn drop(&mut self) { + // SAFETY: We hold the lock and are about to release it. + unsafe { bindings::ww_mutex_unlock(self.mutex.as_ptr()) }; + } +} -- 2.50.0 From nobody Fri Oct 3 07:42:39 2025 Received: from forward206a.mail.yandex.net (forward206a.mail.yandex.net [178.154.239.87]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B43D32ED84F; Wed, 3 Sep 2025 13:14:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.154.239.87 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756905243; cv=none; b=O0egUAcNxb054Arapa5jaKekUPMgLPduKjjb39WPNaOpc3RC6RMa8sEPTBMaai83UbOldeTxpQ3LR+6p3xuu7wy8T6/0cVH29WoGWuOHQFsMMMODTHnStUB8EHVk9E/gbmj0F0oGz8Vc/oJ7kVfzTg1tjoAxICOOK+bVv9Mw1SY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756905243; c=relaxed/simple; bh=roSDQADUv3RkSm0N6OGbYxrmyfPumK04X12VD6UR6xs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=pYdTcR6DnebB7PWrhASjxjKhqyW7EEhQir+7RhyJfOWVDxVZAI4ZoQHjBiK0VUsCpvLPV1Iz9xTgQ7lhWWBTr2tlwJzuSn/oR8drxAaqD1aRKJAuuHK14Yng+PCai6u8DTXTu7d8IwFBrbjrfcdrTjVOV8oyyCerG75d+RfU7SQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=onurozkan.dev; spf=pass smtp.mailfrom=onurozkan.dev; dkim=pass (1024-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b=gdFb9T+g; arc=none smtp.client-ip=178.154.239.87 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b="gdFb9T+g" Received: from forward101a.mail.yandex.net (forward101a.mail.yandex.net [IPv6:2a02:6b8:c0e:500:1:45:d181:d101]) by forward206a.mail.yandex.net (Yandex) with ESMTPS id AC46F81418; Wed, 03 Sep 2025 16:13:58 +0300 (MSK) Received: from mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net (mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net [IPv6:2a02:6b8:c1d:3e17:0:640:f9aa:0]) by forward101a.mail.yandex.net (Yandex) with ESMTPS id 5F97280CBD; Wed, 03 Sep 2025 16:13:51 +0300 (MSK) Received: by mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net (smtp/Yandex) with ESMTPSA id GDgKkBDMquQ0-qWxoUOlp; Wed, 03 Sep 2025 16:13:50 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=onurozkan.dev; s=mail; t=1756905230; bh=Hia1mfGHTnbizHC76TE+aNhM8CV8JljuaRKoDPy92gY=; h=Cc:Message-ID:References:Date:In-Reply-To:Subject:To:From; b=gdFb9T+gfHkHybRLuFgIyx+F3Ea1fFRNx6lL6SOykb0joH3XUWzkjHzoiokt22mWJ tpeG+OQ+TGpUP0KgM/3jzC1b+1XoHJThpzmMfmjYh9GWYKbLkftA04rMe78534XaCF QDGXl2vyB5PaXLNeBg2bmNySYjsqZE7u4jDs1oAU= Authentication-Results: mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net; dkim=pass header.i=@onurozkan.dev From: =?UTF-8?q?Onur=20=C3=96zkan?= To: rust-for-linux@vger.kernel.org Cc: linux-kernel@vger.kernel.org, lossin@kernel.org, lyude@redhat.com, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, dakr@kernel.org, peterz@infradead.org, mingo@redhat.com, will@kernel.org, longman@redhat.com, felipe_life@live.com, daniel@sedlak.dev, bjorn3_gh@protonmail.com, daniel.almeida@collabora.com, =?UTF-8?q?Onur=20=C3=96zkan?= Subject: [PATCH v6 4/7] add KUnit coverage on Rust ww_mutex implementation Date: Wed, 3 Sep 2025 16:13:10 +0300 Message-ID: <20250903131313.4365-5-work@onurozkan.dev> X-Mailer: git-send-email 2.50.0 In-Reply-To: <20250903131313.4365-1-work@onurozkan.dev> References: <20250903131313.4365-1-work@onurozkan.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Adds coverage around the core `ww_mutex` functionality Signed-off-by: Onur =C3=96zkan --- rust/kernel/sync/lock/ww_mutex.rs | 127 ++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/rust/kernel/sync/lock/ww_mutex.rs b/rust/kernel/sync/lock/ww_m= utex.rs index 314360632953..d289718d2c98 100644 --- a/rust/kernel/sync/lock/ww_mutex.rs +++ b/rust/kernel/sync/lock/ww_mutex.rs @@ -421,3 +421,130 @@ fn drop(&mut self) { unsafe { bindings::ww_mutex_unlock(self.mutex.as_ptr()) }; } } + +#[kunit_tests(rust_kernel_ww_mutex)] +mod tests { + use crate::c_str; + use crate::prelude::*; + use crate::sync::Arc; + use pin_init::stack_pin_init; + + use super::*; + + // A simple coverage on `define_ww_class` macro. + define_ww_class!(TEST_WOUND_WAIT_CLASS, wound_wait, c_str!("test_wound= _wait")); + define_ww_class!(TEST_WAIT_DIE_CLASS, wait_die, c_str!("test_wait_die"= )); + + #[test] + fn test_ww_mutex_basic_lock_unlock() -> Result { + stack_pin_init!(let class =3D WwClass::new_wound_wait(c_str!("test= _mutex_class"))); + + let mutex =3D Arc::pin_init(WwMutex::new(42, &class), GFP_KERNEL)?; + + let ctx =3D KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?; + + // Lock. + let guard =3D ctx.lock(&mutex)?; + assert_eq!(*guard, 42); + + // Drop the lock. + drop(guard); + + // Lock it again. + let mut guard =3D ctx.lock(&mutex)?; + *guard =3D 100; + assert_eq!(*guard, 100); + + Ok(()) + } + + #[test] + fn test_ww_mutex_trylock() -> Result { + stack_pin_init!(let class =3D WwClass::new_wound_wait(c_str!("tryl= ock_class"))); + + let mutex =3D Arc::pin_init(WwMutex::new(123, &class), GFP_KERNEL)= ?; + + let ctx =3D KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?; + + // `try_lock` on unlocked mutex should succeed. + let guard =3D ctx.try_lock(&mutex)?; + assert_eq!(*guard, 123); + + // Now it should fail immediately as it's already locked. + assert!(ctx.try_lock(&mutex).is_err()); + + Ok(()) + } + + #[test] + fn test_ww_mutex_is_locked() -> Result { + stack_pin_init!(let class =3D WwClass::new_wait_die(c_str!("locked= _check_class"))); + + let mutex =3D Arc::pin_init(WwMutex::new("hello", &class), GFP_KER= NEL)?; + + let ctx =3D KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?; + + // Should not be locked initially. + assert!(!mutex.is_locked()); + + let guard =3D ctx.lock(&mutex)?; + assert!(mutex.is_locked()); + + drop(guard); + assert!(!mutex.is_locked()); + + Ok(()) + } + + #[test] + fn test_ww_acquire_context() -> Result { + stack_pin_init!(let class =3D WwClass::new_wound_wait(c_str!("ctx_= class"))); + + let mutex1 =3D Arc::pin_init(WwMutex::new(1, &class), GFP_KERNEL)?; + let mutex2 =3D Arc::pin_init(WwMutex::new(2, &class), GFP_KERNEL)?; + + let ctx =3D KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?; + + // Acquire multiple mutexes with the same context. + let guard1 =3D ctx.lock(&mutex1)?; + let guard2 =3D ctx.lock(&mutex2)?; + + assert_eq!(*guard1, 1); + assert_eq!(*guard2, 2); + + ctx.done(); + + // We shouldn't be able to lock once it's `done`. + assert!(ctx.lock(&mutex1).is_err()); + assert!(ctx.lock(&mutex2).is_err()); + + Ok(()) + } + + #[test] + fn test_with_global_classes() -> Result { + let wound_wait_mutex =3D + Arc::pin_init(WwMutex::new(100, &TEST_WOUND_WAIT_CLASS), GFP_K= ERNEL)?; + let wait_die_mutex =3D Arc::pin_init(WwMutex::new(200, &TEST_WAIT_= DIE_CLASS), GFP_KERNEL)?; + + let ww_ctx =3D KBox::pin_init(WwAcquireCtx::new(&TEST_WOUND_WAIT_C= LASS), GFP_KERNEL)?; + let wd_ctx =3D KBox::pin_init(WwAcquireCtx::new(&TEST_WAIT_DIE_CLA= SS), GFP_KERNEL)?; + + let ww_guard =3D ww_ctx.lock(&wound_wait_mutex)?; + let wd_guard =3D wd_ctx.lock(&wait_die_mutex)?; + + assert_eq!(*ww_guard, 100); + assert_eq!(*wd_guard, 200); + + assert!(wound_wait_mutex.is_locked()); + assert!(wait_die_mutex.is_locked()); + + drop(ww_guard); + drop(wd_guard); + + assert!(!wound_wait_mutex.is_locked()); + assert!(!wait_die_mutex.is_locked()); + + Ok(()) + } +} -- 2.50.0 From nobody Fri Oct 3 07:42:39 2025 Received: from forward200a.mail.yandex.net (forward200a.mail.yandex.net [178.154.239.93]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BBC8C302776; Wed, 3 Sep 2025 13:21:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.154.239.93 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756905714; cv=none; b=rmqTEeWx2+0DyAMXaZrAVD77YGvv93IIWy/rcnUdrZQ3CdxVBZwgTYD3ZNeMgEgtUQIYsTwP8QJffmz+qnYkTRSVmQvSo//p3YOnLKk2aoSp3o97zh+v8vKyv4L/yZwQrxML644KvOiAibEZBNsPs74qiEItKsRwNOCnIJUIHWI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756905714; c=relaxed/simple; bh=241EcV2ADYh/RMMNn/20PxWBFaGYRKkSQEUt0Gl0+Jo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=VOoiX4ehLXXaO7juygrlxiN5Lx2OT4Iy4/whc0yTD0pEoQOJAwHuvSWjvSLyFoHnEz1w8xPuq1KW8LmiJhUZ4ht4LD4Iw016veia1Zy698mDmRbGYKpoQsw+AYbbpLp/E7Zv1FD6eolXrQbFfklt/LL+aaTZ6SLalkNzC4wyhFU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=onurozkan.dev; spf=pass smtp.mailfrom=onurozkan.dev; dkim=pass (1024-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b=INEbLN0+; arc=none smtp.client-ip=178.154.239.93 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b="INEbLN0+" Received: from forward101a.mail.yandex.net (forward101a.mail.yandex.net [IPv6:2a02:6b8:c0e:500:1:45:d181:d101]) by forward200a.mail.yandex.net (Yandex) with ESMTPS id 069AF87274; Wed, 03 Sep 2025 16:14:04 +0300 (MSK) Received: from mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net (mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net [IPv6:2a02:6b8:c1d:3e17:0:640:f9aa:0]) by forward101a.mail.yandex.net (Yandex) with ESMTPS id 1AAE980CE4; Wed, 03 Sep 2025 16:13:56 +0300 (MSK) Received: by mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net (smtp/Yandex) with ESMTPSA id GDgKkBDMquQ0-nq4gd7wA; Wed, 03 Sep 2025 16:13:55 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=onurozkan.dev; s=mail; t=1756905235; bh=3cG9UNuKFe5wpsh/oW1uFyaeMYCPP67dHmjUcqImnK0=; h=Cc:Message-ID:References:Date:In-Reply-To:Subject:To:From; b=INEbLN0+31LQnfKTu6qCQA0bkNM1JObFtzmusCrZW6oKSVFLrimrWbzmKX4E7p73i uMZY53x+NyTt4F6JpBNEQPBcW5UuHuxVpvH+FIdea6Isf1Ru4sC7O24SZXUxQd4qqY QhjTt+KRLPruxeegaFOC+Er4xEr23AUgSz1FlXVc= Authentication-Results: mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net; dkim=pass header.i=@onurozkan.dev From: =?UTF-8?q?Onur=20=C3=96zkan?= To: rust-for-linux@vger.kernel.org Cc: linux-kernel@vger.kernel.org, lossin@kernel.org, lyude@redhat.com, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, dakr@kernel.org, peterz@infradead.org, mingo@redhat.com, will@kernel.org, longman@redhat.com, felipe_life@live.com, daniel@sedlak.dev, bjorn3_gh@protonmail.com, daniel.almeida@collabora.com, =?UTF-8?q?Onur=20=C3=96zkan?= Subject: [PATCH v6 5/7] rust: ww_mutex: add context-free locking functions Date: Wed, 3 Sep 2025 16:13:11 +0300 Message-ID: <20250903131313.4365-6-work@onurozkan.dev> X-Mailer: git-send-email 2.50.0 In-Reply-To: <20250903131313.4365-1-work@onurozkan.dev> References: <20250903131313.4365-1-work@onurozkan.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Adds new `WwMutex` functions (`lock`, `lock_interruptible`, `lock_slow`, `lock_slow_interruptible` and `try_lock`) that can be used without `WwAcquireCtx`. This provides simpler API when deadlock avoidance grouping is not needed. Signed-off-by: Onur =C3=96zkan --- rust/kernel/sync/lock/ww_mutex.rs | 162 ++++++++++++++++++++++-------- 1 file changed, 122 insertions(+), 40 deletions(-) diff --git a/rust/kernel/sync/lock/ww_mutex.rs b/rust/kernel/sync/lock/ww_m= utex.rs index d289718d2c98..b415d6deae9b 100644 --- a/rust/kernel/sync/lock/ww_mutex.rs +++ b/rust/kernel/sync/lock/ww_mutex.rs @@ -138,6 +138,75 @@ pub fn new_wound_wait(name: &'static CStr) -> impl Pin= Init { } } +/// Locking kinds used by [`lock_common`] to unify internal FFI locking lo= gic. +#[derive(Copy, Clone, Debug)] +enum LockKind { + /// Blocks until lock is acquired. + Regular, + /// Blocks but can be interrupted by signals. + Interruptible, + /// Used in slow path after deadlock detection. + Slow, + /// Slow path but interruptible. + SlowInterruptible, + /// Does not block, returns immediately if busy. + Try, +} + +/// Internal helper that unifies the different locking kinds. +fn lock_common<'a, T: ?Sized>( + ww_mutex: &'a WwMutex<'a, T>, + ctx: Option<&WwAcquireCtx<'_>>, + kind: LockKind, +) -> Result> { + let ctx_ptr =3D ctx.map_or(core::ptr::null_mut(), |c| c.inner.get()); + + match kind { + LockKind::Regular =3D> { + // SAFETY: `WwMutex` is always pinned. If `WwAcquireCtx` is `S= ome`, it is pinned, + // if `None`, it is set to `core::ptr::null_mut()`. Both cases= are safe. + let ret =3D unsafe { bindings::ww_mutex_lock(ww_mutex.mutex.ge= t(), ctx_ptr) }; + + to_result(ret)?; + } + LockKind::Interruptible =3D> { + // SAFETY: `WwMutex` is always pinned. If `WwAcquireCtx` is `S= ome`, it is pinned, + // if `None`, it is set to `core::ptr::null_mut()`. Both cases= are safe. + let ret =3D + unsafe { bindings::ww_mutex_lock_interruptible(ww_mutex.mu= tex.get(), ctx_ptr) }; + + to_result(ret)?; + } + LockKind::Slow =3D> { + // SAFETY: `WwMutex` is always pinned. If `WwAcquireCtx` is `S= ome`, it is pinned, + // if `None`, it is set to `core::ptr::null_mut()`. Both cases= are safe. + unsafe { bindings::ww_mutex_lock_slow(ww_mutex.mutex.get(), ct= x_ptr) }; + } + LockKind::SlowInterruptible =3D> { + // SAFETY: `WwMutex` is always pinned. If `WwAcquireCtx` is `S= ome`, it is pinned, + // if `None`, it is set to `core::ptr::null_mut()`. Both cases= are safe. + let ret =3D unsafe { + bindings::ww_mutex_lock_slow_interruptible(ww_mutex.mutex.= get(), ctx_ptr) + }; + + to_result(ret)?; + } + LockKind::Try =3D> { + // SAFETY: `WwMutex` is always pinned. If `WwAcquireCtx` is `S= ome`, it is pinned, + // if `None`, it is set to `core::ptr::null_mut()`. Both cases= are safe. + let ret =3D unsafe { bindings::ww_mutex_trylock(ww_mutex.mutex= .get(), ctx_ptr) }; + + if ret =3D=3D 0 { + return Err(EBUSY); + } else { + to_result(ret)?; + } + } + }; + + Ok(WwMutexGuard::new(ww_mutex)) +} + /// Groups multiple mutex acquisitions together for deadlock avoidance. /// /// Must be used when acquiring multiple mutexes of the same class. @@ -196,14 +265,9 @@ pub fn done(&self) { unsafe { bindings::ww_acquire_done(self.inner.get()) }; } - /// Locks the given mutex. + /// Locks the given mutex on this acquire context ([`WwAcquireCtx`]). pub fn lock<'a, T>(&'a self, ww_mutex: &'a WwMutex<'a, T>) -> Result> { - // SAFETY: The mutex is pinned and valid. - let ret =3D unsafe { bindings::ww_mutex_lock(ww_mutex.mutex.get(),= self.inner.get()) }; - - to_result(ret)?; - - Ok(WwMutexGuard::new(ww_mutex)) + lock_common(ww_mutex, Some(self), LockKind::Regular) } /// Similar to `lock`, but can be interrupted by signals. @@ -211,24 +275,14 @@ pub fn lock_interruptible<'a, T>( &'a self, ww_mutex: &'a WwMutex<'a, T>, ) -> Result> { - // SAFETY: The mutex is pinned and valid. - let ret =3D unsafe { - bindings::ww_mutex_lock_interruptible(ww_mutex.mutex.get(), se= lf.inner.get()) - }; - - to_result(ret)?; - - Ok(WwMutexGuard::new(ww_mutex)) + lock_common(ww_mutex, Some(self), LockKind::Interruptible) } - /// Locks the given mutex using the slow path. + /// Locks the given mutex on this acquire context ([`WwAcquireCtx`]) u= sing the slow path. /// /// This function should be used when `lock` fails (typically due to a= potential deadlock). pub fn lock_slow<'a, T>(&'a self, ww_mutex: &'a WwMutex<'a, T>) -> Res= ult> { - // SAFETY: The mutex is pinned and valid, and we're in the slow pa= th. - unsafe { bindings::ww_mutex_lock_slow(ww_mutex.mutex.get(), self.i= nner.get()) }; - - Ok(WwMutexGuard::new(ww_mutex)) + lock_common(ww_mutex, Some(self), LockKind::Slow) } /// Similar to `lock_slow`, but can be interrupted by signals. @@ -236,30 +290,14 @@ pub fn lock_slow_interruptible<'a, T>( &'a self, ww_mutex: &'a WwMutex<'a, T>, ) -> Result> { - // SAFETY: The mutex is pinned and valid, and we are in the slow p= ath. - let ret =3D unsafe { - bindings::ww_mutex_lock_slow_interruptible(ww_mutex.mutex.get(= ), self.inner.get()) - }; - - to_result(ret)?; - - Ok(WwMutexGuard::new(ww_mutex)) + lock_common(ww_mutex, Some(self), LockKind::SlowInterruptible) } - /// Tries to lock the mutex without blocking. + /// Tries to lock the mutex on this acquire context ([`WwAcquireCtx`])= without blocking. /// /// Unlike `lock`, no deadlock handling is performed. pub fn try_lock<'a, T>(&'a self, ww_mutex: &'a WwMutex<'a, T>) -> Resu= lt> { - // SAFETY: The mutex is pinned and valid. - let ret =3D unsafe { bindings::ww_mutex_trylock(ww_mutex.mutex.get= (), self.inner.get()) }; - - if ret =3D=3D 0 { - return Err(EBUSY); - } else { - to_result(ret)?; - } - - Ok(WwMutexGuard::new(ww_mutex)) + lock_common(ww_mutex, Some(self), LockKind::Try) } } @@ -355,7 +393,7 @@ pub fn new(t: T, ww_class: &'ww_class WwClass) -> impl = PinInit { } } -impl WwMutex<'_, T> { +impl<'ww_class, T: ?Sized> WwMutex<'ww_class, T> { /// Returns a raw pointer to the inner mutex. fn as_ptr(&self) -> *mut bindings::ww_mutex { self.mutex.get() @@ -370,6 +408,35 @@ fn is_locked(&self) -> bool { // SAFETY: The mutex is pinned and valid. unsafe { bindings::ww_mutex_is_locked(self.mutex.get()) } } + + /// Locks the given mutex without acquire context ([`WwAcquireCtx`]). + pub fn lock<'a>(&'a self) -> Result> { + lock_common(self, None, LockKind::Regular) + } + + /// Similar to `lock`, but can be interrupted by signals. + pub fn lock_interruptible<'a>(&'a self) -> Result>= { + lock_common(self, None, LockKind::Interruptible) + } + + /// Locks the given mutex without acquire context ([`WwAcquireCtx`]) u= sing the slow path. + /// + /// This function should be used when `lock` fails (typically due to a= potential deadlock). + pub fn lock_slow<'a>(&'a self) -> Result> { + lock_common(self, None, LockKind::Slow) + } + + /// Similar to `lock_slow`, but can be interrupted by signals. + pub fn lock_slow_interruptible<'a>(&'a self) -> Result> { + lock_common(self, None, LockKind::SlowInterruptible) + } + + /// Tries to lock the mutex without acquire context ([`WwAcquireCtx`])= and without blocking. + /// + /// Unlike `lock`, no deadlock handling is performed. + pub fn try_lock<'a>(&'a self) -> Result> { + lock_common(self, None, LockKind::Try) + } } /// A guard that provides exclusive access to the data protected @@ -547,4 +614,19 @@ fn test_with_global_classes() -> Result { Ok(()) } + + #[test] + fn test_mutex_without_ctx() -> Result { + let mutex =3D Arc::pin_init(WwMutex::new(100, &TEST_WOUND_WAIT_CLA= SS), GFP_KERNEL)?; + let guard =3D mutex.lock()?; + + assert_eq!(*guard, 100); + assert!(mutex.is_locked()); + + drop(guard); + + assert!(!mutex.is_locked()); + + Ok(()) + } } -- 2.50.0 From nobody Fri Oct 3 07:42:39 2025 Received: from forward101a.mail.yandex.net (forward101a.mail.yandex.net [178.154.239.84]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8864F2FCBF3; Wed, 3 Sep 2025 13:14:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.154.239.84 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756905244; cv=none; b=MHQbXNKbz3K+eGgp+q3ef9sNJ5SZWKLSdcvjUWfa4uOrSN9v4uJISXgWW37+GXrv0TW3jTJ3pivzQ80RtWoIDxH/MGxdeF0IAVs2TZgxqG9O7JYVkrUKjEy02BeFo8o78f5LAMhjp4wqqUVpLaTZ5tB/m3JtLBWqiQtjz7y28LA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756905244; c=relaxed/simple; bh=8a1/RDIg8GBMVKRyF9jzPeWsj1yUL3ElTD6jBWjf+9I=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=O856Uq1NqOC9NvFJhqaWwcf/DZx9m0oPTmjnHq16ne18vfVxYF3K67aKanlZgexWK18iWKHaNriYYd+eiAN4MATEo01AqnlBElhJcyhtMKhvgdv9czp3ayiyeBAJgW9RQ3wGw1KlIM0wnf8/AD0KuCpkWdqFo+MemUN0ndIvGOo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=onurozkan.dev; spf=pass smtp.mailfrom=onurozkan.dev; dkim=pass (1024-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b=BoK71ECS; arc=none smtp.client-ip=178.154.239.84 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b="BoK71ECS" Received: from mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net (mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net [IPv6:2a02:6b8:c1d:3e17:0:640:f9aa:0]) by forward101a.mail.yandex.net (Yandex) with ESMTPS id EA1BA80667; Wed, 03 Sep 2025 16:14:00 +0300 (MSK) Received: by mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net (smtp/Yandex) with ESMTPSA id GDgKkBDMquQ0-wB0L7ghY; Wed, 03 Sep 2025 16:14:00 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=onurozkan.dev; s=mail; t=1756905240; bh=fX5tIadeGmsn1nuEvbzWpE8yuM5oc8WY6qQ0TIgO6+I=; h=Cc:Message-ID:References:Date:In-Reply-To:Subject:To:From; b=BoK71ECSSRAASnE0p7KIzD1Gxpg95AsBUPV3JmwQr2GW2zWWxZcOO7/9GR4DaIcLe ae2dZ8nNS14pKOW2CJRqwrY1O+r06c9qjIGWMugxPul/36ErELEMZYKwPdNifyTV2o z3yC4ylPLn8DexJjKZ/LqpH5cc1Pdg8g1l6UiUrs= Authentication-Results: mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net; dkim=pass header.i=@onurozkan.dev From: =?UTF-8?q?Onur=20=C3=96zkan?= To: rust-for-linux@vger.kernel.org Cc: linux-kernel@vger.kernel.org, lossin@kernel.org, lyude@redhat.com, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, dakr@kernel.org, peterz@infradead.org, mingo@redhat.com, will@kernel.org, longman@redhat.com, felipe_life@live.com, daniel@sedlak.dev, bjorn3_gh@protonmail.com, daniel.almeida@collabora.com, =?UTF-8?q?Onur=20=C3=96zkan?= Subject: [PATCH v6 6/7] rust: ww_mutex/exec: add high-level API Date: Wed, 3 Sep 2025 16:13:12 +0300 Message-ID: <20250903131313.4365-7-work@onurozkan.dev> X-Mailer: git-send-email 2.50.0 In-Reply-To: <20250903131313.4365-1-work@onurozkan.dev> References: <20250903131313.4365-1-work@onurozkan.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable `ExecContext` is a helper built on top of ww_mutex that provides a retrying interface for lock acquisition. When `EDEADLK` is hit, it drops all held locks, resets the acquire context and retries the given (by the user) locking algorithm until it succeeds. The API keeps track of acquired locks, cleans them up automatically and allows data access to the protected data through `with_locked()`. The `lock_all()` helper allows implementing multi-mutex algorithms in a simpler and less error-prone way while keeping the ww_mutex semantics. Signed-off-by: Onur =C3=96zkan Reviewed-by: Elle Rhumsaa --- rust/kernel/sync/lock/ww_mutex.rs | 2 + rust/kernel/sync/lock/ww_mutex/exec.rs | 176 +++++++++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 rust/kernel/sync/lock/ww_mutex/exec.rs diff --git a/rust/kernel/sync/lock/ww_mutex.rs b/rust/kernel/sync/lock/ww_m= utex.rs index b415d6deae9b..7de6578513e5 100644 --- a/rust/kernel/sync/lock/ww_mutex.rs +++ b/rust/kernel/sync/lock/ww_mutex.rs @@ -16,6 +16,8 @@ use core::cell::UnsafeCell; use core::marker::PhantomData; +pub mod exec; + /// Create static [`WwClass`] instances. /// /// # Examples diff --git a/rust/kernel/sync/lock/ww_mutex/exec.rs b/rust/kernel/sync/lock= /ww_mutex/exec.rs new file mode 100644 index 000000000000..2f1fc540f0b8 --- /dev/null +++ b/rust/kernel/sync/lock/ww_mutex/exec.rs @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! A high-level [`WwMutex`] execution helper. +//! +//! Provides a retrying lock mechanism on top of [`WwMutex`] and [`WwAcqui= reCtx`]. +//! It detects [`EDEADLK`] and handles it by rolling back and retrying the +//! user-supplied locking algorithm until success. + +use crate::prelude::*; +use crate::sync::lock::ww_mutex::{WwAcquireCtx, WwClass, WwMutex, WwMutexG= uard}; +use core::ptr; + +/// High-level execution type for ww_mutex. +/// +/// Tracks a series of locks acquired under a common [`WwAcquireCtx`]. +/// It ensures proper cleanup and retry mechanism on deadlocks and provides +/// type-safe access to locked data via [`with_locked`]. +/// +/// Typical usage is through [`lock_all`], which retries a user-supplied +/// locking algorithm until it succeeds without deadlock. +pub struct ExecContext<'a> { + class: &'a WwClass, + acquire: Pin>>, + taken: KVec>, +} + +impl<'a> Drop for ExecContext<'a> { + fn drop(&mut self) { + self.release_all_locks(); + } +} + +impl<'a> ExecContext<'a> { + /// Creates a new [`ExecContext`] for the given lock class. + /// + /// All locks taken through this context must belong to the same class. + /// + /// TODO: Add some safety mechanism to ensure classes are not differen= t. + pub fn new(class: &'a WwClass) -> Result { + Ok(Self { + class, + acquire: KBox::pin_init(WwAcquireCtx::new(class), GFP_KERNEL)?, + taken: KVec::new(), + }) + } + + /// Attempts to lock a [`WwMutex`] and records the guard. + /// + /// Returns [`EDEADLK`] if lock ordering would cause a deadlock. + pub fn lock(&mut self, mutex: &'a WwMutex<'a, T>) -> Result<()> { + let guard =3D self.acquire.lock(mutex)?; + // SAFETY: Type is erased for storage. Actual access uses `with_lo= cked` + // which safely casts back. + let erased: WwMutexGuard<'a, ()> =3D unsafe { core::mem::transmute= (guard) }; + self.taken.push(erased, GFP_KERNEL)?; + + Ok(()) + } + + /// Runs `locking_algorithm` until success with retrying on deadlock. + /// + /// `locking_algorithm` should attempt to acquire all needed locks. + /// If [`EDEADLK`] is detected, this function will roll back, reset + /// the context and retry automatically. + /// + /// Once all locks are acquired successfully, `on_all_locks_taken` is + /// invoked for exclusive access to the locked values. Afterwards, all + /// locks are released. + /// + /// # Example + /// + /// ``` + /// use kernel::alloc::KBox; + /// use kernel::c_str; + /// use kernel::prelude::*; + /// use kernel::sync::Arc; + /// use kernel::sync::lock::ww_mutex; + /// use pin_init::stack_pin_init; + /// + /// stack_pin_init!(let class =3D ww_mutex::WwClass::new_wound_wait(c_= str!("lock_all_example"))); + /// + /// let mutex1 =3D Arc::pin_init(ww_mutex::WwMutex::new(0, &class), GF= P_KERNEL)?; + /// let mutex2 =3D Arc::pin_init(ww_mutex::WwMutex::new(0, &class), GF= P_KERNEL)?; + /// let mut ctx =3D KBox::pin_init(ww_mutex::exec::ExecContext::new(&c= lass)?, GFP_KERNEL)?; + /// + /// ctx.lock_all( + /// |ctx| { + /// // Try to lock both mutexes. + /// ctx.lock(&mutex1)?; + /// ctx.lock(&mutex2)?; + /// + /// Ok(()) + /// }, + /// |ctx| { + /// // Safely mutate both values while holding the locks. + /// ctx.with_locked(&mutex1, |v| *v +=3D 1)?; + /// ctx.with_locked(&mutex2, |v| *v +=3D 1)?; + /// + /// Ok(()) + /// }, + /// )?; + /// + /// # Ok::<(), Error>(()) + /// ``` + pub fn lock_all( + &mut self, + mut locking_algorithm: T, + mut on_all_locks_taken: Y, + ) -> Result + where + T: FnMut(&mut ExecContext<'a>) -> Result<()>, + Y: FnMut(&mut ExecContext<'a>) -> Result, + { + loop { + match locking_algorithm(self) { + Ok(()) =3D> { + // All locks in `locking_algorithm` succeeded. + // The user can now safely use them in `on_all_locks_t= aken`. + let res =3D on_all_locks_taken(self); + self.release_all_locks(); + + return res; + } + Err(e) if e =3D=3D EDEADLK =3D> { + // Deadlock detected, retry from scratch. + self.cleanup_on_deadlock()?; + continue; + } + Err(e) =3D> { + return Err(e); + } + } + } + } + + /// Executes `f` with a mutable reference to the data behind `mutex`. + /// + /// Fails with [`EINVAL`] if the mutex was not locked in this context. + pub fn with_locked( + &mut self, + mutex: &'a WwMutex<'a, T>, + f: impl FnOnce(&mut T) -> Y, + ) -> Result { + // Find the matching guard. + for guard in &mut self.taken { + if mutex.as_ptr() =3D=3D guard.mutex.as_ptr() { + // SAFETY: We know this guard belongs to `mutex` and holds= the lock. + let typed =3D unsafe { &mut *ptr::from_mut(guard).cast::>() }; + return Ok(f(&mut **typed)); + } + } + + // `mutex` isn't locked in this `ExecContext`. + Err(EINVAL) + } + + /// Releases all currently held locks in this context. + /// + /// It is intended to be used for internal implementation only. + fn release_all_locks(&mut self) { + self.taken.clear(); + } + + /// Resets this context after a deadlock detection. + /// + /// Drops all held locks and reinitializes the [`WwAcquireCtx`]. + /// + /// It is intended to be used for internal implementation only. + fn cleanup_on_deadlock(&mut self) -> Result { + self.release_all_locks(); + // Re-init fresh `WwAcquireCtx`. + self.acquire =3D KBox::pin_init(WwAcquireCtx::new(self.class), GFP= _KERNEL)?; + + Ok(()) + } +} -- 2.50.0 From nobody Fri Oct 3 07:42:39 2025 Received: from forward101a.mail.yandex.net (forward101a.mail.yandex.net [178.154.239.84]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 56CE43054D2; Wed, 3 Sep 2025 13:14:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.154.239.84 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756905249; cv=none; b=HKQp9tGhri2FAZhrWCHwRkbvl6UFxOg926Q7kZTgOUprMHem6mqWSAdpvbgGkHcZKa+zq9i8hXohhtOhCUIXXUb+obXBUixpNcrdT9XhwoMCVk+Vcvsxqhh5yIbNIwszI+Kbe2Tm4R0Wm0lg9JpbwQYKB7CXGcwDAk+R6avUsCw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756905249; c=relaxed/simple; bh=WDgi77rHJ3KjGrxK8G5crmuDAGRMwJ1W8jArU1YRac4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=IiOtkOGT0lL4O4k5rg4aAdS78oyCtMOV5eQux7g2iNNk4tPfHGN0Q8jWv7hd5onR1yyL6vxrJA2eON78eMKuOb+lT1yKpyKIuSfw6zTLEX5QiyMZxOtSmdiJWmBYl7sDrQIB4veF7wbfNs4sN36C/hjSpxJcrE41wGlvVX0FoFo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=onurozkan.dev; spf=pass smtp.mailfrom=onurozkan.dev; dkim=pass (1024-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b=j9k+F5O+; arc=none smtp.client-ip=178.154.239.84 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b="j9k+F5O+" Received: from mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net (mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net [IPv6:2a02:6b8:c1d:3e17:0:640:f9aa:0]) by forward101a.mail.yandex.net (Yandex) with ESMTPS id 915E580CEF; Wed, 03 Sep 2025 16:14:05 +0300 (MSK) Received: by mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net (smtp/Yandex) with ESMTPSA id GDgKkBDMquQ0-8CpvrUK2; Wed, 03 Sep 2025 16:14:04 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=onurozkan.dev; s=mail; t=1756905245; bh=56lL8HtTww3Qk1jXGegwX7g8StiwO1fPanZh6GQONiU=; h=Cc:Message-ID:References:Date:In-Reply-To:Subject:To:From; b=j9k+F5O+beFYhoRAki/f3yizs5o2e7BCmYVntWmTYKExb1XKPc5t16vuY7Y0w6fRU n47MiqlBwLILyyvNnrOQZ+OV4PiZPel/RmeOjLvyNR3pnrGgpVuodwreaUhBgMGguR og6xYNDFANhjwzrP3KIc+dQUx/dtBQi85eaRZJIo= Authentication-Results: mail-nwsmtp-smtp-production-main-60.vla.yp-c.yandex.net; dkim=pass header.i=@onurozkan.dev From: =?UTF-8?q?Onur=20=C3=96zkan?= To: rust-for-linux@vger.kernel.org Cc: linux-kernel@vger.kernel.org, lossin@kernel.org, lyude@redhat.com, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, dakr@kernel.org, peterz@infradead.org, mingo@redhat.com, will@kernel.org, longman@redhat.com, felipe_life@live.com, daniel@sedlak.dev, bjorn3_gh@protonmail.com, daniel.almeida@collabora.com, =?UTF-8?q?Onur=20=C3=96zkan?= Subject: [PATCH v6 7/7] add KUnit coverage on ww_mutex/exec implementation Date: Wed, 3 Sep 2025 16:13:13 +0300 Message-ID: <20250903131313.4365-8-work@onurozkan.dev> X-Mailer: git-send-email 2.50.0 In-Reply-To: <20250903131313.4365-1-work@onurozkan.dev> References: <20250903131313.4365-1-work@onurozkan.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Adds coverage for `ww_mutex/exec.rs` implementation. Signed-off-by: Onur =C3=96zkan Reviewed-by: Elle Rhumsaa --- rust/kernel/sync/lock/ww_mutex/exec.rs | 148 +++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/rust/kernel/sync/lock/ww_mutex/exec.rs b/rust/kernel/sync/lock= /ww_mutex/exec.rs index 2f1fc540f0b8..543c5218232a 100644 --- a/rust/kernel/sync/lock/ww_mutex/exec.rs +++ b/rust/kernel/sync/lock/ww_mutex/exec.rs @@ -174,3 +174,151 @@ fn cleanup_on_deadlock(&mut self) -> Result { Ok(()) } } + +#[kunit_tests(rust_kernel_ww_exec)] +mod tests { + use crate::c_str; + use crate::prelude::*; + use crate::sync::Arc; + use pin_init::stack_pin_init; + + use super::*; + + #[test] + fn test_exec_context_basic_lock_unlock() -> Result { + stack_pin_init!(let class =3D WwClass::new_wound_wait(c_str!("exec= _ctx_basic"))); + + let mutex =3D Arc::pin_init(WwMutex::new(10, &class), GFP_KERNEL)?; + let mut ctx =3D KBox::pin_init(ExecContext::new(&class)?, GFP_KERN= EL)?; + + ctx.lock(&mutex)?; + ctx.with_locked(&mutex, |v| { + assert_eq!(*v, 10); + })?; + + ctx.release_all_locks(); + assert!(!mutex.is_locked()); + + Ok(()) + } + + #[test] + fn test_exec_context_with_locked_mutates_data() -> Result { + stack_pin_init!(let class =3D WwClass::new_wound_wait(c_str!("exec= _ctx_with_locked"))); + + let mutex =3D Arc::pin_init(WwMutex::new(5, &class), GFP_KERNEL)?; + let mut ctx =3D KBox::pin_init(ExecContext::new(&class)?, GFP_KERN= EL)?; + + ctx.lock(&mutex)?; + + ctx.with_locked(&mutex, |v| { + assert_eq!(*v, 5); + // Increment the value. + *v +=3D 7; + })?; + + ctx.with_locked(&mutex, |v| { + // Check that mutation took effect. + assert_eq!(*v, 12); + })?; + + Ok(()) + } + + #[test] + fn test_lock_all_success() -> Result { + stack_pin_init!(let class =3D WwClass::new_wound_wait(c_str!("lock= _all_ok"))); + + let mutex1 =3D Arc::pin_init(WwMutex::new(1, &class), GFP_KERNEL)?; + let mutex2 =3D Arc::pin_init(WwMutex::new(2, &class), GFP_KERNEL)?; + let mut ctx =3D KBox::pin_init(ExecContext::new(&class)?, GFP_KERN= EL)?; + + let res =3D ctx.lock_all( + |ctx| { + let _ =3D ctx.lock(&mutex1)?; + let _ =3D ctx.lock(&mutex2)?; + Ok(()) + }, + |ctx| { + ctx.with_locked(&mutex1, |v| *v +=3D 10)?; + ctx.with_locked(&mutex2, |v| *v +=3D 20)?; + Ok(( + ctx.with_locked(&mutex1, |v| *v)?, + ctx.with_locked(&mutex2, |v| *v)?, + )) + }, + )?; + + assert_eq!(res, (11, 22)); + assert!(!mutex1.is_locked()); + assert!(!mutex2.is_locked()); + + Ok(()) + } + + #[test] + fn test_with_different_input_type() -> Result { + stack_pin_init!(let class =3D WwClass::new_wound_wait(c_str!("lock= _all_ok"))); + + let mutex1 =3D Arc::pin_init(WwMutex::new(1, &class), GFP_KERNEL)?; + let mutex2 =3D Arc::pin_init(WwMutex::new("hello", &class), GFP_KE= RNEL)?; + let mut ctx =3D KBox::pin_init(ExecContext::new(&class)?, GFP_KERN= EL)?; + + ctx.lock_all( + |ctx| { + ctx.lock(&mutex1)?; + ctx.lock(&mutex2)?; + Ok(()) + }, + |ctx| { + ctx.with_locked(&mutex1, |v| assert_eq!(*v, 1))?; + ctx.with_locked(&mutex2, |v| assert_eq!(*v, "hello"))?; + Ok(()) + }, + )?; + + Ok(()) + } + + #[test] + fn test_lock_all_retries_on_deadlock() -> Result { + stack_pin_init!(let class =3D WwClass::new_wound_wait(c_str!("lock= _all_retry"))); + + let mutex =3D Arc::pin_init(WwMutex::new(99, &class), GFP_KERNEL)?; + let mut ctx =3D KBox::pin_init(ExecContext::new(&class)?, GFP_KERN= EL)?; + let mut first_try =3D true; + + let res =3D ctx.lock_all( + |ctx| { + if first_try { + first_try =3D false; + // Simulate deadlock on first attempt. + return Err(EDEADLK); + } + ctx.lock(&mutex) + }, + |ctx| { + ctx.with_locked(&mutex, |v| { + *v +=3D 1; + *v + }) + }, + )?; + + assert_eq!(res, 100); + Ok(()) + } + + #[test] + fn test_with_locked_on_unlocked_mutex() -> Result { + stack_pin_init!(let class =3D WwClass::new_wound_wait(c_str!("with= _unlocked_mutex"))); + + let mutex =3D Arc::pin_init(WwMutex::new(5, &class), GFP_KERNEL)?; + let mut ctx =3D KBox::pin_init(ExecContext::new(&class)?, GFP_KERN= EL)?; + + let ecode =3D ctx.with_locked(&mutex, |_v| {}).unwrap_err(); + assert_eq!(EINVAL, ecode); + + Ok(()) + } +} -- 2.50.0