From nobody Wed Feb 11 05:41:21 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 EE7A01849DA for ; Tue, 18 Jun 2024 23:40:54 +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=1718754056; cv=none; b=u9BPbn3XhRUlG4E/3i7hwVwDn+6AsUReZSQVBOxEFNIIkPztjZgn0Q7rAmnboHIwk8PnFrUj+GZ1zR1XtVHq3YWTy7pYKpsVj3HK6m8xPqPkIMaBRBXGBkewuQCfAGQ6YY4a14LTYKYEVMbpKf+RcLvyJy69VSmH824aDys23fU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718754056; c=relaxed/simple; bh=MLFfiEGNgBcr9bLiufmhyDYAqVr8JMOqYi/vywiSXPs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iYih0oKJhBLlIkgquu9IK5X0ysRaeg0461H+73ylOC1Ly/bkYTY43fZL8hALAisHE/ufprUPhM9DtyBej2UN2+HWOgPekZqbT0CbHaqt+gO8avq/ahUVd1znl8EeUMjP6vgVZO6knjaOqC8stDaFZnp0Wsf8YWBT4rx+Ijgcsw4= 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=eDUbIIgn; 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="eDUbIIgn" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1718754054; 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=1qjkzHfzy3NUrrPbUMcZvTn0a95wVyUbECTV/h26qqI=; b=eDUbIIgnim16pVOWv1vm1FFnxuILIoNbk4OaYdEU4N2UtUW4U93NV7ymqtsbjSb8sK0UyJ qYKZ4ow21V3QyGOeN+cchy1+PzOevUY0Kc7Fvp1Y3dkC80FSMHHRTnH7YyEgJLKaid2ui3 UNNKKGEGzAoY7kW7NQc3zxs/FPUSilA= 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-651-GP1lpgTvN5qoZbizzck9vQ-1; Tue, 18 Jun 2024 19:40:47 -0400 X-MC-Unique: GP1lpgTvN5qoZbizzck9vQ-1 Received: by mail-wm1-f72.google.com with SMTP id 5b1f17b1804b1-42183fdb37cso44087145e9.3 for ; Tue, 18 Jun 2024 16:40:47 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718754046; x=1719358846; 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=1qjkzHfzy3NUrrPbUMcZvTn0a95wVyUbECTV/h26qqI=; b=o4nrfmoACCYSpc0KHaMV6Mstu8M8MYNmbHdk7b2/NuPZohPCsnp0FsevlW8Eww3wv4 9G8vDcPKg9k8YIQ8p5FkQx1Ofeb2gDvGYvz3ufRkR22QQSbl4IJsrZ6CuvVuSymqNqpV 7MqLRP/zvdD6TGUAe65Qfef4ODCH+XZyLhBSGMURyK4AFTSApCrKerSnjSuLc1u3qJg6 +N/AfLwhpnj0XIUen25W78gMsGXnozLUJ9gO9jtv6JCzOX+Fn4JxAOVHJiR208JvGYd/ G3blMb4T46vRcuuwPXhBn/VaGJT/if8PHNMWAO2DVPZsxn+OkGp8/M+oRyWBSWr/sjaV 2ZnA== X-Forwarded-Encrypted: i=1; AJvYcCVmsM45po3FFyMhn+P/l+3wU1zRR4/Da3HzPq7ZvcnP8c+HNTERo+23YBaP/Y+z0AZebENHH5LPreJ8vwwkL6XGMTWPuTs3gpRhgeV3 X-Gm-Message-State: AOJu0YzDU8EXo6kp7ZTxaXh1DvZQ9nzatFFDofcyhp8X2l+oPgREeh3z 03DvbUlxWAGR0yhXiio4lQgCprNAmrH2P79w9KCAP8nQFOSXfpCu07LlEGQCVqBPE5UcHKyopfn kN7DpLupxEN8+7bvVa4/aBBlcjfRBUU8NnUpyGjNrhMMvRBceiZ77G9iD9hZoqA== X-Received: by 2002:a05:600c:26d1:b0:422:35:d19d with SMTP id 5b1f17b1804b1-4247529df2emr5259545e9.36.1718754046259; Tue, 18 Jun 2024 16:40:46 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHDw6Fk9C824R3TVe0mbmNmXLCAKpwzyBK1V1rVulamZFyXGPFnWYHmT55mcrVs+DxsV8w/Pw== X-Received: by 2002:a05:600c:26d1:b0:422:35:d19d with SMTP id 5b1f17b1804b1-4247529df2emr5259325e9.36.1718754045909; Tue, 18 Jun 2024 16:40:45 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-423034f4129sm198773995e9.14.2024.06.18.16.40.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 18 Jun 2024 16:40:45 -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, robh@kernel.org, daniel.almeida@collabora.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [PATCH v2 01/10] rust: pass module name to `Module::init` Date: Wed, 19 Jun 2024 01:39:47 +0200 Message-ID: <20240618234025.15036-2-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240618234025.15036-1-dakr@redhat.com> References: <20240618234025.15036-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" In a subsequent patch we introduce the `Registration` abstraction used to register driver structures. Some subsystems require the module name on driver registration (e.g. PCI in __pci_register_driver()), hence pass the module name to `Module::init`. Signed-off-by: Danilo Krummrich --- rust/kernel/lib.rs | 14 ++++++++++---- rust/kernel/net/phy.rs | 2 +- rust/macros/module.rs | 3 ++- samples/rust/rust_minimal.rs | 2 +- samples/rust/rust_print.rs | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index a791702b4fee..5af00e072a58 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -71,7 +71,7 @@ pub trait Module: Sized + Sync + Send { /// 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 /// A module that is pinned and initialised in-place. @@ -79,13 +79,19 @@ pub trait InPlaceModule: Sync + Send { /// Creates an initialiser for the module. /// /// It is called when the module is loaded. - fn init(module: &'static ThisModule) -> impl init::PinInit; + fn init( + name: &'static str::CStr, + module: &'static ThisModule, + ) -> impl init::PinInit; } =20 impl InPlaceModule for T { - fn init(module: &'static ThisModule) -> impl init::PinInit { + fn init( + name: &'static str::CStr, + module: &'static ThisModule, + ) -> impl init::PinInit { let initer =3D move |slot: *mut Self| { - let m =3D ::init(module)?; + let m =3D ::init(name, module)?; =20 // SAFETY: `slot` is valid for write per the contract with `pi= n_init_from_closure`. unsafe { slot.write(m) }; diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs index fd40b703d224..ccb2552dc107 100644 --- a/rust/kernel/net/phy.rs +++ b/rust/kernel/net/phy.rs @@ -887,7 +887,7 @@ struct Module { [$($crate::net::phy::create_phy_driver::<$driver>()),+]; =20 impl $crate::Module for Module { - fn init(module: &'static ThisModule) -> Result { + fn init(_name: &'static CStr, module: &'static ThisModule)= -> Result { // SAFETY: The anonymous constant guarantees that nobo= dy else can access // the `DRIVERS` static. The array is used only in the= C side. let drivers =3D unsafe { &mut DRIVERS }; diff --git a/rust/macros/module.rs b/rust/macros/module.rs index 105be4797f85..be03b2cf77a1 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -302,7 +302,8 @@ mod __module_init {{ /// /// This function must only be called once. unsafe fn __init() -> core::ffi::c_int {{ - let initer =3D <{type_} as kernel::InPlaceModule>:= :init(&super::super::THIS_MODULE); + let initer =3D <{type_} as kernel::InPlaceModule>:= :init(kernel::c_str!(\"{name}\"), + = &super::super::THIS_MODULE); // SAFETY: No data race, since `__MOD` can only be= accessed by this module // and there only `__init` and `__exit` access it.= These functions are only // called once and `__exit` cannot be called befor= e or during `__init`. 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:21 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 222091849C0 for ; Tue, 18 Jun 2024 23:40:53 +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=1718754055; cv=none; b=RgWe8B9Yn0dWm56TB0/ouLExHqq94JNgmRs9D2Xv4SesCfDFht67RfD4OS1nzS8cGSYqqpBBPn9LBhFGlXRLx/vAjcIV1n8vZolwsOxR5onZELON3F8aNs/52g2Js36bVTCi6xXo2u41C5j+xXVd3KyOW1HwBVU/oVDvoZO9jCE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718754055; c=relaxed/simple; bh=NJ06HFiNnwp5HwQ1Yv82r8BnXq42M6HbbJtDiVlYhkw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=d6COD/5rq6kzO7ZASneOgkfmA6YOSGVFc4VXuRs6U4mbkGjingfMFmdWHk8bRDPJ4Ql5FTX7MpOPS0k9Pu3MiR3gJkkNJbXFkwn5croEDeHK8DfoFVeyzRujTobPwfLNc0/m2/m7XhoiLEbzqyKUs9v4U4RAxK4tcisBCvcgQdI= 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=frOy5sHd; 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="frOy5sHd" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1718754053; 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=FLt8R9mG7+Q5vVfP6l0HGRxBa2WWQOQ3AyEB5PRg0/8=; b=frOy5sHdkz4PjJPuxX+L/cbj4ZRYDRoFdpKpcUUaBsIIMYbM0BMx4b4GB2TUk/9wTDwjCP QCvrxeuW0Q54pe/XM+GT8ijwXMA/39YVx4JC2u+3WZqp758jtCFaIj0NVTK4rLzilDZCDe Z1BWf99KlrpNmCR9Em1ViUngCAA2XHk= 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-510-ZpXiEOe8PpO9WjahEQMO8g-1; Tue, 18 Jun 2024 19:40:51 -0400 X-MC-Unique: ZpXiEOe8PpO9WjahEQMO8g-1 Received: by mail-wr1-f70.google.com with SMTP id ffacd0b85a97d-36083bd1b12so3634798f8f.1 for ; Tue, 18 Jun 2024 16:40:51 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718754050; x=1719358850; 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=FLt8R9mG7+Q5vVfP6l0HGRxBa2WWQOQ3AyEB5PRg0/8=; b=Mnzt6Mj+iwpRlVcttDbmd2CHaE6s0MknVegou9BL6PlVBSTHxNuh5TnOIBKhDwYK1T Bq2/68zhAobPeHlTU7+xVZWYPGrR64aUyGHEN90twZ4mw6KBmH9kj9f0VQiScTO4nAgL f6TULEMJCsExx4JV5oeCyMVneA+j4JPfmK51+Kw15fGxeBi1KMmwr7HHn2B5WzW3/tLU gTLQG9f2FjqBw4c6uw3wynffHtXBOrSI7tMY3O+guZN84oB91OwEV51tGfLTM0/FH8+0 d9VPX6NZDevssKKN4lZezdejuC7FsBuReou+AXyV3LjQUxo9f9eArXtlTRPEQosYGNCE wrmQ== X-Forwarded-Encrypted: i=1; AJvYcCXKZ9On6O2o7D/vEmkL5oghIbWGPZoUqv8bMcRCw9tacmx5fTWov9b/5CSrOh0o2HX41WkWTUoHJxeGPXDsbXrjuOwJTOu69l2/sHHy X-Gm-Message-State: AOJu0Yw0UlPjKf2qcSAxhrAOqeGfowilPjIC10Ag1vuptabzfRQGcDIy ZrFVXsQraxoVo0Ipr34Y9Oxk8HcCYEyYxgvxMkYUfVjytZ9lh3wCt3SnoUYtwVmBu4uh0WJlivC x+dKliOJDcz0jHvl+HThB4G6E93q1rpm7xkH5KlMXiEMix1Fxh9/oxPx0LoueIw== X-Received: by 2002:a5d:4535:0:b0:354:f218:9661 with SMTP id ffacd0b85a97d-363170ed3femr858522f8f.14.1718754050262; Tue, 18 Jun 2024 16:40:50 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGU+U6Wm0EGp4IMDnnc/QucR5wKEoc9VgmpJhXeJunCegvyHy5rlDYh7FvIr5roHHV/75nmtw== X-Received: by 2002:a5d:4535:0:b0:354:f218:9661 with SMTP id ffacd0b85a97d-363170ed3femr858498f8f.14.1718754049849; Tue, 18 Jun 2024 16:40:49 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-360917c264bsm8131346f8f.56.2024.06.18.16.40.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 18 Jun 2024 16:40:49 -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, robh@kernel.org, daniel.almeida@collabora.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [PATCH v2 02/10] rust: implement generic driver registration Date: Wed, 19 Jun 2024 01:39:48 +0200 Message-ID: <20240618234025.15036-3-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240618234025.15036-1-dakr@redhat.com> References: <20240618234025.15036-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" Implement the generic `Registration` type and the `DriverOps` trait. The `Registration` structure is the common type that represents a driver registration and is typically bound to the lifetime of a module. However, it doesn't implement actual calls to the kernel's driver core to register drivers itself. Instead the `DriverOps` trait is provided to subsystems, which have to implement `DriverOps::register` and `DrvierOps::unregister`. Subsystems have to provide an implementation for both of those methods where the subsystem specific variants to register / unregister a driver have to implemented. For instance, the PCI subsystem would call __pci_register_driver() from `DriverOps::register` and pci_unregister_driver() from `DrvierOps::unregister`. This patch is based on previous work from Wedson Almeida Filho. Co-developed-by: Wedson Almeida Filho Signed-off-by: Wedson Almeida Filho Signed-off-by: Danilo Krummrich --- rust/kernel/driver.rs | 128 ++++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 2 files changed, 129 insertions(+) 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..e04406b93b56 --- /dev/null +++ b/rust/kernel/driver.rs @@ -0,0 +1,128 @@ +// 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 all= ows drivers to register +//! using the [`Registration`] class. + +use crate::error::{Error, Result}; +use crate::{init::PinInit, str::CStr, try_pin_init, types::Opaque, ThisMod= ule}; +use core::pin::Pin; +use macros::{pin_data, pinned_drop}; + +/// The [`DriverOps`] trait serves as generic interface for subsystems (e.= g., PCI, Platform, Amba, +/// etc.) to privide the corresponding subsystem specific implementation t= o register / unregister a +/// driver of the particular type (`RegType`). +/// +/// For instance, the PCI subsystem would set `RegType` to `bindings::pci_= driver` and call +/// `bindings::__pci_register_driver` from `DriverOps::register` and +/// `bindings::pci_unregister_driver` from `DriverOps::unregister`. +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`]. + 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`]. + fn unregister(reg: &mut Self::RegType); +} + +/// A [`Registration`] is a generic type that represents the registration = of some driver type (e.g. +/// `bindings::pci_driver`). Therefore a [`Registration`] is initialized w= ith some type that +/// implements the [`DriverOps`] trait, such that the generic `T::register= ` and `T::unregister` +/// calls result in the subsystem specific registration calls. +/// +///Once the `Registration` structure is dropped, the driver is unregistere= d. +#[pin_data(PinnedDrop)] +pub struct Registration { + #[pin] + reg: Opaque, +} + +// 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 {} + +// SAFETY: Both registration and unregistration are implemented in C and s= afe to be performed from +// any thread, so `Registration` is `Send`. +unsafe impl Send for Registration {} + +impl Registration { + /// Creates a new instance of the registration object. + pub fn new(name: &'static CStr, module: &'static ThisModule) -> impl P= inInit { + try_pin_init!(Self { + reg <- Opaque::try_ffi_init(|ptr: *mut T::RegType| { + // SAFETY: `try_ffi_init` guarantees that `ptr` is valid f= or write. + unsafe { ptr.write(T::RegType::default()) }; + + // SAFETY: `try_ffi_init` guarantees that `ptr` is valid f= or write, and it has + // just been initialised above, so it's also valid for rea= d. + let drv =3D unsafe { &mut *ptr }; + + T::register(drv, name, module) + }), + }) + } +} + +#[pinned_drop] +impl PinnedDrop for Registration { + fn drop(self: Pin<&mut Self>) { + let drv =3D unsafe { &mut *self.reg.get() }; + + T::unregister(drv); + } +} + +/// 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. +#[pin_data] +pub struct Module { + #[pin] + _driver: Registration, +} + +impl crate::InPlaceModule for Module { + fn init(name: &'static CStr, module: &'static ThisModule) -> impl PinI= nit { + try_pin_init!(Self { + _driver <- Registration::::new(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 5af00e072a58..5382402cd3db 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -29,6 +29,7 @@ pub mod alloc; mod build_assert; pub mod device; +pub mod driver; pub mod error; #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)] pub mod firmware; --=20 2.45.1 From nobody Wed Feb 11 05:41:21 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 02A781891D4 for ; Tue, 18 Jun 2024 23:40:57 +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=1718754059; cv=none; b=dfgQOfnFXQoAumDj62ankWs/zXICAsdGn0Ac/CYRquX6UwkkFvB8sgiT4qV7aXzUdHQ+OyqREaASYjKI/1WSFspT9E6YxrlK9OaZqLhFPBOEX9ohBmkLAh5bHY40iHu893/CBjU6QSRypIASDUcZ5KpEQsTfxrZA3XurxwJtdTo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718754059; c=relaxed/simple; bh=+7YZ7zoHeaWY2jj+Hmsqe+5GAkc7KbDsloUj0Ihy+bM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HhicJNMDXCf7T5XmjBnyHiapzRu7+IDQzLPHt6LbjRfkw02Fli2xRDEaaLqNa6tUUXLaPJZZ0b7IHHMhtFVfxow74M07DJWNnWcFbn4n1CFOYLCg+JSgL/RneLGTFUdFbRINfNLopKjCh7f7v9bp7Ov785QTsulFxZVOk5DgUQk= 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=UasgZXeA; 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="UasgZXeA" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1718754057; 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=zBDr9a0UbEqIFlf/LskXVOaMDv/jbs0Q1ckYvSRHn7Y=; b=UasgZXeASfmYdUUyHSZRsRdFuZl9n2h7uMCzTGfhDb6twUKCDEDfiIoP7l3V7VIA057ZZH QwLQwoV7XKQlu+R4ykXkraV7XuHfBqV7ONhHcP6kT03GgvJ+5YO1r6Ybay0pnVRFu7M1AD LEKPIAN0NV5Fg7X6IQjKxYiO8lKkAVc= 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-453-rgTKh5UDPpqyOWFgxi8fDQ-1; Tue, 18 Jun 2024 19:40:55 -0400 X-MC-Unique: rgTKh5UDPpqyOWFgxi8fDQ-1 Received: by mail-wm1-f72.google.com with SMTP id 5b1f17b1804b1-4246ed3f877so11568425e9.3 for ; Tue, 18 Jun 2024 16:40:55 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718754054; x=1719358854; 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=zBDr9a0UbEqIFlf/LskXVOaMDv/jbs0Q1ckYvSRHn7Y=; b=rnPbaRM1X7hp7EHb2Hs59aO9UArRBfc7DXfwU9kMBpvqmotqp2+I3GbnwLdsC8y33s 1Y7CQ9sV37F0Yt8Zp9L0EEXMEZDgZh56XrUKq458vwWhv9glaQa8vARzB431TyU8Pt0w wCjL5uJeX+Bc3hKIR9TncZYXbfAyT8JuH9mv9I8A7z/noQ9Ov/0KuUOc6YlkvucxDkHF +p1DNe8yLjG4WJL4mEG2eQyHI+XcVGYKfXfLfc3X5jjGX0SUwJ/C1CxpZJ0aTDdBSqrM Mo9zGHKm7fGx0VizMt3nCGK7Q774WsNsNTfZ4Hog4WQZO5OD0UK66vEOpu3kh4oj+9JT 00NA== X-Forwarded-Encrypted: i=1; AJvYcCXPtj8DeHflY+4EHBvV4N/9zc+rFsQfl0kkcLQuZ3jMWzmAH32WGiPegNAn6UA4gE1cyWK99VupijMAISuZdWCblifomwbPYC35ajsc X-Gm-Message-State: AOJu0YwD+PQM7pNsABt5PKes/keZ65jUfmjYKlHb66cSIGjI+5H1wT+K TJ8MN3QWjR0CYobTLWck9IzZRpPzyEa05BdyfRkFo0EvEedJMUEanasekcgX9jlE8jylYIcDS6k Cl3fBoXy5SGVaa8T9KRO6QIB6GF6FDJEs6lkWsqkE+91Mu3hxf5m15hnHVd6BCg== X-Received: by 2002:a05:600c:4999:b0:422:615f:6499 with SMTP id 5b1f17b1804b1-42475296a36mr5599225e9.31.1718754054190; Tue, 18 Jun 2024 16:40:54 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEufWj1Ckx6qQqVdF2phLsYFyC0caxpANSWAXdqR9eux02qPS2yQharLpwxEtuSMotu8Vow8w== X-Received: by 2002:a05:600c:4999:b0:422:615f:6499 with SMTP id 5b1f17b1804b1-42475296a36mr5599025e9.31.1718754053787; Tue, 18 Jun 2024 16:40:53 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-362e3e1abd5sm1407662f8f.47.2024.06.18.16.40.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 18 Jun 2024 16:40:53 -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, robh@kernel.org, daniel.almeida@collabora.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [PATCH v2 03/10] rust: implement `IdArray`, `IdTable` and `RawDeviceId` Date: Wed, 19 Jun 2024 01:39:49 +0200 Message-ID: <20240618234025.15036-4-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240618234025.15036-1-dakr@redhat.com> References: <20240618234025.15036-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 Most subsystems use some kind of ID to match devices and drivers. Hence, we have to provide Rust drivers an abstraction to register an ID table for the driver to match. Generally, those IDs are subsystem specific and hence need to be implemented by the corresponding subsystem. However, the `IdArray`, `IdTable` and `RawDeviceId` types provide a generalized implementation that makes the life of subsystems easier to do so. Co-developed-by: Asahi Lina Signed-off-by: Asahi Lina Signed-off-by: Wedson Almeida Filho Co-developed-by: Danilo Krummrich Signed-off-by: Danilo Krummrich --- rust/kernel/device_id.rs | 336 +++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + 2 files changed, 338 insertions(+) create mode 100644 rust/kernel/device_id.rs diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs new file mode 100644 index 000000000000..c490300f29bb --- /dev/null +++ b/rust/kernel/device_id.rs @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Generic implementation of device IDs. +//! +//! Each bus / subsystem that matches device and driver through a bus / su= bsystem specific ID is +//! expected to implement [`RawDeviceId`]. + +use core::marker::PhantomData; + +/// 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. +/// - `to_rawid` is implemented and stores `offset` in the context/data = field of the raw device +/// id so that buses can recover the pointer to the data. (This should= actually be a trait +/// function, however, this requires `const_trait_impl`, and hence has= to changed once the +/// feature is stabilized.) +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 the concrete ID = type as a parameter 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::device_id::IdArray<$id_type, U, N> + where + $id_type: $crate::device_id::RawDeviceId + Copy, + <$id_type as $crate::device_id::RawDeviceId>::RawType: Copy + = Clone, + { + let mut raw_ids =3D + [<$id_type as $crate::device_id::RawDeviceId>::ZERO; N]; + let mut i =3D 0usize; + while i < N { + let offset: isize =3D $crate::device_id::IdArray::<$id_typ= e, 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::device_id::IdArray::<$id_type, U, N>::new(raw_ids,= infos) + } + } + + 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 +/// +/// ``` +/// # use kernel::{define_id_array, device_id::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 RawDeviceId for Id { +/// type RawType =3D (u64, isize); +/// const ZERO: Self::RawType =3D (0, 0); +/// } +/// +/// impl Id { +/// #[allow(clippy::wrong_self_convention)] +/// const fn to_rawid(&self, offset: isize) -> ::Ra= wType { +/// (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::device_id::IdArray<$id_type, + $data_type, { + $crate::count_pa= ren_items!($($t)*) + }> =3D $crate::_new_= id_array!( + ($crate::first_i= tem!($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 +/// +/// ``` +/// # use kernel::{define_id_table, device_id::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 RawDeviceId for Id { +/// type RawType =3D (u64, isize); +/// const ZERO: Self::RawType =3D (0, 0); +/// } +/// +/// impl Id { +/// #[allow(clippy::wrong_self_convention)] +/// const fn to_rawid(&self, offset: isize) -> ::Ra= wType { +/// (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::device_id::IdTable<'static, $id_= type, $data_type>> =3D { + $crate::define_id_array!(ARRAY, $id_type, $data_type, [ $($t)*= ]); + Some(ARRAY.as_table()) + }; + }; +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 5382402cd3db..98e1a1425d17 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 device_id; pub mod driver; pub mod error; #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)] --=20 2.45.1 From nobody Wed Feb 11 05:41:21 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 F2EEB18A953 for ; Tue, 18 Jun 2024 23:41:01 +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=1718754064; cv=none; b=YUAJcTv1n6F2xuLZrOx47plNx2fxFtg4WA5UgxwtW6G/6ocsfAPT3cBHW8p1dhGL+N+UEADtTI6fhrtN8IIRUselAnEmau4jlLWAxuRbZysawtKBe+HVBOnXrctgfXT8339A7+kcYcjp4lrptxZUYSC70kFA58mcK1mPRM0CReU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718754064; c=relaxed/simple; bh=8xOJgvmgRDc6UuoCbIh+OUfNqsEyrLgJDPK4zjjIFpo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=FFmVg+GB7AGBEi5kooEFLEobw+SKiBgojGTvKrbTtJYNMD95ORCrgaE7sevLHLM1LSfxIQtbobiRbv1rmEU39oNCkJUcH3a3eT+awdqtlDQd6ssliXlTNGsH1icGQ7KkV5JmNuvbVV9b5kkLyX8Cw18kbceYhu+2vT/oUlwAb5A= 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=g7IYW/BO; 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="g7IYW/BO" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1718754061; 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=jprPy2R3mhSfiLsY0D8Ey4z3vVsEHFqMm7nzo3vj3ew=; b=g7IYW/BOvWpucwg/anAiASqqkTffFI96yKlrt7+oQA25FIkDlpK+Si3DUP/6gH22eOk2WO wW/a5+aUruWIeMERDVPV2kn4buonurPyPckPEtP/HOr57NGfUs95r4YTyO/ZyRkMIdVKk0 EEqLtPb3emqCeujhcnQKNijr3GVxj6c= Received: from mail-lf1-f70.google.com (mail-lf1-f70.google.com [209.85.167.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-142-4qqf85HaOYGCxBwo8GMVBA-1; Tue, 18 Jun 2024 19:40:59 -0400 X-MC-Unique: 4qqf85HaOYGCxBwo8GMVBA-1 Received: by mail-lf1-f70.google.com with SMTP id 2adb3069b0e04-52bc0027a29so3753845e87.3 for ; Tue, 18 Jun 2024 16:40:59 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718754058; x=1719358858; 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=jprPy2R3mhSfiLsY0D8Ey4z3vVsEHFqMm7nzo3vj3ew=; b=aNfSEF//+NxYSwpD6+N5lKfGx/sezXAXVHnKOYUELAGo8vyPhmzsg9QzADApRpl+wf Yg9KvTmckUxwJdUGYiBZs9zSUlQFcBxlxBrV/a4//mAdAKqVIqfi1nsGMPTtfiXtr8yx tTDK9qHBJlLRHZGpHJvFKRkeD/JVeyqU6qfGMsp+W+totWk/D02FYLej+7GK8iM/OwqG jjX4kU/DomRPApQE4hqOobxJeMsPAYwEDlrmI/S2SIZUMIClxooFGF4NeSwDZDV9qGs7 70x+u/SaP8fORnad7UGLjthYcQN11jpbRDy/tOUArovusaaSfSR9QxW7rRONHsg2uOpV MS3g== X-Forwarded-Encrypted: i=1; AJvYcCUpjuGeInF5e7DrvhPJQJnhYWajGhamJzeuFIds+bAiXmEgEbnUdgMayEzwS40lozPo7hngWT1qsg2iruAYbMD2CsO9XKaY5vNResns X-Gm-Message-State: AOJu0YzubJ5gkrwRndxv32kIvaXKhq0fIN/iETp0S9NdJnyS2MqbTSXN qn2mffSAfqJupkwEv4WU+jX+SPel4wllpPW3aFwM8O60tBNNqnXGgGAN0GL10OTAwANhZcZp0eg fHpwl+Mands4RqqqY2pifPeJHmNX0bglNa+p/1oKmt1mdsQiYNCNFDhAw+gCH+Q== X-Received: by 2002:a05:6512:3992:b0:52c:cc38:592c with SMTP id 2adb3069b0e04-52ccc385978mr111665e87.0.1718754058295; Tue, 18 Jun 2024 16:40:58 -0700 (PDT) X-Google-Smtp-Source: AGHT+IG6PRwRcXm8oRvxKpZbUxJQyrNH1GdkYsyClgqCZ2yzVNat84h8w9f3Fio+1+mz3vgR3u+6fg== X-Received: by 2002:a05:6512:3992:b0:52c:cc38:592c with SMTP id 2adb3069b0e04-52ccc385978mr111652e87.0.1718754057790; Tue, 18 Jun 2024 16:40:57 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-422874de623sm244364595e9.31.2024.06.18.16.40.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 18 Jun 2024 16:40:57 -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, robh@kernel.org, daniel.almeida@collabora.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [PATCH v2 04/10] rust: add rcu abstraction Date: Wed, 19 Jun 2024 01:39:50 +0200 Message-ID: <20240618234025.15036-5-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240618234025.15036-1-dakr@redhat.com> References: <20240618234025.15036-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 0e02b2c64c72..0ce40ccb978b 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -166,6 +167,20 @@ rust_helper_krealloc(const void *objp, size_t new_size= , gfp_t flags) } EXPORT_SYMBOL_GPL(rust_helper_krealloc); =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..5a35495f69a4 --- /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`](srctree/include/linux/rcupdate.= 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:21 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 35DC618E75C for ; Tue, 18 Jun 2024 23:41:06 +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=1718754068; cv=none; b=JtN/v/i6Jo8JRrWGthieMeGkfRxZyk08wMcBpQsxJ6/W88Lr6Fpa0ZVWXkedXTkJ86pzZnssa52oj+cgwb0iSL1dp+RCBhzTtQq3Wa61C/ea6PpcP9v178oZkPUY6JqRr8dl568gEkgW0gfbK71ubh6FHTnxOmwkcNeL0Gut3RQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718754068; c=relaxed/simple; bh=UUFxFHITd44dYQQCrdSR7AKzqHAzy/8/2oc53NIFk98=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=r+VFrIM5sXgwctsZvFfqi+0ONVwXo3t47qkux664JlClAEXO2vj1tzaFMOOxbQXCp+kHGDt4d4rsVG2mn4qCNv2S6tFy+A5tUFamhrSWiCV0zFkkztLM45O0BGUII4EzsS2VvDlGq8pnIpBkHoHZwMMMpHTqQPozyQdXOygaQ8k= 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=Ly7CcAZ7; 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="Ly7CcAZ7" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1718754065; 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=Bi44cMQJYS99EJNabY9/CKPO8kbpcfOs6bSOujDcKYk=; b=Ly7CcAZ7olwNaSa9ujRtZyNKWvl1VXKFrK3AL9Gw8PTna9WEVbTkcw+faxuUI+6bJTwSRQ k5tkAEimAYUzoX5HMbM92CaWS3N2kmGAGnMrwAluO2sDZA4I1aAui+Gs455q1YQleCge6e IMa4ttVnYu4FpCDqfOZcixzT5IYwdTY= Received: from mail-lj1-f200.google.com (mail-lj1-f200.google.com [209.85.208.200]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-76-RL92bPOwPzKe4vMmm71ukg-1; Tue, 18 Jun 2024 19:41:04 -0400 X-MC-Unique: RL92bPOwPzKe4vMmm71ukg-1 Received: by mail-lj1-f200.google.com with SMTP id 38308e7fff4ca-2eae96cecaeso43574661fa.2 for ; Tue, 18 Jun 2024 16:41:03 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718754062; x=1719358862; 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=Bi44cMQJYS99EJNabY9/CKPO8kbpcfOs6bSOujDcKYk=; b=HN+lCJIGvwY6P9BTPK8aFA9oGHZ3pb8JZ7qEJ17Vzp2B3VzTdpkWfxeryZUxDMRjEg h7mghvBZTkx4zdvKSiMtDAx6SBesU2T3EBtcTMXvj7V/q+89cfxIqYXbsF4ssy34v47V YQeKDMeVjWbovfFDZq2da+YSLIhxbikzGX4i8mJhDErgkLipNvwwj3tsMHOq6qNy1CvX JRp7MQ/xHce7n8FoipvX1NTqge28URn7frMUFHT9zCVMWaUJuKTUKmu0eUpWc5+aC/UW nRbEPCFt6Um8L2g81GsUSZhaKCKcGMIN+yG1NQ+Twghy7mZdzeMWTFeNrxQdhrH8gdZa kxSA== X-Forwarded-Encrypted: i=1; AJvYcCX2r65Rd7P7AFjMmkQ+Fu4hxmajlmQl0s0mXLrtAhepUw7nVu6AYasLqcu+vWGYiRf11PK/jnlIsj2X11TTrmCiMG9wGEq6NA1q/gFX X-Gm-Message-State: AOJu0YzMwDUg/0+TJxmEH8Qe6KknVgJELNegpfLpAKE7uVKyjw+dOjdK 4gi1Ef0obd2THnxxeN4UMzBJuCVU0+tuFoUzvczDSNlBed/TxqoXL/llTPIL7PWk5hIHQmDjixW Kvrkpdvcasm2e6K0Kwrl3rAXUqMYMpliKDaRdgAVxvuh1VUmQ4EJ0j8PpxEW6HQ== X-Received: by 2002:a2e:860e:0:b0:2ec:1a6:7b01 with SMTP id 38308e7fff4ca-2ec3cfe89ddmr6249771fa.33.1718754062207; Tue, 18 Jun 2024 16:41:02 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEFgW1oPW6Telack7ZlNngY6AO2Ju8qX6ud+6J6wA8ngjmpZTBDF4ZgU97FkysKrX/11O2PFA== X-Received: by 2002:a2e:860e:0:b0:2ec:1a6:7b01 with SMTP id 38308e7fff4ca-2ec3cfe89ddmr6249631fa.33.1718754061829; Tue, 18 Jun 2024 16:41:01 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-36077536f78sm15232625f8f.7.2024.06.18.16.41.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 18 Jun 2024 16:41:01 -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, robh@kernel.org, daniel.almeida@collabora.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [PATCH v2 05/10] rust: add `Revocable` type Date: Wed, 19 Jun 2024 01:39:51 +0200 Message-ID: <20240618234025.15036-6-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240618234025.15036-1-dakr@redhat.com> References: <20240618234025.15036-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 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 another 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). Signed-off-by: Wedson Almeida Filho Signed-off-by: Danilo Krummrich --- rust/kernel/lib.rs | 1 + rust/kernel/revocable.rs | 209 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 rust/kernel/revocable.rs diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 98e1a1425d17..601c3d3c9d54 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -43,6 +43,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..3d13e7b2f2e8 --- /dev/null +++ b/rust/kernel/revocable.rs @@ -0,0 +1,209 @@ +// 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::{AtomicBool, 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 Box::pin_init(Revocable::new(Example { a: 10, b: 20 }), GFP_= KERNEL).unwrap(); +/// 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 Box::pin_init(Revocable::new(Example { a: 10, b: 20 }), GFP_= KERNEL).unwrap(); +/// 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 } + } +} --=20 2.45.1 From nobody Wed Feb 11 05:41:21 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 BB71A18EFE0 for ; Tue, 18 Jun 2024 23:41:10 +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=1718754073; cv=none; b=ijM/pE/0VuZeXFxrlLELoGKnDUOdZ7EJ9YB8iqjJUfq5six0Ye0yfw0JtzboFZQE7n0JFIr4tO2izhOeIX8Zc2YUNf3yFa43GDbmHk56fEL/D4k/NCtUjteuIckrqWazkji5sUU7c7S5KxDXJkjOuymlJT4XvOQrUoX8QB/Vk28= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718754073; c=relaxed/simple; bh=Sb1huo/jVQeqC8cQdV+VwsVxGrjJiIWgiNzFXIV3Gdg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GEfK3wQlkWxN8O+1PG7/E32IArx5VYCyNF381gztSJdDI8jRIVgv8lk2btOPcg3tL0lDwxMlfZlF9Surm3Bo8xEybVMpJ2MqtvbIoop8xMXFptPOfR324SJhNWmX71JuUnw82t7iCM6GYNiAl2yXrsQxTzFChZRX9RolfzEfUQA= 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=gzk89W4j; 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="gzk89W4j" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1718754069; 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=60dG7eRJLNPIXEREyuozdI1ODf6cr9f/vPrt0nYaK/c=; b=gzk89W4jLgO9evHhSwNXbUT5OvjbLfAzGtwgJy8LxUogt5QMcGTkfkArgazTdZWYnxeO53 ieViLk10C3sS7kV/f/W+E/zN04SE7U+H51KtoJ/R85cquMV039kEs4g0yFTal/x8RBgdaT Wi9+3GXXCldZRKXIARj2pbi9PLmyMOA= 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-584-X4Ojh9mbOKGbrkJ_SE2lQw-1; Tue, 18 Jun 2024 19:41:08 -0400 X-MC-Unique: X4Ojh9mbOKGbrkJ_SE2lQw-1 Received: by mail-wr1-f70.google.com with SMTP id ffacd0b85a97d-3625bef4461so173323f8f.1 for ; Tue, 18 Jun 2024 16:41:08 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718754067; x=1719358867; 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=60dG7eRJLNPIXEREyuozdI1ODf6cr9f/vPrt0nYaK/c=; b=U50+ua8CulTqvgiKwzK82iKISbkuf65Ujy4Ja6wiIzBVRJt8GKfUfZ2t2QWuOxfbzt m0F4khfPuBgNFxmeKp+GBeSVkPnAnfo1/gpdINblgnmT6E11W3yDS33yYBWK9MVkAAqI Gnf8Zzo0QpdljZeAZ0AmcAOmWz4gK0TSOsZwo2be7AULHhVU2ixJLzKT0lLH70J9pDUs 59CLcBNa5S6luNEASzsncxHNpvIKjEaw7HOnOrsu6C0J+UqO78iLDQ00YyXp766Vt8nY 1sFvLBpuPE+T9iDpQUHZhuuDeiI1ujq9nxS5svInEmB1CiSzXkWqj00vtFfUpmGEfAl5 wxGQ== X-Forwarded-Encrypted: i=1; AJvYcCXiFsdmz2WOPeGE6f5ug5aK2DGEK4bJuvi4ouEjWMVHy3Xt7BOBQH1oiDGMBrXkrK54FI+dO28wBod5jdrsdp1GsL1t6ku+awdaux2C X-Gm-Message-State: AOJu0Yy+eP2jSLuQGNK8S3VfcaDUydQvCLR4cR/lMyHTKeS71542459/ h2icaSVyX7vbWtn2wPTy3nAEUp/zOwZNkcF4j1b4bYM+6zXwb2Pyd4iF69DjxBe+cpiPZnjthkV rDcMrBo4yKbBRntZzNUpDLcFzK2LgoCGtKjsaqLklPvkdE20YZyrYqxKK5VpvaA== X-Received: by 2002:a05:6000:1a54:b0:363:1c9d:d853 with SMTP id ffacd0b85a97d-3631c9dd9damr728041f8f.32.1718754067233; Tue, 18 Jun 2024 16:41:07 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEi2i3RdP9b80UdfiXW0cwoIttpp/C0z+z1afjXJ+n6o8np/c6FIUGGYfwhNBvXn2JFwAIDHQ== X-Received: by 2002:a05:6000:1a54:b0:363:1c9d:d853 with SMTP id ffacd0b85a97d-3631c9dd9damr728034f8f.32.1718754066845; Tue, 18 Jun 2024 16:41:06 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-360750f0d71sm15350188f8f.86.2024.06.18.16.41.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 18 Jun 2024 16:41:05 -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, robh@kernel.org, daniel.almeida@collabora.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Wedson Almeida Filho , Danilo Krummrich Subject: [PATCH v2 06/10] rust: add `dev_*` print macros. Date: Wed, 19 Jun 2024 01:39:52 +0200 Message-ID: <20240618234025.15036-7-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240618234025.15036-1-dakr@redhat.com> References: <20240618234025.15036-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 | 319 ++++++++++++++++++++++++++++++++++++++++- rust/kernel/prelude.rs | 2 + 2 files changed, 320 insertions(+), 1 deletion(-) diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index e445e87fb7d7..058767339a64 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -8,7 +8,10 @@ bindings, types::{ARef, Opaque}, }; -use core::ptr; +use core::{fmt, ptr}; + +#[cfg(CONFIG_PRINTK)] +use crate::c_str; =20 /// A reference-counted device. /// @@ -79,6 +82,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 reference-counted. @@ -100,3 +207,213 @@ unsafe impl Send for Device {} // SAFETY: `Device` can be shared among threads because all immutable meth= ods are protected by the // synchronization in `struct device`. unsafe impl Sync for Device {} + +#[doc(hidden)] +#[macro_export] +macro_rules! dev_printk { + ($method:ident, $dev:expr, $($f:tt)*) =3D> { + { + ($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:21 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 62DA518FC6D for ; Tue, 18 Jun 2024 23:41:15 +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=1718754077; cv=none; b=GzlMjbL1Lnbe01fY9GQ7V5A7LZXUo2IRrohRHDlApqCloD+Asp+gj1vZBTw7y8F3Slty0hgrqyKmeu3a7jMQqOkyI54O09iRg9PD0qxB24eASKkYF4Qs/nzjpS+Y8KWE727lRLiKDjzE3imy328zwb95rILOcde+xnL7PnCjkro= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718754077; c=relaxed/simple; bh=zvACUlV8IBXU/pD3ic7mnNuB1qziSw9soNd8mfHLC0I=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HAWQoRCfJ1puKQA+/iyfUtXp3GeZi5jbciCmZnSNINFN25dRRsPFwnn6IsJn0kBZAc2aSjH3kYd2tQcvw6erGskf//idMt+9aBElNMXdqg+tcV9s5dLYyrSLjCp2Ib8crwZB7iZ0PGBskZsfT1GfoB1jhtzl1hSKhEOgx0Ik1O8= 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=RGFJ0Mko; 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="RGFJ0Mko" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1718754074; 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=2pc4rm2j75XYt4xCDuTDl+9iM4dO3yt5GwjGMzNcQwQ=; b=RGFJ0Mkowo+V/HrXXURf/wkPvjwWLUFA3fYTPFdzNbeeyvok/I7GUlOnQpLWQpFj8Pl9sY zAJCOlxc62myw3MZVZpy2rIdUo/kHhHGkmbopb/MwU+f8YxOabeGxCAokG8t59DDYwZSB8 9Rcfy/d+I2JV4BDF29Z2t2a3oGnYUQM= Received: from mail-lf1-f69.google.com (mail-lf1-f69.google.com [209.85.167.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-209-uWVAjoGNNPWbWc73vDVXDA-1; Tue, 18 Jun 2024 19:41:13 -0400 X-MC-Unique: uWVAjoGNNPWbWc73vDVXDA-1 Received: by mail-lf1-f69.google.com with SMTP id 2adb3069b0e04-52cb4cf42f6so2610041e87.2 for ; Tue, 18 Jun 2024 16:41:12 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718754071; x=1719358871; 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=2pc4rm2j75XYt4xCDuTDl+9iM4dO3yt5GwjGMzNcQwQ=; b=amzSbjaYxWTjFyQzRcPpmeDp2c/spivg/gQawssKoQAiMB4mggA4RIdMCEMsUxGixV rpdkwafY7w+aj5iVSozv2gA4jS8J6vgtkmaqugg1IpEwjc0XuqOATrfHAfFv4PbhYtw5 oFk0Tr6QkkplAjSRyOCxrLn8XrIKeXhE7x515gYfDBpzSW7OnBvE9gRz6+McGe08ELf/ oKKpjT7YQqSFAC9uLbeXPnF46mIdl85wxh9hUYyIKXi1yXNrPfJx4Cjr90UNRVXqGQJv y/ylMTD/svPQXwBBWecWWhNkZESvJjne3S+7g8tR6cT4s0mBzQMxaoo51UsBkmCGHfuq Fw7Q== X-Forwarded-Encrypted: i=1; AJvYcCXvG/3MZb9//FS49SriJMVxUgIvtNBbmNKryxUR42mSQ/mjH4uHaBfrHUoU8IMuqNlEZVMS4fhRpK4dzUz7qvL0dh/eFoV+iHi7AD8F X-Gm-Message-State: AOJu0YxmvaFawPRMduUjH/E9mzb8tkCh/9t8d1eKpcJqVkpDS6iMrQEu sylph/bB4UUmr433fTH6bPZCcF92RytOexw5oBHW4ACY7jF5Xg0kL7+B7PvPdRc6vybTxH3E7m+ tEimTqlW23RKHakGK7oUGWZumFYSbYzu/thVDOAoc1PDbHGKaBmdTjHrxTaWluQ== X-Received: by 2002:a05:6512:1283:b0:52c:5254:b625 with SMTP id 2adb3069b0e04-52ccaa53e28mr724813e87.52.1718754071315; Tue, 18 Jun 2024 16:41:11 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHyhZD+H4cjxW/UXfmKccooFgq7bXcOlABZ65YfTv5CT4Ga/VtpKU0QwYIRw4WS5nhSk+d31Q== X-Received: by 2002:a05:6512:1283:b0:52c:5254:b625 with SMTP id 2adb3069b0e04-52ccaa53e28mr724788e87.52.1718754070819; Tue, 18 Jun 2024 16:41:10 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-36387d9b812sm47454f8f.26.2024.06.18.16.41.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 18 Jun 2024 16:41:10 -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, robh@kernel.org, daniel.almeida@collabora.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [PATCH v2 07/10] rust: add `io::Io` base type Date: Wed, 19 Jun 2024 01:39:53 +0200 Message-ID: <20240618234025.15036-8-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240618234025.15036-1-dakr@redhat.com> References: <20240618234025.15036-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" I/O memory is typically either mapped through direct calls to ioremap() or subsystem / bus specific ones such as pci_iomap(). Even though subsystem / bus specific functions to map I/O memory are based on ioremap() / iounmap() it is not desirable to re-implement them in Rust. Instead, implement a base type for I/O mapped memory, which generically provides the corresponding accessors, such as `Io::readb` or `Io:try_readb`. `Io` supports an optional const generic, such that a driver can indicate the minimal expected and required size of the mapping at compile time. Correspondingly, calls to the 'non-try' accessors, support compile time checks of the I/O memory offset to read / write, while the 'try' accessors, provide boundary checks on runtime. `Io` is meant to be embedded into a structure (e.g. pci::Bar or io::IoMem) which creates the actual I/O memory mapping and initializes `Io` accordingly. To ensure that I/O mapped memory can't out-live the device it may be bound to, subsystems should embedd the corresponding I/O memory type (e.g. pci::Bar) into a `Devres` container, such that it gets revoked once the device is unbound. Co-developed-by: Philipp Stanner Signed-off-by: Philipp Stanner Signed-off-by: Danilo Krummrich Reviewed-by: Daniel Almeida --- rust/helpers.c | 106 ++++++++++++++++++++++ rust/kernel/io.rs | 219 +++++++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 3 files changed, 326 insertions(+) create mode 100644 rust/kernel/io.rs diff --git a/rust/helpers.c b/rust/helpers.c index 0ce40ccb978b..824b7c0b98dc 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -181,6 +182,111 @@ void rust_helper_rcu_read_unlock(void) EXPORT_SYMBOL_GPL(rust_helper_rcu_read_unlock); /* end rcu */ =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 + /* * `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/io.rs b/rust/kernel/io.rs new file mode 100644 index 000000000000..a19a1226181d --- /dev/null +++ b/rust/kernel/io.rs @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Memory-mapped IO. +//! +//! C header: [`include/asm-generic/io.h`](srctree/include/asm-generic/io.= h) + +use crate::error::{code::EINVAL, Result}; +use crate::{bindings, build_assert}; + +/// IO-mapped memory, starting at the base address @addr and spanning @max= len bytes. +/// +/// The creator (usually a subsystem such as PCI) is responsible for creat= ing the +/// mapping, performing an additional region request etc. +/// +/// # Invariant +/// +/// `addr` is the start and `maxsize` the length of valid I/O remapped mem= ory region. +/// +/// # Examples +/// +/// ``` +/// # use kernel::{bindings, io::Io}; +/// # use core::ops::Deref; +/// +/// // See also [`pci::Bar`] for a real example. +/// struct IoMem(Io); +/// +/// impl IoMem { +/// fn new(paddr: usize) -> Result{ +/// +/// // SAFETY: assert safety for this example +/// let addr =3D unsafe { bindings::ioremap(paddr as _, SIZE.try_i= nto().unwrap()) }; +/// if addr.is_null() { +/// return Err(ENOMEM); +/// } +/// +/// // SAFETY: `addr` is guaranteed to be the start of a valid I/O= mapped memory region of +/// // size `SIZE`. +/// let io =3D unsafe { Io::new(addr as _, SIZE)? }; +/// +/// Ok(IoMem(io)) +/// } +/// } +/// +/// impl Drop for IoMem { +/// fn drop(&mut self) { +/// // SAFETY: Safe as by the invariant of `Io`. +/// unsafe { bindings::iounmap(self.0.base_addr() as _); }; +/// } +/// } +/// +/// impl Deref for IoMem { +/// type Target =3D Io; +/// +/// fn deref(&self) -> &Self::Target { +/// &self.0 +/// } +/// } +/// +/// let iomem =3D IoMem::<{ core::mem::size_of::() }>::new(0xBAAAAAAD= ).unwrap(); +/// iomem.writel(0x42, 0x0); +/// assert!(iomem.try_writel(0x42, 0x0).is_ok()); +/// assert!(iomem.try_writel(0x42, 0x4).is_err()); +/// ``` +pub struct Io { + addr: usize, + maxsize: usize, +} + +macro_rules! define_read { + ($(#[$attr:meta])* $name:ident, $try_name:ident, $type_name:ty) =3D> { + /// Read IO data from a given offset known at compile time. + /// + /// Bound checks are performed on compile time, hence if the offse= t is not known at compile + /// time, the build will fail. + $(#[$attr])* + #[inline] + pub fn $name(&self, offset: usize) -> $type_name { + let addr =3D self.io_addr_assert::<$type_name>(offset); + + unsafe { bindings::$name(addr as _) } + } + + /// Read IO data from a given offset. + /// + /// Bound checks are performed on runtime, it fails if the offset = (plus the type size) is + /// out of bounds. + $(#[$attr])* + pub fn $try_name(&self, offset: usize) -> Result<$type_name> { + let addr =3D self.io_addr::<$type_name>(offset)?; + + Ok(unsafe { bindings::$name(addr as _) }) + } + }; +} + +macro_rules! define_write { + ($(#[$attr:meta])* $name:ident, $try_name:ident, $type_name:ty) =3D> { + /// Write IO data from a given offset known at compile time. + /// + /// Bound checks are performed on compile time, hence if the offse= t is not known at compile + /// time, the build will fail. + $(#[$attr])* + #[inline] + pub fn $name(&self, value: $type_name, offset: usize) { + let addr =3D self.io_addr_assert::<$type_name>(offset); + + unsafe { bindings::$name(value, addr as _, ) } + } + + /// Write IO data from a given offset. + /// + /// Bound checks are performed on runtime, it fails if the offset = (plus the type size) is + /// out of bounds. + $(#[$attr])* + pub fn $try_name(&self, value: $type_name, offset: usize) -> Resul= t { + let addr =3D self.io_addr::<$type_name>(offset)?; + + unsafe { bindings::$name(value, addr as _) } + Ok(()) + } + }; +} + +impl Io { + /// + /// + /// # Safety + /// + /// Callers must ensure that `addr` is the start of a valid I/O mapped= memory region of size + /// `maxsize`. + pub unsafe fn new(addr: usize, maxsize: usize) -> Result { + if maxsize < SIZE { + return Err(EINVAL); + } + + Ok(Self { addr, maxsize }) + } + + /// Returns the base address of this mapping. + #[inline] + pub fn base_addr(&self) -> usize { + self.addr + } + + /// Returns the size of this mapping. + #[inline] + pub fn maxsize(&self) -> usize { + self.maxsize + } + + #[inline] + const fn offset_valid(offset: usize, size: usize) -> bool { + let type_size =3D core::mem::size_of::(); + if let Some(end) =3D offset.checked_add(type_size) { + end <=3D size && offset % type_size =3D=3D 0 + } else { + false + } + } + + #[inline] + fn io_addr(&self, offset: usize) -> Result { + if !Self::offset_valid::(offset, self.maxsize()) { + return Err(EINVAL); + } + + // Probably no need to check, since the safety requirements of `Se= lf::new` guarantee that + // this can't overflow. + self.base_addr().checked_add(offset).ok_or(EINVAL) + } + + #[inline] + fn io_addr_assert(&self, offset: usize) -> usize { + build_assert!(Self::offset_valid::(offset, SIZE)); + + self.base_addr() + offset + } + + define_read!(readb, try_readb, u8); + define_read!(readw, try_readw, u16); + define_read!(readl, try_readl, u32); + define_read!( + #[cfg(CONFIG_64BIT)] + readq, + try_readq, + u64 + ); + + define_read!(readb_relaxed, try_readb_relaxed, u8); + define_read!(readw_relaxed, try_readw_relaxed, u16); + define_read!(readl_relaxed, try_readl_relaxed, u32); + define_read!( + #[cfg(CONFIG_64BIT)] + readq_relaxed, + try_readq_relaxed, + u64 + ); + + define_write!(writeb, try_writeb, u8); + define_write!(writew, try_writew, u16); + define_write!(writel, try_writel, u32); + define_write!( + #[cfg(CONFIG_64BIT)] + writeq, + try_writeq, + u64 + ); + + define_write!(writeb_relaxed, try_writeb_relaxed, u8); + define_write!(writew_relaxed, try_writew_relaxed, u16); + define_write!(writel_relaxed, try_writel_relaxed, u32); + define_write!( + #[cfg(CONFIG_64BIT)] + writeq_relaxed, + try_writeq_relaxed, + u64 + ); +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 601c3d3c9d54..f4dd11014a65 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -56,6 +56,7 @@ =20 #[doc(hidden)] pub use bindings; +pub mod io; pub use macros; pub use uapi; =20 --=20 2.45.1 From nobody Wed Feb 11 05:41:21 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 72A79190693 for ; Tue, 18 Jun 2024 23:41:18 +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=1718754080; cv=none; b=LpBrx7Xv4uhcDWYJjauJOleLNsRvvJtHoX6956k6PA+h98Pi0DPRZCNbajxqellxhR0DaCWfj6ctbPRenqqyb36I9oXIABB0irAKcn6UVF933f52bMsVxwpH/SBCIOGU94mNaTgckYnVtII61teJCj4/ADYwNNlhLJ/cBFvNLoA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718754080; c=relaxed/simple; bh=EBw38hbSMJ4L48oAzDUxRJIjvYWmo5x/U079N5zEgOo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=W9IiXyTGfUf3Z1zqT2SQeX0jhpJmFkvduUmJAo3rAx1WsV2bsnTnBOFjGGVVgA0ez0EIVRkL32uoCZnR9fbBVVWsS32WUR/VosgVSti4ugO/UH7mkUYTiejcoHGLP3gpmK1+I08U5PMnA87QMP/FEqTKK+5dtA30TSP3kL/y4E0= 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=hJbm6px9; 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="hJbm6px9" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1718754077; 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=XqI5ygNChRTcl6pYO7gGep9CWBe4/cTEl5OVzb7sLnk=; b=hJbm6px95NzWjAZ/aP7WzskQI3MwiTEbXft/kP/9Cqetcz3IU5rfK+RwUhZNGjsFPk2r5a bAM4sPEVgyGb71QFiKA/OHg2PVVAggh4vkPLRKf78HbDfkKrtxxGsKFbDb5YjXE0vYZE1R Za4hgLtrubutU6eLnSIwWu0quKngOas= 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-280-A2ivHhhLOJe03z1029KaBg-1; Tue, 18 Jun 2024 19:41:16 -0400 X-MC-Unique: A2ivHhhLOJe03z1029KaBg-1 Received: by mail-wr1-f70.google.com with SMTP id ffacd0b85a97d-362a0c1041dso423361f8f.1 for ; Tue, 18 Jun 2024 16:41:16 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718754075; x=1719358875; 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=XqI5ygNChRTcl6pYO7gGep9CWBe4/cTEl5OVzb7sLnk=; b=EvValVjQugFDBTVosd5sdFZboFgQki0x3ZVq79SKT5B8gy04GBLxik+gz1zIbgrvAo fEyp3MLsDk8XNH0Rmhh+tIu7a7+7UWJU94ECulHRZnHa2oYehzGND0WSK1IAILrZXW4r A7m6mi/thw/Y4Fq1yQSaKNcgiSFdmiRD354xmTf3hVl1i1xXSmsRIcSF4kfW4MtPVzLW nbyeBW02MDD7Z4/HkYnOwCJlRdyEWAaXRgkuW3ICJdBo/xTQ1st49xyc1wxXXp3jV0XN DrtAt6saNExs89fSrq18zcQLLIe3LB4iglyMyieSUaTqOwefDXwekrf0LKkq7K18YER3 z2/A== X-Forwarded-Encrypted: i=1; AJvYcCVnLpOaIhyeKwD9vs81tFX342u0cOXS8BJ+gdk6mWpn2DGJN3en+pui2YNz5qFIMoPl59Z2yINMqZlfBE3NSOsPx8HSBMpmVZPZvcbw X-Gm-Message-State: AOJu0Yz58zzFfSiN3fnU2NdWrXGcUxBMbyicOQUVtySps29/bw9e6mfv HS17IHky6UtZfoCB9Z0WeBy6IrqTxOvpbJ/vzzKcRmNW2PuA4Hdza6UMcC2yMEJvqhAbJPCmD4r gvF3YokTCemBPK8X7gm9PKxn6wRWTFrI/InbzAQRgXozv9Vbkah9ghVuFsK+peQ== X-Received: by 2002:adf:fa44:0:b0:35d:ca63:4e74 with SMTP id ffacd0b85a97d-363199907a4mr576669f8f.70.1718754075086; Tue, 18 Jun 2024 16:41:15 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFhmtLVbtHLOjx+KKo+aXa8pMAKtnnN15KAyO8GWHBuRqRj3fAwBZG4C2+l0meUQKo6/yav/Q== X-Received: by 2002:adf:fa44:0:b0:35d:ca63:4e74 with SMTP id ffacd0b85a97d-363199907a4mr576644f8f.70.1718754074734; Tue, 18 Jun 2024 16:41:14 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-36075114dcfsm15387775f8f.114.2024.06.18.16.41.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 18 Jun 2024 16:41:14 -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, robh@kernel.org, daniel.almeida@collabora.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [PATCH v2 08/10] rust: add devres abstraction Date: Wed, 19 Jun 2024 01:39:54 +0200 Message-ID: <20240618234025.15036-9-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240618234025.15036-1-dakr@redhat.com> References: <20240618234025.15036-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(). Signed-off-by: Danilo Krummrich --- rust/helpers.c | 6 ++ rust/kernel/devres.rs | 168 ++++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 3 files changed, 175 insertions(+) create mode 100644 rust/kernel/devres.rs diff --git a/rust/helpers.c b/rust/helpers.c index 824b7c0b98dc..269f97698588 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -287,6 +287,12 @@ void rust_helper_writeq_relaxed(u64 value, volatile vo= id __iomem *addr) EXPORT_SYMBOL_GPL(rust_helper_writeq_relaxed); #endif =20 +int rust_helper_devm_add_action(struct device *dev, void (*action)(void *)= , void *data) +{ + return devm_add_action(dev, action, data); +} +EXPORT_SYMBOL_GPL(rust_helper_devm_add_action); + /* * `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..ab0a3eb1ea4f --- /dev/null +++ b/rust/kernel/devres.rs @@ -0,0 +1,168 @@ +// 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, + sync::Arc, +}; + +use core::ffi::c_void; +use core::ops::Deref; + +#[pin_data] +struct DevresInner { + #[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::{bindings, c_str, device::Device, devres::Devres, io::Io= }; +/// # use core::ops::Deref; +/// +/// // See also [`pci::Bar`] for a real example. +/// struct IoMem(Io); +/// +/// impl IoMem { +/// fn new(paddr: usize) -> Result{ +/// +/// // SAFETY: assert safety for this example +/// let addr =3D unsafe { bindings::ioremap(paddr as _, SIZE.try_i= nto().unwrap()) }; +/// if addr.is_null() { +/// return Err(ENOMEM); +/// } +/// +/// // SAFETY: `addr` is guaranteed to be the start of a valid I/O= mapped memory region of +/// // size `SIZE`. +/// let io =3D unsafe { Io::new(addr as _, SIZE)? }; +/// +/// Ok(IoMem(io)) +/// } +/// } +/// +/// impl Drop for IoMem { +/// fn drop(&mut self) { +/// // SAFETY: Safe as by the invariant of `Io`. +/// unsafe { bindings::iounmap(self.0.base_addr() as _); }; +/// } +/// } +/// +/// impl Deref for IoMem { +/// type Target =3D Io; +/// +/// fn deref(&self) -> &Self::Target { +/// &self.0 +/// } +/// } +/// +/// # // SAFETY: *NOT* safe, just for the example to get an `ARef`= instance +/// # let dev =3D unsafe { Device::from_raw(core::ptr::null_mut()) }; +/// +/// let iomem =3D IoMem::<{ core::mem::size_of::() }>::new(0xBAAAAAAD= ).unwrap(); +/// let devres =3D Devres::new(&dev, iomem, GFP_KERNEL).unwrap(); +/// +/// let res =3D devres.try_access().ok_or(ENXIO).unwrap(); +/// res.writel(0x42, 0x0); +/// ``` +/// +pub struct Devres(Arc>); + +impl DevresInner { + fn new(dev: &Device, data: T, flags: Flags) -> Result>> { + let inner =3D Arc::pin_init( + pin_init!( DevresInner { + data <- Revocable::new(data), + }), + flags, + )?; + + // Convert `Arc` into a raw pointer and make devres o= wn this reference until + // `Self::devres_callback` is called. + let data =3D inner.clone().into_raw(); + let ret =3D unsafe { + bindings::devm_add_action(dev.as_raw(), Some(Self::devres_call= back), data as _) + }; + + if ret !=3D 0 { + // SAFETY: We just created another reference to `inner` in ord= er to pass it to + // `bindings::devm_add_action`. If `bindings::devm_add_action`= fails, we have to drop + // this reference accordingly. + let _ =3D unsafe { Arc::from_raw(data) }; + return Err(Error::from_errno(ret)); + } + + Ok(inner) + } + + unsafe extern "C" fn devres_callback(ptr: *mut c_void) { + let ptr =3D ptr as *mut DevresInner; + // Devres owned this memory; now that we received the callback, dr= op the `Arc` and hence the + // reference. + // SAFETY: Safe, since we leaked an `Arc` reference to devm_add_ac= tion() in + // `DevresInner::new`. + let inner =3D unsafe { Arc::from_raw(ptr) }; + + inner.data.revoke(); + } +} + +impl Devres { + /// Creates a new [`Devres`] instance of the given `data`. The `data` = encapsulated within the + /// returned `Devres` instance' `data` will be revoked once the device= is detached. + pub fn new(dev: &Device, data: T, flags: Flags) -> Result { + let inner =3D DevresInner::new(dev, data, flags)?; + + Ok(Devres(inner)) + } + + /// Same as [Devres::new`], but does not return a `Devres` instance. I= nstead the given `data` + /// is owned by devres and will be revoked / dropped, once the device = is detached. + pub fn new_foreign_owned(dev: &Device, data: T, flags: Flags) -> Resul= t { + let _ =3D DevresInner::new(dev, data, flags)?; + + Ok(()) + } +} + +impl Deref for Devres { + type Target =3D Revocable; + + fn deref(&self) -> &Self::Target { + &self.0.data + } +} + +impl Drop for Devres { + fn drop(&mut self) { + // Revoke the data, such that it gets dropped already and the actu= al resource is freed. + // `DevresInner` has to stay alive until the devres callback has b= een called. This is + // necessary since we don't know when `Devres` is dropped and call= ing + // `devm_remove_action()` instead could race with `devres_release_= all()`. + self.revoke(); + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index f4dd11014a65..ef9426e32c18 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -31,6 +31,7 @@ mod build_assert; pub mod device; pub mod device_id; +pub mod devres; pub mod driver; pub mod error; #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)] --=20 2.45.1 From nobody Wed Feb 11 05:41:21 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 4B00F198A02 for ; Tue, 18 Jun 2024 23:41:22 +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=1718754084; cv=none; b=N2o2cjWlohh9iJ3vtuQEEiZXNszmPKbn6jDqQ/QRc4/DTODN5ETsmnUJhEtNER7BkHRYLkXiZOP7DvUjhQP8gHtTXVvxvLG4cWTA+ncE3SICjlZ7V43J4pR08AJfUmUD65PIRuBthJZlSzR9Do/8q2S1dwRl3mA8z1YkSJErC1o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718754084; c=relaxed/simple; bh=UFnHR2b1RkzyMe8sQ//KZ/B2Dh7qxTRbvQElPMM07j4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=qos2GI+K4qa7a/iSKJgr49jubMQt6S+2369ucvz+qJ0/xtH2dxzdUWryuxddjTg73Cmi7oFZmzF5zILU5wS/b4Bcq+Oubjh2q7Y02OyZMsQWkO2edRV8HiozFXx7AhB3qGOYZKtjKkidRgcsReQXJeS5bJzl46Jn9+mWlD2dJeY= 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=F48fKsv4; 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="F48fKsv4" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1718754081; 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=QLRFxUPd3gb5I0rzZWJWZKgMG9lV0BT3fJ3l5tLlEmo=; b=F48fKsv49TnotR/Nnv3IWKpxcBBpmsXXx0pG9I91kChAv3fSYeI5vPnxpI7QTfYVzfXVC8 wJqsC8lyIyMpHz09AyQRoi5IMrxshlSX1/00XREarSJLTqtykv/GDPyKSrdVvu9u+US0of FZ1BB/nQxrqnIsJz2cQpT8p9/nkkCho= Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-686-GR34WYXYPsuNkVi3jKQyow-1; Tue, 18 Jun 2024 19:41:20 -0400 X-MC-Unique: GR34WYXYPsuNkVi3jKQyow-1 Received: by mail-wm1-f71.google.com with SMTP id 5b1f17b1804b1-4210d151c5bso44364735e9.3 for ; Tue, 18 Jun 2024 16:41:20 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718754079; x=1719358879; 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=QLRFxUPd3gb5I0rzZWJWZKgMG9lV0BT3fJ3l5tLlEmo=; b=WGwN8yJmFqaBTVRuYlAyohVUIIQ35pZ9i7YyTB/kNWTjN4tOTDCd1noD3wUvTedXuK RYRA9ehsiUpC52zDiKh8cw7/4eGtzcyx7jkElNpAQWZz/l5G/V8DTwghIKUGa2g5NoQw XhmvSYm66uMa424c8ae+pQGP69MAPPlNUBQYjjIYrRmHv4z+IaQwBMM62c5dAlhK73BD /8ezPsn8VqRegHGCB3e20RP9jB6inJLJGQvm++YMw5oSUqIxeOpMgFs+Sx/b8LwpSX2w PVkSBeQ/vhOupgJ4puE3qvv4gSYfD3aMBgRsluPNwYgl4q1LjTt5n6LpSw17RHe8MoB1 JceA== X-Forwarded-Encrypted: i=1; AJvYcCXJEs+l/GY0kg3v7/Q/rPphpr5ThudFG/TzvmT15mwPDWWQxvc2NBOmjZRZfsw1YQQwFUmq03GwDg7vQXAwMHxHvrOus2VOa9jCBKT6 X-Gm-Message-State: AOJu0YwFX9TqhBEfDZF4U2/nBC6/YNnZgNbEM1zuI5xQWEyQGLGGFueU 1NGIfzpgziRxlcMtet/qN9/uQP9TujjAOsH7SQNQySHrDl/exoAW1FffdcqH9tB60SiMivH46ke q1335ZGeknSEIVg92YnpxdE8I9EXP2CcStQ4UQat69buUye/ROOHq7rOOylHklA== X-Received: by 2002:adf:e410:0:b0:35f:2211:5a28 with SMTP id ffacd0b85a97d-363192d0918mr787478f8f.54.1718754079049; Tue, 18 Jun 2024 16:41:19 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFoWOFB721TrUZWeWqqkImFJ6hYgZXirnt34BXfyNcuar464EL2gQ55ZvdXbYLzJLPLHiHHfg== X-Received: by 2002:adf:e410:0:b0:35f:2211:5a28 with SMTP id ffacd0b85a97d-363192d0918mr787466f8f.54.1718754078694; Tue, 18 Jun 2024 16:41:18 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-360750f2303sm15502597f8f.87.2024.06.18.16.41.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 18 Jun 2024 16:41:18 -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, robh@kernel.org, daniel.almeida@collabora.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [PATCH v2 09/10] rust: pci: add basic PCI device / driver abstractions Date: Wed, 19 Jun 2024 01:39:55 +0200 Message-ID: <20240618234025.15036-10-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240618234025.15036-1-dakr@redhat.com> References: <20240618234025.15036-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" Implement the basic PCI abstractions required to write a basic PCI driver. This includes the following data structures: The `pci::Driver` trait represents the interface to the driver and provides `pci::Driver::probe` and `pci::Driver::remove` for the driver to implement. The `pci::Device` abstraction represents a `struct pci_dev` and provides abstractions for common functions, such as `pci::Device::set_master`. In order to provide the PCI specific parts to a generic `driver::Registration` the `driver::DriverOps` trait is implemented by the `pci::Adapter`. `pci::DeviceId` implements PCI device IDs based on the generic `driver::RawDevceId` abstraction. This patch is based on previous work from FUJITA Tomonori. Co-developed-by: FUJITA Tomonori Signed-off-by: FUJITA Tomonori Co-developed-by: Philipp Stanner Signed-off-by: Philipp Stanner Signed-off-by: Danilo Krummrich --- rust/bindings/bindings_helper.h | 1 + rust/helpers.c | 18 ++ rust/kernel/lib.rs | 2 + rust/kernel/pci.rs | 325 ++++++++++++++++++++++++++++++++ 4 files changed, 346 insertions(+) create mode 100644 rust/kernel/pci.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 18a3f05115cb..30ad2a0e22d7 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers.c b/rust/helpers.c index 269f97698588..c7f90b457af5 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -293,6 +294,23 @@ int rust_helper_devm_add_action(struct device *dev, vo= id (*action)(void *), void } EXPORT_SYMBOL_GPL(rust_helper_devm_add_action); =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 ef9426e32c18..4a02946dbbd9 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -59,6 +59,8 @@ pub use bindings; pub mod io; 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..a8230474e9b8 --- /dev/null +++ b/rust/kernel/pci.rs @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Wrappers for the PCI subsystem +//! +//! C header: [`include/linux/pci.h`](srctree/include/linux/pci.h) + +use crate::{ + bindings, container_of, device, + device_id::{IdTable, RawDeviceId}, + 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. +pub struct Adapter(T); + +impl driver::DriverOps for Adapter { + type RegType =3D bindings::pci_driver; + + fn register( + pdrv: &mut Self::RegType, + name: &'static CStr, + module: &'static ThisModule, + ) -> Result { + 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: `pdrv` is guaranteed to be a valid `RegType`. + to_result(unsafe { + bindings::__pci_register_driver(pdrv as _, module.0, name.as_c= har_ptr()) + }) + } + + fn unregister(pdrv: &mut Self::RegType) { + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`. + unsafe { bindings::pci_unregister_driver(pdrv) } + } +} + +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> { + // Let the `struct pci_dev` own a reference of the driver'= s private data. + // SAFETY: The core kernel only ever calls the probe callb= ack with a valid `pdev`. + 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: The core kernel only ever calls the probe callback with= a valid `pdev`. `ptr` + // points to a valid reference of the driver's private data, as it= was set by + // `Adapter::probe_callback`. + let data =3D unsafe { + let ptr =3D bindings::pci_get_drvdata(pdev); + + T::Data::from_foreign(ptr) + }; + + T::remove(&data); + } +} + +/// Declares a kernel module that exposes a single PCI driver. +/// +/// # Example +/// +///```ignore +/// kernel::module_pci_driver! { +/// type: MyDriver, +/// name: "Module name", +/// author: "Author name", +/// description: "Description", +/// license: "GPL v2", +/// } +///``` +#[macro_export] +macro_rules! module_pci_driver { + ($($f:tt)*) =3D> { + $crate::module_driver!(, $crate::pci::Adapter, { $($f)* }); + }; +} + +/// 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 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 +/// +/// See [`Driver`] +/// +#[macro_export] +macro_rules! define_pci_id_table { + ($data_type:ty, $($t:tt)*) =3D> { + type IdInfo =3D $data_type; + const ID_TABLE: $crate::device_id::IdTable<'static, $crate::pci::D= eviceId, $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. +/// +/// # Example +/// +///``` +/// # use kernel::{bindings, define_pci_id_table, pci, sync::Arc}; +/// +/// struct MyDriver; +/// struct MyDeviceData; +/// +/// impl pci::Driver for MyDriver { +/// type Data =3D Arc; +/// +/// define_pci_id_table! { +/// (), +/// [ (pci::DeviceId::new(bindings::PCI_VENDOR_ID_REDHAT, +/// bindings::PCI_ANY_ID as u32), +/// None) +/// ] +/// } +/// +/// fn probe( +/// _pdev: &mut pci::Device, +/// _id_info: Option<&Self::IdInfo> +/// ) -> Result { +/// Err(ENODEV) +/// } +/// +/// fn remove(_data: &Self::Data) { +/// } +/// } +///``` +/// 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 =3D ();` + type Data: ForeignOwnable; + + /// 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: 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 type invaraints. + 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: Safe by the type invariants. + 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: Safe by the type invariants. + 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:21 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 DFFBB1891A3 for ; Tue, 18 Jun 2024 23:41:26 +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=1718754088; cv=none; b=gL8lNbrxxDreFkLXlM5KlTR67xyC8MEWb4h5BtzUH4m0G8rhoP9pDIfFiDDMgRhlIc/E6jLnrPjOizVOO7WgkqJ5kozD5C2lbloU/yrLx65tMVSujWT/U7MQeZDUEVfGj241UtnciRCel79+evI6cFpwYX4EsETd9DSWbZVa8/Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718754088; c=relaxed/simple; bh=RCEMwYVvuUbZz+/rEeI8myzcrUIo6owjk7lRZAsrGQM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=thE5itYUOurLMXtov8GjPsdsVZvoPcLKOkus2EKprLZq1JyxBJbhlDw1r/D7SR21hf8viJawWIw7jKf6cqmD8il+7jKzmId7C0SP3W4I5HwaZPl9iNXiEhcJtrAOn+fx+7J2GzRBQjINlqmNUDxmTDtrb+1rlQ25tWcB91FMJl0= 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=HrkOED4r; 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="HrkOED4r" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1718754086; 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=UVH4NPjG6WbuLQxLO9Ab399xq2yWE6YB8L0prZ4B/nk=; b=HrkOED4rFejrWIUnUy196zFOCGy/mEcNUFo6R2EOsy/867e0tUdhE/whVoTA/Np1/rhBSU +UabwTDe9Cf1o7pU0pJR/Y3rPMay2F8rSZAZn9Bbmf9i4KXBPoB9/CFsn0nR5u9+po6O2m y/Lhhde5Z8HNAOTrn280eGkq/gcyC5Y= Received: from mail-lf1-f72.google.com (mail-lf1-f72.google.com [209.85.167.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-643-zxZlv1cjPY22-I5FQySXQg-1; Tue, 18 Jun 2024 19:41:24 -0400 X-MC-Unique: zxZlv1cjPY22-I5FQySXQg-1 Received: by mail-lf1-f72.google.com with SMTP id 2adb3069b0e04-52c805e6f38so4235129e87.0 for ; Tue, 18 Jun 2024 16:41:24 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718754083; x=1719358883; 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=UVH4NPjG6WbuLQxLO9Ab399xq2yWE6YB8L0prZ4B/nk=; b=pcNJDSPYB5/nousMQbPIZmo5kPBwdm6N5XW7WXbt8U0/X0ITY98KaWd2TvfD1p21vU xOjyHSsj6SzjJ1AaTMD4lbh43+lWSxYcVIQ4g8LsCUNaEjT9zMUh4IW2mGnv5EK7XreB 6bkZ/6UwYq682+2q3/llql5NAXVowbDe4XwxGmQCOOyuK7r2lnpyFjGC6DBbctCw160A sc7UPgk5BGhaLz4vqjX/1coH3p4ixPyNBwaPgaW8f8Ey/RFLr6JiZuXcfcnVeCD2bWIz iQVQnxExtAhQoqXrpT3yjPvGkZhgd02VbkICOf8mBLc2DoBWe8AtSsfhPWTs7QkG0k06 yQYg== X-Forwarded-Encrypted: i=1; AJvYcCXDr9PYs8bWs18lZbZulOLtf6ceuk+iCyHeHhNbxk5BZrBBffli9VSe6b7rMMrqO4ljOODuDNcDbX663Ci99Xfg3OBqQg1e3pAnARcw X-Gm-Message-State: AOJu0YyEymof6cZhpHOVkAIyYJyFi0Oqr2u64qlXkEMFA4cb/e2X1WOL Vl52r8m6ZfrF8+X8r6MOY5NIR0I270t0dBBN4PGejHTla9Xwg53tSUeV9elZ4FMKY+4umI1T5l8 ubxsRebaoagZfXPxac/4UP644gYc4LVOb1f7Sl/1FwHHQeyWhUo5oYhdvWk6MZw== X-Received: by 2002:a05:6512:2c9a:b0:52b:c15f:2613 with SMTP id 2adb3069b0e04-52ccaa62157mr550005e87.35.1718754083003; Tue, 18 Jun 2024 16:41:23 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFoZ2RS3AlTl1V/f0PC+9/w7xUZieCfcwyUejXDg+egJxREfUqX3QPWZZGosxlVoeE0P/NjeQ== X-Received: by 2002:a05:6512:2c9a:b0:52b:c15f:2613 with SMTP id 2adb3069b0e04-52ccaa62157mr549994e87.35.1718754082636; Tue, 18 Jun 2024 16:41:22 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-422870e9145sm242850535e9.22.2024.06.18.16.41.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 18 Jun 2024 16:41:22 -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, robh@kernel.org, daniel.almeida@collabora.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [PATCH v2 10/10] rust: pci: implement I/O mappable `pci::Bar` Date: Wed, 19 Jun 2024 01:39:56 +0200 Message-ID: <20240618234025.15036-11-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240618234025.15036-1-dakr@redhat.com> References: <20240618234025.15036-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" Implement `pci::Bar`, `pci::Device::iomap_region` and `pci::Device::iomap_region_sized` to allow for I/O mappings of PCI BARs. To ensure that a `pci::Bar`, and hence the I/O memory mapping, can't out-live the PCI device, the `pci::Bar` type is always embedded into a `Devres` container, such that the `pci::Bar` is revoked once the device is unbound and hence the I/O mapped memory is unmapped. A `pci::Bar` can be requested with (`pci::Device::iomap_region_sized`) or without (`pci::Device::iomap_region`) a const generic representing the minimal requested size of the I/O mapped memory region. In case of the latter only runtime checked I/O reads / writes are possible. Co-developed-by: Philipp Stanner Signed-off-by: Philipp Stanner Signed-off-by: Danilo Krummrich --- rust/kernel/pci.rs | 142 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index a8230474e9b8..2b61fb59d4a7 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -5,14 +5,18 @@ //! C header: [`include/linux/pci.h`](srctree/include/linux/pci.h) =20 use crate::{ + alloc::flags::*, bindings, container_of, device, device_id::{IdTable, RawDeviceId}, + devres::Devres, driver, error::{to_result, Result}, + io::Io, 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. @@ -281,9 +285,114 @@ pub trait Driver { /// /// 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. +/// +/// # Invariants +/// +/// `Device` hold a valid reference of `ARef` whose underl= ying `struct device` is a +/// member of a `struct pci_dev`. #[derive(Clone)] pub struct Device(ARef); =20 +/// A PCI BAR to perform I/O-Operations on. +/// +/// # Invariants +/// +/// `Bar` always holds an `Io` inststance that holds a valid pointer to th= e start of the I/O memory +/// mapped PCI bar and its size. +pub struct Bar { + pdev: Device, + io: Io, + num: i32, +} + +impl Bar { + fn new(pdev: Device, num: u32, name: &CStr) -> Result { + let len =3D pdev.resource_len(num)?; + if len =3D=3D 0 { + return Err(ENOMEM); + } + + // Convert to `i32`, since that's what all the C bindings use. + let num =3D i32::try_from(num)?; + + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `num` is checked for validity by a previous call to `Device::re= source_len`. + // `name` is always valid. + let ret =3D unsafe { bindings::pci_request_region(pdev.as_raw(), n= um, name.as_char_ptr()) }; + if ret !=3D 0 { + return Err(EBUSY); + } + + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `num` is checked for validity by a previous call to `Device::re= source_len`. + // `name` is always valid. + let ioptr: usize =3D unsafe { bindings::pci_iomap(pdev.as_raw(), n= um, 0) } as usize; + if ioptr =3D=3D 0 { + // SAFETY: + // `pdev` valid by the invariants of `Device`. + // `num` is checked for validity by a previous call to `Device= ::resource_len`. + unsafe { bindings::pci_release_region(pdev.as_raw(), num) }; + return Err(ENOMEM); + } + + // SAFETY: `ioptr` is guaranteed to be the start of a valid I/O ma= pped memory region of size + // `len`. + let io =3D match unsafe { Io::new(ioptr, len as usize) } { + Ok(io) =3D> io, + Err(err) =3D> { + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `ioptr` is guaranteed to be the start of a valid I/O ma= pped memory region. + // `num` is checked for validity by a previous call to `De= vice::resource_len`. + unsafe { Self::do_release(&pdev, ioptr, num) }; + return Err(err); + } + }; + + Ok(Bar { pdev, io, num }) + } + + // SAFETY: `ioptr` must be a valid pointer to the memory mapped PCI ba= r number `num`. + unsafe fn do_release(pdev: &Device, ioptr: usize, num: i32) { + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `ioptr` is valid by the safety requirements. + // `num` is valid by the safety requirements. + unsafe { + bindings::pci_iounmap(pdev.as_raw(), ioptr as _); + bindings::pci_release_region(pdev.as_raw(), num); + } + } + + fn release(&self) { + // SAFETY: Safe by the invariants of `Device` and `Bar`. + unsafe { Self::do_release(&self.pdev, self.io.base_addr(), self.nu= m) }; + } +} + +impl Bar { + fn index_is_valid(index: u32) -> bool { + // A `struct pci_dev` owns an array of resources with at most `PCI= _NUM_RESOURCES` entries. + index < bindings::PCI_NUM_RESOURCES + } +} + +impl Drop for Bar { + fn drop(&mut self) { + self.release(); + } +} + +impl Deref for Bar { + type Target =3D Io; + + fn deref(&self) -> &Self::Target { + &self.io + } +} + impl Device { /// Create a PCI Device instance from an existing `device::Device`. /// @@ -316,6 +425,39 @@ pub fn set_master(&self) { // SAFETY: Safe by the type invariants. unsafe { bindings::pci_set_master(self.as_raw()) }; } + + /// Returns the size of the given PCI bar resource. + pub fn resource_len(&self, bar: u32) -> Result { + if !Bar::index_is_valid(bar) { + return Err(EINVAL); + } + + // SAFETY: Safe by the type invariant. + Ok(unsafe { bindings::pci_resource_len(self.as_raw(), bar.try_into= ()?) }) + } + + /// Mapps an entire PCI-BAR after performing a region-request on it. I= /O operation bound checks + /// can be performed on compile time for offsets (plus the requested t= ype size) < SIZE. + pub fn iomap_region_sized( + &self, + bar: u32, + name: &CStr, + ) -> Result>> { + let bar =3D Bar::::new(self.clone(), bar, name)?; + let devres =3D Devres::new(self.as_ref(), bar, GFP_KERNEL)?; + + Ok(devres) + } + + /// Mapps an entire PCI-BAR after performing a region-request on it. + pub fn iomap_region(&self, bar: u32, name: &CStr) -> Result> { + self.iomap_region_sized::<0>(bar, name) + } + + /// Returns a new `ARef` of the base `device::Device`. + pub fn as_dev(&self) -> ARef { + self.0.clone() + } } =20 impl AsRef for Device { --=20 2.45.1