Commit | Line | Data |
---|---|---|
7b1f55e3 WAF |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | ||
3 | //! A wrapper for data protected by a lock that does not wrap it. | |
4 | ||
5 | use super::{lock::Backend, lock::Lock}; | |
6 | use crate::build_assert; | |
7 | use core::{cell::UnsafeCell, mem::size_of, ptr}; | |
8 | ||
9 | /// Allows access to some data to be serialised by a lock that does not wrap it. | |
10 | /// | |
11 | /// In most cases, data protected by a lock is wrapped by the appropriate lock type, e.g., | |
ed6d0bed VO |
12 | /// [`Mutex`] or [`SpinLock`]. [`LockedBy`] is meant for cases when this is not possible. |
13 | /// For example, if a container has a lock and some data in the contained elements needs | |
7b1f55e3 WAF |
14 | /// to be protected by the same lock. |
15 | /// | |
16 | /// [`LockedBy`] wraps the data in lieu of another locking primitive, and only allows access to it | |
17 | /// when the caller shows evidence that the 'external' lock is locked. It panics if the evidence | |
18 | /// refers to the wrong instance of the lock. | |
19 | /// | |
ed6d0bed VO |
20 | /// [`Mutex`]: super::Mutex |
21 | /// [`SpinLock`]: super::SpinLock | |
22 | /// | |
7b1f55e3 WAF |
23 | /// # Examples |
24 | /// | |
25 | /// The following is an example for illustrative purposes: `InnerDirectory::bytes_used` is an | |
26 | /// aggregate of all `InnerFile::bytes_used` and must be kept consistent; so we wrap `InnerFile` in | |
27 | /// a `LockedBy` so that it shares a lock with `InnerDirectory`. This allows us to enforce at | |
28 | /// compile-time that access to `InnerFile` is only granted when an `InnerDirectory` is also | |
29 | /// locked; we enforce at run time that the right `InnerDirectory` is locked. | |
30 | /// | |
31 | /// ``` | |
32 | /// use kernel::sync::{LockedBy, Mutex}; | |
33 | /// | |
34 | /// struct InnerFile { | |
35 | /// bytes_used: u64, | |
36 | /// } | |
37 | /// | |
38 | /// struct File { | |
39 | /// _ino: u32, | |
40 | /// inner: LockedBy<InnerFile, InnerDirectory>, | |
41 | /// } | |
42 | /// | |
43 | /// struct InnerDirectory { | |
44 | /// /// The sum of the bytes used by all files. | |
45 | /// bytes_used: u64, | |
46 | /// _files: Vec<File>, | |
47 | /// } | |
48 | /// | |
49 | /// struct Directory { | |
50 | /// _ino: u32, | |
51 | /// inner: Mutex<InnerDirectory>, | |
52 | /// } | |
53 | /// | |
54 | /// /// Prints `bytes_used` from both the directory and file. | |
55 | /// fn print_bytes_used(dir: &Directory, file: &File) { | |
56 | /// let guard = dir.inner.lock(); | |
57 | /// let inner_file = file.inner.access(&guard); | |
58 | /// pr_info!("{} {}", guard.bytes_used, inner_file.bytes_used); | |
59 | /// } | |
60 | /// | |
61 | /// /// Increments `bytes_used` for both the directory and file. | |
62 | /// fn inc_bytes_used(dir: &Directory, file: &File) { | |
63 | /// let mut guard = dir.inner.lock(); | |
64 | /// guard.bytes_used += 10; | |
65 | /// | |
66 | /// let file_inner = file.inner.access_mut(&mut guard); | |
67 | /// file_inner.bytes_used += 10; | |
68 | /// } | |
69 | /// | |
70 | /// /// Creates a new file. | |
71 | /// fn new_file(ino: u32, dir: &Directory) -> File { | |
72 | /// File { | |
73 | /// _ino: ino, | |
74 | /// inner: LockedBy::new(&dir.inner, InnerFile { bytes_used: 0 }), | |
75 | /// } | |
76 | /// } | |
77 | /// ``` | |
78 | pub struct LockedBy<T: ?Sized, U: ?Sized> { | |
79 | owner: *const U, | |
80 | data: UnsafeCell<T>, | |
81 | } | |
82 | ||
83 | // SAFETY: `LockedBy` can be transferred across thread boundaries iff the data it protects can. | |
84 | unsafe impl<T: ?Sized + Send, U: ?Sized> Send for LockedBy<T, U> {} | |
85 | ||
86 | // SAFETY: `LockedBy` serialises the interior mutability it provides, so it is `Sync` as long as the | |
87 | // data it protects is `Send`. | |
88 | unsafe impl<T: ?Sized + Send, U: ?Sized> Sync for LockedBy<T, U> {} | |
89 | ||
90 | impl<T, U> LockedBy<T, U> { | |
91 | /// Constructs a new instance of [`LockedBy`]. | |
92 | /// | |
93 | /// It stores a raw pointer to the owner that is never dereferenced. It is only used to ensure | |
94 | /// that the right owner is being used to access the protected data. If the owner is freed, the | |
95 | /// data becomes inaccessible; if another instance of the owner is allocated *on the same | |
96 | /// memory location*, the data becomes accessible again: none of this affects memory safety | |
97 | /// because in any case at most one thread (or CPU) can access the protected data at a time. | |
98 | pub fn new<B: Backend>(owner: &Lock<U, B>, data: T) -> Self { | |
99 | build_assert!( | |
100 | size_of::<Lock<U, B>>() > 0, | |
101 | "The lock type cannot be a ZST because it may be impossible to distinguish instances" | |
102 | ); | |
103 | Self { | |
104 | owner: owner.data.get(), | |
105 | data: UnsafeCell::new(data), | |
106 | } | |
107 | } | |
108 | } | |
109 | ||
110 | impl<T: ?Sized, U> LockedBy<T, U> { | |
111 | /// Returns a reference to the protected data when the caller provides evidence (via a | |
112 | /// reference) that the owner is locked. | |
113 | /// | |
114 | /// `U` cannot be a zero-sized type (ZST) because there are ways to get an `&U` that matches | |
115 | /// the data protected by the lock without actually holding it. | |
116 | /// | |
117 | /// # Panics | |
118 | /// | |
119 | /// Panics if `owner` is different from the data protected by the lock used in | |
120 | /// [`new`](LockedBy::new). | |
121 | pub fn access<'a>(&'a self, owner: &'a U) -> &'a T { | |
122 | build_assert!( | |
123 | size_of::<U>() > 0, | |
124 | "`U` cannot be a ZST because `owner` wouldn't be unique" | |
125 | ); | |
126 | if !ptr::eq(owner, self.owner) { | |
127 | panic!("mismatched owners"); | |
128 | } | |
129 | ||
130 | // SAFETY: `owner` is evidence that the owner is locked. | |
131 | unsafe { &*self.data.get() } | |
132 | } | |
133 | ||
134 | /// Returns a mutable reference to the protected data when the caller provides evidence (via a | |
135 | /// mutable owner) that the owner is locked mutably. | |
136 | /// | |
137 | /// `U` cannot be a zero-sized type (ZST) because there are ways to get an `&mut U` that | |
138 | /// matches the data protected by the lock without actually holding it. | |
139 | /// | |
140 | /// Showing a mutable reference to the owner is sufficient because we know no other references | |
141 | /// can exist to it. | |
142 | /// | |
143 | /// # Panics | |
144 | /// | |
145 | /// Panics if `owner` is different from the data protected by the lock used in | |
146 | /// [`new`](LockedBy::new). | |
147 | pub fn access_mut<'a>(&'a self, owner: &'a mut U) -> &'a mut T { | |
148 | build_assert!( | |
149 | size_of::<U>() > 0, | |
150 | "`U` cannot be a ZST because `owner` wouldn't be unique" | |
151 | ); | |
152 | if !ptr::eq(owner, self.owner) { | |
153 | panic!("mismatched owners"); | |
154 | } | |
155 | ||
156 | // SAFETY: `owner` is evidence that there is only one reference to the owner. | |
157 | unsafe { &mut *self.data.get() } | |
158 | } | |
159 | } |