zx/info.rs
1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Type-safe bindings for Zircon object information.
6
7use crate::{ok, sys, HandleRef, Status};
8use std::mem::MaybeUninit;
9use std::ops::Deref;
10use zerocopy::{FromBytes, Immutable};
11
12// Tuning constants for get_info_vec(). pub(crate) to support unit tests.
13pub(crate) const INFO_VEC_SIZE_INITIAL: usize = 16;
14const INFO_VEC_SIZE_PAD: usize = 2;
15
16#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
17#[repr(transparent)]
18pub struct Topic(sys::zx_object_info_topic_t);
19
20impl Deref for Topic {
21 type Target = sys::zx_object_info_topic_t;
22
23 fn deref(&self) -> &Self::Target {
24 &self.0
25 }
26}
27
28/// A query to get info about a zircon object.
29///
30/// # Safety
31///
32/// `InfoTy` must be the same size as what the kernel expects to return for the provided topic.
33pub(crate) unsafe trait ObjectQuery {
34 /// A `Topic` identifying this query.
35 const TOPIC: Topic;
36 /// The datatype returned by querying for Self::TOPIC.
37 type InfoTy: FromBytes + Immutable;
38}
39
40assoc_values!(Topic, [
41 NONE = sys::ZX_INFO_NONE;
42 HANDLE_VALID = sys::ZX_INFO_HANDLE_VALID;
43 HANDLE_BASIC = sys::ZX_INFO_HANDLE_BASIC;
44 PROCESS = sys::ZX_INFO_PROCESS;
45 PROCESS_THREADS = sys::ZX_INFO_PROCESS_THREADS;
46 VMAR = sys::ZX_INFO_VMAR;
47 VMAR_MAPS = sys::ZX_INFO_VMAR_MAPS;
48 JOB_CHILDREN = sys::ZX_INFO_JOB_CHILDREN;
49 JOB_PROCESSES = sys::ZX_INFO_JOB_PROCESSES;
50 THREAD = sys::ZX_INFO_THREAD;
51 THREAD_EXCEPTION_REPORT = sys::ZX_INFO_THREAD_EXCEPTION_REPORT;
52 TASK_STATS = sys::ZX_INFO_TASK_STATS;
53 TASK_RUNTIME = sys::ZX_INFO_TASK_RUNTIME;
54 PROCESS_MAPS = sys::ZX_INFO_PROCESS_MAPS;
55 PROCESS_VMOS = sys::ZX_INFO_PROCESS_VMOS;
56 THREAD_STATS = sys::ZX_INFO_THREAD_STATS;
57 CPU_STATS = sys::ZX_INFO_CPU_STATS;
58 KMEM_STATS = sys::ZX_INFO_KMEM_STATS;
59 KMEM_STATS_EXTENDED = sys::ZX_INFO_KMEM_STATS_EXTENDED;
60 KMEM_STATS_COMPRESSION = sys::ZX_INFO_KMEM_STATS_COMPRESSION;
61 RESOURCE = sys::ZX_INFO_RESOURCE;
62 HANDLE_COUNT = sys::ZX_INFO_HANDLE_COUNT;
63 BTI = sys::ZX_INFO_BTI;
64 PROCESS_HANDLE_STATS = sys::ZX_INFO_PROCESS_HANDLE_STATS;
65 SOCKET = sys::ZX_INFO_SOCKET;
66 TIMER = sys::ZX_INFO_TIMER;
67 VMO = sys::ZX_INFO_VMO;
68 JOB = sys::ZX_INFO_JOB;
69 IOB = sys::ZX_INFO_IOB;
70 IOB_REGIONS = sys::ZX_INFO_IOB_REGIONS;
71 MEMORY_STALL = sys::ZX_INFO_MEMORY_STALL;
72 CLOCK_MAPPED_SIZE = sys::ZX_INFO_CLOCK_MAPPED_SIZE;
73]);
74
75/// Query information about a zircon object. Returns a valid slice and any remaining capacity on
76/// success, along with a count of how many infos the kernel had available.
77pub(crate) fn object_get_info<'a, Q: ObjectQuery>(
78 handle: HandleRef<'_>,
79 out: &'a mut [MaybeUninit<Q::InfoTy>],
80) -> Result<(&'a mut [Q::InfoTy], &'a mut [MaybeUninit<Q::InfoTy>], usize), Status>
81where
82 Q::InfoTy: FromBytes + Immutable,
83{
84 let mut actual = 0;
85 let mut avail = 0;
86
87 // SAFETY: The slice pointer is known valid to write to for `size_of_val` because it came from
88 // a mutable reference.
89 let status = unsafe {
90 sys::zx_object_get_info(
91 handle.raw_handle(),
92 *Q::TOPIC,
93 out.as_mut_ptr().cast::<u8>(),
94 std::mem::size_of_val(out),
95 &mut actual,
96 &mut avail,
97 )
98 };
99 ok(status)?;
100
101 let (initialized, uninit) = out.split_at_mut(actual);
102
103 // TODO(https://fxbug.dev/352398385) switch to MaybeUninit::slice_assume_init_mut
104 // SAFETY: these values have been initialized by the kernel and implement the right zerocopy
105 // traits to be instantiated from arbitrary bytes.
106 let initialized: &mut [Q::InfoTy] = unsafe {
107 std::slice::from_raw_parts_mut(
108 initialized.as_mut_ptr().cast::<Q::InfoTy>(),
109 initialized.len(),
110 )
111 };
112
113 Ok((initialized, uninit, avail))
114}
115
116/// Query information about a zircon object, expecting only a single info in the return.
117pub(crate) fn object_get_info_single<Q: ObjectQuery>(
118 handle: HandleRef<'_>,
119) -> Result<Q::InfoTy, Status>
120where
121 Q::InfoTy: Copy + FromBytes + Immutable,
122{
123 let mut info = MaybeUninit::<Q::InfoTy>::uninit();
124 let (info, _, _) = object_get_info::<Q>(handle, std::slice::from_mut(&mut info))?;
125 Ok(info[0])
126}
127
128/// Query multiple records of information about a zircon object.
129/// Returns a vec of Q::InfoTy on success.
130/// Intended for calls that return multiple small objects.
131pub(crate) fn object_get_info_vec<Q: ObjectQuery>(
132 handle: HandleRef<'_>,
133) -> Result<Vec<Q::InfoTy>, Status> {
134 // Start with a few slots
135 let mut out = Vec::<Q::InfoTy>::with_capacity(INFO_VEC_SIZE_INITIAL);
136 loop {
137 let (init, _uninit, avail) =
138 object_get_info::<Q>(handle.clone(), out.spare_capacity_mut())?;
139 let num_initialized = init.len();
140 if num_initialized == avail {
141 // SAFETY: the kernel has initialized all of these values.
142 unsafe { out.set_len(num_initialized) };
143 return Ok(out);
144 } else {
145 // The number of records may increase between retries; reserve space for that.
146 // TODO(https://fxbug.dev/384531846) grow more conservatively
147 let needed_space = avail * INFO_VEC_SIZE_PAD;
148 if let Some(to_grow) = needed_space.checked_sub(out.capacity()) {
149 out.reserve_exact(to_grow);
150 }
151
152 // We may ask the kernel to copy more than a page in the next iteration of this loop, so
153 // prefault each of the pages in the region.
154 //
155 // Currently Zircon has a very slow path when performing large usercopies into freshly
156 // allocated buffers. In practice our large heap allocated buffers are typically newly
157 // mapped and do not yet have any pages faulted in. Unlike a copy performed by
158 // userspace, Zircon itself has to handle any page faults and in order to avoid
159 // deadlocking with user pagers (among other issues), it will drop locks it holds,
160 // service the page fault, and then reacquire the locks (see linked bug below).
161 //
162 // In order to avoid hitting this slow path, we want to ensure that all of the pages of
163 // our Vec have already been faulted in before we hand off to the kernel to write data
164 // into them. Ideally the kernel would handle this for us but in the meantime we can
165 // shave a lot of time from the heaviest zx_object_get_info calls by faulting the pages
166 // ourselves.
167 //
168 // We only want to do this when the bindings themselves are responsible for creating the
169 // vector, because callers who manage their own buffer for the syscall may be doing
170 // their own page management for the buffer and we don't want to penalize them.
171 // TODO(https://fxbug.dev/383401884) remove once zircon prefaults get_info usercopies
172 // TODO(https://fxbug.dev/384941113) consider zx_vmar_op_range on root vmar instead
173 let maybe_unfaulted = out.spare_capacity_mut();
174
175 // SAFETY: zerocopy::FromBytes means it's OK to write arbitrary bytes to the slice.
176 // TODO(https://github.com/rust-lang/rust/issues/93092) use MaybeUninit::slice_as_bytes_mut
177 let maybe_unfaulted_bytes: &mut [MaybeUninit<u8>] = unsafe {
178 std::slice::from_raw_parts_mut(
179 maybe_unfaulted.as_mut_ptr().cast::<MaybeUninit<u8>>(),
180 std::mem::size_of_val(maybe_unfaulted),
181 )
182 };
183
184 // chunks_mut doesn't give us page alignment but that doesn't matter for pre-faulting.
185 for page in maybe_unfaulted_bytes.chunks_mut(crate::system_get_page_size() as usize) {
186 // This writes a single byte to each page to avoid unnecessary memory traffic. A
187 // non-zero byte is written so that these pages won't get picked up by Zircon's
188 // zero page scanner.
189 page[0] = MaybeUninit::new(1u8);
190 }
191 }
192 }
193}