userfaultfd 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: c4c0a7c4dc065c872850b1c6d1cec3c19130f606c7ecad76efc0403e84418693
4
+ data.tar.gz: 718b058ac60f5abff6c5f66d22b9e7a38c82c4b704490f669762c16994bdbba1
5
+ SHA512:
6
+ metadata.gz: 138ef38522fc668d6e92ec41d38f05451eece15f5518949c2197bf6020ebf3839cd193ad0f22fc8a9bfe84dc2c2f3d94766b18beb8f630da220b25974b57aca9
7
+ data.tar.gz: 0ce48388e31b212bcd9553cdd3243fd6d20c6a9071f311613e212b2767c85c6c5e2c1d6b0d01130728097c9a017449a1da81858ec197e6ee51a3da396f7fae1a
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,142 @@
1
+ # userfaultfd
2
+
3
+ Ruby bindings for Linux `userfaultfd(2)`, with mmap-backed regions and handlers that avoid blocking Ruby's Global VM Lock (GVL).
4
+
5
+ ## The deadlock rule
6
+
7
+ A Ruby thread must never touch registered memory through a raw pointer while a Ruby callback is expected to resolve the fault. The faulting thread keeps the GVL, so the callback cannot run.
8
+
9
+ Use one of these supported arrangements:
10
+
11
+ - `Region#read` / `Region#write`: the C extension releases the GVL around memory access.
12
+ - A forked faulting process: the parent callback has a separate GVL. `UFFD_FEATURE_EVENT_FORK` also requires `CAP_SYS_PTRACE`; check `enabled_features`.
13
+ - A native `:zero_fill`, `:backing_file`, or `:prefilled` handler: the handler never enters Ruby.
14
+
15
+ See [docs/gvl-and-page-faults.md](docs/gvl-and-page-faults.md) for the full failure sequence and implementation details.
16
+
17
+ ## Requirements
18
+
19
+ - Linux 4.11 or newer with `CONFIG_USERFAULTFD`
20
+ - Ruby 3.2 or newer
21
+ - A C compiler; the Linux 4.11 baseline UAPI is bundled for build hosts without `linux/userfaultfd.h`
22
+ - Permission through `/dev/userfaultfd`, or the `userfaultfd(2)` syscall with `UFFD_USER_MODE_ONLY`
23
+
24
+ The gem tries `/dev/userfaultfd` first and falls back to the syscall. Requiring the gem succeeds on unsupported systems; `UserfaultFD.supported?` returns `false`, and opening a descriptor raises `UserfaultFD::UnsupportedError` or the relevant `Errno::*` permission error. Linux before 5.11 does not understand `UFFD_USER_MODE_ONLY`; an appropriately permitted caller can explicitly use `user_mode_only: false` with `.supported?`, `.features`, and `.new`. This opts into kernel-originated faults and is never automatic.
25
+
26
+ ## Installation
27
+
28
+ ```ruby
29
+ gem "userfaultfd"
30
+ ```
31
+
32
+ Then run `bundle install`. The `userfaultfd` RubyGems name was unclaimed when this project was initialized; until it is published, use the repository source in your Gemfile.
33
+
34
+ ## Native zero fill
35
+
36
+ This mode is safe even when the main Ruby thread faults:
37
+
38
+ ```ruby
39
+ require "userfaultfd"
40
+
41
+ region = UserfaultFD::Region.new(size: 64 * 1024 * 1024)
42
+ uffd = UserfaultFD.new(features: [])
43
+ uffd.register(region, mode: :missing)
44
+ handler = uffd.start_handler(mode: :zero_fill)
45
+
46
+ data = region.read(0, region.size)
47
+ raise "not zero" unless data == "\0".b * region.size
48
+
49
+ handler.stop
50
+ uffd.close
51
+ region.unmap
52
+ ```
53
+
54
+ ## Ruby callback in one process
55
+
56
+ `Region#read` releases the GVL, allowing the handler's Ruby block to run:
57
+
58
+ ```ruby
59
+ page_size = UserfaultFD::Region.allocate.page_size
60
+ region = UserfaultFD::Region.new(size: page_size)
61
+ uffd = UserfaultFD.new(features: [])
62
+ uffd.register(region, mode: :missing)
63
+ handler = uffd.start_handler do |event|
64
+ event.copy("x" * region.page_size) if event.is_a?(UserfaultFD::Fault)
65
+ end
66
+
67
+ region.read(0, region.page_size) #=> "x" * page_size
68
+
69
+ handler.stop
70
+ uffd.close
71
+ region.unmap
72
+ ```
73
+
74
+ ## Forked faulting process
75
+
76
+ When permitted, `UserfaultFD.new` enables `EVENT_FORK` by default. A native event reader consumes the fork event without waiting for the GVL, then delivers `ForkEvent` and child faults to the Ruby handler.
77
+
78
+ ```ruby
79
+ page_size = UserfaultFD::Region.allocate.page_size
80
+ region = UserfaultFD::Region.new(size: page_size)
81
+ uffd = UserfaultFD.new
82
+ raise "EVENT_FORK needs CAP_SYS_PTRACE" unless uffd.enabled_features.include?(:event_fork)
83
+
84
+ uffd.register(region, mode: :missing)
85
+ handler = uffd.start_handler do |event|
86
+ case event
87
+ when UserfaultFD::Fault
88
+ event.copy("child".ljust(region.page_size, "\0"))
89
+ when UserfaultFD::ForkEvent
90
+ # The handler monitors and closes event.child_uffd automatically.
91
+ end
92
+ end
93
+
94
+ pid = fork { exit!(region.read(0, 5) == "child" ? 0 : 1) }
95
+ Process.wait(pid)
96
+
97
+ handler.stop
98
+ uffd.close
99
+ region.unmap
100
+ ```
101
+
102
+ ## Other operations
103
+
104
+ - `Fault#zero`, `#copy`, `#wake`, `#continue`, `#poison`, and `#move`
105
+ - `UserfaultFD#writeprotect` with `mode: [:missing, :wp]`
106
+ - `Region#madvise(:dontneed)` to drop page-table entries and `:remove` to discard shared pages
107
+ - `mode: :backing_file, io: file` for native lazy file loading
108
+ - `mode: :prefilled, source: region` for native copies from another region
109
+ - `ForkEvent`, `RemapEvent`, `RemoveEvent`, and `UnmapEvent`
110
+ - `UserfaultFD.features` for kernel capabilities and `#enabled_features` for this descriptor
111
+
112
+ Optional ioctls raise `UnsupportedError` when they are absent from the build headers or running kernel.
113
+
114
+ ## Unsupported and constrained environments
115
+
116
+ | Environment | Result |
117
+ |---|---|
118
+ | macOS, BSD, Windows | `require` works; `supported?` is `false` |
119
+ | Linux before 4.11 or without `CONFIG_USERFAULTFD` | Unsupported |
120
+ | Linux 4.11–5.10 | An appropriately permitted caller must explicitly pass `user_mode_only: false` |
121
+ | Docker with the default seccomp profile | The syscall is commonly blocked; allow `userfaultfd` or use an accessible `/dev/userfaultfd` |
122
+ | Docker Desktop / WSL2 | Depends on the VM kernel, config, device, and seccomp policy rather than the guest userspace alone |
123
+ | Linux with `vm.unprivileged_userfaultfd=0` | User-mode-only faults still work on kernels that honor `UFFD_USER_MODE_ONLY`; fork events require `CAP_SYS_PTRACE` |
124
+ | GitHub-hosted runners | Unit tests run; system tests skip when the host policy denies userfaultfd |
125
+ | Kernels before 6.6 / 6.8 | `POISON` / `MOVE` capabilities are absent |
126
+
127
+ Never register Ruby's object heap. Registered memory must come from `UserfaultFD::Region`; otherwise GC or ordinary Ruby code can fault while holding the GVL.
128
+
129
+ ## Development
130
+
131
+ ```sh
132
+ rake compile
133
+ rake test:unit
134
+ rake test:system
135
+ rbs -I sig validate
136
+ ```
137
+
138
+ System examples use timeouts because an unresolved page fault intentionally blocks its faulting process. The kernel matrix runner is in `tools/vm`.
139
+
140
+ ## License
141
+
142
+ MIT. See [LICENSE.txt](LICENSE.txt).
@@ -0,0 +1,58 @@
1
+ # GVL and userfaultfd page faults
2
+
3
+ ## Why the naive Ruby callback deadlocks
4
+
5
+ The kernel suspends a thread that faults on a registered page until another thread resolves the event. If ordinary Ruby code performs that access through a raw pointer, the suspended thread still owns the GVL:
6
+
7
+ 1. Ruby thread A touches the registered address while holding the GVL.
8
+ 2. The kernel suspends A and publishes `UFFD_EVENT_PAGEFAULT`.
9
+ 3. Handler thread B reads the event.
10
+ 4. B needs the GVL before it can invoke the Ruby block.
11
+ 5. A cannot release the GVL until the fault is resolved, while B cannot resolve it until A releases the GVL.
12
+
13
+ `spec/system/userfaultfd_spec.rb` fixes this behavior as an intentional test: a child process performs a `Fiddle::Pointer` read, the test confirms it remains blocked for two seconds, then sends `SIGKILL`. The timeout and process isolation prevent a failed test from hanging the suite.
14
+
15
+ ## Arrangement B: GVL-free memory access
16
+
17
+ `Region#read` and `Region#write` copy memory inside `rb_thread_call_without_gvl`. A native event-reader thread moves kernel messages into a pipe. The Ruby handler can therefore acquire the GVL, create `Fault` objects, and call `UFFDIO_COPY` or `UFFDIO_ZEROPAGE`.
18
+
19
+ The resolution ioctls also run without the GVL. Strings passed to `Fault#copy` are copied into C-owned memory first, so Ruby GC cannot move or free the source while the ioctl is running.
20
+
21
+ ## Arrangement A: process separation and fork events
22
+
23
+ Fork handling has an extra cycle: `EVENT_FORK` must be read before `fork(2)` completes, but the Ruby thread invoking `fork` holds the GVL. A Ruby event-reader thread therefore cannot make progress.
24
+
25
+ The extension uses a C event-reader thread that never enters Ruby while polling the parent and child descriptors. It reads `EVENT_FORK`, starts monitoring the child fd, and writes fixed-size records to a pipe. Once `fork` returns and Ruby can schedule again, the handler converts those records into `ForkEvent` and `Fault` objects.
26
+
27
+ This requires `UFFD_FEATURE_EVENT_FORK`, which the kernel restricts with `CAP_SYS_PTRACE`. `UserfaultFD.new` tries to enable it and falls back to a descriptor without fork events on `EPERM`; callers must check `enabled_features` before using the fork arrangement.
28
+
29
+ ## Arrangement C: native handlers
30
+
31
+ The native modes resolve missing faults entirely in a pthread:
32
+
33
+ - `zero_fill` issues `UFFDIO_ZEROPAGE`.
34
+ - `backing_file` uses `pread` and `UFFDIO_COPY`.
35
+ - `prefilled` copies from a second mmap-backed `Region`.
36
+
37
+ No Ruby callback or Ruby-managed buffer is involved. These modes are the appropriate choice for fault rates that a Ruby callback cannot sustain.
38
+
39
+ ## Boundaries
40
+
41
+ - Do not register the Ruby heap.
42
+ - Do not dereference `Region#to_ptr` from Ruby while using a Ruby handler.
43
+ - A handler monitors at most 64 fork descendants. An epoll-backed dynamic set is the upgrade path if external process fan-out exceeds that ceiling.
44
+ - Native handlers currently service the descriptor on which they started; use the Ruby handler/event reader for fork descendants.
45
+ - Always stop handlers before closing descriptors or unmapping regions.
46
+
47
+ ## Permission survey
48
+
49
+ `vm.unprivileged_userfaultfd` is a host-kernel setting, not a reliable property of the userspace distribution. Current [upstream kernel documentation](https://www.kernel.org/doc/html/latest/admin-guide/sysctl/vm.html#unprivileged-userfaultfd) and [Debian's packaged kernel documentation](https://sources.debian.org/src/linux/6.12.96-1/Documentation/admin-guide/sysctl/vm.rst) document a default of `0`: unprivileged callers must use `UFFD_USER_MODE_ONLY`. Ubuntu's [userfaultfd man page](https://manpages.ubuntu.com/manpages/jammy/man2/userfaultfd.2.html) likewise documents `EPERM` when the value is `0` and the caller lacks `CAP_SYS_PTRACE`.
50
+
51
+ Cloud images, administrators, and container hosts can override that value, and containers observe the host kernel rather than the image's `/etc/os-release`. The gem therefore probes the real syscall instead of guessing from a distribution name. Diagnose a target with:
52
+
53
+ ```sh
54
+ cat /proc/sys/vm/unprivileged_userfaultfd
55
+ grep USERFAULTFD /boot/config-"$(uname -r)" 2>/dev/null
56
+ ```
57
+
58
+ On Linux 5.11 and newer, the default `user_mode_only: true` remains usable when the sysctl is `0`. Linux 4.11–5.10 requires the explicitly less restrictive `user_mode_only: false` and whatever privilege or sysctl policy that kernel enforces.
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "userfaultfd"
4
+
5
+ abort "write-protect faults are unavailable" unless UserfaultFD.features.include?(:pagefault_flag_wp)
6
+
7
+ page_size = UserfaultFD::Region.allocate.page_size
8
+ region = UserfaultFD::Region.new(size: page_size)
9
+ uffd = UserfaultFD.new(features: [:pagefault_flag_wp])
10
+ uffd.register(region, mode: %i[missing wp])
11
+ dirty_pages = []
12
+ handler = uffd.start_handler do |event|
13
+ next unless event.is_a?(UserfaultFD::Fault)
14
+
15
+ if event.wp?
16
+ dirty_pages << event.address
17
+ uffd.writeprotect(region, enabled: false)
18
+ else
19
+ event.zero
20
+ end
21
+ end
22
+
23
+ region.read(0, 1)
24
+ uffd.writeprotect(region, enabled: true)
25
+ region.write(0, "changed")
26
+ puts "dirty pages: #{dirty_pages.length}"
27
+
28
+ handler.stop
29
+ uffd.close
30
+ region.unmap
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "userfaultfd"
4
+
5
+ path = ARGV.fetch(0)
6
+ file = File.open(path, "rb")
7
+ page_size = UserfaultFD::Region.allocate.page_size
8
+ size = [((file.size + page_size - 1) / page_size) * page_size, page_size].max
9
+ region = UserfaultFD::Region.new(size: size)
10
+ uffd = UserfaultFD.new(features: [])
11
+ uffd.register(region, mode: :missing)
12
+ handler = uffd.start_handler(mode: :backing_file, io: file)
13
+
14
+ puts region.read(0, [file.size, page_size].min)
15
+
16
+ handler.stop
17
+ uffd.close
18
+ region.unmap
19
+ file.close
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "userfaultfd"
4
+
5
+ required = %i[poison event_fork]
6
+ abort "UFFDIO_POISON and EVENT_FORK are required" unless (required - UserfaultFD.features).empty?
7
+
8
+ page_size = UserfaultFD::Region.allocate.page_size
9
+ region = UserfaultFD::Region.new(size: page_size)
10
+ uffd = UserfaultFD.new(features: required)
11
+ uffd.register(region, mode: :missing)
12
+ handler = uffd.start_handler do |event|
13
+ event.poison if event.is_a?(UserfaultFD::Fault)
14
+ end
15
+
16
+ pid = fork do
17
+ Process.setrlimit(Process::RLIMIT_CORE, 0)
18
+ region.read(0, 1)
19
+ exit! 0
20
+ end
21
+ Process.wait(pid)
22
+ puts "child status: #{$?}"
23
+
24
+ handler.stop
25
+ uffd.close
26
+ region.unmap
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "socket"
4
+ require "userfaultfd"
5
+
6
+ page_size = UserfaultFD::Region.allocate.page_size
7
+ region = UserfaultFD::Region.new(size: page_size)
8
+ uffd = UserfaultFD.new
9
+ abort "EVENT_FORK requires CAP_SYS_PTRACE" unless uffd.enabled_features.include?(:event_fork)
10
+
11
+ sender, receiver = Socket.pair(:UNIX, :STREAM)
12
+ sender.write("migrated".ljust(page_size, "\0"))
13
+ uffd.register(region, mode: :missing)
14
+ handler = uffd.start_handler do |event|
15
+ event.copy(receiver.read(page_size)) if event.is_a?(UserfaultFD::Fault)
16
+ end
17
+
18
+ pid = fork do
19
+ STDOUT.write("#{region.read(0, 8)}\n")
20
+ STDOUT.flush
21
+ exit! 0
22
+ end
23
+ Process.wait(pid)
24
+
25
+ handler.stop
26
+ uffd.close
27
+ region.unmap
28
+ sender.close
29
+ receiver.close
@@ -0,0 +1,237 @@
1
+ #ifndef USERFAULTFD_COMPAT_H
2
+ #define USERFAULTFD_COMPAT_H
3
+
4
+ #ifdef __linux__
5
+ # include <stdint.h>
6
+ # include <sys/ioctl.h>
7
+
8
+ # ifdef HAVE_LINUX_USERFAULTFD_H
9
+ # include <linux/userfaultfd.h>
10
+ # else
11
+ /* Linux 4.11 baseline UAPI for build hosts without userfaultfd.h. */
12
+ # define UFFD_API UINT64_C(0xAA)
13
+
14
+ # define _UFFDIO_REGISTER 0x00
15
+ # define _UFFDIO_UNREGISTER 0x01
16
+ # define _UFFDIO_WAKE 0x02
17
+ # define _UFFDIO_COPY 0x03
18
+ # define _UFFDIO_ZEROPAGE 0x04
19
+ # define _UFFDIO_WRITEPROTECT 0x06
20
+ # define _UFFDIO_CONTINUE 0x07
21
+ # define _UFFDIO_API 0x3F
22
+ # define UFFDIO 0xAA
23
+
24
+ struct uffd_msg {
25
+ uint8_t event;
26
+ uint8_t reserved1;
27
+ uint16_t reserved2;
28
+ uint32_t reserved3;
29
+ union {
30
+ struct {
31
+ uint64_t flags;
32
+ uint64_t address;
33
+ union { uint32_t ptid; } feat;
34
+ } pagefault;
35
+ struct { uint32_t ufd; } fork;
36
+ struct { uint64_t from, to, len; } remap;
37
+ struct { uint64_t start, end; } remove;
38
+ struct { uint64_t reserved1, reserved2, reserved3; } reserved;
39
+ } arg;
40
+ } __attribute__((packed));
41
+
42
+ # define UFFD_EVENT_PAGEFAULT 0x12
43
+ # define UFFD_EVENT_FORK 0x13
44
+ # define UFFD_EVENT_REMAP 0x14
45
+ # define UFFD_EVENT_REMOVE 0x15
46
+ # define UFFD_EVENT_UNMAP 0x16
47
+
48
+ # define UFFD_PAGEFAULT_FLAG_WRITE (1 << 0)
49
+ # define UFFD_PAGEFAULT_FLAG_WP (1 << 1)
50
+ # define UFFD_PAGEFAULT_FLAG_MINOR (1 << 2)
51
+
52
+ struct uffdio_api { uint64_t api, features, ioctls; };
53
+ # define UFFD_FEATURE_PAGEFAULT_FLAG_WP (UINT64_C(1) << 0)
54
+ # define UFFD_FEATURE_EVENT_FORK (UINT64_C(1) << 1)
55
+ # define UFFD_FEATURE_EVENT_REMAP (UINT64_C(1) << 2)
56
+ # define UFFD_FEATURE_EVENT_REMOVE (UINT64_C(1) << 3)
57
+ # define UFFD_FEATURE_MISSING_HUGETLBFS (UINT64_C(1) << 4)
58
+ # define UFFD_FEATURE_MISSING_SHMEM (UINT64_C(1) << 5)
59
+ # define UFFD_FEATURE_EVENT_UNMAP (UINT64_C(1) << 6)
60
+ # define UFFD_FEATURE_SIGBUS (UINT64_C(1) << 7)
61
+ # define UFFD_FEATURE_THREAD_ID (UINT64_C(1) << 8)
62
+
63
+ struct uffdio_range { uint64_t start, len; };
64
+ struct uffdio_register {
65
+ struct uffdio_range range;
66
+ uint64_t mode, ioctls;
67
+ };
68
+ # define UFFDIO_REGISTER_MODE_MISSING (UINT64_C(1) << 0)
69
+ # define UFFDIO_REGISTER_MODE_WP (UINT64_C(1) << 1)
70
+ # define UFFDIO_REGISTER_MODE_MINOR (UINT64_C(1) << 2)
71
+
72
+ struct uffdio_copy {
73
+ uint64_t dst, src, len, mode;
74
+ int64_t copy;
75
+ };
76
+ # define UFFDIO_COPY_MODE_DONTWAKE (UINT64_C(1) << 0)
77
+
78
+ struct uffdio_zeropage {
79
+ struct uffdio_range range;
80
+ uint64_t mode;
81
+ int64_t zeropage;
82
+ };
83
+ # define UFFDIO_ZEROPAGE_MODE_DONTWAKE (UINT64_C(1) << 0)
84
+
85
+ struct uffdio_writeprotect {
86
+ struct uffdio_range range;
87
+ uint64_t mode;
88
+ };
89
+ # define UFFDIO_WRITEPROTECT_MODE_WP (UINT64_C(1) << 0)
90
+
91
+ struct uffdio_continue {
92
+ struct uffdio_range range;
93
+ uint64_t mode;
94
+ int64_t mapped;
95
+ };
96
+ # define UFFDIO_CONTINUE_MODE_DONTWAKE (UINT64_C(1) << 0)
97
+
98
+ # define UFFDIO_API _IOWR(UFFDIO, _UFFDIO_API, struct uffdio_api)
99
+ # define UFFDIO_REGISTER _IOWR(UFFDIO, _UFFDIO_REGISTER, struct uffdio_register)
100
+ # define UFFDIO_UNREGISTER _IOR(UFFDIO, _UFFDIO_UNREGISTER, struct uffdio_range)
101
+ # define UFFDIO_WAKE _IOR(UFFDIO, _UFFDIO_WAKE, struct uffdio_range)
102
+ # define UFFDIO_COPY _IOWR(UFFDIO, _UFFDIO_COPY, struct uffdio_copy)
103
+ # define UFFDIO_ZEROPAGE _IOWR(UFFDIO, _UFFDIO_ZEROPAGE, struct uffdio_zeropage)
104
+ # define UFFDIO_WRITEPROTECT _IOWR(UFFDIO, _UFFDIO_WRITEPROTECT, struct uffdio_writeprotect)
105
+ # define UFFDIO_CONTINUE _IOWR(UFFDIO, _UFFDIO_CONTINUE, struct uffdio_continue)
106
+ # endif
107
+
108
+ # ifndef UFFD_USER_MODE_ONLY
109
+ # define UFFD_USER_MODE_ONLY 1
110
+ # endif
111
+
112
+ /* Keep old or absent build headers feature-complete; the kernel still probes support. */
113
+ # ifndef UFFD_PAGEFAULT_FLAG_WP
114
+ # define UFFD_PAGEFAULT_FLAG_WP (1 << 1)
115
+ # endif
116
+ # ifndef UFFD_PAGEFAULT_FLAG_MINOR
117
+ # define UFFD_PAGEFAULT_FLAG_MINOR (1 << 2)
118
+ # endif
119
+ # ifndef UFFD_FEATURE_PAGEFAULT_FLAG_WP
120
+ # define UFFD_FEATURE_PAGEFAULT_FLAG_WP (UINT64_C(1) << 0)
121
+ # endif
122
+ # ifndef UFFD_FEATURE_EVENT_FORK
123
+ # define UFFD_FEATURE_EVENT_FORK (UINT64_C(1) << 1)
124
+ # endif
125
+ # ifndef UFFD_FEATURE_EVENT_REMAP
126
+ # define UFFD_FEATURE_EVENT_REMAP (UINT64_C(1) << 2)
127
+ # endif
128
+ # ifndef UFFD_FEATURE_EVENT_REMOVE
129
+ # define UFFD_FEATURE_EVENT_REMOVE (UINT64_C(1) << 3)
130
+ # endif
131
+ # ifndef UFFD_FEATURE_MISSING_HUGETLBFS
132
+ # define UFFD_FEATURE_MISSING_HUGETLBFS (UINT64_C(1) << 4)
133
+ # endif
134
+ # ifndef UFFD_FEATURE_MISSING_SHMEM
135
+ # define UFFD_FEATURE_MISSING_SHMEM (UINT64_C(1) << 5)
136
+ # endif
137
+ # ifndef UFFD_FEATURE_EVENT_UNMAP
138
+ # define UFFD_FEATURE_EVENT_UNMAP (UINT64_C(1) << 6)
139
+ # endif
140
+ # ifndef UFFD_FEATURE_SIGBUS
141
+ # define UFFD_FEATURE_SIGBUS (UINT64_C(1) << 7)
142
+ # endif
143
+ # ifndef UFFD_FEATURE_THREAD_ID
144
+ # define UFFD_FEATURE_THREAD_ID (UINT64_C(1) << 8)
145
+ # endif
146
+ # ifndef UFFD_FEATURE_MINOR_HUGETLBFS
147
+ # define UFFD_FEATURE_MINOR_HUGETLBFS (UINT64_C(1) << 9)
148
+ # endif
149
+ # ifndef UFFD_FEATURE_MINOR_SHMEM
150
+ # define UFFD_FEATURE_MINOR_SHMEM (UINT64_C(1) << 10)
151
+ # endif
152
+ # ifndef UFFD_FEATURE_EXACT_ADDRESS
153
+ # define UFFD_FEATURE_EXACT_ADDRESS (UINT64_C(1) << 11)
154
+ # endif
155
+ # ifndef UFFD_FEATURE_WP_HUGETLBFS_SHMEM
156
+ # define UFFD_FEATURE_WP_HUGETLBFS_SHMEM (UINT64_C(1) << 12)
157
+ # endif
158
+ # ifndef UFFD_FEATURE_WP_UNPOPULATED
159
+ # define UFFD_FEATURE_WP_UNPOPULATED (UINT64_C(1) << 13)
160
+ # endif
161
+ # ifndef UFFD_FEATURE_POISON
162
+ # define UFFD_FEATURE_POISON (UINT64_C(1) << 14)
163
+ # endif
164
+ # ifndef UFFD_FEATURE_WP_ASYNC
165
+ # define UFFD_FEATURE_WP_ASYNC (UINT64_C(1) << 15)
166
+ # endif
167
+ # ifndef UFFD_FEATURE_MOVE
168
+ # define UFFD_FEATURE_MOVE (UINT64_C(1) << 16)
169
+ # endif
170
+
171
+ # ifndef UFFDIO_REGISTER_MODE_WP
172
+ # define UFFDIO_REGISTER_MODE_WP (UINT64_C(1) << 1)
173
+ # endif
174
+ # ifndef UFFDIO_REGISTER_MODE_MINOR
175
+ # define UFFDIO_REGISTER_MODE_MINOR (UINT64_C(1) << 2)
176
+ # endif
177
+
178
+ # ifndef UFFDIO_WRITEPROTECT
179
+ # ifndef _UFFDIO_WRITEPROTECT
180
+ # define _UFFDIO_WRITEPROTECT 0x06
181
+ # endif
182
+ struct uffdio_writeprotect {
183
+ struct uffdio_range range;
184
+ uint64_t mode;
185
+ };
186
+ # define UFFDIO_WRITEPROTECT_MODE_WP (UINT64_C(1) << 0)
187
+ # define UFFDIO_WRITEPROTECT _IOWR(UFFDIO, _UFFDIO_WRITEPROTECT, struct uffdio_writeprotect)
188
+ # endif
189
+
190
+ # ifndef UFFDIO_CONTINUE
191
+ # ifndef _UFFDIO_CONTINUE
192
+ # define _UFFDIO_CONTINUE 0x07
193
+ # endif
194
+ struct uffdio_continue {
195
+ struct uffdio_range range;
196
+ uint64_t mode;
197
+ int64_t mapped;
198
+ };
199
+ # define UFFDIO_CONTINUE_MODE_DONTWAKE (UINT64_C(1) << 0)
200
+ # define UFFDIO_CONTINUE _IOWR(UFFDIO, _UFFDIO_CONTINUE, struct uffdio_continue)
201
+ # endif
202
+
203
+ # ifndef UFFDIO_POISON
204
+ # ifndef _UFFDIO_POISON
205
+ # define _UFFDIO_POISON 0x08
206
+ # endif
207
+ struct uffdio_poison {
208
+ struct uffdio_range range;
209
+ uint64_t mode;
210
+ int64_t updated;
211
+ };
212
+ # define UFFDIO_POISON_MODE_DONTWAKE (UINT64_C(1) << 0)
213
+ # define UFFDIO_POISON _IOWR(UFFDIO, _UFFDIO_POISON, struct uffdio_poison)
214
+ # endif
215
+
216
+ # ifndef UFFDIO_MOVE
217
+ # ifndef _UFFDIO_MOVE
218
+ # define _UFFDIO_MOVE 0x05
219
+ # endif
220
+ struct uffdio_move {
221
+ uint64_t dst, src, len, mode;
222
+ int64_t move;
223
+ };
224
+ # define UFFDIO_MOVE_MODE_DONTWAKE (UINT64_C(1) << 0)
225
+ # define UFFDIO_MOVE _IOWR(UFFDIO, _UFFDIO_MOVE, struct uffdio_move)
226
+ # endif
227
+
228
+ /* Added in Linux 5.19. Old headers still build and use the syscall path. */
229
+ # ifndef USERFAULTFD_IOC
230
+ # define USERFAULTFD_IOC 0xAA
231
+ # endif
232
+ # ifndef USERFAULTFD_IOC_NEW
233
+ # define USERFAULTFD_IOC_NEW _IO(USERFAULTFD_IOC, 0x00)
234
+ # endif
235
+ #endif
236
+
237
+ #endif
@@ -0,0 +1,102 @@
1
+ /* Generated by rake gen:constants. Do not edit. */
2
+ #include "ruby.h"
3
+ #include "extconf.h"
4
+
5
+ #ifdef __linux__
6
+ # include "compat.h"
7
+ # define UFFD_CONST(name) rb_define_const(klass, #name, ULL2NUM(name))
8
+ #else
9
+ # define UFFD_CONST(name) ((void)0)
10
+ #endif
11
+
12
+ void
13
+ userfaultfd_define_constants(VALUE klass)
14
+ {
15
+ #ifdef UFFD_API
16
+ UFFD_CONST(UFFD_API);
17
+ #endif
18
+ #ifdef UFFD_EVENT_PAGEFAULT
19
+ UFFD_CONST(UFFD_EVENT_PAGEFAULT);
20
+ #endif
21
+ #ifdef UFFD_EVENT_FORK
22
+ UFFD_CONST(UFFD_EVENT_FORK);
23
+ #endif
24
+ #ifdef UFFD_EVENT_REMAP
25
+ UFFD_CONST(UFFD_EVENT_REMAP);
26
+ #endif
27
+ #ifdef UFFD_EVENT_REMOVE
28
+ UFFD_CONST(UFFD_EVENT_REMOVE);
29
+ #endif
30
+ #ifdef UFFD_EVENT_UNMAP
31
+ UFFD_CONST(UFFD_EVENT_UNMAP);
32
+ #endif
33
+ #ifdef UFFD_PAGEFAULT_FLAG_WRITE
34
+ UFFD_CONST(UFFD_PAGEFAULT_FLAG_WRITE);
35
+ #endif
36
+ #ifdef UFFD_PAGEFAULT_FLAG_WP
37
+ UFFD_CONST(UFFD_PAGEFAULT_FLAG_WP);
38
+ #endif
39
+ #ifdef UFFD_PAGEFAULT_FLAG_MINOR
40
+ UFFD_CONST(UFFD_PAGEFAULT_FLAG_MINOR);
41
+ #endif
42
+ #ifdef UFFD_FEATURE_PAGEFAULT_FLAG_WP
43
+ UFFD_CONST(UFFD_FEATURE_PAGEFAULT_FLAG_WP);
44
+ #endif
45
+ #ifdef UFFD_FEATURE_EVENT_FORK
46
+ UFFD_CONST(UFFD_FEATURE_EVENT_FORK);
47
+ #endif
48
+ #ifdef UFFD_FEATURE_EVENT_REMAP
49
+ UFFD_CONST(UFFD_FEATURE_EVENT_REMAP);
50
+ #endif
51
+ #ifdef UFFD_FEATURE_EVENT_REMOVE
52
+ UFFD_CONST(UFFD_FEATURE_EVENT_REMOVE);
53
+ #endif
54
+ #ifdef UFFD_FEATURE_EVENT_UNMAP
55
+ UFFD_CONST(UFFD_FEATURE_EVENT_UNMAP);
56
+ #endif
57
+ #ifdef UFFD_FEATURE_MISSING_HUGETLBFS
58
+ UFFD_CONST(UFFD_FEATURE_MISSING_HUGETLBFS);
59
+ #endif
60
+ #ifdef UFFD_FEATURE_MISSING_SHMEM
61
+ UFFD_CONST(UFFD_FEATURE_MISSING_SHMEM);
62
+ #endif
63
+ #ifdef UFFD_FEATURE_SIGBUS
64
+ UFFD_CONST(UFFD_FEATURE_SIGBUS);
65
+ #endif
66
+ #ifdef UFFD_FEATURE_THREAD_ID
67
+ UFFD_CONST(UFFD_FEATURE_THREAD_ID);
68
+ #endif
69
+ #ifdef UFFD_FEATURE_MINOR_HUGETLBFS
70
+ UFFD_CONST(UFFD_FEATURE_MINOR_HUGETLBFS);
71
+ #endif
72
+ #ifdef UFFD_FEATURE_MINOR_SHMEM
73
+ UFFD_CONST(UFFD_FEATURE_MINOR_SHMEM);
74
+ #endif
75
+ #ifdef UFFD_FEATURE_EXACT_ADDRESS
76
+ UFFD_CONST(UFFD_FEATURE_EXACT_ADDRESS);
77
+ #endif
78
+ #ifdef UFFD_FEATURE_WP_HUGETLBFS_SHMEM
79
+ UFFD_CONST(UFFD_FEATURE_WP_HUGETLBFS_SHMEM);
80
+ #endif
81
+ #ifdef UFFD_FEATURE_WP_UNPOPULATED
82
+ UFFD_CONST(UFFD_FEATURE_WP_UNPOPULATED);
83
+ #endif
84
+ #ifdef UFFD_FEATURE_POISON
85
+ UFFD_CONST(UFFD_FEATURE_POISON);
86
+ #endif
87
+ #ifdef UFFD_FEATURE_WP_ASYNC
88
+ UFFD_CONST(UFFD_FEATURE_WP_ASYNC);
89
+ #endif
90
+ #ifdef UFFD_FEATURE_MOVE
91
+ UFFD_CONST(UFFD_FEATURE_MOVE);
92
+ #endif
93
+ #ifdef UFFDIO_REGISTER_MODE_MISSING
94
+ UFFD_CONST(UFFDIO_REGISTER_MODE_MISSING);
95
+ #endif
96
+ #ifdef UFFDIO_REGISTER_MODE_WP
97
+ UFFD_CONST(UFFDIO_REGISTER_MODE_WP);
98
+ #endif
99
+ #ifdef UFFDIO_REGISTER_MODE_MINOR
100
+ UFFD_CONST(UFFDIO_REGISTER_MODE_MINOR);
101
+ #endif
102
+ }
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+
5
+ have_header("linux/userfaultfd.h")
6
+ have_header("sys/ioctl.h")
7
+ have_header("sys/syscall.h")
8
+ have_func("syscall", "unistd.h")
9
+ have_library("pthread")
10
+
11
+ create_header
12
+ create_makefile("userfaultfd/userfaultfd")