Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.10.11.
  1// SPDX-License-Identifier: GPL-2.0
  2
  3//! Generic disk abstraction.
  4//!
  5//! C header: [`include/linux/blkdev.h`](srctree/include/linux/blkdev.h)
  6//! C header: [`include/linux/blk_mq.h`](srctree/include/linux/blk_mq.h)
  7
  8use crate::block::mq::{raw_writer::RawWriter, Operations, TagSet};
  9use crate::{bindings, error::from_err_ptr, error::Result, sync::Arc};
 10use crate::{error, static_lock_class};
 11use core::fmt::{self, Write};
 12
 13/// A builder for [`GenDisk`].
 14///
 15/// Use this struct to configure and add new [`GenDisk`] to the VFS.
 16pub struct GenDiskBuilder {
 17    rotational: bool,
 18    logical_block_size: u32,
 19    physical_block_size: u32,
 20    capacity_sectors: u64,
 21}
 22
 23impl Default for GenDiskBuilder {
 24    fn default() -> Self {
 25        Self {
 26            rotational: false,
 27            logical_block_size: bindings::PAGE_SIZE as u32,
 28            physical_block_size: bindings::PAGE_SIZE as u32,
 29            capacity_sectors: 0,
 30        }
 31    }
 32}
 33
 34impl GenDiskBuilder {
 35    /// Create a new instance.
 36    pub fn new() -> Self {
 37        Self::default()
 38    }
 39
 40    /// Set the rotational media attribute for the device to be built.
 41    pub fn rotational(mut self, rotational: bool) -> Self {
 42        self.rotational = rotational;
 43        self
 44    }
 45
 46    /// Validate block size by verifying that it is between 512 and `PAGE_SIZE`,
 47    /// and that it is a power of two.
 48    fn validate_block_size(size: u32) -> Result {
 49        if !(512..=bindings::PAGE_SIZE as u32).contains(&size) || !size.is_power_of_two() {
 50            Err(error::code::EINVAL)
 51        } else {
 52            Ok(())
 53        }
 54    }
 55
 56    /// Set the logical block size of the device to be built.
 57    ///
 58    /// This method will check that block size is a power of two and between 512
 59    /// and 4096. If not, an error is returned and the block size is not set.
 60    ///
 61    /// This is the smallest unit the storage device can address. It is
 62    /// typically 4096 bytes.
 63    pub fn logical_block_size(mut self, block_size: u32) -> Result<Self> {
 64        Self::validate_block_size(block_size)?;
 65        self.logical_block_size = block_size;
 66        Ok(self)
 67    }
 68
 69    /// Set the physical block size of the device to be built.
 70    ///
 71    /// This method will check that block size is a power of two and between 512
 72    /// and 4096. If not, an error is returned and the block size is not set.
 73    ///
 74    /// This is the smallest unit a physical storage device can write
 75    /// atomically. It is usually the same as the logical block size but may be
 76    /// bigger. One example is SATA drives with 4096 byte physical block size
 77    /// that expose a 512 byte logical block size to the operating system.
 78    pub fn physical_block_size(mut self, block_size: u32) -> Result<Self> {
 79        Self::validate_block_size(block_size)?;
 80        self.physical_block_size = block_size;
 81        Ok(self)
 82    }
 83
 84    /// Set the capacity of the device to be built, in sectors (512 bytes).
 85    pub fn capacity_sectors(mut self, capacity: u64) -> Self {
 86        self.capacity_sectors = capacity;
 87        self
 88    }
 89
 90    /// Build a new `GenDisk` and add it to the VFS.
 91    pub fn build<T: Operations>(
 92        self,
 93        name: fmt::Arguments<'_>,
 94        tagset: Arc<TagSet<T>>,
 95    ) -> Result<GenDisk<T>> {
 96        // SAFETY: `bindings::queue_limits` contain only fields that are valid when zeroed.
 97        let mut lim: bindings::queue_limits = unsafe { core::mem::zeroed() };
 98
 99        lim.logical_block_size = self.logical_block_size;
100        lim.physical_block_size = self.physical_block_size;
101        if self.rotational {
102            lim.features = bindings::BLK_FEAT_ROTATIONAL;
103        }
104
105        // SAFETY: `tagset.raw_tag_set()` points to a valid and initialized tag set
106        let gendisk = from_err_ptr(unsafe {
107            bindings::__blk_mq_alloc_disk(
108                tagset.raw_tag_set(),
109                &mut lim,
110                core::ptr::null_mut(),
111                static_lock_class!().as_ptr(),
112            )
113        })?;
114
115        const TABLE: bindings::block_device_operations = bindings::block_device_operations {
116            submit_bio: None,
117            open: None,
118            release: None,
119            ioctl: None,
120            compat_ioctl: None,
121            check_events: None,
122            unlock_native_capacity: None,
123            getgeo: None,
124            set_read_only: None,
125            swap_slot_free_notify: None,
126            report_zones: None,
127            devnode: None,
128            alternative_gpt_sector: None,
129            get_unique_id: None,
130            // TODO: Set to THIS_MODULE. Waiting for const_refs_to_static feature to
131            // be merged (unstable in rustc 1.78 which is staged for linux 6.10)
132            // https://github.com/rust-lang/rust/issues/119618
133            owner: core::ptr::null_mut(),
134            pr_ops: core::ptr::null_mut(),
135            free_disk: None,
136            poll_bio: None,
137        };
138
139        // SAFETY: `gendisk` is a valid pointer as we initialized it above
140        unsafe { (*gendisk).fops = &TABLE };
141
142        let mut raw_writer = RawWriter::from_array(
143            // SAFETY: `gendisk` points to a valid and initialized instance. We
144            // have exclusive access, since the disk is not added to the VFS
145            // yet.
146            unsafe { &mut (*gendisk).disk_name },
147        )?;
148        raw_writer.write_fmt(name)?;
149        raw_writer.write_char('\0')?;
150
151        // SAFETY: `gendisk` points to a valid and initialized instance of
152        // `struct gendisk`. `set_capacity` takes a lock to synchronize this
153        // operation, so we will not race.
154        unsafe { bindings::set_capacity(gendisk, self.capacity_sectors) };
155
156        crate::error::to_result(
157            // SAFETY: `gendisk` points to a valid and initialized instance of
158            // `struct gendisk`.
159            unsafe {
160                bindings::device_add_disk(core::ptr::null_mut(), gendisk, core::ptr::null_mut())
161            },
162        )?;
163
164        // INVARIANT: `gendisk` was initialized above.
165        // INVARIANT: `gendisk` was added to the VFS via `device_add_disk` above.
166        Ok(GenDisk {
167            _tagset: tagset,
168            gendisk,
169        })
170    }
171}
172
173/// A generic block device.
174///
175/// # Invariants
176///
177/// - `gendisk` must always point to an initialized and valid `struct gendisk`.
178/// - `gendisk` was added to the VFS through a call to
179///   `bindings::device_add_disk`.
180pub struct GenDisk<T: Operations> {
181    _tagset: Arc<TagSet<T>>,
182    gendisk: *mut bindings::gendisk,
183}
184
185// SAFETY: `GenDisk` is an owned pointer to a `struct gendisk` and an `Arc` to a
186// `TagSet` It is safe to send this to other threads as long as T is Send.
187unsafe impl<T: Operations + Send> Send for GenDisk<T> {}
188
189impl<T: Operations> Drop for GenDisk<T> {
190    fn drop(&mut self) {
191        // SAFETY: By type invariant, `self.gendisk` points to a valid and
192        // initialized instance of `struct gendisk`, and it was previously added
193        // to the VFS.
194        unsafe { bindings::del_gendisk(self.gendisk) };
195    }
196}