| 1 | |
| 2 |
|
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 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 | |
| 21 | |
| 22 | |
| 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 | |
| 43 | pub fn new() -> Self {
|
| 44 | Self::default()
|
| 45 | }
|
| 46 |
|
| 47 | |
| 48 | pub fn rotational(mut self, rotational: bool) -> Self {
|
| 49 | self.rotational = rotational;
|
| 50 | self
|
| 51 | }
|
| 52 |
|
| 53 | |
| 54 | |
| 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 | |
| 64 | |
| 65 | |
| 66 | |
| 67 | |
| 68 | |
| 69 | |
| 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 | |
| 77 | |
| 78 | |
| 79 | |
| 80 | |
| 81 | |
| 82 | |
| 83 | |
| 84 | |
| 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 | |
| 92 | pub fn capacity_sectors(mut self, capacity: u64) -> Self {
|
| 93 | self.capacity_sectors = capacity;
|
| 94 | self
|
| 95 | }
|
| 96 |
|
| 97 | |
| 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 | |
| 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 | |
| 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 | |
| 144 | owner: core::ptr::null_mut(),
|
| 145 | pr_ops: core::ptr::null_mut(),
|
| 146 | free_disk: None,
|
| 147 | poll_bio: None,
|
| 148 | };
|
| 149 |
|
| 150 | |
| 151 | unsafe { (*gendisk).fops = &TABLE };
|
| 152 |
|
| 153 | let cleanup_failure = ScopeGuard::new_with_data((gendisk, data), |(gendisk, data)| {
|
| 154 | |
| 155 | |
| 156 | unsafe { bindings::put_disk(gendisk) };
|
| 157 | |
| 158 | |
| 159 | drop(unsafe { T::QueueData::from_foreign(data) });
|
| 160 | });
|
| 161 |
|
| 162 | |
| 163 | |
| 164 | recover_data.dismiss();
|
| 165 |
|
| 166 | let mut writer = NullTerminatedFormatter::new(
|
| 167 | |
| 168 | |
| 169 | |
| 170 | unsafe { &mut (*gendisk).disk_name },
|
| 171 | )
|
| 172 | .ok_or(EINVAL)?;
|
| 173 | writer.write_fmt(name)?;
|
| 174 |
|
| 175 | |
| 176 | |
| 177 | |
| 178 | unsafe { bindings::set_capacity(gendisk, self.capacity_sectors) };
|
| 179 |
|
| 180 | crate::error::to_result(
|
| 181 | |
| 182 | |
| 183 | unsafe {
|
| 184 | bindings::device_add_disk(core::ptr::null_mut(), gendisk, core::ptr::null_mut())
|
| 185 | },
|
| 186 | )?;
|
| 187 |
|
| 188 | cleanup_failure.dismiss();
|
| 189 |
|
| 190 | |
| 191 | |
| 192 | |
| 193 | |
| 194 | Ok(GenDisk {
|
| 195 | _tagset: tagset,
|
| 196 | gendisk,
|
| 197 | })
|
| 198 | }
|
| 199 | }
|
| 200 |
|
| 201 | |
| 202 | |
| 203 | |
| 204 | |
| 205 | |
| 206 | |
| 207 | |
| 208 | |
| 209 | pub struct GenDisk<T: Operations> {
|
| 210 | _tagset: Arc<TagSet<T>>,
|
| 211 | gendisk: *mut bindings::gendisk,
|
| 212 | }
|
| 213 |
|
| 214 | |
| 215 | |
| 216 | unsafe impl<T: Operations + Send> Send for GenDisk<T> {}
|
| 217 |
|
| 218 | impl<T: Operations> Drop for GenDisk<T> {
|
| 219 | fn drop(&mut self) {
|
| 220 | |
| 221 | |
| 222 | |
| 223 | |
| 224 | let queue_data = unsafe { (*(*self.gendisk).queue).queuedata };
|
| 225 |
|
| 226 | |
| 227 | |
| 228 | |
| 229 | unsafe { bindings::del_gendisk(self.gendisk) };
|
| 230 |
|
| 231 | |
| 232 | |
| 233 | |
| 234 | unsafe { bindings::put_disk(self.gendisk) };
|
| 235 |
|
| 236 | |
| 237 | |
| 238 | |
| 239 | drop(unsafe { T::QueueData::from_foreign(queue_data) });
|
| 240 | }
|
| 241 | }
|