ublk_sys/
srv.rs

1// SPDX-License-Identifier: MIT
2#![allow(dead_code)]
3#![allow(non_camel_case_types)]
4#![allow(clippy::missing_safety_doc)] // FIXME
5
6use crate::iouring;
7use crate::{__IncompleteArrayField, cmd, d};
8use libc::{__u64, c_char, c_int, c_long, c_uint, c_ulong, c_ulonglong, c_ushort, c_void};
9
10pub const MAX_NR_HW_QUEUES: u32 = 32;
11pub const MAX_QD: u32 = 1024;
12pub const MAX_BUF_SIZE: u32 = 1024 << 10;
13
14pub const DEF_NR_HW_QUEUES: u32 = 1;
15pub const DEF_QD: u32 = 256;
16pub const DEF_BUF_SIZE: u32 = 512 << 10;
17
18pub const UBLKSRV_SHM_DIR: &[u8; 8] = b"ublksrv\0";
19pub const UBLKSRV_SHM_SIZE: u32 = 1024;
20
21/// stored in ublksrv_ctrl_dev_info->ublksrv_flags
22
23/// HAS_IO_DAEMON means io handler has its own daemon context which isn't
24/// same with control command context, so shared memory communication is
25/// required between control task and io daemon
26pub const UBLKSRV_F_HAS_IO_DAEMON: u32 = 1;
27
28/// target may not use io_uring for handling io, so eventfd is required
29/// for wakeup io command io_uring context
30pub const UBLKSRV_F_NEED_EVENTFD: u32 = 2;
31
32// A opaque type
33// TODO: Replace with a extern type once RFC 1861 becomes stable
34// #![feature(extern_types)]
35// extern "C" {
36//     type ublksrv_aio_ctx;
37// }
38#[repr(C)]
39#[derive(Debug, Copy, Clone)]
40pub struct ublksrv_aio_ctx {
41    _private: [u8; 0],
42}
43
44/// Generic data for creating one ublk device
45///
46/// Target specific data is handled by ->init_tgt
47#[repr(C)]
48#[derive(Debug, Copy, Clone)]
49pub struct ublksrv_dev_data {
50    pub dev_id: c_int,
51    pub max_io_buf_bytes: c_uint,
52    pub nr_hw_queues: c_ushort,
53    pub queue_depth: c_ushort,
54    pub tgt_type: *const c_char,
55    pub tgt_ops: *const ublksrv_tgt_type,
56    pub tgt_argc: c_int,
57    pub tgt_argv: *mut *mut c_char,
58    pub run_dir: *const c_char,
59    pub flags: c_ulong,
60    pub ublksrv_flags: c_ulong,
61    pub reserved: [c_ulong; 7],
62}
63d!(ublksrv_dev_data);
64
65#[repr(C)]
66#[derive(Debug, Copy, Clone)]
67pub struct ublksrv_ctrl_dev {
68    pub ring: iouring::io_uring,
69    pub ctrl_fd: c_int,
70    pub bs_shift: c_uint,
71    pub dev_info: cmd::ublksrv_ctrl_dev_info,
72    pub tgt_type: *const c_char,
73    pub tgt_ops: *const ublksrv_tgt_type,
74    /// default is UBLKSRV_RUN_DIR but can be specified via command line,
75    /// pid file will be saved there
76    pub run_dir: *const c_char,
77    pub tgt_argc: c_int,
78    pub tgt_argv: *mut *mut c_char,
79    pub queues_cpuset: *mut libc::cpu_set_t,
80}
81d!(ublksrv_ctrl_dev);
82
83pub const UBLKSRV_NEED_FETCH_RQ: u32 = 1 << 0;
84pub const UBLKSRV_NEED_COMMIT_RQ_COMP: u32 = 1 << 1;
85pub const UBLKSRV_IO_FREE: u32 = 1 << 2;
86pub const UBLKSRV_NEED_GET_DATA: u32 = 1 << 3;
87
88#[repr(C)]
89#[derive(Copy, Clone)]
90pub struct ublk_io {
91    pub buf_addr: *mut c_char,
92    pub flags: c_uint,
93    pub union: ublk_io_anon_union_ty,
94    pub tgt_io_cqe: *mut iouring::io_uring_cqe,
95    pub io_data: c_ulong,
96}
97d!(ublk_io);
98
99#[repr(C)]
100#[derive(Copy, Clone)]
101pub union ublk_io_anon_union_ty {
102    /// result is updated after all target ios are done
103    pub result: c_uint,
104    /// current completed target io cqe
105    pub queued_tgt_io: c_int,
106}
107d!(ublk_io_anon_union_ty);
108
109pub const UBLKSRV_QUEUE_STOPPING: u32 = 1;
110pub const UBLKSRV_QUEUE_IDLE: u32 = 2;
111pub const UBLKSRV_NR_CTX_BATCH: u32 = 4;
112
113#[repr(C)]
114pub struct ublksrv_queue {
115    pub q_id: c_int,
116    pub q_depth: c_int,
117    pub private_data: *mut c_void,
118    /// Read only by ublksrv daemon, setup via mmap on /dev/ublkcN.
119    ///
120    /// ublksrv_io_desc(iod) is stored in this buffer, so iod
121    /// can be retrieved by request's tag directly.
122    ///
123    /// ublksrv writes the iod into this array, and notify ublksrv daemon
124    /// by issued io_uring command beforehand.
125    pub io_cmd_buf: *mut c_char,
126    pub io_buf: *mut c_char,
127    pub cmd_inflight: c_uint,
128    pub tgt_io_inflight: c_uint,
129    pub state: c_uint,
130    /// eventfd
131    pub efd: c_int,
132    /// cache tgt ops
133    pub tgt_ops: *const ublksrv_tgt_type,
134    /// ring for submit io command to ublk driver, can only be issued from ublksrv daemon.
135    /// ring depth == dev_info->queue_depth.
136    pub ring: iouring::io_uring,
137    pub dev: *mut ublksrv_dev,
138    pub tid: c_uint,
139    pub nr_ctxs: c_int,
140    pub ctxs: [*mut ublksrv_aio_ctx; UBLKSRV_NR_CTX_BATCH as usize],
141    pub ios: __IncompleteArrayField<ublk_io>,
142}
143d!(ublksrv_queue);
144
145pub const UBLKSRV_TGT_MAX_FDS: u32 = 32;
146
147// enum
148/// evaluate communication cost, ublksrv_null vs /dev/nullb0
149pub const UBLKSRV_TGT_TYPE_NULL: c_uint = 0;
150/// ublksrv_loop vs. /dev/loop
151pub const UBLKSRV_TGT_TYPE_LOOP: c_uint = 1;
152pub const UBLKSRV_TGT_TYPE_MAX: c_uint = 256;
153
154#[repr(C)]
155#[derive(Debug, Copy, Clone)]
156pub struct ublksrv_tgt_info {
157    pub dev_size: c_ulonglong,
158    /// at most in-flight ios
159    pub tgt_ring_depth: c_uint,
160    pub nr_fds: c_uint,
161    pub fds: [c_int; UBLKSRV_TGT_MAX_FDS as usize],
162    pub tgt_data: *mut c_void,
163    pub ops: *const ublksrv_tgt_type,
164}
165d!(ublksrv_tgt_info);
166
167#[repr(C)]
168#[derive(Debug, Copy, Clone)]
169pub struct ublksrv_tgt_type {
170    /// One IO request comes from /dev/ublkbN, so notify target code
171    /// for handling the IO. Inside target code, the IO can be handled
172    /// with our io_uring too, if this is true, ->tgt_io_done callback
173    /// has to be implemented. Otherwise, target can implement
174    /// ->handle_event() for processing io completion there.
175    pub handle_io_async:
176        Option<unsafe extern "C" fn(arg1: *mut ublksrv_queue, tag: c_int) -> c_int>,
177
178    /// target io is handled by our io_uring, and once the target io
179    /// is completed, this callback is called
180    pub tgt_io_done:
181        Option<unsafe extern "C" fn(arg1: *mut ublksrv_queue, arg2: *mut iouring::io_uring_cqe)>,
182
183    /// Someone has written to our eventfd, so let target handle the
184    /// event, most of times, it is for handling io completion by
185    /// calling ublksrv_complete_io() which has to be run in ubq_daemon context.
186    ///
187    /// Follows the typical scenario:
188    ///
189    /// 1) one target io is completed in target pthread context, so
190    /// target code calls ublksrv_queue_send_event for notifying ubq daemon
191    ///
192    /// 2) ubq daemon gets notified, so wakeup from io_uring_enter(),
193    /// then found eventfd is completed, so call ->handle_event()
194    ///
195    /// 3) inside ->handle_event(), if any io represented by one io
196    /// command is completed, ublksrv_complete_io() is called for this io.
197    ///
198    /// 4) after returning from ->handle_event(), ubq_daemon will
199    /// queue & submit the eventfd io immediately for getting
200    /// notification from future event.
201    pub handle_event: Option<unsafe extern "C" fn(arg1: *mut ublksrv_queue)>,
202
203    /// One typical use case is to flush meta data, which is usually done
204    /// in background. So there isn't any tag from libublksrv for this kind
205    /// of IOs, and the target code has to request for allocating extra ios
206    /// by passing tgt_type->extra_ios and let this callback consume & handle
207    /// these extra IOs.
208    ///
209    /// @nr_queued_io: count of queued IOs in ublksrv_reap_events_uring of this time
210    pub handle_io_background:
211        Option<unsafe extern "C" fn(arg1: *mut ublksrv_queue, nr_queued_io: c_int)>,
212
213    /// show target specific command line for adding new device
214    ///
215    /// Be careful: this callback is the only one which is not run from
216    /// ublk device daemon task context.
217    pub usage_for_add: Option<unsafe extern "C" fn()>,
218
219    /// initialize this new target, argc/argv includes target specific
220    /// command line parameters
221    pub init_tgt: Option<
222        unsafe extern "C" fn(
223            arg1: *mut ublksrv_dev,
224            type_: c_int,
225            argc: c_int,
226            argv: *mut *mut c_char,
227        ) -> c_int,
228    >,
229
230    /// deinitialize this target
231    pub deinit_tgt: Option<unsafe extern "C" fn(arg1: *mut ublksrv_dev)>,
232
233    pub alloc_io_buf:
234        Option<unsafe extern "C" fn(q: *mut ublksrv_queue, tag: c_int, size: c_int) -> *mut c_void>,
235    pub free_io_buf:
236        Option<unsafe extern "C" fn(q: *mut ublksrv_queue, buf: *mut c_void, tag: c_int)>,
237
238    pub type_: c_int,
239
240    /// flags required for ublk driver
241    pub ublk_flags: c_uint,
242    /// flags required for ublksrv
243    pub ublksrv_flags: c_uint,
244    /// extra io slots allocated for handling
245    pub extra_ios: c_int,
246    /// target specific IOs, such as meta io
247    pub name: *const c_char,
248}
249d!(ublksrv_tgt_type);
250
251#[repr(C)]
252#[derive(Debug, Copy, Clone)]
253pub struct ublksrv_dev {
254    pub tgt: ublksrv_tgt_info,
255    pub __queues: [*mut ublksrv_queue; MAX_NR_HW_QUEUES as usize],
256    pub io_buf_start: *mut c_char,
257    pub thread: *mut libc::pthread_t,
258    pub cdev_fd: c_int,
259    pub pid_file_fd: c_int,
260    pub ctrl_dev: *const ublksrv_ctrl_dev,
261    pub target_data: *mut c_void,
262}
263d!(ublksrv_dev);
264
265pub unsafe fn ublksrv_get_iod(q: *const ublksrv_queue, tag: c_int) -> *mut cmd::ublksrv_io_desc {
266    let idx = tag as usize * std::mem::size_of::<cmd::ublksrv_io_desc>();
267    (*q).io_cmd_buf.add(idx) as *mut cmd::ublksrv_io_desc
268}
269
270pub unsafe fn build_user_data(
271    tag: c_uint,
272    op: c_uint,
273    tgt_data: c_uint,
274    is_target_io: c_uint,
275) -> __u64 {
276    assert!((tag >> 16 == 0) && (op >> 8 == 0) && (tgt_data >> 16 == 0));
277    tag as __u64 | (op << 16) as __u64 | (tgt_data << 24) as __u64 | (is_target_io as __u64) << 63
278}
279
280pub unsafe fn user_data_to_tag(user_data: __u64) -> c_int {
281    (user_data & 0xffff) as c_int
282}
283
284pub unsafe fn user_data_to_op(user_data: __u64) -> c_int {
285    ((user_data >> 16) & 0xff) as c_int
286}
287
288pub unsafe fn user_data_to_tgt_data(user_data: __u64) -> c_int {
289    ((user_data >> 24) & 0xffff) as c_int
290}
291
292extern "C" {
293    pub fn ublksrv_ctrl_deinit(dev: *mut ublksrv_ctrl_dev);
294    pub fn ublksrv_ctrl_init(data: *mut ublksrv_dev_data) -> *mut ublksrv_ctrl_dev;
295    pub fn ublksrv_ctrl_get_affinity(ctrl_dev: *mut ublksrv_ctrl_dev) -> c_int;
296    pub fn ublksrv_ctrl_add_dev(dev: *mut ublksrv_ctrl_dev) -> c_int;
297    pub fn ublksrv_ctrl_del_dev(dev: *mut ublksrv_ctrl_dev) -> c_int;
298    pub fn ublksrv_ctrl_get_info(dev: *mut ublksrv_ctrl_dev) -> c_int;
299    pub fn ublksrv_ctrl_stop_dev(dev: *mut ublksrv_ctrl_dev) -> c_int;
300    pub fn ublksrv_ctrl_dump(dev: *mut ublksrv_ctrl_dev, buf: *const c_char);
301    pub fn ublksrv_ctrl_start_dev(ctrl_dev: *mut ublksrv_ctrl_dev, daemon_pid: c_int) -> c_int;
302    pub fn ublksrv_ctrl_set_params(
303        dev: *mut ublksrv_ctrl_dev,
304        params: *mut cmd::ublk_params,
305    ) -> c_int;
306    pub fn ublksrv_ctrl_get_params(
307        dev: *mut ublksrv_ctrl_dev,
308        params: *mut cmd::ublk_params,
309    ) -> c_int;
310    pub fn ublksrv_dev_init(ctrl_dev: *const ublksrv_ctrl_dev) -> *mut ublksrv_dev;
311    pub fn ublksrv_dev_deinit(dev: *mut ublksrv_dev);
312}
313
314/// target json has to include the following key/value
315pub const UBLKSRV_TGT_NAME_MAX_LEN: u32 = 32;
316#[repr(C)]
317#[derive(Debug, Default, Copy, Clone)]
318pub struct ublksrv_tgt_base_json {
319    pub name: [c_char; UBLKSRV_TGT_NAME_MAX_LEN as usize],
320    pub type_: c_int,
321    pub dev_size: c_ulonglong,
322}
323
324extern "C" {
325    pub fn ublksrv_json_write_dev_info(
326        dev: *const ublksrv_ctrl_dev,
327        buf: *mut c_char,
328        len: c_int,
329    ) -> c_int;
330    pub fn ublksrv_json_read_dev_info(
331        json_buf: *const c_char,
332        info: *mut cmd::ublksrv_ctrl_dev_info,
333    ) -> c_int;
334    pub fn ublksrv_json_write_queue_info(
335        dev: *const ublksrv_ctrl_dev,
336        jbuf: *mut c_char,
337        len: c_int,
338        qid: c_int,
339        ubq_daemon_tid: c_int,
340    ) -> c_int;
341    pub fn ublksrv_json_read_queue_info(
342        jbuf: *const c_char,
343        qid: c_int,
344        tid: *mut c_uint,
345        affinity_buf: *mut c_char,
346        len: c_int,
347    ) -> c_int;
348    pub fn ublksrv_json_read_target_info(
349        jbuf: *const c_char,
350        tgt_buf: *mut c_char,
351        len: c_int,
352    ) -> c_int;
353    pub fn ublksrv_json_write_target_str_info(
354        jbuf: *mut c_char,
355        len: c_int,
356        name: *const c_char,
357        val: *const c_char,
358    ) -> c_int;
359    pub fn ublksrv_json_write_target_long_info(
360        jbuf: *mut c_char,
361        len: c_int,
362        name: *const c_char,
363        val: c_long,
364    ) -> c_int;
365    pub fn ublksrv_json_write_target_ulong_info(
366        jbuf: *mut c_char,
367        len: c_int,
368        name: *const c_char,
369        val: c_ulong,
370    ) -> c_int;
371    pub fn ublksrv_json_dump(jbuf: *const c_char);
372    pub fn ublksrv_json_read_target_base_info(
373        jbuf: *const c_char,
374        tgt: *mut ublksrv_tgt_base_json,
375    ) -> c_int;
376    pub fn ublksrv_json_write_target_base_info(
377        jbuf: *mut c_char,
378        len: c_int,
379        tgt: *const ublksrv_tgt_base_json,
380    ) -> c_int;
381    pub fn ublksrv_json_read_params(p: *mut cmd::ublk_params, jbuf: *const c_char) -> c_int;
382    pub fn ublksrv_json_write_params(
383        p: *const cmd::ublk_params,
384        jbuf: *mut c_char,
385        len: c_int,
386    ) -> c_int;
387    pub fn ublksrv_json_dump_params(jbuf: *const c_char) -> c_int;
388    pub fn ublksrv_json_get_length(jbuf: *const c_char) -> c_int;
389}
390
391pub unsafe fn ublksrv_queue_get_data(q: *const ublksrv_queue) -> *mut c_void {
392    (*q).private_data
393}
394
395extern "C" {
396    pub fn ublksrv_queue_init(
397        dev: *mut ublksrv_dev,
398        q_id: c_ushort,
399        nr_extra_ios: c_int,
400        queue_data: *mut c_void,
401    ) -> *mut ublksrv_queue;
402    pub fn ublksrv_queue_deinit(q: *mut ublksrv_queue);
403    pub fn ublksrv_queue_handled_event(q: *mut ublksrv_queue) -> c_int;
404    pub fn ublksrv_queue_send_event(q: *mut ublksrv_queue) -> c_int;
405    pub fn ublksrv_get_queue(dev: *const ublksrv_dev, q_id: c_int) -> *mut ublksrv_queue;
406    pub fn ublksrv_process_io(q: *mut ublksrv_queue) -> c_int;
407    pub fn ublksrv_complete_io(q: *mut ublksrv_queue, tag: c_uint, res: c_int) -> c_int;
408    pub fn ublksrv_register_tgt_type(type_: *mut ublksrv_tgt_type) -> c_int;
409    pub fn ublksrv_unregister_tgt_type(type_: *mut ublksrv_tgt_type);
410    pub fn ublksrv_for_each_tgt_type(
411        handle_tgt_type: Option<
412            unsafe extern "C" fn(idx: c_uint, type_: *const ublksrv_tgt_type, data: *mut c_void),
413        >,
414        data: *mut c_void,
415    );
416    pub fn ublksrv_find_tgt_type(name: *const c_char) -> *const ublksrv_tgt_type;
417    pub fn ublksrv_apply_oom_protection();
418}
419
420/* --------------------------------------------*/
421
422/* --------------------------------------------*/