Merge android16-6.12 into android16-6.12-lts

This merges the android16-6.12 branch into the -lts branch, catching
it up with the latest changes in there.

It contains the following commits:

* 21ed84930c UPSTREAM: Revert "usb: xhci: Implement xhci_handshake_check_state() helper"
* 5b3ae3bcbe BACKPORT: usb: xhci: Skip xhci_reset in xhci_resume if xhci is being removed
* 5c72e9faba ANDROID: rust_binder: adjust errors from death notifications
* 9e02edea7f ANDROID: rust_binder: use u64 for death cookie
* 4317f0aeff ANDROID: f2fs: fixup ABI break due to reserved_pin_section
* 25bdb4a624 Revert "ANDROID: ABI: update symbol list for honor"
* a76eb2b67b ANDROID: GKI: Update oplus symbol list
* 6222007a04 ANDROID: mm/readahead: add for bypass high order allocation
* 659d7bb454 ANDROID: ABI: Update symbol list for exynos
* 26937a37f5 ANDROID: MODVERSIONS: hide type definition in drivers/usb/core/driver.c
* 8760b6e4f5 ANDROID: usb: Add vendor hook for usb suspend and resume
* da662aecc8 FROMLIST: KVM: Avoid synchronize_srcu() in kvm_io_bus_register_dev()
* 4be05c6524 FROMLIST: KVM: arm64: vgic: Explicitly implement vgic_dist::ready ordering
* d6045efc66 FROMLIST: KVM: arm64: vgic-init: Remove vgic_ready() macro
* f06dd0cd35 ANDROID: rust_binder: release threads before refs
* 5bbd30a60b ANDROID: ABI: Update pixel symbol list
* bafbebf2ab ANDROID: GKI: Update symbol list for xiaomi
* b7b130b7cc ANDROID: export folio_deactivate() for GKI purpose.
* 41f730f9c4 ANDROID: GKI: update exynos symbol list
* 766ecae19f UPSTREAM: xhci: dbctty: disable ECHO flag by default
* 8ea40f5243 ANDROID: GKI: Update xiaomi symbol list.
* 5594b4731d ANDROID: vendor_hooks: export tracepoint symbols
* 0d4cc1daff ANDROID: KVM: arm64: Don't update IOMMU under memory pressure
* 672185e575 ANDROID: iommu/iommu: Handle multi-page deferred sg mappings
* 740d42d181 ANDROID: vendor_hooks: Add vendor_hook in futex to fix the OEM scheduling priority bug
* 6eb6f346ac ANDROID: ABI: Update symbol list for mtk
* c302079179 ANDROID: vendor_hooks: Add vendor hook for GenieZone demand paging
* 5c1cddc983 ANDROID: vendor_hooks: Add vendor hook for GenieZone para-virtualization
* d893caf112 ANDROID: ashmem_rust: Add support for retrieving an ashmem area's vmfile
* 0be74214c0 ANDROID: ashmem_rust: Add support for querying the size of an ashmem region
* eb50f663c4 ANDROID: ashmem_rust: Add support for providing an ashmem region's name
* 6bdbae6ea9 ANDROID: ashmem_rust: Add is_ashmem_file()
* 0d890f867e ANDROID: ABI: update symbol list for honor
* 12727f8a4b FROMGIT: f2fs: introduce reserved_pin_section sysfs entry
* 286cd9d628 ANDROID: GKI: Update RTK STB KMI symbol list
* 7b4f7682b5 ANDROID: GKI: Update symbol list for Amlogic
* 862ce4b2c4 ANDROID: KVM: arm64: iommu: Fix power tracking
* 61184996a8 ANDROID: drivers/iommu: Fix return value in iommu_map_sg
* acad0cd51d ANDROID: ABI: update symbol list for galaxy
* 393dbad32c ANDROID: vendor_hook: add condition to call for freezing fail
* b62fe47ba2 ANDROID: fix ashmem_rust return EINVAL bug in ashmem_rust.rs
* a7e1300b95 ANDROID: Revert "cpufreq: Avoid using inconsistent policy->min and policy->max"
* 15d2fe0544 ANDROID: qcom: Update the ABI symbol list
* f6ca783ba2 UPSTREAM: scsi: ufs: qcom: Check gear against max gear in vop freq_to_gear()
* 237708e9d3 ANDROID: GKI: Update symbols list file for honor White list the vm_normal_folio_pmd
* f18e354aa9 ANDROID: mm: export vm_normal_folio_pmd to allow vendors to implement simplified smaps
* c181c478b0 ANDROID: vendor_hooks: add hook to record slab free
* d2e452e197 ANDROID: Build fixups with PROXY_EXEC v18 + !CONFIG_SMP
* 4f9e4406e4 ANDROID: Update proxy-exec logic from v14 to v18
* 3fa8dabe1a ANDROID: GKI: update asr symbols list
* 94310b3f77 ANDROID: Add the dma header to aarch64 allowlist
* 880d6538c5 UPSTREAM: usb: gadget: u_serial: Fix race condition in TTY wakeup
* b115bf2302 ANDROID: ABI: Update symbol list for mtk
* e87018c5f9 FROMGIT: sched/deadline: Fix dl_server runtime calculation formula
* e2bf362ee2 FROMGIT: sched/core: Fix migrate_swap() vs. hotplug
* 06ca12d7d2 ANDROID: GKI: update the ABI symbol list
* 55972ed83a ANDROID: Fixup init_user_ns CRC change
* 4e873ad607 ANDROID: user: Add vendor hook to user for GKI purpose
* a097cd9c30 ANDROID: export find_user() for GKI purpose.
* 85b8233f7e ANDROID: rust_binder: use euid from the task
* 969c904869 ANDROID: ashmem: rename VmAreaNew->VmaNew
* 2ab3e5f283 ANDROID: rust_binder: rename VmAreaNew->VmaNew
* 2ef75ab83a ANDROID: rust_binder: use tgid_nr_ns for getting pid
* 6a2be11026 UPSTREAM: task: rust: rework how current is accessed
* 602e2300de UPSTREAM: rust: add PidNamespace
* 12dfc1d9cb UPSTREAM: rust: miscdevice: add mmap support
* 8e67cb756f UPSTREAM: mm: rust: add VmaNew for f_ops->mmap()
* bd140ddf75 UPSTREAM: mm: rust: add mmput_async support
* 0c50773076 UPSTREAM: mm: rust: add lock_vma_under_rcu
* 0b5465bb31 UPSTREAM: mm: rust: add vm_insert_page
* d7f52612c5 UPSTREAM: mm: rust: add vm_area_struct methods that require read access
* f03d4f7490 UPSTREAM: mm: rust: add abstraction for struct mm_struct
* 2ef6dbc73e BACKPORT: rust: miscdevice: change how f_ops vtable is constructed
* 1acd3b312f Revert "FROMLIST: mm: rust: add abstraction for struct mm_struct"
* a012c15566 Revert "FROMLIST: mm: rust: add vm_area_struct methods that require read access"
* 3be00a9bf8 Revert "FROMLIST: mm: rust: add vm_insert_page"
* 3aed88205e Revert "FROMLIST: mm: rust: add lock_vma_under_rcu"
* a121b6e72f Revert "FROMLIST: mm: rust: add mmput_async support"
* 9248564a81 Revert "FROMLIST: mm: rust: add VmAreaNew for f_ops->mmap()"
* 6de3ace5b5 Revert "FROMLIST: rust: miscdevice: add mmap support"
* b7f54dd23b Revert "BACKPORT: FROMLIST: task: rust: rework how current is accessed"
* 5913c80b22 ANDROID: iommu/arm-smmu-v3-kvm: Fix idmap free_leaf
* c40c54e669 UPSTREAM: erofs: impersonate the opener's credentials when accessing backing file
* 4d0200d0a9 BACKPORT: erofs: add 'fsoffset' mount option to specify filesystem offset
* 399deda7b5 ANDROID: scsi: ufs: add UFSHCD_ANDROID_QUIRK_NO_IS_READ_ON_H8
* f6b1ab83f6 ANDROID: rust_binder: remove binder_logs/procs/pid immediately
* dd35623c83 ANDROID: ABI: update symbol list for mtktv
* 58beebb30f FROMLIST: fuse: give wakeup hints to the scheduler
* 0f917e4066 ANDROID: virt: gunyah: Replace arm_smccc_1_1_smc with arm_smccc_1_1_invoke
* 33429dd323 UPSTREAM: posix-cpu-timers: fix race between handle_posix_cpu_timers() and posix_cpu_timer_del()
* 6483832947 ANDROID: GKI: Update symbol list file for xiaomi
* 668635cd34 UPSTREAM: usb: gadget: uvc: dont call usb_composite_setup_continue when not streaming

