Received: by 2002:a05:6358:16cc:b0:ea:6187:17c9 with SMTP id r12csp4108922rwl; Tue, 27 Dec 2022 22:28:26 -0800 (PST) X-Google-Smtp-Source: AMrXdXu9ezJiLEM3idlfqDhjU2SD/CGOpTF6MtZwoD2oz0bIxm0UXXLlUjCxoaIHQAUG6LBumHDU X-Received: by 2002:a62:3883:0:b0:580:b942:e85 with SMTP id f125-20020a623883000000b00580b9420e85mr17178854pfa.16.1672208906162; Tue, 27 Dec 2022 22:28:26 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1672208906; cv=none; d=google.com; s=arc-20160816; b=ETVkBO1IEpsp0W+bZNiF0nyG0WAtgs406cy9XSa04wdG2gyxz1YixGNMIa8OAskZC8 B2B9eozPbjoRleKNgjcVtTRwb21nrtiixDK4BOzyacyeSPbNE9UVSBijSoW1zAgXOoWh UCXxvgkKD163yjbZR5EBoHv5YxlFLDneupa/J9UmZz2YhRwr+H3PbDa2bznQ19yNwQHm 2m11OrJntDhoiSt+cQ3pcCmfDWIr6uOBMmGe2L5Lz80I/AqpewdKFvwwgr4NpCy77KFq XFbxpZhW/j6rv+eCM4vbNseYzNEVzP5p/LJXpJcsFwx7lPMHUeYJg5ppr6+kOGj6+p00 kmIA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=s76JV/Exvx+RM2maTBqxn8/tfygp8gqzjmIPH1TmhL4=; b=K7SRZlmD2DLBdLvPtfa/I506J9kasH5F0VAHQ2YzBtoY/0nC+tt3iBYu35PKNHXCmE rgHd4IUQ1QeV0C6fl3ON98f/kGXJW5kDIgGrpekh0xWZ6EhPjsp89GULyYmjR0Co03To +XejTP4WP55THjZiKjMTdQKSFURn+Lo6k68snPm9dhR19MubVHAATWJ23P22kq+NQP3m EkAU4MinZbyvjfdXo7p+4EyGV6+mLqtCMKHujN64AOrclRuVnZlhegwUdp1gas57zLeJ YfxCot2wDN2W/gW4l+LhPSZWfhnvdLaNu1comLCOTENqFvPdd9CULCR9LtRqVGdHQWGf sXTw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=ZRxl3nUZ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id 73-20020a62144c000000b0058167c50a3asi3697707pfu.142.2022.12.27.22.28.16; Tue, 27 Dec 2022 22:28:26 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=ZRxl3nUZ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231533AbiL1GFF (ORCPT + 65 others); Wed, 28 Dec 2022 01:05:05 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59054 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229494AbiL1GEm (ORCPT ); Wed, 28 Dec 2022 01:04:42 -0500 Received: from mail-wr1-x430.google.com (mail-wr1-x430.google.com [IPv6:2a00:1450:4864:20::430]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 185FEDF1B; Tue, 27 Dec 2022 22:04:41 -0800 (PST) Received: by mail-wr1-x430.google.com with SMTP id bx10so14105631wrb.0; Tue, 27 Dec 2022 22:04:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=s76JV/Exvx+RM2maTBqxn8/tfygp8gqzjmIPH1TmhL4=; b=ZRxl3nUZob8rBBEhsqXnWL0rXlFlstDxouDNrtu7aJYWnNEUFtG6Azp9erA4Y4alPC XSu+SYDAGGkDQ49yjn+eFmOiN1oadkbrhc86SbsqxCMwrlG6Uv+D1ti2w3qeqDUM74kn OFoTBsiuX9oAqA1QqdMrRAleoiGb6Nso0ar+AoyNXlPCt9QC9mbo3bTHYuEBRo7okWpX KSCZIDSs29WNd8wLBwOB11u9kNlqqnL06hoY6PGhbmJA8Kc8ovJRDKgkfM/wwC14jgTM ZYvaPuz5aOX0jNhXjqsex1rQlS+RH2841sAislzO3z4nQoQ/Xh8uvQa5z7PV1sW/5mbk NC1A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=s76JV/Exvx+RM2maTBqxn8/tfygp8gqzjmIPH1TmhL4=; b=wMArZmZ7fkFGnRod/2+dOjg9ujfsaf/87APvYN3XDQL0Nu065u9Y0bLW77JV05jGOn J5dRN9Rcp6IIaoKR0S0H5c36IGJx70yD5DguGq2Q0cUuzsI+18X2WcrR47PTG0JLSz2K QIPDMOFq6ABpsh2ooCDf4yigACbjyFZqvSg+0USQ0rABanjQKdiHvl4pdwlX6oIwt4wC PFRhokhwjjuKQCl/koS2puscvzEuFxXAGFA7b68CxWcoAQGrKOjvvmtv4ny97RQKXlsR bOQE8LECaqDTu+fcz49hW0Dw1VVoRnKIPx6MpZFsZIpFb2yMdDow3qO7X3sGOs/JX+7g 7fAg== X-Gm-Message-State: AFqh2ko+2EOd7BPNZ2nOzaSSYb7zB5kZG6vHk1Ehb2gQm463r21fv+Aa hw8cKyJ+h8M1jUV0eiUXbMFZon3HY0WeCA== X-Received: by 2002:a05:6000:1801:b0:242:1f22:df11 with SMTP id m1-20020a056000180100b002421f22df11mr15566170wrh.4.1672207479545; Tue, 27 Dec 2022 22:04:39 -0800 (PST) Received: from wedsonaf-dev.. ([81.2.152.129]) by smtp.googlemail.com with ESMTPSA id x16-20020a5d6510000000b002755e301eeasm12128867wru.100.2022.12.27.22.04.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 27 Dec 2022 22:04:39 -0800 (PST) From: Wedson Almeida Filho To: rust-for-linux@vger.kernel.org Cc: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [PATCH 6/7] rust: sync: introduce `UniqueArc` Date: Wed, 28 Dec 2022 06:03:45 +0000 Message-Id: <20221228060346.352362-6-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20221228060346.352362-1-wedsonaf@gmail.com> References: <20221228060346.352362-1-wedsonaf@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Since `Arc` does not allow mutating `T` directly (i.e., without inner mutability), it is currently not possible to do some initialisation of `T` post construction but before being shared. `UniqueArc` addresses this problem essentially being an `Arc` that has a refcount of 1 and is therefore writable. Once initialisation is completed, it can be transitioned (without failure paths) into an `Arc`. Suggested-by: Gary Guo Signed-off-by: Wedson Almeida Filho --- rust/kernel/sync.rs | 2 +- rust/kernel/sync/arc.rs | 152 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 151 insertions(+), 3 deletions(-) diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs index 5de03ea83ea1..33da23e3076d 100644 --- a/rust/kernel/sync.rs +++ b/rust/kernel/sync.rs @@ -7,4 +7,4 @@ mod arc; -pub use arc::{Arc, ArcBorrow}; +pub use arc::{Arc, ArcBorrow, UniqueArc}; diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index 84f31c85a513..832bafc74a90 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -19,8 +19,9 @@ use crate::{bindings, error::Result, types::Opaque}; use alloc::boxed::Box; use core::{ marker::{PhantomData, Unsize}, - mem::ManuallyDrop, - ops::Deref, + mem::{ManuallyDrop, MaybeUninit}, + ops::{Deref, DerefMut}, + pin::Pin, ptr::NonNull, }; @@ -222,6 +223,19 @@ impl Drop for Arc { } } +impl From> for Arc { + fn from(item: UniqueArc) -> Self { + item.inner + } +} + +impl From>> for Arc { + fn from(item: Pin>) -> Self { + // SAFETY: The type invariants of `Arc` guarantee that the data is pinned. + unsafe { Pin::into_inner_unchecked(item).inner } + } +} + /// A borrowed reference to an [`Arc`] instance. /// /// For cases when one doesn't ever need to increment the refcount on the allocation, it is simpler @@ -328,3 +342,137 @@ impl Deref for ArcBorrow<'_, T> { unsafe { &self.inner.as_ref().data } } } + +/// A refcounted object that is known to have a refcount of 1. +/// +/// It is mutable and can be converted to an [`Arc`] so that it can be shared. +/// +/// # Invariants +/// +/// `inner` always has a reference count of 1. +/// +/// # Examples +/// +/// In the following example, we make changes to the inner object before turning it into an +/// `Arc` object (after which point, it cannot be mutated directly). Note that `x.into()` +/// cannot fail. +/// +/// ``` +/// use kernel::sync::{Arc, UniqueArc}; +/// +/// struct Example { +/// a: u32, +/// b: u32, +/// } +/// +/// fn test() -> Result> { +/// let mut x = UniqueArc::try_new(Example { a: 10, b: 20 })?; +/// x.a += 1; +/// x.b += 1; +/// Ok(x.into()) +/// } +/// +/// # test().unwrap(); +/// ``` +/// +/// In the following example we first allocate memory for a ref-counted `Example` but we don't +/// initialise it on allocation. We do initialise it later with a call to [`UniqueArc::write`], +/// followed by a conversion to `Arc`. This is particularly useful when allocation happens +/// in one context (e.g., sleepable) and initialisation in another (e.g., atomic): +/// +/// ``` +/// use kernel::sync::{Arc, UniqueArc}; +/// +/// struct Example { +/// a: u32, +/// b: u32, +/// } +/// +/// fn test() -> Result> { +/// let x = UniqueArc::try_new_uninit()?; +/// Ok(x.write(Example { a: 10, b: 20 }).into()) +/// } +/// +/// # test().unwrap(); +/// ``` +/// +/// In the last example below, the caller gets a pinned instance of `Example` while converting to +/// `Arc`; this is useful in scenarios where one needs a pinned reference during +/// initialisation, for example, when initialising fields that are wrapped in locks. +/// +/// ``` +/// use kernel::sync::{Arc, UniqueArc}; +/// +/// struct Example { +/// a: u32, +/// b: u32, +/// } +/// +/// fn test() -> Result> { +/// let mut pinned = Pin::from(UniqueArc::try_new(Example { a: 10, b: 20 })?); +/// // We can modify `pinned` because it is `Unpin`. +/// pinned.as_mut().a += 1; +/// Ok(pinned.into()) +/// } +/// +/// # test().unwrap(); +/// ``` +pub struct UniqueArc { + inner: Arc, +} + +impl UniqueArc { + /// Tries to allocate a new [`UniqueArc`] instance. + pub fn try_new(value: T) -> Result { + Ok(Self { + // INVARIANT: The newly-created object has a ref-count of 1. + inner: Arc::try_new(value)?, + }) + } + + /// Tries to allocate a new [`UniqueArc`] instance whose contents are not initialised yet. + pub fn try_new_uninit() -> Result>> { + Ok(UniqueArc::> { + // INVARIANT: The newly-created object has a ref-count of 1. + inner: Arc::try_new(MaybeUninit::uninit())?, + }) + } +} + +impl UniqueArc> { + /// Converts a `UniqueArc>` into a `UniqueArc` by writing a value into it. + pub fn write(mut self, value: T) -> UniqueArc { + self.deref_mut().write(value); + let inner = ManuallyDrop::new(self).inner.ptr; + UniqueArc { + // SAFETY: The new `Arc` is taking over `ptr` from `self.inner` (which won't be + // dropped). The types are compatible because `MaybeUninit` is compatible with `T`. + inner: unsafe { Arc::from_inner(inner.cast()) }, + } + } +} + +impl From> for Pin> { + fn from(obj: UniqueArc) -> Self { + // SAFETY: It is not possible to move/replace `T` inside a `Pin>` (unless `T` + // is `Unpin`), so it is ok to convert it to `Pin>`. + unsafe { Pin::new_unchecked(obj) } + } +} + +impl Deref for UniqueArc { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.inner.deref() + } +} + +impl DerefMut for UniqueArc { + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: By the `Arc` type invariant, there is necessarily a reference to the object, so + // it is safe to dereference it. Additionally, we know there is only one reference when + // it's inside a `UniqueArc`, so it is safe to get a mutable reference. + unsafe { &mut self.inner.ptr.as_mut().data } + } +} -- 2.34.1