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: *21ed84930cUPSTREAM: Revert "usb: xhci: Implement xhci_handshake_check_state() helper" *5b3ae3bcbeBACKPORT: usb: xhci: Skip xhci_reset in xhci_resume if xhci is being removed *5c72e9fabaANDROID: rust_binder: adjust errors from death notifications *9e02edea7fANDROID: rust_binder: use u64 for death cookie *4317f0aeffANDROID: f2fs: fixup ABI break due to reserved_pin_section *25bdb4a624Revert "ANDROID: ABI: update symbol list for honor" *a76eb2b67bANDROID: GKI: Update oplus symbol list *6222007a04ANDROID: mm/readahead: add for bypass high order allocation *659d7bb454ANDROID: ABI: Update symbol list for exynos *26937a37f5ANDROID: MODVERSIONS: hide type definition in drivers/usb/core/driver.c *8760b6e4f5ANDROID: usb: Add vendor hook for usb suspend and resume *da662aecc8FROMLIST: KVM: Avoid synchronize_srcu() in kvm_io_bus_register_dev() *4be05c6524FROMLIST: KVM: arm64: vgic: Explicitly implement vgic_dist::ready ordering *d6045efc66FROMLIST: KVM: arm64: vgic-init: Remove vgic_ready() macro *f06dd0cd35ANDROID: rust_binder: release threads before refs *5bbd30a60bANDROID: ABI: Update pixel symbol list *bafbebf2abANDROID: GKI: Update symbol list for xiaomi *b7b130b7ccANDROID: export folio_deactivate() for GKI purpose. *41f730f9c4ANDROID: GKI: update exynos symbol list *766ecae19fUPSTREAM: xhci: dbctty: disable ECHO flag by default *8ea40f5243ANDROID: GKI: Update xiaomi symbol list. *5594b4731dANDROID: vendor_hooks: export tracepoint symbols *0d4cc1daffANDROID: KVM: arm64: Don't update IOMMU under memory pressure *672185e575ANDROID: iommu/iommu: Handle multi-page deferred sg mappings *740d42d181ANDROID: vendor_hooks: Add vendor_hook in futex to fix the OEM scheduling priority bug *6eb6f346acANDROID: ABI: Update symbol list for mtk *c302079179ANDROID: vendor_hooks: Add vendor hook for GenieZone demand paging *5c1cddc983ANDROID: vendor_hooks: Add vendor hook for GenieZone para-virtualization *d893caf112ANDROID: ashmem_rust: Add support for retrieving an ashmem area's vmfile *0be74214c0ANDROID: ashmem_rust: Add support for querying the size of an ashmem region *eb50f663c4ANDROID: ashmem_rust: Add support for providing an ashmem region's name *6bdbae6ea9ANDROID: ashmem_rust: Add is_ashmem_file() *0d890f867eANDROID: ABI: update symbol list for honor *12727f8a4bFROMGIT: f2fs: introduce reserved_pin_section sysfs entry *286cd9d628ANDROID: GKI: Update RTK STB KMI symbol list *7b4f7682b5ANDROID: GKI: Update symbol list for Amlogic *862ce4b2c4ANDROID: KVM: arm64: iommu: Fix power tracking *61184996a8ANDROID: drivers/iommu: Fix return value in iommu_map_sg *acad0cd51dANDROID: ABI: update symbol list for galaxy *393dbad32cANDROID: vendor_hook: add condition to call for freezing fail *b62fe47ba2ANDROID: fix ashmem_rust return EINVAL bug in ashmem_rust.rs *a7e1300b95ANDROID: Revert "cpufreq: Avoid using inconsistent policy->min and policy->max" *15d2fe0544ANDROID: qcom: Update the ABI symbol list *f6ca783ba2UPSTREAM: scsi: ufs: qcom: Check gear against max gear in vop freq_to_gear() *237708e9d3ANDROID: GKI: Update symbols list file for honor White list the vm_normal_folio_pmd *f18e354aa9ANDROID: mm: export vm_normal_folio_pmd to allow vendors to implement simplified smaps *c181c478b0ANDROID: vendor_hooks: add hook to record slab free *d2e452e197ANDROID: Build fixups with PROXY_EXEC v18 + !CONFIG_SMP *4f9e4406e4ANDROID: Update proxy-exec logic from v14 to v18 *3fa8dabe1aANDROID: GKI: update asr symbols list *94310b3f77ANDROID: Add the dma header to aarch64 allowlist *880d6538c5UPSTREAM: usb: gadget: u_serial: Fix race condition in TTY wakeup *b115bf2302ANDROID: ABI: Update symbol list for mtk *e87018c5f9FROMGIT: sched/deadline: Fix dl_server runtime calculation formula *e2bf362ee2FROMGIT: sched/core: Fix migrate_swap() vs. hotplug *06ca12d7d2ANDROID: GKI: update the ABI symbol list *55972ed83aANDROID: Fixup init_user_ns CRC change *4e873ad607ANDROID: user: Add vendor hook to user for GKI purpose *a097cd9c30ANDROID: export find_user() for GKI purpose. *85b8233f7eANDROID: rust_binder: use euid from the task *969c904869ANDROID: ashmem: rename VmAreaNew->VmaNew *2ab3e5f283ANDROID: rust_binder: rename VmAreaNew->VmaNew *2ef75ab83aANDROID: rust_binder: use tgid_nr_ns for getting pid *6a2be11026UPSTREAM: task: rust: rework how current is accessed *602e2300deUPSTREAM: rust: add PidNamespace *12dfc1d9cbUPSTREAM: rust: miscdevice: add mmap support *8e67cb756fUPSTREAM: mm: rust: add VmaNew for f_ops->mmap() *bd140ddf75UPSTREAM: mm: rust: add mmput_async support *0c50773076UPSTREAM: mm: rust: add lock_vma_under_rcu *0b5465bb31UPSTREAM: mm: rust: add vm_insert_page *d7f52612c5UPSTREAM: mm: rust: add vm_area_struct methods that require read access *f03d4f7490UPSTREAM: mm: rust: add abstraction for struct mm_struct *2ef6dbc73eBACKPORT: rust: miscdevice: change how f_ops vtable is constructed *1acd3b312fRevert "FROMLIST: mm: rust: add abstraction for struct mm_struct" *a012c15566Revert "FROMLIST: mm: rust: add vm_area_struct methods that require read access" *3be00a9bf8Revert "FROMLIST: mm: rust: add vm_insert_page" *3aed88205eRevert "FROMLIST: mm: rust: add lock_vma_under_rcu" *a121b6e72fRevert "FROMLIST: mm: rust: add mmput_async support" *9248564a81Revert "FROMLIST: mm: rust: add VmAreaNew for f_ops->mmap()" *6de3ace5b5Revert "FROMLIST: rust: miscdevice: add mmap support" *b7f54dd23bRevert "BACKPORT: FROMLIST: task: rust: rework how current is accessed" *5913c80b22ANDROID: iommu/arm-smmu-v3-kvm: Fix idmap free_leaf *c40c54e669UPSTREAM: erofs: impersonate the opener's credentials when accessing backing file *4d0200d0a9BACKPORT: erofs: add 'fsoffset' mount option to specify filesystem offset *399deda7b5ANDROID: scsi: ufs: add UFSHCD_ANDROID_QUIRK_NO_IS_READ_ON_H8 *f6b1ab83f6ANDROID: rust_binder: remove binder_logs/procs/pid immediately *dd35623c83ANDROID: ABI: update symbol list for mtktv *58beebb30fFROMLIST: fuse: give wakeup hints to the scheduler *0f917e4066ANDROID: virt: gunyah: Replace arm_smccc_1_1_smc with arm_smccc_1_1_invoke *33429dd323UPSTREAM: posix-cpu-timers: fix race between handle_posix_cpu_timers() and posix_cpu_timer_del() *6483832947ANDROID: GKI: Update symbol list file for xiaomi *668635cd34UPSTREAM: 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:
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user