|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | + |
| 3 | +//! A kernel read/write mutex. |
| 4 | +//! |
| 5 | +//! This module allows Rust code to use the kernel's [`struct rw_semaphore`]. |
| 6 | +//! |
| 7 | +//! C header: [`include/linux/rwsem.h`](../../../../include/linux/rwsem.h) |
| 8 | +
|
| 9 | +use super::{mutex::EmptyGuardContext, CreatableLock, Guard, Lock, ReadLock}; |
| 10 | +use crate::{bindings, str::CStr, Opaque}; |
| 11 | +use core::{cell::UnsafeCell, marker::PhantomPinned, pin::Pin}; |
| 12 | + |
| 13 | +/// Safely initialises a [`RwSemaphore`] with the given name, generating a new lock class. |
| 14 | +#[macro_export] |
| 15 | +macro_rules! rwsemaphore_init { |
| 16 | + ($rwsem:expr, $name:literal) => { |
| 17 | + $crate::init_with_lockdep!($rwsem, $name) |
| 18 | + }; |
| 19 | +} |
| 20 | + |
| 21 | +/// Exposes the kernel's [`struct rw_semaphore`]. |
| 22 | +/// |
| 23 | +/// It's a read/write mutex. That is, it allows multiple readers to acquire it concurrently, but |
| 24 | +/// only one writer at a time. On contention, waiters sleep. |
| 25 | +/// |
| 26 | +/// A [`RwSemaphore`] must first be initialised with a call to [`RwSemaphore::init_lock`] before it |
| 27 | +/// can be used. The [`rwsemaphore_init`] macro is provided to automatically assign a new lock |
| 28 | +/// class to an [`RwSemaphore`] instance. |
| 29 | +/// |
| 30 | +/// Since it may block, [`RwSemaphore`] needs to be used with care in atomic contexts. |
| 31 | +/// |
| 32 | +/// [`struct rw_semaphore`]: ../../../include/linux/rwsem.h |
| 33 | +pub struct RwSemaphore<T: ?Sized> { |
| 34 | + /// The kernel `struct rw_semaphore` object. |
| 35 | + rwsem: Opaque<bindings::rw_semaphore>, |
| 36 | + |
| 37 | + /// An rwsem needs to be pinned because it contains a [`struct list_head`] that is |
| 38 | + /// self-referential, so it cannot be safely moved once it is initialised. |
| 39 | + _pin: PhantomPinned, |
| 40 | + |
| 41 | + /// The data protected by the rwsem. |
| 42 | + data: UnsafeCell<T>, |
| 43 | +} |
| 44 | + |
| 45 | +// SAFETY: `RwSemaphore` can be transferred across thread boundaries iff the data it protects can. |
| 46 | +#[allow(clippy::non_send_fields_in_send_ty)] |
| 47 | +unsafe impl<T: ?Sized + Send> Send for RwSemaphore<T> {} |
| 48 | + |
| 49 | +// SAFETY: `RwSemaphore` requires that the protected type be `Sync` for it to be `Sync` as well |
| 50 | +// because the read mode allows multiple threads to access the protected data concurrently. It |
| 51 | +// requires `Send` because the write lock allows a `&mut T` to be accessible from an arbitrary |
| 52 | +// thread. |
| 53 | +unsafe impl<T: ?Sized + Send + Sync> Sync for RwSemaphore<T> {} |
| 54 | + |
| 55 | +impl<T> RwSemaphore<T> { |
| 56 | + /// Constructs a new rw semaphore. |
| 57 | + /// |
| 58 | + /// # Safety |
| 59 | + /// |
| 60 | + /// The caller must call [`RwSemaphore::init_lock`] before using the rw semaphore. |
| 61 | + pub unsafe fn new(t: T) -> Self { |
| 62 | + Self { |
| 63 | + rwsem: Opaque::uninit(), |
| 64 | + data: UnsafeCell::new(t), |
| 65 | + _pin: PhantomPinned, |
| 66 | + } |
| 67 | + } |
| 68 | +} |
| 69 | + |
| 70 | +impl<T: ?Sized> RwSemaphore<T> { |
| 71 | + /// Locks the rw semaphore in write (exclusive) mode and gives the caller access to the data |
| 72 | + /// protected by it. Only one thread at a time is allowed to access the protected data. |
| 73 | + pub fn write(&self) -> Guard<'_, Self> { |
| 74 | + let ctx = <Self as Lock>::lock_noguard(self); |
| 75 | + // SAFETY: The rw semaphore was just acquired in write mode. |
| 76 | + unsafe { Guard::new(self, ctx) } |
| 77 | + } |
| 78 | + |
| 79 | + /// Locks the rw semaphore in read (shared) mode and gives the caller access to the data |
| 80 | + /// protected by it. Only one thread at a time is allowed to access the protected data. |
| 81 | + pub fn read(&self) -> Guard<'_, Self, ReadLock> { |
| 82 | + let ctx = <Self as Lock<ReadLock>>::lock_noguard(self); |
| 83 | + // SAFETY: The rw semaphore was just acquired in read mode. |
| 84 | + unsafe { Guard::new(self, ctx) } |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +impl<T> CreatableLock for RwSemaphore<T> { |
| 89 | + unsafe fn new_lock(data: Self::Inner) -> Self { |
| 90 | + // SAFETY: The safety requirements of `new_lock` also require that `init_lock` be called. |
| 91 | + unsafe { Self::new(data) } |
| 92 | + } |
| 93 | + |
| 94 | + unsafe fn init_lock( |
| 95 | + self: Pin<&mut Self>, |
| 96 | + name: &'static CStr, |
| 97 | + key: *mut bindings::lock_class_key, |
| 98 | + ) { |
| 99 | + unsafe { bindings::__init_rwsem(self.rwsem.get(), name.as_char_ptr(), key) }; |
| 100 | + } |
| 101 | +} |
| 102 | + |
| 103 | +// SAFETY: The underlying kernel `struct rw_semaphore` object ensures mutual exclusion because it's |
| 104 | +// acquired in write mode. |
| 105 | +unsafe impl<T: ?Sized> Lock for RwSemaphore<T> { |
| 106 | + type Inner = T; |
| 107 | + type GuardContext = EmptyGuardContext; |
| 108 | + |
| 109 | + fn lock_noguard(&self) -> EmptyGuardContext { |
| 110 | + // SAFETY: `rwsem` points to valid memory. |
| 111 | + unsafe { bindings::down_write(self.rwsem.get()) }; |
| 112 | + EmptyGuardContext |
| 113 | + } |
| 114 | + |
| 115 | + unsafe fn unlock(&self, _: &mut EmptyGuardContext) { |
| 116 | + // SAFETY: The safety requirements of the function ensure that the rw semaphore is owned by |
| 117 | + // the caller. |
| 118 | + unsafe { bindings::up_write(self.rwsem.get()) }; |
| 119 | + } |
| 120 | + |
| 121 | + fn locked_data(&self) -> &UnsafeCell<T> { |
| 122 | + &self.data |
| 123 | + } |
| 124 | +} |
| 125 | + |
| 126 | +// SAFETY: The underlying kernel `struct rw_semaphore` object ensures that only shared references |
| 127 | +// are accessible from other threads because it's acquired in read mode. |
| 128 | +unsafe impl<T: ?Sized> Lock<ReadLock> for RwSemaphore<T> { |
| 129 | + type Inner = T; |
| 130 | + type GuardContext = EmptyGuardContext; |
| 131 | + |
| 132 | + fn lock_noguard(&self) -> EmptyGuardContext { |
| 133 | + // SAFETY: `rwsem` points to valid memory. |
| 134 | + unsafe { bindings::down_read(self.rwsem.get()) }; |
| 135 | + EmptyGuardContext |
| 136 | + } |
| 137 | + |
| 138 | + unsafe fn unlock(&self, _: &mut EmptyGuardContext) { |
| 139 | + // SAFETY: The safety requirements of the function ensure that the rw semaphore is owned by |
| 140 | + // the caller. |
| 141 | + unsafe { bindings::up_read(self.rwsem.get()) }; |
| 142 | + } |
| 143 | + |
| 144 | + fn locked_data(&self) -> &UnsafeCell<T> { |
| 145 | + &self.data |
| 146 | + } |
| 147 | +} |
0 commit comments