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 let mut lim: bindings::queue_limits = pin_init::zeroed(); 111 112 lim.logical_block_size = self.logical_block_size; 113 lim.physical_block_size = self.physical_block_size; 114 if self.rotational { 115 lim.features = bindings::BLK_FEAT_ROTATIONAL; 116 } 117 118 // SAFETY: `tagset.raw_tag_set()` points to a valid and initialized tag set 119 let gendisk = from_err_ptr(unsafe { 120 bindings::__blk_mq_alloc_disk( 121 tagset.raw_tag_set(), 122 &mut lim, 123 data, 124 static_lock_class!().as_ptr(), 125 ) 126 })?; 127 128 const TABLE: bindings::block_device_operations = bindings::block_device_operations { 129 submit_bio: None, 130 open: None, 131 release: None, 132 ioctl: None, 133 compat_ioctl: None, 134 check_events: None, 135 unlock_native_capacity: None, 136 getgeo: None, 137 set_read_only: None, 138 swap_slot_free_notify: None, 139 report_zones: None, 140 devnode: None, 141 alternative_gpt_sector: None, 142 get_unique_id: None, 143 // TODO: Set to THIS_MODULE. Waiting for const_refs_to_static feature to 144 // be merged (unstable in rustc 1.78 which is staged for linux 6.10) 145 // <https://github.com/rust-lang/rust/issues/119618> 146 owner: core::ptr::null_mut(), 147 pr_ops: core::ptr::null_mut(), 148 free_disk: None, 149 poll_bio: None, 150 }; 151 152 // SAFETY: `gendisk` is a valid pointer as we initialized it above 153 unsafe { (*gendisk).fops = &TABLE }; 154 155 let mut writer = NullTerminatedFormatter::new( 156 // SAFETY: `gendisk` points to a valid and initialized instance. We 157 // have exclusive access, since the disk is not added to the VFS 158 // yet. 159 unsafe { &mut (*gendisk).disk_name }, 160 ) 161 .ok_or(EINVAL)?; 162 writer.write_fmt(name)?; 163 164 // SAFETY: `gendisk` points to a valid and initialized instance of 165 // `struct gendisk`. `set_capacity` takes a lock to synchronize this 166 // operation, so we will not race. 167 unsafe { bindings::set_capacity(gendisk, self.capacity_sectors) }; 168 169 crate::error::to_result( 170 // SAFETY: `gendisk` points to a valid and initialized instance of 171 // `struct gendisk`. 172 unsafe { 173 bindings::device_add_disk(core::ptr::null_mut(), gendisk, core::ptr::null_mut()) 174 }, 175 )?; 176 177 recover_data.dismiss(); 178 179 // INVARIANT: `gendisk` was initialized above. 180 // INVARIANT: `gendisk` was added to the VFS via `device_add_disk` above. 181 // INVARIANT: `gendisk.queue.queue_data` is set to `data` in the call to 182 // `__blk_mq_alloc_disk` above. 183 Ok(GenDisk { 184 _tagset: tagset, 185 gendisk, 186 }) 187 } 188 } 189 190 /// A generic block device. 191 /// 192 /// # Invariants 193 /// 194 /// - `gendisk` must always point to an initialized and valid `struct gendisk`. 195 /// - `gendisk` was added to the VFS through a call to 196 /// `bindings::device_add_disk`. 197 /// - `self.gendisk.queue.queuedata` is initialized by a call to `ForeignOwnable::into_foreign`. 198 pub struct GenDisk<T: Operations> { 199 _tagset: Arc<TagSet<T>>, 200 gendisk: *mut bindings::gendisk, 201 } 202 203 // SAFETY: `GenDisk` is an owned pointer to a `struct gendisk` and an `Arc` to a 204 // `TagSet` It is safe to send this to other threads as long as T is Send. 205 unsafe impl<T: Operations + Send> Send for GenDisk<T> {} 206 207 impl<T: Operations> Drop for GenDisk<T> { 208 fn drop(&mut self) { 209 // SAFETY: By type invariant of `Self`, `self.gendisk` points to a valid 210 // and initialized instance of `struct gendisk`, and, `queuedata` was 211 // initialized with the result of a call to 212 // `ForeignOwnable::into_foreign`. 213 let queue_data = unsafe { (*(*self.gendisk).queue).queuedata }; 214 215 // SAFETY: By type invariant, `self.gendisk` points to a valid and 216 // initialized instance of `struct gendisk`, and it was previously added 217 // to the VFS. 218 unsafe { bindings::del_gendisk(self.gendisk) }; 219 220 // SAFETY: `queue.queuedata` was created by `GenDiskBuilder::build` with 221 // a call to `ForeignOwnable::into_foreign` to create `queuedata`. 222 // `ForeignOwnable::from_foreign` is only called here. 223 drop(unsafe { T::QueueData::from_foreign(queue_data) }); 224 } 225 } 226