From patchwork Tue Jun 18 23:39:47 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13703174 X-Patchwork-Delegate: bhelgaas@google.com 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 AB5AE1849D9 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=N6N71gbcNocqHm3ErmNLnCg1+1OgvLZtg5cHXMIR4w4tqZK/+xEAzws5zrBf00Ecim2ilTS17ba+wWO8b9uxcauJwIJxHXMa1FGikkeydxY96mEIMvkgx04vQ5wNqhidzihv8BltqvNiFBPszlgtoGUIvDIALzsg1d4ORvTW9nI= 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=NENU/ngc; 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="NENU/ngc" 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=1qjkzHfzy3NUrrPbUMcZvTn0a95wVyUbECTV/h26qqI=; b=NENU/ngcNaDrUpC48f/BpY5z8D9XQMMeDYlhY4KsvGsH+x262yaWIRYoEoT802UHvEURd2 KCyTWpddiCD74JLKho3EbV6dx6KGUsFWNKmx8t4HAIvdxaGqhI1QaqJLsnini5DfEbv/ML qcXSji4iZvE/F6ebwGwnmYZIHCKsTk8= 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-621-9EGvT5YrM7-vPTzGCdE6UA-1; Tue, 18 Jun 2024 19:40:47 -0400 X-MC-Unique: 9EGvT5YrM7-vPTzGCdE6UA-1 Received: by mail-wm1-f71.google.com with SMTP id 5b1f17b1804b1-42183fdb37cso44087095e9.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=cIvGqTk72qzYAnU6uhP7ZhEGJBHNVejilNnkhcOUl7iD1Vqq4IKYGS5EgPbJTqrw6Z H1YyDvEGO0Avmrjn95mDhxl2r0f3mIhSIGKKZZRME8HkD/2ZiJt0LxxMzEuSia31GiIQ tzL8IcE/gAxiWw0CGdUMaF4xfCKM30Nx7GmXVKRp4jJTszr8kV6gfAonesKYZdtiOXxI J2v4DtQC5x9xEhip6eG8kYOXU6SjpNQJAsY1mR4JsS+WBWrD0HAB3V6sRz9/CFsCU7IJ /dYPyRA/rdbhZV3NUFeg2A3ypvmfIjJZd7QmGCvJSCIeqIcBj3Bmxb8j10njco3OFMIL BVtQ== X-Forwarded-Encrypted: i=1; AJvYcCUhUiCAaYMHJq6uuduNtv0h1NZvdIXRUoeUgK3VeU2KajsSNsN9QaDCf6uun9TzaXbgoyzr39cYx4656Z4kigLH9y5d6YRxONe7 X-Gm-Message-State: AOJu0YwJS4dDghMy39tawzF0rzszyTSu+B8UEc3k1vifbPF1sg2Dd+Os xnZM+GLLKge95zGAzCWm8Z5Hg983qrdXtcRWOhxB6Gi7oqImWqqK0Wf3tywfSncuKc8Zu+m+tzT zYip3GmTg/uo5bagCnzwBsYjdvF1tQdYLe90rVEwByuZQd5pgVESrOYwkYw== X-Received: by 2002:a05:600c:26d1:b0:422:35:d19d with SMTP id 5b1f17b1804b1-4247529df2emr5259425e9.36.1718754046255; 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-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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) -> error::Result; } /// 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; } 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 = move |slot: *mut Self| { - let m = ::init(module)?; + let m = ::init(name, module)?; // SAFETY: `slot` is valid for write per the contract with `pin_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>()),+]; 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 nobody else can access // the `DRIVERS` static. The array is used only in the C side. let drivers = 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 = <{type_} as kernel::InPlaceModule>::init(&super::super::THIS_MODULE); + let initer = <{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 before 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 { } impl kernel::Module for RustMinimal { - fn init(_module: &'static ThisModule) -> Result { + fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result { pr_info!("Rust minimal sample (init)\n"); pr_info!("Am I built-in? {}\n", !cfg!(MODULE)); 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 { } impl kernel::Module for RustPrint { - fn init(_module: &'static ThisModule) -> Result { + fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result { pr_info!("Rust printing macros sample (init)\n"); pr_emerg!("Emergency message (level 0) without args\n"); From patchwork Tue Jun 18 23:39:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13703173 X-Patchwork-Delegate: bhelgaas@google.com 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 A338316FF28 for ; Tue, 18 Jun 2024 23:40:53 +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=1718754055; cv=none; b=e+Ph6peRS1ZBEY/SZZ/M9bqcpHfstg5Wh3g6h/H1uM1HVob7DJCCpuORooIbM2GfFDlxqUOuFBUq82pO0fgSshz8CUR341ePktRBkm2uf5cCP1SwTf6C4ZRsYL4+t3sK79sG6k6C1MXknDGCuDawOgejpsK/xi34O6TCQwWIOOI= 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=S2Mu+U1e; 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="S2Mu+U1e" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1718754052; 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=S2Mu+U1eqnofsmsd8CmdQfEt+RqOO/yIZD+CGNTiBLp9JVlCGzIIL0qpE8eJe5tiZuB8kw xKI6IrGJYsszFJBwUl1YiHvXIDJ8LPKwGScf/yzCLO6KdL4a+EKIs4vGmLgY8MGGgKsKtl ReC7GYcjMxisL+WVpPdIih8bYVW3MJ8= Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-631-3I7I7SV_NG63OyBryD2GbA-1; Tue, 18 Jun 2024 19:40:51 -0400 X-MC-Unique: 3I7I7SV_NG63OyBryD2GbA-1 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-3625b8355b5so628695f8f.3 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=cLjJ0HJQgcJUC4mjmjIb14OEV98wua5XGrIfwAgfxNqA04bMMuQVIlfTdlVaQrcTmM flhfk9NIQ3cOZBbIW6GnPdEBjYD8Ic+IkG9CJCm97rUP+p4u4IlhpLONCbQei8I0gp9w vs5zzSoVLFy6+dsMmw3sqL/nPaIvUU5oSwUblj199iQ+vKp9/5rMwNT24t7Riirvn9+p gKs19xbljFRet2LA/BFFIRGnH7XJ7+O6dZKnhf/h2WRSXLpXtoEZwhUWmhPIUaz5ZrE/ G/G36gtUAVLDHsc1QfFsTUV5G4FaizJIdIyXTV1Hns+nWj9Ky+WIj4kOA8MP0uY2x7// HWGw== X-Forwarded-Encrypted: i=1; AJvYcCV4xUsyH6tNo6FRU9kxSGvPVuMk3IBLOf7Y82r0ThQC4aGuVtPDcbgBEw9RLjMrWBn7Gd0OYObgsKJ+IiO98eCx7N/23lKepIjZ X-Gm-Message-State: AOJu0Yzqz5Pbwl2XWg2sJALiAskH3pwdvzzJN7Z1GMTrcgFVYub4fM/T AT2Tvi3Nrnzq6P2SYlyTgUse2iSy40BLABdNO3g3RFD9H71NAy+qMh2p9pvI61T/cpDuhtPjr6c 9dXbk3uUSdofzP0BOxg6qg9jRlEQAUI8sMlHJsQ177k3Y96upZK2n5jmW8g== X-Received: by 2002:a5d:4535:0:b0:354:f218:9661 with SMTP id ffacd0b85a97d-363170ed3femr858512f8f.14.1718754050261; 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-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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, Amba, etc.). +//! +//! Each bus / subsystem is expected to implement [`DriverOps`], which allows drivers to register +//! using the [`Registration`] class. + +use crate::error::{Error, Result}; +use crate::{init::PinInit, str::CStr, try_pin_init, types::Opaque, ThisModule}; +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 to 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 typically 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 may 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::register`]. + /// + /// # Safety + /// + /// `reg` must point to valid writable memory, initialised by a previous 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 with 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 unregistered. +#[pin_data(PinnedDrop)] +pub struct Registration { + #[pin] + reg: Opaque, +} + +// SAFETY: `Registration` has no fields or methods accessible via `&Registration`, 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 safe 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 PinInit { + try_pin_init!(Self { + reg <- Opaque::try_ffi_init(|ptr: *mut T::RegType| { + // SAFETY: `try_ffi_init` guarantees that `ptr` is valid for write. + unsafe { ptr.write(T::RegType::default()) }; + + // SAFETY: `try_ffi_init` guarantees that `ptr` is valid for write, and it has + // just been initialised above, so it's also valid for read. + let drv = unsafe { &mut *ptr }; + + T::register(drv, name, module) + }), + }) + } +} + +#[pinned_drop] +impl PinnedDrop for Registration { + fn drop(self: Pin<&mut Self>) { + let drv = 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-functionality 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 PinInit { + 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 more easily expose their own +/// macros. +#[macro_export] +macro_rules! module_driver { + (<$gen_type:ident>, $driver_ops:ty, { type: $type:ty, $($f:tt)* }) => { + type Ops<$gen_type> = $driver_ops; + type ModuleType = $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; From patchwork Tue Jun 18 23:39:49 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13703175 X-Patchwork-Delegate: bhelgaas@google.com 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 D8B151891CF for ; Tue, 18 Jun 2024 23:40:57 +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=1718754059; cv=none; b=iYumt1UWuJwVzkZqI14T7xmm+rZH+AfgOa3tnerdo356KfN/wPyiXxaIxPc4O1uUiDqJTSTm+7L2TwrBQykrpogW9UEyrPXTF9URdeZhW5FCs8u0K36AVgKjTrXnm/vqgyKPQRvx9x88/O/d7Fs3SERxSCQdPkLwNpRWCrMmmxU= 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.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="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-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-290-Be2ejHNLNxeSImeCu6TgDg-1; Tue, 18 Jun 2024 19:40:55 -0400 X-MC-Unique: Be2ejHNLNxeSImeCu6TgDg-1 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-4246ed3f877so11568415e9.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=VkqFjICuq5Y52p3cEPda/0NXx+qYRNXCI0OBeLJpslTs59g3VtzQM2tNFP4oyNWHLN MwYkzbBofQ3/zptapRwWdDePXhJlcLsd0vFcwrMVCpO15kkurM/88fuW8PoRNt3WBLFh PR5S2yEXUhpX1FE6m9SZrUunvLxedomY4CyyhiFKoC6ds9bOZLChhPkYzn6nESTGCMHV idKmTiIRn3Vf/4nfWZOg+Fu+DGePpFe0WSrw7DnVBDyLtz58QsAnru5awDNHMhvnJcqq 2kZYHr5ygBnqrUosgOGtJwhSbtnuBeveqTVSxk5W2f0vmx2T6p2K02Dv8NZDPVGCMKyi GE0Q== X-Forwarded-Encrypted: i=1; AJvYcCVZQwoli6i/dE3tPpSwwe8jAOrEPFaHsexriHToFbsv+H/npyJU4qmWnyl9kzMVh/3NZwQhibumoW1ZBgFg92jz92x8OAAit1rG X-Gm-Message-State: AOJu0YxxytyAkHT2PRWpizsGzyT6FkYaYY9XlpG2DhX5flqygiDTSc9K aSEm0DU7yI7DIUYJYDBrGXu56VRqKx5xC941P8BZyTrqR+uB83duHu7wyNnzSW/O3o2QMqh0aFI iyPcONQOXRuNwcJ5VR9ZNl3DMbUEBy0o2f0db69T/0jmzQJG+um937Y69Vg== X-Received: by 2002:a05:600c:4999:b0:422:615f:6499 with SMTP id 5b1f17b1804b1-42475296a36mr5599295e9.31.1718754054191; 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-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 / subsystem 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 use [`IdTable`] to +/// guarantee (at compile-time) zero-termination of device id tables provided by drivers. +/// +/// Originally, RawDeviceId was implemented as a const trait. However, this unstable feature is +/// broken/gone in 1.73. To work around this, turn IdArray::new() into a macro such that it can use +/// concrete types (which can still have const associated functions) instead 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 = 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 information. + #[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 = 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` pointer, whose size 1, + // so the pointers are necessarily 1-byte aligned. + let ret = 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 necessary until a new +// const_trait_impl implementation lands, since the existing implementation was removed in Rust +// 1.73. +#[macro_export] +#[doc(hidden)] +macro_rules! _new_id_array { + (($($args:tt)*), $id_type:ty) => {{ + /// Creates a new instance of the array. + /// + /// The contents are derived from the given identifiers and context information. + const fn new< U, const N: usize>(ids: [$id_type; N], infos: [Option; 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 = + [<$id_type as $crate::device_id::RawDeviceId>::ZERO; N]; + let mut i = 0usize; + while i < N { + let offset: isize = $crate::device_id::IdArray::<$id_type, U, N>::get_offset(i); + raw_ids[i] = ids[i].to_rawid(offset); + i += 1; + } + + // SAFETY: We are passing valid arguments computed with the correct 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)*) => { 1 + $crate::count_paren_items!($($remaining)*) }; + (($($item:tt)*)) => { 1 }; + () => { 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: 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: 30}, Y),)); +/// ``` +#[macro_export] +macro_rules! first_item { + ($id_type:ty, $(({$($first:tt)*}, $second:expr)),* $(,)?) => { + { + type IdType = $id_type; + [$(IdType{$($first)*},)*] + } + }; + ($id_type:ty, $(($first:expr, $second:expr)),* $(,)?) => { [$($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)),* $(,)?) => { [$($second,)*] }; + ($(($first:expr, $second:expr)),* $(,)?) => { [$($second,)*] }; +} + +/// Defines a new constant [`IdArray`] with a concise syntax. +/// +/// It is meant to be used by buses and subsystems to create a similar macro 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 = (u64, isize); +/// const ZERO: Self::RawType = (0, 0); +/// } +/// +/// impl Id { +/// #[allow(clippy::wrong_self_convention)] +/// const fn to_rawid(&self, offset: isize) -> ::RawType { +/// (self.0 as u64 + 1, offset) +/// } +/// } +/// +/// define_id_array!(A1, Id, (), []); +/// define_id_array!(A2, Id, &'static [u8], [(Id(10), None)]); +/// define_id_array!(A3, Id, &'static [u8], [(Id(10), Some(b"id1")), ]); +/// define_id_array!(A4, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), Some(b"id2"))]); +/// define_id_array!(A5, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), 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(20), 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)* ]) => { + const $table_name: $crate::device_id::IdArray<$id_type, + $data_type, { + $crate::count_paren_items!($($t)*) + }> = $crate::_new_id_array!( + ($crate::first_item!($id_type, $($t)*), + $crate::second_item!($($t)*)), + $id_type); + }; +} + +/// Defines a new constant [`IdTable`] with a concise syntax. +/// +/// It is meant to be used by buses and subsystems to create a similar macro 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 = (u64, isize); +/// const ZERO: Self::RawType = (0, 0); +/// } +/// +/// impl Id { +/// #[allow(clippy::wrong_self_convention)] +/// const fn to_rawid(&self, offset: isize) -> ::RawType { +/// (self.0 as u64 + 1, offset) +/// } +/// } +/// +/// define_id_table!(T1, Id, &'static [u8], [(Id(10), None)]); +/// define_id_table!(T2, Id, &'static [u8], [(Id(10), Some(b"id1")), ]); +/// define_id_table!(T3, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), Some(b"id2"))]); +/// define_id_table!(T4, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), 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(20), 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)* ]) => { + const $table_name: Option<$crate::device_id::IdTable<'static, $id_type, $data_type>> = { + $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 @@ #![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)] From patchwork Tue Jun 18 23:39:50 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13703176 X-Patchwork-Delegate: bhelgaas@google.com 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 EBA1B18A930 for ; Tue, 18 Jun 2024 23:41:01 +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=1718754064; cv=none; b=enHvZmxX2KM0I+uweIs2h7+U6oXsMR92L1OTmZdZgv4/sDJbViXkP5Wnk+uXtJwuo4voh4HNWE7ePnYfo4xU70YkI3cvSDqZAFzjljCIZzPif3bkH0Gt2M657N1c1QCUsY7iSUn42lKHGuLnILlZqLfygf+OoVxQuXPoeBxHISo= 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.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="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-f71.google.com (mail-lf1-f71.google.com [209.85.167.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-151-RXdq7r-CPfGMYFpfWvgEjw-1; Tue, 18 Jun 2024 19:40:59 -0400 X-MC-Unique: RXdq7r-CPfGMYFpfWvgEjw-1 Received: by mail-lf1-f71.google.com with SMTP id 2adb3069b0e04-52bc124ef2eso1585120e87.1 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=BCXL78cz6ZHprcbgxxfA1/8MZ//mGqcHgMIAOAwH3mWqiQRLgFKaz2oOW0aI6PWX+x ddzyMqTSnTAv3D2vA2j0hLmHhlUdSfr1X9JvATm2cmU38F/e4COGJofDmh4CXXO8UOdd AmQj5ALScZvx45hmGjojAOUNnRENzAHu9tfHHC0iH9lGhZLimjSSMhwaP/q7hNrg7cD2 tlwbBQAQjcbwtZQiqc67DOvWwabEZ5sj3y7Nf7K99acT9XND/qJLmb255MaJ7Cm90ZMH UU++WOaEb5IjxHeF17g76xrveCJMOxmQbsOWyiJr/JjX7gH0ePGq2wu9yVWo6+QA8vIS qRww== X-Forwarded-Encrypted: i=1; AJvYcCVKrXJC1c8hdhrV19nW828cAVeEjPfLGrhd6ulqi65lKAjJYSvJ0SHwFQBzR0IhzjAdovTqgp6r2jlB3hlca5T8pf2N+j5Gh9+I X-Gm-Message-State: AOJu0YzzymOWq0LIVQu0cAd1BS+qTUl3ZVG8au7OS6jYajpjE5cKlMRK ujz5rYWU7fBlON7s+9J4qlM26BI7yiBget65NiXKSb8oRGQJ3CaVYefUEjFM5mD7Ks1dZEwFyoP qet/RtXmfldz1WH2Hfp3l+VIcYp4MeVgvzukXu/OJ0VSgUdi9TXhFdAgi2g== X-Received: by 2002:a05:6512:3992:b0:52c:cc38:592c with SMTP id 2adb3069b0e04-52ccc385978mr111660e87.0.1718754058292; 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-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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); +/* 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) indices. 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; 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() +} From patchwork Tue Jun 18 23:39:51 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13703177 X-Patchwork-Delegate: bhelgaas@google.com 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 E1CA218EFD0 for ; Tue, 18 Jun 2024 23:41:09 +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=1718754071; cv=none; b=awCFvY2vS7H9DPuoGYNwYfQNvm0bCh14FTu4e1CEjo2HVpFMwSWRa5yf6P/ntJj7GvAlmAnIOcL8MvyT66CRTPGW4Nk83URNt6b4+0YM8S6s2w0bbDOJTHVVZJCTNmL2pD2bgxTTDdvm2TQj5gQt6C86uc/e8eVPRlDh4OtDvdc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718754071; c=relaxed/simple; bh=UUFxFHITd44dYQQCrdSR7AKzqHAzy/8/2oc53NIFk98=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Y4ox4NNnal2QFLwr8/02ZB6XQNs8eRaMLj0Zs2Fd2HLf8R7kcJXx7AlWRduipS6DO/Rw+F88rk5s9J3l4F4atQQatDHBqeHAIjmUBpuBCqZJTH2DEO+cjh/UNMZS1Zjt4iTB/ZLuiDKYF6/dGff+cZkE67Yqr3PoYBzOSZKPZ/I= 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=ieZXq3e+; 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="ieZXq3e+" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1718754068; 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=ieZXq3e+XQHYg2TBJWJC3L9l3/bmv3Ytx0lhTdMr4Vhfb2VVvIj1NcNKPECay3KUlW0UWk 94S2h/+vbzvarYu4lFtywifaAdLBurC4mHg464GBVFRctftg+ahRolrgkEDxKLeJVHlzuq NajGkcSYauDFpE64y0rOqpAMoMHyeSw= Received: from mail-lj1-f198.google.com (mail-lj1-f198.google.com [209.85.208.198]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-330-we408EiSM6-deLZzkSg4jQ-1; Tue, 18 Jun 2024 19:41:05 -0400 X-MC-Unique: we408EiSM6-deLZzkSg4jQ-1 Received: by mail-lj1-f198.google.com with SMTP id 38308e7fff4ca-2ec3cb4354bso4202761fa.3 for ; Tue, 18 Jun 2024 16:41:05 -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=eeG4M7wcfitHGN/5gywY/7U5/Q3yU9Sv+jDfm5RMWu1Ac8zpDNsfwMXeR/4B9DrOpK wVyTY0oEGq1VBTk0TW8N40rXUPY5eezisKeRoSFueRUr6VtkDKtVrh36nRTPhsAon8Ru W9lSmn+JHjKhWde1TK14K9fJZpR9IcNz2ZuzC+8wuy0kLfmLSCQNn6Klt5wgV7K9H3kC zDZBEFQQIbGwNNJCTprPvQ5UR4lk1y5rw5N/NH7fx27fyCUN66TYOvROjgFws7mLXaBO AfyZ55iT/nY9ziw8pb9H+UEiBVxDYzZW5xkeF/u3A+mmLjIFsNJf6NTbItjrCTqEL1IU w0kg== X-Forwarded-Encrypted: i=1; AJvYcCV7nJSF0JG/Y427wRgj1WW0VLPj7KaGQhIKtGYEMwYYlqAVP6RQGSIokaHz0YGZeadqK7takEKvAOYoGx33zrhiTT9Fq02mQv1q X-Gm-Message-State: AOJu0YwTRhvM+bwR+V3d9ZQNublhaPrvQZNVT2DYZEa+9FNvnldkgsy4 mWoDAH3pouTMP7VfbFMVl06zmXsmKWhzjPSh6DJ9V8VY4l3LiAGfdnMWDrba1VvaOxodabG8PIL kq49321uY3fZ+GNyQKQtpp34hQTtzYrAmD15qXHIYcsurpdbgAd4DN75sUg== X-Received: by 2002:a2e:860e:0:b0:2ec:1a6:7b01 with SMTP id 38308e7fff4ca-2ec3cfe89ddmr6249761fa.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-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 existing 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 = v.try_access()?; +/// Some(guard.a + guard.b) +/// } +/// +/// let v = 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 = rcu::read_lock(); +/// let e = v.try_access_with_guard(&guard)?; +/// Some(e.a + e.b) +/// } +/// +/// let v = 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`. This is because while the +// functionality exposed by `Revocable` can be accessed from any thread/CPU, 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::::__pinned_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 object 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 this object. + pub fn try_access(&self) -> Option> { + let guard = rcu::read_lock(); + if self.is_available.load(Ordering::Relaxed) { + // SAFETY: Since `self.is_available` is true, data is initialised 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 the 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 initialised 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 [`Revocable::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 function 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::Relaxed) + .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 can succeed the + // `compare_exchange` above that takes `is_available` from `true` 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 = unsafe { self.get_unchecked_mut() }; + if *p.is_available.get_mut() { + // SAFETY: We know `self.data` is valid because no other CPU has changed + // `is_available` to `false` yet, and no other CPU can do it anymore 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 = 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 } + } +} From patchwork Tue Jun 18 23:39:52 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13703178 X-Patchwork-Delegate: bhelgaas@google.com 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 55F6018EFE4 for ; Tue, 18 Jun 2024 23:41:11 +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=1718754073; cv=none; b=czh/0j5Cvk7fxRBQyZ1E8nT1F1f4br5tNHxxu6JMfUaTYTFzAAqAfMzI6sOlbRoMJPtnwr+y6XczgaCX1us/q7z3DgnD4QDE4A2gtiepYrpBV+QRi24375s5wjELKcm6vPtAfTzDvFbhtTHNfwO9qpC5NyE2ZFMRKiBvIbAuDkA= 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=U3lm64u5; 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="U3lm64u5" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1718754070; 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=U3lm64u5IrV20Z8s2+Ys8Rf/ozFybzmK75zqoKamviry0KDLo3RCa0+5duy2NAfi+Xfmgp AM0eHhV7Zrucyo/k7my0x3Lg36J2HOrFYAK0+lyTSXlqUqAFlAL/hFOUjzidupVIEBFwz7 plxZyYJsw7FpkGl9G5zWxgANmye+gI4= Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-97-Y-yIWcvXOTym3GwjdN-hbA-1; Tue, 18 Jun 2024 19:41:08 -0400 X-MC-Unique: Y-yIWcvXOTym3GwjdN-hbA-1 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-3625bef4461so173325f8f.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=IkK0koRvXYTOpOWOgST/3Uv6pn4FFlKdeDD7tz6+mXnkkGHiUMx4gYescrTOiomsdM U1IKk2nqMql5SGEab57rIUe3XsucYQ2cRbmq3xohwXh3/USjp1vAUjLbykd5HEXIkJjS edTK2OXh6Y6Jk3IMbh1Hcc63WdgFahKA93hZnHNPrd5Q5TMoER6lMExghWfCEUpxFTPs 3TnGm5PLXfUQS+/WL5mjncgPxx2k36DikPzZP+jtCPrNL94ExTIsQFsdu7jOxl+Kncdj +4oHVMJtXccmurkrTozmwLNvzpWjBmdo5GhXO9C2hqDF+Eenvgu1E2heOJWCdT/H/YFR 2K0w== X-Forwarded-Encrypted: i=1; AJvYcCWBOOXMNQX8PKO4jEmGwis9+B0P/fF7qQ5norUgb/Y8TumzU/RqE4Hh2MiFhWpUWgSly6TvwTNIUeReoz7bUc7v6RNHRtnFjQUD X-Gm-Message-State: AOJu0YyJovGRysNw4r+V2zZd686R3NscqaALYVSFg8Bu6PhkS9f9gtg5 5B9Jhp1nho5D8HfNg2ZPNiX+PM1Ctf7xZiPPeT1E8uw1FC4Ru2tDYkwx4qG/m7gYDqE6Ik92/v1 U5uMVdh/qxnyNptX0V7YWDEZSr9lJZGKh+1iv866grNdpZd+SyM9WT/xkLg== X-Received: by 2002:a05:6000:1a54:b0:363:1c9d:d853 with SMTP id ffacd0b85a97d-3631c9dd9damr728060f8f.32.1718754067235; 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-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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; /// 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 information. + /// + /// 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 constants. + unsafe { self.printk(bindings::KERN_EMERG, args) }; + } + + /// Prints an alert-level message (level 1) prefixed with device information. + /// + /// 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 constants. + unsafe { self.printk(bindings::KERN_ALERT, args) }; + } + + /// Prints a critical-level message (level 2) prefixed with device information. + /// + /// 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 constants. + unsafe { self.printk(bindings::KERN_CRIT, args) }; + } + + /// Prints an error-level message (level 3) prefixed with device information. + /// + /// 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 constants. + unsafe { self.printk(bindings::KERN_ERR, args) }; + } + + /// Prints a warning-level message (level 4) prefixed with device information. + /// + /// 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 constants. + unsafe { self.printk(bindings::KERN_WARNING, args) }; + } + + /// Prints a notice-level message (level 5) prefixed with device information. + /// + /// 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 constants. + unsafe { self.printk(bindings::KERN_NOTICE, args) }; + } + + /// Prints an info-level message (level 6) prefixed with device information. + /// + /// 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 constants. + unsafe { self.printk(bindings::KERN_INFO, args) }; + } + + /// Prints a debug-level message (level 7) prefixed with device information. + /// + /// 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 particular, 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 constants. `self.as_raw` + // is valid because `self` is valid. The "%pA" format string expects a pointer to + // `fmt::Arguments`, which is what we're passing as the last argument. + #[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, + ) + }; + } } // 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 methods 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)*) => { + { + ($dev).$method(core::format_args!($($f)*)); + } + } +} + +/// Prints an emergency-level message (level 0) prefixed with device information. +/// +/// 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 syntax 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)*) => { $crate::dev_printk!(pr_emerg, $($f)*); } +} + +/// Prints an alert-level message (level 1) prefixed with device information. +/// +/// 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 syntax 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)*) => { $crate::dev_printk!(pr_alert, $($f)*); } +} + +/// Prints a critical-level message (level 2) prefixed with device information. +/// +/// 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 syntax 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)*) => { $crate::dev_printk!(pr_crit, $($f)*); } +} + +/// Prints an error-level message (level 3) prefixed with device information. +/// +/// 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 syntax 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)*) => { $crate::dev_printk!(pr_err, $($f)*); } +} + +/// Prints a warning-level message (level 4) prefixed with device information. +/// +/// 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 syntax 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)*) => { $crate::dev_printk!(pr_warn, $($f)*); } +} + +/// Prints a notice-level message (level 5) prefixed with device information. +/// +/// 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 syntax 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)*) => { $crate::dev_printk!(pr_notice, $($f)*); } +} + +/// Prints an info-level message (level 6) prefixed with device information. +/// +/// 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 syntax 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)*) => { $crate::dev_printk!(pr_info, $($f)*); } +} + +/// Prints a debug-level message (level 7) prefixed with device information. +/// +/// This level should be used for debug messages. +/// +/// Equivalent to the kernel's `dev_dbg` macro, except that it doesn't support dynamic debug yet. +/// +/// Mimics the interface of [`std::print!`]. More information about the syntax 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)*) => { $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 reason. #[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}; pub use super::{init, pin_init, try_init, try_pin_init}; From patchwork Tue Jun 18 23:39:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13703179 X-Patchwork-Delegate: bhelgaas@google.com 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 53286185E70 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-f71.google.com (mail-lf1-f71.google.com [209.85.167.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-81-mSrmhyveMoS1ZJoJkuXZVg-1; Tue, 18 Jun 2024 19:41:13 -0400 X-MC-Unique: mSrmhyveMoS1ZJoJkuXZVg-1 Received: by mail-lf1-f71.google.com with SMTP id 2adb3069b0e04-52c989652e6so4072565e87.0 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=LI5FbC+1fYIeabneDDdAz7TiBVWREmbybss4SFZ3on0z5whYBk8s/sinKm91BGZpvY LhgOT7IrWTStYDAadYAmVkxrHTLd2/9LSkZMZuCTO38omXOfi7Hk6lA1mJWmyQLbk0AS b1zJFEBEfwOwxM+LLXVD6NLJJzr8fWVFE41tF+ml1+0hGpMiTQ9yCi98DqqtXjTkuF+p 1TCVm34YVFqFcdthq1JJImDIFDwid3wU07y66SFy3XHtay4Yvgg1BKFN+bShVaDWPmUW tCBYh3oTdR2C0zWvQpHBkjTo+67zYo177kg9fJWLP+kjPschZhWk/kLsfkJkxfJ1hUv6 2UDw== X-Forwarded-Encrypted: i=1; AJvYcCVj4RVzVL5z9DNzncU5Qg/kv502+VLUmTT2vIh6xODEhxse7QabHQRL8E3aBmxy1WsVwGAYNWIztyhno9ypkxZsoegBdIin3ms8 X-Gm-Message-State: AOJu0Yx3/Cs7ZVNDK7BAzEX+qOPqSRBqoA3WMIa+h8bJRwr2snkuHQZR rjJi/HiSVNTFI8rftzaxthijZWueD/J5H2UkFIOybUgpvOfsFcau/6qCcIDjDqzr6/7tjo0l22G /479qIX8dGswzbrnhl6EP1rb6JzwUditgDKA98BuO1vDJQ/FrryQ2FnemfQ== X-Received: by 2002:a05:6512:1283:b0:52c:5254:b625 with SMTP id 2adb3069b0e04-52ccaa53e28mr724797e87.52.1718754071308; 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-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 */ +/* 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) indices. 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 @maxlen bytes. +/// +/// The creator (usually a subsystem such as PCI) is responsible for creating the +/// mapping, performing an additional region request etc. +/// +/// # Invariant +/// +/// `addr` is the start and `maxsize` the length of valid I/O remapped memory 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 = unsafe { bindings::ioremap(paddr as _, SIZE.try_into().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 = 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 = Io; +/// +/// fn deref(&self) -> &Self::Target { +/// &self.0 +/// } +/// } +/// +/// let iomem = 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) => { + /// Read IO data from a given offset known at compile time. + /// + /// Bound checks are performed on compile time, hence if the offset is not known at compile + /// time, the build will fail. + $(#[$attr])* + #[inline] + pub fn $name(&self, offset: usize) -> $type_name { + let addr = 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 = 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) => { + /// Write IO data from a given offset known at compile time. + /// + /// Bound checks are performed on compile time, hence if the offset is not known at compile + /// time, the build will fail. + $(#[$attr])* + #[inline] + pub fn $name(&self, value: $type_name, offset: usize) { + let addr = 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) -> Result { + let addr = 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 = core::mem::size_of::(); + if let Some(end) = offset.checked_add(type_size) { + end <= size && offset % type_size == 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 `Self::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 @@ #[doc(hidden)] pub use bindings; +pub mod io; pub use macros; pub use uapi; From patchwork Tue Jun 18 23:39:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13703180 X-Patchwork-Delegate: bhelgaas@google.com 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 9ED36191465 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-656-CqJva_F9NFa3IsyMfk2Ofw-1; Tue, 18 Jun 2024 19:41:16 -0400 X-MC-Unique: CqJva_F9NFa3IsyMfk2Ofw-1 Received: by mail-wr1-f70.google.com with SMTP id ffacd0b85a97d-35f1fcd3bbcso2904112f8f.0 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=BSWqg7+rgQT8TfujUwnixJb8jJDFpTL517D073obC67NwSk08UsVetYfE2SmKj1+Vo ernwbsyzOfbzc8WnGxFcmuMxDwx4+EnZhjHfoJCiZ0RCeCHGR5DYvvnoQAlKAzYh6I2v Ad8OjLgPSO1COkJvguJzqn3W5JXy8rjudKAj6T3AHcj8lQkEECDkpxFVFahrkJrc55Ea NCD+hKleLeD/P48bEfi7vUsP2aYM0XRl1Q9pFLAmcAWwjB7u1iRJSVOs5IWx6BWtSnIJ V3aIAJASPwYZzIe7ts5SgfRLykB4pdZ7O6/R8bpsZiM2LYvbLvojEg7nNGlLFHIn/kfJ ysxg== X-Forwarded-Encrypted: i=1; AJvYcCXrUZnCemBVgpqMERMtE9s9rwP8LvvUfEKxNurtx9x8eKiRz/IVjpNooReFP9VheeCaLqsxKFtpPmBh99RSduySmifNNl0Mc2w8 X-Gm-Message-State: AOJu0YyswnX9ZD4qcGsnAyVX17ZdPNAF01+mhGyCnNT758se2Ju0UB0P 8hqY9mlYNo4HRiwLabs987QXX7u5s3p7nCw2jnrFOTuOnww9iTqidOYEIVqKmhjSGvmKRnxUDpm GT6mGl/ZN1pch2iqNKkW9AjUbkTOP+zgTUfLdGss/zDU8dNyfjuRrcbFbsQ== X-Received: by 2002:adf:fa44:0:b0:35d:ca63:4e74 with SMTP id ffacd0b85a97d-363199907a4mr576647f8f.70.1718754075073; 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-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 void __iomem *addr) EXPORT_SYMBOL_GPL(rust_helper_writeq_relaxed); #endif +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) indices. 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 resource 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 [`Device`] bound resources to +/// manage their lifetime. +/// +/// [`Device`] bound resources should be freed when either the resource goes out of scope or the +/// [`Device`] is unbound respectively, depending on what happens first. +/// +/// To achieve that [`Devres`] registers a devres callback on creation, which is called once the +/// [`Device`] is unbound, revoking access to the encapsulated resource (see 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 backing 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 = unsafe { bindings::ioremap(paddr as _, SIZE.try_into().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 = 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 = Io; +/// +/// fn deref(&self) -> &Self::Target { +/// &self.0 +/// } +/// } +/// +/// # // SAFETY: *NOT* safe, just for the example to get an `ARef` instance +/// # let dev = unsafe { Device::from_raw(core::ptr::null_mut()) }; +/// +/// let iomem = IoMem::<{ core::mem::size_of::() }>::new(0xBAAAAAAD).unwrap(); +/// let devres = Devres::new(&dev, iomem, GFP_KERNEL).unwrap(); +/// +/// let res = 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 = Arc::pin_init( + pin_init!( DevresInner { + data <- Revocable::new(data), + }), + flags, + )?; + + // Convert `Arc` into a raw pointer and make devres own this reference until + // `Self::devres_callback` is called. + let data = inner.clone().into_raw(); + let ret = unsafe { + bindings::devm_add_action(dev.as_raw(), Some(Self::devres_callback), data as _) + }; + + if ret != 0 { + // SAFETY: We just created another reference to `inner` in order to pass it to + // `bindings::devm_add_action`. If `bindings::devm_add_action` fails, we have to drop + // this reference accordingly. + let _ = 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 = ptr as *mut DevresInner; + // Devres owned this memory; now that we received the callback, drop the `Arc` and hence the + // reference. + // SAFETY: Safe, since we leaked an `Arc` reference to devm_add_action() in + // `DevresInner::new`. + let inner = 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 = DevresInner::new(dev, data, flags)?; + + Ok(Devres(inner)) + } + + /// Same as [Devres::new`], but does not return a `Devres` instance. Instead 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) -> Result { + let _ = DevresInner::new(dev, data, flags)?; + + Ok(()) + } +} + +impl Deref for Devres { + type Target = 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 actual resource is freed. + // `DevresInner` has to stay alive until the devres callback has been called. This is + // necessary since we don't know when `Devres` is dropped and calling + // `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)] From patchwork Tue Jun 18 23:39:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13703181 X-Patchwork-Delegate: bhelgaas@google.com 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 6D43E1990A8 for ; Tue, 18 Jun 2024 23:41:22 +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=1718754084; cv=none; b=Fq9T59tr1sSSi3OCb3womSNsjysDPgE1p0hXAsITx11mGXaTZrhZnez23uH88lNh9iNwGXhJO6MuJG+JWyO6Y3MXPUJvaZck7OxuZsKoTDy1m48Zi+Nk18E5zup271krCb0oa1OaG7Ah5k/fjn7NUmY0HmbWUXxouof4xDYgUGA= 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.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="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-wr1-f71.google.com (mail-wr1-f71.google.com [209.85.221.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-401-jOFn9zqkNVi9A__jf8iZ7Q-1; Tue, 18 Jun 2024 19:41:20 -0400 X-MC-Unique: jOFn9zqkNVi9A__jf8iZ7Q-1 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-3634a634f8aso113505f8f.2 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=ClGfRFH9SwDxUBt1JAN+Wc5orMVZCylEvOj0kJFfgTXWcKsmh1yOPo4JaMMK2bb483 ZPqxjfllqh6jtiEi2dXnZmpFYQOvg9hwPYLSmYZWq23rmdT3vdTPe25q18TrUAuiWh2r wPT07oFpMe5kTmjY8zejDFcdVKgfCayPsNC9N9VeD+dacPvMe7ji8EfBeTEtx3S8klJ7 P1QROzMB3OuAMHuYZOC3z57hYNdO8l7Bmq0ONzg55/FG3sY9/P7kUfimtwV3d/Ju5feh 9J81ne6RLEOorbagaPLh+RvAzPHoTq0lrgT0uQgYUhgnYo3mcUTThMzGeZ4cL+E5FLsp Z8yg== X-Forwarded-Encrypted: i=1; AJvYcCXcgjK5t2MfxW2Rvzahn8aE2vnbzDdDChG4i72x0kFMBJUIOm6uU67x26bpvzgO65rFe5dzIb47yAF+Lvols8mAfW7TvUcx9HU+ X-Gm-Message-State: AOJu0Yxw5Rv4rR5mC5joMCPHzyQy3aoCD8Rn+KIlY9d+ppjdivaVihB0 QSHIGHfUz3ll8HNVdurv9EayTiNzMB3WokQeTNjuvMmLsHZLqpZgbHMgUB1EhSxoA0aWK6NWWFs AzOl/bkIdjak+WTKH221w6a07s7gEpq+Mn0MeeTqbFfi8jIE/S1qD9ci7eA== X-Received: by 2002:adf:e410:0:b0:35f:2211:5a28 with SMTP id ffacd0b85a97d-363192d0918mr787481f8f.54.1718754079050; 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-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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_helper.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, void (*action)(void *), void } EXPORT_SYMBOL_GPL(rust_helper_devm_add_action); +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) indices. 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; #[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 = bindings::pci_driver; + + fn register( + pdrv: &mut Self::RegType, + name: &'static CStr, + module: &'static ThisModule, + ) -> Result { + pdrv.name = name.as_char_ptr(); + pdrv.probe = Some(Self::probe_callback); + pdrv.remove = Some(Self::remove_callback); + pdrv.id_table = 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_char_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 = unsafe { device::Device::from_raw(&mut (*pdev).dev) }; + // SAFETY: Guaranteed by the rules described above. + let mut pdev = unsafe { Device::from_dev(dev) }; + + // SAFETY: `id` is a pointer within the static table, so it's always valid. + let offset = unsafe { (*id).driver_data }; + let info = { + // SAFETY: The offset comes from a previous call to `offset_from` in `IdArray::new`, + // which guarantees that the resulting pointer is within the table. + let ptr = 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) => { + // Let the `struct pci_dev` own a reference of the driver's private data. + // SAFETY: The core kernel only ever calls the probe callback with a valid `pdev`. + unsafe { bindings::pci_set_drvdata(pdev.as_raw(), data.into_foreign() as _) }; + } + Err(err) => 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 = unsafe { + let ptr = 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)*) => { + $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 = !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 = bindings::pci_device_id; + + const ZERO: Self::RawType = 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)*) => { + type IdInfo = $data_type; + const ID_TABLE: $crate::device_id::IdTable<'static, $crate::pci::DeviceId, $data_type> = { + $crate::define_id_array!(ARRAY, $crate::pci::DeviceId, $data_type, $($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 = 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 registered. 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 = ();` + 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 = (); + 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` instance. 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 = unsafe { bindings::pci_enable_device_mem(self.as_raw()) }; + if ret != 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 + } +} From patchwork Tue Jun 18 23:39:56 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13703182 X-Patchwork-Delegate: bhelgaas@google.com 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 CED7F19939D 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=txRp272UX5/fTGHy25QDxiqtp/Rtgaz7Z0vrKHw/p2wmzrlSI9znOAREFclTZrQYkprphoL+jGoGZhnBhfMuDNmazpOadRv3hrfd5OLvXbC8I/QrkCfwSGr0cKf7IRQlX4G2BiBah2/+FplpnpdKpMtxvfd6As8Pm6d/hVXPsMs= 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=DGwZJ4Yb; 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="DGwZJ4Yb" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1718754085; 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=DGwZJ4YbtYH9HOJ4/jek+UxxYAhSLg4AUFA61yu553L2BFT0ay0tkZFh0G2I+4fXj3TNzP AZv4F7ptLOlU45QbdFztreZ81iuuz4uy1naky197xC3tvpR0kYik/NJIZlvNJlKzvj0KY+ MKoU2LfmnD5P0+GNJHkFiJSIqDnfou4= Received: from mail-lj1-f199.google.com (mail-lj1-f199.google.com [209.85.208.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-515-a1ZCXxNgOJa-Fk5kPJldtg-1; Tue, 18 Jun 2024 19:41:24 -0400 X-MC-Unique: a1ZCXxNgOJa-Fk5kPJldtg-1 Received: by mail-lj1-f199.google.com with SMTP id 38308e7fff4ca-2ec35a23e59so9652711fa.3 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=Q2m1k2kLjuiC+HKBSOqeJ1D5ShgS43wdmI4Kt0X+BxnJh7RCDeRduflRg+wjdKqu4f sjQKk2Im52q63BQFpfPN2ZqN2iiRoCL81kNy4DeOoLh8D6OIp+J9uIBPQtnP9E+ZFk26 ntb64bX7+TLwUdNc3mnngzBVXZsIn6TYs0aU+N53f9fI5S2NA5anBDPuBE5a5gTp3SwO TnLYArhVRXHXJlDoy6MerqKh6Ef0fxmCz7iylpkgiveXx9hRlK1i9CpvARNXSXvNavrM KpPPIh/VR1SNqEzzIpBNuBgUlv+5DaJqLp5in0Sx0BXkETpa3xXEXwmdYj/P6nTOs2gJ MF/g== X-Forwarded-Encrypted: i=1; AJvYcCXpWNLG1gqB6PP/FaA2swOnxiEGu3L7Z7bCtjKJYj5KsHUM0J9Ji1UN71WRRgeNhv/e/tZ/Xq2U8Tuq51oIc4Hjo+ivtMwxy6j7 X-Gm-Message-State: AOJu0YzCQkFJwZOmHIHdv9BHBcSkl7jLu6e6/oI/6mVlGe4BZl3kEM3p 86fEUCgPfVTHoXBT/53o9UUZ3IeQtDDkzfo88pAUbd6WQRKCVSlGrRzcQgrk5Szqcm4PAyF/UYA MAmzDHGQQVSLIFRbb3C8vWZg5Ksbp6e127LzsL3arGrAiYwSLV34qT6YdaQ== X-Received: by 2002:a05:6512:2c9a:b0:52b:c15f:2613 with SMTP id 2adb3069b0e04-52ccaa62157mr550013e87.35.1718754083005; 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-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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) 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 /// 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` instance. Cloning a PCI /// device, hence, also increments the base device' reference count. +/// +/// # Invariants +/// +/// `Device` hold a valid reference of `ARef` whose underlying `struct device` is a +/// member of a `struct pci_dev`. #[derive(Clone)] pub struct Device(ARef); +/// A PCI BAR to perform I/O-Operations on. +/// +/// # Invariants +/// +/// `Bar` always holds an `Io` inststance that holds a valid pointer to the 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 = pdev.resource_len(num)?; + if len == 0 { + return Err(ENOMEM); + } + + // Convert to `i32`, since that's what all the C bindings use. + let num = i32::try_from(num)?; + + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `num` is checked for validity by a previous call to `Device::resource_len`. + // `name` is always valid. + let ret = unsafe { bindings::pci_request_region(pdev.as_raw(), num, name.as_char_ptr()) }; + if ret != 0 { + return Err(EBUSY); + } + + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `num` is checked for validity by a previous call to `Device::resource_len`. + // `name` is always valid. + let ioptr: usize = unsafe { bindings::pci_iomap(pdev.as_raw(), num, 0) } as usize; + if ioptr == 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 mapped memory region of size + // `len`. + let io = match unsafe { Io::new(ioptr, len as usize) } { + Ok(io) => io, + Err(err) => { + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `ioptr` is guaranteed to be the start of a valid I/O mapped memory region. + // `num` is checked for validity by a previous call to `Device::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 bar 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.num) }; + } +} + +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 = 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 type size) < SIZE. + pub fn iomap_region_sized( + &self, + bar: u32, + name: &CStr, + ) -> Result>> { + let bar = Bar::::new(self.clone(), bar, name)?; + let devres = 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() + } } impl AsRef for Device {