From patchwork Mon May 20 17:25:38 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13668507 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 66C10137C5A for ; Mon, 20 May 2024 17:27:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226023; cv=none; b=VbfN2yyDkXzGsrhIKeY19dfQoAS0NNf99LWiENy2rYHOQ99SbF3IeV9DXSqQfAdXecdbl0T4m6j+tW4OUufsRujyIa3qva7xtZ2K9lOh8xeWv5do172Fcb6bPowILULsxWfwtbj6ssrNuUP8OQ+mF2rkwEvpJbu4ZLRObaf3NJo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226023; c=relaxed/simple; bh=rAhFipo4bK8HmFg4tJSDidtDJaUeETRwaBwak+PMNa8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=i7ghbskCTVDz5MgsDW1Xt8nK2mATKJfEU2o4gGtTvnDZFiTboRtwWO/fCqO+VRZD5aPTITSHyeOeCn/PAP9vneJQK4BQsKl9We/KJgShrJgJ/bcwlBwSeQ9IqS9gBBxvZHlJ+D5EJ+ijWNv9z32ZGX+J5qCOZ3WiYGDqniDAj08= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=NXtih3sE; 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="NXtih3sE" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226020; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=R91zVu6bvxpu1aXFiFEweUnqMPVUvsNACvUAs6c3Gw8=; b=NXtih3sEu3EVoULI8ELtahmVpQ2Sx/VtfC/RoDIOjWZyF1dOZEKWcND9kzXbJrvTNJSHyu oG8Vdy0AWmEwLvhNgAdafotemtWYkPXNhSNkr3K399jOy3NwIplaNXavUu1c3gRvGbzc1x Tmti/1Th249tL9PVrMOjpdARr+kYmuk= 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-452-IQCZzAtKNDm1STrEQfhqLw-1; Mon, 20 May 2024 13:26:59 -0400 X-MC-Unique: IQCZzAtKNDm1STrEQfhqLw-1 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-34dc66313b3so7172315f8f.2 for ; Mon, 20 May 2024 10:26:58 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226018; x=1716830818; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=R91zVu6bvxpu1aXFiFEweUnqMPVUvsNACvUAs6c3Gw8=; b=u/HUrrw9QK9LlTMP4+XntysnVNWogXQ6157W3RSRN+dYgh3uYJkIESA1y7bzxV4bO/ MWNSfPzYH1UkJdfUvVYoM6mL2WaZ4GGlOeI+0bx40OZkb0KGfNwBLGd+lQvP5skbDHDG 1os5SUk/bBtJzU9i+biUZDlgzWXN8T54KRVryNvKQeGocTO+Z2ShTiq2bHea/LvfBR7a VWtPSQN3JCec33FanbKk/MVThMBQPOPtb/UIfTJM4gwmp/PuiPPkp343u3hkEgUOXR0c ouDSwoZIfkkghYNmNiksGT4wBIIfl+Ur18H+ksAhUTjH/GmCU+n12FFDoxttJXyReFvd ac+g== X-Forwarded-Encrypted: i=1; AJvYcCVn2P9CHqWMM3Es/YTHAHZ02CK+t9pUr5+kI6GnKB6jJbs9DYvRP35MrGZ4XAQ/p93YqCYQ6+PSkPbgKn81z6o/I4/zTg/mUrp8 X-Gm-Message-State: AOJu0YwfvM5MagOypTPtopBnHfSFY2YA1cQGhK+33JL6Mbc493/7Rp5P /hfdrIliwDxKbuQSTt4EeHAtyZxKD/TOiQOXSEBFKccsWdsoGXaKRapB8rMGrrkyRxLu2bgaVxb 1cSb3JctaAaVoCY7wZZvoCxnlnHza7PYfqVZvz0GEkq0C4TlRZ87m0Schsg== X-Received: by 2002:a05:6000:1968:b0:34f:96ba:ca3a with SMTP id ffacd0b85a97d-3504a61c7cemr32867600f8f.13.1716226017551; Mon, 20 May 2024 10:26:57 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHs4uhCF4pA+lofxQie6yGvKq+LktARuDSM8VVafM6mbOTF89vjV0qTL+7VA81QWNIEkG1APA== X-Received: by 2002:a05:6000:1968:b0:34f:96ba:ca3a with SMTP id ffacd0b85a97d-3504a61c7cemr32867560f8f.13.1716226016428; Mon, 20 May 2024 10:26:56 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-354bd2e2a5asm6620842f8f.45.2024.05.20.10.26.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:26:55 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 01/11] rust: add abstraction for struct device Date: Mon, 20 May 2024 19:25:38 +0200 Message-ID: <20240520172554.182094-2-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add an (always) reference counted abstraction for a generic struct device. This abstraction encapsulates existing struct device instances and manages its reference count. Subsystems may use this abstraction as a base to abstract subsystem specific device instances based on a generic struct device. Co-developed-by: Wedson Almeida Filho Signed-off-by: Wedson Almeida Filho Signed-off-by: Danilo Krummrich --- rust/helpers.c | 1 + rust/kernel/device.rs | 76 +++++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 3 files changed, 78 insertions(+) create mode 100644 rust/kernel/device.rs diff --git a/rust/helpers.c b/rust/helpers.c index 4c8b7b92a4f4..f9d2db1d1f33 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs new file mode 100644 index 000000000000..fafec70effb6 --- /dev/null +++ b/rust/kernel/device.rs @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Generic devices that are part of the kernel's driver model. +//! +//! C header: [`include/linux/device.h`](../../../../include/linux/device.h) + +use crate::{ + bindings, + types::{ARef, Opaque}, +}; +use core::ptr; + +/// A ref-counted device. +/// +/// # Invariants +/// +/// The pointer stored in `Self` is non-null and valid for the lifetime of the ARef instance. In +/// particular, the ARef instance owns an increment on underlying object’s reference count. +#[repr(transparent)] +pub struct Device(Opaque); + +impl Device { + /// Creates a new ref-counted instance of an existing device pointer. + /// + /// # Safety + /// + /// Callers must ensure that `ptr` is valid, non-null, and has a non-zero reference count. + pub unsafe fn from_raw(ptr: *mut bindings::device) -> ARef { + // SAFETY: By the safety requirements, ptr is valid. + // Initially increase the reference count by one to compensate for the final decrement once + // this newly created `ARef` instance is dropped. + unsafe { bindings::get_device(ptr) }; + + // CAST: `Self` is a `repr(transparent)` wrapper around `bindings::device`. + let ptr = ptr.cast::(); + + // SAFETY: By the safety requirements, ptr is valid. + unsafe { ARef::from_raw(ptr::NonNull::new_unchecked(ptr)) } + } + + /// Obtain the raw `struct device *`. + pub(crate) fn as_raw(&self) -> *mut bindings::device { + self.0.get() + } + + /// Convert a raw `struct device` pointer to a `&Device`. + /// + /// # Safety + /// + /// Callers must ensure that `ptr` is valid, non-null, and has a non-zero reference count for + /// the entire duration when the returned reference exists. + pub unsafe fn as_ref<'a>(ptr: *mut bindings::device) -> &'a Self { + // SAFETY: Guaranteed by the safety requirements of the function. + unsafe { &*ptr.cast() } + } +} + +// SAFETY: Instances of `Device` are always ref-counted. +unsafe impl crate::types::AlwaysRefCounted for Device { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference guarantees that the refcount is nonzero. + unsafe { bindings::get_device(self.as_raw()) }; + } + + unsafe fn dec_ref(obj: ptr::NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is nonzero. + unsafe { bindings::put_device(obj.cast().as_ptr()) } + } +} + +// SAFETY: `Device` only holds a pointer to a C device, which is safe to be used from any thread. +unsafe impl Send for Device {} + +// SAFETY: `Device` only holds a pointer to a C device, references to which are safe to be used +// from any thread. +unsafe impl Sync for Device {} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 9a943d99c71a..4ba3d4a49e9c 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -28,6 +28,7 @@ pub mod alloc; mod build_assert; +pub mod device; pub mod error; pub mod init; pub mod ioctl; From patchwork Mon May 20 17:25:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13668508 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 7BF2C1384AE for ; Mon, 20 May 2024 17:27:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226027; cv=none; b=ELFLn85wJiWPPjna0lQENKGwWDE5XpmuzOJgwQSauQfVERGCXERuxKXrAPN6tz3ebIOJkZtQtf3YTuknDMDCQScS4AV0pBGPG+SxXwCwrfYC0gnyXsh/erAhFTU8IhdaMM4W4bwbXqyMJMsYMlcax2BWg9lMCPQDFBbDhwOONXY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226027; c=relaxed/simple; bh=HLxn+yy/99tAi8nj3dRf0FfZndbE49uTmIZinfHUwRY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EFlBwrYQXrib7B9PsOe8sAuySQ42ohfdQxmYAvH7QGYedHNfMbx58FH27/3y7TVy2hddmzm5udKTuy1j9KF3zw1kqiPImeG9WyV1DJbOreq+Eep7bubKoE7OPhqzjMeUK0RN5gdp7FpffkFKVdyzAgVkQaDpYvzqi56iHFUd564= 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=TymEYWOR; 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="TymEYWOR" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226024; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=S2szcc1h6YvmKD7Qd7G/Viagtvo0+d8wDp1xeEAOqWQ=; b=TymEYWOREsqxljrpM+UAHtGjTkDdJRlPlEIwQzaMb5rsc28EDYJAUzjjhDzPhl134bGdYn fyXJD4WbWSDWZunFD8bWJGl/zV8X1x3g5xNrE5pAbRwvfpJKyrwSWNnEZb9tVe0ueQeR+n u8T5ZMKLY8YovUatuHekrz1y74AkH04= 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-203-sMn-XnsWMYOPAYZXZkr8ug-1; Mon, 20 May 2024 13:27:02 -0400 X-MC-Unique: sMn-XnsWMYOPAYZXZkr8ug-1 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-351bd229b88so5936819f8f.1 for ; Mon, 20 May 2024 10:27:02 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226022; x=1716830822; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=S2szcc1h6YvmKD7Qd7G/Viagtvo0+d8wDp1xeEAOqWQ=; b=QQSbQXD3jxob3qjb0LRF6vON+S0UjBZR0EPCwYV28aoAbZllQR45vtmjeqdARWeR+n C2en60+Wbc1yYK/HCqBXhrgMkdECYHFhDAlMeZ397Z231D6zPnbnKNM+cGUGlIRKkRO4 KttdeCb4M7AuKRBwmpWC+EMMgyZgS8ATnG6/gDKQf1BCncLKp7d3wj2zsKcg/EWYmwuj 0aiM5TLa2nKEky9UH6Kr8Qw/dURMgUPMvWWkMT/zGbDZo2jpMdkFJAjrHV58s+0x02cP sm2WBR+GLwknTdjpxGV0OgWgPmu0eyao5BIswkWa8vraw1aqUM4YbUWiMuyNtIkzFbUc PG+g== X-Forwarded-Encrypted: i=1; AJvYcCUKvpa1KPTGwra3U7gmDrbffnLSLsaX+y3vKTTTFaFuKNvGxv04dDk7NEhsVuMxMsCO1QVFDMxti1ytq404OhPs0T9yrrneekKU X-Gm-Message-State: AOJu0YzaOwJHjVaC8Wu5vvMipeLD+D3sD42Hv9YGwejlBzwF6hbr8Wpw PWxuD5zk3q5u/dUoxXP6UFajC0Aah2aOjykk3CvOvev1MhckipyZNRQ3acNqyLNseiJe2nRO1Of /EAREVm6y9iPXWKbxEFg4pH47LYWBgubWvLb7fiZcn4UGyAVWGzksYxbq3A== X-Received: by 2002:a5d:6884:0:b0:351:d3e1:a547 with SMTP id ffacd0b85a97d-351d3e1a614mr11481492f8f.6.1716226021099; Mon, 20 May 2024 10:27:01 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEr7pN/+1T/SxXBrpWkGnaFsOuSQnfr+1EG4cOBwzMMTuZBSshqhYfkJ1Gp9nzPBLwrRiw7kg== X-Received: by 2002:a5d:6884:0:b0:351:d3e1:a547 with SMTP id ffacd0b85a97d-351d3e1a614mr11481470f8f.6.1716226020381; Mon, 20 May 2024 10:27:00 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-354c9bcaa98sm2238571f8f.4.2024.05.20.10.26.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:26:59 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 02/11] rust: add driver abstraction Date: Mon, 20 May 2024 19:25:39 +0200 Message-ID: <20240520172554.182094-3-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Wedson Almeida Filho This defines general functionality related to registering drivers with their respective subsystems, and registering modules that implement drivers. Co-developed-by: Asahi Lina Signed-off-by: Asahi Lina Co-developed-by: Andreas Hindborg Signed-off-by: Andreas Hindborg Signed-off-by: Wedson Almeida Filho Signed-off-by: Danilo Krummrich --- rust/kernel/driver.rs | 492 +++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 4 +- rust/macros/module.rs | 2 +- samples/rust/rust_minimal.rs | 2 +- samples/rust/rust_print.rs | 2 +- 5 files changed, 498 insertions(+), 4 deletions(-) create mode 100644 rust/kernel/driver.rs diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs new file mode 100644 index 000000000000..e0cfc36d47ff --- /dev/null +++ b/rust/kernel/driver.rs @@ -0,0 +1,492 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Generic support for drivers of different buses (e.g., PCI, Platform, Amba, etc.). +//! +//! Each bus/subsystem is expected to implement [`DriverOps`], which allows drivers to register +//! using the [`Registration`] class. + +use crate::{ + alloc::{box_ext::BoxExt, flags::*}, + error::code::*, + error::Result, + str::CStr, + sync::Arc, + ThisModule, +}; +use alloc::boxed::Box; +use core::{cell::UnsafeCell, marker::PhantomData, ops::Deref, pin::Pin}; + +/// A subsystem (e.g., PCI, Platform, Amba, etc.) that allows drivers to be written for it. +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`]. + unsafe 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`]. + unsafe fn unregister(reg: *mut Self::RegType); +} + +/// The registration of a driver. +pub struct Registration { + is_registered: bool, + concrete_reg: UnsafeCell, +} + +// SAFETY: `Registration` has no fields or methods accessible via `&Registration`, so it is safe to +// share references to it with multiple threads as nothing can be done. +unsafe impl Sync for Registration {} + +impl Registration { + /// Creates a new instance of the registration object. + pub fn new() -> Self { + Self { + is_registered: false, + concrete_reg: UnsafeCell::new(T::RegType::default()), + } + } + + /// Allocates a pinned registration object and registers it. + /// + /// Returns a pinned heap-allocated representation of the registration. + pub fn new_pinned(name: &'static CStr, module: &'static ThisModule) -> Result>> { + let mut reg = Pin::from(Box::new(Self::new(), GFP_KERNEL)?); + reg.as_mut().register(name, module)?; + Ok(reg) + } + + /// Registers a driver with its subsystem. + /// + /// It must be pinned because the memory block that represents the registration is potentially + /// self-referential. + pub fn register( + self: Pin<&mut Self>, + name: &'static CStr, + module: &'static ThisModule, + ) -> Result { + // SAFETY: We never move out of `this`. + let this = unsafe { self.get_unchecked_mut() }; + if this.is_registered { + // Already registered. + return Err(EINVAL); + } + + // SAFETY: `concrete_reg` was initialised via its default constructor. It is only freed + // after `Self::drop` is called, which first calls `T::unregister`. + unsafe { T::register(this.concrete_reg.get(), name, module) }?; + + this.is_registered = true; + Ok(()) + } +} + +impl Default for Registration { + fn default() -> Self { + Self::new() + } +} + +impl Drop for Registration { + fn drop(&mut self) { + if self.is_registered { + // SAFETY: This path only runs if a previous call to `T::register` completed + // successfully. + unsafe { T::unregister(self.concrete_reg.get()) }; + } + } +} + +/// Conversion from a device id to a raw device id. +/// +/// This is meant to be implemented by buses/subsystems so that they can 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. +/// - [`RawDeviceId::to_rawid`] stores `offset` in the context/data field of the raw device id so +/// that buses can recover the pointer to the data. +pub unsafe trait RawDeviceId { + /// The raw type that holds the device id. + /// + /// Id tables created from [`Self`] are going to hold this type in its zero-terminated array. + type RawType: Copy; + + /// A zeroed-out representation of the raw device id. + /// + /// Id tables created from [`Self`] use [`Self::ZERO`] as the sentinel to indicate the end of + /// the table. + const ZERO: Self::RawType; +} + +/// A zero-terminated device id array, followed by context data. +#[repr(C)] +pub struct IdArray { + ids: [T::RawType; N], + sentinel: T::RawType, + id_infos: [Option; N], +} + +impl IdArray { + const U_NONE: Option = 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 as a parameter the concrete ID type 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::driver::IdArray<$id_type, U, N> + where + $id_type: $crate::driver::RawDeviceId + Copy, + <$id_type as $crate::driver::RawDeviceId>::RawType: Copy + Clone, + { + let mut raw_ids = + [<$id_type as $crate::driver::RawDeviceId>::ZERO; N]; + let mut i = 0usize; + while i < N { + let offset: isize = $crate::driver::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::driver::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 +/// +// TODO: Exported but not usable by kernel modules (requires `const_trait_impl`). +/// ```ignore +/// #![feature(const_trait_impl)] +/// # use kernel::{define_id_array, driver::RawDeviceId}; +/// +/// #[derive(Copy, Clone)] +/// struct Id(u32); +/// +/// // SAFETY: `ZERO` is all zeroes and `to_rawid` stores `offset` as the second element of the raw +/// // device id pair. +/// unsafe impl const RawDeviceId for Id { +/// type RawType = (u64, isize); +/// const ZERO: Self::RawType = (0, 0); +/// fn to_rawid(&self, offset: isize) -> Self::RawType { +/// (self.0 as u64 + 1, offset) +/// } +/// } +/// +/// define_id_array!(A1, Id, (), []); +/// define_id_array!(A2, Id, &'static [u8], [(Id(10), None)]); +/// define_id_array!(A3, Id, &'static [u8], [(Id(10), Some(b"id1")), ]); +/// define_id_array!(A4, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(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::driver::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 +/// +// TODO: Exported but not usable by kernel modules (requires `const_trait_impl`). +/// ```ignore +/// #![feature(const_trait_impl)] +/// # use kernel::{define_id_table, driver::RawDeviceId}; +/// +/// #[derive(Copy, Clone)] +/// struct Id(u32); +/// +/// // SAFETY: `ZERO` is all zeroes and `to_rawid` stores `offset` as the second element of the raw +/// // device id pair. +/// unsafe impl const RawDeviceId for Id { +/// type RawType = (u64, isize); +/// const ZERO: Self::RawType = (0, 0); +/// fn to_rawid(&self, offset: isize) -> Self::RawType { +/// (self.0 as u64 + 1, offset) +/// } +/// } +/// +/// define_id_table!(T1, Id, &'static [u8], [(Id(10), None)]); +/// define_id_table!(T2, Id, &'static [u8], [(Id(10), Some(b"id1")), ]); +/// define_id_table!(T3, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(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::driver::IdTable<'static, $id_type, $data_type>> = { + $crate::define_id_array!(ARRAY, $id_type, $data_type, [ $($t)* ]); + Some(ARRAY.as_table()) + }; + }; +} + +/// Custom code within device removal. +pub trait DeviceRemoval { + /// Cleans resources up when the device is removed. + /// + /// This is called when a device is removed and offers implementers the chance to run some code + /// that cleans state up. + fn device_remove(&self); +} + +impl DeviceRemoval for () { + fn device_remove(&self) {} +} + +impl DeviceRemoval for Arc { + fn device_remove(&self) { + self.deref().device_remove(); + } +} + +impl DeviceRemoval for Box { + fn device_remove(&self) { + self.deref().device_remove(); + } +} + +/// A kernel module that only registers the given driver on init. +/// +/// This is a helper struct to make it easier to define single-functionality modules, in this case, +/// modules that offer a single driver. +pub struct Module { + _driver: Pin>>, +} + +impl crate::Module for Module { + fn init(name: &'static CStr, module: &'static ThisModule) -> Result { + Ok(Self { + _driver: Registration::new_pinned(name, module)?, + }) + } +} + +/// Declares a kernel module that exposes a single driver. +/// +/// It is meant to be used as a helper by other subsystems so they can 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 4ba3d4a49e9c..698121c925f3 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 driver; pub mod error; pub mod init; pub mod ioctl; @@ -69,7 +71,7 @@ pub trait Module: Sized + Sync { /// should do. /// /// Equivalent to the `module_init` macro in the C API. - fn init(module: &'static ThisModule) -> error::Result; + fn init(name: &'static str::CStr, module: &'static ThisModule) -> error::Result; } /// Equivalent to `THIS_MODULE` in the C API. diff --git a/rust/macros/module.rs b/rust/macros/module.rs index 27979e582e4b..3e7a6a8560f5 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -275,7 +275,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { }} fn __init() -> core::ffi::c_int {{ - match <{type_} as kernel::Module>::init(&THIS_MODULE) {{ + match <{type_} as kernel::Module>::init(kernel::c_str!(\"{name}\"), &THIS_MODULE) {{ Ok(m) => {{ unsafe {{ __MOD = Some(m); diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs index 2a9eaab62d1c..3b918ff5eebb 100644 --- a/samples/rust/rust_minimal.rs +++ b/samples/rust/rust_minimal.rs @@ -17,7 +17,7 @@ struct RustMinimal { } 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 Mon May 20 17:25:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13668509 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 BF7A5137C48 for ; Mon, 20 May 2024 17:27:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226030; cv=none; b=nyDQ2svgwhOMBZIYjpxKlzW9ack7cet96FzFdTMJ0nQHgCjxH1vWj53fM+GKjv1FVo8cMt9wc00knQGHONjw0Pm1UMkpyToWP0xP2tpuvQ3OoXeTmgWeW9MZnlnSU/idq6WyWZoDO4NVsqXC15tvLosm2A/RYCBDFORZkWrx6lk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226030; c=relaxed/simple; bh=wZpumSaAgofzk39YfBsp32v4+ebHuETq3cEP9jzCvOU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Vic02Adg4ONVV4yWdt3I2UUrTdn6PgrajCrxGn7miFnjFYaEs8wOF8Kqwo/KMYAQTfo9WdbQnWwyZ557KxO5PUZ+BUphAFnZvX/IZQJGBBfz22Z3KZCn+9C8Y/mdo8yOBfUBcwsyrey7ndyU046zW+a2ZXcaRqNe02tExIHiVEU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=M8kUejGT; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="M8kUejGT" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226027; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=5CImq0b4mEhq1pg3qJ1z+g35+FLQKy2u4BGJkZOUedY=; b=M8kUejGT966ZQvgKzLtiyjigQvuoaYOysHWIe7ZurdnJEX9cVpvPrjmyzMfqCxMmQAbXvk cTsVFlvq5AJHzjI00g1mvJbRA7X/fAWhsn6t6VVzE3chqgXKKOGceXCbpM/wRgYN3S2SEF kMXb/kPu4CqyPjSMGDvOZFQQPkt8T4s= Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-446-0tdEDKQWN1ercoXJkX9N_w-1; Mon, 20 May 2024 13:27:06 -0400 X-MC-Unique: 0tdEDKQWN1ercoXJkX9N_w-1 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-41ffc807e80so45569375e9.2 for ; Mon, 20 May 2024 10:27:06 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226025; x=1716830825; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=5CImq0b4mEhq1pg3qJ1z+g35+FLQKy2u4BGJkZOUedY=; b=gOHXqdLgTgSxao+5FeMfyi8WF9b+KfXWBPEp7IrZN3eIWw7nZZiaid8iQZWDV0T4lb dIIpsErUYTnZs9Swu1Xsw2jzOTgYZ8lnTvQyC91ZpIsQvV746O7Wng4TDJwhlJmfbOH9 s4X6iuuz4WGog6sxIzuVrvUlTMNBrU/mtdEGHkIv7b9EQPcI3PQEz8Qkh7FV73p5Pj4k yGTnwa6p/HTjGLNl9EzYx8pmVu1ywoH/rk7g9DT2xPeTh4aZXo1YbkBULCJxaKr6hJOe lqX6NyigR4cE2pv5GeF8KUnfJmNpwB9jAHvDj6kTW+LBAVoaRa7/lYWpwZZkCCg4P/0w l1og== X-Forwarded-Encrypted: i=1; AJvYcCVpN34D+l+DD7IHWGIZVVCq3giaqFxAGDmFYawlOyZVSjzh6iCAaN+EuAVIxx01c8z9S4yaT2WN0Gn4J92eS6ve9al2TOXTvOqI X-Gm-Message-State: AOJu0YxtMVXqdTDHiJgUYznjIRlPZaMvaRKLXDCkkH73KG1CiDnQ1WUV EsT3XLGXX+c+KrFSx4y/BnRIm/wX8+R1YIwXjoOJ+hdhmYYormBs3XQc9niteM/jcM5rmX2JknJ hS7EZWeERoLyUATnKmN8Wj7LyUkeE1GuQMZyqTULhz9dC665Jw2OeY2r+SQ== X-Received: by 2002:a05:600c:4f93:b0:41b:e416:43d3 with SMTP id 5b1f17b1804b1-41fead6a51dmr216213155e9.35.1716226025167; Mon, 20 May 2024 10:27:05 -0700 (PDT) X-Google-Smtp-Source: AGHT+IF/bF4bRoSUln2A7cqDJukXexNcMjambbxJHYjat/XzwST1JPeAlpPwPz2OHv0SZFXR65NeCA== X-Received: by 2002:a05:600c:4f93:b0:41b:e416:43d3 with SMTP id 5b1f17b1804b1-41fead6a51dmr216212835e9.35.1716226024359; Mon, 20 May 2024 10:27:04 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-42028c7730dsm189604005e9.25.2024.05.20.10.27.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:03 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 03/11] rust: add rcu abstraction Date: Mon, 20 May 2024 19:25:40 +0200 Message-ID: <20240520172554.182094-4-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-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 f9d2db1d1f33..1d3e800140fc 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -158,6 +159,20 @@ void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func, } EXPORT_SYMBOL_GPL(rust_helper_init_work_with_key); +/* 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..1a1c8ea49359 --- /dev/null +++ b/rust/kernel/sync/rcu.rs @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! RCU support. +//! +//! C header: [`include/linux/rcupdate.h`](../../../../include/linux/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 Mon May 20 17:25:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13668510 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 AE20713959A for ; Mon, 20 May 2024 17:27:12 +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=1716226035; cv=none; b=ni4rk9LegbwQRgsbpfNXXLJDzSzmvzfYQa88OuwYwg2T6yE0ZYZznOnBSwJfPWtFIxATYbJuYTHISfJvlZeFUrkQ8qPkPtdYpAxPSmmnhpf71XZ13HW23Kd9G54ZmOb1nlUyCV4CE61PWuo9Mq3q9OpYD96xPGbHOKFmBsYHYA8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226035; c=relaxed/simple; bh=N85iMowovDf/JhZ/dLh0R6pfZHYB+vOre1zGRULPwYU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jAsJKQfhn01KY9xLCSzOpzHnO9XJUqDBvz4j5HzbSbDTkRBmBA9Im4Oa64zQZPghM2sS7vgw/MjboQaP/FkSNoheZzguHICetLKFYfRvEiSwdrHtd1uSjSgBm1OnodGcp/szGwfaEKEzra6d5cSO1v01HIcBT6fLX674KVRzPtU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=dAoVHpZS; arc=none smtp.client-ip=170.10.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="dAoVHpZS" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226031; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Fu/toNLVWWyRq23aezf3ytH43dbJ0WC5O1SCuEo+dyc=; b=dAoVHpZSv1PnPnETiNC7pnXDbLbGlT9ExDnAAxaweoqiXWgts8mJ5T7wPvFDATaz86aJhp ZDXiIdHaAI+lGfpLqOWu7afpsJQYU00rZz8k/v6TlBXn8ySirf170fIf6WfJwZf9GeosZf 8G2xAB+yRTVDgY5lUs9tR2Nl9FrI6zw= Received: from mail-wr1-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-263--a4X-EM2PDidNN4JcEQ_Lg-1; Mon, 20 May 2024 13:27:10 -0400 X-MC-Unique: -a4X-EM2PDidNN4JcEQ_Lg-1 Received: by mail-wr1-f70.google.com with SMTP id ffacd0b85a97d-351d77e63fdso2286462f8f.3 for ; Mon, 20 May 2024 10:27:09 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226029; x=1716830829; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Fu/toNLVWWyRq23aezf3ytH43dbJ0WC5O1SCuEo+dyc=; b=bNEDG5GKpLLM4t2A4Mwk3tohmJ0e+vzos+6ejvyd8ePum5nd2rLdCxjxAqz7b5mrco X9AcjFUrvII85OcFCghWwKGRX6xXclTtjULBfJ1DBQqOh8DQvQR+Bn0J+xlVO9o5hPKd mOMcXvcAjQXOYHYmKrvqZcK9c3doU2LcHoUNOKc/XqOIEaQC7a0SsyQkf//nOK7FRG5v ff5iKlKKo97pg9z6ewAP0SzIzhbTU6Bl7BmRlmnBE6o3m54qsH1dJzu5iwUR03cUhgXy SX+KFMbqclL6h0KcDpIFFhiwXPZDGj8vLMmuWJMO06kRlkqCkYt8GSLyIhv+EJDNkeeF 7dVA== X-Forwarded-Encrypted: i=1; AJvYcCXaapfy1pxArItGEJBNtrzlQ0lheT01zZBubDB9GZZSv0j+anhTU2uCJvI4Jd86X1ZD58sv6uo8QRGnawwENXrDmjf+QUtxMuOn X-Gm-Message-State: AOJu0YxdIm7M9HBQvLkYQ/A78dMMFSRNcyEL1AZGf0viLMqJoTgtBdJr 55zaX+C5dUpDfHe/Q0WCT+18pKqzpIBDxln0bLG3c5Irry5ISxkGSJe8xoT5RCugZc7altt01ef Zjjkpu0zhwQviTlLNzaa/pw/GmZbhogsSsAn6O1vwao5d4f7oLVbX2+AVUA== X-Received: by 2002:adf:ef4c:0:b0:349:bccc:a1e7 with SMTP id ffacd0b85a97d-3504a6310c6mr30303203f8f.19.1716226028928; Mon, 20 May 2024 10:27:08 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFBsX478+NUBs509kzcMbzIJbFh4JBLi9OQ2gqKGhis3wbf55/L1xrKM6Shc/S0VsZJL65/uw== X-Received: by 2002:adf:ef4c:0:b0:349:bccc:a1e7 with SMTP id ffacd0b85a97d-3504a6310c6mr30303156f8f.19.1716226028289; Mon, 20 May 2024 10:27:08 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-354ccd66865sm784772f8f.89.2024.05.20.10.27.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:07 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 04/11] rust: add revocable mutex Date: Mon, 20 May 2024 19:25:41 +0200 Message-ID: <20240520172554.182094-5-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Wedson Almeida Filho This is a mutex where access to its contents can be revoked at runtime. This is different from `Revocable` in a few ways: 1. The caller may sleep while holding a guard. 2. Accessors are all serialised. 3. Accessing is not as cheap (because it involves acquiring the mutex). 4. The size of the object is larger (because it involves a mutex + flag). An example of where this a good fit to be used in device state that holds registrations to other subsystems. Signed-off-by: Wedson Almeida Filho Co-developed-by: Andreas Hindborg Signed-off-by: Andreas Hindborg Co-developed-by: Danilo Krummrich Signed-off-by: Danilo Krummrich --- rust/kernel/sync.rs | 2 + rust/kernel/sync/revocable.rs | 148 ++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 rust/kernel/sync/revocable.rs diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs index 1806767359fe..13257a4bcff6 100644 --- a/rust/kernel/sync.rs +++ b/rust/kernel/sync.rs @@ -12,12 +12,14 @@ pub mod lock; mod locked_by; pub mod rcu; +mod revocable; pub use arc::{Arc, ArcBorrow, UniqueArc}; pub use condvar::{new_condvar, CondVar, CondVarTimeoutResult}; pub use lock::mutex::{new_mutex, Mutex}; pub use lock::spinlock::{new_spinlock, SpinLock}; pub use locked_by::LockedBy; +pub use revocable::{RevocableMutex, RevocableMutexGuard}; /// Represents a lockdep class. It's a wrapper around C's `lock_class_key`. #[repr(transparent)] diff --git a/rust/kernel/sync/revocable.rs b/rust/kernel/sync/revocable.rs new file mode 100644 index 000000000000..4632bb838180 --- /dev/null +++ b/rust/kernel/sync/revocable.rs @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Synchronisation primitives where access to their contents can be revoked at runtime. + +use macros::pin_data; + +use crate::{ + init::PinInit, + pin_init, + str::CStr, + sync::{lock, lock::Lock, LockClassKey}, +}; +use core::{ + mem::MaybeUninit, + ops::{Deref, DerefMut}, + pin::Pin, +}; + +use super::lock::Guard; + +/// The state within the revocable synchronisation primitive. +/// +/// We don't use simply `Option` because we need to drop in-place because the contents are +/// implicitly pinned. +/// +/// # Invariants +/// +/// The `is_available` field determines if `data` is initialised. +struct Inner { + is_available: bool, + data: MaybeUninit, +} + +impl Inner { + fn new(data: T) -> Self { + // INVARIANT: `data` is initialised and `is_available` is `true`, so the state matches. + Self { + is_available: true, + data: MaybeUninit::new(data), + } + } + + fn drop_in_place(&mut self) { + if !self.is_available { + // Already dropped. + return; + } + + // INVARIANT: `data` is being dropped and `is_available` is set to `false`, so the state + // matches. + self.is_available = false; + + // SAFETY: By the type invariants, `data` is valid because `is_available` was true. + unsafe { self.data.assume_init_drop() }; + } +} + +impl Drop for Inner { + fn drop(&mut self) { + self.drop_in_place(); + } +} + +#[pin_data] +pub struct Revocable { + #[pin] + inner: Lock, B>, +} + +/// Safely initialises a [`Revocable`] instance with the given name, generating a new lock class. +// #[macro_export] +// macro_rules! revocable_init { +// ($mutex:expr, $name:literal) => { +// $crate::init_with_lockdep!($mutex, $name) +// }; +// } + +impl Revocable +where + B: lock::Backend, +{ + /// Creates a new revocable instance of the given lock. + pub fn new(data: T, name: &'static CStr, key: &'static LockClassKey) -> impl PinInit { + pin_init!(Self { + inner <- Lock::new(Inner::new(data), name, key) , + }) + } + + /// Revokes access to and drops the wrapped object. + /// + /// Revocation and dropping happen after ongoing accessors complete. + pub fn revoke(&self) { + self.lock().drop_in_place(); + } + + pub fn try_write(&self) -> Option> { + let inner = self.lock(); + + if !inner.is_available { + return None; + } + + Some(RevocableGuard::new(inner)) + } + + fn lock(&self) -> Guard<'_, Inner, B> { + self.inner.lock() + } +} + +pub struct RevocableGuard<'a, T, B> +where + B: lock::Backend, +{ + guard: Guard<'a, Inner, B>, +} + +impl<'a, T, B: lock::Backend> RevocableGuard<'a, T, B> { + fn new(guard: Guard<'a, Inner, B>) -> Self { + Self { guard } + } +} + +impl RevocableGuard<'_, T, B> { + pub fn as_pinned_mut(&mut self) -> Pin<&mut T> { + unsafe { Pin::new_unchecked(&mut *self) } + } +} + +impl Deref for RevocableGuard<'_, T, B> { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.guard.data.as_ptr() } + } +} + +impl DerefMut for RevocableGuard<'_, T, B> { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *self.guard.data.as_mut_ptr() } + } +} + +/// Type alias for a `Revocable` with a `MutexBackend`. +pub type RevocableMutex = Revocable; + +/// Type alias for a `RevocableGuard` with a `MutexBackend`. +pub type RevocableMutexGuard<'a, T> = RevocableGuard<'a, T, super::lock::mutex::MutexBackend>; From patchwork Mon May 20 17:25:42 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13668511 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 60C3C138484 for ; Mon, 20 May 2024 17:27:16 +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=1716226038; cv=none; b=W2rN3sxOf83653xlYRiICw4GCgq80YhW/wAiJnB5SjVKKEZbj778d3gRrh3AB9VHHlsN+uZsFfEvdwdAW/CiODqGiFf4J51JoxdofirtyIwZaeu3QveV9vL+T7TnQ2/1oVzM9wDiJSjwN0JyTK4vFM8idKglJbdZNLLTW3bAlhA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226038; c=relaxed/simple; bh=0F0rfov5AgIy+BJUKXtpd/KEtgXk5R3qsYOeTsHEmZE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=c1TJlQCToRicg0letTeMZTY1M6lSWZJDk/XHOaicYb5SqdpxIHIPlrffQyeBBOpWKomWyQ1gl4jN0/p68jOx7Cc/h4aYgtuOIMMZaeg4pF7jXznDpuG2znqNdUfX4b9T7bH/W+bT+QSPzDpo68kQmM0nQSY/X4crR0Jtx7hEPkU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=acU0rm1k; arc=none smtp.client-ip=170.10.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="acU0rm1k" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226035; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=0Wnrlx8Ze5f4eVrQIFsuBZ5dBRmGRAk/pP24qmNaV1o=; b=acU0rm1k1DzGrYyRCThba68QD36JmcSBRe4zVzPvEwR8KxKpZYJLCiXnKf1o7tN7XKcnpk wHnp00cIsv/e1V0m2iG6LF1DSR3H3mNPEpUNuPXuWOXQmMh77BlDVxDH4oxDqn5ZwbG2DC T0S45+fevODdlqECn05+3HlLaIWIx5M= Received: from mail-wm1-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-269-8Hwyjs1AMcO5Qn1GU5Wa5w-1; Mon, 20 May 2024 13:27:14 -0400 X-MC-Unique: 8Hwyjs1AMcO5Qn1GU5Wa5w-1 Received: by mail-wm1-f71.google.com with SMTP id 5b1f17b1804b1-41ff6049fdbso54830785e9.0 for ; Mon, 20 May 2024 10:27:13 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226033; x=1716830833; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=0Wnrlx8Ze5f4eVrQIFsuBZ5dBRmGRAk/pP24qmNaV1o=; b=bcNADiBkWCPg33TqNcRFL+LDhu5a3xXg6jQSoV+RTSF7Oi8Ft9UtFlimo2/ncY1iu6 yam4pSkzhy8X7qVKJjdtzXsLz98RI+e7N31vDVBKcDrsohqbHOaqrB/GVsuJthkQYvcL LZC/tZ6sMRDI8lT5jehd8ox6ZarT7n5R0TFfuU3DQYZcDGA3+B45QT9GjjhD9K32e3cG jlHOeKIphGXA9q0PMd1wxPgrJ1rnK3yffYC3FfVAwWccKKJvrNnGllWC4VQUKXfGGcqK ztiENO/eYz7tiEa1yfTfC0mrkq4MCmQ6EgWktyhQ51Gtk2YalevAkMTjsir1r3lDEPOn 9QKQ== X-Forwarded-Encrypted: i=1; AJvYcCW049xlm+iQw4+0hbaT+2EL45MXufVCnEdzcllJU3Ybn3cGzO9JZfNBt5/KfqOoGeNW3NCH6RBMXbVBFOY66pFseHjRTMOASA3/ X-Gm-Message-State: AOJu0YxsdgG34+QUzk6qnSRpNgEkXKV8Y37NBpP4y0LYZnn5c9zBJHiG i3eyEBlcZc3AgPc9oIKizAp1HDGDu60xQw4Gy7hfZQ0S2EzhG0sJv+KH5pfUyfgK6m4otxI5cPb BnjDE6TG/gi6ETQOCTyK9KT8cnljYe/8HNhfgHhuIBi+GLedDmNhjpXY86w== X-Received: by 2002:a05:600c:1c84:b0:420:29dd:84df with SMTP id 5b1f17b1804b1-42029dd8775mr125127055e9.6.1716226032664; Mon, 20 May 2024 10:27:12 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFlR0P93NDSEN+c3uilX1UD/l/rtLF9q10KC2o8WHFlFlK9VFo1eKLTW22d/kSe0Iv2GKa+KQ== X-Received: by 2002:a05:600c:1c84:b0:420:29dd:84df with SMTP id 5b1f17b1804b1-42029dd8775mr125126795e9.6.1716226032228; Mon, 20 May 2024 10:27:12 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-41fccce25casm426922425e9.20.2024.05.20.10.27.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:11 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 05/11] rust: add revocable objects Date: Mon, 20 May 2024 19:25:42 +0200 Message-ID: <20240520172554.182094-6-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Wedson Almeida Filho This implements the Revocable and AsyncRevocable types. Revocable allows access to objects to be safely revoked at run time. This is useful, for example, for resources allocated during device probe; when the device is removed, the driver should stop accessing the device resources even if other state is kept in memory due to existing references (i.e., device context data is ref-counted and has a non-zero refcount after removal of the device). AsyncRevocable allows access to objects to be revoked without having to wait for existing users to complete. This will be used to drop futures in tasks when executors are being torn down. Co-developed-by: Andreas Hindborg Signed-off-by: Andreas Hindborg Signed-off-by: Wedson Almeida Filho Signed-off-by: Danilo Krummrich --- rust/kernel/lib.rs | 1 + rust/kernel/revocable.rs | 441 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 442 insertions(+) create mode 100644 rust/kernel/revocable.rs diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 698121c925f3..d7d415429517 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -40,6 +40,7 @@ pub mod net; pub mod prelude; pub mod print; +pub mod revocable; mod static_assert; #[doc(hidden)] pub mod std_vendor; diff --git a/rust/kernel/revocable.rs b/rust/kernel/revocable.rs new file mode 100644 index 000000000000..71408039a117 --- /dev/null +++ b/rust/kernel/revocable.rs @@ -0,0 +1,441 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Revocable objects. +//! +//! The [`Revocable`] type wraps other types and allows access to them to be revoked. The existence +//! of a [`RevocableGuard`] ensures that objects remain valid. + +use crate::{ + bindings, + init::{self}, + prelude::*, + sync::rcu, +}; +use core::{ + cell::UnsafeCell, + marker::PhantomData, + mem::MaybeUninit, + ops::Deref, + ptr::drop_in_place, + sync::atomic::{fence, AtomicBool, AtomicU32, Ordering}, +}; + +/// An object that can become inaccessible at runtime. +/// +/// Once access is revoked and all concurrent users complete (i.e., all 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 = Revocable::new(Example { a: 10, b: 20 }); +/// assert_eq!(add_two(&v), Some(30)); +/// v.revoke(); +/// assert_eq!(add_two(&v), None); +/// ``` +/// +/// Sample example as above, but explicitly using the rcu read side lock. +/// +/// ``` +/// # use kernel::revocable::Revocable; +/// use kernel::sync::rcu; +/// +/// struct Example { +/// a: u32, +/// b: u32, +/// } +/// +/// fn add_two(v: &Revocable) -> Option { +/// let guard = rcu::read_lock(); +/// let e = v.try_access_with_guard(&guard)?; +/// Some(e.a + e.b) +/// } +/// +/// let v = Revocable::new(Example { a: 10, b: 20 }); +/// assert_eq!(add_two(&v), Some(30)); +/// v.revoke(); +/// assert_eq!(add_two(&v), None); +/// ``` +#[pin_data(PinnedDrop)] +pub struct Revocable { + is_available: AtomicBool, + #[pin] + data: MaybeUninit>, +} + +// SAFETY: `Revocable` is `Send` if the wrapped object is also `Send`. 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 } + } +} + +/// An object that can become inaccessible at runtime. +/// +/// Once access is revoked and all concurrent users complete (i.e., all existing instances of +/// [`AsyncRevocableGuard`] are dropped), the wrapped object is also dropped. +/// +/// Unlike [`Revocable`], [`AsyncRevocable`] does not wait for concurrent users of the wrapped +/// object to finish before [`AsyncRevocable::revoke`] completes -- thus the async qualifier. This +/// has the advantage of not requiring RCU locks or waits of any kind. +/// +/// # Examples +/// +/// ``` +/// # use kernel::revocable::AsyncRevocable; +/// +/// struct Example { +/// a: u32, +/// b: u32, +/// } +/// +/// fn add_two(v: &AsyncRevocable) -> Option { +/// let guard = v.try_access()?; +/// Some(guard.a + guard.b) +/// } +/// +/// let v = AsyncRevocable::new(Example { a: 10, b: 20 }); +/// assert_eq!(add_two(&v), Some(30)); +/// v.revoke(); +/// assert_eq!(add_two(&v), None); +/// ``` +/// +/// Example where revocation happens while there is a user: +/// +/// ``` +/// # use kernel::revocable::AsyncRevocable; +/// use core::sync::atomic::{AtomicBool, Ordering}; +/// +/// struct Example { +/// a: u32, +/// b: u32, +/// } +/// +/// static DROPPED: AtomicBool = AtomicBool::new(false); +/// +/// impl Drop for Example { +/// fn drop(&mut self) { +/// DROPPED.store(true, Ordering::Relaxed); +/// } +/// } +/// +/// fn add_two(v: &AsyncRevocable) -> Option { +/// let guard = v.try_access()?; +/// Some(guard.a + guard.b) +/// } +/// +/// let v = AsyncRevocable::new(Example { a: 10, b: 20 }); +/// assert_eq!(add_two(&v), Some(30)); +/// +/// let guard = v.try_access().unwrap(); +/// assert!(!v.is_revoked()); +/// assert!(!DROPPED.load(Ordering::Relaxed)); +/// v.revoke(); +/// assert!(!DROPPED.load(Ordering::Relaxed)); +/// assert!(v.is_revoked()); +/// assert!(v.try_access().is_none()); +/// assert_eq!(guard.a + guard.b, 30); +/// drop(guard); +/// assert!(DROPPED.load(Ordering::Relaxed)); +/// ``` +pub struct AsyncRevocable { + usage_count: AtomicU32, + data: MaybeUninit>, +} + +// SAFETY: `AsyncRevocable` is `Send` if the wrapped object is also `Send`. This is because while +// the functionality exposed by `AsyncRevocable` can be accessed from any thread/CPU, it is +// possible that this isn't supported by the wrapped object. +unsafe impl Send for AsyncRevocable {} + +// SAFETY: `AsyncRevocable` is `Sync` if the wrapped object is both `Send` and `Sync`. We require +// `Send` from the wrapped object as well because of `AsyncRevocable::revoke`, which can trigger +// the `Drop` implementation of the wrapped object from an arbitrary thread. +unsafe impl Sync for AsyncRevocable {} + +const REVOKED: u32 = 0x80000000; +const COUNT_MASK: u32 = !REVOKED; +const SATURATED_COUNT: u32 = REVOKED - 1; + +impl AsyncRevocable { + /// Creates a new asynchronously revocable instance of the given data. + pub fn new(data: T) -> Self { + Self { + usage_count: AtomicU32::new(0), + data: MaybeUninit::new(UnsafeCell::new(data)), + } + } + + /// Tries to access the \[revocable\] wrapped object. + /// + /// Returns `None` if the object has been revoked and is therefore no longer accessible. + /// + /// Returns a guard that gives access to the object otherwise; the object is guaranteed to + /// remain accessible while the guard is alive. + pub fn try_access(&self) -> Option> { + loop { + let count = self.usage_count.load(Ordering::Relaxed); + + // Fail attempt to access if the object is already revoked. + if count & REVOKED != 0 { + return None; + } + + // No need to increment if the count is saturated. + if count == SATURATED_COUNT + || self + .usage_count + .compare_exchange(count, count + 1, Ordering::Relaxed, Ordering::Relaxed) + .is_ok() + { + return Some(AsyncRevocableGuard { revocable: self }); + } + } + } + + /// Revokes access to the protected object. + /// + /// Returns `true` if access has been revoked, or `false` when the object has already been + /// revoked by a previous call to [`AsyncRevocable::revoke`]. + /// + /// This call is non-blocking, that is, no new users of the revocable object will be allowed, + /// but potential current users are able to continue to use it and the thread won't wait for + /// them to finish. In such cases, the object will be dropped when the last user completes. + pub fn revoke(&self) -> bool { + // Set the `REVOKED` bit. + // + // The acquire barrier matches up with the release when decrementing the usage count. + let prev = self.usage_count.fetch_or(REVOKED, Ordering::Acquire); + if prev & REVOKED != 0 { + // Another thread already revoked this object. + return false; + } + + if prev == 0 { + // SAFETY: This thread just revoked the object and the usage count is zero, so the + // object is valid and there will be no future users. + unsafe { drop_in_place(UnsafeCell::raw_get(self.data.as_ptr())) }; + } + + true + } + + /// Returns whether access to the object has been revoked. + pub fn is_revoked(&self) -> bool { + self.usage_count.load(Ordering::Relaxed) & REVOKED != 0 + } +} + +impl Drop for AsyncRevocable { + fn drop(&mut self) { + let count = *self.usage_count.get_mut(); + if count != REVOKED { + // The object hasn't been dropped yet, so we do it now. + + // This matches with the release when decrementing the usage count. + fence(Ordering::Acquire); + + // SAFETY: Since `count` is does not indicate a count of 0 and the REVOKED bit set, the + // object is still valid. + unsafe { drop_in_place(UnsafeCell::raw_get(self.data.as_ptr())) }; + } + } +} + +/// A guard that allows access to a revocable object and keeps it alive. +/// +/// # Invariants +/// +/// The owner owns an increment on the usage count (which may have saturated it), which keeps the +/// revocable object alive. +pub struct AsyncRevocableGuard<'a, T> { + revocable: &'a AsyncRevocable, +} + +impl Deref for AsyncRevocableGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + // SAFETY: The type invariants guarantee that the caller owns an increment. + unsafe { &*self.revocable.data.assume_init_ref().get() } + } +} + +impl Drop for AsyncRevocableGuard<'_, T> { + fn drop(&mut self) { + loop { + let count = self.revocable.usage_count.load(Ordering::Relaxed); + let actual_count = count & COUNT_MASK; + if actual_count == SATURATED_COUNT { + // The count is saturated, so we won't decrement (nor do we drop the object). + return; + } + + if actual_count == 0 { + // Trying to underflow the count. + panic!("actual_count is zero"); + } + + // On success, we use release ordering, which matches with the acquire in one of the + // places where we drop the object, namely: below, in `AsyncRevocable::revoke`, or in + // `AsyncRevocable::drop`. + if self + .revocable + .usage_count + .compare_exchange(count, count - 1, Ordering::Release, Ordering::Relaxed) + .is_ok() + { + if count == 1 | REVOKED { + // `count` is now zero and it is revoked, so free it now. + + // This matches with the release above (which may have happened in other + // threads concurrently). + fence(Ordering::Acquire); + + // SAFETY: Since `count` was 1, the object is still alive. + unsafe { drop_in_place(UnsafeCell::raw_get(self.revocable.data.as_ptr())) }; + } + + return; + } + } + } +} From patchwork Mon May 20 17:25:43 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13668512 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 9373313A24C for ; Mon, 20 May 2024 17:27:20 +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=1716226043; cv=none; b=b4IYUgkn/cMxIH6vU8pQza1nt+aN1BEH1X1pzb+UEzAfPuVXwrgV1ZT+CGCVxVN4t9Q1n6TyQUaeLk3b27xYRBuzybeKTZEOB5sZBHm5HTVkMAQfdz1q/08+lUtC9QiP4FTdhHMqk5lulq3ywCyYwM5vLSwC0mxrrw5r+hJTVGA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226043; c=relaxed/simple; bh=Xo1H50xmNmCwFq/YzacitBz7yAiTOIYKpZRdmQ6Ez64=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=n8CG3KtyMvHetCINvd7CMA6lxZ2tSt0UCNeXDpZViLW5Isd2XcCcU34ZZw2ohk2XbwHhsMm0GB4aJyQ1niZ5Aj4oVUtj4NAQrKLGN6k/nCAUKrKnq+YqWXYdu9YZ1o367Zau4jwWunZbdBTBAzf11k1haa8XE0zDKL9favKZJDg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=dMBSD8ss; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="dMBSD8ss" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226039; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=agQximp23XkAIvccriflMJpmlFVjIztZE/y37x455Bs=; b=dMBSD8sspjv5W/6vGT6Ze+AozAX26Mo2Hz5M1w/0US+aJbxhr5FmoYbAP/c/uiHTrADkbB WAMy8nwt8UzRVM7NI/zUcCL+/0j++Q8nmzrHnvXWGMcFhERWKaMOXVFfGmUbjYb+F9Qbdj hSMda63zTqzl1mF9R83k/hwvkvCsUgk= Received: from mail-wr1-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-150-kmASd2nlPDi5it6dvHvqww-1; Mon, 20 May 2024 13:27:17 -0400 X-MC-Unique: kmASd2nlPDi5it6dvHvqww-1 Received: by mail-wr1-f70.google.com with SMTP id ffacd0b85a97d-34f7618a1f2so6485225f8f.1 for ; Mon, 20 May 2024 10:27:17 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226036; x=1716830836; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=agQximp23XkAIvccriflMJpmlFVjIztZE/y37x455Bs=; b=UFUmyTuUr4TMbpBnCx94VzSXqX1F5AjSQF/Cajhl/ZairhuEWPak/8cTEtIY/zu8GS zIgpp7VCWGuIySQcgyOTKkh5eTfdikAbR4e4Q+izi3dipSCdaEy3dwfllbcVqnCgrmtK 1rzlj7BuAg3LSaqFjy+wggA/g3mmNkVrvKYLr4tS+X8S1vIEnGEYrceeS7oN7DMfFVF6 kyoYaOZp/oK2lCtjLdScp5SJxgy5rBEdStOSDhVYgjbs9gnyLeNkeodMvOILBgYvOQH7 5H3VpjCeGHD7uvkYrviOQJnVWTIEk8i6jODBbV/dwKRQ/0pnI2uuQgjZMdvvU3JSFySL uFxw== X-Forwarded-Encrypted: i=1; AJvYcCXdPcsG9KvjfjnT+mdhUMTQ2kDpJAeuDX3VXc3UMzwmhK4qrKwRdf8drJ91Rev0WSoEG46qL47X4QQauJl43504DXksTXiSgxZX X-Gm-Message-State: AOJu0YzDYKqkOk4L5DgpttLOpsUDytSMWLNzcSt5apUdZ7AxZR9iHLu6 /X/cWubjn4uUNEcVCcS2RhJHvCd9iewKJoUXkT9249n3YdDdB8V74lpuB5rykVJdWZLi0imXR3B MnQ1+Gi9jhIiMdr8JaOB/1ncoQR+lGnP0vrZOFYLgjA1E5+8iJ9pw/tyHPA== X-Received: by 2002:a5d:6350:0:b0:34d:8206:e76b with SMTP id ffacd0b85a97d-354b8df9230mr5873133f8f.9.1716226036482; Mon, 20 May 2024 10:27:16 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHyj+aQzyFz124Ivaxlo9plfP8f/ni9Dw8Yk9DDidlmWu1q+cPLQmubwDGw7A6qtXoC6DrB8Q== X-Received: by 2002:a5d:6350:0:b0:34d:8206:e76b with SMTP id ffacd0b85a97d-354b8df9230mr5873110f8f.9.1716226036132; Mon, 20 May 2024 10:27:16 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3518d817ee2sm25948722f8f.2.2024.05.20.10.27.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:15 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 06/11] rust: add device::Data Date: Mon, 20 May 2024 19:25:43 +0200 Message-ID: <20240520172554.182094-7-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Wedson Almeida Filho Add a generic type `device::Data` to represent driver specific data bound to a device. `device::Data` also stores and allows access to registrations, which are revoked automatically when the corresponding device is unbound, even if the `device::Data`'s reference count is non-zero. Signed-off-by: Wedson Almeida Filho Co-developed-by: Danilo Krummrich Signed-off-by: Danilo Krummrich --- rust/kernel/device.rs | 103 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index fafec70effb6..b1c3f7a0d623 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -4,11 +4,24 @@ //! //! C header: [`include/linux/device.h`](../../../../include/linux/device.h) +use macros::pin_data; + use crate::{ + alloc::flags::*, bindings, + error::Result, + init::InPlaceInit, + init::PinInit, + pin_init, + str::CStr, + sync::{LockClassKey, RevocableMutex, RevocableMutexGuard, UniqueArc}, types::{ARef, Opaque}, }; -use core::ptr; +use core::{ + ops::{Deref, DerefMut}, + pin::Pin, + ptr, +}; /// A ref-counted device. /// @@ -74,3 +87,91 @@ unsafe impl Send for Device {} // SAFETY: `Device` only holds a pointer to a C device, references to which are safe to be used // from any thread. unsafe impl Sync for Device {} + +/// Device data. +/// +/// When a device is unbound (for whatever reason, for example, because the device was unplugged or +/// because the user decided to unbind the driver), the driver is given a chance to clean up its +/// state. +/// +/// The device data is reference-counted because other subsystems may hold pointers to it; some +/// device state must be freed and not used anymore, while others must remain accessible. +/// +/// This struct separates the device data into two categories: +/// 1. Registrations: are destroyed when the device is removed. +/// 2. General data: remain available as long as the reference count is nonzero. +/// +/// This struct implements the `DeviceRemoval` trait such that `registrations` can be revoked when +/// the device is unbound. +#[pin_data] +pub struct Data { + #[pin] + registrations: RevocableMutex, + #[pin] + general: U, +} + +/// Safely creates an new reference-counted instance of [`Data`]. +#[doc(hidden)] +#[macro_export] +macro_rules! new_device_data { + ($reg:expr, $gen:expr, $name:literal) => {{ + static CLASS1: $crate::sync::LockClassKey = $crate::sync::LockClassKey::new(); + let regs = $reg; + let gen = $gen; + let name = $crate::c_str!($name); + $crate::device::Data::try_new(regs, gen, name, &CLASS1) + }}; +} + +impl Data { + /// Creates a new instance of `Data`. + /// + /// It is recommended that the [`new_device_data`] macro be used as it automatically creates + /// the lock classes. + pub fn try_new( + registrations: T, + general: impl PinInit, + name: &'static CStr, + key1: &'static LockClassKey, + ) -> Result>> { + let ret = UniqueArc::pin_init( + pin_init!(Self { + registrations <- RevocableMutex::new( + registrations, + name, + key1, + ), + general <- general, + }), + GFP_KERNEL, + )?; + + Ok(ret) + } + + /// Returns the locked registrations if they're still available. + pub fn registrations(&self) -> Option> { + self.registrations.try_write() + } +} + +impl crate::driver::DeviceRemoval for Data { + fn device_remove(&self) { + self.registrations.revoke(); + } +} + +impl Deref for Data { + type Target = U; + + fn deref(&self) -> &U { + &self.general + } +} + +impl DerefMut for Data { + fn deref_mut(&mut self) -> &mut U { + &mut self.general + } +} From patchwork Mon May 20 17:25:44 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13668513 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 0C5B013A3EF for ; Mon, 20 May 2024 17:27:24 +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=1716226047; cv=none; b=B3JIxBmEiF6f5O53jfnh88ljyTQRXiAmIiEWQarI4nCeK63t8UlhM+5iyFhsnjTDr2GSylCbxKf8k/R/JWe5cHMMXTm0QKGJ+zcP2ZGpgiorFpAsUOche7ySsMLiXLsH//da72RL0ZbzzUn8U+j1UUELUUbMqp/5me7XA3uyK8A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226047; c=relaxed/simple; bh=Z/Z6FQt+IfhzYJGtOfnWM7hOAZBD5lo+z7OWyx94ou4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DHsq+kDBI82RNVlCZgLoLAy2Sba0V09x2CmPouErB3LKEWiFzzfPIelkr06ymZB0meCFJlUpIz2V8P4UgAlepNI3FsRDy6cYkgpRzycb+f0iBv3ilaGCiwcok7tf88czZOWdC7SiBUWdwZyflKFnlwc1sqWqyFtWqWBBTyGz6TE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=gg/pdnz2; arc=none smtp.client-ip=170.10.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="gg/pdnz2" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226044; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=8ykRJ38aWUQhPojOF0arCmW/c5j5xwBTUzFlxtSsGSA=; b=gg/pdnz24dAklWnuQx4VylVTXDgOvgEnOIPFwloF5ulEP7EwF+52q2kwvJ3TlYfdDqEqJT hfpLB5x3B0CV3BRti8Yxhmy2Ya0000cabF/U9h+zZ1OWMGzNLbilM5V8339M+WMYcqMVuK fUfKarTuuYI0z/8m/NLmiMNGkVghiBc= Received: from mail-wr1-f70.google.com (mail-wr1-f70.google.com [209.85.221.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-379-86xex3CeNU6qDIxOVaKVgA-1; Mon, 20 May 2024 13:27:22 -0400 X-MC-Unique: 86xex3CeNU6qDIxOVaKVgA-1 Received: by mail-wr1-f70.google.com with SMTP id ffacd0b85a97d-354c7c04325so643439f8f.0 for ; Mon, 20 May 2024 10:27:22 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226041; x=1716830841; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=8ykRJ38aWUQhPojOF0arCmW/c5j5xwBTUzFlxtSsGSA=; b=rS03vjgYotD7tv0dM61mkdnm0/c8AYQXZLgMFLogsWDOWwJcuiHRqebrOqyu0ks9fR EfbpEx6FNBplrAE0iOgL3s/UIoi8K3MWDp+VWrYjYUcBVKZUThSTnYixA5eYim/vf24R ymhiAUbpKGR6jPRwkV7XVEUPfla0NZ48tMGZ0+oiyGhnTuVRNt7J3FwBAw3Pivfplnnm VKylXbw6QND7ywus7nqcChYhQDGJtLjJQ2IM42c5a+eK52mVO+pi3CQK9oglzMY1b+0x aCm3eWjbyeSKom4Dk3eAYWOQ1BY+Ny4abj97R+2oVKU8/u99kzxiVJkEdk7VivFyqP6W NxWA== X-Forwarded-Encrypted: i=1; AJvYcCVcHurpIIlITLyxnPM73RFFQiqOc6xC/S8xkaqA7M9CmgpE7tGCdUPeHDC3JQ+quizL2Ch5gcPqqEm7lAids8c4CQ+fCunB0jM1 X-Gm-Message-State: AOJu0YzqmcbZhAUDZGjgI4TlnHddZYh1stZXL/Sqp8y2zuAyh/qP2ARX Xs4D3zWB/O5FScABMGDtnKwqj58hx7/ng77fGNGPrO2RUQyHyFJMrXTp4yYSDIbsXMdnc//tJri WX9g3hKH6iYPAVWGhUeVGWTZw80eSzrI0m8VS4yoTB8It5kVSw9JjJNhphQ== X-Received: by 2002:a05:6000:1083:b0:350:2ba9:ca03 with SMTP id ffacd0b85a97d-354b8e6577cmr5817508f8f.23.1716226041243; Mon, 20 May 2024 10:27:21 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEi7mAWZQwNorKfygViuMjJctRMt7yGRf02f78cLaq2jisR8KhggcCcLDZDt+WNalb6qNuquA== X-Received: by 2002:a05:6000:1083:b0:350:2ba9:ca03 with SMTP id ffacd0b85a97d-354b8e6577cmr5817476f8f.23.1716226040747; Mon, 20 May 2024 10:27:20 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3525f7f7d88sm10352988f8f.57.2024.05.20.10.27.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:19 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Wedson Almeida Filho , Danilo Krummrich Subject: [RFC PATCH 07/11] rust: add `dev_*` print macros. Date: Mon, 20 May 2024 19:25:44 +0200 Message-ID: <20240520172554.182094-8-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-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 | 321 +++++++++++++++++++++++++++++++++++++++++ rust/kernel/prelude.rs | 2 + 2 files changed, 323 insertions(+) diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index b1c3f7a0d623..2988aeb4e040 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -18,11 +18,15 @@ types::{ARef, Opaque}, }; use core::{ + fmt, ops::{Deref, DerefMut}, pin::Pin, ptr, }; +#[cfg(CONFIG_PRINTK)] +use crate::c_str; + /// A ref-counted device. /// /// # Invariants @@ -66,6 +70,110 @@ pub unsafe fn as_ref<'a>(ptr: *mut bindings::device) -> &'a Self { // SAFETY: Guaranteed by the safety requirements of the function. unsafe { &*ptr.cast() } } + + /// Prints an emergency-level message (level 0) prefixed with device 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 ref-counted. @@ -175,3 +283,216 @@ fn deref_mut(&mut self) -> &mut U { &mut self.general } } + +#[doc(hidden)] +#[macro_export] +macro_rules! dev_printk { + ($method:ident, $dev:expr, $($f:tt)*) => { + { + // We have an explicity `use` statement here so that callers of this macro are not + // required to explicitly use the `RawDevice` trait to use its functions. + use $crate::device::Device; + ($dev).$method(core::format_args!($($f)*)); + } + } +} + +/// Prints an emergency-level message (level 0) prefixed with device 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 Mon May 20 17:25:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13668514 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 A3C0513A41B for ; Mon, 20 May 2024 17:27:29 +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=1716226051; cv=none; b=DhWD7viFNQAzoN/L/wfRWVAa6JCc64oP9QAd2UQhbdyKzsfayf6R3oZqqO+BE2nOvpcliGeHq2RIwZHpsOcXHiEgeNGGiPnuS4pfaaNjcq13XH4yb15jjRoPtuUPdbof1c992NtVYI1Nh/5PiO3bJe+sIHDqXdAOpJqkHgbW38c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226051; c=relaxed/simple; bh=Pf+OOnuHz5mcZsGqaVhIEIMHurAZcjaBobj/NJHA3CI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MlfV8YKP9TT4wb7nqTJLfgrzw57LTsXxUs73Vw4k03KHqzqjyZHBWrtKEFq862/0IkcnMNRSK9p1xDAH/34tKZSuPxZEGhypruHqpk/P0ii7ZBNVvXb3pavEtqRJo/RqM5avHvY4HSYOAbLlpE8xg3vx11SsJqmGw0JnlvTwqy8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=SvzrCzGo; arc=none smtp.client-ip=170.10.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="SvzrCzGo" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226048; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=1CsnJXGBZcLyYjfrlAMv2FyBTh8bc+Mt3a3EbW5Rnks=; b=SvzrCzGo9wb2DXXlFn5leFcfH0Ok8ax85GHnujv3sSaCXzxc3fWYxmyILkAFDSveCfq+iU A1TwcOIIArL8YwJdMh1bPX9pIykLt/VPoZjbgRY8Vvgjt47mvAtXLQmN050AbjHfibPAsx ZR/IFYijSgZl+lY4rhFyrusppqWZlKs= Received: from mail-lj1-f197.google.com (mail-lj1-f197.google.com [209.85.208.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-119-pyBgG96wPI-qIzMbH9ytMg-1; Mon, 20 May 2024 13:27:26 -0400 X-MC-Unique: pyBgG96wPI-qIzMbH9ytMg-1 Received: by mail-lj1-f197.google.com with SMTP id 38308e7fff4ca-2e2035036f5so110750061fa.0 for ; Mon, 20 May 2024 10:27:26 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226045; x=1716830845; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=1CsnJXGBZcLyYjfrlAMv2FyBTh8bc+Mt3a3EbW5Rnks=; b=uvIY9lzQ0BwqPvZRWcT9oreKbJeznfcxi3ATG9u8uMi4LNUPMFE/H6EwKYdaHSotte GblGFR5L23GkrdvDQzF06gm6Y5viWkq+zaFYzvr1ZB4hXZo1SwVHipiVxUJ5+ySb0h+/ kcrZmgA2Q9qnXC4HlNiCbQJZs1YW0K237iV81tlDkkMKdh0hkAsnzd6BtkoLXuimWwKg lbC01gINQUbjLrxT5847bHiKAyBmNgMZJxSBc7dbVfdm0b+IjNHn1aCxHm/pQ+p/Bdjj iYLDyfsbp854oZvF3JdWy5EOyHZYVhpa7h7qUx5nP077bo2SeF3vExtP7Hn0Yx3BH5O/ KNMg== X-Forwarded-Encrypted: i=1; AJvYcCXq5BEZHdalzrB7pX2tsmPVFp1D7xl8XeVODjSTFnlHwi/+vsDES7vRpIzOiSJuV7jtruEJAHXkn07A1r5WElFDBurLXYYc0jfS X-Gm-Message-State: AOJu0Yy1i2/jLqpmSWyPWuydjpdxh7BetKz+x95z2knBeAOU5fzwm3oH gCz08geKqyIq4FSG0L67bxRLRdlM8sc3KKDxgUuj4ZuPj5SVv09MiDCnLKlSCuQE1q/teqEKB3A vbXgg2f6kr3CVleUHxwvzOdfCXea8nvGzZGR2afm5f5oLjUdxfXzOOtYB3w== X-Received: by 2002:a2e:be9f:0:b0:2e3:ba0e:de12 with SMTP id 38308e7fff4ca-2e51ff5cf48mr319538851fa.22.1716226045285; Mon, 20 May 2024 10:27:25 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHbLE5+nNhphlxlGh18a3Zy/HbW451IH7B3zyMvPHBImYdCotrNMydqRvyk8NUf6/7klPUZ8Q== X-Received: by 2002:a2e:be9f:0:b0:2e3:ba0e:de12 with SMTP id 38308e7fff4ca-2e51ff5cf48mr319538611fa.22.1716226044936; Mon, 20 May 2024 10:27:24 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-42009eda143sm363816305e9.14.2024.05.20.10.27.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:24 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 08/11] rust: add devres abstraction Date: Mon, 20 May 2024 19:25:45 +0200 Message-ID: <20240520172554.182094-9-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-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(). Co-developed-by: Philipp Stanner Signed-off-by: Philipp Stanner Signed-off-by: Danilo Krummrich --- rust/helpers.c | 5 ++ rust/kernel/devres.rs | 151 ++++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 3 files changed, 157 insertions(+) create mode 100644 rust/kernel/devres.rs diff --git a/rust/helpers.c b/rust/helpers.c index 1d3e800140fc..34061eca05a0 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -173,6 +173,11 @@ void rust_helper_rcu_read_unlock(void) EXPORT_SYMBOL_GPL(rust_helper_rcu_read_unlock); /* end rcu */ +int rust_helper_devm_add_action(struct device *dev, void (*action)(void *), void *data) +{ + return devm_add_action(dev, action, data); +} + /* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices. diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs new file mode 100644 index 000000000000..bf7bd304cd9b --- /dev/null +++ b/rust/kernel/devres.rs @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Devres abstraction +//! +//! [`Devres`] represents an abstraction for the kernel devres (device resource management) +//! implementation. + +use crate::{ + alloc::Flags, + bindings, + device::Device, + error::{Error, Result}, + prelude::*, + revocable::Revocable, + types::ARef, +}; + +use core::ffi::c_void; +use core::ops::Deref; + +#[pin_data] +struct DevresInner { + dev: ARef, + #[pin] + data: Revocable, +} + +/// This abstraction is meant to be used by subsystems to containerize [`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::devres::Devres; +/// +/// // See also [`pci::Bar`] for a real example. +/// struct IoRemap(IoMem); +/// +/// impl IoRemap { +/// fn new(usize paddr, usize len) -> Result{ +/// // assert success +/// let addr = unsafe { bindings::ioremap(paddr as _); }; +/// let iomem = IoMem::new(addr, len)?; +/// +/// Ok(IoRemap(iomem)) +/// } +/// } +/// +/// impl Drop for IoRemap { +/// fn drop(&mut self) { +/// unsafe { bindings::iounmap(self.0.ioptr as _); }; +/// } +/// } +/// +/// impl Deref for IoRemap { +/// type Target = IoMem; +/// +/// fn deref(&self) -> &Self::Target { +/// &self.0 +/// } +/// } +/// +/// let devres = Devres::new(dev, IoRemap::new(0xBAAAAAAD, 0x4)?, GFP_KERNEL)?; +/// +/// let res = devres.try_access().ok_or(ENXIO)?; +/// res.writel(0xBAD); +/// ``` +/// +pub struct Devres { + inner: Pin>>, + callback: unsafe extern "C" fn(*mut c_void), +} + +impl DevresInner { + fn as_ptr(&self) -> *const DevresInner { + self as *const DevresInner + } + + fn as_cptr(&self) -> *mut c_void { + self.as_ptr() as *mut c_void + } +} + +unsafe extern "C" fn devres_callback(inner: *mut c_void) { + let inner = inner as *const DevresInner; + let inner = unsafe { &*inner }; + + inner.data.revoke(); +} + +impl Devres { + /// Creates a new [`Devres`] instance of the give data. + pub fn new(dev: ARef, data: T, flags: Flags) -> Result { + let callback = devres_callback::; + + let inner = Box::pin_init( + pin_init!( DevresInner { + dev: dev, + data <- Revocable::new(data), + }), + flags, + )?; + + let ret = unsafe { + bindings::devm_add_action(inner.dev.as_raw(), Some(callback), inner.as_cptr()) + }; + + if ret != 0 { + return Err(Error::from_errno(ret)); + } + + // We have to store the exact callback function pointer used with + // `bindings::devm_add_action` for `bindings::devm_remove_action`. There compiler might put + // multiple definitions of `devres_callback` for the same `T` in both the kernel itself + // and modules. Hence, we might see different pointer values depending on whether we look + // at `devres_callback`'s address from `Devres::new` or `Devres::drop`. + Ok(Devres { inner, callback }) + } +} + +impl Deref for Devres { + type Target = Revocable; + + fn deref(&self) -> &Self::Target { + &self.inner.data + } +} + +impl Drop for Devres { + fn drop(&mut self) { + unsafe { + bindings::devm_remove_action( + self.inner.dev.as_raw(), + Some(self.callback), + self.inner.as_cptr(), + ) + } + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index d7d415429517..11645060b444 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -30,6 +30,7 @@ pub mod alloc; mod build_assert; pub mod device; +pub mod devres; pub mod driver; pub mod error; pub mod init; From patchwork Mon May 20 17:25:46 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 13668515 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 31BB613A89C for ; Mon, 20 May 2024 17:27:32 +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=1716226055; cv=none; b=meo39T7ZX9rhcXrh7H102h1G5JUiIt+X0ME7ZGsLVbrKCt4yIcC1QEIF9Fb+5AQjHKOkHSA5x/cgXh7IO3wOnDM2WzTSzCvVTexPQl46X3vG8YkVBSYYqNIxVUl/4sFEOdb71bE/4ZzE+GhL5irqhpnpIU7W4YVDlUFjLSQ6iMU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226055; c=relaxed/simple; bh=5ysJ7veRsD9TP3SKhDxVg22CoKwjiIVIqxUU9mdur14=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VHCXOwVPzSqG7eBN/Tg5/B5J0YudI19u3DXgEJkyMuV5iv4/qMB2l4MtqncPlSUOplctmWMzpPKW+TcMuIqVYnynnCAzeUFa1LSZ4y7FRH8kWbVJ+jvq6VPMVfKQNEfAFG4VGZFOdUmdRr8wqP1VLa2p/kG/9dpsjU28Sr3VCa4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=eEexPLJW; arc=none smtp.client-ip=170.10.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="eEexPLJW" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226052; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=6S1JMvs3wWwoYY5yLjwWSAqPBxm7FbzVGctvxFDmowc=; b=eEexPLJWNNzKfZkiLJ3SALMCPI9Gg4TZamzSfFBSYsuK4bP6qeGGSFS0yKy/roiEvIfX1C GwVcnMa8UDZUm6QRShpLgVTCLJaNXMmrp2Na/PpQgzaTMP4a9DpQ9ChG1oDkGfbDA7eZ4H Hmqeu8ud3P/w4fmzG7vtCKHWbzOmUCA= Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-596-GWFxlPLePXSDETWkYKBc0w-1; Mon, 20 May 2024 13:27:30 -0400 X-MC-Unique: GWFxlPLePXSDETWkYKBc0w-1 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-41ffc807e80so45571575e9.2 for ; Mon, 20 May 2024 10:27:30 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226049; x=1716830849; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=6S1JMvs3wWwoYY5yLjwWSAqPBxm7FbzVGctvxFDmowc=; b=W6G8qU+sCiauU+/IYtnB5irha/horFubdK1sTaJFngNHW+YEyfln933jmnKs7qGznk CZq3c+pSyuyrT1twYwNgJLHzjU72QRC+q5XcKCAPS56rPtQUAbIDhCW27K6anrWmiDLC DpM42lQUp0eV3yOnHUmdyUuniT1NzaibOZDgoLxdUDEI1aKMY7z4rGY1XfvZW9/an/Zb lE3n4h489s/HJQeon+1v5xRcdgkgWrAi6DWn75Zm7x2/J7Gs/aKcX/f9iPy7l4YMZdk/ mXlzEYtxH3vtkF9ss5f4lezydEcjGi6cb7ZmiSSbj1hXeKGyQW2d1UtY+hcITkXYL3ZN 2KXw== X-Forwarded-Encrypted: i=1; AJvYcCWSFTl8LSTJo79e4yGXfqB2CT4DxF3ZUs1zQCA1ZTd9G///SNX/XHz1zQif/Gx3Ki2XxtdYwNgoPmWMmcCA43anVeh1ZlywtNFJ X-Gm-Message-State: AOJu0YwydQKaDEbXTLQm9ISIoxsopmsjgkRAYrfjr1FblGHAUVwMMgEC lskCTKS1ixPynHxpsWqKndeODr7bCOv1xP6f6ZrkH6ZXMeiomb+sneY0k62KcwGiGnHTjxpDNpo kc98mXoNkWmobb8EibY6JBIZFBM7K51hn3JLBs8fQj+PyMhWGBVfjWM+PSA== X-Received: by 2002:a05:600c:1547:b0:420:1078:a74c with SMTP id 5b1f17b1804b1-4201078a89cmr192092395e9.20.1716226049305; Mon, 20 May 2024 10:27:29 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEowXlkhLoQlD536HTIQse+gl+PqtvMMQTha8X+ha5edJrAVhb5zSf/srjBFRZN7CNK5aE/Hg== X-Received: by 2002:a05:600c:1547:b0:420:1078:a74c with SMTP id 5b1f17b1804b1-4201078a89cmr192092225e9.20.1716226048892; Mon, 20 May 2024 10:27:28 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-41fccce24c0sm430961865e9.17.2024.05.20.10.27.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:28 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 09/11] rust: add basic PCI driver abstractions Date: Mon, 20 May 2024 19:25:46 +0200 Message-ID: <20240520172554.182094-10-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: FUJITA Tomonori This commit implements the abstractions necessary to bind a most basic PCI driver to a PCI device. It also serves as a ground layer for further PCI functionality. Specifically, a basic PCI driver has to provide register() and unregister() methods, a PCI device structure for Rust, and probe() and remove() callbacks for the C side. A PCI driver shall be able to register itself for the desired devices, recognized by their device ID. Another basic necessity is the ability to store driver data, i.e., through pci_set_drvdata(). In congruency with the C implementation of pci_dev, a Rust PCI device holds a basic device (device::Device) which is always reference counted to ensure it cannot disappear as long as there are still users. Holding a basic device allows for both using interfaces that require a device, as well as such that demand a pci_dev, which can be obtained through as_raw(), using the established container_of() macro. Implement a basic driver model with probe() and remove() callbacks, implementing the corresponding traits from the 'driver' crate. Implement PCI device IDs. Implement pci::Device with basic methods, holding an always reference counted device::Device. Signed-off-by: FUJITA Tomonori Co-developed-by: Philipp Stanner Signed-off-by: Philipp Stanner Co-developed-by: Danilo Krummrich Signed-off-by: Danilo Krummrich --- rust/bindings/bindings_helper.h | 1 + rust/helpers.c | 18 ++ rust/kernel/lib.rs | 2 + rust/kernel/pci.rs | 328 ++++++++++++++++++++++++++++++++ 4 files changed, 349 insertions(+) create mode 100644 rust/kernel/pci.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index ddb5644d4fd9..32221de16e57 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers.c b/rust/helpers.c index 34061eca05a0..c3d80301185c 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -33,6 +33,7 @@ #include #include #include +#include __noreturn void rust_helper_BUG(void) { @@ -178,6 +179,23 @@ int rust_helper_devm_add_action(struct device *dev, void (*action)(void *), void return devm_add_action(dev, action, data); } +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 11645060b444..606391cbff83 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -55,6 +55,8 @@ #[doc(hidden)] pub use bindings; pub use macros; +#[cfg(all(CONFIG_PCI, CONFIG_PCI_MSI))] +pub mod pci; pub use uapi; #[doc(hidden)] diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs new file mode 100644 index 000000000000..323aea565d84 --- /dev/null +++ b/rust/kernel/pci.rs @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Wrappers for the PCI subsystem +//! +//! C header: [`include/linux/pci.h`](../../../../include/linux/pci.h) + +use crate::{ + bindings, container_of, device, driver, + error::{to_result, Result}, + str::CStr, + types::{ARef, ForeignOwnable}, + ThisModule, +}; +use kernel::prelude::*; // for pinned_drop + +/// An adapter for the registration of PCI drivers. +/// +/// # Example +/// +///``` +/// use kernel::pci; +/// +/// impl pci::Driver for MyDriver { +/// type Data = Arc; +/// +/// define_pci_id_table! { +/// (), +/// [ (pci::DeviceId::new(bindings::PCI_VENDOR_ID_MY_VENDOR, +/// bindings::PCI_ANY_ID as u32), +/// None) +/// ] +/// } +/// +/// fn probe( +/// pdev: &mut pci::Device, +/// id_info: Option<&Self::IdInfo> +/// ) -> Result> { +/// ... +/// } +/// +/// fn remove(data: &Self::Data) { +/// ... +/// } +/// } +/// +/// struct MyModule { +/// _registration: Pin>>>, +/// } +/// +/// impl kernel::Module for MyModule { +/// fn init(_name: &'static CStr, module: &'static ThisModule) -> Result { +/// let registration = driver::Registration::new_pinned(c_str!("MyDriver"), module)?; +/// +/// Ok(Self { +/// _registration: registration, +/// }) +/// } +/// } +///``` +pub struct Adapter(T); + +impl driver::DriverOps for Adapter { + type RegType = bindings::pci_driver; + + // SAFETY: The caller must ensure that `reg` is valid and unequal NULL. + unsafe fn register( + reg: *mut bindings::pci_driver, + name: &'static CStr, + module: &'static ThisModule, + ) -> Result { + // SAFETY: Guaranteed by the safety requirements of this function. + let pdrv: &mut bindings::pci_driver = unsafe { &mut *reg }; + + 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: Guaranteed by the safety requirements of this function. + to_result(unsafe { bindings::__pci_register_driver(reg, module.0, name.as_char_ptr()) }) + } + + // SAFETY: The caller must ensure that `reg` is valid and unequal NULL. + unsafe fn unregister(reg: *mut bindings::pci_driver) { + // SAFETY: Guaranteed by the safety requirements of this function. + unsafe { bindings::pci_unregister_driver(reg) } + } +} + +impl Adapter { + extern "C" fn probe_callback( + pdev: *mut bindings::pci_dev, + id: *const bindings::pci_device_id, + ) -> core::ffi::c_int { + // SAFETY: Safe because the core kernel only ever calls the probe callback with a valid + // `pdev`. + let dev = 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) => { + // SAFETY: + // A valid `pdev` is always passed to this function. `data` is always valid since + // it's created in Rust. + unsafe { bindings::pci_set_drvdata(pdev.as_raw(), data.into_foreign() as _) }; + } + Err(err) => return Error::to_errno(err), + } + + 0 + } + + extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) { + // SAFETY: This function is called by the C side and always with a valid `pdev`. + let ptr = unsafe { bindings::pci_get_drvdata(pdev) }; + // SAFETY: Guaranteed by the preceding safety requirement. + let data = unsafe { T::Data::from_foreign(ptr) }; + T::remove(&data); + ::device_remove(&data); + } +} + +/// Abstraction for bindings::pci_device_id. +#[derive(Clone, Copy)] +pub struct DeviceId { + /// Vendor ID + pub vendor: u32, + /// Device ID + pub device: u32, + /// Subsystem vendor ID + pub subvendor: u32, + /// Subsystem device ID + pub subdevice: u32, + /// Device class and subclass + pub class: u32, + /// Limit which sub-fields of the class + pub class_mask: u32, +} + +impl DeviceId { + const PCI_ANY_ID: u32 = !0; + + /// PCI_DEVICE macro. + pub const fn new(vendor: u32, device: u32) -> Self { + Self { + vendor, + device, + subvendor: DeviceId::PCI_ANY_ID, + subdevice: DeviceId::PCI_ANY_ID, + class: 0, + class_mask: 0, + } + } + + /// PCI_DEVICE_CLASS macro. + pub const fn with_class(class: u32, class_mask: u32) -> Self { + Self { + vendor: DeviceId::PCI_ANY_ID, + device: DeviceId::PCI_ANY_ID, + subvendor: DeviceId::PCI_ANY_ID, + subdevice: DeviceId::PCI_ANY_ID, + class, + class_mask, + } + } + + /// PCI_DEVICE_ID macro. + pub const fn to_rawid(&self, offset: isize) -> bindings::pci_device_id { + bindings::pci_device_id { + vendor: self.vendor, + device: self.device, + subvendor: self.subvendor, + subdevice: self.subdevice, + class: self.class, + class_mask: self.class_mask, + driver_data: offset as _, + override_only: 0, + } + } +} + +// SAFETY: `ZERO` is all zeroed-out and `to_rawid` stores `offset` in `pci_device_id::driver_data`. +unsafe impl driver::RawDeviceId for DeviceId { + type RawType = 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 +/// +/// ```ignore +/// # use kernel::{pci, define_pci_id_table}; +/// # +/// struct MyDriver; +/// impl pci::Driver for MyDriver { +/// // [...] +/// # fn probe(_dev: &mut pci::Device, _id_info: Option<&Self::IdInfo>) -> Result { +/// # Ok(()) +/// # } +/// # define_pci_id_table! {u32, [ +/// # (pci::DeviceId::new(0x010800, 0xffffff), None), +/// # (pci::DeviceId::with_class(0x010802, 0xfffff), Some(0x10)), +/// # ]} +/// } +/// ``` +#[macro_export] +macro_rules! define_pci_id_table { + ($data_type:ty, $($t:tt)*) => { + type IdInfo = $data_type; + const ID_TABLE: $crate::driver::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. +/// +/// 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 + driver::DeviceRemoval = ();` + type Data: ForeignOwnable + driver::DeviceRemoval; + + /// The type holding information about each device id supported by the driver. + /// + /// TODO: Use associated_type_defaults once stabilized: + /// + /// type IdInfo: 'static = (); + type IdInfo: 'static; + + /// The table of device ids supported by the driver. + const ID_TABLE: driver::IdTable<'static, DeviceId, Self::IdInfo>; + + /// PCI driver probe. + /// + /// Called when a new platform device is added or discovered. + /// Implementers should attempt to initialize the device here. + fn probe(dev: &mut Device, id: Option<&Self::IdInfo>) -> Result; + + /// PCI driver remove. + /// + /// Called when a platform device is removed. + /// Implementers should prepare the device for complete removal here. + fn remove(_data: &Self::Data); +} + +/// The PCI device representation. +/// +/// A PCI device is based on an always reference counted `device:Device` 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 requirements described in pci::Device::new(). + unsafe { container_of!(self.0.as_raw(), bindings::pci_dev, dev) as _ } + } + + /// Enable the Device's memory. + pub fn enable_device_mem(&self) -> Result { + // SAFETY: By the type invariants, we know that `self.ptr` is non-null and valid. + let ret = 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: By the type invariants, we know that `self.ptr` is non-null and valid. + unsafe { bindings::pci_set_master(self.as_raw()) }; + } +} + +impl AsRef for Device { + fn as_ref(&self) -> &device::Device { + &self.0 + } +} From patchwork Mon May 20 17:25: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: 13668516 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 13A1B13AA54 for ; Mon, 20 May 2024 17:27:36 +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=1716226058; cv=none; b=Oow7hIYq3kr00RqQYBxKvoly0oBhIoFXPL4wrD4eN2aH0qp3izHaIIUwlBQJyAdciUAydQdhVHEZZqTeMabqARC9Zxt7nFWRDySaSsspMwXCa1tf6ir7zROi1l0gQ1YJ7mPZ0Owe0aMFxfEw7SXdfES8uvIUIJME6GV5oJF3MlY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226058; c=relaxed/simple; bh=ByeqkzM2jzSrDGnfHWAPGFr2QDs6pyQQOFnYXBVMUb0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=az9R83WeSmYJg5whqJKbI5jBUmel6lBbDeFpaBo23xiaTIr4d4KUsE8s/uMzHMMwsLfTgmG0+F9tJ11TinYHosJYYflFjk33mPxL6+jTpX3fs4VzqrtK0CyDXp5A5UHl+neHTZpMFypKodP9FdNJHmG9NNF7qIHQKfu7Xlbymso= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=TEY0UYzJ; arc=none smtp.client-ip=170.10.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="TEY0UYzJ" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226056; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=q+KTbXYumAmw16h3rTz/Xv/htYjuUG13nuBarwTBt2A=; b=TEY0UYzJ6TIs6wVxhJ+xBpd92VnDrqcllEDmysz3qSLu0ySSC3+s5//HOBPj6q7ORveuqu t+R7ws+0wXs59UuMAj/j9sExtqTiBa+0wpVe8PzAJJDBOJJOZ8UTQESlLESMYo37kNedh2 H+d4RTfjz3ZrVmM6aInTck+7V0cJ/JU= Received: from mail-wr1-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-49-TYO6iMh7OymeUC5O7D4RMg-1; Mon, 20 May 2024 13:27:34 -0400 X-MC-Unique: TYO6iMh7OymeUC5O7D4RMg-1 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-34e01a857a6so6390212f8f.2 for ; Mon, 20 May 2024 10:27:34 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226053; x=1716830853; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=q+KTbXYumAmw16h3rTz/Xv/htYjuUG13nuBarwTBt2A=; b=F1+f8IiSfPNSubQisEKTSWpIEfbnKa1+YRj7MiC9RAuHGJzsDI2sOg6CHFUtCC1cel ZDr+RusqY7X6e2OS6ETGSJVBXzaP2xvYuGUz4N7SK3NXHDk115yJWAxqXsxVx55nGSGr NVawJG7F/vK4FZuFhzi1SS3BvlBCNVYdTmOgRqCdEoJTFClIbM0N/G1wpuNTT8GPOH9t D1wU0K9x6iTyEXbI4SX6xE/Nul1cKE2upNmC7tWunoibF0W6HG7CqJJT6iXBCzEbCA9h 9KaSVWVD2WcjngGfks7hDCtyHRmDY2zy8jCM7YpA541D935OArBx1qMPn9JomXyY16+m VC3g== X-Forwarded-Encrypted: i=1; AJvYcCWabm+Bz6/5rPToEv9/4sOjGDc3rdg7rb5MQ+h4kDf1Ci856Yq9lyhF+skmVig2ZhO43RdclGXOWAHS7GWtZZFuiYM/vBWpiN0N X-Gm-Message-State: AOJu0YwiIaLQ8+DWP2VjUD3AyKiEjPrTz/kdO28eqaAn01VSf9Y96az7 pYq3Fh5ku3XEFHAkNB/rOJ0gdKMxH0mprhoYxbnwnoJS2Cnoti7fhVmdphXQvoIqmQUcYtrZmYt tPgpiXFA08OlTLJpdks4dDH4/0+U+FXVwW1A370G1jk2vCyr0KRaGeDkAkw== X-Received: by 2002:a5d:4801:0:b0:34d:a5fd:977b with SMTP id ffacd0b85a97d-3504a969ffcmr20289213f8f.60.1716226053146; Mon, 20 May 2024 10:27:33 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFa/Dnd6VAJ4Gwv9ggHRXIUtmNL29Xn1V3x1XOit8dv4TaIg0ybWQ9BXCbBKMeVj5eQB/k2Pw== X-Received: by 2002:a5d:4801:0:b0:34d:a5fd:977b with SMTP id ffacd0b85a97d-3504a969ffcmr20289201f8f.60.1716226052819; Mon, 20 May 2024 10:27:32 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-351d7d0f3absm12506111f8f.73.2024.05.20.10.27.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:32 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 10/11] rust: add basic abstractions for iomem operations Date: Mon, 20 May 2024 19:25:47 +0200 Message-ID: <20240520172554.182094-11-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Philipp Stanner Access to the kernel's IO-functions (e.g., readb()) is needed by almost all drivers. Currently, there are no abstractions for those functions. Since iomem is so widely used, it's necessary to provide a generic interface that all subsystems providing IO memory can use. The existing C implementations of such subsystems typically provide their own wrappers around functions like ioremap() which take care of respecting resource boundaries etc. It is, therefore, desirable to use these wrappers because using ioremap() and iounmap() directly would effectively result in parts of those subsystems being reimplemented in Rust. As most if not all device drivers should be fully satisfied regarding their iomem demands by the existing subsystem interfaces, Rust abstractions as congruent as possible with the existing infrastructure shall use the existing subsystem (e.g., PCI) interfaces for creating IO mappings, while simultaneously wrapping those mappings in Rust containers whose Drop() traits ensure that the resources are released again. The process for mapping iomem would consequently look as follows: 1. The subsystem abstraction (e.g., PCI) requests and ioremaps the memory through the corresponding C functions. 2. The subsystem uses resources obtained in step #1 to create a Rust IoMem data structure that implements the IO functionality such as readb() for the iomem. 3. The subsystem code wrapps IoMem into additional containers that ensure, e.g., thread safety, prevent UAF etc. Additionally, the subsystem ensures that access to IoMem is revoked latest when the driver's remove() callback is invoked. Hereby, the subsystem data structure obtains ownership over the iomem. Release of the iomem and, possibly, other subsystem associated data is then handled through the Drop() trait of the subsystem data structure. IO memory can become invalid during runtime (for example because the driver's remove() callback was invoked, revoking access to the driver's resources). However, in parallel executing routines might still be active. Consequently, the subsytem should also guard the iomem in some way. One way to do this is the Devres implementation, which provides a container that is capable of revoking access to its payload when the driver's remove() callback is invoked. The figure illustrates what usage of IoMem through subsystems might look like: Devres *------------------------------* | | | subsystem data structure | | *----------------------* | | | IoMem | | | | *------------------* | | | | | io_addr = 0x42, | | | | | | io_len = 9001, | | | | | | | | | | | | readb(), | | | | | | writeb(), | | | | | | ... | | | | | *------------------* | | | | deref(), | | | | drop(), | | | | ... | | | *----------------------* | | deref(), | | drop(), | *------------------------------* For additional convenience, subsystem abstractions can implement the Deref() trait for their data structures so that access he iomem can be fully transparent. In summary, IoMem would just serve as a container providing the IO functions, and the subsystem, which knows about the memory layout, request mechanisms etc, shall create and guard IoMem and ensure that its resources are released latest at remove() (through Devres) or earlier through its Drop() implementation. Add 'IoMem', a struct holding an IO-Pointer and a length parameter, which both shall be initialized validly by the subsystem. Add Rust abstractions for basic IO memory operations on IoMem. Signed-off-by: Philipp Stanner Signed-off-by: Danilo Krummrich --- rust/helpers.c | 106 +++++++++++++++++++++++++++++++++ rust/kernel/iomem.rs | 135 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 241 insertions(+) create mode 100644 rust/kernel/iomem.rs diff --git a/rust/helpers.c b/rust/helpers.c index c3d80301185c..dc2405772b1a 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -34,6 +34,7 @@ #include #include #include +#include __noreturn void rust_helper_BUG(void) { @@ -179,6 +180,111 @@ int rust_helper_devm_add_action(struct device *dev, void (*action)(void *), void return devm_add_action(dev, action, data); } +/* io.h */ +u8 rust_helper_readb(const volatile void __iomem *addr) +{ + return readb(addr); +} +EXPORT_SYMBOL_GPL(rust_helper_readb); + +u16 rust_helper_readw(const volatile void __iomem *addr) +{ + return readw(addr); +} +EXPORT_SYMBOL_GPL(rust_helper_readw); + +u32 rust_helper_readl(const volatile void __iomem *addr) +{ + return readl(addr); +} +EXPORT_SYMBOL_GPL(rust_helper_readl); + +#ifdef CONFIG_64BIT +u64 rust_helper_readq(const volatile void __iomem *addr) +{ + return readq(addr); +} +EXPORT_SYMBOL_GPL(rust_helper_readq); +#endif + +void rust_helper_writeb(u8 value, volatile void __iomem *addr) +{ + writeb(value, addr); +} +EXPORT_SYMBOL_GPL(rust_helper_writeb); + +void rust_helper_writew(u16 value, volatile void __iomem *addr) +{ + writew(value, addr); +} +EXPORT_SYMBOL_GPL(rust_helper_writew); + +void rust_helper_writel(u32 value, volatile void __iomem *addr) +{ + writel(value, addr); +} +EXPORT_SYMBOL_GPL(rust_helper_writel); + +#ifdef CONFIG_64BIT +void rust_helper_writeq(u64 value, volatile void __iomem *addr) +{ + writeq(value, addr); +} +EXPORT_SYMBOL_GPL(rust_helper_writeq); +#endif + +u8 rust_helper_readb_relaxed(const volatile void __iomem *addr) +{ + return readb_relaxed(addr); +} +EXPORT_SYMBOL_GPL(rust_helper_readb_relaxed); + +u16 rust_helper_readw_relaxed(const volatile void __iomem *addr) +{ + return readw_relaxed(addr); +} +EXPORT_SYMBOL_GPL(rust_helper_readw_relaxed); + +u32 rust_helper_readl_relaxed(const volatile void __iomem *addr) +{ + return readl_relaxed(addr); +} +EXPORT_SYMBOL_GPL(rust_helper_readl_relaxed); + +#ifdef CONFIG_64BIT +u64 rust_helper_readq_relaxed(const volatile void __iomem *addr) +{ + return readq_relaxed(addr); +} +EXPORT_SYMBOL_GPL(rust_helper_readq_relaxed); +#endif + +void rust_helper_writeb_relaxed(u8 value, volatile void __iomem *addr) +{ + writeb_relaxed(value, addr); +} +EXPORT_SYMBOL_GPL(rust_helper_writeb_relaxed); + +void rust_helper_writew_relaxed(u16 value, volatile void __iomem *addr) +{ + writew_relaxed(value, addr); +} +EXPORT_SYMBOL_GPL(rust_helper_writew_relaxed); + +void rust_helper_writel_relaxed(u32 value, volatile void __iomem *addr) +{ + writel_relaxed(value, addr); +} +EXPORT_SYMBOL_GPL(rust_helper_writel_relaxed); + +#ifdef CONFIG_64BIT +void rust_helper_writeq_relaxed(u64 value, volatile void __iomem *addr) +{ + writeq_relaxed(value, addr); +} +EXPORT_SYMBOL_GPL(rust_helper_writeq_relaxed); +#endif + void rust_helper_pci_set_drvdata(struct pci_dev *pdev, void *data) { pci_set_drvdata(pdev, data); diff --git a/rust/kernel/iomem.rs b/rust/kernel/iomem.rs new file mode 100644 index 000000000000..efb6cd0829b4 --- /dev/null +++ b/rust/kernel/iomem.rs @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0 + +use crate::bindings; +use crate::error::{code::EINVAL, Result}; + +/// IO-mapped memory, starting at the base pointer @ioptr and spanning @malxen bytes. +/// +/// The creator (usually a subsystem such as PCI) is responsible for creating the +/// mapping, performing an additional region request etc. +pub struct IoMem { + pub ioptr: usize, + maxlen: usize, +} + +impl IoMem { + pub(crate) fn new(ioptr: usize, maxlen: usize) -> Result { + if ioptr == 0 || maxlen == 0 { + return Err(EINVAL); + } + + Ok(Self { ioptr, maxlen }) + } + + fn get_io_addr(&self, offset: usize, len: usize) -> Result { + if offset + len > self.maxlen { + return Err(EINVAL); + } + + Ok(self.ioptr + offset) + } + + pub fn readb(&self, offset: usize) -> Result { + let ioptr: usize = self.get_io_addr(offset, 1)?; + + Ok(unsafe { bindings::readb(ioptr as _) }) + } + + pub fn readw(&self, offset: usize) -> Result { + let ioptr: usize = self.get_io_addr(offset, 2)?; + + Ok(unsafe { bindings::readw(ioptr as _) }) + } + + pub fn readl(&self, offset: usize) -> Result { + let ioptr: usize = self.get_io_addr(offset, 4)?; + + Ok(unsafe { bindings::readl(ioptr as _) }) + } + + pub fn readq(&self, offset: usize) -> Result { + let ioptr: usize = self.get_io_addr(offset, 8)?; + + Ok(unsafe { bindings::readq(ioptr as _) }) + } + + pub fn readb_relaxed(&self, offset: usize) -> Result { + let ioptr: usize = self.get_io_addr(offset, 1)?; + + Ok(unsafe { bindings::readb_relaxed(ioptr as _) }) + } + + pub fn readw_relaxed(&self, offset: usize) -> Result { + let ioptr: usize = self.get_io_addr(offset, 2)?; + + Ok(unsafe { bindings::readw_relaxed(ioptr as _) }) + } + + pub fn readl_relaxed(&self, offset: usize) -> Result { + let ioptr: usize = self.get_io_addr(offset, 4)?; + + Ok(unsafe { bindings::readl_relaxed(ioptr as _) }) + } + + pub fn readq_relaxed(&self, offset: usize) -> Result { + let ioptr: usize = self.get_io_addr(offset, 8)?; + + Ok(unsafe { bindings::readq_relaxed(ioptr as _) }) + } + + pub fn writeb(&self, byte: u8, offset: usize) -> Result { + let ioptr: usize = self.get_io_addr(offset, 1)?; + + unsafe { bindings::writeb(byte, ioptr as _) } + Ok(()) + } + + pub fn writew(&self, word: u16, offset: usize) -> Result { + let ioptr: usize = self.get_io_addr(offset, 2)?; + + unsafe { bindings::writew(word, ioptr as _) } + Ok(()) + } + + pub fn writel(&self, lword: u32, offset: usize) -> Result { + let ioptr: usize = self.get_io_addr(offset, 4)?; + + unsafe { bindings::writel(lword, ioptr as _) } + Ok(()) + } + + pub fn writeq(&self, qword: u64, offset: usize) -> Result { + let ioptr: usize = self.get_io_addr(offset, 8)?; + + unsafe { bindings::writeq(qword, ioptr as _) } + Ok(()) + } + + pub fn writeb_relaxed(&self, byte: u8, offset: usize) -> Result { + let ioptr: usize = self.get_io_addr(offset, 1)?; + + unsafe { bindings::writeb_relaxed(byte, ioptr as _) } + Ok(()) + } + + pub fn writew_relaxed(&self, word: u16, offset: usize) -> Result { + let ioptr: usize = self.get_io_addr(offset, 2)?; + + unsafe { bindings::writew_relaxed(word, ioptr as _) } + Ok(()) + } + + pub fn writel_relaxed(&self, lword: u32, offset: usize) -> Result { + let ioptr: usize = self.get_io_addr(offset, 4)?; + + unsafe { bindings::writel_relaxed(lword, ioptr as _) } + Ok(()) + } + + pub fn writeq_relaxed(&self, qword: u64, offset: usize) -> Result { + let ioptr: usize = self.get_io_addr(offset, 8)?; + + unsafe { bindings::writeq_relaxed(qword, ioptr as _) } + Ok(()) + } +} From patchwork Mon May 20 17:25: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: 13668517 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 E23541386C0 for ; Mon, 20 May 2024 17:27:42 +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=1716226064; cv=none; b=bu8fd82TzmtkY02lT4kCl3AHweECuRP28oEw/4I4XX/JLkw6Q0hClZAYVhUXr1l6zqppwyvIwtkmjIjqTrcIcudSsygrMRVVOkcZ8k7/KGajHZf4+EzDo40+ngxGmt+kLXtFM796pkhego9C+AmvozuX+KtpmL3FG1OFFQqECmM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716226064; c=relaxed/simple; bh=YZfg+yFUJvGKf66LKiZJyB7z2AoSnnfdsff2xWWMZwY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QF945G7JavT3qoi7muGMQMxNRqdhShuscGlV/2syl3FIfZNLnSNVZXI0yeApe/hFXIYDxsiHO3EirKeP5wsnK9u1RWbYf9hAimx3Ir+Ui3bbFmJt5OpW28MBnrhj8vI3Z8dXVeE4RdVc8VG6MxfK26S7j7KVOTbk5OeI9xtho9c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=KLzz7ZDg; arc=none smtp.client-ip=170.10.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="KLzz7ZDg" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1716226061; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=V3g+oeQpArsYWOlDSausMXlW95ZfMKvIKOo6W5/n3oc=; b=KLzz7ZDgqn0J7XZX960fRcn+mXV9zLG4zdJGhf5MEn+4LEKYRSu0FQD9RZrFxcEpvkzflJ 46Dk/YIw753rJgefscAOqBR5NIZ1FBtzSbQ5OdojiYinFrMmR5dxPugs4ZWnl9W4wMyFWj EXsaCddAwh0qSdj49RJJxDH+/BqDfTE= Received: from mail-wr1-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-695-jxYG_8uiPOq_FfMSZjCYYQ-1; Mon, 20 May 2024 13:27:38 -0400 X-MC-Unique: jxYG_8uiPOq_FfMSZjCYYQ-1 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-34f10f6b3aeso8002088f8f.3 for ; Mon, 20 May 2024 10:27:38 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716226057; x=1716830857; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=V3g+oeQpArsYWOlDSausMXlW95ZfMKvIKOo6W5/n3oc=; b=CJ1ENB0BYQbvWoTbdrCLS85lkDarV8sIy6epA1efVpRD8xhfBa9Fq3fvWMZn8GsVd3 OEg8XA6P7fiI4y/VvGnzXFCkwSlt3o71B+p4GdsNyl/Jo4Dzq0HvsLvKTY7nzhiB1Gml 2PBPd6ftfsiAPhSVleyx3HVXlL4LZCPDze4zpSlj067w9EdgVzxIWd+V6UO4M3ZJKcVA P0P7rVWiLZOHfHvavQwKT/y0/HviV4SyT0HVjroM3WttCBIJIc6NGiBD49MedzLRApbz sxcNixx0sFkGNJSEg5nAfb/6jYNUqnMli15QR+g13AwbW56BIIgFMUBmpEwxwIYvZRJw cuUA== X-Forwarded-Encrypted: i=1; AJvYcCXSFPWy/AbJgw3xRUV6FdbN2iqYYGw++xKsbky35nhyWl6UDkmALKHpQPM40SuXl2V1+QwOXjLtmSnmqg+I+Ejk8Ls9lfgshZ39 X-Gm-Message-State: AOJu0YyY+oCHiuWVE4D28HFdoWaNvIIXn4vwLisAfSai46mN9OaZLB1H SrfB9S4cNKl95se6i+2pV5/e58/T83LVwZDBJXibzPtMLfTfSuEvGcFDax13ExRHH/QWxqJ3H/t gQmRal1JZyNdO+mLcutEfbkVVnP3CxdJolx5o0UYblm8L0eeBdOE0gZ7ogQ== X-Received: by 2002:adf:ab1a:0:b0:351:4e42:c5f0 with SMTP id ffacd0b85a97d-3514e42c73emr20272331f8f.52.1716226057085; Mon, 20 May 2024 10:27:37 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGu7wi4ZLZ2yvRdkdYrPpzRkpeebcFzF47KqCnjyY1JeHk/jzOQJr9+KzuhFk2uiAX8OmXa2g== X-Received: by 2002:adf:ab1a:0:b0:351:4e42:c5f0 with SMTP id ffacd0b85a97d-3514e42c73emr20272302f8f.52.1716226056700; Mon, 20 May 2024 10:27:36 -0700 (PDT) Received: from cassiopeiae.. ([2a02:810d:4b3f:ee94:642:1aff:fe31:a19f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-352f264c7f1sm9827066f8f.42.2024.05.20.10.27.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 10:27:36 -0700 (PDT) From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com, fujita.tomonori@gmail.com, lina@asahilina.net, pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [RFC PATCH 11/11] rust: PCI: add BAR request and ioremap Date: Mon, 20 May 2024 19:25:48 +0200 Message-ID: <20240520172554.182094-12-dakr@redhat.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240520172554.182094-1-dakr@redhat.com> References: <20240520172554.182094-1-dakr@redhat.com> Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Philipp Stanner This commit implements a basic mechanism for requesting and IO-mapping PCI BARs. To perform IO on PCI devices it is necessary to have memory mapped PCI BARs. Before mapping those, a region request should be performed so that collisions with other drivers can be avoided. Since all the logic necessary to obtain the aforementioned resources are already implemented in C, Rust abstractions should use these interfaces. Hereby, the Rust implementation has to ensure that all resources are released again latest when the driver's remove() callback gets invoked, or earlier if the driver drop()s the PCI resource. This can be achieved through the Devres container, which uses devres callbacks combined with Revocable to block access to the resource - in this case, the PCI BAR and its IoMem. A pci::Bar's Drop() trait shall deregister the memory region request and iounmap() the mapping. In case remove() is invoked before such a Bar is drop()ed, the Devres container shall ensure that access to the Bar is revoke()d (through Revocable) so that no UAFs can occur. Implement 'Bar', a container for requested and ioremapped PCI BARs. Implement the Drop() trait such that the memory request and IO-mapping get freed if Bar goes out of scope. Implement Deref() so that the container is transparent. Implement iomap_region() to create a Bar and have the result returned through a Devres container, ensuring that the resources are latest freed in the driver's remove() callback and access to the Bar is revoke()d for outstanding users. Signed-off-by: Philipp Stanner Co-developed-by: Danilo Krummrich Signed-off-by: Danilo Krummrich --- rust/kernel/lib.rs | 1 + rust/kernel/pci.rs | 123 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 606391cbff83..15730deca822 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -54,6 +54,7 @@ #[doc(hidden)] pub use bindings; +mod iomem; pub use macros; #[cfg(all(CONFIG_PCI, CONFIG_PCI_MSI))] pub mod pci; diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 323aea565d84..403a1f53eb25 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -5,12 +5,17 @@ //! C header: [`include/linux/pci.h`](../../../../include/linux/pci.h) use crate::{ - bindings, container_of, device, driver, + alloc::flags::*, + bindings, container_of, device, + devres::Devres, + driver, error::{to_result, Result}, + iomem::IoMem, str::CStr, types::{ARef, ForeignOwnable}, ThisModule, }; +use core::ops::Deref; use kernel::prelude::*; // for pinned_drop /// An adapter for the registration of PCI drivers. @@ -287,6 +292,104 @@ pub trait Driver { #[derive(Clone)] pub struct Device(ARef); +/// A PCI BAR to perform IO-Operations on. +pub struct Bar { + pdev: Device, + iomem: IoMem, + num: u8, +} + +impl Bar { + fn new(pdev: Device, num: u8, name: &CStr) -> Result { + let barnr = num as i32; + + let barlen = pdev.resource_len(num)?; + if barlen == 0 { + return Err(ENOMEM); + } + + // SAFETY: + // `pdev` is always valid. + // `barnr` is checked for validity at the top of the function. + // `name` is always valid. + let ret = unsafe { bindings::pci_request_region(pdev.as_raw(), barnr, name.as_char_ptr()) }; + if ret != 0 { + return Err(EBUSY); + } + + // SAFETY: + // `pdev` is always valid. + // `barnr` is checked for validity at the top of the function. + // `name` is always valid. + let ioptr: usize = unsafe { bindings::pci_iomap(pdev.as_raw(), barnr, 0) } as usize; + if ioptr == 0 { + // SAFETY: + // `pdev` is always valid. + // `barnr` is checked for validity at the top of the function. + unsafe { bindings::pci_release_region(pdev.as_raw(), barnr) }; + return Err(ENOMEM); + } + + let iomem = match IoMem::new(ioptr, barlen as usize) { + Ok(iomem) => iomem, + Err(err) => { + // SAFETY: + // `pdev` is always valid. + // `ioptr` was created above, and `num` was checked at the top of the function. + unsafe { Self::do_release(&pdev, ioptr, num) }; + return Err(err); + } + }; + + Ok(Bar { pdev, iomem, num }) + } + + fn index_is_valid(i: u8) -> bool { + // A pci_dev on the C side owns an array of resources with at most + // PCI_NUM_RESOURCES entries. + if i as i32 >= bindings::PCI_NUM_RESOURCES as i32 { + return false; + } + + true + } + + // SAFETY: The caller should ensure that `ioptr` is valid. + unsafe fn do_release(pdev: &Device, ioptr: usize, num: u8) { + // SAFETY: + // `pdev` is Rust data and guaranteed to be valid. + // A valid `ioptr` should be provided by the caller, but an invalid one + // does not cause faults on the C side. + // `num` is checked for validity above. + unsafe { + bindings::pci_iounmap(pdev.as_raw(), ioptr as _); + bindings::pci_release_region(pdev.as_raw(), num as i32); + } + } + + fn release(&self) { + // SAFETY: + // Safe because `self` always contains a refcounted device that belongs + // to a pci::Device. + // `ioptr` and `num` are always valid because the Bar was created successfully. + unsafe { Self::do_release(&self.pdev, self.iomem.ioptr, self.num) }; + } +} + +impl Drop for Bar { + fn drop(&mut self) { + self.release(); + } +} + +impl Deref for Bar { + type Target = IoMem; + + fn deref(&self) -> &Self::Target { + &self.iomem + } +} + impl Device { /// Create a PCI Device instance from an existing `device::Device`. /// @@ -319,6 +422,24 @@ pub fn set_master(&self) { // SAFETY: By the type invariants, we know that `self.ptr` is non-null and valid. unsafe { bindings::pci_set_master(self.as_raw()) }; } + + /// Returns the size of the given PCI bar resource. + pub fn resource_len(&self, bar: u8) -> Result { + if !Bar::index_is_valid(bar) { + return Err(EINVAL); + } + + // SAFETY: Safe as by the type invariant. + Ok(unsafe { bindings::pci_resource_len(self.as_raw(), bar.into()) }) + } + + /// Mapps an entire PCI-BAR after performing a region-request on it. + pub fn iomap_region(&mut self, barnr: u8, name: &CStr) -> Result> { + let bar = Bar::new(self.clone(), barnr, name)?; + let devres = Devres::new(self.0.clone(), bar, GFP_KERNEL)?; + + Ok(devres) + } } impl AsRef for Device {