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