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

`+

}

`