rust: block: introduce kernel::block::mq
module · oracle/linux-uek@3253aba (original) (raw)
``
1
`+
// SPDX-License-Identifier: GPL-2.0
`
``
2
+
``
3
`+
//! Generic disk abstraction.
`
``
4
`+
//!
`
``
5
`` +
//! C header: include/linux/blkdev.h
``
``
6
`` +
//! C header: include/linux/blk_mq.h
``
``
7
+
``
8
`+
use crate::block::mq::{raw_writer::RawWriter, Operations, TagSet};
`
``
9
`+
use crate::error;
`
``
10
`+
use crate::{bindings, error::from_err_ptr, error::Result, sync::Arc};
`
``
11
`+
use core::fmt::{self, Write};
`
``
12
+
``
13
`` +
/// A builder for [GenDisk
].
``
``
14
`+
///
`
``
15
`` +
/// Use this struct to configure and add new [GenDisk
] to the VFS.
``
``
16
`+
pub struct GenDiskBuilder {
`
``
17
`+
rotational: bool,
`
``
18
`+
logical_block_size: u32,
`
``
19
`+
physical_block_size: u32,
`
``
20
`+
capacity_sectors: u64,
`
``
21
`+
}
`
``
22
+
``
23
`+
impl 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
+
``
34
`+
impl 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 {
`
``
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 {
`
``
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>,
`
``
95
`+
) -> Result<GenDisk> {
`
``
96
`+
let lock_class_key = crate::sync::LockClassKey::new();
`
``
97
+
``
98
`` +
// SAFETY: tagset.raw_tag_set()
points to a valid and initialized tag set
``
``
99
`+
let gendisk = from_err_ptr(unsafe {
`
``
100
`+
bindings::__blk_mq_alloc_disk(
`
``
101
`+
tagset.raw_tag_set(),
`
``
102
`+
core::ptr::null_mut(), // TODO: We can pass queue limits right here
`
``
103
`+
core::ptr::null_mut(),
`
``
104
`+
lock_class_key.as_ptr(),
`
``
105
`+
)
`
``
106
`+
})?;
`
``
107
+
``
108
`+
const TABLE: bindings::block_device_operations = bindings::block_device_operations {
`
``
109
`+
submit_bio: None,
`
``
110
`+
open: None,
`
``
111
`+
release: None,
`
``
112
`+
ioctl: None,
`
``
113
`+
compat_ioctl: None,
`
``
114
`+
check_events: None,
`
``
115
`+
unlock_native_capacity: None,
`
``
116
`+
getgeo: None,
`
``
117
`+
set_read_only: None,
`
``
118
`+
swap_slot_free_notify: None,
`
``
119
`+
report_zones: None,
`
``
120
`+
devnode: None,
`
``
121
`+
alternative_gpt_sector: None,
`
``
122
`+
get_unique_id: None,
`
``
123
`+
// TODO: Set to THIS_MODULE. Waiting for const_refs_to_static feature to
`
``
124
`+
// be merged (unstable in rustc 1.78 which is staged for linux 6.10)
`
``
125
`+
// https://github.com/rust-lang/rust/issues/119618
`
``
126
`+
owner: core::ptr::null_mut(),
`
``
127
`+
pr_ops: core::ptr::null_mut(),
`
``
128
`+
free_disk: None,
`
``
129
`+
poll_bio: None,
`
``
130
`+
};
`
``
131
+
``
132
`` +
// SAFETY: gendisk
is a valid pointer as we initialized it above
``
``
133
`+
unsafe { (*gendisk).fops = &TABLE };
`
``
134
+
``
135
`+
let mut raw_writer = RawWriter::from_array(
`
``
136
`` +
// SAFETY: gendisk
points to a valid and initialized instance. We
``
``
137
`+
// have exclusive access, since the disk is not added to the VFS
`
``
138
`+
// yet.
`
``
139
`+
unsafe { &mut (*gendisk).disk_name },
`
``
140
`+
)?;
`
``
141
`+
raw_writer.write_fmt(name)?;
`
``
142
`+
raw_writer.write_char('\0')?;
`
``
143
+
``
144
`` +
// SAFETY: gendisk
points to a valid and initialized instance of
``
``
145
`` +
// struct gendisk
. We have exclusive access, so we cannot race.
``
``
146
`+
unsafe {
`
``
147
`+
bindings::blk_queue_logical_block_size((*gendisk).queue, self.logical_block_size)
`
``
148
`+
};
`
``
149
+
``
150
`` +
// SAFETY: gendisk
points to a valid and initialized instance of
``
``
151
`` +
// struct gendisk
. We have exclusive access, so we cannot race.
``
``
152
`+
unsafe {
`
``
153
`+
bindings::blk_queue_physical_block_size((*gendisk).queue, self.physical_block_size)
`
``
154
`+
};
`
``
155
+
``
156
`` +
// SAFETY: gendisk
points to a valid and initialized instance of
``
``
157
`` +
// struct gendisk
. set_capacity
takes a lock to synchronize this
``
``
158
`+
// operation, so we will not race.
`
``
159
`+
unsafe { bindings::set_capacity(gendisk, self.capacity_sectors) };
`
``
160
+
``
161
`+
if !self.rotational {
`
``
162
`` +
// SAFETY: gendisk
points to a valid and initialized instance of
``
``
163
`` +
// struct gendisk
. This operation uses a relaxed atomic bit flip
``
``
164
`+
// operation, so there is no race on this field.
`
``
165
`+
unsafe { bindings::blk_queue_flag_set(bindings::QUEUE_FLAG_NONROT, (*gendisk).queue) };
`
``
166
`+
} else {
`
``
167
`` +
// SAFETY: gendisk
points to a valid and initialized instance of
``
``
168
`` +
// struct gendisk
. This operation uses a relaxed atomic bit flip
``
``
169
`+
// operation, so there is no race on this field.
`
``
170
`+
unsafe {
`
``
171
`+
bindings::blk_queue_flag_clear(bindings::QUEUE_FLAG_NONROT, (*gendisk).queue)
`
``
172
`+
};
`
``
173
`+
}
`
``
174
+
``
175
`+
crate::error::to_result(
`
``
176
`` +
// SAFETY: gendisk
points to a valid and initialized instance of
``
``
177
`` +
// struct gendisk
.
``
``
178
`+
unsafe {
`
``
179
`+
bindings::device_add_disk(core::ptr::null_mut(), gendisk, core::ptr::null_mut())
`
``
180
`+
},
`
``
181
`+
)?;
`
``
182
+
``
183
`` +
// INVARIANT: gendisk
was initialized above.
``
``
184
`` +
// INVARIANT: gendisk
was added to the VFS via device_add_disk
above.
``
``
185
`+
Ok(GenDisk {
`
``
186
`+
_tagset: tagset,
`
``
187
`+
gendisk,
`
``
188
`+
})
`
``
189
`+
}
`
``
190
`+
}
`
``
191
+
``
192
`+
/// A generic block device.
`
``
193
`+
///
`
``
194
`+
/// # Invariants
`
``
195
`+
///
`
``
196
`` +
/// - gendisk
must always point to an initialized and valid struct gendisk
.
``
``
197
`` +
/// - gendisk
was added to the VFS through a call to
``
``
198
`` +
/// bindings::device_add_disk
.
``
``
199
`+
pub struct GenDisk<T: Operations> {
`
``
200
`+
_tagset: Arc<TagSet>,
`
``
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 {}
`
``
207
+
``
208
`+
impl<T: Operations> Drop for GenDisk {
`
``
209
`+
fn drop(&mut self) {
`
``
210
`` +
// SAFETY: By type invariant, self.gendisk
points to a valid and
``
``
211
`` +
// initialized instance of struct gendisk
, and it was previously added
``
``
212
`+
// to the VFS.
`
``
213
`+
unsafe { bindings::del_gendisk(self.gendisk) };
`
``
214
`+
}
`
``
215
`+
}
`