Add a lifetime parameter to IoMem<'a, SIZE> and ExclusiveIoMem<'a, SIZE>, storing a &'a Device<Bound> reference to tie the mapping to the device's lifetime. This mirrors the pci::Bar<'a, SIZE> design and enables drivers to hold I/O memory mappings directly in their HRT private data, tied to the device lifetime. IoRequest::iomap_* methods now return the mapping directly instead of wrapping it in Devres. Callers that need device-managed revocation can call the new into_devres() method.
Showing the changes to rust/kernel (1 of 3 files). Show all
Unified · Split
| @@ -74,22 +74,19 @@ impl<'a> IoRequest<'a> { · 73 unmodified lines | ||
| 74 | 74 | /// // |
| 75 | 75 | /// // No runtime checks will apply when reading and writing. |
| 76 | 76 | /// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; |
| 77 | − /// let iomem = request.iomap_sized::<42>(); | |
| 78 | − /// let iomem = KBox::pin_init(iomem, GFP_KERNEL)?; | |
| 79 | − /// | |
| 80 | − /// let io = iomem.access(pdev.as_ref())?; | |
| 77 | + /// let iomem = request.iomap_sized::<42>()?; | |
| 81 | 78 | /// |
| 82 | 79 | /// // Read and write a 32-bit value at `offset`. |
| 83 | − /// let data = io.read32(offset); | |
| 80 | + /// let data = iomem.read32(offset); | |
| 84 | 81 | /// |
| 85 | − /// io.write32(data, offset); | |
| 82 | + /// iomem.write32(data, offset); | |
| 86 | 83 | /// |
| 87 | 84 | /// # Ok(SampleDriver) |
| 88 | 85 | /// } |
| 89 | 86 | /// } |
| 90 | 87 | /// ``` |
| 91 | − pub fn iomap_sized<const SIZE: usize>(self) -> impl PinInit<Devres<IoMem<SIZE>>, Error> + 'a { | |
| 92 | − IoMem::new(self) | |
| 88 | + pub fn iomap_sized<const SIZE: usize>(self) -> Result<IoMem<'a, SIZE>> { | |
| 89 | + IoMem::ioremap(self.device, self.resource) | |
| 93 | 90 | } |
| 94 | 91 | |
| 95 | 92 | /// Same as [`Self::iomap_sized`] but with exclusive access to the |
| @@ -98,10 +95,8 @@ impl<'a> IoRequest<'a> { · 2 unmodified lines | ||
| 98 | 95 | /// This uses the [`ioremap()`] C API. |
| 99 | 96 | /// |
| 100 | 97 | /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device |
| 101 | − pub fn iomap_exclusive_sized<const SIZE: usize>( | |
| 102 | − self, | |
| 103 | − ) -> impl PinInit<Devres<ExclusiveIoMem<SIZE>>, Error> + 'a { | |
| 104 | − ExclusiveIoMem::new(self) | |
| 98 | + pub fn iomap_exclusive_sized<const SIZE: usize>(self) -> Result<ExclusiveIoMem<'a, SIZE>> { | |
| 99 | + ExclusiveIoMem::ioremap(self.device, self.resource) | |
| 105 | 100 | } |
| 106 | 101 | |
| 107 | 102 | /// Maps an [`IoRequest`] where the size is not known at compile time, |
| @@ -140,27 +135,24 @@ impl<'a> IoRequest<'a> { · 32 unmodified lines | ||
| 140 | 135 | /// // family of functions should be used, leading to runtime checks on every |
| 141 | 136 | /// // access. |
| 142 | 137 | /// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; |
| 143 | − /// let iomem = request.iomap(); | |
| 144 | − /// let iomem = KBox::pin_init(iomem, GFP_KERNEL)?; | |
| 145 | − /// | |
| 146 | − /// let io = iomem.access(pdev.as_ref())?; | |
| 138 | + /// let iomem = request.iomap()?; | |
| 147 | 139 | /// |
| 148 | − /// let data = io.try_read32(offset)?; | |
| 140 | + /// let data = iomem.try_read32(offset)?; | |
| 149 | 141 | /// |
| 150 | − /// io.try_write32(data, offset)?; | |
| 142 | + /// iomem.try_write32(data, offset)?; | |
| 151 | 143 | /// |
| 152 | 144 | /// # Ok(SampleDriver) |
| 153 | 145 | /// } |
| 154 | 146 | /// } |
| 155 | 147 | /// ``` |
| 156 | − pub fn iomap(self) -> impl PinInit<Devres<IoMem<0>>, Error> + 'a { | |
| 157 | − Self::iomap_sized::<0>(self) | |
| 148 | + pub fn iomap(self) -> Result<IoMem<'a>> { | |
| 149 | + self.iomap_sized::<0>() | |
| 158 | 150 | } |
| 159 | 151 | |
| 160 | 152 | /// Same as [`Self::iomap`] but with exclusive access to the underlying |
| 161 | 153 | /// region. |
| 162 | − pub fn iomap_exclusive(self) -> impl PinInit<Devres<ExclusiveIoMem<0>>, Error> + 'a { | |
| 163 | − Self::iomap_exclusive_sized::<0>(self) | |
| 154 | + pub fn iomap_exclusive(self) -> Result<ExclusiveIoMem<'a, 0>> { | |
| 155 | + self.iomap_exclusive_sized::<0>() | |
| 164 | 156 | } |
| 165 | 157 | } |
| 166 | 158 | |
| @@ -169,9 +161,9 @@ impl<'a> IoRequest<'a> { · 2 unmodified lines | ||
| 169 | 161 | /// # Invariants |
| 170 | 162 | /// |
| 171 | 163 | /// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`]. |
| 172 | −pub struct ExclusiveIoMem<const SIZE: usize> { | |
| 164 | +pub struct ExclusiveIoMem<'a, const SIZE: usize> { | |
| 173 | 165 | /// The underlying `IoMem` instance. |
| 174 | − iomem: IoMem<SIZE>, | |
| 166 | + iomem: IoMem<'a, SIZE>, | |
| 175 | 167 | |
| 176 | 168 | /// The region abstraction. This represents exclusive access to the |
| 177 | 169 | /// range represented by the underlying `iomem`. |
| @@ -180,9 +172,9 @@ pub struct ExclusiveIoMem<const SIZE: usize> { · 2 unmodified lines | ||
| 180 | 172 | _region: Region, |
| 181 | 173 | } |
| 182 | 174 | |
| 183 | −impl<const SIZE: usize> ExclusiveIoMem<SIZE> { | |
| 175 | +impl<'a, const SIZE: usize> ExclusiveIoMem<'a, SIZE> { | |
| 184 | 176 | /// Creates a new `ExclusiveIoMem` instance. |
| 185 | − fn ioremap(resource: &Resource) -> Result<Self> { | |
| 177 | + fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> { | |
| 186 | 178 | let start = resource.start(); |
| 187 | 179 | let size = resource.size(); |
| 188 | 180 | let name = resource.name().unwrap_or_default(); |
| @@ -196,26 +188,29 @@ impl<const SIZE: usize> ExclusiveIoMem<SIZE> { · 7 unmodified lines | ||
| 196 | 188 | ) |
| 197 | 189 | .ok_or(EBUSY)?; |
| 198 | 190 | |
| 199 | − let iomem = IoMem::ioremap(resource)?; | |
| 191 | + let iomem = IoMem::ioremap(dev, resource)?; | |
| 200 | 192 | |
| 201 | − let iomem = ExclusiveIoMem { | |
| 193 | + Ok(ExclusiveIoMem { | |
| 202 | 194 | iomem, |
| 203 | 195 | _region: region, |
| 204 | − }; | |
| 205 | − | |
| 206 | − Ok(iomem) | |
| 196 | + }) | |
| 207 | 197 | } |
| 208 | 198 | |
| 209 | − /// Creates a new `ExclusiveIoMem` instance from a previously acquired [`IoRequest`]. | |
| 210 | − pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a { | |
| 211 | − let dev = io_request.device; | |
| 212 | − let res = io_request.resource; | |
| 213 | − | |
| 214 | − Devres::new(dev, Self::ioremap(res)) | |
| 199 | + /// Consume the `ExclusiveIoMem` and register it as a device-managed resource. | |
| 200 | + /// | |
| 201 | + /// The returned `Devres<ExclusiveIoMem<'static, SIZE>>` can outlive the original lifetime | |
| 202 | + /// `'a`. Access to the I/O memory is revoked when the device is unbound. | |
| 203 | + pub fn into_devres(self) -> Result<Devres<ExclusiveIoMem<'static, SIZE>>> { | |
| 204 | + // SAFETY: Casting to `'static` is sound because `Devres` guarantees the | |
| 205 | + // `ExclusiveIoMem` does not actually outlive the device -- access is revoked and the | |
| 206 | + // resource is released when the device is unbound. | |
| 207 | + let iomem: ExclusiveIoMem<'static, SIZE> = unsafe { core::mem::transmute(self) }; | |
| 208 | + let dev = iomem.iomem.dev; | |
| 209 | + Devres::new(dev, iomem) | |
| 215 | 210 | } |
| 216 | 211 | } |
| 217 | 212 | |
| 218 | −impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> { | |
| 213 | +impl<const SIZE: usize> Deref for ExclusiveIoMem<'_, SIZE> { | |
| 219 | 214 | type Target = Mmio<SIZE>; |
| 220 | 215 | |
| 221 | 216 | fn deref(&self) -> &Self::Target { |
| @@ -232,12 +227,13 @@ impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> { · 10 unmodified lines | ||
| 232 | 227 | /// |
| 233 | 228 | /// [`IoMem`] always holds an [`MmioRaw`] instance that holds a valid pointer to the |
| 234 | 229 | /// start of the I/O memory mapped region. |
| 235 | −pub struct IoMem<const SIZE: usize = 0> { | |
| 230 | +pub struct IoMem<'a, const SIZE: usize = 0> { | |
| 231 | + dev: &'a Device<Bound>, | |
| 236 | 232 | io: MmioRaw<SIZE>, |
| 237 | 233 | } |
| 238 | 234 | |
| 239 | −impl<const SIZE: usize> IoMem<SIZE> { | |
| 240 | − fn ioremap(resource: &Resource) -> Result<Self> { | |
| 235 | +impl<'a, const SIZE: usize> IoMem<'a, SIZE> { | |
| 236 | + fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> { | |
| 241 | 237 | // Note: Some ioremap() implementations use types that depend on the CPU |
| 242 | 238 | // word width rather than the bus address width. |
| 243 | 239 | // |
| @@ -269,28 +265,33 @@ impl<const SIZE: usize> IoMem<SIZE> { · 25 unmodified lines | ||
| 269 | 265 | } |
| 270 | 266 | |
| 271 | 267 | let io = MmioRaw::new(addr as usize, size)?; |
| 272 | − let io = IoMem { io }; | |
| 273 | 268 | |
| 274 | − Ok(io) | |
| 269 | + Ok(IoMem { dev, io }) | |
| 275 | 270 | } |
| 276 | 271 | |
| 277 | − /// Creates a new `IoMem` instance from a previously acquired [`IoRequest`]. | |
| 278 | − pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a { | |
| 279 | − let dev = io_request.device; | |
| 280 | − let res = io_request.resource; | |
| 281 | − | |
| 282 | − Devres::new(dev, Self::ioremap(res)) | |
| 272 | + /// Consume the `IoMem` and register it as a device-managed resource. | |
| 273 | + /// | |
| 274 | + /// The returned `Devres<IoMem<'static, SIZE>>` can outlive the original | |
| 275 | + /// lifetime `'a`. Access to the I/O memory is revoked when the device | |
| 276 | + /// is unbound. | |
| 277 | + pub fn into_devres(self) -> Result<Devres<IoMem<'static, SIZE>>> { | |
| 278 | + // SAFETY: Casting to `'static` is sound because `Devres` guarantees the `IoMem` does not | |
| 279 | + // actually outlive the device -- access is revoked and the resource is released when the | |
| 280 | + // device is unbound. | |
| 281 | + let iomem: IoMem<'static, SIZE> = unsafe { core::mem::transmute(self) }; | |
| 282 | + let dev = iomem.dev; | |
| 283 | + Devres::new(dev, iomem) | |
| 283 | 284 | } |
| 284 | 285 | } |
| 285 | 286 | |
| 286 | −impl<const SIZE: usize> Drop for IoMem<SIZE> { | |
| 287 | +impl<const SIZE: usize> Drop for IoMem<'_, SIZE> { | |
| 287 | 288 | fn drop(&mut self) { |
| 288 | 289 | // SAFETY: Safe as by the invariant of `Io`. |
| 289 | 290 | unsafe { bindings::iounmap(self.io.addr() as *mut c_void) } |
| 290 | 291 | } |
| 291 | 292 | } |
| 292 | 293 | |
| 293 | −impl<const SIZE: usize> Deref for IoMem<SIZE> { | |
| 294 | +impl<const SIZE: usize> Deref for IoMem<'_, SIZE> { | |
| 294 | 295 | type Target = Mmio<SIZE>; |
| 295 | 296 | |
| 296 | 297 | fn deref(&self) -> &Self::Target { |