Change-Id: I64074144d1a6da9fdd3b4dd5f8314ccea4f9d9e8
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
Greg Kroah-Hartman
2025-07-13 12:16:44 +00:00
99 changed files with 58457 additions and 999 deletions
+1
View File
@@ -21,6 +21,7 @@
#include "mman.c"
#include "mutex.c"
#include "page.c"
#include "pid_namespace.c"
#include "poll.c"
#include "rbtree.c"
#include "refcount.c"
+26
View File
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/pid_namespace.h>
#include <linux/cleanup.h>
struct pid_namespace *rust_helper_get_pid_ns(struct pid_namespace *ns)
{
return get_pid_ns(ns);
}
void rust_helper_put_pid_ns(struct pid_namespace *ns)
{
put_pid_ns(ns);
}
/* Get a reference on a task's pid namespace. */
struct pid_namespace *rust_helper_task_get_pid_ns(struct task_struct *task)
{
struct pid_namespace *pid_ns;
guard(rcu)();
pid_ns = task_active_pid_ns(task);
if (pid_ns)
get_pid_ns(pid_ns);
return pid_ns;
}
+1
View File
@@ -52,6 +52,7 @@ pub mod mm;
pub mod net;
pub mod page;
pub mod page_size_compat;
pub mod pid_namespace;
pub mod prelude;
pub mod print;
pub mod rbtree;
+249 -247
View File
@@ -14,7 +14,7 @@ use crate::{
error::{to_result, Error, Result, VTABLE_DEFAULT_ERROR},
ffi::{c_int, c_long, c_uint, c_ulong, c_void},
fs::{File, LocalFile},
mm::virt::VmAreaNew,
mm::virt::VmaNew,
prelude::*,
seq_file::SeqFile,
str::CStr,
@@ -40,7 +40,7 @@ impl MiscDeviceOptions {
let mut result: bindings::miscdevice = unsafe { MaybeUninit::zeroed().assume_init() };
result.minor = bindings::MISC_DYNAMIC_MINOR as _;
result.name = self.name.as_char_ptr();
result.fops = create_vtable::<T>();
result.fops = MiscdeviceVTable::<T>::build();
result
}
}
@@ -135,9 +135,9 @@ pub trait MiscDevice: Sized {
fn mmap(
_device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
_file: &File,
_vma: &VmAreaNew,
_vma: &VmaNew,
) -> Result {
kernel::build_error!(VTABLE_DEFAULT_ERROR)
kernel::build_error(VTABLE_DEFAULT_ERROR)
}
/// Seeks this miscdevice.
@@ -270,263 +270,265 @@ impl IovIter {
}
}
const fn create_vtable<T: MiscDevice>() -> &'static bindings::file_operations {
const fn maybe_fn<T: Copy>(check: bool, func: T) -> Option<T> {
if check {
Some(func)
} else {
None
/// A vtable for the file operations of a Rust miscdevice.
struct MiscdeviceVTable<T: MiscDevice>(PhantomData<T>);
impl<T: MiscDevice> MiscdeviceVTable<T> {
/// # Safety
///
/// `file` and `inode` must be the file and inode for a file that is undergoing initialization.
/// The file must be associated with a `MiscDeviceRegistration<T>`.
unsafe extern "C" fn open(inode: *mut bindings::inode, raw_file: *mut bindings::file) -> c_int {
// SAFETY: The pointers are valid and for a file being opened.
let ret = unsafe { bindings::generic_file_open(inode, raw_file) };
if ret != 0 {
return ret;
}
// SAFETY: The open call of a file can access the private data.
let misc_ptr = unsafe { (*raw_file).private_data };
// SAFETY: This is a miscdevice, so `misc_open()` set the private data to a pointer to the
// associated `struct miscdevice` before calling into this method. Furthermore,
// `misc_open()` ensures that the miscdevice can't be unregistered and freed during this
// call to `fops_open`.
let misc = unsafe { &*misc_ptr.cast::<MiscDeviceRegistration<T>>() };
// SAFETY:
// * This underlying file is valid for (much longer than) the duration of `T::open`.
// * There is no active fdget_pos region on the file on this thread.
let file = unsafe { File::from_raw_file(raw_file) };
let ptr = match T::open(file, misc) {
Ok(ptr) => ptr,
Err(err) => return err.to_errno(),
};
// This overwrites the private data with the value specified by the user, changing the type
// of this file's private data. All future accesses to the private data is performed by
// other fops_* methods in this file, which all correctly cast the private data to the new
// type.
//
// SAFETY: The open call of a file can access the private data.
unsafe { (*raw_file).private_data = ptr.into_foreign().cast_mut() };
0
}
/// # Safety
///
/// `file` and `inode` must be the file and inode for a file that is being released. The file
/// must be associated with a `MiscDeviceRegistration<T>`.
unsafe extern "C" fn release(_inode: *mut bindings::inode, file: *mut bindings::file) -> c_int {
// SAFETY: The release call of a file owns the private data.
let private = unsafe { (*file).private_data };
// SAFETY: The release call of a file owns the private data.
let ptr = unsafe { <T::Ptr as ForeignOwnable>::from_foreign(private) };
// SAFETY:
// * The file is valid for the duration of this call.
// * There is no active fdget_pos region on the file on this thread.
T::release(ptr, unsafe { File::from_raw_file(file) });
0
}
/// # Safety
///
/// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
/// `vma` must be a vma that is currently being mmap'ed with this file.
unsafe extern "C" fn mmap(
file: *mut bindings::file,
vma: *mut bindings::vm_area_struct,
) -> c_int {
// SAFETY: The mmap call of a file can access the private data.
let private = unsafe { (*file).private_data };
// SAFETY: This is a Rust Miscdevice, so we call `into_foreign` in `open` and
// `from_foreign` in `release`, and `fops_mmap` is guaranteed to be called between those
// two operations.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
// SAFETY: The caller provides a vma that is undergoing initial VMA setup.
let area = unsafe { VmaNew::from_raw(vma) };
// SAFETY:
// * The file is valid for the duration of this call.
// * There is no active fdget_pos region on the file on this thread.
let file = unsafe { File::from_raw_file(file) };
match T::mmap(device, file, area) {
Ok(()) => 0,
Err(err) => err.to_errno(),
}
}
struct VtableHelper<T: MiscDevice> {
_t: PhantomData<T>,
/// # Safety
///
/// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
unsafe extern "C" fn llseek(
file: *mut bindings::file,
offset: loff_t,
whence: c_int,
) -> loff_t {
// SAFETY: The release call of a file owns the private data.
let private = unsafe { (*file).private_data };
// SAFETY: Ioctl calls can borrow the private data of the file.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
// SAFETY:
// * The file is valid for the duration of this call.
// * We are inside an fdget_pos region, so there cannot be any active fdget_pos regions on
// other threads.
let file = unsafe { LocalFile::from_raw_file(file) };
match T::llseek(device, file, offset, whence) {
Ok(res) => res as loff_t,
Err(err) => err.to_errno() as loff_t,
}
}
impl<T: MiscDevice> VtableHelper<T> {
const VTABLE: bindings::file_operations = bindings::file_operations {
open: Some(fops_open::<T>),
release: Some(fops_release::<T>),
mmap: maybe_fn(T::HAS_MMAP, fops_mmap::<T>),
llseek: maybe_fn(T::HAS_LLSEEK, fops_llseek::<T>),
read_iter: maybe_fn(T::HAS_READ_ITER, fops_read_iter::<T>),
write_iter: maybe_fn(T::HAS_WRITE_ITER, fops_write_iter::<T>),
unlocked_ioctl: maybe_fn(T::HAS_IOCTL, fops_ioctl::<T>),
#[cfg(CONFIG_COMPAT)]
compat_ioctl: if T::HAS_COMPAT_IOCTL {
Some(fops_compat_ioctl::<T>)
} else if T::HAS_IOCTL {
Some(bindings::compat_ptr_ioctl)
} else {
None
},
show_fdinfo: maybe_fn(T::HAS_SHOW_FDINFO, fops_show_fdinfo::<T>),
// SAFETY: All zeros is a valid value for `bindings::file_operations`.
..unsafe { MaybeUninit::zeroed().assume_init() }
/// # Safety
///
/// Arguments must be valid.
unsafe extern "C" fn read_iter(
kiocb: *mut bindings::kiocb,
iter: *mut bindings::iov_iter,
) -> isize {
let kiocb = Kiocb {
inner: unsafe { NonNull::new_unchecked(kiocb) },
_phantom: PhantomData,
};
let iov = unsafe { &mut *iter.cast::<IovIter>() };
match T::read_iter(kiocb, iov) {
Ok(res) => res as isize,
Err(err) => err.to_errno() as isize,
}
}
&VtableHelper::<T>::VTABLE
}
/// # Safety
///
/// Arguments must be valid.
unsafe extern "C" fn write_iter(
kiocb: *mut bindings::kiocb,
iter: *mut bindings::iov_iter,
) -> isize {
let kiocb = Kiocb {
inner: unsafe { NonNull::new_unchecked(kiocb) },
_phantom: PhantomData,
};
let iov = unsafe { &mut *iter.cast::<IovIter>() };
/// # Safety
///
/// `file` and `inode` must be the file and inode for a file that is undergoing initialization.
/// The file must be associated with a `MiscDeviceRegistration<T>`.
unsafe extern "C" fn fops_open<T: MiscDevice>(
inode: *mut bindings::inode,
raw_file: *mut bindings::file,
) -> c_int {
// SAFETY: The pointers are valid and for a file being opened.
let ret = unsafe { bindings::generic_file_open(inode, raw_file) };
if ret != 0 {
return ret;
match T::write_iter(kiocb, iov) {
Ok(res) => res as isize,
Err(err) => err.to_errno() as isize,
}
}
// SAFETY: The open call of a file can access the private data.
let misc_ptr = unsafe { (*raw_file).private_data };
/// # Safety
///
/// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
unsafe extern "C" fn ioctl(file: *mut bindings::file, cmd: c_uint, arg: c_ulong) -> c_long {
// SAFETY: The ioctl call of a file can access the private data.
let private = unsafe { (*file).private_data };
// SAFETY: Ioctl calls can borrow the private data of the file.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
// SAFETY: This is a miscdevice, so `misc_open()` set the private data to a pointer to the
// associated `struct miscdevice` before calling into this method. Furthermore, `misc_open()`
// ensures that the miscdevice can't be unregistered and freed during this call to `fops_open`.
let misc = unsafe { &*misc_ptr.cast::<MiscDeviceRegistration<T>>() };
// SAFETY:
// * The file is valid for the duration of this call.
// * There is no active fdget_pos region on the file on this thread.
let file = unsafe { File::from_raw_file(file) };
// SAFETY:
// * This underlying file is valid for (much longer than) the duration of `T::open`.
// * There is no active fdget_pos region on the file on this thread.
let file = unsafe { File::from_raw_file(raw_file) };
match T::ioctl(device, file, cmd, arg) {
Ok(ret) => ret as c_long,
Err(err) => err.to_errno() as c_long,
}
}
let ptr = match T::open(file, misc) {
Ok(ptr) => ptr,
Err(err) => return err.to_errno(),
/// # Safety
///
/// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
#[cfg(CONFIG_COMPAT)]
unsafe extern "C" fn compat_ioctl(
file: *mut bindings::file,
cmd: c_uint,
arg: c_ulong,
) -> c_long {
// SAFETY: The compat ioctl call of a file can access the private data.
let private = unsafe { (*file).private_data };
// SAFETY: Ioctl calls can borrow the private data of the file.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
// SAFETY:
// * The file is valid for the duration of this call.
// * There is no active fdget_pos region on the file on this thread.
let file = unsafe { File::from_raw_file(file) };
match T::compat_ioctl(device, file, cmd, arg) {
Ok(ret) => ret as c_long,
Err(err) => err.to_errno() as c_long,
}
}
/// # Safety
///
/// - `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
/// - `seq_file` must be a valid `struct seq_file` that we can write to.
unsafe extern "C" fn show_fdinfo(seq_file: *mut bindings::seq_file, file: *mut bindings::file) {
// SAFETY: The release call of a file owns the private data.
let private = unsafe { (*file).private_data };
// SAFETY: Ioctl calls can borrow the private data of the file.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
// SAFETY:
// * The file is valid for the duration of this call.
// * There is no active fdget_pos region on the file on this thread.
let file = unsafe { File::from_raw_file(file) };
// SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in
// which this method is called.
let m = unsafe { SeqFile::from_raw(seq_file) };
T::show_fdinfo(device, m, file);
}
const VTABLE: bindings::file_operations = bindings::file_operations {
open: Some(Self::open),
release: Some(Self::release),
mmap: if T::HAS_MMAP { Some(Self::mmap) } else { None },
llseek: if T::HAS_LLSEEK {
Some(Self::llseek)
} else {
None
},
read_iter: if T::HAS_READ_ITER {
Some(Self::read_iter)
} else {
None
},
write_iter: if T::HAS_WRITE_ITER {
Some(Self::write_iter)
} else {
None
},
unlocked_ioctl: if T::HAS_IOCTL {
Some(Self::ioctl)
} else {
None
},
#[cfg(CONFIG_COMPAT)]
compat_ioctl: if T::HAS_COMPAT_IOCTL {
Some(Self::compat_ioctl)
} else if T::HAS_IOCTL {
Some(bindings::compat_ptr_ioctl)
} else {
None
},
show_fdinfo: if T::HAS_SHOW_FDINFO {
Some(Self::show_fdinfo)
} else {
None
},
// SAFETY: All zeros is a valid value for `bindings::file_operations`.
..unsafe { MaybeUninit::zeroed().assume_init() }
};
// This overwrites the private data with the value specified by the user, changing the type of
// this file's private data. All future accesses to the private data is performed by other
// fops_* methods in this file, which all correctly cast the private data to the new type.
//
// SAFETY: The open call of a file can access the private data.
unsafe { (*raw_file).private_data = ptr.into_foreign().cast_mut() };
0
}
/// # Safety
///
/// `file` and `inode` must be the file and inode for a file that is being released. The file must
/// be associated with a `MiscDeviceRegistration<T>`.
unsafe extern "C" fn fops_release<T: MiscDevice>(
_inode: *mut bindings::inode,
file: *mut bindings::file,
) -> c_int {
// SAFETY: The release call of a file owns the private data.
let private = unsafe { (*file).private_data };
// SAFETY: The release call of a file owns the private data.
let ptr = unsafe { <T::Ptr as ForeignOwnable>::from_foreign(private) };
// SAFETY:
// * The file is valid for the duration of this call.
// * There is no active fdget_pos region on the file on this thread.
T::release(ptr, unsafe { File::from_raw_file(file) });
0
}
/// # Safety
///
/// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
/// `vma` must be a vma that is currently being mmap'ed with this file.
unsafe extern "C" fn fops_mmap<T: MiscDevice>(
file: *mut bindings::file,
vma: *mut bindings::vm_area_struct,
) -> c_int {
// SAFETY: The mmap call of a file can access the private data.
let private = unsafe { (*file).private_data };
// SAFETY: This is a Rust Miscdevice, so we call `into_foreign` in `open` and `from_foreign` in
// `release`, and `fops_mmap` is guaranteed to be called between those two operations.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
// SAFETY: The caller provides a vma that is undergoing initial VMA setup.
let area = unsafe { VmAreaNew::from_raw(vma) };
// SAFETY:
// * The file is valid for the duration of this call.
// * There is no active fdget_pos region on the file on this thread.
let file = unsafe { File::from_raw_file(file) };
match T::mmap(device, file, area) {
Ok(()) => 0,
Err(err) => err.to_errno() as c_int,
const fn build() -> &'static bindings::file_operations {
&Self::VTABLE
}
}
/// # Safety
///
/// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
unsafe extern "C" fn fops_llseek<T: MiscDevice>(
file: *mut bindings::file,
offset: loff_t,
whence: c_int,
) -> loff_t {
// SAFETY: The release call of a file owns the private data.
let private = unsafe { (*file).private_data };
// SAFETY: Ioctl calls can borrow the private data of the file.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
// SAFETY:
// * The file is valid for the duration of this call.
// * We are inside an fdget_pos region, so there cannot be any active fdget_pos regions on
// other threads.
let file = unsafe { LocalFile::from_raw_file(file) };
match T::llseek(device, file, offset, whence) {
Ok(res) => res as loff_t,
Err(err) => err.to_errno() as loff_t,
}
}
/// # Safety
///
/// Arguments must be valid.
unsafe extern "C" fn fops_read_iter<T: MiscDevice>(
kiocb: *mut bindings::kiocb,
iter: *mut bindings::iov_iter,
) -> isize {
let kiocb = Kiocb {
inner: unsafe { NonNull::new_unchecked(kiocb) },
_phantom: PhantomData,
};
let iov = unsafe { &mut *iter.cast::<IovIter>() };
match T::read_iter(kiocb, iov) {
Ok(res) => res as isize,
Err(err) => err.to_errno() as isize,
}
}
/// # Safety
///
/// Arguments must be valid.
unsafe extern "C" fn fops_write_iter<T: MiscDevice>(
kiocb: *mut bindings::kiocb,
iter: *mut bindings::iov_iter,
) -> isize {
let kiocb = Kiocb {
inner: unsafe { NonNull::new_unchecked(kiocb) },
_phantom: PhantomData,
};
let iov = unsafe { &mut *iter.cast::<IovIter>() };
match T::write_iter(kiocb, iov) {
Ok(res) => res as isize,
Err(err) => err.to_errno() as isize,
}
}
/// # Safety
///
/// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
unsafe extern "C" fn fops_ioctl<T: MiscDevice>(
file: *mut bindings::file,
cmd: c_uint,
arg: c_ulong,
) -> c_long {
// SAFETY: The ioctl call of a file can access the private data.
let private = unsafe { (*file).private_data };
// SAFETY: Ioctl calls can borrow the private data of the file.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
// SAFETY:
// * The file is valid for the duration of this call.
// * There is no active fdget_pos region on the file on this thread.
let file = unsafe { File::from_raw_file(file) };
match T::ioctl(device, file, cmd, arg as usize) {
Ok(ret) => ret as c_long,
Err(err) => err.to_errno() as c_long,
}
}
/// # Safety
///
/// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
#[cfg(CONFIG_COMPAT)]
unsafe extern "C" fn fops_compat_ioctl<T: MiscDevice>(
file: *mut bindings::file,
cmd: c_uint,
arg: c_ulong,
) -> c_long {
// SAFETY: The compat ioctl call of a file can access the private data.
let private = unsafe { (*file).private_data };
// SAFETY: Ioctl calls can borrow the private data of the file.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
// SAFETY:
// * The file is valid for the duration of this call.
// * There is no active fdget_pos region on the file on this thread.
let file = unsafe { File::from_raw_file(file) };
match T::compat_ioctl(device, file, cmd, arg as usize) {
Ok(ret) => ret as c_long,
Err(err) => err.to_errno() as c_long,
}
}
/// # Safety
///
/// - `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
/// - `seq_file` must be a valid `struct seq_file` that we can write to.
unsafe extern "C" fn fops_show_fdinfo<T: MiscDevice>(
seq_file: *mut bindings::seq_file,
file: *mut bindings::file,
) {
// SAFETY: The release call of a file owns the private data.
let private = unsafe { (*file).private_data };
// SAFETY: Ioctl calls can borrow the private data of the file.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
// SAFETY:
// * The file is valid for the duration of this call.
// * There is no active fdget_pos region on the file on this thread.
let file = unsafe { File::from_raw_file(file) };
// SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which
// this method is called.
let m = unsafe { SeqFile::from_raw(seq_file) };
T::show_fdinfo(device, m, file);
}
+20 -15
View File
@@ -10,6 +10,7 @@
//! control what happens when userspace reads or writes to that region of memory.
//!
//! C header: [`include/linux/mm.h`](srctree/include/linux/mm.h)
#![cfg(CONFIG_MMU)]
use crate::{
bindings,
@@ -18,7 +19,7 @@ use crate::{
use core::{ops::Deref, ptr::NonNull};
pub mod virt;
use virt::VmAreaRef;
use virt::VmaRef;
/// A wrapper for the kernel's `struct mm_struct`.
///
@@ -131,11 +132,13 @@ unsafe impl Sync for MmWithUserAsync {}
// SAFETY: By the type invariants, this type is always refcounted.
unsafe impl AlwaysRefCounted for MmWithUserAsync {
#[inline]
fn inc_ref(&self) {
// SAFETY: The pointer is valid since self is a reference.
unsafe { bindings::mmget(self.as_raw()) };
}
#[inline]
unsafe fn dec_ref(obj: NonNull<Self>) {
// SAFETY: The caller is giving up their refcount.
unsafe { bindings::mmput_async(obj.cast().as_ptr()) };
@@ -222,12 +225,12 @@ impl MmWithUser {
{
// SAFETY: Calling `bindings::lock_vma_under_rcu` is always okay given an mm where
// `mm_users` is non-zero.
let vma = unsafe { bindings::lock_vma_under_rcu(self.as_raw(), vma_addr as _) };
let vma = unsafe { bindings::lock_vma_under_rcu(self.as_raw(), vma_addr) };
if !vma.is_null() {
return Some(VmaReadGuard {
// SAFETY: If `lock_vma_under_rcu` returns a non-null ptr, then it points at a
// valid vma. The vma is stable for as long as the vma read lock is held.
vma: unsafe { VmAreaRef::from_raw(vma) },
vma: unsafe { VmaRef::from_raw(vma) },
_nts: NotThreadSafe,
});
}
@@ -285,18 +288,20 @@ pub struct MmapReadGuard<'a> {
impl<'a> MmapReadGuard<'a> {
/// Look up a vma at the given address.
#[inline]
pub fn vma_lookup(&self, vma_addr: usize) -> Option<&virt::VmAreaRef> {
// SAFETY: We hold a reference to the mm, so the pointer must be valid. Any value is okay
// for `vma_addr`.
let vma = unsafe { bindings::vma_lookup(self.mm.as_raw(), vma_addr as _) };
pub fn vma_lookup(&self, vma_addr: usize) -> Option<&virt::VmaRef> {
// SAFETY: By the type invariants we hold the mmap read guard, so we can safely call this
// method. Any value is okay for `vma_addr`.
let vma = unsafe { bindings::vma_lookup(self.mm.as_raw(), vma_addr) };
if vma.is_null() {
None
} else {
// SAFETY: We just checked that a vma was found, so the pointer is valid. Furthermore,
// the returned area will borrow from this read lock guard, so it can only be used
// while the mmap read lock is still held.
unsafe { Some(virt::VmAreaRef::from_raw(vma)) }
// SAFETY: We just checked that a vma was found, so the pointer references a valid vma.
//
// Furthermore, the returned vma is still under the protection of the read lock guard
// and can be used while the mmap read lock is still held. That the vma is not used
// after the MmapReadGuard gets dropped is enforced by the borrow-checker.
unsafe { Some(virt::VmaRef::from_raw(vma)) }
}
}
}
@@ -315,17 +320,17 @@ impl Drop for MmapReadGuard<'_> {
///
/// This `VmaReadGuard` guard owns the vma read lock.
pub struct VmaReadGuard<'a> {
vma: &'a VmAreaRef,
vma: &'a VmaRef,
// `vma_end_read` must be called on the same thread as where the lock was taken
_nts: NotThreadSafe,
}
// Make all `VmAreaRef` methods available on `VmaReadGuard`.
// Make all `VmaRef` methods available on `VmaReadGuard`.
impl Deref for VmaReadGuard<'_> {
type Target = VmAreaRef;
type Target = VmaRef;
#[inline]
fn deref(&self) -> &VmAreaRef {
fn deref(&self) -> &VmaRef {
self.vma
}
}
+34 -39
View File
@@ -10,7 +10,7 @@
//!
//! The module has several different Rust types that all correspond to the C type called
//! `vm_area_struct`. The different structs represent what kind of access you have to the VMA, e.g.
//! [`VmAreaRef`] is used when you hold the mmap or vma read lock. Using the appropriate struct
//! [`VmaRef`] is used when you hold the mmap or vma read lock. Using the appropriate struct
//! ensures that you can't, for example, accidentally call a function that requires holding the
//! write lock when you only hold the read lock.
@@ -32,13 +32,13 @@ use core::ops::Deref;
///
/// The caller must hold the mmap read lock or the vma read lock.
#[repr(transparent)]
pub struct VmAreaRef {
pub struct VmaRef {
vma: Opaque<bindings::vm_area_struct>,
}
// Methods you can call when holding the mmap or vma read lock (or stronger). They must be usable
// no matter what the vma flags are.
impl VmAreaRef {
impl VmaRef {
/// Access a virtual memory area given a raw pointer.
///
/// # Safety
@@ -73,7 +73,7 @@ impl VmAreaRef {
pub fn flags(&self) -> vm_flags_t {
// SAFETY: By the type invariants, the caller holds at least the mmap read lock, so this
// access is not a data race.
unsafe { (*self.as_ptr()).__bindgen_anon_2.vm_flags as _ }
unsafe { (*self.as_ptr()).__bindgen_anon_2.vm_flags }
}
/// Returns the (inclusive) start address of the virtual memory area.
@@ -81,7 +81,7 @@ impl VmAreaRef {
pub fn start(&self) -> usize {
// SAFETY: By the type invariants, the caller holds at least the mmap read lock, so this
// access is not a data race.
unsafe { (*self.as_ptr()).__bindgen_anon_1.__bindgen_anon_1.vm_start as _ }
unsafe { (*self.as_ptr()).__bindgen_anon_1.__bindgen_anon_1.vm_start }
}
/// Returns the (exclusive) end address of the virtual memory area.
@@ -89,7 +89,7 @@ impl VmAreaRef {
pub fn end(&self) -> usize {
// SAFETY: By the type invariants, the caller holds at least the mmap read lock, so this
// access is not a data race.
unsafe { (*self.as_ptr()).__bindgen_anon_1.__bindgen_anon_1.vm_end as _ }
unsafe { (*self.as_ptr()).__bindgen_anon_1.__bindgen_anon_1.vm_end }
}
/// Zap pages in the given page range.
@@ -124,27 +124,22 @@ impl VmAreaRef {
// sufficient for this method call. This method has no requirements on the vma flags. The
// address range is checked to be within the vma.
unsafe {
bindings::zap_page_range_single(
self.as_ptr(),
address as _,
size as _,
core::ptr::null_mut(),
)
bindings::zap_page_range_single(self.as_ptr(), address, size, core::ptr::null_mut())
};
}
/// If the [`VM_MIXEDMAP`] flag is set, returns a [`VmAreaMixedMap`] to this VMA, otherwise
/// If the [`VM_MIXEDMAP`] flag is set, returns a [`VmaMixedMap`] to this VMA, otherwise
/// returns `None`.
///
/// This can be used to access methods that require [`VM_MIXEDMAP`] to be set.
///
/// [`VM_MIXEDMAP`]: flags::MIXEDMAP
#[inline]
pub fn as_mixedmap_vma(&self) -> Option<&VmAreaMixedMap> {
pub fn as_mixedmap_vma(&self) -> Option<&VmaMixedMap> {
if self.flags() & flags::MIXEDMAP != 0 {
// SAFETY: We just checked that `VM_MIXEDMAP` is set. All other requirements are
// satisfied by the type invariants of `VmAreaRef`.
Some(unsafe { VmAreaMixedMap::from_raw(self.as_ptr()) })
// satisfied by the type invariants of `VmaRef`.
Some(unsafe { VmaMixedMap::from_raw(self.as_ptr()) })
} else {
None
}
@@ -155,7 +150,7 @@ impl VmAreaRef {
///
/// It represents an area of virtual memory.
///
/// This struct is identical to [`VmAreaRef`] except that it must only be used when the
/// This struct is identical to [`VmaRef`] except that it must only be used when the
/// [`VM_MIXEDMAP`] flag is set on the vma.
///
/// # Invariants
@@ -165,21 +160,21 @@ impl VmAreaRef {
///
/// [`VM_MIXEDMAP`]: flags::MIXEDMAP
#[repr(transparent)]
pub struct VmAreaMixedMap {
vma: VmAreaRef,
pub struct VmaMixedMap {
vma: VmaRef,
}
// Make all `VmAreaRef` methods available on `VmAreaMixedMap`.
impl Deref for VmAreaMixedMap {
type Target = VmAreaRef;
// Make all `VmaRef` methods available on `VmaMixedMap`.
impl Deref for VmaMixedMap {
type Target = VmaRef;
#[inline]
fn deref(&self) -> &VmAreaRef {
fn deref(&self) -> &VmaRef {
&self.vma
}
}
impl VmAreaMixedMap {
impl VmaMixedMap {
/// Access a virtual memory area given a raw pointer.
///
/// # Safety
@@ -199,36 +194,36 @@ impl VmAreaMixedMap {
pub fn vm_insert_page(&self, address: usize, page: &Page) -> Result {
// SAFETY: By the type invariant of `Self` caller has read access and has verified that
// `VM_MIXEDMAP` is set. By invariant on `Page` the page has order 0.
to_result(unsafe { bindings::vm_insert_page(self.as_ptr(), address as _, page.as_ptr()) })
to_result(unsafe { bindings::vm_insert_page(self.as_ptr(), address, page.as_ptr()) })
}
}
/// A configuration object for setting up a VMA in an `f_ops->mmap()` hook.
///
/// The `f_ops->mmap()` hook is called when a new VMA is being created, and the hook is able to
/// configure the VMA in various ways to fit the driver that owns it. Using `VmAreaNew` indicates
/// that you are allowed to perform operations on the VMA that can only be performed before the VMA
/// is fully initialized.
/// configure the VMA in various ways to fit the driver that owns it. Using `VmaNew` indicates that
/// you are allowed to perform operations on the VMA that can only be performed before the VMA is
/// fully initialized.
///
/// # Invariants
///
/// For the duration of 'a, the referenced vma must be undergoing initialization in an
/// `f_ops->mmap()` hook.
pub struct VmAreaNew {
vma: VmAreaRef,
pub struct VmaNew {
vma: VmaRef,
}
// Make all `VmAreaRef` methods available on `VmAreaNew`.
impl Deref for VmAreaNew {
type Target = VmAreaRef;
// Make all `VmaRef` methods available on `VmaNew`.
impl Deref for VmaNew {
type Target = VmaRef;
#[inline]
fn deref(&self) -> &VmAreaRef {
fn deref(&self) -> &VmaRef {
&self.vma
}
}
impl VmAreaNew {
impl VmaNew {
/// Access a virtual memory area given a raw pointer.
///
/// # Safety
@@ -252,7 +247,7 @@ impl VmAreaNew {
flags &= !unset;
// SAFETY: This is not a data race: the vma is undergoing initial setup, so it's not yet
// shared. Additionally, `VmAreaNew` is `!Sync`, so it cannot be used to write in parallel.
// shared. Additionally, `VmaNew` is `!Sync`, so it cannot be used to write in parallel.
// The caller promises that this does not set the flags to an invalid value.
unsafe { (*self.as_ptr()).__bindgen_anon_2.__vm_flags = flags };
}
@@ -262,13 +257,13 @@ impl VmAreaNew {
/// This enables the vma to contain both `struct page` and pure PFN pages. Returns a reference
/// that can be used to call `vm_insert_page` on the vma.
#[inline]
pub fn set_mixedmap(&self) -> &VmAreaMixedMap {
pub fn set_mixedmap(&self) -> &VmaMixedMap {
// SAFETY: We don't yet provide a way to set VM_PFNMAP, so this cannot put the flags in an
// invalid state.
unsafe { self.update_flags(flags::MIXEDMAP, 0) };
// SAFETY: We just set `VM_MIXEDMAP` on the vma.
unsafe { VmAreaMixedMap::from_raw(self.vma.as_ptr()) }
unsafe { VmaMixedMap::from_raw(self.vma.as_ptr()) }
}
/// Set the `VM_IO` flag on this vma.
@@ -391,7 +386,7 @@ impl VmAreaNew {
#[doc(inline)]
pub use bindings::vm_flags_t;
/// All possible flags for [`VmAreaRef`].
/// All possible flags for [`VmaRef`].
pub mod flags {
use super::vm_flags_t;
use crate::bindings;
+68
View File
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2024 Christian Brauner <brauner@kernel.org>
//! Pid namespaces.
//!
//! C header: [`include/linux/pid_namespace.h`](srctree/include/linux/pid_namespace.h) and
//! [`include/linux/pid.h`](srctree/include/linux/pid.h)
use crate::{
bindings,
types::{AlwaysRefCounted, Opaque},
};
use core::ptr;
/// Wraps the kernel's `struct pid_namespace`. Thread safe.
///
/// This structure represents the Rust abstraction for a C `struct pid_namespace`. This
/// implementation abstracts the usage of an already existing C `struct pid_namespace` within Rust
/// code that we get passed from the C side.
#[repr(transparent)]
pub struct PidNamespace {
inner: Opaque<bindings::pid_namespace>,
}
impl PidNamespace {
/// Returns a raw pointer to the inner C struct.
#[inline]
pub fn as_ptr(&self) -> *mut bindings::pid_namespace {
self.inner.get()
}
/// Creates a reference to a [`PidNamespace`] from a valid pointer.
///
/// # Safety
///
/// The caller must ensure that `ptr` is valid and remains valid for the lifetime of the
/// returned [`PidNamespace`] reference.
pub unsafe fn from_ptr<'a>(ptr: *const bindings::pid_namespace) -> &'a Self {
// SAFETY: The safety requirements guarantee the validity of the dereference, while the
// `PidNamespace` type being transparent makes the cast ok.
unsafe { &*ptr.cast() }
}
}
// SAFETY: Instances of `PidNamespace` are always reference-counted.
unsafe impl AlwaysRefCounted for PidNamespace {
#[inline]
fn inc_ref(&self) {
// SAFETY: The existence of a shared reference means that the refcount is nonzero.
unsafe { bindings::get_pid_ns(self.as_ptr()) };
}
#[inline]
unsafe fn dec_ref(obj: ptr::NonNull<PidNamespace>) {
// SAFETY: The safety requirements guarantee that the refcount is non-zero.
unsafe { bindings::put_pid_ns(obj.cast().as_ptr()) }
}
}
// SAFETY:
// - `PidNamespace::dec_ref` can be called from any thread.
// - It is okay to send ownership of `PidNamespace` across thread boundaries.
unsafe impl Send for PidNamespace {}
// SAFETY: It's OK to access `PidNamespace` through shared references from other threads because
// we're either accessing properties that don't change or that are properly synchronised by C code.
unsafe impl Sync for PidNamespace {}
+60 -6
View File
@@ -8,7 +8,8 @@ use crate::{
bindings,
ffi::{c_int, c_long, c_uint},
mm::MmWithUser,
types::{NotThreadSafe, Opaque},
pid_namespace::PidNamespace,
types::{ARef, NotThreadSafe, Opaque},
};
use core::{
cmp::{Eq, PartialEq},
@@ -238,11 +239,32 @@ impl Task {
unsafe { bindings::signal_pending(self.as_ptr()) != 0 }
}
/// Returns the given task's pid in the current pid namespace.
pub fn pid_in_current_ns(&self) -> Pid {
// SAFETY: It's valid to pass a null pointer as the namespace (defaults to current
// namespace). The task pointer is also valid.
unsafe { bindings::task_tgid_nr_ns(self.as_ptr(), ptr::null_mut()) }
/// Returns task's pid namespace with elevated reference count
pub fn get_pid_ns(&self) -> Option<ARef<PidNamespace>> {
// SAFETY: By the type invariant, we know that `self.0` is valid.
let ptr = unsafe { bindings::task_get_pid_ns(self.0.get()) };
if ptr.is_null() {
None
} else {
// SAFETY: `ptr` is valid by the safety requirements of this function. And we own a
// reference count via `task_get_pid_ns()`.
// CAST: `Self` is a `repr(transparent)` wrapper around `bindings::pid_namespace`.
Some(unsafe { ARef::from_raw(ptr::NonNull::new_unchecked(ptr.cast::<PidNamespace>())) })
}
}
/// Returns the given task's pid in the provided pid namespace.
#[doc(alias = "task_tgid_nr_ns")]
pub fn tgid_nr_ns(&self, pidns: Option<&PidNamespace>) -> Pid {
let pidns = match pidns {
Some(pidns) => pidns.as_ptr(),
None => core::ptr::null_mut(),
};
// SAFETY: By the type invariant, we know that `self.0` is valid. We received a valid
// PidNamespace that we can use as a pointer or we received an empty PidNamespace and
// thus pass a null pointer. The underlying C function is safe to be used with NULL
// pointers.
unsafe { bindings::task_tgid_nr_ns(self.0.get(), pidns) }
}
/// Wakes up the task.
@@ -347,6 +369,38 @@ impl CurrentTask {
// scope is ended with `kthread_unuse_mm()`.
Some(unsafe { MmWithUser::from_raw(mm) })
}
/// Access the pid namespace of the current task.
///
/// This function does not touch the refcount of the namespace or use RCU protection.
///
/// To access the pid namespace of another task, see [`Task::get_pid_ns`].
#[doc(alias = "task_active_pid_ns")]
#[inline]
pub fn active_pid_ns(&self) -> Option<&PidNamespace> {
// SAFETY: It is safe to call `task_active_pid_ns` without RCU protection when calling it
// on the current task.
let active_ns = unsafe { bindings::task_active_pid_ns(self.as_ptr()) };
if active_ns.is_null() {
return None;
}
// The lifetime of `PidNamespace` is bound to `Task` and `struct pid`.
//
// The `PidNamespace` of a `Task` doesn't ever change once the `Task` is alive.
//
// From system call context retrieving the `PidNamespace` for the current task is always
// safe and requires neither RCU locking nor a reference count to be held. Retrieving the
// `PidNamespace` after `release_task()` for current will return `NULL` but no codepath
// like that is exposed to Rust.
//
// SAFETY: If `current`'s pid ns is non-null, then it references a valid pid ns.
// Furthermore, the returned `&PidNamespace` borrows from this `CurrentTask`, so it cannot
// escape the scope in which the current pointer was obtained, e.g. it cannot live past a
// `release_task()` call.
Some(unsafe { PidNamespace::from_ptr(active_ns) })
}
}
// SAFETY: The type invariants guarantee that `Task` is always refcounted.