From nobody Wed Feb 11 05:41:11 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (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 6BDD813848D for ; Mon, 20 May 2024 17:27:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226023; cv=none; b=HxMqvZtHGe1BRFsZNagDSg/0aNPgqguqXHQSi8KpU5pMGVNj+hxt9+CmmyeNoZPBBKA8/8QXOASAqWjs89UEmzxKTr3hZnDIablD+gNlboSu5EFirrdu4TYaKJVZ+F89/zdORf9qMDoHiKLykkAESyRl/TgIf6f6i2Q+zjW3pnk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226023; c=relaxed/simple; bh=rAhFipo4bK8HmFg4tJSDidtDJaUeETRwaBwak+PMNa8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=i7ghbskCTVDz5MgsDW1Xt8nK2mATKJfEU2o4gGtTvnDZFiTboRtwWO/fCqO+VRZD5aPTITSHyeOeCn/PAP9vneJQK4BQsKl9We/KJgShrJgJ/bcwlBwSeQ9IqS9gBBxvZHlJ+D5EJ+ijWNv9z32ZGX+J5qCOZ3WiYGDqniDAj08= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=htO9WfRd; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="htO9WfRd" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226021; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=R91zVu6bvxpu1aXFiFEweUnqMPVUvsNACvUAs6c3Gw8=; b=htO9WfRdbgrEMp2RuBk3I+y4J0LlwHIKAH9QbQPOIaAwGFmopjdT07YjefqjFJ4A9oPqxM VBns2QsRK1B81OIYSqNmuOhmmunwIXiWE8FqTQDsfioP8htZUdMHCsGZctxTYW2jL7q/MR cGRYGEsfRHPy5GTdD/y/GU9Qi8hc1DY= Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-487-exUnV4UoNymy51CgRSf7oQ-1; Mon, 20 May 2024 13:26:59 -0400 X-MC-Unique: exUnV4UoNymy51CgRSf7oQ-1 Received: by mail-wm1-f72.google.com with SMTP id 5b1f17b1804b1-41fc5c5cc95so51939445e9.0 for ; Mon, 20 May 2024 10:26:58 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226018; x=1716830818; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=R91zVu6bvxpu1aXFiFEweUnqMPVUvsNACvUAs6c3Gw8=; b=dhUP3DIsB2LqMSpPp19dSweDvh4zy/4+82vT+zlBozKaYLym5ytvDWuCj3SueF1dmq OOBe0CE8zqxFkgovOxDgEBwpoh0vyCn5iP3avjvu7Ecod/z0dxhkLvOrUZ0P0pMgqN8J 1JNf6otLo9Vd8Ljg3H8BVXMubwVmMiapC5iEKEocprMxvoyC/4RMeCdl0f4g9c0yvgwQ 7Dmvfki/55A9RDdVkbM575OdaPRq2VgGLV4ORQwzmti7lAJJDSQvt+PY3kabPAUWvnii WdfoudiT8WlWwMFGHcek+x6rBn9VAh0Iil0IfvKvMWQ1/qdvRvQat3/+uUn21nLtHD5A iMbQ== X-Forwarded-Encrypted: i=1; AJvYcCWCzINwXWXiARV6Nrlo1MgYoNcxEAwQ7bvxwneiA9wunXUgfOcParGpaS2owuytX5T6N2QylZ8A2/Y6SrkgHEvkPMfrzsojeiY8oNxv X-Gm-Message-State: AOJu0Yyf1kEibdGVoAyycRsAsSFGq5JU1AIelkB9AE2YtQhn6jYAi9Cc T5JHErMkK5x96+5W1gDSDdsfOhGX+sZmby/pOS3C2IdM+znG8DB9C5XMNQLq8D//cowvKbXzwIT t/5rfBUA6GnFSlAH1583Prv0DfO9bwjMJl0xt4NYILwECs8+cVgAKxZHOziJWkw== X-Received: by 2002:a05:6000:1968:b0:34f:96ba:ca3a with SMTP id ffacd0b85a97d-3504a61c7cemr32867603f8f.13.1716226017555; Mon, 20 May 2024 10:26:57 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHs4uhCF4pA+lofxQie6yGvKq+LktARuDSM8VVafM6mbOTF89vjV0qTL+7VA81QWNIEkG1APA== X-Received: by 2002:a05:6000:1968:b0:34f:96ba:ca3a with SMTP id ffacd0b85a97d-3504a61c7cemr32867560f8f.13.1716226016428; Mon, 20 May 2024 10:26:56 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-354bd2e2a5asm6620842f8f.45.2024.05.20.10.26.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:26:55 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 01/11] rust: add abstraction for struct device Date: Mon, 20 May 2024 19:25:38 +0200 Message-ID: <20240520172554.182094-2-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> 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 Add an (always) reference counted abstraction for a generic struct device. This abstraction encapsulates existing struct device instances and manages its reference count. Subsystems may use this abstraction as a base to abstract subsystem specific device instances based on a generic struct device. Co-developed-by: Wedson Almeida Filho Signed-off-by: Wedson Almeida Filho Signed-off-by: Danilo Krummrich --- rust/helpers.c | 1 + rust/kernel/device.rs | 76 +++++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 3 files changed, 78 insertions(+) create mode 100644 rust/kernel/device.rs diff --git a/rust/helpers.c b/rust/helpers.c index 4c8b7b92a4f4..f9d2db1d1f33 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs new file mode 100644 index 000000000000..fafec70effb6 --- /dev/null +++ b/rust/kernel/device.rs @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Generic devices that are part of the kernel's driver model. +//! +//! C header: [`include/linux/device.h`](../../../../include/linux/device.= h) + +use crate::{ + bindings, + types::{ARef, Opaque}, +}; +use core::ptr; + +/// A ref-counted device. +/// +/// # Invariants +/// +/// The pointer stored in `Self` is non-null and valid for the lifetime of= the ARef instance. In +/// particular, the ARef instance owns an increment on underlying object= =E2=80=99s reference count. +#[repr(transparent)] +pub struct Device(Opaque); + +impl Device { + /// Creates a new ref-counted instance of an existing device pointer. + /// + /// # Safety + /// + /// Callers must ensure that `ptr` is valid, non-null, and has a non-z= ero reference count. + pub unsafe fn from_raw(ptr: *mut bindings::device) -> ARef { + // SAFETY: By the safety requirements, ptr is valid. + // Initially increase the reference count by one to compensate for= the final decrement once + // this newly created `ARef` instance is dropped. + unsafe { bindings::get_device(ptr) }; + + // CAST: `Self` is a `repr(transparent)` wrapper around `bindings:= :device`. + let ptr =3D ptr.cast::(); + + // SAFETY: By the safety requirements, ptr is valid. + unsafe { ARef::from_raw(ptr::NonNull::new_unchecked(ptr)) } + } + + /// Obtain the raw `struct device *`. + pub(crate) fn as_raw(&self) -> *mut bindings::device { + self.0.get() + } + + /// Convert a raw `struct device` pointer to a `&Device`. + /// + /// # Safety + /// + /// Callers must ensure that `ptr` is valid, non-null, and has a non-z= ero reference count for + /// the entire duration when the returned reference exists. + pub unsafe fn as_ref<'a>(ptr: *mut bindings::device) -> &'a Self { + // SAFETY: Guaranteed by the safety requirements of the function. + unsafe { &*ptr.cast() } + } +} + +// SAFETY: Instances of `Device` are always ref-counted. +unsafe impl crate::types::AlwaysRefCounted for Device { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference guarantees that the= refcount is nonzero. + unsafe { bindings::get_device(self.as_raw()) }; + } + + unsafe fn dec_ref(obj: ptr::NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is = nonzero. + unsafe { bindings::put_device(obj.cast().as_ptr()) } + } +} + +// SAFETY: `Device` only holds a pointer to a C device, which is safe to b= e used from any thread. +unsafe impl Send for Device {} + +// SAFETY: `Device` only holds a pointer to a C device, references to whic= h are safe to be used +// from any thread. +unsafe impl Sync for Device {} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 9a943d99c71a..4ba3d4a49e9c 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -28,6 +28,7 @@ =20 pub mod alloc; mod build_assert; +pub mod device; pub mod error; pub mod init; pub mod ioctl; --=20 2.45.1 From nobody Wed Feb 11 05:41:11 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (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 021C71384B3 for ; Mon, 20 May 2024 17:27:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226028; cv=none; b=Ws2FcpwoRzky4uf0ylrX5er6FGNVDDAyoeSTPZgZQVY3xyNL11EwZuqZ26/XsHCh3SoYM1xiPWw2kbXWy8C4Jn2mVCajPFZtiGprWk97AkDp7dwmA7VK5MbKyVn3k9VUgeuCDdCy1/d2K2kFYcctHV+EQooJWzdYZhgB8acH7tk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226028; c=relaxed/simple; bh=HLxn+yy/99tAi8nj3dRf0FfZndbE49uTmIZinfHUwRY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Vq2CUNzo3joxmZXQ5jq9varMR0JDBjn5CYOHuOiQN1dqNQMHecv+/Bd57opN3mQ1J92P1Znri8BCvdzUhq3PPa0WsqU55zSmC+QxXbVSE6wHAzNL8WHROplonhUP6uONrcSmVCKNk3PCR8DYYz+soINY7PiT9rtniIt9SNKuRZM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=GOY9wMgc; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="GOY9wMgc" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226025; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=S2szcc1h6YvmKD7Qd7G/Viagtvo0+d8wDp1xeEAOqWQ=; b=GOY9wMgcAzFuG4Kvet0HVut7T/2fzyYqvJ/jbXS6nkjAfUnPecurc5IzJa/zAxdXADkZ7P 3E/MiYBDm0XPlusPyn6KgkMs4K03rRwYhCpxX/qchi4bsZhYLpdxw6+qxCDQosvePRGa6D 3r9McZVx2SWcdDUs3/LabS99BYSWh7w= Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-563-Qx1UBcXzMHOwvW70N_s1fQ-1; Mon, 20 May 2024 13:27:03 -0400 X-MC-Unique: Qx1UBcXzMHOwvW70N_s1fQ-1 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-351bd229b88so5936826f8f.1 for ; Mon, 20 May 2024 10:27:03 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226022; x=1716830822; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=S2szcc1h6YvmKD7Qd7G/Viagtvo0+d8wDp1xeEAOqWQ=; b=qJkc3lflOgpfx7IchR81D26NjHAGsZSPlwBF9sya+baLD813OTl5QU8XsUAKMom2ef MFtbtxO4L74GwoNSuRwsc+Y9JfgviKLTXAZTBscCoExZVNF4A4aK6gV36NICaOjRtEzu x2XKLFNrUhSw61YxTtT8ZsApKGl+amB+yyM8qMIFsYrIaDzg43j2vwGM0KLDzcAHExUW RpyffjUMUD4+8gyh4MgTdREatfqV0lNgMux8SUudsV77zcHgOQkLgLju6AV441i+QXqO zb7xvrwdeR9y39VzJT8lyDytOUO8ioxdqxGnkAQEPi2T3/0DEeLpAlt+gskCxRxeXPF7 tj1Q== X-Forwarded-Encrypted: i=1; AJvYcCUTHC7h7x/EMGqyoi095jegij3kDG74CqVTjRlv6Ec5NIzdtC1OWq2NBQDo/YV2qErVnWA46uedv6TYvlgD8dYOGP6kaph3K0anX9V4 X-Gm-Message-State: AOJu0Yygjt/FT4jwS3DoOXO+n1yUt+hYSovVlEEuGPpvbOrFadYEwVGT xN2eudg6l3+O9IhgXkJhlFQ3pzd286VgH2HR/mCsbHTbteUgle/AmEBtX3Y4DKEoBz5yMAYKKw8 YJE0Y/9gcwk5Cpuyi69qYeog2YGw8dFSqeUY7saPVlABs4DWzkxUfPQ+LiDue2g== X-Received: by 2002:a5d:6884:0:b0:351:d3e1:a547 with SMTP id ffacd0b85a97d-351d3e1a614mr11481498f8f.6.1716226021105; Mon, 20 May 2024 10:27:01 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEr7pN/+1T/SxXBrpWkGnaFsOuSQnfr+1EG4cOBwzMMTuZBSshqhYfkJ1Gp9nzPBLwrRiw7kg== X-Received: by 2002:a5d:6884:0:b0:351:d3e1:a547 with SMTP id ffacd0b85a97d-351d3e1a614mr11481470f8f.6.1716226020381; Mon, 20 May 2024 10:27:00 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-354c9bcaa98sm2238571f8f.4.2024.05.20.10.26.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:26:59 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 02/11] rust: add driver abstraction Date: Mon, 20 May 2024 19:25:39 +0200 Message-ID: <20240520172554.182094-3-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Wedson Almeida Filho This defines general functionality related to registering drivers with their respective subsystems, and registering modules that implement drivers. Co-developed-by: Asahi Lina Signed-off-by: Asahi Lina Co-developed-by: Andreas Hindborg Signed-off-by: Andreas Hindborg Signed-off-by: Wedson Almeida Filho Signed-off-by: Danilo Krummrich --- rust/kernel/driver.rs | 492 +++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 4 +- rust/macros/module.rs | 2 +- samples/rust/rust_minimal.rs | 2 +- samples/rust/rust_print.rs | 2 +- 5 files changed, 498 insertions(+), 4 deletions(-) create mode 100644 rust/kernel/driver.rs diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs new file mode 100644 index 000000000000..e0cfc36d47ff --- /dev/null +++ b/rust/kernel/driver.rs @@ -0,0 +1,492 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Generic support for drivers of different buses (e.g., PCI, Platform, A= mba, etc.). +//! +//! Each bus/subsystem is expected to implement [`DriverOps`], which allow= s drivers to register +//! using the [`Registration`] class. + +use crate::{ + alloc::{box_ext::BoxExt, flags::*}, + error::code::*, + error::Result, + str::CStr, + sync::Arc, + ThisModule, +}; +use alloc::boxed::Box; +use core::{cell::UnsafeCell, marker::PhantomData, ops::Deref, pin::Pin}; + +/// A subsystem (e.g., PCI, Platform, Amba, etc.) that allows drivers to b= e written for it. +pub trait DriverOps { + /// The type that holds information about the registration. This is ty= pically a struct defined + /// by the C portion of the kernel. + type RegType: Default; + + /// Registers a driver. + /// + /// # Safety + /// + /// `reg` must point to valid, initialised, and writable memory. It ma= y be modified by this + /// function to hold registration state. + /// + /// On success, `reg` must remain pinned and valid until the matching = call to + /// [`DriverOps::unregister`]. + unsafe fn register( + reg: *mut Self::RegType, + name: &'static CStr, + module: &'static ThisModule, + ) -> Result; + + /// Unregisters a driver previously registered with [`DriverOps::regis= ter`]. + /// + /// # Safety + /// + /// `reg` must point to valid writable memory, initialised by a previo= us successful call to + /// [`DriverOps::register`]. + unsafe fn unregister(reg: *mut Self::RegType); +} + +/// The registration of a driver. +pub struct Registration { + is_registered: bool, + concrete_reg: UnsafeCell, +} + +// SAFETY: `Registration` has no fields or methods accessible via `&Regist= ration`, so it is safe to +// share references to it with multiple threads as nothing can be done. +unsafe impl Sync for Registration {} + +impl Registration { + /// Creates a new instance of the registration object. + pub fn new() -> Self { + Self { + is_registered: false, + concrete_reg: UnsafeCell::new(T::RegType::default()), + } + } + + /// Allocates a pinned registration object and registers it. + /// + /// Returns a pinned heap-allocated representation of the registration. + pub fn new_pinned(name: &'static CStr, module: &'static ThisModule) ->= Result>> { + let mut reg =3D Pin::from(Box::new(Self::new(), GFP_KERNEL)?); + reg.as_mut().register(name, module)?; + Ok(reg) + } + + /// Registers a driver with its subsystem. + /// + /// It must be pinned because the memory block that represents the reg= istration is potentially + /// self-referential. + pub fn register( + self: Pin<&mut Self>, + name: &'static CStr, + module: &'static ThisModule, + ) -> Result { + // SAFETY: We never move out of `this`. + let this =3D unsafe { self.get_unchecked_mut() }; + if this.is_registered { + // Already registered. + return Err(EINVAL); + } + + // SAFETY: `concrete_reg` was initialised via its default construc= tor. It is only freed + // after `Self::drop` is called, which first calls `T::unregister`. + unsafe { T::register(this.concrete_reg.get(), name, module) }?; + + this.is_registered =3D true; + Ok(()) + } +} + +impl Default for Registration { + fn default() -> Self { + Self::new() + } +} + +impl Drop for Registration { + fn drop(&mut self) { + if self.is_registered { + // SAFETY: This path only runs if a previous call to `T::regis= ter` completed + // successfully. + unsafe { T::unregister(self.concrete_reg.get()) }; + } + } +} + +/// Conversion from a device id to a raw device id. +/// +/// This is meant to be implemented by buses/subsystems so that they can u= se [`IdTable`] to +/// guarantee (at compile-time) zero-termination of device id tables provi= ded by drivers. +/// +/// Originally, RawDeviceId was implemented as a const trait. However, thi= s unstable feature is +/// broken/gone in 1.73. To work around this, turn IdArray::new() into a m= acro such that it can use +/// concrete types (which can still have const associated functions) inste= ad of a trait. +/// +/// # Safety +/// +/// Implementers must ensure that: +/// - [`RawDeviceId::ZERO`] is actually a zeroed-out version of the raw = device id. +/// - [`RawDeviceId::to_rawid`] stores `offset` in the context/data fiel= d of the raw device id so +/// that buses can recover the pointer to the data. +pub unsafe trait RawDeviceId { + /// The raw type that holds the device id. + /// + /// Id tables created from [`Self`] are going to hold this type in its= zero-terminated array. + type RawType: Copy; + + /// A zeroed-out representation of the raw device id. + /// + /// Id tables created from [`Self`] use [`Self::ZERO`] as the sentinel= to indicate the end of + /// the table. + const ZERO: Self::RawType; +} + +/// A zero-terminated device id array, followed by context data. +#[repr(C)] +pub struct IdArray { + ids: [T::RawType; N], + sentinel: T::RawType, + id_infos: [Option; N], +} + +impl IdArray { + const U_NONE: Option =3D None; + + /// Returns an `IdTable` backed by `self`. + /// + /// This is used to essentially erase the array size. + pub const fn as_table(&self) -> IdTable<'_, T, U> { + IdTable { + first: &self.ids[0], + _p: PhantomData, + } + } + + /// Creates a new instance of the array. + /// + /// The contents are derived from the given identifiers and context in= formation. + #[doc(hidden)] + pub const unsafe fn new(raw_ids: [T::RawType; N], infos: [Option; N= ]) -> Self + where + T: RawDeviceId + Copy, + T::RawType: Copy + Clone, + { + Self { + ids: raw_ids, + sentinel: T::ZERO, + id_infos: infos, + } + } + + #[doc(hidden)] + pub const fn get_offset(idx: usize) -> isize + where + T: RawDeviceId + Copy, + T::RawType: Copy + Clone, + { + // SAFETY: We are only using this dummy value to get offsets. + let array =3D unsafe { Self::new([T::ZERO; N], [Self::U_NONE; N]) = }; + // SAFETY: Both pointers are within `array` (or one byte beyond), = consequently they are + // derived from the same allocated object. We are using a `u8` poi= nter, whose size 1, + // so the pointers are necessarily 1-byte aligned. + let ret =3D unsafe { + (&array.id_infos[idx] as *const _ as *const u8) + .offset_from(&array.ids[idx] as *const _ as _) + }; + core::mem::forget(array); + ret + } +} + +// Creates a new ID array. This is a macro so it can take as a parameter t= he concrete ID type in +// order to call to_rawid() on it, and still remain const. This is necessa= ry until a new +// const_trait_impl implementation lands, since the existing implementatio= n was removed in Rust +// 1.73. +#[macro_export] +#[doc(hidden)] +macro_rules! _new_id_array { + (($($args:tt)*), $id_type:ty) =3D> {{ + /// Creates a new instance of the array. + /// + /// The contents are derived from the given identifiers and contex= t information. + const fn new< U, const N: usize>(ids: [$id_type; N], infos: [Optio= n; N]) + -> $crate::driver::IdArray<$id_type, U, N> + where + $id_type: $crate::driver::RawDeviceId + Copy, + <$id_type as $crate::driver::RawDeviceId>::RawType: Copy + Clo= ne, + { + let mut raw_ids =3D + [<$id_type as $crate::driver::RawDeviceId>::ZERO; N]; + let mut i =3D 0usize; + while i < N { + let offset: isize =3D $crate::driver::IdArray::<$id_type, = U, N>::get_offset(i); + raw_ids[i] =3D ids[i].to_rawid(offset); + i +=3D 1; + } + + // SAFETY: We are passing valid arguments computed with the co= rrect offsets. + unsafe { + $crate::driver::IdArray::<$id_type, U, N>::new(raw_ids, in= fos) + } + } + + new($($args)*) + }} +} + +/// A device id table. +/// +/// The table is guaranteed to be zero-terminated and to be followed by an= array of context data of +/// type `Option`. +pub struct IdTable<'a, T: RawDeviceId, U> { + first: &'a T::RawType, + _p: PhantomData<&'a U>, +} + +impl AsRef for IdTable<'_, T, U> { + fn as_ref(&self) -> &T::RawType { + self.first + } +} + +/// Counts the number of parenthesis-delimited, comma-separated items. +/// +/// # Examples +/// +/// ``` +/// # use kernel::count_paren_items; +/// +/// assert_eq!(0, count_paren_items!()); +/// assert_eq!(1, count_paren_items!((A))); +/// assert_eq!(1, count_paren_items!((A),)); +/// assert_eq!(2, count_paren_items!((A), (B))); +/// assert_eq!(2, count_paren_items!((A), (B),)); +/// assert_eq!(3, count_paren_items!((A), (B), (C))); +/// assert_eq!(3, count_paren_items!((A), (B), (C),)); +/// ``` +#[macro_export] +macro_rules! count_paren_items { + (($($item:tt)*), $($remaining:tt)*) =3D> { 1 + $crate::count_paren_ite= ms!($($remaining)*) }; + (($($item:tt)*)) =3D> { 1 }; + () =3D> { 0 }; +} + +/// Converts a comma-separated list of pairs into an array with the first = element. That is, it +/// discards the second element of the pair. +/// +/// Additionally, it automatically introduces a type if the first element = is warpped in curly +/// braces, for example, if it's `{v: 10}`, it becomes `X { v: 10 }`; this= is to avoid repeating +/// the type. +/// +/// # Examples +/// +/// ``` +/// # use kernel::first_item; +/// +/// #[derive(PartialEq, Debug)] +/// struct X { +/// v: u32, +/// } +/// +/// assert_eq!([] as [X; 0], first_item!(X, )); +/// assert_eq!([X { v: 10 }], first_item!(X, ({ v: 10 }, Y))); +/// assert_eq!([X { v: 10 }], first_item!(X, ({ v: 10 }, Y),)); +/// assert_eq!([X { v: 10 }], first_item!(X, (X { v: 10 }, Y))); +/// assert_eq!([X { v: 10 }], first_item!(X, (X { v: 10 }, Y),)); +/// assert_eq!([X { v: 10 }, X { v: 20 }], first_item!(X, ({ v: 10 }, Y), = ({ v: 20 }, Y))); +/// assert_eq!([X { v: 10 }, X { v: 20 }], first_item!(X, ({ v: 10 }, Y), = ({ v: 20 }, Y),)); +/// assert_eq!([X { v: 10 }, X { v: 20 }], first_item!(X, (X { v: 10 }, Y)= , (X { v: 20 }, Y))); +/// assert_eq!([X { v: 10 }, X { v: 20 }], first_item!(X, (X { v: 10 }, Y)= , (X { v: 20 }, Y),)); +/// assert_eq!([X { v: 10 }, X { v: 20 }, X { v: 30 }], +/// first_item!(X, ({ v: 10 }, Y), ({ v: 20 }, Y), ({v: 30}, Y)= )); +/// assert_eq!([X { v: 10 }, X { v: 20 }, X { v: 30 }], +/// first_item!(X, ({ v: 10 }, Y), ({ v: 20 }, Y), ({v: 30}, Y)= ,)); +/// assert_eq!([X { v: 10 }, X { v: 20 }, X { v: 30 }], +/// first_item!(X, (X { v: 10 }, Y), (X { v: 20 }, Y), (X {v: 3= 0}, Y))); +/// assert_eq!([X { v: 10 }, X { v: 20 }, X { v: 30 }], +/// first_item!(X, (X { v: 10 }, Y), (X { v: 20 }, Y), (X {v: 3= 0}, Y),)); +/// ``` +#[macro_export] +macro_rules! first_item { + ($id_type:ty, $(({$($first:tt)*}, $second:expr)),* $(,)?) =3D> { + { + type IdType =3D $id_type; + [$(IdType{$($first)*},)*] + } + }; + ($id_type:ty, $(($first:expr, $second:expr)),* $(,)?) =3D> { [$($first= ,)*] }; +} + +/// Converts a comma-separated list of pairs into an array with the second= element. That is, it +/// discards the first element of the pair. +/// +/// # Examples +/// +/// ``` +/// # use kernel::second_item; +/// +/// assert_eq!([] as [u32; 0], second_item!()); +/// assert_eq!([10u32], second_item!((X, 10u32))); +/// assert_eq!([10u32], second_item!((X, 10u32),)); +/// assert_eq!([10u32], second_item!(({ X }, 10u32))); +/// assert_eq!([10u32], second_item!(({ X }, 10u32),)); +/// assert_eq!([10u32, 20], second_item!((X, 10u32), (X, 20))); +/// assert_eq!([10u32, 20], second_item!((X, 10u32), (X, 20),)); +/// assert_eq!([10u32, 20], second_item!(({ X }, 10u32), ({ X }, 20))); +/// assert_eq!([10u32, 20], second_item!(({ X }, 10u32), ({ X }, 20),)); +/// assert_eq!([10u32, 20, 30], second_item!((X, 10u32), (X, 20), (X, 30))= ); +/// assert_eq!([10u32, 20, 30], second_item!((X, 10u32), (X, 20), (X, 30),= )); +/// assert_eq!([10u32, 20, 30], second_item!(({ X }, 10u32), ({ X }, 20), = ({ X }, 30))); +/// assert_eq!([10u32, 20, 30], second_item!(({ X }, 10u32), ({ X }, 20), = ({ X }, 30),)); +/// ``` +#[macro_export] +macro_rules! second_item { + ($(({$($first:tt)*}, $second:expr)),* $(,)?) =3D> { [$($second,)*] }; + ($(($first:expr, $second:expr)),* $(,)?) =3D> { [$($second,)*] }; +} + +/// Defines a new constant [`IdArray`] with a concise syntax. +/// +/// It is meant to be used by buses and subsystems to create a similar mac= ro with their device id +/// type already specified, i.e., with fewer parameters to the end user. +/// +/// # Examples +/// +// TODO: Exported but not usable by kernel modules (requires `const_trait_= impl`). +/// ```ignore +/// #![feature(const_trait_impl)] +/// # use kernel::{define_id_array, driver::RawDeviceId}; +/// +/// #[derive(Copy, Clone)] +/// struct Id(u32); +/// +/// // SAFETY: `ZERO` is all zeroes and `to_rawid` stores `offset` as the = second element of the raw +/// // device id pair. +/// unsafe impl const RawDeviceId for Id { +/// type RawType =3D (u64, isize); +/// const ZERO: Self::RawType =3D (0, 0); +/// fn to_rawid(&self, offset: isize) -> Self::RawType { +/// (self.0 as u64 + 1, offset) +/// } +/// } +/// +/// define_id_array!(A1, Id, (), []); +/// define_id_array!(A2, Id, &'static [u8], [(Id(10), None)]); +/// define_id_array!(A3, Id, &'static [u8], [(Id(10), Some(b"id1")), ]); +/// define_id_array!(A4, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(2= 0), Some(b"id2"))]); +/// define_id_array!(A5, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(2= 0), Some(b"id2")), ]); +/// define_id_array!(A6, Id, &'static [u8], [(Id(10), None), (Id(20), Some= (b"id2")), ]); +/// define_id_array!(A7, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(2= 0), None), ]); +/// define_id_array!(A8, Id, &'static [u8], [(Id(10), None), (Id(20), None= ), ]); +/// ``` +#[macro_export] +macro_rules! define_id_array { + ($table_name:ident, $id_type:ty, $data_type:ty, [ $($t:tt)* ]) =3D> { + const $table_name: + $crate::driver::IdArray<$id_type, $data_type, { $crate::count_= paren_items!($($t)*) }> =3D + $crate::_new_id_array!(( + $crate::first_item!($id_type, $($t)*), $crate::second_= item!($($t)*)), $id_type); + }; +} + +/// Defines a new constant [`IdTable`] with a concise syntax. +/// +/// It is meant to be used by buses and subsystems to create a similar mac= ro with their device id +/// type already specified, i.e., with fewer parameters to the end user. +/// +/// # Examples +/// +// TODO: Exported but not usable by kernel modules (requires `const_trait_= impl`). +/// ```ignore +/// #![feature(const_trait_impl)] +/// # use kernel::{define_id_table, driver::RawDeviceId}; +/// +/// #[derive(Copy, Clone)] +/// struct Id(u32); +/// +/// // SAFETY: `ZERO` is all zeroes and `to_rawid` stores `offset` as the = second element of the raw +/// // device id pair. +/// unsafe impl const RawDeviceId for Id { +/// type RawType =3D (u64, isize); +/// const ZERO: Self::RawType =3D (0, 0); +/// fn to_rawid(&self, offset: isize) -> Self::RawType { +/// (self.0 as u64 + 1, offset) +/// } +/// } +/// +/// define_id_table!(T1, Id, &'static [u8], [(Id(10), None)]); +/// define_id_table!(T2, Id, &'static [u8], [(Id(10), Some(b"id1")), ]); +/// define_id_table!(T3, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(2= 0), Some(b"id2"))]); +/// define_id_table!(T4, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(2= 0), Some(b"id2")), ]); +/// define_id_table!(T5, Id, &'static [u8], [(Id(10), None), (Id(20), Some= (b"id2")), ]); +/// define_id_table!(T6, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(2= 0), None), ]); +/// define_id_table!(T7, Id, &'static [u8], [(Id(10), None), (Id(20), None= ), ]); +/// ``` +#[macro_export] +macro_rules! define_id_table { + ($table_name:ident, $id_type:ty, $data_type:ty, [ $($t:tt)* ]) =3D> { + const $table_name: Option<$crate::driver::IdTable<'static, $id_typ= e, $data_type>> =3D { + $crate::define_id_array!(ARRAY, $id_type, $data_type, [ $($t)*= ]); + Some(ARRAY.as_table()) + }; + }; +} + +/// Custom code within device removal. +pub trait DeviceRemoval { + /// Cleans resources up when the device is removed. + /// + /// This is called when a device is removed and offers implementers th= e chance to run some code + /// that cleans state up. + fn device_remove(&self); +} + +impl DeviceRemoval for () { + fn device_remove(&self) {} +} + +impl DeviceRemoval for Arc { + fn device_remove(&self) { + self.deref().device_remove(); + } +} + +impl DeviceRemoval for Box { + fn device_remove(&self) { + self.deref().device_remove(); + } +} + +/// A kernel module that only registers the given driver on init. +/// +/// This is a helper struct to make it easier to define single-functionali= ty modules, in this case, +/// modules that offer a single driver. +pub struct Module { + _driver: Pin>>, +} + +impl crate::Module for Module { + fn init(name: &'static CStr, module: &'static ThisModule) -> Result { + Ok(Self { + _driver: Registration::new_pinned(name, module)?, + }) + } +} + +/// Declares a kernel module that exposes a single driver. +/// +/// It is meant to be used as a helper by other subsystems so they can mor= e easily expose their own +/// macros. +#[macro_export] +macro_rules! module_driver { + (<$gen_type:ident>, $driver_ops:ty, { type: $type:ty, $($f:tt)* }) =3D= > { + type Ops<$gen_type> =3D $driver_ops; + type ModuleType =3D $crate::driver::Module>; + $crate::prelude::module! { + type: ModuleType, + $($f)* + } + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 4ba3d4a49e9c..698121c925f3 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -13,6 +13,7 @@ =20 #![no_std] #![feature(coerce_unsized)] +#![feature(const_refs_to_cell)] #![feature(dispatch_from_dyn)] #![feature(new_uninit)] #![feature(receiver_trait)] @@ -29,6 +30,7 @@ pub mod alloc; mod build_assert; pub mod device; +pub mod driver; pub mod error; pub mod init; pub mod ioctl; @@ -69,7 +71,7 @@ pub trait Module: Sized + Sync { /// should do. /// /// Equivalent to the `module_init` macro in the C API. - fn init(module: &'static ThisModule) -> error::Result; + fn init(name: &'static str::CStr, module: &'static ThisModule) -> erro= r::Result; } =20 /// Equivalent to `THIS_MODULE` in the C API. diff --git a/rust/macros/module.rs b/rust/macros/module.rs index 27979e582e4b..3e7a6a8560f5 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -275,7 +275,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { }} =20 fn __init() -> core::ffi::c_int {{ - match <{type_} as kernel::Module>::init(&THIS_MODULE) {{ + match <{type_} as kernel::Module>::init(kernel::c_str!(\"{= name}\"), &THIS_MODULE) {{ Ok(m) =3D> {{ unsafe {{ __MOD =3D Some(m); diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs index 2a9eaab62d1c..3b918ff5eebb 100644 --- a/samples/rust/rust_minimal.rs +++ b/samples/rust/rust_minimal.rs @@ -17,7 +17,7 @@ struct RustMinimal { } =20 impl kernel::Module for RustMinimal { - fn init(_module: &'static ThisModule) -> Result { + fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<= Self> { pr_info!("Rust minimal sample (init)\n"); pr_info!("Am I built-in? {}\n", !cfg!(MODULE)); =20 diff --git a/samples/rust/rust_print.rs b/samples/rust/rust_print.rs index 6eabb0d79ea3..722275a735f1 100644 --- a/samples/rust/rust_print.rs +++ b/samples/rust/rust_print.rs @@ -40,7 +40,7 @@ fn arc_print() -> Result { } =20 impl kernel::Module for RustPrint { - fn init(_module: &'static ThisModule) -> Result { + fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<= Self> { pr_info!("Rust printing macros sample (init)\n"); =20 pr_emerg!("Emergency message (level 0) without args\n"); --=20 2.45.1 From nobody Wed Feb 11 05:41:11 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (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 A06A41386DA for ; Mon, 20 May 2024 17:27:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226030; cv=none; b=nyDQ2svgwhOMBZIYjpxKlzW9ack7cet96FzFdTMJ0nQHgCjxH1vWj53fM+GKjv1FVo8cMt9wc00knQGHONjw0Pm1UMkpyToWP0xP2tpuvQ3OoXeTmgWeW9MZnlnSU/idq6WyWZoDO4NVsqXC15tvLosm2A/RYCBDFORZkWrx6lk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226030; c=relaxed/simple; bh=wZpumSaAgofzk39YfBsp32v4+ebHuETq3cEP9jzCvOU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Vic02Adg4ONVV4yWdt3I2UUrTdn6PgrajCrxGn7miFnjFYaEs8wOF8Kqwo/KMYAQTfo9WdbQnWwyZ557KxO5PUZ+BUphAFnZvX/IZQJGBBfz22Z3KZCn+9C8Y/mdo8yOBfUBcwsyrey7ndyU046zW+a2ZXcaRqNe02tExIHiVEU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=M8kUejGT; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="M8kUejGT" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226027; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=5CImq0b4mEhq1pg3qJ1z+g35+FLQKy2u4BGJkZOUedY=; b=M8kUejGT966ZQvgKzLtiyjigQvuoaYOysHWIe7ZurdnJEX9cVpvPrjmyzMfqCxMmQAbXvk cTsVFlvq5AJHzjI00g1mvJbRA7X/fAWhsn6t6VVzE3chqgXKKOGceXCbpM/wRgYN3S2SEF kMXb/kPu4CqyPjSMGDvOZFQQPkt8T4s= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-446-vxCQewDYPjSiEheEoxU0qg-1; Mon, 20 May 2024 13:27:06 -0400 X-MC-Unique: vxCQewDYPjSiEheEoxU0qg-1 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-42015260835so39352435e9.0 for ; Mon, 20 May 2024 10:27:06 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226025; x=1716830825; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=5CImq0b4mEhq1pg3qJ1z+g35+FLQKy2u4BGJkZOUedY=; b=Yu7FXvtuFOzaGU6zZAvVv6qY0RxMB3ijXrtw4cKVRABG2WwIxKcar+tBc6cXkOXI2E r6P49iSnC0572/A5hFZveYejr4kYVTiAY+muTQBq+f/jI5FUIwAe2S9LuhZwvLnRxlr5 /aSW2PfI+sJeIfKQSkY4ENB/5wJaUugNWAI9aucFGgySFZM3VWzoHP+trDrXXGDtQrZ+ hg5PIm1RO9rGZABrErtydMXoTMJkvL3AsLQjTHcCBNgWBbVxqWYdkg2RXMtperAJGVFG KgWgt7dx34nVqk2G44+oP6xmMa7ak9ktXVg+pAIibwMp1zV/1up4YjHIonAjkMlvdLJ2 zw0A== X-Forwarded-Encrypted: i=1; AJvYcCVJDXyzkTq6weNBo/FFzXtUhMVBintvklAK1scrjmAk5PhXU46UBU5YrBgNa9XMDuGoBQ4XXsy0Ot/dTEfVhQeOpdJCVw3qfgIvt5bM X-Gm-Message-State: AOJu0YzoW6EiyNBpctB/2XrMCg/HOP5mXh0GLv63+eXhJMULcqPy0Lbi FOtFEKIV7iPkmlH7vWViHWzCKa7xBSTfsSJdLtRdrKqA4bQXSIeFvFASXN1Lu/aZjHdzBfpPFaH 6TReprGqBX1OyxTpjoeFUoe8csK7aVcx2Zx8QuWbkeL0xXnEqePWJ3v30J6Xj6w== X-Received: by 2002:a05:600c:4f93:b0:41b:e416:43d3 with SMTP id 5b1f17b1804b1-41fead6a51dmr216213275e9.35.1716226025185; Mon, 20 May 2024 10:27:05 -0700 (PDT) X-Google-Smtp-Source: AGHT+IF/bF4bRoSUln2A7cqDJukXexNcMjambbxJHYjat/XzwST1JPeAlpPwPz2OHv0SZFXR65NeCA== X-Received: by 2002:a05:600c:4f93:b0:41b:e416:43d3 with SMTP id 5b1f17b1804b1-41fead6a51dmr216212835e9.35.1716226024359; Mon, 20 May 2024 10:27:04 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-42028c7730dsm189604005e9.25.2024.05.20.10.27.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:03 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 03/11] rust: add rcu abstraction Date: Mon, 20 May 2024 19:25:40 +0200 Message-ID: <20240520172554.182094-4-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Wedson Almeida Filho Add a simple abstraction to guard critical code sections with an rcu read lock. Co-developed-by: Andreas Hindborg Signed-off-by: Andreas Hindborg Signed-off-by: Wedson Almeida Filho Signed-off-by: Danilo Krummrich --- rust/helpers.c | 15 ++++++++++++ rust/kernel/sync.rs | 1 + rust/kernel/sync/rcu.rs | 52 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 rust/kernel/sync/rcu.rs diff --git a/rust/helpers.c b/rust/helpers.c index f9d2db1d1f33..1d3e800140fc 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -158,6 +159,20 @@ void rust_helper_init_work_with_key(struct work_struct= *work, work_func_t func, } EXPORT_SYMBOL_GPL(rust_helper_init_work_with_key); =20 +/* rcu */ +void rust_helper_rcu_read_lock(void) +{ + rcu_read_lock(); +} +EXPORT_SYMBOL_GPL(rust_helper_rcu_read_lock); + +void rust_helper_rcu_read_unlock(void) +{ + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(rust_helper_rcu_read_unlock); +/* end rcu */ + /* * `bindgen` binds 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) indi= ces. diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs index 0ab20975a3b5..1806767359fe 100644 --- a/rust/kernel/sync.rs +++ b/rust/kernel/sync.rs @@ -11,6 +11,7 @@ mod condvar; pub mod lock; mod locked_by; +pub mod rcu; =20 pub use arc::{Arc, ArcBorrow, UniqueArc}; pub use condvar::{new_condvar, CondVar, CondVarTimeoutResult}; diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs new file mode 100644 index 000000000000..1a1c8ea49359 --- /dev/null +++ b/rust/kernel/sync/rcu.rs @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! RCU support. +//! +//! C header: [`include/linux/rcupdate.h`](../../../../include/linux/rcupd= ate.h) + +use crate::bindings; +use core::marker::PhantomData; + +/// Evidence that the RCU read side lock is held on the current thread/CPU. +/// +/// The type is explicitly not `Send` because this property is per-thread/= CPU. +/// +/// # Invariants +/// +/// The RCU read side lock is actually held while instances of this guard = exist. +pub struct Guard { + _not_send: PhantomData<*mut ()>, +} + +impl Guard { + /// Acquires the RCU read side lock and returns a guard. + pub fn new() -> Self { + // SAFETY: An FFI call with no additional requirements. + unsafe { bindings::rcu_read_lock() }; + // INVARIANT: The RCU read side lock was just acquired above. + Self { + _not_send: PhantomData, + } + } + + /// Explicitly releases the RCU read side lock. + pub fn unlock(self) {} +} + +impl Default for Guard { + fn default() -> Self { + Self::new() + } +} + +impl Drop for Guard { + fn drop(&mut self) { + // SAFETY: By the type invariants, the rcu read side is locked, so= it is ok to unlock it. + unsafe { bindings::rcu_read_unlock() }; + } +} + +/// Acquires the RCU read side lock. +pub fn read_lock() -> Guard { + Guard::new() +} --=20 2.45.1 From nobody Wed Feb 11 05:41:11 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (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 CEAAD13959F for ; Mon, 20 May 2024 17:27:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226034; cv=none; b=TbMrT2sHzwoXBWHTsT3vkCJrMRwNOaU/PRuLn7HmCpLiJwq8H6ymSc7wLywDd2AVL0PPOWvmpeP3kPvnev2LsW+6VM2IgOIXJzLSzxOdOSz8gCFS1NDRlTbiEsMEKKSf/MhFl25xtVIpymtcda/w0yWGMwG8dd+MTu/ABCibCls= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226034; c=relaxed/simple; bh=N85iMowovDf/JhZ/dLh0R6pfZHYB+vOre1zGRULPwYU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Xlg1bZvVCvWe4AAjkA50IRRJMsX7t9tV+1/BDf2BbEjHRi7r61bqtLYDVuReUHvEsMaQhxaiYGxnWr+BCLXISGUOuGjH4K3mYn79ih9ECWoznWT3ZZswUpcYjFPAmsqxgHm8vNGuLQWvgaWY6qep4pxoJqKyJF2qdzEDeAGVwmw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=dAoVHpZS; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="dAoVHpZS" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226031; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Fu/toNLVWWyRq23aezf3ytH43dbJ0WC5O1SCuEo+dyc=; b=dAoVHpZSv1PnPnETiNC7pnXDbLbGlT9ExDnAAxaweoqiXWgts8mJ5T7wPvFDATaz86aJhp ZDXiIdHaAI+lGfpLqOWu7afpsJQYU00rZz8k/v6TlBXn8ySirf170fIf6WfJwZf9GeosZf 8G2xAB+yRTVDgY5lUs9tR2Nl9FrI6zw= Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-378-8nF8dQO4OEGYm-53TJY2LA-1; Mon, 20 May 2024 13:27:10 -0400 X-MC-Unique: 8nF8dQO4OEGYm-53TJY2LA-1 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-351bf7f7f73so4851015f8f.0 for ; Mon, 20 May 2024 10:27:09 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226029; x=1716830829; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Fu/toNLVWWyRq23aezf3ytH43dbJ0WC5O1SCuEo+dyc=; b=OJQ0NFqCSuZzrgQCIKJwgbXHz6vz2GG3NWffhoIWWXBaAnLj8FNNbXpX5t23j43KkJ MRhXH0ve+lYT8e5jWkIuLx7gXFX7NsOtDS9UwvkfGRA043JyRvk6rUVmILOE9skjZkNK Uz+hJDvDAKT7Yw6VzLw6t64j0YneLMmarOwTpGkVtWGnBB3mBP4q4KU7j4G7wb55N5uv cDtEMbJDAVDMzxSCkAvcczODSKlfM8k1eTHeiabRUeGhCEG/eiF/0jpI2dH/SwOjsmel z1P/EbPpoU0KQfuNFC21d/Bj8FBmzFmXkq4zsX9i0TE+7znJXEXY/lJqVDwtRS+21iu0 jFwg== X-Forwarded-Encrypted: i=1; AJvYcCW+X8utODWx+pHSOxazz9Er1gKjxLuEKGcq/WEbjN9F8CsQVo9TWoXd6cPkuWRNAHaRVthyPIUMyQZ2ydhTQTGBFxYVzmS48A5bmvc3 X-Gm-Message-State: AOJu0YyIcEo4iP8EQsEYAGZ3yKXPBDVbCuqqriKdK6gdH07DPSbkyfnw LiR5Sf/Hz1vUSTFNS5uc8h3MB9eS4ekIydqw8m31yxUIhlID5bMhTB/xTWSFWKmZYq/P3O4WpJ6 bXh/D/CwFrl3aXA5Swz5yCHYCriSUFj3/AnL5o2KCouOElTAvj6ld/07wzBfcTQ== X-Received: by 2002:adf:ef4c:0:b0:349:bccc:a1e7 with SMTP id ffacd0b85a97d-3504a6310c6mr30303194f8f.19.1716226028925; Mon, 20 May 2024 10:27:08 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFBsX478+NUBs509kzcMbzIJbFh4JBLi9OQ2gqKGhis3wbf55/L1xrKM6Shc/S0VsZJL65/uw== X-Received: by 2002:adf:ef4c:0:b0:349:bccc:a1e7 with SMTP id ffacd0b85a97d-3504a6310c6mr30303156f8f.19.1716226028289; Mon, 20 May 2024 10:27:08 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-354ccd66865sm784772f8f.89.2024.05.20.10.27.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:07 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 04/11] rust: add revocable mutex Date: Mon, 20 May 2024 19:25:41 +0200 Message-ID: <20240520172554.182094-5-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Wedson Almeida Filho This is a mutex where access to its contents can be revoked at runtime. This is different from `Revocable` in a few ways: 1. The caller may sleep while holding a guard. 2. Accessors are all serialised. 3. Accessing is not as cheap (because it involves acquiring the mutex). 4. The size of the object is larger (because it involves a mutex + flag). An example of where this a good fit to be used in device state that holds registrations to other subsystems. Signed-off-by: Wedson Almeida Filho Co-developed-by: Andreas Hindborg Signed-off-by: Andreas Hindborg Co-developed-by: Danilo Krummrich Signed-off-by: Danilo Krummrich --- rust/kernel/sync.rs | 2 + rust/kernel/sync/revocable.rs | 148 ++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 rust/kernel/sync/revocable.rs diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs index 1806767359fe..13257a4bcff6 100644 --- a/rust/kernel/sync.rs +++ b/rust/kernel/sync.rs @@ -12,12 +12,14 @@ pub mod lock; mod locked_by; pub mod rcu; +mod revocable; =20 pub use arc::{Arc, ArcBorrow, UniqueArc}; pub use condvar::{new_condvar, CondVar, CondVarTimeoutResult}; pub use lock::mutex::{new_mutex, Mutex}; pub use lock::spinlock::{new_spinlock, SpinLock}; pub use locked_by::LockedBy; +pub use revocable::{RevocableMutex, RevocableMutexGuard}; =20 /// Represents a lockdep class. It's a wrapper around C's `lock_class_key`. #[repr(transparent)] diff --git a/rust/kernel/sync/revocable.rs b/rust/kernel/sync/revocable.rs new file mode 100644 index 000000000000..4632bb838180 --- /dev/null +++ b/rust/kernel/sync/revocable.rs @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Synchronisation primitives where access to their contents can be revok= ed at runtime. + +use macros::pin_data; + +use crate::{ + init::PinInit, + pin_init, + str::CStr, + sync::{lock, lock::Lock, LockClassKey}, +}; +use core::{ + mem::MaybeUninit, + ops::{Deref, DerefMut}, + pin::Pin, +}; + +use super::lock::Guard; + +/// The state within the revocable synchronisation primitive. +/// +/// We don't use simply `Option` because we need to drop in-place becau= se the contents are +/// implicitly pinned. +/// +/// # Invariants +/// +/// The `is_available` field determines if `data` is initialised. +struct Inner { + is_available: bool, + data: MaybeUninit, +} + +impl Inner { + fn new(data: T) -> Self { + // INVARIANT: `data` is initialised and `is_available` is `true`, = so the state matches. + Self { + is_available: true, + data: MaybeUninit::new(data), + } + } + + fn drop_in_place(&mut self) { + if !self.is_available { + // Already dropped. + return; + } + + // INVARIANT: `data` is being dropped and `is_available` is set to= `false`, so the state + // matches. + self.is_available =3D false; + + // SAFETY: By the type invariants, `data` is valid because `is_ava= ilable` was true. + unsafe { self.data.assume_init_drop() }; + } +} + +impl Drop for Inner { + fn drop(&mut self) { + self.drop_in_place(); + } +} + +#[pin_data] +pub struct Revocable { + #[pin] + inner: Lock, B>, +} + +/// Safely initialises a [`Revocable`] instance with the given name, gener= ating a new lock class. +// #[macro_export] +// macro_rules! revocable_init { +// ($mutex:expr, $name:literal) =3D> { +// $crate::init_with_lockdep!($mutex, $name) +// }; +// } + +impl Revocable +where + B: lock::Backend, +{ + /// Creates a new revocable instance of the given lock. + pub fn new(data: T, name: &'static CStr, key: &'static LockClassKey) -= > impl PinInit { + pin_init!(Self { + inner <- Lock::new(Inner::new(data), name, key) , + }) + } + + /// Revokes access to and drops the wrapped object. + /// + /// Revocation and dropping happen after ongoing accessors complete. + pub fn revoke(&self) { + self.lock().drop_in_place(); + } + + pub fn try_write(&self) -> Option> { + let inner =3D self.lock(); + + if !inner.is_available { + return None; + } + + Some(RevocableGuard::new(inner)) + } + + fn lock(&self) -> Guard<'_, Inner, B> { + self.inner.lock() + } +} + +pub struct RevocableGuard<'a, T, B> +where + B: lock::Backend, +{ + guard: Guard<'a, Inner, B>, +} + +impl<'a, T, B: lock::Backend> RevocableGuard<'a, T, B> { + fn new(guard: Guard<'a, Inner, B>) -> Self { + Self { guard } + } +} + +impl RevocableGuard<'_, T, B> { + pub fn as_pinned_mut(&mut self) -> Pin<&mut T> { + unsafe { Pin::new_unchecked(&mut *self) } + } +} + +impl Deref for RevocableGuard<'_, T, B> { + type Target =3D T; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.guard.data.as_ptr() } + } +} + +impl DerefMut for RevocableGuard<'_, T, B> { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *self.guard.data.as_mut_ptr() } + } +} + +/// Type alias for a `Revocable` with a `MutexBackend`. +pub type RevocableMutex =3D Revocable; + +/// Type alias for a `RevocableGuard` with a `MutexBackend`. +pub type RevocableMutexGuard<'a, T> =3D RevocableGuard<'a, T, super::lock:= :mutex::MutexBackend>; --=20 2.45.1 From nobody Wed Feb 11 05:41:11 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (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 A52BB139D0E for ; Mon, 20 May 2024 17:27:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226038; cv=none; b=PbxfCPf8pxvD/0JFVl6+2AglU8Rw8D+D9ZE/qk9fKLThn/1Y70yhjp+SVgJow92OZB1WAOEq7GG9k2k2HhcJuS/Aa5X1RECtEa1d/7jXTZPdZK+1HDQgyG6d8ljdQMa5LSeiVLKkBvDneXaKCWvPomLD+B7wHcfx95AiJu4ymkY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226038; c=relaxed/simple; bh=0F0rfov5AgIy+BJUKXtpd/KEtgXk5R3qsYOeTsHEmZE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=c1TJlQCToRicg0letTeMZTY1M6lSWZJDk/XHOaicYb5SqdpxIHIPlrffQyeBBOpWKomWyQ1gl4jN0/p68jOx7Cc/h4aYgtuOIMMZaeg4pF7jXznDpuG2znqNdUfX4b9T7bH/W+bT+QSPzDpo68kQmM0nQSY/X4crR0Jtx7hEPkU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=acU0rm1k; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="acU0rm1k" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226035; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=0Wnrlx8Ze5f4eVrQIFsuBZ5dBRmGRAk/pP24qmNaV1o=; b=acU0rm1k1DzGrYyRCThba68QD36JmcSBRe4zVzPvEwR8KxKpZYJLCiXnKf1o7tN7XKcnpk wHnp00cIsv/e1V0m2iG6LF1DSR3H3mNPEpUNuPXuWOXQmMh77BlDVxDH4oxDqn5ZwbG2DC T0S45+fevODdlqECn05+3HlLaIWIx5M= Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-584-9K3XSX5DNRmPioZw6jqfaw-1; Mon, 20 May 2024 13:27:14 -0400 X-MC-Unique: 9K3XSX5DNRmPioZw6jqfaw-1 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-41ff6049fdbso54830805e9.0 for ; Mon, 20 May 2024 10:27:14 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226033; x=1716830833; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=0Wnrlx8Ze5f4eVrQIFsuBZ5dBRmGRAk/pP24qmNaV1o=; b=iSXfryBcyfMfg9kRBUfAmF0xoinR5u6H85u/qTucL8zknJxlEIgWy6b6TsOUP2lnMS GslF6Gibj/mAmPf1OPYj2f7ZY7v80yNzCGLFUPABF5V4Y+ygHaRLT+VrKg8vLUaj4zH4 hPzzthRrLNRS91/YueCKyvKDLBeN7Q+3+bP04WvVjUyXz6CbaidWEj527WaiAGxQTFRH KCIT9LXid9wOm2Z7OkJuMO2I5pynQwD9jPL6ckBxQba2+E/DQ6uSixukacrUnKNHuOHQ sj9yo/LPWfHifYkN19Q2iXCOkcFJQK4uPaZww9fUxdfS+rw24X26HozQGNSIMlB1a56m 4dVQ== X-Forwarded-Encrypted: i=1; AJvYcCWRpQLJE+sf+myVsCtCOTlT4qmqCRNfKHipjgUn4lsG32gmFH0prmyNJ5Ylr0DCDOAbTbRztngDeuN8UbZZ9nJi0EOvxvNHl3DBC/J5 X-Gm-Message-State: AOJu0Yyqn8k+SgTMNzU61glDh6Rq0+1qD3DWVcgThndMIvzn7KTlje7f 8Bm/BynIhcp1MYaUNhnOd3COJL/HhKSxU7pkdpcK5H9MWsjJKhWLEiynp+UcPslWeaAcDdSPGBr c3qUkyroKv1l0MkumYEcXH1JXrHmLwXxVNL8y4zC4nYiULBw3rBnDaRfm3pIc7Q== X-Received: by 2002:a05:600c:1c84:b0:420:29dd:84df with SMTP id 5b1f17b1804b1-42029dd8775mr125126995e9.6.1716226032654; Mon, 20 May 2024 10:27:12 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFlR0P93NDSEN+c3uilX1UD/l/rtLF9q10KC2o8WHFlFlK9VFo1eKLTW22d/kSe0Iv2GKa+KQ== X-Received: by 2002:a05:600c:1c84:b0:420:29dd:84df with SMTP id 5b1f17b1804b1-42029dd8775mr125126795e9.6.1716226032228; Mon, 20 May 2024 10:27:12 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-41fccce25casm426922425e9.20.2024.05.20.10.27.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:11 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 05/11] rust: add revocable objects Date: Mon, 20 May 2024 19:25:42 +0200 Message-ID: <20240520172554.182094-6-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Wedson Almeida Filho This implements the Revocable and AsyncRevocable types. Revocable allows access to objects to be safely revoked at run time. This is useful, for example, for resources allocated during device probe; when the device is removed, the driver should stop accessing the device resources even if other state is kept in memory due to existing references (i.e., device context data is ref-counted and has a non-zero refcount after removal of the device). AsyncRevocable allows access to objects to be revoked without having to wait for existing users to complete. This will be used to drop futures in tasks when executors are being torn down. Co-developed-by: Andreas Hindborg Signed-off-by: Andreas Hindborg Signed-off-by: Wedson Almeida Filho Signed-off-by: Danilo Krummrich --- rust/kernel/lib.rs | 1 + rust/kernel/revocable.rs | 441 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 442 insertions(+) create mode 100644 rust/kernel/revocable.rs diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 698121c925f3..d7d415429517 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -40,6 +40,7 @@ pub mod net; pub mod prelude; pub mod print; +pub mod revocable; mod static_assert; #[doc(hidden)] pub mod std_vendor; diff --git a/rust/kernel/revocable.rs b/rust/kernel/revocable.rs new file mode 100644 index 000000000000..71408039a117 --- /dev/null +++ b/rust/kernel/revocable.rs @@ -0,0 +1,441 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Revocable objects. +//! +//! The [`Revocable`] type wraps other types and allows access to them to = be revoked. The existence +//! of a [`RevocableGuard`] ensures that objects remain valid. + +use crate::{ + bindings, + init::{self}, + prelude::*, + sync::rcu, +}; +use core::{ + cell::UnsafeCell, + marker::PhantomData, + mem::MaybeUninit, + ops::Deref, + ptr::drop_in_place, + sync::atomic::{fence, AtomicBool, AtomicU32, Ordering}, +}; + +/// An object that can become inaccessible at runtime. +/// +/// Once access is revoked and all concurrent users complete (i.e., all ex= isting instances of +/// [`RevocableGuard`] are dropped), the wrapped object is also dropped. +/// +/// # Examples +/// +/// ``` +/// # use kernel::revocable::Revocable; +/// +/// struct Example { +/// a: u32, +/// b: u32, +/// } +/// +/// fn add_two(v: &Revocable) -> Option { +/// let guard =3D v.try_access()?; +/// Some(guard.a + guard.b) +/// } +/// +/// let v =3D Revocable::new(Example { a: 10, b: 20 }); +/// assert_eq!(add_two(&v), Some(30)); +/// v.revoke(); +/// assert_eq!(add_two(&v), None); +/// ``` +/// +/// Sample example as above, but explicitly using the rcu read side lock. +/// +/// ``` +/// # use kernel::revocable::Revocable; +/// use kernel::sync::rcu; +/// +/// struct Example { +/// a: u32, +/// b: u32, +/// } +/// +/// fn add_two(v: &Revocable) -> Option { +/// let guard =3D rcu::read_lock(); +/// let e =3D v.try_access_with_guard(&guard)?; +/// Some(e.a + e.b) +/// } +/// +/// let v =3D Revocable::new(Example { a: 10, b: 20 }); +/// assert_eq!(add_two(&v), Some(30)); +/// v.revoke(); +/// assert_eq!(add_two(&v), None); +/// ``` +#[pin_data(PinnedDrop)] +pub struct Revocable { + is_available: AtomicBool, + #[pin] + data: MaybeUninit>, +} + +// SAFETY: `Revocable` is `Send` if the wrapped object is also `Send`. Thi= s is because while the +// functionality exposed by `Revocable` can be accessed from any thread/CP= U, it is possible that +// this isn't supported by the wrapped object. +unsafe impl Send for Revocable {} + +// SAFETY: `Revocable` is `Sync` if the wrapped object is both `Send` and = `Sync`. We require `Send` +// from the wrapped object as well because of `Revocable::revoke`, which = can trigger the `Drop` +// implementation of the wrapped object from an arbitrary thread. +unsafe impl Sync for Revocable {} + +impl Revocable { + /// Creates a new revocable instance of the given data. + pub fn new(data: impl PinInit) -> impl PinInit { + pin_init!(Self { + is_available: AtomicBool::new(true), + data <- unsafe { + init::pin_init_from_closure(move |slot: *mut MaybeUninit>| { + init::PinInit::::__pinne= d_init(data, + = slot as *mut T)?; + Ok::<(), core::convert::Infallible>(()) + }) + }, + }) + } + + /// Tries to access the \[revocable\] wrapped object. + /// + /// Returns `None` if the object has been revoked and is therefore no = longer accessible. + /// + /// Returns a guard that gives access to the object otherwise; the obj= ect is guaranteed to + /// remain accessible while the guard is alive. In such cases, callers= are not allowed to sleep + /// because another CPU may be waiting to complete the revocation of t= his object. + pub fn try_access(&self) -> Option> { + let guard =3D rcu::read_lock(); + if self.is_available.load(Ordering::Relaxed) { + // SAFETY: Since `self.is_available` is true, data is initiali= sed and has to remain + // valid because the RCU read side lock prevents it from being= dropped. + Some(unsafe { RevocableGuard::new(self.data.assume_init_ref().= get(), guard) }) + } else { + None + } + } + + /// Tries to access the \[revocable\] wrapped object. + /// + /// Returns `None` if the object has been revoked and is therefore no = longer accessible. + /// + /// Returns a shared reference to the object otherwise; the object is = guaranteed to + /// remain accessible while the rcu read side guard is alive. In such = cases, callers are not + /// allowed to sleep because another CPU may be waiting to complete th= e revocation of this + /// object. + pub fn try_access_with_guard<'a>(&'a self, _guard: &'a rcu::Guard) -> = Option<&'a T> { + if self.is_available.load(Ordering::Relaxed) { + // SAFETY: Since `self.is_available` is true, data is initiali= sed and has to remain + // valid because the RCU read side lock prevents it from being= dropped. + Some(unsafe { &*self.data.assume_init_ref().get() }) + } else { + None + } + } + + /// Revokes access to and drops the wrapped object. + /// + /// Access to the object is revoked immediately to new callers of [`Re= vocable::try_access`]. If + /// there are concurrent users of the object (i.e., ones that called [= `Revocable::try_access`] + /// beforehand and still haven't dropped the returned guard), this fun= ction waits for the + /// concurrent access to complete before dropping the wrapped object. + pub fn revoke(&self) { + if self + .is_available + .compare_exchange(true, false, Ordering::Relaxed, Ordering::Re= laxed) + .is_ok() + { + // SAFETY: Just an FFI call, there are no further requirements. + unsafe { bindings::synchronize_rcu() }; + + // SAFETY: We know `self.data` is valid because only one CPU c= an succeed the + // `compare_exchange` above that takes `is_available` from `tr= ue` to `false`. + unsafe { drop_in_place(self.data.assume_init_ref().get()) }; + } + } +} + +#[pinned_drop] +impl PinnedDrop for Revocable { + fn drop(self: Pin<&mut Self>) { + // Drop only if the data hasn't been revoked yet (in which case it= has already been + // dropped). + // SAFETY: We are not moving out of `p`, only dropping in place + let p =3D unsafe { self.get_unchecked_mut() }; + if *p.is_available.get_mut() { + // SAFETY: We know `self.data` is valid because no other CPU h= as changed + // `is_available` to `false` yet, and no other CPU can do it a= nymore because this CPU + // holds the only reference (mutable) to `self` now. + unsafe { drop_in_place(p.data.assume_init_ref().get()) }; + } + } +} + +/// A guard that allows access to a revocable object and keeps it alive. +/// +/// CPUs may not sleep while holding on to [`RevocableGuard`] because it's= in atomic context +/// holding the RCU read-side lock. +/// +/// # Invariants +/// +/// The RCU read-side lock is held while the guard is alive. +pub struct RevocableGuard<'a, T> { + data_ref: *const T, + _rcu_guard: rcu::Guard, + _p: PhantomData<&'a ()>, +} + +impl RevocableGuard<'_, T> { + fn new(data_ref: *const T, rcu_guard: rcu::Guard) -> Self { + Self { + data_ref, + _rcu_guard: rcu_guard, + _p: PhantomData, + } + } +} + +impl Deref for RevocableGuard<'_, T> { + type Target =3D T; + + fn deref(&self) -> &Self::Target { + // SAFETY: By the type invariants, we hold the rcu read-side lock,= so the object is + // guaranteed to remain valid. + unsafe { &*self.data_ref } + } +} + +/// An object that can become inaccessible at runtime. +/// +/// Once access is revoked and all concurrent users complete (i.e., all ex= isting instances of +/// [`AsyncRevocableGuard`] are dropped), the wrapped object is also dropp= ed. +/// +/// Unlike [`Revocable`], [`AsyncRevocable`] does not wait for concurrent = users of the wrapped +/// object to finish before [`AsyncRevocable::revoke`] completes -- thus t= he async qualifier. This +/// has the advantage of not requiring RCU locks or waits of any kind. +/// +/// # Examples +/// +/// ``` +/// # use kernel::revocable::AsyncRevocable; +/// +/// struct Example { +/// a: u32, +/// b: u32, +/// } +/// +/// fn add_two(v: &AsyncRevocable) -> Option { +/// let guard =3D v.try_access()?; +/// Some(guard.a + guard.b) +/// } +/// +/// let v =3D AsyncRevocable::new(Example { a: 10, b: 20 }); +/// assert_eq!(add_two(&v), Some(30)); +/// v.revoke(); +/// assert_eq!(add_two(&v), None); +/// ``` +/// +/// Example where revocation happens while there is a user: +/// +/// ``` +/// # use kernel::revocable::AsyncRevocable; +/// use core::sync::atomic::{AtomicBool, Ordering}; +/// +/// struct Example { +/// a: u32, +/// b: u32, +/// } +/// +/// static DROPPED: AtomicBool =3D AtomicBool::new(false); +/// +/// impl Drop for Example { +/// fn drop(&mut self) { +/// DROPPED.store(true, Ordering::Relaxed); +/// } +/// } +/// +/// fn add_two(v: &AsyncRevocable) -> Option { +/// let guard =3D v.try_access()?; +/// Some(guard.a + guard.b) +/// } +/// +/// let v =3D AsyncRevocable::new(Example { a: 10, b: 20 }); +/// assert_eq!(add_two(&v), Some(30)); +/// +/// let guard =3D v.try_access().unwrap(); +/// assert!(!v.is_revoked()); +/// assert!(!DROPPED.load(Ordering::Relaxed)); +/// v.revoke(); +/// assert!(!DROPPED.load(Ordering::Relaxed)); +/// assert!(v.is_revoked()); +/// assert!(v.try_access().is_none()); +/// assert_eq!(guard.a + guard.b, 30); +/// drop(guard); +/// assert!(DROPPED.load(Ordering::Relaxed)); +/// ``` +pub struct AsyncRevocable { + usage_count: AtomicU32, + data: MaybeUninit>, +} + +// SAFETY: `AsyncRevocable` is `Send` if the wrapped object is also `Send`= . This is because while +// the functionality exposed by `AsyncRevocable` can be accessed from any = thread/CPU, it is +// possible that this isn't supported by the wrapped object. +unsafe impl Send for AsyncRevocable {} + +// SAFETY: `AsyncRevocable` is `Sync` if the wrapped object is both `Send`= and `Sync`. We require +// `Send` from the wrapped object as well because of `AsyncRevocable::rev= oke`, which can trigger +// the `Drop` implementation of the wrapped object from an arbitrary threa= d. +unsafe impl Sync for AsyncRevocable {} + +const REVOKED: u32 =3D 0x80000000; +const COUNT_MASK: u32 =3D !REVOKED; +const SATURATED_COUNT: u32 =3D REVOKED - 1; + +impl AsyncRevocable { + /// Creates a new asynchronously revocable instance of the given data. + pub fn new(data: T) -> Self { + Self { + usage_count: AtomicU32::new(0), + data: MaybeUninit::new(UnsafeCell::new(data)), + } + } + + /// Tries to access the \[revocable\] wrapped object. + /// + /// Returns `None` if the object has been revoked and is therefore no = longer accessible. + /// + /// Returns a guard that gives access to the object otherwise; the obj= ect is guaranteed to + /// remain accessible while the guard is alive. + pub fn try_access(&self) -> Option> { + loop { + let count =3D self.usage_count.load(Ordering::Relaxed); + + // Fail attempt to access if the object is already revoked. + if count & REVOKED !=3D 0 { + return None; + } + + // No need to increment if the count is saturated. + if count =3D=3D SATURATED_COUNT + || self + .usage_count + .compare_exchange(count, count + 1, Ordering::Relaxed,= Ordering::Relaxed) + .is_ok() + { + return Some(AsyncRevocableGuard { revocable: self }); + } + } + } + + /// Revokes access to the protected object. + /// + /// Returns `true` if access has been revoked, or `false` when the obj= ect has already been + /// revoked by a previous call to [`AsyncRevocable::revoke`]. + /// + /// This call is non-blocking, that is, no new users of the revocable = object will be allowed, + /// but potential current users are able to continue to use it and the= thread won't wait for + /// them to finish. In such cases, the object will be dropped when the= last user completes. + pub fn revoke(&self) -> bool { + // Set the `REVOKED` bit. + // + // The acquire barrier matches up with the release when decrementi= ng the usage count. + let prev =3D self.usage_count.fetch_or(REVOKED, Ordering::Acquire); + if prev & REVOKED !=3D 0 { + // Another thread already revoked this object. + return false; + } + + if prev =3D=3D 0 { + // SAFETY: This thread just revoked the object and the usage c= ount is zero, so the + // object is valid and there will be no future users. + unsafe { drop_in_place(UnsafeCell::raw_get(self.data.as_ptr())= ) }; + } + + true + } + + /// Returns whether access to the object has been revoked. + pub fn is_revoked(&self) -> bool { + self.usage_count.load(Ordering::Relaxed) & REVOKED !=3D 0 + } +} + +impl Drop for AsyncRevocable { + fn drop(&mut self) { + let count =3D *self.usage_count.get_mut(); + if count !=3D REVOKED { + // The object hasn't been dropped yet, so we do it now. + + // This matches with the release when decrementing the usage c= ount. + fence(Ordering::Acquire); + + // SAFETY: Since `count` is does not indicate a count of 0 and= the REVOKED bit set, the + // object is still valid. + unsafe { drop_in_place(UnsafeCell::raw_get(self.data.as_ptr())= ) }; + } + } +} + +/// A guard that allows access to a revocable object and keeps it alive. +/// +/// # Invariants +/// +/// The owner owns an increment on the usage count (which may have saturat= ed it), which keeps the +/// revocable object alive. +pub struct AsyncRevocableGuard<'a, T> { + revocable: &'a AsyncRevocable, +} + +impl Deref for AsyncRevocableGuard<'_, T> { + type Target =3D T; + + fn deref(&self) -> &Self::Target { + // SAFETY: The type invariants guarantee that the caller owns an i= ncrement. + unsafe { &*self.revocable.data.assume_init_ref().get() } + } +} + +impl Drop for AsyncRevocableGuard<'_, T> { + fn drop(&mut self) { + loop { + let count =3D self.revocable.usage_count.load(Ordering::Relaxe= d); + let actual_count =3D count & COUNT_MASK; + if actual_count =3D=3D SATURATED_COUNT { + // The count is saturated, so we won't decrement (nor do w= e drop the object). + return; + } + + if actual_count =3D=3D 0 { + // Trying to underflow the count. + panic!("actual_count is zero"); + } + + // On success, we use release ordering, which matches with the= acquire in one of the + // places where we drop the object, namely: below, in `AsyncRe= vocable::revoke`, or in + // `AsyncRevocable::drop`. + if self + .revocable + .usage_count + .compare_exchange(count, count - 1, Ordering::Release, Ord= ering::Relaxed) + .is_ok() + { + if count =3D=3D 1 | REVOKED { + // `count` is now zero and it is revoked, so free it = now. + + // This matches with the release above (which may have= happened in other + // threads concurrently). + fence(Ordering::Acquire); + + // SAFETY: Since `count` was 1, the object is still al= ive. + unsafe { drop_in_place(UnsafeCell::raw_get(self.revoca= ble.data.as_ptr())) }; + } + + return; + } + } + } +} --=20 2.45.1 From nobody Wed Feb 11 05:41:11 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (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 09DB953E13 for ; Mon, 20 May 2024 17:27:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226041; cv=none; b=oMJAJ8tlvcoIDnDhZfbEt1njbL0nzQSePeAH8XB+GMnx4BymCuTLzTyJr0hbgid1QdBy5+Qpqk7ZW3hl9XfmQiuvqYVr0yOStrQzqQ3MDqaL+X1swsM2w57NWk9f1RF72ukHUcG6qkGvjbfx+tOPFhn8Nl6j8eOHy0BAE0N8TSk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226041; c=relaxed/simple; bh=Xo1H50xmNmCwFq/YzacitBz7yAiTOIYKpZRdmQ6Ez64=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=X6osESgAKVPt9kGI1ekivO1pv1mkNeqnuoVrWytw+ZzJIMmQLmUgyy/Fsd+6PokyMur+Mff6zECjVt+EY8Xcvi/0pmf0AzkaYw5CHU8ncJKrUzJIbSobPIt+bZGye/fqt0YpFmPNb7G3dgZ25x9Bhh504uj7w/N63+qcMoRzYvc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=dMBSD8ss; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="dMBSD8ss" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226039; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=agQximp23XkAIvccriflMJpmlFVjIztZE/y37x455Bs=; b=dMBSD8sspjv5W/6vGT6Ze+AozAX26Mo2Hz5M1w/0US+aJbxhr5FmoYbAP/c/uiHTrADkbB WAMy8nwt8UzRVM7NI/zUcCL+/0j++Q8nmzrHnvXWGMcFhERWKaMOXVFfGmUbjYb+F9Qbdj hSMda63zTqzl1mF9R83k/hwvkvCsUgk= Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-36-uMHFYx9fM4G6oKTQRfWSHA-1; Mon, 20 May 2024 13:27:17 -0400 X-MC-Unique: uMHFYx9fM4G6oKTQRfWSHA-1 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-34f7618a1f2so6485229f8f.1 for ; Mon, 20 May 2024 10:27:17 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226036; x=1716830836; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=agQximp23XkAIvccriflMJpmlFVjIztZE/y37x455Bs=; b=dA2gb6tp+KzcXu5fCUh3pii5W6PKp9MCnfadZ+sNL9yEouEob0RrwBf3aEXuZt52CK uipNaZUPpT8qWvx4b1bkBt1yyq4wzR3RY8Bw0cwFKgmFDwQpScLteDTx8AWseSGw+VQQ a/3y2NQmH78dhOGOmx/zh3EMprrI1LPLNbKrblSiGZsElnm0TZtlPaTpbkW4qWQ8bXjK J/+6gO1QnnPamaHOhzAJpVD0Tg989u7+8rfNXVLfxPpUNND7A7ThNv1HDIoQXqWHvWvm 31Sx0LnhOD82v5hu/i7aaGfTUyIsLiECvnogxfeqSBVhZk2nYR6+vhH9CSQc0f9hBzb6 n/mA== X-Forwarded-Encrypted: i=1; AJvYcCUIspH7p0TZxu0NZ/chLja0QsaQYIPdHU7XIlh0bGhUMugWVz2NV2wbjX4+1h582qYew1D+jJ4k1v/wGA2rn8w+fEKbOgZFWY7daPn1 X-Gm-Message-State: AOJu0Yw1ZHyw7BjunVBkiu7NPPA7N/t94s0CJBsgZWjwPUE9wnvjkskc hynmo1GITBUfS483udjgjdWuBbCNr4tvKh4ufLWH9dxdkv+CfOerxH2GR3ERUao0bzVUT321VAk tRw3kVJtUfUVTPQq2zbo3dx6WsO9E9zBlcV2wHv5D1d3Lf6qIALKh6fChZIzkHg== X-Received: by 2002:a5d:6350:0:b0:34d:8206:e76b with SMTP id ffacd0b85a97d-354b8df9230mr5873130f8f.9.1716226036481; Mon, 20 May 2024 10:27:16 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHyj+aQzyFz124Ivaxlo9plfP8f/ni9Dw8Yk9DDidlmWu1q+cPLQmubwDGw7A6qtXoC6DrB8Q== X-Received: by 2002:a5d:6350:0:b0:34d:8206:e76b with SMTP id ffacd0b85a97d-354b8df9230mr5873110f8f.9.1716226036132; Mon, 20 May 2024 10:27:16 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3518d817ee2sm25948722f8f.2.2024.05.20.10.27.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:15 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 06/11] rust: add device::Data Date: Mon, 20 May 2024 19:25:43 +0200 Message-ID: <20240520172554.182094-7-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Wedson Almeida Filho Add a generic type `device::Data` to represent driver specific data bound to a device. `device::Data` also stores and allows access to registrations, which are revoked automatically when the corresponding device is unbound, even if the `device::Data`'s reference count is non-zero. Signed-off-by: Wedson Almeida Filho Co-developed-by: Danilo Krummrich Signed-off-by: Danilo Krummrich --- rust/kernel/device.rs | 103 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index fafec70effb6..b1c3f7a0d623 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -4,11 +4,24 @@ //! //! C header: [`include/linux/device.h`](../../../../include/linux/device.= h) =20 +use macros::pin_data; + use crate::{ + alloc::flags::*, bindings, + error::Result, + init::InPlaceInit, + init::PinInit, + pin_init, + str::CStr, + sync::{LockClassKey, RevocableMutex, RevocableMutexGuard, UniqueArc}, types::{ARef, Opaque}, }; -use core::ptr; +use core::{ + ops::{Deref, DerefMut}, + pin::Pin, + ptr, +}; =20 /// A ref-counted device. /// @@ -74,3 +87,91 @@ unsafe impl Send for Device {} // SAFETY: `Device` only holds a pointer to a C device, references to whic= h are safe to be used // from any thread. unsafe impl Sync for Device {} + +/// Device data. +/// +/// When a device is unbound (for whatever reason, for example, because th= e device was unplugged or +/// because the user decided to unbind the driver), the driver is given a = chance to clean up its +/// state. +/// +/// The device data is reference-counted because other subsystems may hold= pointers to it; some +/// device state must be freed and not used anymore, while others must rem= ain accessible. +/// +/// This struct separates the device data into two categories: +/// 1. Registrations: are destroyed when the device is removed. +/// 2. General data: remain available as long as the reference count is = nonzero. +/// +/// This struct implements the `DeviceRemoval` trait such that `registrati= ons` can be revoked when +/// the device is unbound. +#[pin_data] +pub struct Data { + #[pin] + registrations: RevocableMutex, + #[pin] + general: U, +} + +/// Safely creates an new reference-counted instance of [`Data`]. +#[doc(hidden)] +#[macro_export] +macro_rules! new_device_data { + ($reg:expr, $gen:expr, $name:literal) =3D> {{ + static CLASS1: $crate::sync::LockClassKey =3D $crate::sync::LockCl= assKey::new(); + let regs =3D $reg; + let gen =3D $gen; + let name =3D $crate::c_str!($name); + $crate::device::Data::try_new(regs, gen, name, &CLASS1) + }}; +} + +impl Data { + /// Creates a new instance of `Data`. + /// + /// It is recommended that the [`new_device_data`] macro be used as it= automatically creates + /// the lock classes. + pub fn try_new( + registrations: T, + general: impl PinInit, + name: &'static CStr, + key1: &'static LockClassKey, + ) -> Result>> { + let ret =3D UniqueArc::pin_init( + pin_init!(Self { + registrations <- RevocableMutex::new( + registrations, + name, + key1, + ), + general <- general, + }), + GFP_KERNEL, + )?; + + Ok(ret) + } + + /// Returns the locked registrations if they're still available. + pub fn registrations(&self) -> Option> { + self.registrations.try_write() + } +} + +impl crate::driver::DeviceRemoval for Data { + fn device_remove(&self) { + self.registrations.revoke(); + } +} + +impl Deref for Data { + type Target =3D U; + + fn deref(&self) -> &U { + &self.general + } +} + +impl DerefMut for Data { + fn deref_mut(&mut self) -> &mut U { + &mut self.general + } +} --=20 2.45.1 From nobody Wed Feb 11 05:41:11 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (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 1FC8513A3F4 for ; Mon, 20 May 2024 17:27:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226047; cv=none; b=O2I74cGxR7YFqMfCDfFMkOoaEY6gRFrdfM53k2eZKk/tbTOrZDqroXXDyPh2bsortqyuYRxBlTib6qGOz4LZI8U2Fb9kVJG3yvxL1fhsRqJJX5dmd/Pw0bFvxUDwFpzBszgAutVcOoLBxvShNVoP2uebJBuGYu4ZU4lu0b9PZ9Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226047; c=relaxed/simple; bh=Z/Z6FQt+IfhzYJGtOfnWM7hOAZBD5lo+z7OWyx94ou4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DHsq+kDBI82RNVlCZgLoLAy2Sba0V09x2CmPouErB3LKEWiFzzfPIelkr06ymZB0meCFJlUpIz2V8P4UgAlepNI3FsRDy6cYkgpRzycb+f0iBv3ilaGCiwcok7tf88czZOWdC7SiBUWdwZyflKFnlwc1sqWqyFtWqWBBTyGz6TE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=gg/pdnz2; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="gg/pdnz2" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226044; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=8ykRJ38aWUQhPojOF0arCmW/c5j5xwBTUzFlxtSsGSA=; b=gg/pdnz24dAklWnuQx4VylVTXDgOvgEnOIPFwloF5ulEP7EwF+52q2kwvJ3TlYfdDqEqJT hfpLB5x3B0CV3BRti8Yxhmy2Ya0000cabF/U9h+zZ1OWMGzNLbilM5V8339M+WMYcqMVuK fUfKarTuuYI0z/8m/NLmiMNGkVghiBc= Received: from mail-wr1-f70.google.com (mail-wr1-f70.google.com [209.85.221.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-572-tMt8TENbNmmJSlRxsx6Rgw-1; Mon, 20 May 2024 13:27:22 -0400 X-MC-Unique: tMt8TENbNmmJSlRxsx6Rgw-1 Received: by mail-wr1-f70.google.com with SMTP id ffacd0b85a97d-354cbc79848so390447f8f.0 for ; Mon, 20 May 2024 10:27:22 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226041; x=1716830841; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=8ykRJ38aWUQhPojOF0arCmW/c5j5xwBTUzFlxtSsGSA=; b=dYHGHGt5GnScYWPICbtsFWkYytEvgz82v2U0dtNNVbS4QzLMej0r1456RU5GezFPve 6MIcFkBnI6S4X0NoLxyn/mjGJtECCKYHcOPz9wTqLoszDzCVute1/Si+hkPuohMgqn3E ClruwCu9UoCijhxO955nth6KNOmUPY4on5Fyg3gXQaqe+f94DkAEOaJ2rQdqlpBxzz+5 83u8sYeo7tPmzZ5l+MSnOcOrFhtYpVZcjiZXGU1MW6icSZjw9FjFHiqYAMKPyb45iBHZ 2jgQcOsJDe8sILV9s+LPJSVz0sE0XeKm7J3NoiDm/W0guI7bvcwgOZ95ko3xPCZVjgp5 7toQ== X-Forwarded-Encrypted: i=1; AJvYcCWAMs8lMdg8W5CO3cx8osm4ztxvs+y7ZHTnXg3lCoXl2ntTM2gN+5QFBSOX39/TyB8damvOz5Jd+K+iDcq+9Gjyx02Z22fxXx3kXP/J X-Gm-Message-State: AOJu0Yy0JLlvu77VtjjW591GTQCrKGCYgscuj9ukiiRjnPltuK+Rzb7B 6QElwKEDt1NVPgaqrCl3KSEZpH0DCme+mrEE7U/TCHnT7+BAp5+c1yIZMRqaGeECxr3R2zfuMSt fi21ZVbgMxO3xDwueUz6dQvafV+3OqimQWB5e9zsmZvehicPeju1exf25AvWeIw== X-Received: by 2002:a05:6000:1083:b0:350:2ba9:ca03 with SMTP id ffacd0b85a97d-354b8e6577cmr5817501f8f.23.1716226041235; Mon, 20 May 2024 10:27:21 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEi7mAWZQwNorKfygViuMjJctRMt7yGRf02f78cLaq2jisR8KhggcCcLDZDt+WNalb6qNuquA== X-Received: by 2002:a05:6000:1083:b0:350:2ba9:ca03 with SMTP id ffacd0b85a97d-354b8e6577cmr5817476f8f.23.1716226040747; Mon, 20 May 2024 10:27:20 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3525f7f7d88sm10352988f8f.57.2024.05.20.10.27.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:19 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Wedson Almeida Filho , Danilo Krummrich Subject: [RFC PATCH 07/11] rust: add `dev_*` print macros. Date: Mon, 20 May 2024 19:25:44 +0200 Message-ID: <20240520172554.182094-8-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Wedson Almeida Filho Implement `dev_*` print macros for `device::Device`. They behave like the macros with the same names in C, i.e., they print messages to the kernel ring buffer with the given level, prefixing the messages with corresponding device information. Signed-off-by: Wedson Almeida Filho Signed-off-by: Danilo Krummrich --- rust/kernel/device.rs | 321 +++++++++++++++++++++++++++++++++++++++++ rust/kernel/prelude.rs | 2 + 2 files changed, 323 insertions(+) diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index b1c3f7a0d623..2988aeb4e040 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -18,11 +18,15 @@ types::{ARef, Opaque}, }; use core::{ + fmt, ops::{Deref, DerefMut}, pin::Pin, ptr, }; =20 +#[cfg(CONFIG_PRINTK)] +use crate::c_str; + /// A ref-counted device. /// /// # Invariants @@ -66,6 +70,110 @@ pub unsafe fn as_ref<'a>(ptr: *mut bindings::device) ->= &'a Self { // SAFETY: Guaranteed by the safety requirements of the function. unsafe { &*ptr.cast() } } + + /// Prints an emergency-level message (level 0) prefixed with device i= nformation. + /// + /// More details are available from [`dev_emerg`]. + /// + /// [`dev_emerg`]: crate::dev_emerg + pub fn pr_emerg(&self, args: fmt::Arguments<'_>) { + // SAFETY: `klevel` is null-terminated, uses one of the kernel con= stants. + unsafe { self.printk(bindings::KERN_EMERG, args) }; + } + + /// Prints an alert-level message (level 1) prefixed with device infor= mation. + /// + /// More details are available from [`dev_alert`]. + /// + /// [`dev_alert`]: crate::dev_alert + pub fn pr_alert(&self, args: fmt::Arguments<'_>) { + // SAFETY: `klevel` is null-terminated, uses one of the kernel con= stants. + unsafe { self.printk(bindings::KERN_ALERT, args) }; + } + + /// Prints a critical-level message (level 2) prefixed with device inf= ormation. + /// + /// More details are available from [`dev_crit`]. + /// + /// [`dev_crit`]: crate::dev_crit + pub fn pr_crit(&self, args: fmt::Arguments<'_>) { + // SAFETY: `klevel` is null-terminated, uses one of the kernel con= stants. + unsafe { self.printk(bindings::KERN_CRIT, args) }; + } + + /// Prints an error-level message (level 3) prefixed with device infor= mation. + /// + /// More details are available from [`dev_err`]. + /// + /// [`dev_err`]: crate::dev_err + pub fn pr_err(&self, args: fmt::Arguments<'_>) { + // SAFETY: `klevel` is null-terminated, uses one of the kernel con= stants. + unsafe { self.printk(bindings::KERN_ERR, args) }; + } + + /// Prints a warning-level message (level 4) prefixed with device info= rmation. + /// + /// More details are available from [`dev_warn`]. + /// + /// [`dev_warn`]: crate::dev_warn + pub fn pr_warn(&self, args: fmt::Arguments<'_>) { + // SAFETY: `klevel` is null-terminated, uses one of the kernel con= stants. + unsafe { self.printk(bindings::KERN_WARNING, args) }; + } + + /// Prints a notice-level message (level 5) prefixed with device infor= mation. + /// + /// More details are available from [`dev_notice`]. + /// + /// [`dev_notice`]: crate::dev_notice + pub fn pr_notice(&self, args: fmt::Arguments<'_>) { + // SAFETY: `klevel` is null-terminated, uses one of the kernel con= stants. + unsafe { self.printk(bindings::KERN_NOTICE, args) }; + } + + /// Prints an info-level message (level 6) prefixed with device inform= ation. + /// + /// More details are available from [`dev_info`]. + /// + /// [`dev_info`]: crate::dev_info + pub fn pr_info(&self, args: fmt::Arguments<'_>) { + // SAFETY: `klevel` is null-terminated, uses one of the kernel con= stants. + unsafe { self.printk(bindings::KERN_INFO, args) }; + } + + /// Prints a debug-level message (level 7) prefixed with device inform= ation. + /// + /// More details are available from [`dev_dbg`]. + /// + /// [`dev_dbg`]: crate::dev_dbg + pub fn pr_dbg(&self, args: fmt::Arguments<'_>) { + if cfg!(debug_assertions) { + // SAFETY: `klevel` is null-terminated, uses one of the kernel= constants. + unsafe { self.printk(bindings::KERN_DEBUG, args) }; + } + } + + /// Prints the provided message to the console. + /// + /// # Safety + /// + /// Callers must ensure that `klevel` is null-terminated; in particula= r, one of the + /// `KERN_*`constants, for example, `KERN_CRIT`, `KERN_ALERT`, etc. + #[cfg_attr(not(CONFIG_PRINTK), allow(unused_variables))] + unsafe fn printk(&self, klevel: &[u8], msg: fmt::Arguments<'_>) { + // SAFETY: `klevel` is null-terminated and one of the kernel const= ants. `self.as_raw` + // is valid because `self` is valid. The "%pA" format string expec= ts a pointer to + // `fmt::Arguments`, which is what we're passing as the last argum= ent. + #[cfg(CONFIG_PRINTK)] + unsafe { + bindings::_dev_printk( + klevel as *const _ as *const core::ffi::c_char, + self.as_raw(), + c_str!("%pA").as_char_ptr(), + &msg as *const _ as *const core::ffi::c_void, + ) + }; + } } =20 // SAFETY: Instances of `Device` are always ref-counted. @@ -175,3 +283,216 @@ fn deref_mut(&mut self) -> &mut U { &mut self.general } } + +#[doc(hidden)] +#[macro_export] +macro_rules! dev_printk { + ($method:ident, $dev:expr, $($f:tt)*) =3D> { + { + // We have an explicity `use` statement here so that callers o= f this macro are not + // required to explicitly use the `RawDevice` trait to use its= functions. + use $crate::device::Device; + ($dev).$method(core::format_args!($($f)*)); + } + } +} + +/// Prints an emergency-level message (level 0) prefixed with device infor= mation. +/// +/// This level should be used if the system is unusable. +/// +/// Equivalent to the kernel's `dev_emerg` macro. +/// +/// Mimics the interface of [`std::print!`]. More information about the sy= ntax is available from +/// [`core::fmt`] and [`alloc::format!`]. +/// +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// # use kernel::device::Device; +/// +/// fn example(dev: &Device) { +/// dev_emerg!(dev, "hello {}\n", "there"); +/// } +/// ``` +#[macro_export] +macro_rules! dev_emerg { + ($($f:tt)*) =3D> { $crate::dev_printk!(pr_emerg, $($f)*); } +} + +/// Prints an alert-level message (level 1) prefixed with device informati= on. +/// +/// This level should be used if action must be taken immediately. +/// +/// Equivalent to the kernel's `dev_alert` macro. +/// +/// Mimics the interface of [`std::print!`]. More information about the sy= ntax is available from +/// [`core::fmt`] and [`alloc::format!`]. +/// +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// # use kernel::device::Device; +/// +/// fn example(dev: &Device) { +/// dev_alert!(dev, "hello {}\n", "there"); +/// } +/// ``` +#[macro_export] +macro_rules! dev_alert { + ($($f:tt)*) =3D> { $crate::dev_printk!(pr_alert, $($f)*); } +} + +/// Prints a critical-level message (level 2) prefixed with device informa= tion. +/// +/// This level should be used in critical conditions. +/// +/// Equivalent to the kernel's `dev_crit` macro. +/// +/// Mimics the interface of [`std::print!`]. More information about the sy= ntax is available from +/// [`core::fmt`] and [`alloc::format!`]. +/// +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// # use kernel::device::Device; +/// +/// fn example(dev: &Device) { +/// dev_crit!(dev, "hello {}\n", "there"); +/// } +/// ``` +#[macro_export] +macro_rules! dev_crit { + ($($f:tt)*) =3D> { $crate::dev_printk!(pr_crit, $($f)*); } +} + +/// Prints an error-level message (level 3) prefixed with device informati= on. +/// +/// This level should be used in error conditions. +/// +/// Equivalent to the kernel's `dev_err` macro. +/// +/// Mimics the interface of [`std::print!`]. More information about the sy= ntax is available from +/// [`core::fmt`] and [`alloc::format!`]. +/// +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// # use kernel::device::Device; +/// +/// fn example(dev: &Device) { +/// dev_err!(dev, "hello {}\n", "there"); +/// } +/// ``` +#[macro_export] +macro_rules! dev_err { + ($($f:tt)*) =3D> { $crate::dev_printk!(pr_err, $($f)*); } +} + +/// Prints a warning-level message (level 4) prefixed with device informat= ion. +/// +/// This level should be used in warning conditions. +/// +/// Equivalent to the kernel's `dev_warn` macro. +/// +/// Mimics the interface of [`std::print!`]. More information about the sy= ntax is available from +/// [`core::fmt`] and [`alloc::format!`]. +/// +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// # use kernel::device::Device; +/// +/// fn example(dev: &Device) { +/// dev_warn!(dev, "hello {}\n", "there"); +/// } +/// ``` +#[macro_export] +macro_rules! dev_warn { + ($($f:tt)*) =3D> { $crate::dev_printk!(pr_warn, $($f)*); } +} + +/// Prints a notice-level message (level 5) prefixed with device informati= on. +/// +/// This level should be used in normal but significant conditions. +/// +/// Equivalent to the kernel's `dev_notice` macro. +/// +/// Mimics the interface of [`std::print!`]. More information about the sy= ntax is available from +/// [`core::fmt`] and [`alloc::format!`]. +/// +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// # use kernel::device::Device; +/// +/// fn example(dev: &Device) { +/// dev_notice!(dev, "hello {}\n", "there"); +/// } +/// ``` +#[macro_export] +macro_rules! dev_notice { + ($($f:tt)*) =3D> { $crate::dev_printk!(pr_notice, $($f)*); } +} + +/// Prints an info-level message (level 6) prefixed with device informatio= n. +/// +/// This level should be used for informational messages. +/// +/// Equivalent to the kernel's `dev_info` macro. +/// +/// Mimics the interface of [`std::print!`]. More information about the sy= ntax is available from +/// [`core::fmt`] and [`alloc::format!`]. +/// +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// # use kernel::device::Device; +/// +/// fn example(dev: &Device) { +/// dev_info!(dev, "hello {}\n", "there"); +/// } +/// ``` +#[macro_export] +macro_rules! dev_info { + ($($f:tt)*) =3D> { $crate::dev_printk!(pr_info, $($f)*); } +} + +/// Prints a debug-level message (level 7) prefixed with device informatio= n. +/// +/// This level should be used for debug messages. +/// +/// Equivalent to the kernel's `dev_dbg` macro, except that it doesn't sup= port dynamic debug yet. +/// +/// Mimics the interface of [`std::print!`]. More information about the sy= ntax is available from +/// [`core::fmt`] and [`alloc::format!`]. +/// +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// # use kernel::device::Device; +/// +/// fn example(dev: &Device) { +/// dev_dbg!(dev, "hello {}\n", "there"); +/// } +/// ``` +#[macro_export] +macro_rules! dev_dbg { + ($($f:tt)*) =3D> { $crate::dev_printk!(pr_dbg, $($f)*); } +} diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs index b37a0b3180fb..c5765ab863d6 100644 --- a/rust/kernel/prelude.rs +++ b/rust/kernel/prelude.rs @@ -27,6 +27,8 @@ // `super::std_vendor` is hidden, which makes the macro inline for some re= ason. #[doc(no_inline)] pub use super::dbg; +pub use super::fmt; +pub use super::{dev_alert, dev_crit, dev_dbg, dev_emerg, dev_err, dev_info= , dev_notice, dev_warn}; pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr= _notice, pr_warn}; =20 pub use super::{init, pin_init, try_init, try_pin_init}; --=20 2.45.1 From nobody Wed Feb 11 05:41:11 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (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 6877D13A418 for ; Mon, 20 May 2024 17:27:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226051; cv=none; b=kfS+En8/WnH6wj8oKGTNq19lXa8jopNSlHfHokjEdJUC41t18tAzX9AJYXbvQP2QXoFBX+JC+6phjM3mJdAeom8xpylAJr33KGKrgwsaHGzKVqiT1RutLuVcIenTKTSbruZdXInh9KaPM+f399EGZcqFrhpFcbBp1MYorMvKV04= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226051; c=relaxed/simple; bh=Pf+OOnuHz5mcZsGqaVhIEIMHurAZcjaBobj/NJHA3CI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MlfV8YKP9TT4wb7nqTJLfgrzw57LTsXxUs73Vw4k03KHqzqjyZHBWrtKEFq862/0IkcnMNRSK9p1xDAH/34tKZSuPxZEGhypruHqpk/P0ii7ZBNVvXb3pavEtqRJo/RqM5avHvY4HSYOAbLlpE8xg3vx11SsJqmGw0JnlvTwqy8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=SvzrCzGo; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="SvzrCzGo" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226048; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=1CsnJXGBZcLyYjfrlAMv2FyBTh8bc+Mt3a3EbW5Rnks=; b=SvzrCzGo9wb2DXXlFn5leFcfH0Ok8ax85GHnujv3sSaCXzxc3fWYxmyILkAFDSveCfq+iU A1TwcOIIArL8YwJdMh1bPX9pIykLt/VPoZjbgRY8Vvgjt47mvAtXLQmN050AbjHfibPAsx ZR/IFYijSgZl+lY4rhFyrusppqWZlKs= Received: from mail-lj1-f198.google.com (mail-lj1-f198.google.com [209.85.208.198]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-628-Gp4t6pQcOKab6F_be5iwVg-1; Mon, 20 May 2024 13:27:26 -0400 X-MC-Unique: Gp4t6pQcOKab6F_be5iwVg-1 Received: by mail-lj1-f198.google.com with SMTP id 38308e7fff4ca-2e3208c29abso103894141fa.2 for ; Mon, 20 May 2024 10:27:26 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226045; x=1716830845; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=1CsnJXGBZcLyYjfrlAMv2FyBTh8bc+Mt3a3EbW5Rnks=; b=TOImeyVpe0RVKgP6uHsnGm3dFCxtc0EjKq+qjHfkexAgQ40uY2ab/tzg3CMDnsT4lo poHTpx5vWzioAn1/qOS0hc5qEG3L9FGKebkvlNUUsvOmHH+2WZD2vAGw4ui8hEToEjtu rJ3vqCBpoD62G9+sTsUxmKxwR9EXIdEI/A/ZGqvZU/tSKDw8c78FiZ+kbnxBBE3PRiYR l/RVYNhRSOxDOEtCS8l79cioWBJcu45hB5qxhBWWlpSXycLK7X/BQKqC+13/oHjTHeb/ WEteUbrKjuuhgL22Bzb9Z3KcK+45NmQAxtlaDjelTd5okTxoUycVj2kvc04dXpjz8Gsy fLYg== X-Forwarded-Encrypted: i=1; AJvYcCUzQwwt4GWLl6FMn6QMhs/0g527GMYmS30/mB2jSkshruuc3ZIeX4A6l86BiW1CdER9T6+e9D9zClVSu6qxRdKGcZy+dBbzV6w/K9RI X-Gm-Message-State: AOJu0YyjYP93EL6J1PHwXhrALob0YepZ8+JAazXqdN+SSEsw/fre15Fi exixOEMHEl4X4tPGPH0Pp5bUP7AUa7BpqTdvPmIr7Ge8PZT0e++fsOMQ6P30rfuG+MuYBFcTjt8 fzHh8+QTF9UnyhC/Ta3ZnClechu74eyYMdVHrZCikKP/ujmsXEI++4Wf9IMwbCA== X-Received: by 2002:a2e:be9f:0:b0:2e3:ba0e:de12 with SMTP id 38308e7fff4ca-2e51ff5cf48mr319538881fa.22.1716226045291; Mon, 20 May 2024 10:27:25 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHbLE5+nNhphlxlGh18a3Zy/HbW451IH7B3zyMvPHBImYdCotrNMydqRvyk8NUf6/7klPUZ8Q== X-Received: by 2002:a2e:be9f:0:b0:2e3:ba0e:de12 with SMTP id 38308e7fff4ca-2e51ff5cf48mr319538611fa.22.1716226044936; Mon, 20 May 2024 10:27:24 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-42009eda143sm363816305e9.14.2024.05.20.10.27.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:24 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 08/11] rust: add devres abstraction Date: Mon, 20 May 2024 19:25:45 +0200 Message-ID: <20240520172554.182094-9-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a Rust abstraction for the kernel's devres (device resource management) implementation. The Devres type acts as a container to manage the lifetime and accessibility of device bound resources. Therefore it registers a devres callback and revokes access to the resource on invocation. Users of the Devres abstraction can simply free the corresponding resources in their Drop implementation, which is invoked when either the Devres instance goes out of scope or the devres callback leads to the resource being revoked, which implies a call to drop_in_place(). Co-developed-by: Philipp Stanner Signed-off-by: Philipp Stanner Signed-off-by: Danilo Krummrich --- rust/helpers.c | 5 ++ rust/kernel/devres.rs | 151 ++++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 3 files changed, 157 insertions(+) create mode 100644 rust/kernel/devres.rs diff --git a/rust/helpers.c b/rust/helpers.c index 1d3e800140fc..34061eca05a0 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -173,6 +173,11 @@ void rust_helper_rcu_read_unlock(void) EXPORT_SYMBOL_GPL(rust_helper_rcu_read_unlock); /* end rcu */ =20 +int rust_helper_devm_add_action(struct device *dev, void (*action)(void *)= , void *data) +{ + return devm_add_action(dev, action, data); +} + /* * `bindgen` binds 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) indi= ces. diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs new file mode 100644 index 000000000000..bf7bd304cd9b --- /dev/null +++ b/rust/kernel/devres.rs @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Devres abstraction +//! +//! [`Devres`] represents an abstraction for the kernel devres (device res= ource management) +//! implementation. + +use crate::{ + alloc::Flags, + bindings, + device::Device, + error::{Error, Result}, + prelude::*, + revocable::Revocable, + types::ARef, +}; + +use core::ffi::c_void; +use core::ops::Deref; + +#[pin_data] +struct DevresInner { + dev: ARef, + #[pin] + data: Revocable, +} + +/// This abstraction is meant to be used by subsystems to containerize [`D= evice`] bound resources to +/// manage their lifetime. +/// +/// [`Device`] bound resources should be freed when either the resource go= es out of scope or the +/// [`Device`] is unbound respectively, depending on what happens first. +/// +/// To achieve that [`Devres`] registers a devres callback on creation, wh= ich is called once the +/// [`Device`] is unbound, revoking access to the encapsulated resource (s= ee also [`Revocable`]). +/// +/// After the [`Devres`] has been unbound it is not possible to access the= encapsulated resource +/// anymore. +/// +/// [`Devres`] users should make sure to simply free the corresponding bac= king resource in `T`'s +/// [`Drop`] implementation. +/// +/// # Example +/// +/// ``` +/// use kernel::devres::Devres; +/// +/// // See also [`pci::Bar`] for a real example. +/// struct IoRemap(IoMem); +/// +/// impl IoRemap { +/// fn new(usize paddr, usize len) -> Result{ +/// // assert success +/// let addr =3D unsafe { bindings::ioremap(paddr as _); }; +/// let iomem =3D IoMem::new(addr, len)?; +/// +/// Ok(IoRemap(iomem)) +/// } +/// } +/// +/// impl Drop for IoRemap { +/// fn drop(&mut self) { +/// unsafe { bindings::iounmap(self.0.ioptr as _); }; +/// } +/// } +/// +/// impl Deref for IoRemap { +/// type Target =3D IoMem; +/// +/// fn deref(&self) -> &Self::Target { +/// &self.0 +/// } +/// } +/// +/// let devres =3D Devres::new(dev, IoRemap::new(0xBAAAAAAD, 0x4)?, GFP_KE= RNEL)?; +/// +/// let res =3D devres.try_access().ok_or(ENXIO)?; +/// res.writel(0xBAD); +/// ``` +/// +pub struct Devres { + inner: Pin>>, + callback: unsafe extern "C" fn(*mut c_void), +} + +impl DevresInner { + fn as_ptr(&self) -> *const DevresInner { + self as *const DevresInner + } + + fn as_cptr(&self) -> *mut c_void { + self.as_ptr() as *mut c_void + } +} + +unsafe extern "C" fn devres_callback(inner: *mut c_void) { + let inner =3D inner as *const DevresInner; + let inner =3D unsafe { &*inner }; + + inner.data.revoke(); +} + +impl Devres { + /// Creates a new [`Devres`] instance of the give data. + pub fn new(dev: ARef, data: T, flags: Flags) -> Result { + let callback =3D devres_callback::; + + let inner =3D Box::pin_init( + pin_init!( DevresInner { + dev: dev, + data <- Revocable::new(data), + }), + flags, + )?; + + let ret =3D unsafe { + bindings::devm_add_action(inner.dev.as_raw(), Some(callback), = inner.as_cptr()) + }; + + if ret !=3D 0 { + return Err(Error::from_errno(ret)); + } + + // We have to store the exact callback function pointer used with + // `bindings::devm_add_action` for `bindings::devm_remove_action`.= There compiler might put + // multiple definitions of `devres_callback` for the same `T` i= n both the kernel itself + // and modules. Hence, we might see different pointer values depen= ding on whether we look + // at `devres_callback`'s address from `Devres::new` or `Devres= ::drop`. + Ok(Devres { inner, callback }) + } +} + +impl Deref for Devres { + type Target =3D Revocable; + + fn deref(&self) -> &Self::Target { + &self.inner.data + } +} + +impl Drop for Devres { + fn drop(&mut self) { + unsafe { + bindings::devm_remove_action( + self.inner.dev.as_raw(), + Some(self.callback), + self.inner.as_cptr(), + ) + } + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index d7d415429517..11645060b444 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -30,6 +30,7 @@ pub mod alloc; mod build_assert; pub mod device; +pub mod devres; pub mod driver; pub mod error; pub mod init; --=20 2.45.1 From nobody Wed Feb 11 05:41:11 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (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 D5BE813A894 for ; Mon, 20 May 2024 17:27:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226054; cv=none; b=qjyhQjokMtYzI5u9gru/ONCWCIL3guRQaDWydxTb3YxycysvJaoqeq9/rHBcArQWFE9EiNWUUkm7iP4eFQfQmvLoOCZ348ogf3ckHwaiLbXYvY+rqZJsEqe3yGnbpEHA7q5XPtSb8AttzoMbD+YAqpiOUS2RetcD/ZRXhqmm3Yw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226054; c=relaxed/simple; bh=5ysJ7veRsD9TP3SKhDxVg22CoKwjiIVIqxUU9mdur14=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=gnOYe3CA1+kOLcHk97uRUmbIZ+lxUSjiL6wFQX23Yx3Qjcl8MnBqJicdQ8d2KMFzF/OGFpllCwaLG0uZsOTj44Op+ru5Hn2aHqhG+igpcs92o+UIMDM9JGEqXnhU5XnpkiNQrPJPFbvM1pSdicuI5zoBHr/NEhag8wJ9mWq1LtQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=eEexPLJW; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="eEexPLJW" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226052; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=6S1JMvs3wWwoYY5yLjwWSAqPBxm7FbzVGctvxFDmowc=; b=eEexPLJWNNzKfZkiLJ3SALMCPI9Gg4TZamzSfFBSYsuK4bP6qeGGSFS0yKy/roiEvIfX1C GwVcnMa8UDZUm6QRShpLgVTCLJaNXMmrp2Na/PpQgzaTMP4a9DpQ9ChG1oDkGfbDA7eZ4H Hmqeu8ud3P/w4fmzG7vtCKHWbzOmUCA= Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-613-MPpKqFhrMpaM3Wu3awzEhg-1; Mon, 20 May 2024 13:27:30 -0400 X-MC-Unique: MPpKqFhrMpaM3Wu3awzEhg-1 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-42015260835so39354525e9.0 for ; Mon, 20 May 2024 10:27:30 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226049; x=1716830849; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=6S1JMvs3wWwoYY5yLjwWSAqPBxm7FbzVGctvxFDmowc=; b=UFskFUHNKo6hFpNVbQdmAD78ZZmruAlzgv7w8CMk42qnFz/3BRTQX1FfxEcY+ucYwM Fp8OdpTlmx5BWjXEcftH/3y4dvxBeLqrBWXRaUb9lOG4GxveNkW+eYUuSFdqO/jiVoZ1 nAyxKGOisvI8aD8g7Q5ypN1JjOCV/yibEq3Y8sowv56anqrJWMSWhXZ32/CqpMQGkjZi j0Hj9kCjv9c7nTGAMIETl25zkPBgshkqsxM+xuF5D0QUQlDeEQvuaGqZOJWsXhVdwztj NQE+ThNpw0NEpj4ztD3r9M+SRPyFA1YzipKObKmnw2mWvIIBajG5yG0gZqsTCFQN/Dp5 e1og== X-Forwarded-Encrypted: i=1; AJvYcCXW7V2GveM0ISRLYcGjFXP/2G0S1Nr3zsSN3fNkyWA5m20EpJPGZybz4me1W3NwVnPWIHAdKBSy2ADwL4YnfFtiNMeoKqIO3Srn71ZD X-Gm-Message-State: AOJu0Yzy9JdPhTYiEc0rYZGc34SKnnqlnyVOQm2Re/q9cEEXkUuMplz9 hofBmakQU5qsLP6VN4ruKpDwS1bAp+jKjdqHQYUNAF2Z0kSvxF80osTw0KL0nEEbHDTnCK6ozry RrbtW5LyBxnaVkNcNbrNNzfZvyCD5PGEXrEZacSjdpSenuFgbzEqvEjSzFe6IpA== X-Received: by 2002:a05:600c:1547:b0:420:1078:a74c with SMTP id 5b1f17b1804b1-4201078a89cmr192092525e9.20.1716226049337; Mon, 20 May 2024 10:27:29 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEowXlkhLoQlD536HTIQse+gl+PqtvMMQTha8X+ha5edJrAVhb5zSf/srjBFRZN7CNK5aE/Hg== X-Received: by 2002:a05:600c:1547:b0:420:1078:a74c with SMTP id 5b1f17b1804b1-4201078a89cmr192092225e9.20.1716226048892; Mon, 20 May 2024 10:27:28 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-41fccce24c0sm430961865e9.17.2024.05.20.10.27.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:28 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 09/11] rust: add basic PCI driver abstractions Date: Mon, 20 May 2024 19:25:46 +0200 Message-ID: <20240520172554.182094-10-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: FUJITA Tomonori This commit implements the abstractions necessary to bind a most basic PCI driver to a PCI device. It also serves as a ground layer for further PCI functionality. Specifically, a basic PCI driver has to provide register() and unregister() methods, a PCI device structure for Rust, and probe() and remove() callbacks for the C side. A PCI driver shall be able to register itself for the desired devices, recognized by their device ID. Another basic necessity is the ability to store driver data, i.e., through pci_set_drvdata(). In congruency with the C implementation of pci_dev, a Rust PCI device holds a basic device (device::Device) which is always reference counted to ensure it cannot disappear as long as there are still users. Holding a basic device allows for both using interfaces that require a device, as well as such that demand a pci_dev, which can be obtained through as_raw(), using the established container_of() macro. Implement a basic driver model with probe() and remove() callbacks, implementing the corresponding traits from the 'driver' crate. Implement PCI device IDs. Implement pci::Device with basic methods, holding an always reference counted device::Device. Signed-off-by: FUJITA Tomonori Co-developed-by: Philipp Stanner Signed-off-by: Philipp Stanner Co-developed-by: Danilo Krummrich Signed-off-by: Danilo Krummrich --- rust/bindings/bindings_helper.h | 1 + rust/helpers.c | 18 ++ rust/kernel/lib.rs | 2 + rust/kernel/pci.rs | 328 ++++++++++++++++++++++++++++++++ 4 files changed, 349 insertions(+) create mode 100644 rust/kernel/pci.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index ddb5644d4fd9..32221de16e57 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers.c b/rust/helpers.c index 34061eca05a0..c3d80301185c 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -33,6 +33,7 @@ #include #include #include +#include =20 __noreturn void rust_helper_BUG(void) { @@ -178,6 +179,23 @@ int rust_helper_devm_add_action(struct device *dev, vo= id (*action)(void *), void return devm_add_action(dev, action, data); } =20 +void rust_helper_pci_set_drvdata(struct pci_dev *pdev, void *data) +{ + pci_set_drvdata(pdev, data); +} +EXPORT_SYMBOL_GPL(rust_helper_pci_set_drvdata); + +void *rust_helper_pci_get_drvdata(struct pci_dev *pdev) +{ + return pci_get_drvdata(pdev); +} +EXPORT_SYMBOL_GPL(rust_helper_pci_get_drvdata); + +u64 rust_helper_pci_resource_len(struct pci_dev *pdev, int barnr) +{ + return pci_resource_len(pdev, barnr); +} + /* * `bindgen` binds 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) indi= ces. diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 11645060b444..606391cbff83 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -55,6 +55,8 @@ #[doc(hidden)] pub use bindings; pub use macros; +#[cfg(all(CONFIG_PCI, CONFIG_PCI_MSI))] +pub mod pci; pub use uapi; =20 #[doc(hidden)] diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs new file mode 100644 index 000000000000..323aea565d84 --- /dev/null +++ b/rust/kernel/pci.rs @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Wrappers for the PCI subsystem +//! +//! C header: [`include/linux/pci.h`](../../../../include/linux/pci.h) + +use crate::{ + bindings, container_of, device, driver, + error::{to_result, Result}, + str::CStr, + types::{ARef, ForeignOwnable}, + ThisModule, +}; +use kernel::prelude::*; // for pinned_drop + +/// An adapter for the registration of PCI drivers. +/// +/// # Example +/// +///``` +/// use kernel::pci; +/// +/// impl pci::Driver for MyDriver { +/// type Data =3D Arc; +/// +/// define_pci_id_table! { +/// (), +/// [ (pci::DeviceId::new(bindings::PCI_VENDOR_ID_MY_VENDOR, +/// bindings::PCI_ANY_ID as u32), +/// None) +/// ] +/// } +/// +/// fn probe( +/// pdev: &mut pci::Device, +/// id_info: Option<&Self::IdInfo> +/// ) -> Result> { +/// ... +/// } +/// +/// fn remove(data: &Self::Data) { +/// ... +/// } +/// } +/// +/// struct MyModule { +/// _registration: Pin= >>>, +/// } +/// +/// impl kernel::Module for MyModule { +/// fn init(_name: &'static CStr, module: &'static ThisModule) -> Resu= lt { +/// let registration =3D driver::Registration::new_pinned(c_str!("= MyDriver"), module)?; +/// +/// Ok(Self { +/// _registration: registration, +/// }) +/// } +/// } +///``` +pub struct Adapter(T); + +impl driver::DriverOps for Adapter { + type RegType =3D bindings::pci_driver; + + // SAFETY: The caller must ensure that `reg` is valid and unequal NULL. + unsafe fn register( + reg: *mut bindings::pci_driver, + name: &'static CStr, + module: &'static ThisModule, + ) -> Result { + // SAFETY: Guaranteed by the safety requirements of this function. + let pdrv: &mut bindings::pci_driver =3D unsafe { &mut *reg }; + + pdrv.name =3D name.as_char_ptr(); + pdrv.probe =3D Some(Self::probe_callback); + pdrv.remove =3D Some(Self::remove_callback); + pdrv.id_table =3D T::ID_TABLE.as_ref(); + // SAFETY: Guaranteed by the safety requirements of this function. + to_result(unsafe { bindings::__pci_register_driver(reg, module.0, = name.as_char_ptr()) }) + } + + // SAFETY: The caller must ensure that `reg` is valid and unequal NULL. + unsafe fn unregister(reg: *mut bindings::pci_driver) { + // SAFETY: Guaranteed by the safety requirements of this function. + unsafe { bindings::pci_unregister_driver(reg) } + } +} + +impl Adapter { + extern "C" fn probe_callback( + pdev: *mut bindings::pci_dev, + id: *const bindings::pci_device_id, + ) -> core::ffi::c_int { + // SAFETY: Safe because the core kernel only ever calls the probe = callback with a valid + // `pdev`. + let dev =3D unsafe { device::Device::from_raw(&mut (*pdev).dev) }; + // SAFETY: Guaranteed by the rules described above. + let mut pdev =3D unsafe { Device::from_dev(dev) }; + + // SAFETY: `id` is a pointer within the static table, so it's alwa= ys valid. + let offset =3D unsafe { (*id).driver_data }; + let info =3D { + // SAFETY: The offset comes from a previous call to `offset_fr= om` in `IdArray::new`, + // which guarantees that the resulting pointer is within the t= able. + let ptr =3D unsafe { + id.cast::() + .offset(offset as _) + .cast::>() + }; + // SAFETY: Guaranteed by the preceding safety requirement. + unsafe { (*ptr).as_ref() } + }; + match T::probe(&mut pdev, info) { + Ok(data) =3D> { + // SAFETY: + // A valid `pdev` is always passed to this function. `data= ` is always valid since + // it's created in Rust. + unsafe { bindings::pci_set_drvdata(pdev.as_raw(), data.int= o_foreign() as _) }; + } + Err(err) =3D> return Error::to_errno(err), + } + + 0 + } + + extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) { + // SAFETY: This function is called by the C side and always with a= valid `pdev`. + let ptr =3D unsafe { bindings::pci_get_drvdata(pdev) }; + // SAFETY: Guaranteed by the preceding safety requirement. + let data =3D unsafe { T::Data::from_foreign(ptr) }; + T::remove(&data); + ::device_remove(&data); + } +} + +/// Abstraction for bindings::pci_device_id. +#[derive(Clone, Copy)] +pub struct DeviceId { + /// Vendor ID + pub vendor: u32, + /// Device ID + pub device: u32, + /// Subsystem vendor ID + pub subvendor: u32, + /// Subsystem device ID + pub subdevice: u32, + /// Device class and subclass + pub class: u32, + /// Limit which sub-fields of the class + pub class_mask: u32, +} + +impl DeviceId { + const PCI_ANY_ID: u32 =3D !0; + + /// PCI_DEVICE macro. + pub const fn new(vendor: u32, device: u32) -> Self { + Self { + vendor, + device, + subvendor: DeviceId::PCI_ANY_ID, + subdevice: DeviceId::PCI_ANY_ID, + class: 0, + class_mask: 0, + } + } + + /// PCI_DEVICE_CLASS macro. + pub const fn with_class(class: u32, class_mask: u32) -> Self { + Self { + vendor: DeviceId::PCI_ANY_ID, + device: DeviceId::PCI_ANY_ID, + subvendor: DeviceId::PCI_ANY_ID, + subdevice: DeviceId::PCI_ANY_ID, + class, + class_mask, + } + } + + /// PCI_DEVICE_ID macro. + pub const fn to_rawid(&self, offset: isize) -> bindings::pci_device_id= { + bindings::pci_device_id { + vendor: self.vendor, + device: self.device, + subvendor: self.subvendor, + subdevice: self.subdevice, + class: self.class, + class_mask: self.class_mask, + driver_data: offset as _, + override_only: 0, + } + } +} + +// SAFETY: `ZERO` is all zeroed-out and `to_rawid` stores `offset` in `pci= _device_id::driver_data`. +unsafe impl driver::RawDeviceId for DeviceId { + type RawType =3D bindings::pci_device_id; + + const ZERO: Self::RawType =3D bindings::pci_device_id { + vendor: 0, + device: 0, + subvendor: 0, + subdevice: 0, + class: 0, + class_mask: 0, + driver_data: 0, + override_only: 0, + }; +} + +/// Define a const pci device id table +/// +/// # Examples +/// +/// ```ignore +/// # use kernel::{pci, define_pci_id_table}; +/// # +/// struct MyDriver; +/// impl pci::Driver for MyDriver { +/// // [...] +/// # fn probe(_dev: &mut pci::Device, _id_info: Option<&Self::IdInfo>) = -> Result { +/// # Ok(()) +/// # } +/// # define_pci_id_table! {u32, [ +/// # (pci::DeviceId::new(0x010800, 0xffffff), None), +/// # (pci::DeviceId::with_class(0x010802, 0xfffff), Some(0x10)), +/// # ]} +/// } +/// ``` +#[macro_export] +macro_rules! define_pci_id_table { + ($data_type:ty, $($t:tt)*) =3D> { + type IdInfo =3D $data_type; + const ID_TABLE: $crate::driver::IdTable<'static, $crate::pci::Devi= ceId, $data_type> =3D { + $crate::define_id_array!(ARRAY, $crate::pci::DeviceId, $data_t= ype, $($t)* ); + ARRAY.as_table() + }; + }; +} +pub use define_pci_id_table; + +/// The PCI driver trait. +/// +/// Drivers must implement this trait in order to get a PCI driver registe= red. Please refer to the +/// `Adapter` documentation for an example. +pub trait Driver { + /// Data stored on device by driver. + /// + /// Corresponds to the data set or retrieved via the kernel's + /// `pci_{set,get}_drvdata()` functions. + /// + /// Require that `Data` implements `ForeignOwnable`. We guarantee to + /// never move the underlying wrapped data structure. + /// + /// TODO: Use associated_type_defaults once stabilized: + /// + /// `type Data: ForeignOwnable + driver::DeviceRemoval =3D ();` + type Data: ForeignOwnable + driver::DeviceRemoval; + + /// The type holding information about each device id supported by the= driver. + /// + /// TODO: Use associated_type_defaults once stabilized: + /// + /// type IdInfo: 'static =3D (); + type IdInfo: 'static; + + /// The table of device ids supported by the driver. + const ID_TABLE: driver::IdTable<'static, DeviceId, Self::IdInfo>; + + /// PCI driver probe. + /// + /// Called when a new platform device is added or discovered. + /// Implementers should attempt to initialize the device here. + fn probe(dev: &mut Device, id: Option<&Self::IdInfo>) -> Result; + + /// PCI driver remove. + /// + /// Called when a platform device is removed. + /// Implementers should prepare the device for complete removal here. + fn remove(_data: &Self::Data); +} + +/// The PCI device representation. +/// +/// A PCI device is based on an always reference counted `device:Device` i= nstance. Cloning a PCI +/// device, hence, also increments the base device' reference count. +#[derive(Clone)] +pub struct Device(ARef); + +impl Device { + /// Create a PCI Device instance from an existing `device::Device`. + /// + /// # Safety + /// + /// `dev` must be an `ARef` whose underlying `bindings= ::device` is a member of + /// a `bindings::pci_dev`. + pub unsafe fn from_dev(dev: ARef) -> Self { + Self(dev) + } + + fn as_raw(&self) -> *mut bindings::pci_dev { + // SAFETY: Guaranteed by the requirements described in pci::Device= ::new(). + unsafe { container_of!(self.0.as_raw(), bindings::pci_dev, dev) as= _ } + } + + /// Enable the Device's memory. + pub fn enable_device_mem(&self) -> Result { + // SAFETY: By the type invariants, we know that `self.ptr` is non-= null and valid. + let ret =3D unsafe { bindings::pci_enable_device_mem(self.as_raw()= ) }; + if ret !=3D 0 { + Err(Error::from_errno(ret)) + } else { + Ok(()) + } + } + + /// Set the Device's master. + pub fn set_master(&self) { + // SAFETY: By the type invariants, we know that `self.ptr` is non-= null and valid. + unsafe { bindings::pci_set_master(self.as_raw()) }; + } +} + +impl AsRef for Device { + fn as_ref(&self) -> &device::Device { + &self.0 + } +} --=20 2.45.1 From nobody Wed Feb 11 05:41:11 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (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 E52181386AA for ; Mon, 20 May 2024 17:27:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226058; cv=none; b=YPt9sVW0K3lUZEr4T8BCqmZ8os6Oqfc9gCm27qpIvESA7a53AJM98i0OuYKXIvpAOeoCr7Bka2SzvsWps9jO9/ANsIp4V0uW3coZ6XzhSQq8NhjH57ExrI3c1J8Dbybtnt672wO4m95EPZqh0b3JtWT/gSkl1tbMCcAM2clmIaA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226058; c=relaxed/simple; bh=ByeqkzM2jzSrDGnfHWAPGFr2QDs6pyQQOFnYXBVMUb0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=az9R83WeSmYJg5whqJKbI5jBUmel6lBbDeFpaBo23xiaTIr4d4KUsE8s/uMzHMMwsLfTgmG0+F9tJ11TinYHosJYYflFjk33mPxL6+jTpX3fs4VzqrtK0CyDXp5A5UHl+neHTZpMFypKodP9FdNJHmG9NNF7qIHQKfu7Xlbymso= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=TEY0UYzJ; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="TEY0UYzJ" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226056; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=q+KTbXYumAmw16h3rTz/Xv/htYjuUG13nuBarwTBt2A=; b=TEY0UYzJ6TIs6wVxhJ+xBpd92VnDrqcllEDmysz3qSLu0ySSC3+s5//HOBPj6q7ORveuqu t+R7ws+0wXs59UuMAj/j9sExtqTiBa+0wpVe8PzAJJDBOJJOZ8UTQESlLESMYo37kNedh2 H+d4RTfjz3ZrVmM6aInTck+7V0cJ/JU= Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-302-yQS4QFV5MRSfqhYFbZFpCQ-1; Mon, 20 May 2024 13:27:34 -0400 X-MC-Unique: yQS4QFV5MRSfqhYFbZFpCQ-1 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-34ffd710a31so7610336f8f.0 for ; Mon, 20 May 2024 10:27:34 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226053; x=1716830853; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=q+KTbXYumAmw16h3rTz/Xv/htYjuUG13nuBarwTBt2A=; b=WclUhWVehN8eYzokePAvIrviGyTynIc1aQkVok/utYf2lptl4PX7OsuJ89b4qD+2Mb oOY+m9jQGcJ7LJxIF2eMTpD0A2NNeVCCA3Y43nijIbxFKK+YcpKqec0B+2pCSj/eeT3q STfNVbGI9OLgrzHZin6MvI6JBV5z52QqgznFfoQxQkIkpJOrgfDi8w03dVGhDRxVnmEd cNeX8JL45mPeuApgXlpJSaR1NFL1Me/TGNt8VC2jAQX4y3Um2DSV5Gdj3P1gmZia4Rfx 5XpDm4kIcJjQsOGtWK5kuAXSOWzrzdxuBMLS5ygS+d40RVLLtPgfN+fI8JZ9s8uPzrYs fCZQ== X-Forwarded-Encrypted: i=1; AJvYcCXZOWLY64BOkuTPrdzfdpSCdUNNeZ6tZNwTh32zPFYKzzeK+FmG1Z4bKoSz1in8jRNSjePZXMOiYn4hnSZ4XO9zpuuo8195zh9aIZiM X-Gm-Message-State: AOJu0Yxac/ER9E0Mn2Y7yjrsrt+yY7f9AoJt7WUW+ku11qdnDOQ9B+AT g+dI02J5vKHYWr016CZEqhhJZdn1bLCiyz6ejfZahm1et8nhNfjoNcb9qKwDX4BNMfdS2gzKZ9L es0FPRwoQ2fGucVDuOVCd2iuIwJVf57DHYK9OppIUusK2yp0RX2ofF4SflzbxEg== X-Received: by 2002:a5d:4801:0:b0:34d:a5fd:977b with SMTP id ffacd0b85a97d-3504a969ffcmr20289225f8f.60.1716226053173; Mon, 20 May 2024 10:27:33 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFa/Dnd6VAJ4Gwv9ggHRXIUtmNL29Xn1V3x1XOit8dv4TaIg0ybWQ9BXCbBKMeVj5eQB/k2Pw== X-Received: by 2002:a5d:4801:0:b0:34d:a5fd:977b with SMTP id ffacd0b85a97d-3504a969ffcmr20289201f8f.60.1716226052819; Mon, 20 May 2024 10:27:32 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-351d7d0f3absm12506111f8f.73.2024.05.20.10.27.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:32 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 10/11] rust: add basic abstractions for iomem operations Date: Mon, 20 May 2024 19:25:47 +0200 Message-ID: <20240520172554.182094-11-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Philipp Stanner Access to the kernel's IO-functions (e.g., readb()) is needed by almost all drivers. Currently, there are no abstractions for those functions. Since iomem is so widely used, it's necessary to provide a generic interface that all subsystems providing IO memory can use. The existing C implementations of such subsystems typically provide their own wrappers around functions like ioremap() which take care of respecting resource boundaries etc. It is, therefore, desirable to use these wrappers because using ioremap() and iounmap() directly would effectively result in parts of those subsystems being reimplemented in Rust. As most if not all device drivers should be fully satisfied regarding their iomem demands by the existing subsystem interfaces, Rust abstractions as congruent as possible with the existing infrastructure shall use the existing subsystem (e.g., PCI) interfaces for creating IO mappings, while simultaneously wrapping those mappings in Rust containers whose Drop() traits ensure that the resources are released again. The process for mapping iomem would consequently look as follows: 1. The subsystem abstraction (e.g., PCI) requests and ioremaps the memory through the corresponding C functions. 2. The subsystem uses resources obtained in step #1 to create a Rust IoMem data structure that implements the IO functionality such as readb() for the iomem. 3. The subsystem code wrapps IoMem into additional containers that ensure, e.g., thread safety, prevent UAF etc. Additionally, the subsystem ensures that access to IoMem is revoked latest when the driver's remove() callback is invoked. Hereby, the subsystem data structure obtains ownership over the iomem. Release of the iomem and, possibly, other subsystem associated data is then handled through the Drop() trait of the subsystem data structure. IO memory can become invalid during runtime (for example because the driver's remove() callback was invoked, revoking access to the driver's resources). However, in parallel executing routines might still be active. Consequently, the subsytem should also guard the iomem in some way. One way to do this is the Devres implementation, which provides a container that is capable of revoking access to its payload when the driver's remove() callback is invoked. The figure illustrates what usage of IoMem through subsystems might look like: Devres *------------------------------* | | | subsystem data structure | | *----------------------* | | | IoMem | | | | *------------------* | | | | | io_addr =3D 0x42, | | | | | | io_len =3D 9001, | | | | | | | | | | | | readb(), | | | | | | writeb(), | | | | | | ... | | | | | *------------------* | | | | deref(), | | | | drop(), | | | | ... | | | *----------------------* | | deref(), | | drop(), | *------------------------------* For additional convenience, subsystem abstractions can implement the Deref() trait for their data structures so that access he iomem can be fully transparent. In summary, IoMem would just serve as a container providing the IO functions, and the subsystem, which knows about the memory layout, request mechanisms etc, shall create and guard IoMem and ensure that its resources are released latest at remove() (through Devres) or earlier through its Drop() implementation. Add 'IoMem', a struct holding an IO-Pointer and a length parameter, which both shall be initialized validly by the subsystem. Add Rust abstractions for basic IO memory operations on IoMem. Signed-off-by: Philipp Stanner Signed-off-by: Danilo Krummrich --- rust/helpers.c | 106 +++++++++++++++++++++++++++++++++ rust/kernel/iomem.rs | 135 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 241 insertions(+) create mode 100644 rust/kernel/iomem.rs diff --git a/rust/helpers.c b/rust/helpers.c index c3d80301185c..dc2405772b1a 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -34,6 +34,7 @@ #include #include #include +#include =20 __noreturn void rust_helper_BUG(void) { @@ -179,6 +180,111 @@ int rust_helper_devm_add_action(struct device *dev, v= oid (*action)(void *), void return devm_add_action(dev, action, data); } =20 +/* io.h */ +u8 rust_helper_readb(const volatile void __iomem *addr) +{ + return readb(addr); +} +EXPORT_SYMBOL_GPL(rust_helper_readb); + +u16 rust_helper_readw(const volatile void __iomem *addr) +{ + return readw(addr); +} +EXPORT_SYMBOL_GPL(rust_helper_readw); + +u32 rust_helper_readl(const volatile void __iomem *addr) +{ + return readl(addr); +} +EXPORT_SYMBOL_GPL(rust_helper_readl); + +#ifdef CONFIG_64BIT +u64 rust_helper_readq(const volatile void __iomem *addr) +{ + return readq(addr); +} +EXPORT_SYMBOL_GPL(rust_helper_readq); +#endif + +void rust_helper_writeb(u8 value, volatile void __iomem *addr) +{ + writeb(value, addr); +} +EXPORT_SYMBOL_GPL(rust_helper_writeb); + +void rust_helper_writew(u16 value, volatile void __iomem *addr) +{ + writew(value, addr); +} +EXPORT_SYMBOL_GPL(rust_helper_writew); + +void rust_helper_writel(u32 value, volatile void __iomem *addr) +{ + writel(value, addr); +} +EXPORT_SYMBOL_GPL(rust_helper_writel); + +#ifdef CONFIG_64BIT +void rust_helper_writeq(u64 value, volatile void __iomem *addr) +{ + writeq(value, addr); +} +EXPORT_SYMBOL_GPL(rust_helper_writeq); +#endif + +u8 rust_helper_readb_relaxed(const volatile void __iomem *addr) +{ + return readb_relaxed(addr); +} +EXPORT_SYMBOL_GPL(rust_helper_readb_relaxed); + +u16 rust_helper_readw_relaxed(const volatile void __iomem *addr) +{ + return readw_relaxed(addr); +} +EXPORT_SYMBOL_GPL(rust_helper_readw_relaxed); + +u32 rust_helper_readl_relaxed(const volatile void __iomem *addr) +{ + return readl_relaxed(addr); +} +EXPORT_SYMBOL_GPL(rust_helper_readl_relaxed); + +#ifdef CONFIG_64BIT +u64 rust_helper_readq_relaxed(const volatile void __iomem *addr) +{ + return readq_relaxed(addr); +} +EXPORT_SYMBOL_GPL(rust_helper_readq_relaxed); +#endif + +void rust_helper_writeb_relaxed(u8 value, volatile void __iomem *addr) +{ + writeb_relaxed(value, addr); +} +EXPORT_SYMBOL_GPL(rust_helper_writeb_relaxed); + +void rust_helper_writew_relaxed(u16 value, volatile void __iomem *addr) +{ + writew_relaxed(value, addr); +} +EXPORT_SYMBOL_GPL(rust_helper_writew_relaxed); + +void rust_helper_writel_relaxed(u32 value, volatile void __iomem *addr) +{ + writel_relaxed(value, addr); +} +EXPORT_SYMBOL_GPL(rust_helper_writel_relaxed); + +#ifdef CONFIG_64BIT +void rust_helper_writeq_relaxed(u64 value, volatile void __iomem *addr) +{ + writeq_relaxed(value, addr); +} +EXPORT_SYMBOL_GPL(rust_helper_writeq_relaxed); +#endif + void rust_helper_pci_set_drvdata(struct pci_dev *pdev, void *data) { pci_set_drvdata(pdev, data); diff --git a/rust/kernel/iomem.rs b/rust/kernel/iomem.rs new file mode 100644 index 000000000000..efb6cd0829b4 --- /dev/null +++ b/rust/kernel/iomem.rs @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0 + +use crate::bindings; +use crate::error::{code::EINVAL, Result}; + +/// IO-mapped memory, starting at the base pointer @ioptr and spanning @ma= lxen bytes. +/// +/// The creator (usually a subsystem such as PCI) is responsible for creat= ing the +/// mapping, performing an additional region request etc. +pub struct IoMem { + pub ioptr: usize, + maxlen: usize, +} + +impl IoMem { + pub(crate) fn new(ioptr: usize, maxlen: usize) -> Result { + if ioptr =3D=3D 0 || maxlen =3D=3D 0 { + return Err(EINVAL); + } + + Ok(Self { ioptr, maxlen }) + } + + fn get_io_addr(&self, offset: usize, len: usize) -> Result { + if offset + len > self.maxlen { + return Err(EINVAL); + } + + Ok(self.ioptr + offset) + } + + pub fn readb(&self, offset: usize) -> Result { + let ioptr: usize =3D self.get_io_addr(offset, 1)?; + + Ok(unsafe { bindings::readb(ioptr as _) }) + } + + pub fn readw(&self, offset: usize) -> Result { + let ioptr: usize =3D self.get_io_addr(offset, 2)?; + + Ok(unsafe { bindings::readw(ioptr as _) }) + } + + pub fn readl(&self, offset: usize) -> Result { + let ioptr: usize =3D self.get_io_addr(offset, 4)?; + + Ok(unsafe { bindings::readl(ioptr as _) }) + } + + pub fn readq(&self, offset: usize) -> Result { + let ioptr: usize =3D self.get_io_addr(offset, 8)?; + + Ok(unsafe { bindings::readq(ioptr as _) }) + } + + pub fn readb_relaxed(&self, offset: usize) -> Result { + let ioptr: usize =3D self.get_io_addr(offset, 1)?; + + Ok(unsafe { bindings::readb_relaxed(ioptr as _) }) + } + + pub fn readw_relaxed(&self, offset: usize) -> Result { + let ioptr: usize =3D self.get_io_addr(offset, 2)?; + + Ok(unsafe { bindings::readw_relaxed(ioptr as _) }) + } + + pub fn readl_relaxed(&self, offset: usize) -> Result { + let ioptr: usize =3D self.get_io_addr(offset, 4)?; + + Ok(unsafe { bindings::readl_relaxed(ioptr as _) }) + } + + pub fn readq_relaxed(&self, offset: usize) -> Result { + let ioptr: usize =3D self.get_io_addr(offset, 8)?; + + Ok(unsafe { bindings::readq_relaxed(ioptr as _) }) + } + + pub fn writeb(&self, byte: u8, offset: usize) -> Result { + let ioptr: usize =3D self.get_io_addr(offset, 1)?; + + unsafe { bindings::writeb(byte, ioptr as _) } + Ok(()) + } + + pub fn writew(&self, word: u16, offset: usize) -> Result { + let ioptr: usize =3D self.get_io_addr(offset, 2)?; + + unsafe { bindings::writew(word, ioptr as _) } + Ok(()) + } + + pub fn writel(&self, lword: u32, offset: usize) -> Result { + let ioptr: usize =3D self.get_io_addr(offset, 4)?; + + unsafe { bindings::writel(lword, ioptr as _) } + Ok(()) + } + + pub fn writeq(&self, qword: u64, offset: usize) -> Result { + let ioptr: usize =3D self.get_io_addr(offset, 8)?; + + unsafe { bindings::writeq(qword, ioptr as _) } + Ok(()) + } + + pub fn writeb_relaxed(&self, byte: u8, offset: usize) -> Result { + let ioptr: usize =3D self.get_io_addr(offset, 1)?; + + unsafe { bindings::writeb_relaxed(byte, ioptr as _) } + Ok(()) + } + + pub fn writew_relaxed(&self, word: u16, offset: usize) -> Result { + let ioptr: usize =3D self.get_io_addr(offset, 2)?; + + unsafe { bindings::writew_relaxed(word, ioptr as _) } + Ok(()) + } + + pub fn writel_relaxed(&self, lword: u32, offset: usize) -> Result { + let ioptr: usize =3D self.get_io_addr(offset, 4)?; + + unsafe { bindings::writel_relaxed(lword, ioptr as _) } + Ok(()) + } + + pub fn writeq_relaxed(&self, qword: u64, offset: usize) -> Result { + let ioptr: usize =3D self.get_io_addr(offset, 8)?; + + unsafe { bindings::writeq_relaxed(qword, ioptr as _) } + Ok(()) + } +} --=20 2.45.1 From nobody Wed Feb 11 05:41:11 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (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 C850713A894 for ; Mon, 20 May 2024 17:27:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226063; cv=none; b=ZxKMUzp8HQtp8gILpiwiiB9pOeErXEsOoZqOIP0t/xGQ01kE9Eg7WFF/8SPU5/gl0khZY05EmXg2TCZqoiS4IY1on4vqguVd1/0lJjfnrHiNHdcwOrpLBeMNObcpKmYRrv8zcLnoO+tt7xbi2n8k35oZu7RTl1wBFrf5JsKregw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226063; c=relaxed/simple; bh=YZfg+yFUJvGKf66LKiZJyB7z2AoSnnfdsff2xWWMZwY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=FJZzZ1dqMYG5f7FC9PgmxiWq3iD4Ml6BNT0RlVBeUAoCWWqDnpnbNP27FonV+KMQrZiUnOsYDn6QRQLPwkkKzSrgcol+NMRA7c5LbSPNSjkv9Ob00WUkcqovFVswgdL/Z3MvMUyi3Y+AEJt1uvqaA1dFcjxeUsS86v3YM1iLgW0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=KLzz7ZDg; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="KLzz7ZDg" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226061; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=V3g+oeQpArsYWOlDSausMXlW95ZfMKvIKOo6W5/n3oc=; b=KLzz7ZDgqn0J7XZX960fRcn+mXV9zLG4zdJGhf5MEn+4LEKYRSu0FQD9RZrFxcEpvkzflJ 46Dk/YIw753rJgefscAOqBR5NIZ1FBtzSbQ5OdojiYinFrMmR5dxPugs4ZWnl9W4wMyFWj EXsaCddAwh0qSdj49RJJxDH+/BqDfTE= Received: from mail-wr1-f71.google.com (mail-wr1-f71.google.com [209.85.221.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-398-RV6_kUaoMv26Y79GDCCgDg-1; Mon, 20 May 2024 13:27:38 -0400 X-MC-Unique: RV6_kUaoMv26Y79GDCCgDg-1 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-34da03e19beso7946800f8f.1 for ; Mon, 20 May 2024 10:27:38 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226057; x=1716830857; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=V3g+oeQpArsYWOlDSausMXlW95ZfMKvIKOo6W5/n3oc=; b=fTvSXWyzgSyhnXEHm7Gy4nkwkoJI6hlX258J6FqT+ZkgsSOHCind52nUcmczi6ECmQ 8rinD9iF3IkEZQ+YYt8HejJZ4oONHFgaHmiNtOPoDQdtN1aBSUpYAWCSQpsx21/kAZEk zypsnsSWAcc8ZiLSoTt6iH2zV8vSoiT20x+rPWpGYvl9TyD8c2gL5oRLjB404dBA/WM3 2wL4WNkHPQJyHHKVWR54/tVZX3Ev0Q0cIMS53YVLcbFXaeDvmie7+yB4S67enWNp3TZZ DH9yFNwYRVC4tx0ljpnMsRhTfy69sYLrl7AMB4yMo1x8feGaHG348rXi2XQku7GMQPCL 2RwA== X-Forwarded-Encrypted: i=1; AJvYcCVextjsu1Wfgq8T+0Y5jDELlMjOOjHWpgyDFFrVoeuveCDPzjHog/SqFVn1k8WeLccuiqu+tp5B1t2AAMfFTkSVYHWkal+I/nhEew+m X-Gm-Message-State: AOJu0Yy9brLcf6JPJG0k4pVseauXrR+anGwGhC2myKRxNkhbpCDCn8n6 tYali46fZB6tFD6/qn3aE7YcKxX8yHP9PkzYM4Cm7LWdUIJwGkARBTsBA3e3MvH9sJVmW+HNRwf ZF2QrQa+cuLTcqbHMWSfs8UBSS6leyMMcqqF2nZeDT4kiYdabDvwFeufxnvmn8A== X-Received: by 2002:adf:ab1a:0:b0:351:4e42:c5f0 with SMTP id ffacd0b85a97d-3514e42c73emr20272321f8f.52.1716226057072; Mon, 20 May 2024 10:27:37 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGu7wi4ZLZ2yvRdkdYrPpzRkpeebcFzF47KqCnjyY1JeHk/jzOQJr9+KzuhFk2uiAX8OmXa2g== X-Received: by 2002:adf:ab1a:0:b0:351:4e42:c5f0 with SMTP id ffacd0b85a97d-3514e42c73emr20272302f8f.52.1716226056700; Mon, 20 May 2024 10:27:36 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-352f264c7f1sm9827066f8f.42.2024.05.20.10.27.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:36 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 11/11] rust: PCI: add BAR request and ioremap Date: Mon, 20 May 2024 19:25:48 +0200 Message-ID: <20240520172554.182094-12-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Philipp Stanner This commit implements a basic mechanism for requesting and IO-mapping PCI BARs. To perform IO on PCI devices it is necessary to have memory mapped PCI BARs. Before mapping those, a region request should be performed so that collisions with other drivers can be avoided. Since all the logic necessary to obtain the aforementioned resources are already implemented in C, Rust abstractions should use these interfaces. Hereby, the Rust implementation has to ensure that all resources are released again latest when the driver's remove() callback gets invoked, or earlier if the driver drop()s the PCI resource. This can be achieved through the Devres container, which uses devres callbacks combined with Revocable to block access to the resource - in this case, the PCI BAR and its IoMem. A pci::Bar's Drop() trait shall deregister the memory region request and iounmap() the mapping. In case remove() is invoked before such a Bar is drop()ed, the Devres container shall ensure that access to the Bar is revoke()d (through Revocable) so that no UAFs can occur. Implement 'Bar', a container for requested and ioremapped PCI BARs. Implement the Drop() trait such that the memory request and IO-mapping get freed if Bar goes out of scope. Implement Deref() so that the container is transparent. Implement iomap_region() to create a Bar and have the result returned through a Devres container, ensuring that the resources are latest freed in the driver's remove() callback and access to the Bar is revoke()d for outstanding users. Signed-off-by: Philipp Stanner Co-developed-by: Danilo Krummrich Signed-off-by: Danilo Krummrich --- rust/kernel/lib.rs | 1 + rust/kernel/pci.rs | 123 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 606391cbff83..15730deca822 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -54,6 +54,7 @@ =20 #[doc(hidden)] pub use bindings; +mod iomem; pub use macros; #[cfg(all(CONFIG_PCI, CONFIG_PCI_MSI))] pub mod pci; diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 323aea565d84..403a1f53eb25 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -5,12 +5,17 @@ //! C header: [`include/linux/pci.h`](../../../../include/linux/pci.h) =20 use crate::{ - bindings, container_of, device, driver, + alloc::flags::*, + bindings, container_of, device, + devres::Devres, + driver, error::{to_result, Result}, + iomem::IoMem, str::CStr, types::{ARef, ForeignOwnable}, ThisModule, }; +use core::ops::Deref; use kernel::prelude::*; // for pinned_drop =20 /// An adapter for the registration of PCI drivers. @@ -287,6 +292,104 @@ pub trait Driver { #[derive(Clone)] pub struct Device(ARef); =20 +/// A PCI BAR to perform IO-Operations on. +pub struct Bar { + pdev: Device, + iomem: IoMem, + num: u8, +} + +impl Bar { + fn new(pdev: Device, num: u8, name: &CStr) -> Result { + let barnr =3D num as i32; + + let barlen =3D pdev.resource_len(num)?; + if barlen =3D=3D 0 { + return Err(ENOMEM); + } + + // SAFETY: + // `pdev` is always valid. + // `barnr` is checked for validity at the top of the function. + // `name` is always valid. + let ret =3D unsafe { bindings::pci_request_region(pdev.as_raw(), b= arnr, name.as_char_ptr()) }; + if ret !=3D 0 { + return Err(EBUSY); + } + + // SAFETY: + // `pdev` is always valid. + // `barnr` is checked for validity at the top of the function. + // `name` is always valid. + let ioptr: usize =3D unsafe { bindings::pci_iomap(pdev.as_raw(), b= arnr, 0) } as usize; + if ioptr =3D=3D 0 { + // SAFETY: + // `pdev` is always valid. + // `barnr` is checked for validity at the top of the function. + unsafe { bindings::pci_release_region(pdev.as_raw(), barnr) }; + return Err(ENOMEM); + } + + let iomem =3D match IoMem::new(ioptr, barlen as usize) { + Ok(iomem) =3D> iomem, + Err(err) =3D> { + // SAFETY: + // `pdev` is always valid. + // `ioptr` was created above, and `num` was checked at the= top of the function. + unsafe { Self::do_release(&pdev, ioptr, num) }; + return Err(err); + } + }; + + Ok(Bar { pdev, iomem, num }) + } + + fn index_is_valid(i: u8) -> bool { + // A pci_dev on the C side owns an array of resources with at most + // PCI_NUM_RESOURCES entries. + if i as i32 >=3D bindings::PCI_NUM_RESOURCES as i32 { + return false; + } + + true + } + + // SAFETY: The caller should ensure that `ioptr` is valid. + unsafe fn do_release(pdev: &Device, ioptr: usize, num: u8) { + // SAFETY: + // `pdev` is Rust data and guaranteed to be valid. + // A valid `ioptr` should be provided by the caller, but an invali= d one + // does not cause faults on the C side. + // `num` is checked for validity above. + unsafe { + bindings::pci_iounmap(pdev.as_raw(), ioptr as _); + bindings::pci_release_region(pdev.as_raw(), num as i32); + } + } + + fn release(&self) { + // SAFETY: + // Safe because `self` always contains a refcounted device that be= longs + // to a pci::Device. + // `ioptr` and `num` are always valid because the Bar was created = successfully. + unsafe { Self::do_release(&self.pdev, self.iomem.ioptr, self.num) = }; + } +} + +impl Drop for Bar { + fn drop(&mut self) { + self.release(); + } +} + +impl Deref for Bar { + type Target =3D IoMem; + + fn deref(&self) -> &Self::Target { + &self.iomem + } +} + impl Device { /// Create a PCI Device instance from an existing `device::Device`. /// @@ -319,6 +422,24 @@ pub fn set_master(&self) { // SAFETY: By the type invariants, we know that `self.ptr` is non-= null and valid. unsafe { bindings::pci_set_master(self.as_raw()) }; } + + /// Returns the size of the given PCI bar resource. + pub fn resource_len(&self, bar: u8) -> Result { + if !Bar::index_is_valid(bar) { + return Err(EINVAL); + } + + // SAFETY: Safe as by the type invariant. + Ok(unsafe { bindings::pci_resource_len(self.as_raw(), bar.into()) = }) + } + + /// Mapps an entire PCI-BAR after performing a region-request on it. + pub fn iomap_region(&mut self, barnr: u8, name: &CStr) -> Result> { + let bar =3D Bar::new(self.clone(), barnr, name)?; + let devres =3D Devres::new(self.0.clone(), bar, GFP_KERNEL)?; + + Ok(devres) + } } =20 impl AsRef for Device { --=20 2.45.1