Received: by 2002:a05:7412:419a:b0:f3:1519:9f41 with SMTP id i26csp4732530rdh; Wed, 29 Nov 2023 09:07:30 -0800 (PST) X-Google-Smtp-Source: AGHT+IFe0dNBRiyXvKpZTs/DWceza6WRHgvWpMiNhk5Aq6IYYIDAVethUz2sd/uPzZ0ff0siRKHQ X-Received: by 2002:a05:6808:221d:b0:3a7:7bea:d3cc with SMTP id bd29-20020a056808221d00b003a77bead3ccmr23843405oib.0.1701277650269; Wed, 29 Nov 2023 09:07:30 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1701277650; cv=none; d=google.com; s=arc-20160816; b=y3HnxWLcxi6/2gZVBasuIZE1IUgLOOxWHHvL4OHQ5MTpQQLS9pwp9+oAC/kmX82vgG Mhc5ISKhkCwXd8uvnUV7jHp90VwAYNPTS4cdSxc1Xu5qjcMzGeFYh6fBaFTU00uWNFeD ue3AT3+R+KcxwB/X1+mQbfT6UBEBerxsaThButwkJRRtjKR9vtnMAb/vQzsiYiR2u3VF CdbkLnLydScCvofoTxzDm32nn6p1fR5ie+Hdsvrubp/AmH7gyYemJwyq5eNx6scM56BE tuK1EAYU8Iwf4b8kI/2Os5fitOwKZQhw5mVqMSVjbFSAlcn+9PWcS9EMoc/coFn8aBCe 91zg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-disposition:mime-version :references:message-id:subject:cc:to:from:date:dkim-signature; bh=zZ4NnTctH4jQkaAYnd1EutYWAQe/W5aqZw7EEYMVFVQ=; fh=nEaMtykN3yen8T6sC08/oNzV0+ox843bdf8+ebojgNE=; b=Kj82dxSCtY0lqawgUOQShnTFCI36v3scyoheOFmTG8kPcEYECK3WB/D6Jdk22DtbVi +8q8ZSVrQrmF8Wi0/0het+LpRfl/K9i1+sHDfxjXcCVQAj0k1WLGkigxHizOTXoqkL1G r3I8URSYddV1klFCK9ZeI6BoXqe+10yfWZPMV3LqIgzCRx8GKcg5Z+B/wIn7Ejym7vKF /rBNrcrgcVCdHHzvctEi1cVPO4w+2VLxuEIEib1qjpoO6oWeo7zuHvBuDICcmZ0RaelR 9Zp5qk6ozhm8iLKbppKQTlC8WO85vk2t5QKp2DZyNl4SEejKjiIZb6sxvmthvI3F/ge6 CI1g== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=jErP8aZi; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:5 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Return-Path: Received: from groat.vger.email (groat.vger.email. [2620:137:e000::3:5]) by mx.google.com with ESMTPS id a18-20020a056808129200b003ae574fe6cfsi6261949oiw.170.2023.11.29.09.07.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 29 Nov 2023 09:07:30 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:5 as permitted sender) client-ip=2620:137:e000::3:5; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=jErP8aZi; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:5 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by groat.vger.email (Postfix) with ESMTP id 57C0E80BFEC4; Wed, 29 Nov 2023 09:07:21 -0800 (PST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.11 at groat.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229650AbjK2RHA (ORCPT + 99 others); Wed, 29 Nov 2023 12:07:00 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41670 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229501AbjK2RG7 (ORCPT ); Wed, 29 Nov 2023 12:06:59 -0500 Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 12DD194 for ; Wed, 29 Nov 2023 09:07:04 -0800 (PST) Received: by smtp.kernel.org (Postfix) with ESMTPSA id E0472C433C8; Wed, 29 Nov 2023 17:06:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1701277623; bh=8ovlfa1jt1/2RsMv84ug7kOY4w3Kdl7fxXEWpoiPyc8=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=jErP8aZipGQn9AL7a6Rn8uXDKqUA8Rm4FqEXzQxdBb6lBkRgjYSmfK7tJOcjqN/qI u2WrnBagL9Oc3neXQegD84MgB7PD9Dp+OaEg4ZzuGkIgGHPdzKh5wBRDvw6foLc5iv NvT9Kyxq3yx961wAEJos3jdxxNqJdZoDm/YLEN01nWmwKGnqLcg8I/UQLjRkDIWIj1 hmPoYaUOvwWaZxjMlm0YfXJhcOJq7EZM6AzuAwzk0STykpAabVtPiaeYFsrDyXrWmk A3RZ/TXG+A4/YEz3d+cickXh2zA2jgrYTw+ArcqBVSzIJCoStEmX/dbaRVub6spb5Y /rpfbS+BgXnOQ== Date: Wed, 29 Nov 2023 18:06:55 +0100 From: Christian Brauner To: Alice Ryhl Cc: Miguel Ojeda , Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , =?utf-8?B?QmrDtnJu?= Roy Baron , Benno Lossin , Andreas Hindborg , Peter Zijlstra , Alexander Viro , Greg Kroah-Hartman , Arve =?utf-8?B?SGrDuG5uZXbDpWc=?= , Todd Kjos , Martijn Coenen , Joel Fernandes , Carlos Llamas , Suren Baghdasaryan , Dan Williams , Kees Cook , Matthew Wilcox , Thomas Gleixner , Daniel Xu , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-fsdevel@vger.kernel.org Subject: Re: [PATCH 1/7] rust: file: add Rust abstraction for `struct file` Message-ID: <20231129-geleckt-verebben-04ea0c08a53c@brauner> References: <20231129-alice-file-v1-0-f81afe8c7261@google.com> <20231129-alice-file-v1-1-f81afe8c7261@google.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: <20231129-alice-file-v1-1-f81afe8c7261@google.com> X-Spam-Status: No, score=-1.2 required=5.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,MAILING_LIST_MULTI, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on groat.vger.email Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (groat.vger.email [0.0.0.0]); Wed, 29 Nov 2023 09:07:21 -0800 (PST) On Wed, Nov 29, 2023 at 12:51:07PM +0000, Alice Ryhl wrote: > From: Wedson Almeida Filho > > This abstraction makes it possible to manipulate the open files for a > process. The new `File` struct wraps the C `struct file`. When accessing > it using the smart pointer `ARef`, the pointer will own a > reference count to the file. When accessing it as `&File`, then the > reference does not own a refcount, but the borrow checker will ensure > that the reference count does not hit zero while the `&File` is live. Could you explain this in more details please? Ideally with some C and how that translates to your Rust wrappers, please. Sorry, this is going to be a long journey... > > Since this is intended to manipulate the open files of a process, we > introduce a `from_fd` constructor that corresponds to the C `fget` > method. In future patches, it will become possible to create a new fd in > a process and bind it to a `File`. Rust Binder will use these to send > fds from one process to another. > > We also provide a method for accessing the file's flags. Rust Binder > will use this to access the flags of the Binder fd to check whether the > non-blocking flag is set, which affects what the Binder ioctl does. > > This introduces a struct for the EBADF error type, rather than just > using the Error type directly. This has two advantages: > * `File::from_fd` returns a `Result, BadFdError>`, which the > compiler will represent as a single pointer, with null being an error. > This is possible because the compiler understands that `BadFdError` > has only one possible value, and it also understands that the > `ARef` smart pointer is guaranteed non-null. > * Additionally, we promise to users of the method that the method can > only fail with EBADF, which means that they can rely on this promise > without having to inspect its implementation. > That said, there are also two disadvantages: > * Defining additional error types involves boilerplate. > * The question mark operator will only utilize the `From` trait once, > which prevents you from using the question mark operator on > `BadFdError` in methods that return some third error type that the > kernel `Error` is convertible into. (However, it works fine in methods > that return `Error`.) > > Signed-off-by: Wedson Almeida Filho > Co-developed-by: Daniel Xu > Signed-off-by: Daniel Xu > Co-developed-by: Alice Ryhl > Signed-off-by: Alice Ryhl > --- > rust/bindings/bindings_helper.h | 2 + > rust/helpers.c | 7 ++ > rust/kernel/file.rs | 182 ++++++++++++++++++++++++++++++++++++++++ > rust/kernel/lib.rs | 1 + > 4 files changed, 192 insertions(+) > > diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h > index 85f013ed4ca4..beed3ef1fbc3 100644 > --- a/rust/bindings/bindings_helper.h > +++ b/rust/bindings/bindings_helper.h > @@ -8,6 +8,8 @@ > > #include > #include > +#include > +#include > #include > #include > #include > diff --git a/rust/helpers.c b/rust/helpers.c > index 70e59efd92bc..03141a3608a4 100644 > --- a/rust/helpers.c > +++ b/rust/helpers.c > @@ -25,6 +25,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -157,6 +158,12 @@ void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func, > } > EXPORT_SYMBOL_GPL(rust_helper_init_work_with_key); > > +struct file *rust_helper_get_file(struct file *f) > +{ > + return get_file(f); > +} > +EXPORT_SYMBOL_GPL(rust_helper_get_file); > + > /* > * `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/file.rs b/rust/kernel/file.rs > new file mode 100644 > index 000000000000..ee4ec8b919af > --- /dev/null > +++ b/rust/kernel/file.rs > @@ -0,0 +1,182 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +//! Files and file descriptors. > +//! > +//! C headers: [`include/linux/fs.h`](../../../../include/linux/fs.h) and > +//! [`include/linux/file.h`](../../../../include/linux/file.h) > + > +use crate::{ > + bindings, > + error::{code::*, Error, Result}, > + types::{ARef, AlwaysRefCounted, Opaque}, > +}; > +use core::ptr; > + > +/// Flags associated with a [`File`]. > +pub mod flags { > + /// File is opened in append mode. > + pub const O_APPEND: u32 = bindings::O_APPEND; > + > + /// Signal-driven I/O is enabled. > + pub const O_ASYNC: u32 = bindings::FASYNC; > + > + /// Close-on-exec flag is set. > + pub const O_CLOEXEC: u32 = bindings::O_CLOEXEC; > + > + /// File was created if it didn't already exist. > + pub const O_CREAT: u32 = bindings::O_CREAT; > + > + /// Direct I/O is enabled for this file. > + pub const O_DIRECT: u32 = bindings::O_DIRECT; > + > + /// File must be a directory. > + pub const O_DIRECTORY: u32 = bindings::O_DIRECTORY; > + > + /// Like [`O_SYNC`] except metadata is not synced. > + pub const O_DSYNC: u32 = bindings::O_DSYNC; > + > + /// Ensure that this file is created with the `open(2)` call. > + pub const O_EXCL: u32 = bindings::O_EXCL; > + > + /// Large file size enabled (`off64_t` over `off_t`). > + pub const O_LARGEFILE: u32 = bindings::O_LARGEFILE; > + > + /// Do not update the file last access time. > + pub const O_NOATIME: u32 = bindings::O_NOATIME; > + > + /// File should not be used as process's controlling terminal. > + pub const O_NOCTTY: u32 = bindings::O_NOCTTY; > + > + /// If basename of path is a symbolic link, fail open. > + pub const O_NOFOLLOW: u32 = bindings::O_NOFOLLOW; > + > + /// File is using nonblocking I/O. > + pub const O_NONBLOCK: u32 = bindings::O_NONBLOCK; > + > + /// Also known as `O_NDELAY`. > + /// > + /// This is effectively the same flag as [`O_NONBLOCK`] on all architectures > + /// except SPARC64. > + pub const O_NDELAY: u32 = bindings::O_NDELAY; > + > + /// Used to obtain a path file descriptor. > + pub const O_PATH: u32 = bindings::O_PATH; > + > + /// Write operations on this file will flush data and metadata. > + pub const O_SYNC: u32 = bindings::O_SYNC; > + > + /// This file is an unnamed temporary regular file. > + pub const O_TMPFILE: u32 = bindings::O_TMPFILE; > + > + /// File should be truncated to length 0. > + pub const O_TRUNC: u32 = bindings::O_TRUNC; > + > + /// Bitmask for access mode flags. > + /// > + /// # Examples > + /// > + /// ``` > + /// use kernel::file; > + /// # fn do_something() {} > + /// # let flags = 0; > + /// if (flags & file::flags::O_ACCMODE) == file::flags::O_RDONLY { > + /// do_something(); > + /// } > + /// ``` > + pub const O_ACCMODE: u32 = bindings::O_ACCMODE; > + > + /// File is read only. > + pub const O_RDONLY: u32 = bindings::O_RDONLY; > + > + /// File is write only. > + pub const O_WRONLY: u32 = bindings::O_WRONLY; > + > + /// File can be both read and written. > + pub const O_RDWR: u32 = bindings::O_RDWR; > +} > + > +/// Wraps the kernel's `struct file`. > +/// > +/// # Invariants > +/// > +/// Instances of this type are always ref-counted, that is, a call to `get_file` ensures that the > +/// allocation remains valid at least until the matching call to `fput`. > +#[repr(transparent)] > +pub struct File(Opaque); > + > +// SAFETY: By design, the only way to access a `File` is via an immutable reference or an `ARef`. > +// This means that the only situation in which a `File` can be accessed mutably is when the > +// refcount drops to zero and the destructor runs. It is safe for that to happen on any thread, so > +// it is ok for this type to be `Send`. > +unsafe impl Send for File {} > + > +// SAFETY: It's OK to access `File` through shared references from other threads because we're > +// either accessing properties that don't change or that are properly synchronised by C code. Uhm, what guarantees are you talking about specifically, please? Examples would help. > +unsafe impl Sync for File {} > + > +impl File { > + /// Constructs a new `struct file` wrapper from a file descriptor. > + /// > + /// The file descriptor belongs to the current process. > + pub fn from_fd(fd: u32) -> Result, BadFdError> { > + // SAFETY: FFI call, there are no requirements on `fd`. > + let ptr = ptr::NonNull::new(unsafe { bindings::fget(fd) }).ok_or(BadFdError)?; > + > + // INVARIANT: `fget` increments the refcount before returning. > + Ok(unsafe { ARef::from_raw(ptr.cast()) }) > + } I think this is really misnamed. File reference counting has two modes. For simplicity let's ignore fdget_pos() for now: (1) fdget() Return file either with or without an increased reference count. If the fdtable was shared increment reference count, if not don't increment refernce count. (2) fget() Always increase refcount. Your File implementation currently only deals with (2). And this terminology is terribly important as far as I'm concerned. This wants to be fget() and not from_fd(). The latter tells me nothing. I feel we really need to try and mirror the current naming closely. Not religiously ofc but core stuff such as this really benefits from having an almost 1:1 mapping between C names and Rust names, I think. Especially in the beginning. > + > + /// Creates a reference to a [`File`] from a valid pointer. > + /// > + /// # Safety > + /// > + /// The caller must ensure that `ptr` points at a valid file and that its refcount does not > + /// reach zero during the lifetime 'a. > + pub unsafe fn from_ptr<'a>(ptr: *const bindings::file) -> &'a File { > + // INVARIANT: The safety requirements guarantee that the refcount does not hit zero during > + // 'a. The cast is okay because `File` is `repr(transparent)`. > + unsafe { &*ptr.cast() } > + } How does that work and what is this used for? It's required that a caller has called from_fd()/fget() first before from_ptr() can be used? Can you show how this would be used in an example, please? Unless you hold file_lock it is now invalid to access fields in struct file just with rcu lock held for example. Which is why returning a pointer without holding a reference seems dodgy. I'm probably just missing context. > + > + /// Returns the flags associated with the file. > + /// > + /// The flags are a combination of the constants in [`flags`]. > + pub fn flags(&self) -> u32 { > + // This `read_volatile` is intended to correspond to a READ_ONCE call. > + // > + // SAFETY: The file is valid because the shared reference guarantees a nonzero refcount. I really need to understand what you mean by shared reference. At least in the current C implementation you can't share a reference without another task as the other task might fput() behind you and then you're hosed. That's why we have the fdget() logic. > + // > + // TODO: Replace with `read_once` when available on the Rust side. > + unsafe { core::ptr::addr_of!((*self.0.get()).f_flags).read_volatile() } > + } > +} > + > +// SAFETY: The type invariants guarantee that `File` is always ref-counted. > +unsafe impl AlwaysRefCounted for File { > + fn inc_ref(&self) { > + // SAFETY: The existence of a shared reference means that the refcount is nonzero. > + unsafe { bindings::get_file(self.0.get()) }; > + } Why inc_ref() and not just get_file()? > + > + unsafe fn dec_ref(obj: ptr::NonNull) { > + // SAFETY: The safety requirements guarantee that the refcount is nonzero. > + unsafe { bindings::fput(obj.cast().as_ptr()) } > + } Ok, so this makes me think that from_ptr() requires you to have called from_fd()/fget() first which would be good. > +} > + > +/// Represents the `EBADF` error code. > +/// > +/// Used for methods that can only fail with `EBADF`. > +pub struct BadFdError; > + > +impl From for Error { > + fn from(_: BadFdError) -> Error { > + EBADF > + } > +} > + > +impl core::fmt::Debug for BadFdError { > + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { > + f.pad("EBADF") > + } > +} > diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs > index e6aff80b521f..ce9abceab784 100644 > --- a/rust/kernel/lib.rs > +++ b/rust/kernel/lib.rs > @@ -34,6 +34,7 @@ > mod allocator; > mod build_assert; > pub mod error; > +pub mod file; > pub mod init; > pub mod ioctl; > #[cfg(CONFIG_KUNIT)] > > -- > 2.43.0.rc1.413.gea7ed67945-goog >