mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 14:53:58 -04:00
Pull drm updates from Dave Airlie:
"Highlights:
- new DRM RAS infrastructure using netlink
- amdgpu: enable DC on CIK APUs, and more IP enablement, and more
user queue work
- xe: purgeable BO support, and new hw enablement
- dma-buf : add revocable operations
Full summary:
mm:
- two-pass MMU interval notifiers
- add gpu active/reclaim per-node stat counters
math:
- provide __KERNEL_DIV_ROUND_CLOSEST() in UAPI
- implement DIV_ROUND_CLOSEST() with __KERNEL_DIV_ROUND_CLOSEST()
rust:
- shared tag with driver-core: register macro and io infra
- core: rework DMA coherent API
- core: add interop::list to interop with C linked lists
- core: add more num::Bounded operations
- core: enable generic_arg_infer and add EMSGSIZE
- workqueue: add ARef<T> support for work and delayed work
- add GPU buddy allocator abstraction
- add DRM shmem GEM helper abstraction
- allow drm:::Device to dispatch work and delayed work items
to driver private data
- add dma_resv_lock helper and raw accessors
core:
- introduce DRM RAS infrastructure over netlink
- add connector panel_type property
- fourcc: add ARM interleaved 64k modifier
- colorop: add destroy helper
- suballoc: split into alloc and init helpers
- mode: provide DRM_ARGB_GET*() macros for reading color components
edid:
- provide drm_output_color_Format
dma-buf:
- provide revoke mechanism for shared buffers
- rename move_notify to invalidate_mappings
- always enable move_notify
- protect dma_fence_ops with RCU and improve locking
- clean pages with helpers
atomic:
- allocate drm_private_state via callback
- helper: use system_percpu_wq
buddy:
- make buddy allocator available to gpu level
- add kernel-doc for buddy allocator
- improve aligned allocation
ttm:
- fix fence signalling
- improve tests and docs
- improve handling of gfp_retry_mayfail
- use per-node stat counters to track memory allocations
- port pool to use list_lru
- drop NUMA specific pools
- make pool shrinker numa aware
- track allocated pages per numa node
coreboot:
- cleanup coreboot framebuffer support
sched:
- fix race condition in drm_sched_fini
pagemap:
- enable THP support
- pass pagemap_addr by reference
gem-shmem:
- Track page accessed/dirty status across mmap/vmap
gpusvm:
- reenable device to device migration
- fix unbalanced unclock
bridge:
- anx7625: Support USB-C plus DT bindings
- connector: Fix EDID detection
- dw-hdmi-qp: Support Vendor-Specfic and SDP Infoframes; improve
others
- fsl-ldb: Fix visual artifacts plus related DT property
'enable-termination-resistor'
- imx8qxp-pixel-link: Improve bridge reference handling
- lt9611: Support Port-B-only input plus DT bindings
- tda998x: Support DRM_BRIDGE_ATTACH_NO_CONNECTOR; Clean up
- Support TH1520 HDMI plus DT bindings
- waveshare-dsi: Fix register and attach; Support 1..4 DSI lanes plus
DT bindings
- anx7625: Fix USB Type-C handling
- cdns-mhdp8546-core: Handle HDCP state in bridge atomic_check
- Support Lontium LT8713SX DP MST bridge plus DT bindings
- analogix_dp: Use DP helpers for link training
panel:
- panel-jdi-lt070me05000: Use mipi-dsi multi functions
- panel-edp: Support Add AUO B116XAT04.1 (HW: 1A); Support CMN
N116BCL-EAK (C2); Support FriendlyELEC plus DT changes
- panel-edp: Fix timings for BOE NV140WUM-N64
- ilitek-ili9882t: Allow GPIO calls to sleep
- jadard: Support TAIGUAN XTI05101-01A
- lxd: Support LXD M9189A plus DT bindings
- mantix: Fix pixel clock; Clean up
- motorola: Support Motorola Atrix 4G and Droid X2 plus DT bindings
- novatek: Support Novatek/Tianma NT37700F plus DT bindings
- simple: Support EDT ET057023UDBA plus DT bindings; Support Powertip
PH800480T032-ZHC19 plus DT bindings; Support Waveshare 13.3"
- novatek-nt36672a: Use mipi_dsi_*_multi() functions
- panel-edp: Support BOE NV153WUM-N42, CMN N153JCA-ELK, CSW
MNF307QS3-2
- support Himax HX83121A plus DT bindings
- support JuTouch JT070TM041 plus DT bindings
- support Samsung S6E8FC0 plus DT bindings
- himax-hx83102c: support Samsung S6E8FC0 plus DT bindings; support
backlight
- ili9806e: support Rocktech RK050HR345-CT106A plus DT bindings
- simple: support Tianma TM050RDH03 plus DT bindings
amdgpu:
- enable DC by default on CIK APUs
- userq fence ioctl param size fixes
- set panel_type to OLED for eDP
- refactor DC i2c code
- FAMS2 update
- rework ttm handling to allow multiple engines
- DC DCE 6.x cleanup
- DC support for NUTMEG/TRAVIS DP bridge
- DCN 4.2 support
- GC12 idle power fix for compute
- use struct drm_edid in non-DC code
- enable NV12/P010 support on primary planes
- support newer IP discovery tables
- VCN/JPEG 5.0.2 support
- GC/MES 12.1 updates
- USERQ fixes
- add DC idle state manager
- eDP DSC seamless boot
amdkfd:
- GC 12.1 updates
- non 4K page fixes
xe:
- basic Xe3p_LPG and NVL-P enabling patches
- allow VM_BIND decompress support
- add purgeable buffer object support
- add xe_vm_get_property_ioctl
- restrict multi-lrc to VCS/VECS engines
- allow disabling VM overcommit in fault mode
- dGPU memory optimizations
- Workaround cleanups and simplification
- Allow VFs VRAM quote changes using sysfs
- convert GT stats to per-cpu counters
- pagefault refactors
- enable multi-queue on xe3p_xpc
- disable DCC on PTL
- make MMIO communication more robust
- disable D3Cold for BMG on specific platforms
- vfio: improve FLR sync for Xe VFIO
i915/display:
- C10/C20/LT PHY PLL divider verification
- use trans push mechanism to generate PSR frame change on LNL+
- refactor DP DSC slice config
- VGA decode refactoring
- refactor DPT, gen2-4 overlay, masked field register macro helpers
- refactor stolen memory allocation decisions
- prepare for UHBR DP tunnels
- refactor LT PHY PLL to use DPLL framework
- implement register polling/waiting in display code
- add shared stepping header between i915 and display
i915:
- fix potential overflow of shmem scatterlist length
nouveau:
- provide Z cull info to userspace
- initial GA100 support
- shutdown on PCI device shutdown
nova-core:
- harden GSP command queue
- add support for large RPCs
- simplify GSP sequencer and message handling
- refactor falcon firmware handling
- convert to new register macro
- conver to new DMA coherent API
- use checked arithmetic
- add debugfs support for gsp-rm log buffers
- fix aux device registration for multi-GPU
msm:
- CI:
- Uprev mesa
- Restore CI jobs for Qualcomm APQ8016 and APQ8096 devices
- Core:
- Switched to of_get_available_child_by_name()
- DPU:
- Fixes for DSC panels
- Fixed brownout because of the frequency / OPP mismatch
- Quad pipe preparation (not enabled yet)
- Switched to virtual planes by default
- Dropped VBIF_NRT support
- Added support for Eliza platform
- Reworked alpha handling
- Switched to correct CWB definitions on Eliza
- Dropped dummy INTF_0 on MSM8953
- Corrected INTFs related to DP-MST
- DP:
- Removed debug prints looking into PHY internals
- DSI:
- Fixes for DSC panels
- RGB101010 support
- Support for SC8280XP
- Moved PHY bindings from display/ to phy/
- GPU:
- Preemption support for x2-85 and a840
- IFPC support for a840
- SKU detection support for x2-85 and a840
- Expose AQE support (VK ray-pipeline)
- Avoid locking in VM_BIND fence signaling path
- Fix to avoid reclaim in GPU snapshot path
- Disallow foreign mapping of _NO_SHARE BOs
- HDMI:
- Fixed infoframes programming
- MDP5:
- Dropped support for MSM8974v1
- Dropped now unused code for MSM8974 v1 and SDM660 / MSM8998
panthor:
- add tracepoints for power and IRQs
- fix fence handling
- extend timestamp query with flags
- support various sources for timestamp queries
tyr:
- fix names and model/versions
rockchip:
- vop2: use drm logging function
- rk3576 displayport support
- support CRTC background color
atmel-hlcdc:
- support sana5d65 LCD controller
tilcdc:
- use DT bindings schema
- use managed DRM interfaces
- support DRM_BRIDGE_ATTACH_NO_CONNECTOR
verisilicon:
- support DC8200 + DT bindings
virtgpu:
- support PRIME import with 3D enabled
komeda:
- fix integer overflow in AFBC checks
mcde:
- improve bridge handling
gma500:
- use drm client buffer for fbdev framebuffer
amdxdna:
- add sensors ioctls
- provide NPU power estimate
- support column utilization sensor
- allow forcing DMA through IOMMU IOVA
- support per-BO mem usage queries
- refactor GEM implementation
ivpu:
- update boot API to v3.29.4
- limit per-user number of doorbells/contexts
- perform engine reset on TDR error
loongson:
- replace custom code with drm_gem_ttm_dumb_map_offset()
imx:
- support planes behind the primary plane
- fix bus-format selection
vkms:
- support CRTC background color
v3d:
- improve handling of struct v3d_stats
komeda:
- support Arm China Linlon D6 plus DT bindings
imagination:
- improve power-off sequence
- support context-reset notification from firmware
mediatek:
- mtk_dsi: enable hs clock during pre-enable
- Remove all conflicting aperture devices during probe
- Add support for mt8167 display blocks"
* tag 'drm-next-2026-04-15' of https://gitlab.freedesktop.org/drm/kernel: (1735 commits)
drm/ttm/tests: Remove checks from ttm_pool_free_no_dma_alloc
drm/ttm/tests: fix lru_count ASSERT
drm/vram: remove DRM_VRAM_MM_FILE_OPERATIONS from docs
drm/fb-helper: Fix a locking bug in an error path
dma-fence: correct kernel-doc function parameter @flags
ttm/pool: track allocated_pages per numa node.
ttm/pool: make pool shrinker NUMA aware (v2)
ttm/pool: drop numa specific pools
ttm/pool: port to list_lru. (v2)
drm/ttm: use gpu mm stats to track gpu memory allocations. (v4)
mm: add gpu active/reclaim per-node stat counters (v2)
gpu: nova-core: fix missing colon in SEC2 boot debug message
gpu: nova-core: vbios: use from_le_bytes() for PCI ROM header parsing
gpu: nova-core: bitfield: fix broken Default implementation
gpu: nova-core: falcon: pad firmware DMA object size to required block alignment
gpu: nova-core: gsp: fix undefined behavior in command queue code
drm/shmem_helper: Make sure PMD entries get the writeable upgrade
accel/ivpu: Trigger recovery on TDR with OS scheduling
drm/msm: Use of_get_available_child_by_name()
dt-bindings: display/msm: move DSI PHY bindings to phy/ subdir
...
564 lines
19 KiB
Rust
564 lines
19 KiB
Rust
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
//! Kernel errors.
|
|
//!
|
|
//! C header: [`include/uapi/asm-generic/errno-base.h`](srctree/include/uapi/asm-generic/errno-base.h)\
|
|
//! C header: [`include/uapi/asm-generic/errno.h`](srctree/include/uapi/asm-generic/errno.h)\
|
|
//! C header: [`include/linux/errno.h`](srctree/include/linux/errno.h)
|
|
|
|
use crate::{
|
|
alloc::{layout::LayoutError, AllocError},
|
|
fmt,
|
|
str::CStr,
|
|
};
|
|
|
|
use core::num::NonZeroI32;
|
|
use core::num::TryFromIntError;
|
|
use core::str::Utf8Error;
|
|
|
|
/// Contains the C-compatible error codes.
|
|
#[rustfmt::skip]
|
|
pub mod code {
|
|
macro_rules! declare_err {
|
|
($err:tt $(,)? $($doc:expr),+) => {
|
|
$(
|
|
#[doc = $doc]
|
|
)*
|
|
pub const $err: super::Error =
|
|
match super::Error::try_from_errno(-(crate::bindings::$err as i32)) {
|
|
Some(err) => err,
|
|
None => panic!("Invalid errno in `declare_err!`"),
|
|
};
|
|
};
|
|
}
|
|
|
|
declare_err!(EPERM, "Operation not permitted.");
|
|
declare_err!(ENOENT, "No such file or directory.");
|
|
declare_err!(ESRCH, "No such process.");
|
|
declare_err!(EINTR, "Interrupted system call.");
|
|
declare_err!(EIO, "I/O error.");
|
|
declare_err!(ENXIO, "No such device or address.");
|
|
declare_err!(E2BIG, "Argument list too long.");
|
|
declare_err!(ENOEXEC, "Exec format error.");
|
|
declare_err!(EBADF, "Bad file number.");
|
|
declare_err!(ECHILD, "No child processes.");
|
|
declare_err!(EAGAIN, "Try again.");
|
|
declare_err!(ENOMEM, "Out of memory.");
|
|
declare_err!(EACCES, "Permission denied.");
|
|
declare_err!(EFAULT, "Bad address.");
|
|
declare_err!(ENOTBLK, "Block device required.");
|
|
declare_err!(EBUSY, "Device or resource busy.");
|
|
declare_err!(EEXIST, "File exists.");
|
|
declare_err!(EXDEV, "Cross-device link.");
|
|
declare_err!(ENODEV, "No such device.");
|
|
declare_err!(ENOTDIR, "Not a directory.");
|
|
declare_err!(EISDIR, "Is a directory.");
|
|
declare_err!(EINVAL, "Invalid argument.");
|
|
declare_err!(ENFILE, "File table overflow.");
|
|
declare_err!(EMFILE, "Too many open files.");
|
|
declare_err!(ENOTTY, "Not a typewriter.");
|
|
declare_err!(ETXTBSY, "Text file busy.");
|
|
declare_err!(EFBIG, "File too large.");
|
|
declare_err!(ENOSPC, "No space left on device.");
|
|
declare_err!(ESPIPE, "Illegal seek.");
|
|
declare_err!(EROFS, "Read-only file system.");
|
|
declare_err!(EMLINK, "Too many links.");
|
|
declare_err!(EPIPE, "Broken pipe.");
|
|
declare_err!(EDOM, "Math argument out of domain of func.");
|
|
declare_err!(ERANGE, "Math result not representable.");
|
|
declare_err!(EOVERFLOW, "Value too large for defined data type.");
|
|
declare_err!(EMSGSIZE, "Message too long.");
|
|
declare_err!(ETIMEDOUT, "Connection timed out.");
|
|
declare_err!(ERESTARTSYS, "Restart the system call.");
|
|
declare_err!(ERESTARTNOINTR, "System call was interrupted by a signal and will be restarted.");
|
|
declare_err!(ERESTARTNOHAND, "Restart if no handler.");
|
|
declare_err!(ENOIOCTLCMD, "No ioctl command.");
|
|
declare_err!(ERESTART_RESTARTBLOCK, "Restart by calling sys_restart_syscall.");
|
|
declare_err!(EPROBE_DEFER, "Driver requests probe retry.");
|
|
declare_err!(EOPENSTALE, "Open found a stale dentry.");
|
|
declare_err!(ENOPARAM, "Parameter not supported.");
|
|
declare_err!(EBADHANDLE, "Illegal NFS file handle.");
|
|
declare_err!(ENOTSYNC, "Update synchronization mismatch.");
|
|
declare_err!(EBADCOOKIE, "Cookie is stale.");
|
|
declare_err!(ENOTSUPP, "Operation is not supported.");
|
|
declare_err!(ETOOSMALL, "Buffer or request is too small.");
|
|
declare_err!(ESERVERFAULT, "An untranslatable error occurred.");
|
|
declare_err!(EBADTYPE, "Type not supported by server.");
|
|
declare_err!(EJUKEBOX, "Request initiated, but will not complete before timeout.");
|
|
declare_err!(EIOCBQUEUED, "iocb queued, will get completion event.");
|
|
declare_err!(ERECALLCONFLICT, "Conflict with recalled state.");
|
|
declare_err!(ENOGRACE, "NFS file lock reclaim refused.");
|
|
}
|
|
|
|
/// Generic integer kernel error.
|
|
///
|
|
/// The kernel defines a set of integer generic error codes based on C and
|
|
/// POSIX ones. These codes may have a more specific meaning in some contexts.
|
|
///
|
|
/// # Invariants
|
|
///
|
|
/// The value is a valid `errno` (i.e. `>= -MAX_ERRNO && < 0`).
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
pub struct Error(NonZeroI32);
|
|
|
|
impl Error {
|
|
/// Creates an [`Error`] from a kernel error code.
|
|
///
|
|
/// `errno` must be within error code range (i.e. `>= -MAX_ERRNO && < 0`).
|
|
///
|
|
/// It is a bug to pass an out-of-range `errno`. [`code::EINVAL`] is returned in such a case.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// assert_eq!(Error::from_errno(-1), EPERM);
|
|
/// assert_eq!(Error::from_errno(-2), ENOENT);
|
|
/// ```
|
|
///
|
|
/// The following calls are considered a bug:
|
|
///
|
|
/// ```
|
|
/// assert_eq!(Error::from_errno(0), EINVAL);
|
|
/// assert_eq!(Error::from_errno(-1000000), EINVAL);
|
|
/// ```
|
|
pub fn from_errno(errno: crate::ffi::c_int) -> Error {
|
|
if let Some(error) = Self::try_from_errno(errno) {
|
|
error
|
|
} else {
|
|
// TODO: Make it a `WARN_ONCE` once available.
|
|
crate::pr_warn!(
|
|
"attempted to create `Error` with out of range `errno`: {}\n",
|
|
errno
|
|
);
|
|
code::EINVAL
|
|
}
|
|
}
|
|
|
|
/// Creates an [`Error`] from a kernel error code.
|
|
///
|
|
/// Returns [`None`] if `errno` is out-of-range.
|
|
const fn try_from_errno(errno: crate::ffi::c_int) -> Option<Error> {
|
|
if errno < -(bindings::MAX_ERRNO as i32) || errno >= 0 {
|
|
return None;
|
|
}
|
|
|
|
// SAFETY: `errno` is checked above to be in a valid range.
|
|
Some(unsafe { Error::from_errno_unchecked(errno) })
|
|
}
|
|
|
|
/// Creates an [`Error`] from a kernel error code.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// `errno` must be within error code range (i.e. `>= -MAX_ERRNO && < 0`).
|
|
const unsafe fn from_errno_unchecked(errno: crate::ffi::c_int) -> Error {
|
|
// INVARIANT: The contract ensures the type invariant
|
|
// will hold.
|
|
// SAFETY: The caller guarantees `errno` is non-zero.
|
|
Error(unsafe { NonZeroI32::new_unchecked(errno) })
|
|
}
|
|
|
|
/// Returns the kernel error code.
|
|
pub fn to_errno(self) -> crate::ffi::c_int {
|
|
self.0.get()
|
|
}
|
|
|
|
#[cfg(CONFIG_BLOCK)]
|
|
pub(crate) fn to_blk_status(self) -> bindings::blk_status_t {
|
|
// SAFETY: `self.0` is a valid error due to its invariant.
|
|
unsafe { bindings::errno_to_blk_status(self.0.get()) }
|
|
}
|
|
|
|
/// Returns the error encoded as a pointer.
|
|
pub fn to_ptr<T>(self) -> *mut T {
|
|
// SAFETY: `self.0` is a valid error due to its invariant.
|
|
unsafe { bindings::ERR_PTR(self.0.get() as crate::ffi::c_long).cast() }
|
|
}
|
|
|
|
/// Returns a string representing the error, if one exists.
|
|
#[cfg(not(testlib))]
|
|
pub fn name(&self) -> Option<&'static CStr> {
|
|
// SAFETY: Just an FFI call, there are no extra safety requirements.
|
|
let ptr = unsafe { bindings::errname(-self.0.get()) };
|
|
if ptr.is_null() {
|
|
None
|
|
} else {
|
|
use crate::str::CStrExt as _;
|
|
|
|
// SAFETY: The string returned by `errname` is static and `NUL`-terminated.
|
|
Some(unsafe { CStr::from_char_ptr(ptr) })
|
|
}
|
|
}
|
|
|
|
/// Returns a string representing the error, if one exists.
|
|
///
|
|
/// When `testlib` is configured, this always returns `None` to avoid the dependency on a
|
|
/// kernel function so that tests that use this (e.g., by calling [`Result::unwrap`]) can still
|
|
/// run in userspace.
|
|
#[cfg(testlib)]
|
|
pub fn name(&self) -> Option<&'static CStr> {
|
|
None
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for Error {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self.name() {
|
|
// Print out number if no name can be found.
|
|
None => f.debug_tuple("Error").field(&-self.0).finish(),
|
|
Some(name) => f
|
|
.debug_tuple(
|
|
// SAFETY: These strings are ASCII-only.
|
|
unsafe { core::str::from_utf8_unchecked(name.to_bytes()) },
|
|
)
|
|
.finish(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<AllocError> for Error {
|
|
#[inline]
|
|
fn from(_: AllocError) -> Error {
|
|
code::ENOMEM
|
|
}
|
|
}
|
|
|
|
impl From<TryFromIntError> for Error {
|
|
#[inline]
|
|
fn from(_: TryFromIntError) -> Error {
|
|
code::EINVAL
|
|
}
|
|
}
|
|
|
|
impl From<Utf8Error> for Error {
|
|
#[inline]
|
|
fn from(_: Utf8Error) -> Error {
|
|
code::EINVAL
|
|
}
|
|
}
|
|
|
|
impl From<LayoutError> for Error {
|
|
#[inline]
|
|
fn from(_: LayoutError) -> Error {
|
|
code::ENOMEM
|
|
}
|
|
}
|
|
|
|
impl From<fmt::Error> for Error {
|
|
#[inline]
|
|
fn from(_: fmt::Error) -> Error {
|
|
code::EINVAL
|
|
}
|
|
}
|
|
|
|
impl From<core::convert::Infallible> for Error {
|
|
#[inline]
|
|
fn from(e: core::convert::Infallible) -> Error {
|
|
match e {}
|
|
}
|
|
}
|
|
|
|
/// A [`Result`] with an [`Error`] error type.
|
|
///
|
|
/// To be used as the return type for functions that may fail.
|
|
///
|
|
/// # Error codes in C and Rust
|
|
///
|
|
/// In C, it is common that functions indicate success or failure through
|
|
/// their return value; modifying or returning extra data through non-`const`
|
|
/// pointer parameters. In particular, in the kernel, functions that may fail
|
|
/// typically return an `int` that represents a generic error code. We model
|
|
/// those as [`Error`].
|
|
///
|
|
/// In Rust, it is idiomatic to model functions that may fail as returning
|
|
/// a [`Result`]. Since in the kernel many functions return an error code,
|
|
/// [`Result`] is a type alias for a [`core::result::Result`] that uses
|
|
/// [`Error`] as its error type.
|
|
///
|
|
/// Note that even if a function does not return anything when it succeeds,
|
|
/// it should still be modeled as returning a [`Result`] rather than
|
|
/// just an [`Error`].
|
|
///
|
|
/// Calling a function that returns [`Result`] forces the caller to handle
|
|
/// the returned [`Result`].
|
|
///
|
|
/// This can be done "manually" by using [`match`]. Using [`match`] to decode
|
|
/// the [`Result`] is similar to C where all the return value decoding and the
|
|
/// error handling is done explicitly by writing handling code for each
|
|
/// error to cover. Using [`match`] the error and success handling can be
|
|
/// implemented in all detail as required. For example (inspired by
|
|
/// [`samples/rust/rust_minimal.rs`]):
|
|
///
|
|
/// ```
|
|
/// # #[allow(clippy::single_match)]
|
|
/// fn example() -> Result {
|
|
/// let mut numbers = KVec::new();
|
|
///
|
|
/// match numbers.push(72, GFP_KERNEL) {
|
|
/// Err(e) => {
|
|
/// pr_err!("Error pushing 72: {e:?}");
|
|
/// return Err(e.into());
|
|
/// }
|
|
/// // Do nothing, continue.
|
|
/// Ok(()) => (),
|
|
/// }
|
|
///
|
|
/// match numbers.push(108, GFP_KERNEL) {
|
|
/// Err(e) => {
|
|
/// pr_err!("Error pushing 108: {e:?}");
|
|
/// return Err(e.into());
|
|
/// }
|
|
/// // Do nothing, continue.
|
|
/// Ok(()) => (),
|
|
/// }
|
|
///
|
|
/// match numbers.push(200, GFP_KERNEL) {
|
|
/// Err(e) => {
|
|
/// pr_err!("Error pushing 200: {e:?}");
|
|
/// return Err(e.into());
|
|
/// }
|
|
/// // Do nothing, continue.
|
|
/// Ok(()) => (),
|
|
/// }
|
|
///
|
|
/// Ok(())
|
|
/// }
|
|
/// # example()?;
|
|
/// # Ok::<(), Error>(())
|
|
/// ```
|
|
///
|
|
/// An alternative to be more concise is the [`if let`] syntax:
|
|
///
|
|
/// ```
|
|
/// fn example() -> Result {
|
|
/// let mut numbers = KVec::new();
|
|
///
|
|
/// if let Err(e) = numbers.push(72, GFP_KERNEL) {
|
|
/// pr_err!("Error pushing 72: {e:?}");
|
|
/// return Err(e.into());
|
|
/// }
|
|
///
|
|
/// if let Err(e) = numbers.push(108, GFP_KERNEL) {
|
|
/// pr_err!("Error pushing 108: {e:?}");
|
|
/// return Err(e.into());
|
|
/// }
|
|
///
|
|
/// if let Err(e) = numbers.push(200, GFP_KERNEL) {
|
|
/// pr_err!("Error pushing 200: {e:?}");
|
|
/// return Err(e.into());
|
|
/// }
|
|
///
|
|
/// Ok(())
|
|
/// }
|
|
/// # example()?;
|
|
/// # Ok::<(), Error>(())
|
|
/// ```
|
|
///
|
|
/// Instead of these verbose [`match`]/[`if let`], the [`?`] operator can
|
|
/// be used to handle the [`Result`]. Using the [`?`] operator is often
|
|
/// the best choice to handle [`Result`] in a non-verbose way as done in
|
|
/// [`samples/rust/rust_minimal.rs`]:
|
|
///
|
|
/// ```
|
|
/// fn example() -> Result {
|
|
/// let mut numbers = KVec::new();
|
|
///
|
|
/// numbers.push(72, GFP_KERNEL)?;
|
|
/// numbers.push(108, GFP_KERNEL)?;
|
|
/// numbers.push(200, GFP_KERNEL)?;
|
|
///
|
|
/// Ok(())
|
|
/// }
|
|
/// # example()?;
|
|
/// # Ok::<(), Error>(())
|
|
/// ```
|
|
///
|
|
/// Another possibility is to call [`unwrap()`](Result::unwrap) or
|
|
/// [`expect()`](Result::expect). However, use of these functions is
|
|
/// *heavily discouraged* in the kernel because they trigger a Rust
|
|
/// [`panic!`] if an error happens, which may destabilize the system or
|
|
/// entirely break it as a result -- just like the C [`BUG()`] macro.
|
|
/// Please see the documentation for the C macro [`BUG()`] for guidance
|
|
/// on when to use these functions.
|
|
///
|
|
/// Alternatively, depending on the use case, using [`unwrap_or()`],
|
|
/// [`unwrap_or_else()`], [`unwrap_or_default()`] or [`unwrap_unchecked()`]
|
|
/// might be an option, as well.
|
|
///
|
|
/// For even more details, please see the [Rust documentation].
|
|
///
|
|
/// [`match`]: https://doc.rust-lang.org/reference/expressions/match-expr.html
|
|
/// [`samples/rust/rust_minimal.rs`]: srctree/samples/rust/rust_minimal.rs
|
|
/// [`if let`]: https://doc.rust-lang.org/reference/expressions/if-expr.html#if-let-expressions
|
|
/// [`?`]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator
|
|
/// [`unwrap()`]: Result::unwrap
|
|
/// [`expect()`]: Result::expect
|
|
/// [`BUG()`]: https://docs.kernel.org/process/deprecated.html#bug-and-bug-on
|
|
/// [`unwrap_or()`]: Result::unwrap_or
|
|
/// [`unwrap_or_else()`]: Result::unwrap_or_else
|
|
/// [`unwrap_or_default()`]: Result::unwrap_or_default
|
|
/// [`unwrap_unchecked()`]: Result::unwrap_unchecked
|
|
/// [Rust documentation]: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html
|
|
pub type Result<T = (), E = Error> = core::result::Result<T, E>;
|
|
|
|
/// Converts an integer as returned by a C kernel function to a [`Result`].
|
|
///
|
|
/// If the integer is negative, an [`Err`] with an [`Error`] as given by [`Error::from_errno`] is
|
|
/// returned. This means the integer must be `>= -MAX_ERRNO`.
|
|
///
|
|
/// Otherwise, it returns [`Ok`].
|
|
///
|
|
/// It is a bug to pass an out-of-range negative integer. `Err(EINVAL)` is returned in such a case.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// This function may be used to easily perform early returns with the [`?`] operator when working
|
|
/// with C APIs within Rust abstractions:
|
|
///
|
|
/// ```
|
|
/// # use kernel::error::to_result;
|
|
/// # mod bindings {
|
|
/// # #![expect(clippy::missing_safety_doc)]
|
|
/// # use kernel::prelude::*;
|
|
/// # pub(super) unsafe fn f1() -> c_int { 0 }
|
|
/// # pub(super) unsafe fn f2() -> c_int { EINVAL.to_errno() }
|
|
/// # }
|
|
/// fn f() -> Result {
|
|
/// // SAFETY: ...
|
|
/// to_result(unsafe { bindings::f1() })?;
|
|
///
|
|
/// // SAFETY: ...
|
|
/// to_result(unsafe { bindings::f2() })?;
|
|
///
|
|
/// // ...
|
|
///
|
|
/// Ok(())
|
|
/// }
|
|
/// # assert_eq!(f(), Err(EINVAL));
|
|
/// ```
|
|
///
|
|
/// [`?`]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator
|
|
pub fn to_result(err: crate::ffi::c_int) -> Result {
|
|
if err < 0 {
|
|
Err(Error::from_errno(err))
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Transform a kernel "error pointer" to a normal pointer.
|
|
///
|
|
/// Some kernel C API functions return an "error pointer" which optionally
|
|
/// embeds an `errno`. Callers are supposed to check the returned pointer
|
|
/// for errors. This function performs the check and converts the "error pointer"
|
|
/// to a normal pointer in an idiomatic fashion.
|
|
///
|
|
/// Note that a `NULL` pointer is not considered an error pointer, and is returned
|
|
/// as-is, wrapped in [`Ok`].
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```ignore
|
|
/// # use kernel::from_err_ptr;
|
|
/// # use kernel::bindings;
|
|
/// fn devm_platform_ioremap_resource(
|
|
/// pdev: &mut PlatformDevice,
|
|
/// index: u32,
|
|
/// ) -> Result<*mut kernel::ffi::c_void> {
|
|
/// // SAFETY: `pdev` points to a valid platform device. There are no safety requirements
|
|
/// // on `index`.
|
|
/// from_err_ptr(unsafe { bindings::devm_platform_ioremap_resource(pdev.to_ptr(), index) })
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// ```
|
|
/// # use kernel::error::from_err_ptr;
|
|
/// # mod bindings {
|
|
/// # #![expect(clippy::missing_safety_doc)]
|
|
/// # use kernel::prelude::*;
|
|
/// # pub(super) unsafe fn einval_err_ptr() -> *mut kernel::ffi::c_void {
|
|
/// # EINVAL.to_ptr()
|
|
/// # }
|
|
/// # pub(super) unsafe fn null_ptr() -> *mut kernel::ffi::c_void {
|
|
/// # core::ptr::null_mut()
|
|
/// # }
|
|
/// # pub(super) unsafe fn non_null_ptr() -> *mut kernel::ffi::c_void {
|
|
/// # 0x1234 as *mut kernel::ffi::c_void
|
|
/// # }
|
|
/// # }
|
|
/// // SAFETY: ...
|
|
/// let einval_err = from_err_ptr(unsafe { bindings::einval_err_ptr() });
|
|
/// assert_eq!(einval_err, Err(EINVAL));
|
|
///
|
|
/// // SAFETY: ...
|
|
/// let null_ok = from_err_ptr(unsafe { bindings::null_ptr() });
|
|
/// assert_eq!(null_ok, Ok(core::ptr::null_mut()));
|
|
///
|
|
/// // SAFETY: ...
|
|
/// let non_null = from_err_ptr(unsafe { bindings::non_null_ptr() }).unwrap();
|
|
/// assert_ne!(non_null, core::ptr::null_mut());
|
|
/// ```
|
|
pub fn from_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
|
|
// CAST: Casting a pointer to `*const crate::ffi::c_void` is always valid.
|
|
let const_ptr: *const crate::ffi::c_void = ptr.cast();
|
|
// SAFETY: The FFI function does not deref the pointer.
|
|
if unsafe { bindings::IS_ERR(const_ptr) } {
|
|
// SAFETY: The FFI function does not deref the pointer.
|
|
let err = unsafe { bindings::PTR_ERR(const_ptr) };
|
|
|
|
#[allow(clippy::unnecessary_cast)]
|
|
// CAST: If `IS_ERR()` returns `true`,
|
|
// then `PTR_ERR()` is guaranteed to return a
|
|
// negative value greater-or-equal to `-bindings::MAX_ERRNO`,
|
|
// which always fits in an `i16`, as per the invariant above.
|
|
// And an `i16` always fits in an `i32`. So casting `err` to
|
|
// an `i32` can never overflow, and is always valid.
|
|
//
|
|
// SAFETY: `IS_ERR()` ensures `err` is a
|
|
// negative value greater-or-equal to `-bindings::MAX_ERRNO`.
|
|
return Err(unsafe { Error::from_errno_unchecked(err as crate::ffi::c_int) });
|
|
}
|
|
Ok(ptr)
|
|
}
|
|
|
|
/// Calls a closure returning a [`crate::error::Result<T>`] and converts the result to
|
|
/// a C integer result.
|
|
///
|
|
/// This is useful when calling Rust functions that return [`crate::error::Result<T>`]
|
|
/// from inside `extern "C"` functions that need to return an integer error result.
|
|
///
|
|
/// `T` should be convertible from an `i16` via `From<i16>`.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```ignore
|
|
/// # use kernel::from_result;
|
|
/// # use kernel::bindings;
|
|
/// unsafe extern "C" fn probe_callback(
|
|
/// pdev: *mut bindings::platform_device,
|
|
/// ) -> kernel::ffi::c_int {
|
|
/// from_result(|| {
|
|
/// let ptr = devm_alloc(pdev)?;
|
|
/// bindings::platform_set_drvdata(pdev, ptr);
|
|
/// Ok(0)
|
|
/// })
|
|
/// }
|
|
/// ```
|
|
pub fn from_result<T, F>(f: F) -> T
|
|
where
|
|
T: From<i16>,
|
|
F: FnOnce() -> Result<T>,
|
|
{
|
|
match f() {
|
|
Ok(v) => v,
|
|
// NO-OVERFLOW: negative `errno`s are no smaller than `-bindings::MAX_ERRNO`,
|
|
// `-bindings::MAX_ERRNO` fits in an `i16` as per invariant above,
|
|
// therefore a negative `errno` always fits in an `i16` and will not overflow.
|
|
Err(e) => T::from(e.to_errno() as i16),
|
|
}
|
|
}
|
|
|
|
/// Error message for calling a default function of a [`#[vtable]`](macros::vtable) trait.
|
|
pub const VTABLE_DEFAULT_ERROR: &str =
|
|
"This function must not be called, see the #[vtable] documentation.";
|