ublk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: bba0d82a93398079b7ed866556fcc9e6b6d7c88f16a6afcf0c254bd4b18d8a5b
4
+ data.tar.gz: 52f2b58dd1fae0a22fbfe1d2bffd28f139f4e8cc34b1f6f7b22a040b92f01052
5
+ SHA512:
6
+ metadata.gz: b8d17740f6692d5cbd4a0f159c3f715348e909daf499cf1e899d1fc426a5fcf20f002186744fda7d7f22ea3eb69f734b7a2c050c687ca206e5164b1808e45f84
7
+ data.tar.gz: 23c5cf0c070cd73825935f7531bf43e2e3d9b5e08f93875a14b9c24fb1544887d608b3050532af464d5fd143163ff48c947a1f25223424b5e0908255851e7002
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Yudai Takada
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # UBLK
2
+
3
+ Write Linux block-device targets in Ruby. UBLK uses a C extension, liburing,
4
+ and the kernel's `UBLK_F_USER_COPY` interface so target implementations only
5
+ exchange Ruby strings.
6
+
7
+ > [!WARNING]
8
+ > This is experimental. Never use it for a root filesystem, swap, or data you
9
+ > cannot recreate. A Ruby GC pause stops device I/O, and a crashed server makes
10
+ > outstanding I/O fail unless recovery was enabled.
11
+
12
+ ## Requirements
13
+
14
+ - Linux 6.6 or newer with `CONFIG_BLK_DEV_UBLK`
15
+ - `/dev/ublk-control` and normally `CAP_SYS_ADMIN`
16
+ - Ruby 3.2 or newer
17
+ - liburing headers (`liburing-dev` on Debian/Ubuntu, `liburing-devel` on Fedora)
18
+
19
+ The gem deliberately fails to build without liburing. Once installed,
20
+ `require "ublk"` remains safe on unsupported hosts and `UBLK.supported?`
21
+ reports whether the running kernel exposes USER_COPY.
22
+
23
+ ```sh
24
+ bundle install
25
+ bundle exec rake compile
26
+ ```
27
+
28
+ ## RAM disk
29
+
30
+ ```ruby
31
+ require "ublk"
32
+
33
+ class RamDisk < UBLK::Target
34
+ def initialize(size)
35
+ @data = "\0".b * size
36
+ super(size:)
37
+ end
38
+
39
+ def read(offset, length) = @data.byteslice(offset, length)
40
+
41
+ def write(offset, data)
42
+ @data[offset, data.bytesize] = data
43
+ data.bytesize
44
+ end
45
+ end
46
+
47
+ device = UBLK::Device.create(RamDisk.new(256 * 1024 * 1024))
48
+ puts device.path
49
+ device.run
50
+ ```
51
+
52
+ `run` blocks until another thread calls `stop` or the process receives an
53
+ interrupt. For a background server, call `start`, use `device.path`, then call
54
+ `delete`. Created devices are also deleted by an `at_exit` hook.
55
+
56
+ Targets must implement `read` and `write`. `flush` succeeds by default;
57
+ `discard` and `write_zeroes` default to `EOPNOTSUPP`. Raising an `Errno::*`
58
+ exception returns that errno to the kernel; other exceptions become `EIO`.
59
+
60
+ `Device.create` locks current and future memory by default to avoid a paging
61
+ deadlock. Pass `mlock: false` only for disposable development devices. Multiple
62
+ hardware queues use one Ruby thread each, but callbacks remain serialized by
63
+ the GVL.
64
+
65
+ See `examples/` for RAM, file-backed, HTTP read-only, encrypted, tracing, and
66
+ fault-injection targets.
67
+
68
+ ## Recovery
69
+
70
+ Create with `recovery: true`, then reconnect after a server restart:
71
+
72
+ ```ruby
73
+ device = UBLK::Device.recover(target, id: 0)
74
+ device.run
75
+ ```
76
+
77
+ Outstanding requests fail during this v1 recovery mode; they are not reissued.
78
+
79
+ ## Unsupported environments
80
+
81
+ - macOS and Windows: no Linux ublk driver
82
+ - Docker Desktop: its VM normally does not expose `/dev/ublk-control`
83
+ - containers without the host ublk device and the required capability
84
+ - WSL2 kernels without `CONFIG_BLK_DEV_UBLK`
85
+ - Linux before 6.6: no required USER_COPY interface
86
+ - standard GitHub-hosted jobs: no usable privileged ublk device
87
+
88
+ ## Tests
89
+
90
+ ```sh
91
+ bundle exec rake compile
92
+ bundle exec rake test:unit
93
+ bundle exec rbs validate
94
+ ```
95
+
96
+ The destructive system suite only runs when explicitly enabled inside a VM.
97
+ It covers 64 MiB raw reads, maximum-I/O boundary crossing, ext4 and fsck,
98
+ flush/discard callbacks, errno propagation, device deletion, SIGKILL recovery,
99
+ four-way verified fio, a one-minute RSS leak check, and 1,000 control opens.
100
+
101
+ ```sh
102
+ tools/vm/run.sh v6.6
103
+ tools/vm/run.sh v6.12
104
+ ```
105
+
106
+ Run 4K random-read, random-write, and sequential-read fio workloads in the
107
+ same VM:
108
+
109
+ ```sh
110
+ UBLK_BENCH=1 bundle exec ruby tools/bench.rb
111
+ UBLK_BENCH=1 UBLK_BENCH_GC_STRESS=1 bundle exec ruby tools/bench.rb
112
+ UBLK_BENCH=1 UBLK_BENCH_DEVICE=/dev/ublkb0 bundle exec ruby tools/bench.rb
113
+ ```
114
+
115
+ The last form measures an externally started device, such as `ublksrv`'s C
116
+ loop target, with the identical fio arguments. fio's JSON includes IOPS and
117
+ p99 latency. No hardware-independent number is claimed: CPU, Ruby, GC, and VM
118
+ acceleration materially change the result, and Ruby callbacks remain bounded
119
+ by the GVL.
120
+
121
+ ### Reference benchmark
122
+
123
+ One reproducible comparison was run on Linux 6.12.0 under QEMU TCG (2 vCPUs,
124
+ 1.5 GiB RAM), using Ruby 3.2, fio 3.36, and upstream ublksrv 1.6.1
125
+ (`abbfea2b5918`). Both loop targets used a 256 MiB sparse file on `/tmp`, one
126
+ queue of depth 128, and buffered backing-file I/O. fio used its io_uring engine,
127
+ 4 KiB direct I/O, iodepth 32, and one 30-second run per workload.
128
+
129
+ | Target | Workload | IOPS | completion p99 |
130
+ |---|---:|---:|---:|
131
+ | Ruby loop | random read | 33,774 | 2.900 ms |
132
+ | Ruby loop | random write | 33,896 | 2.376 ms |
133
+ | Ruby loop | sequential read | 26,801 | 3.850 ms |
134
+ | C ublksrv loop | random read | 64,213 | 0.938 ms |
135
+ | C ublksrv loop | random write | 56,456 | 1.090 ms |
136
+ | C ublksrv loop | sequential read | 62,011 | 0.987 ms |
137
+ | Ruby loop, `GC.stress` | random read | 9.72 | 6.610 s |
138
+ | Ruby loop, `GC.stress` | random write | 12.61 | 4.178 s |
139
+ | Ruby loop, `GC.stress` | sequential read | 7.42 | 7.952 s |
140
+
141
+ These are diagnostic VM results, not production performance claims. In this
142
+ run the C loop target delivered roughly 1.7–2.3 times the IOPS of normal Ruby,
143
+ while `GC.stress` demonstrated that stop-the-world GC can make latency
144
+ unacceptable for block storage.
145
+
146
+ ## License
147
+
148
+ MIT
data/Rakefile ADDED
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "fileutils"
5
+ require "rake/extensiontask"
6
+ require "rspec/core/rake_task"
7
+ require "yard"
8
+
9
+ Rake::ExtensionTask.new("ublk") do |ext|
10
+ ext.lib_dir = "lib/ublk"
11
+ end
12
+
13
+ YARD::Rake::YardocTask.new
14
+
15
+ namespace :test do
16
+ RSpec::Core::RakeTask.new(:unit) { |task| task.pattern = "spec/unit/**/*_spec.rb" }
17
+ RSpec::Core::RakeTask.new(:system) { |task| task.pattern = "spec/system/**/*_spec.rb" }
18
+ end
19
+
20
+ namespace :gen do
21
+ desc "Regenerate Ruby constants from the installed Linux ublk headers"
22
+ task :constants do
23
+ executable = "tmp/dump_ublk_constants"
24
+ FileUtils.mkdir_p("tmp")
25
+ sh RbConfig::CONFIG.fetch("CC"), "-Iext/ublk", "tools/dump_constants.c", "-o", executable
26
+ names = IO.popen([executable], &:read).lines.map { |line| line.split("=", 2).first }
27
+ body = names.map { |name| " UBLK_CONST(#{name});" }.join("\n")
28
+ File.write("ext/ublk/constants.c", <<~C)
29
+ /* Generated by rake gen:constants. */
30
+ #include "internal.h"
31
+
32
+ #define UBLK_CONST(name) rb_define_const(ublk_native_module, #name, ULL2NUM(name))
33
+
34
+ void ublk_init_constants(void)
35
+ {
36
+ #{body}
37
+ }
38
+ C
39
+ end
40
+ end
41
+
42
+ task default: "test:unit"
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openssl"
4
+ require "ublk"
5
+
6
+ class EncryptedDisk < UBLK::Target
7
+ def initialize(path, size:, key:)
8
+ raise ArgumentError, "key must be 32 bytes" unless key.bytesize == 32
9
+
10
+ @file = File.open(path, File::RDWR | File::CREAT, 0o600)
11
+ @file.truncate(size)
12
+ @key = key
13
+ super(size:)
14
+ end
15
+
16
+ def read(offset, length) = crypt(offset, @file.pread(length, offset))
17
+
18
+ def write(offset, data)
19
+ @file.pwrite(crypt(offset, data), offset)
20
+ end
21
+
22
+ def flush = @file.fsync
23
+
24
+ def discard(offset, length)
25
+ write(offset, "\0".b * length)
26
+ 0
27
+ end
28
+
29
+ alias write_zeroes discard
30
+
31
+ private
32
+
33
+ def crypt(offset, data)
34
+ cipher = OpenSSL::Cipher.new("aes-256-ctr")
35
+ cipher.encrypt
36
+ cipher.key = @key
37
+ cipher.iv = [0, offset / 16].pack("Q>Q>")
38
+ cipher.update(data) + cipher.final
39
+ end
40
+ end
41
+
42
+ abort "usage: UBLK_KEY_HEX=<64 hex chars> #{$PROGRAM_NAME} FILE SIZE" unless ARGV.size == 2 && ENV["UBLK_KEY_HEX"]
43
+ key = [ENV.fetch("UBLK_KEY_HEX")].pack("H*")
44
+ UBLK::Device.create(EncryptedDisk.new(ARGV.fetch(0), size: Integer(ARGV.fetch(1), 10), key:)).run
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ramdisk"
4
+
5
+ class FaultInjectionDisk < RamDisk
6
+ def initialize(size, failing_range)
7
+ @failing_range = failing_range
8
+ super(size)
9
+ end
10
+
11
+ def read(offset, length)
12
+ fail_if_needed(offset, length)
13
+ super
14
+ end
15
+
16
+ def write(offset, data)
17
+ fail_if_needed(offset, data.bytesize)
18
+ super
19
+ end
20
+
21
+ private
22
+
23
+ def fail_if_needed(offset, length)
24
+ raise Errno::EIO if offset < @failing_range.end && offset + length > @failing_range.begin
25
+ end
26
+ end
27
+
28
+ if $PROGRAM_NAME == __FILE__
29
+ disk = FaultInjectionDisk.new(256 * 1024 * 1024, (64 * 1024 * 1024)...(65 * 1024 * 1024))
30
+ UBLK::Device.create(disk).run
31
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "ublk"
5
+
6
+ class HTTPDisk < UBLK::Target
7
+ def initialize(url)
8
+ @uri = URI(url)
9
+ response = Net::HTTP.start(@uri.host, @uri.port, use_ssl: @uri.scheme == "https") { |http| http.head(@uri.request_uri) }
10
+ super(size: Integer(response.fetch("content-length"), 10))
11
+ end
12
+
13
+ def read(offset, length)
14
+ request = Net::HTTP::Get.new(@uri)
15
+ request["Range"] = "bytes=#{offset}-#{offset + length - 1}"
16
+ response = Net::HTTP.start(@uri.host, @uri.port, use_ssl: @uri.scheme == "https") { |http| http.request(request) }
17
+ raise Errno::EIO, "HTTP #{response.code}" unless response.is_a?(Net::HTTPSuccess)
18
+
19
+ response.body
20
+ end
21
+
22
+ def read_only? = true
23
+ end
24
+
25
+ abort "usage: #{$PROGRAM_NAME} URL" unless ARGV.one?
26
+ UBLK::Device.create(HTTPDisk.new(ARGV.fetch(0))).run
data/examples/loop.rb ADDED
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ublk"
4
+
5
+ class LoopTarget < UBLK::Target
6
+ def initialize(path, size: File.size(path), read_only: false)
7
+ @file = File.open(path, read_only ? "rb" : "r+b")
8
+ @read_only = read_only
9
+ super(size:)
10
+ end
11
+
12
+ def read(offset, length) = @file.pread(length, offset)
13
+ def write(offset, data) = @file.pwrite(data, offset)
14
+ def flush = @file.fsync
15
+ def read_only? = @read_only
16
+ end
17
+
18
+ if $PROGRAM_NAME == __FILE__
19
+ abort "usage: #{$PROGRAM_NAME} IMAGE" unless ARGV.one?
20
+ UBLK::Device.create(LoopTarget.new(ARGV.fetch(0))).run
21
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ublk"
4
+
5
+ class RamDisk < UBLK::Target
6
+ def initialize(size)
7
+ @data = "\0".b * size
8
+ super(size:)
9
+ end
10
+
11
+ def read(offset, length) = @data.byteslice(offset, length)
12
+
13
+ def write(offset, data)
14
+ @data[offset, data.bytesize] = data
15
+ data.bytesize
16
+ end
17
+
18
+ def discard(offset, length)
19
+ @data[offset, length] = "\0".b * length
20
+ 0
21
+ end
22
+
23
+ alias write_zeroes discard
24
+ end
25
+
26
+ UBLK::Device.create(RamDisk.new(256 * 1024 * 1024)).run if $PROGRAM_NAME == __FILE__
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ramdisk"
4
+
5
+ class TracingTarget < UBLK::Target
6
+ def initialize(target, output: $stderr)
7
+ @target = target
8
+ @output = output
9
+ super(size: target.size, queues: target.queues, depth: target.depth)
10
+ end
11
+
12
+ %i[read write discard write_zeroes].each do |operation|
13
+ define_method(operation) do |offset, value|
14
+ @output.puts("#{operation} offset=#{offset} length=#{value.respond_to?(:bytesize) ? value.bytesize : value}")
15
+ @target.public_send(operation, offset, value)
16
+ end
17
+ end
18
+
19
+ def flush
20
+ @output.puts("flush")
21
+ @target.flush
22
+ end
23
+ end
24
+
25
+ UBLK::Device.create(TracingTarget.new(RamDisk.new(256 * 1024 * 1024))).run
data/ext/ublk/compat.h ADDED
@@ -0,0 +1,188 @@
1
+ #ifndef UBLK_COMPAT_H
2
+ #define UBLK_COMPAT_H
3
+
4
+ #include <linux/ioctl.h>
5
+ #include <linux/types.h>
6
+ #include <stddef.h>
7
+
8
+ #if !defined(UBLK_FORCE_COMPAT) && defined(__has_include)
9
+ #if __has_include(<linux/ublk_cmd.h>)
10
+ #include <linux/ublk_cmd.h>
11
+ #define UBLK_HAVE_UAPI_HEADER 1
12
+ #endif
13
+ #endif
14
+
15
+ #ifndef UBLK_HAVE_UAPI_HEADER
16
+ #define UBLK_CMD_GET_QUEUE_AFFINITY 0x01
17
+ #define UBLK_CMD_GET_DEV_INFO 0x02
18
+ #define UBLK_CMD_ADD_DEV 0x04
19
+ #define UBLK_CMD_DEL_DEV 0x05
20
+ #define UBLK_CMD_START_DEV 0x06
21
+ #define UBLK_CMD_STOP_DEV 0x07
22
+ #define UBLK_CMD_SET_PARAMS 0x08
23
+ #define UBLK_CMD_GET_PARAMS 0x09
24
+ #define UBLK_CMD_START_USER_RECOVERY 0x10
25
+ #define UBLK_CMD_END_USER_RECOVERY 0x11
26
+ #define UBLK_CMD_GET_DEV_INFO2 0x12
27
+
28
+ #define UBLK_IO_FETCH_REQ 0x20
29
+ #define UBLK_IO_COMMIT_AND_FETCH_REQ 0x21
30
+ #define UBLK_IO_NEED_GET_DATA 0x22
31
+
32
+ #define UBLK_F_USER_RECOVERY (1ULL << 3)
33
+
34
+ #define UBLK_IO_OP_READ 0
35
+ #define UBLK_IO_OP_WRITE 1
36
+ #define UBLK_IO_OP_FLUSH 2
37
+ #define UBLK_IO_OP_DISCARD 3
38
+ #define UBLK_IO_OP_WRITE_SAME 4
39
+ #define UBLK_IO_OP_WRITE_ZEROES 5
40
+
41
+ #define UBLK_ATTR_READ_ONLY (1U << 0)
42
+ #define UBLK_ATTR_ROTATIONAL (1U << 1)
43
+ #define UBLK_ATTR_VOLATILE_CACHE (1U << 2)
44
+ #define UBLK_ATTR_FUA (1U << 3)
45
+ #define UBLK_PARAM_TYPE_BASIC (1U << 0)
46
+ #define UBLK_PARAM_TYPE_DISCARD (1U << 1)
47
+
48
+ struct ublksrv_io_desc {
49
+ __u32 op_flags;
50
+ __u32 nr_sectors;
51
+ __u64 start_sector;
52
+ __u64 addr;
53
+ };
54
+
55
+ struct ublksrv_io_cmd {
56
+ __u16 q_id;
57
+ __u16 tag;
58
+ __s32 result;
59
+ __u64 addr;
60
+ };
61
+
62
+ struct ublk_param_basic {
63
+ __u32 attrs;
64
+ __u8 logical_bs_shift;
65
+ __u8 physical_bs_shift;
66
+ __u8 io_opt_shift;
67
+ __u8 io_min_shift;
68
+ __u32 max_sectors;
69
+ __u32 chunk_sectors;
70
+ __u64 dev_sectors;
71
+ __u64 virt_boundary_mask;
72
+ };
73
+
74
+ struct ublk_param_discard {
75
+ __u32 discard_alignment;
76
+ __u32 discard_granularity;
77
+ __u32 max_discard_sectors;
78
+ __u32 max_write_zeroes_sectors;
79
+ __u16 max_discard_segments;
80
+ __u16 reserved0;
81
+ };
82
+ #endif
83
+
84
+ #ifndef UBLK_F_CMD_IOCTL_ENCODE
85
+ #define UBLK_F_CMD_IOCTL_ENCODE (1ULL << 6)
86
+ #endif
87
+ #ifndef UBLK_F_USER_COPY
88
+ #define UBLK_F_USER_COPY (1ULL << 7)
89
+ #endif
90
+ #ifndef UBLK_MAX_QUEUE_DEPTH
91
+ #define UBLK_MAX_QUEUE_DEPTH 4096
92
+ #endif
93
+ #ifndef UBLK_MAX_NR_QUEUES
94
+ #define UBLK_MAX_NR_QUEUES 4096
95
+ #endif
96
+ #ifndef UBLKSRV_CMD_BUF_OFFSET
97
+ #define UBLKSRV_CMD_BUF_OFFSET 0
98
+ #endif
99
+ #ifndef UBLKSRV_IO_BUF_OFFSET
100
+ #define UBLKSRV_IO_BUF_OFFSET 0x80000000ULL
101
+ #endif
102
+ #ifndef UBLK_CMD_GET_FEATURES
103
+ #define UBLK_CMD_GET_FEATURES 0x13
104
+ #endif
105
+ #ifndef UBLK_IO_BUF_BITS
106
+ #define UBLK_IO_BUF_BITS 25
107
+ #endif
108
+ #ifndef UBLK_TAG_OFF
109
+ #define UBLK_TAG_OFF UBLK_IO_BUF_BITS
110
+ #endif
111
+ #ifndef UBLK_QID_OFF
112
+ #define UBLK_QID_OFF (UBLK_TAG_OFF + 16)
113
+ #endif
114
+
115
+ struct ublk_rb_ctrl_cmd {
116
+ __u32 dev_id;
117
+ __u16 queue_id;
118
+ __u16 len;
119
+ __u64 addr;
120
+ __u64 data[1];
121
+ __u16 dev_path_len;
122
+ __u16 pad;
123
+ __u32 reserved;
124
+ };
125
+
126
+ struct ublk_rb_dev_info {
127
+ __u16 nr_hw_queues;
128
+ __u16 queue_depth;
129
+ __u16 state;
130
+ __u16 pad0;
131
+ __u32 max_io_buf_bytes;
132
+ __u32 dev_id;
133
+ __s32 ublksrv_pid;
134
+ __u32 pad1;
135
+ __u64 flags;
136
+ __u64 ublksrv_flags;
137
+ __u32 owner_uid;
138
+ __u32 owner_gid;
139
+ __u64 reserved1;
140
+ __u64 reserved2;
141
+ };
142
+
143
+ struct ublk_rb_params {
144
+ __u32 len;
145
+ __u32 types;
146
+ struct ublk_param_basic basic;
147
+ struct ublk_param_discard discard;
148
+ };
149
+
150
+ #define UBLK_RB_U_READ_CMD(op) _IOR('u', (op), struct ublk_rb_ctrl_cmd)
151
+ #define UBLK_RB_U_RDWR_CMD(op) _IOWR('u', (op), struct ublk_rb_ctrl_cmd)
152
+ #define UBLK_RB_U_IO_CMD(op) _IOWR('u', (op), struct ublksrv_io_cmd)
153
+
154
+ _Static_assert(sizeof(struct ublk_rb_ctrl_cmd) == 32, "unexpected ublk control command layout");
155
+ _Static_assert(sizeof(struct ublk_rb_dev_info) == 64, "unexpected ublk device info layout");
156
+ _Static_assert(sizeof(struct ublk_rb_params) == 64, "unexpected ublk parameter layout");
157
+ _Static_assert(sizeof(struct ublksrv_io_desc) == 24, "unexpected ublk I/O descriptor layout");
158
+ _Static_assert(sizeof(struct ublksrv_io_cmd) == 16, "unexpected ublk I/O command layout");
159
+
160
+ #ifdef UBLK_U_CMD_GET_FEATURES
161
+ _Static_assert(UBLK_RB_U_READ_CMD(UBLK_CMD_GET_FEATURES) == UBLK_U_CMD_GET_FEATURES,
162
+ "GET_FEATURES command encoding mismatch");
163
+ #endif
164
+ #ifdef UBLK_U_CMD_GET_DEV_INFO
165
+ _Static_assert(UBLK_RB_U_READ_CMD(UBLK_CMD_GET_DEV_INFO) == UBLK_U_CMD_GET_DEV_INFO,
166
+ "GET_DEV_INFO command encoding mismatch");
167
+ #endif
168
+ #ifdef UBLK_U_CMD_ADD_DEV
169
+ _Static_assert(UBLK_RB_U_RDWR_CMD(UBLK_CMD_ADD_DEV) == UBLK_U_CMD_ADD_DEV,
170
+ "ADD_DEV command encoding mismatch");
171
+ #endif
172
+
173
+ #ifdef UBLK_HAVE_UAPI_HEADER
174
+ _Static_assert(sizeof(struct ublk_rb_ctrl_cmd) == sizeof(struct ublksrv_ctrl_cmd),
175
+ "control command size differs from Linux UAPI");
176
+ _Static_assert(offsetof(struct ublk_rb_ctrl_cmd, addr) == offsetof(struct ublksrv_ctrl_cmd, addr),
177
+ "control command address offset differs from Linux UAPI");
178
+ _Static_assert(sizeof(struct ublk_rb_dev_info) == sizeof(struct ublksrv_ctrl_dev_info),
179
+ "device info size differs from Linux UAPI");
180
+ _Static_assert(offsetof(struct ublk_rb_dev_info, flags) == offsetof(struct ublksrv_ctrl_dev_info, flags),
181
+ "device info flags offset differs from Linux UAPI");
182
+ _Static_assert(offsetof(struct ublk_rb_params, basic) == offsetof(struct ublk_params, basic),
183
+ "basic parameter offset differs from Linux UAPI");
184
+ _Static_assert(offsetof(struct ublk_rb_params, discard) == offsetof(struct ublk_params, discard),
185
+ "discard parameter offset differs from Linux UAPI");
186
+ #endif
187
+
188
+ #endif
@@ -0,0 +1,18 @@
1
+ /* Generated by rake gen:constants. */
2
+ #include "internal.h"
3
+
4
+ #define UBLK_CONST(name) rb_define_const(ublk_native_module, #name, ULL2NUM(name))
5
+
6
+ void ublk_init_constants(void)
7
+ {
8
+ UBLK_CONST(UBLK_F_USER_COPY);
9
+ UBLK_CONST(UBLK_F_CMD_IOCTL_ENCODE);
10
+ UBLK_CONST(UBLK_F_USER_RECOVERY);
11
+ UBLK_CONST(UBLK_MAX_QUEUE_DEPTH);
12
+ UBLK_CONST(UBLKSRV_IO_BUF_OFFSET);
13
+ UBLK_CONST(UBLK_IO_OP_READ);
14
+ UBLK_CONST(UBLK_IO_OP_WRITE);
15
+ UBLK_CONST(UBLK_IO_OP_FLUSH);
16
+ UBLK_CONST(UBLK_IO_OP_DISCARD);
17
+ UBLK_CONST(UBLK_IO_OP_WRITE_ZEROES);
18
+ }