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}