use crate::{
alloc::{AllocError, Flags},
container_of,
prelude::*,
sync::{
aref::{
ARef,
AlwaysRefCounted, },
Arc,
LockClassKey, },
time::Jiffies,
types::Opaque,
};
use core::{marker::PhantomData, ptr::NonNull};
#[macro_export]
macro_rules! new_work {
($($name:literal)?) => {
$crate::workqueue::Work::new($crate::optional_name!($($name)?), $crate::static_lock_class!())
};
}
pub use new_work;
#[macro_export]
macro_rules! new_delayed_work {
() => {
$crate::workqueue::DelayedWork::new(
$crate::optional_name!(),
$crate::static_lock_class!(),
$crate::c_str!(::core::concat!(
::core::file!(),
":",
::core::line!(),
"_timer"
)),
$crate::static_lock_class!(),
)
};
($name:literal) => {
$crate::workqueue::DelayedWork::new(
$crate::c_str!($name),
$crate::static_lock_class!(),
$crate::c_str!(::core::concat!($name, "_timer")),
$crate::static_lock_class!(),
)
};
}
pub use new_delayed_work;
#[repr(transparent)]
pub struct Queue(Opaque<bindings::workqueue_struct>);
unsafe impl Send for Queue {}
unsafe impl Sync for Queue {}
impl Queue {
pub unsafe fn from_raw<'a>(ptr: *const bindings::workqueue_struct) -> &'a Queue {
unsafe { &*ptr.cast::<Queue>() }
}
pub fn enqueue<W, const ID: u64>(&self, w: W) -> W::EnqueueOutput
where
W: RawWorkItem<ID> + Send + 'static,
{
let queue_ptr = self.0.get();
unsafe {
w.__enqueue(move |work_ptr| {
bindings::queue_work_on(
bindings::wq_misc_consts_WORK_CPU_UNBOUND as ffi::c_int,
queue_ptr,
work_ptr,
)
})
}
}
pub fn enqueue_delayed<W, const ID: u64>(&self, w: W, delay: Jiffies) -> W::EnqueueOutput
where
W: RawDelayedWorkItem<ID> + Send + 'static,
{
let queue_ptr = self.0.get();
unsafe {
w.__enqueue(move |work_ptr| {
bindings::queue_delayed_work_on(
bindings::wq_misc_consts_WORK_CPU_UNBOUND as ffi::c_int,
queue_ptr,
container_of!(work_ptr, bindings::delayed_work, work),
delay,
)
})
}
}
pub fn try_spawn<T: 'static + Send + FnOnce()>(
&self,
flags: Flags,
func: T,
) -> Result<(), AllocError> {
let init = pin_init!(ClosureWork {
work <- new_work!("Queue::try_spawn"),
func: Some(func),
});
self.enqueue(KBox::pin_init(init, flags).map_err(|_| AllocError)?);
Ok(())
}
}
#[pin_data]
struct ClosureWork<T> {
#[pin]
work: Work<ClosureWork<T>>,
func: Option<T>,
}
impl<T: FnOnce()> WorkItem for ClosureWork<T> {
type Pointer = Pin<KBox<Self>>;
fn run(mut this: Pin<KBox<Self>>) {
if let Some(func) = this.as_mut().project().func.take() {
(func)()
}
}
}
pub unsafe trait RawWorkItem<const ID: u64> {
type EnqueueOutput;
unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput
where
F: FnOnce(*mut bindings::work_struct) -> bool;
}
pub unsafe trait RawDelayedWorkItem<const ID: u64>: RawWorkItem<ID> {}
pub unsafe trait WorkItemPointer<const ID: u64>: RawWorkItem<ID> {
unsafe extern "C" fn run(ptr: *mut bindings::work_struct);
}
pub trait WorkItem<const ID: u64 = 0> {
type Pointer: WorkItemPointer<ID>;
fn run(this: Self::Pointer);
}
#[pin_data]
#[repr(transparent)]
pub struct Work<T: ?Sized, const ID: u64 = 0> {
#[pin]
work: Opaque<bindings::work_struct>,
_inner: PhantomData<T>,
}
unsafe impl<T: ?Sized, const ID: u64> Send for Work<T, ID> {}
unsafe impl<T: ?Sized, const ID: u64> Sync for Work<T, ID> {}
impl<T: ?Sized, const ID: u64> Work<T, ID> {
#[inline]
pub fn new(name: &'static CStr, key: Pin<&'static LockClassKey>) -> impl PinInit<Self>
where
T: WorkItem<ID>,
{
pin_init!(Self {
work <- Opaque::ffi_init(|slot| {
unsafe {
bindings::init_work_with_key(
slot,
Some(T::Pointer::run),
false,
name.as_char_ptr(),
key.as_ptr(),
)
}
}),
_inner: PhantomData,
})
}
#[inline]
pub unsafe fn raw_get(ptr: *const Self) -> *mut bindings::work_struct {
unsafe { Opaque::cast_into(core::ptr::addr_of!((*ptr).work)) }
}
}
pub unsafe trait HasWork<T, const ID: u64 = 0> {
unsafe fn raw_get_work(ptr: *mut Self) -> *mut Work<T, ID>;
unsafe fn work_container_of(ptr: *mut Work<T, ID>) -> *mut Self;
}
#[macro_export]
macro_rules! impl_has_work {
($(impl$({$($generics:tt)*})?
HasWork<$work_type:ty $(, $id:tt)?>
for $self:ty
{ self.$field:ident }
)*) => {$(
unsafe impl$(<$($generics)+>)? $crate::workqueue::HasWork<$work_type $(, $id)?> for $self {
#[inline]
unsafe fn raw_get_work(ptr: *mut Self) -> *mut $crate::workqueue::Work<$work_type $(, $id)?> {
unsafe {
::core::ptr::addr_of_mut!((*ptr).$field)
}
}
#[inline]
unsafe fn work_container_of(
ptr: *mut $crate::workqueue::Work<$work_type $(, $id)?>,
) -> *mut Self {
unsafe { $crate::container_of!(ptr, Self, $field) }
}
}
)*};
}
pub use impl_has_work;
impl_has_work! {
impl{T} HasWork<Self> for ClosureWork<T> { self.work }
}
#[pin_data]
#[repr(transparent)]
pub struct DelayedWork<T: ?Sized, const ID: u64 = 0> {
#[pin]
dwork: Opaque<bindings::delayed_work>,
_inner: PhantomData<T>,
}
unsafe impl<T: ?Sized, const ID: u64> Send for DelayedWork<T, ID> {}
unsafe impl<T: ?Sized, const ID: u64> Sync for DelayedWork<T, ID> {}
impl<T: ?Sized, const ID: u64> DelayedWork<T, ID> {
#[inline]
pub fn new(
work_name: &'static CStr,
work_key: Pin<&'static LockClassKey>,
timer_name: &'static CStr,
timer_key: Pin<&'static LockClassKey>,
) -> impl PinInit<Self>
where
T: WorkItem<ID>,
{
pin_init!(Self {
dwork <- Opaque::ffi_init(|slot: *mut bindings::delayed_work| {
unsafe {
bindings::init_work_with_key(
core::ptr::addr_of_mut!((*slot).work),
Some(T::Pointer::run),
false,
work_name.as_char_ptr(),
work_key.as_ptr(),
)
}
unsafe {
bindings::timer_init_key(
core::ptr::addr_of_mut!((*slot).timer),
Some(bindings::delayed_work_timer_fn),
bindings::TIMER_IRQSAFE,
timer_name.as_char_ptr(),
timer_key.as_ptr(),
)
}
}),
_inner: PhantomData,
})
}
#[inline]
pub unsafe fn raw_as_work(ptr: *const Self) -> *mut Work<T, ID> {
let dw: *mut bindings::delayed_work =
unsafe { Opaque::cast_into(core::ptr::addr_of!((*ptr).dwork)) };
let wrk: *mut bindings::work_struct = unsafe { core::ptr::addr_of_mut!((*dw).work) };
wrk.cast()
}
}
pub unsafe trait HasDelayedWork<T, const ID: u64 = 0>: HasWork<T, ID> {}
#[macro_export]
macro_rules! impl_has_delayed_work {
($(impl$({$($generics:tt)*})?
HasDelayedWork<$work_type:ty $(, $id:tt)?>
for $self:ty
{ self.$field:ident }
)*) => {$(
unsafe impl$(<$($generics)+>)?
$crate::workqueue::HasDelayedWork<$work_type $(, $id)?> for $self {}
unsafe impl$(<$($generics)+>)? $crate::workqueue::HasWork<$work_type $(, $id)?> for $self {
#[inline]
unsafe fn raw_get_work(
ptr: *mut Self
) -> *mut $crate::workqueue::Work<$work_type $(, $id)?> {
let ptr: *mut $crate::workqueue::DelayedWork<$work_type $(, $id)?> = unsafe {
::core::ptr::addr_of_mut!((*ptr).$field)
};
unsafe { $crate::workqueue::DelayedWork::raw_as_work(ptr) }
}
#[inline]
unsafe fn work_container_of(
ptr: *mut $crate::workqueue::Work<$work_type $(, $id)?>,
) -> *mut Self {
let ptr = unsafe { $crate::workqueue::Work::raw_get(ptr) };
let delayed_work = unsafe {
$crate::container_of!(ptr, $crate::bindings::delayed_work, work)
};
let delayed_work: *mut $crate::workqueue::DelayedWork<$work_type $(, $id)?> =
delayed_work.cast();
unsafe { $crate::container_of!(delayed_work, Self, $field) }
}
}
)*};
}
pub use impl_has_delayed_work;
unsafe impl<T, const ID: u64> WorkItemPointer<ID> for Arc<T>
where
T: WorkItem<ID, Pointer = Self>,
T: HasWork<T, ID>,
{
unsafe extern "C" fn run(ptr: *mut bindings::work_struct) {
let ptr = ptr.cast::<Work<T, ID>>();
let ptr = unsafe { T::work_container_of(ptr) };
let arc = unsafe { Arc::from_raw(ptr) };
T::run(arc)
}
}
unsafe impl<T, const ID: u64> RawWorkItem<ID> for Arc<T>
where
T: WorkItem<ID, Pointer = Self>,
T: HasWork<T, ID>,
{
type EnqueueOutput = Result<(), Self>;
unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput
where
F: FnOnce(*mut bindings::work_struct) -> bool,
{
let ptr = Arc::into_raw(self).cast_mut();
let work_ptr = unsafe { T::raw_get_work(ptr) };
let work_ptr = unsafe { Work::raw_get(work_ptr) };
if queue_work_on(work_ptr) {
Ok(())
} else {
Err(unsafe { Arc::from_raw(ptr) })
}
}
}
unsafe impl<T, const ID: u64> RawDelayedWorkItem<ID> for Arc<T>
where
T: WorkItem<ID, Pointer = Self>,
T: HasDelayedWork<T, ID>,
{
}
unsafe impl<T, const ID: u64> WorkItemPointer<ID> for Pin<KBox<T>>
where
T: WorkItem<ID, Pointer = Self>,
T: HasWork<T, ID>,
{
unsafe extern "C" fn run(ptr: *mut bindings::work_struct) {
let ptr = ptr.cast::<Work<T, ID>>();
let ptr = unsafe { T::work_container_of(ptr) };
let boxed = unsafe { KBox::from_raw(ptr) };
let pinned = unsafe { Pin::new_unchecked(boxed) };
T::run(pinned)
}
}
unsafe impl<T, const ID: u64> RawWorkItem<ID> for Pin<KBox<T>>
where
T: WorkItem<ID, Pointer = Self>,
T: HasWork<T, ID>,
{
type EnqueueOutput = ();
unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput
where
F: FnOnce(*mut bindings::work_struct) -> bool,
{
let boxed = unsafe { Pin::into_inner_unchecked(self) };
let ptr = KBox::into_raw(boxed);
let work_ptr = unsafe { T::raw_get_work(ptr) };
let work_ptr = unsafe { Work::raw_get(work_ptr) };
if !queue_work_on(work_ptr) {
unsafe { ::core::hint::unreachable_unchecked() }
}
}
}
unsafe impl<T, const ID: u64> RawDelayedWorkItem<ID> for Pin<KBox<T>>
where
T: WorkItem<ID, Pointer = Self>,
T: HasDelayedWork<T, ID>,
{
}
unsafe impl<T, const ID: u64> WorkItemPointer<ID> for ARef<T>
where
T: AlwaysRefCounted,
T: WorkItem<ID, Pointer = Self>,
T: HasWork<T, ID>,
{
unsafe extern "C" fn run(ptr: *mut bindings::work_struct) {
let ptr = ptr.cast::<Work<T, ID>>();
let ptr = unsafe { T::work_container_of(ptr) };
let ptr = unsafe { NonNull::new_unchecked(ptr) };
let aref = unsafe { ARef::from_raw(ptr) };
T::run(aref)
}
}
unsafe impl<T, const ID: u64> RawWorkItem<ID> for ARef<T>
where
T: AlwaysRefCounted,
T: WorkItem<ID, Pointer = Self>,
T: HasWork<T, ID>,
{
type EnqueueOutput = Result<(), Self>;
unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput
where
F: FnOnce(*mut bindings::work_struct) -> bool,
{
let ptr = ARef::into_raw(self);
let work_ptr = unsafe { T::raw_get_work(ptr.as_ptr()) };
let work_ptr = unsafe { Work::raw_get(work_ptr) };
if queue_work_on(work_ptr) {
Ok(())
} else {
Err(unsafe { ARef::from_raw(ptr) })
}
}
}
unsafe impl<T, const ID: u64> RawDelayedWorkItem<ID> for ARef<T>
where
T: WorkItem<ID, Pointer = Self>,
T: HasDelayedWork<T, ID>,
T: AlwaysRefCounted,
{
}
pub fn system() -> &'static Queue {
unsafe { Queue::from_raw(bindings::system_wq) }
}
pub fn system_highpri() -> &'static Queue {
unsafe { Queue::from_raw(bindings::system_highpri_wq) }
}
pub fn system_long() -> &'static Queue {
unsafe { Queue::from_raw(bindings::system_long_wq) }
}
pub fn system_unbound() -> &'static Queue {
unsafe { Queue::from_raw(bindings::system_unbound_wq) }
}
pub fn system_freezable() -> &'static Queue {
unsafe { Queue::from_raw(bindings::system_freezable_wq) }
}
pub fn system_power_efficient() -> &'static Queue {
unsafe { Queue::from_raw(bindings::system_power_efficient_wq) }
}
pub fn system_freezable_power_efficient() -> &'static Queue {
unsafe { Queue::from_raw(bindings::system_freezable_power_efficient_wq) }
}
pub fn system_bh() -> &'static Queue {
unsafe { Queue::from_raw(bindings::system_bh_wq) }
}
pub fn system_bh_highpri() -> &'static Queue {
unsafe { Queue::from_raw(bindings::system_bh_highpri_wq) }
}