Skip to content

Commit 35e66e8

Browse files
committed
eventfd
1 parent 4742146 commit 35e66e8

5 files changed

Lines changed: 213 additions & 1 deletion

File tree

kernel/interfaces/system/syscall/vfs.cppm

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ export namespace syscall::vfs
201201
int fsopen(const char __user *fsname, std::uint32_t flags);
202202

203203
int inotify_init1(int flags);
204+
205+
int eventfd2(unsigned int count, int flags);
206+
int eventfd(unsigned int count);
204207
} // export namespace syscall::vfs
205208

206209
namespace syscall::vfs::detail

kernel/interfaces/system/vfs/vfs.cppm

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,8 @@ export namespace vfs
571571
{
572572
if (!ops)
573573
return std::unexpected { lib::err::invalid_device_or_address };
574+
if (!ops->seekable())
575+
return ops->read(shared_from_this(), offset, buffer);
574576
const std::unique_lock _ { lock };
575577
const auto ret = ops->read(shared_from_this(), offset, buffer);
576578
if (ret.has_value())
@@ -582,6 +584,8 @@ export namespace vfs
582584
{
583585
if (!ops)
584586
return std::unexpected { lib::err::invalid_device_or_address };
587+
if (!ops->seekable())
588+
return ops->write(shared_from_this(), offset, buffer);
585589
const std::unique_lock _ { lock };
586590
const auto ret = ops->write(shared_from_this(), offset, buffer);
587591
if (ret.has_value())

kernel/source/arch/x86_64/system/syscall.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,9 @@ namespace x86_64::syscall
192192
[273] = { "set_robust_list", proc::set_robust_list },
193193
[274] = { "get_robust_list", proc::get_robust_list },
194194
[280] = { "utimensat", vfs::utimensat },
195+
[284] = { "eventfd", vfs::eventfd, true },
195196
[288] = { "accept4", vfs::accept4, true },
197+
[290] = { "eventfd2", vfs::eventfd2, true },
196198
[292] = { "dup3", vfs::dup3, true },
197199
[293] = { "pipe2", vfs::pipe2 },
198200
[294] = { "inotify_init1", vfs::inotify_init1, true },
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
// Copyright (C) 2024-2026 ilobilo
2+
3+
module system.syscall.vfs;
4+
5+
namespace syscall::vfs
6+
{
7+
using namespace ::vfs;
8+
9+
namespace
10+
{
11+
constexpr auto ev_max = std::numeric_limits<std::uint64_t>::max() - 1;
12+
constexpr int efd_semaphore = (1 << 0);
13+
constexpr int efd_cloexec = o_cloexec;
14+
constexpr int efd_nonblock = o_nonblock;
15+
16+
struct data_t
17+
{
18+
lib::locker<
19+
std::uint64_t,
20+
lib::spinlock
21+
> counter;
22+
sched::wait_queue_t bell;
23+
bool semaphore;
24+
25+
data_t(std::uint64_t initial, bool semaphore)
26+
: counter { initial }, bell { }, semaphore { semaphore } { }
27+
};
28+
29+
struct ops : ::vfs::ops
30+
{
31+
static std::shared_ptr<ops> singleton()
32+
{
33+
static auto instance = std::make_shared<ops>();
34+
return instance;
35+
}
36+
37+
bool seekable() const override { return false; }
38+
39+
lib::expect<void> open(std::shared_ptr<vfs::file> file, int flags, pid_t pid) override
40+
{
41+
lib::unused(file, flags, pid);
42+
return std::unexpected { lib::err::invalid_device_or_address };
43+
}
44+
45+
lib::expect<std::size_t> read(
46+
std::shared_ptr<vfs::file> file, std::uint64_t offset,
47+
lib::maybe_uspan<std::byte> buffer
48+
) override
49+
{
50+
lib::unused(offset);
51+
if (buffer.size() < sizeof(std::uint64_t))
52+
return std::unexpected { lib::err::invalid_argument };
53+
54+
const bool nonblock = file->flags & efd_nonblock;
55+
auto data = std::static_pointer_cast<data_t>(file->private_data);
56+
57+
std::uint64_t ret;
58+
{
59+
auto locked = data->counter.lock();
60+
again:
61+
if (*locked != 0)
62+
{
63+
if (!data->semaphore)
64+
{
65+
ret = *locked;
66+
*locked = 0;
67+
}
68+
else
69+
{
70+
ret = 1;
71+
(*locked)--;
72+
}
73+
}
74+
else
75+
{
76+
if (nonblock)
77+
return std::unexpected { lib::err::try_again };
78+
79+
while (true)
80+
{
81+
const auto gen = data->bell.snapshot_gen();
82+
locked.unlock();
83+
{
84+
const auto res = data->bell.wait_prepared(gen);
85+
if (res.interrupted || res.killed)
86+
return std::unexpected { lib::err::interrupted };
87+
}
88+
locked.lock();
89+
if (*locked != 0)
90+
goto again;
91+
}
92+
}
93+
}
94+
95+
if (!buffer.copy_from(std::as_bytes(std::span { &ret, 1 })))
96+
return std::unexpected { lib::err::invalid_address };
97+
98+
data->bell.wake_all();
99+
return sizeof(ret);
100+
}
101+
102+
lib::expect<std::size_t> write(
103+
std::shared_ptr<vfs::file> file, std::uint64_t offset,
104+
lib::maybe_uspan<std::byte> buffer
105+
) override
106+
{
107+
lib::unused(offset);
108+
109+
if (buffer.size() < sizeof(std::uint64_t))
110+
return std::unexpected { lib::err::invalid_argument };
111+
112+
std::uint64_t val;
113+
if (!buffer.copy_to(std::as_writable_bytes(std::span { &val, 1 })))
114+
return std::unexpected { lib::err::invalid_address };
115+
116+
if (val > ev_max)
117+
return std::unexpected { lib::err::invalid_argument };
118+
119+
const bool nonblock = file->flags & efd_nonblock;
120+
auto data = std::static_pointer_cast<data_t>(file->private_data);
121+
122+
while (true)
123+
{
124+
bool blocked = false;
125+
std::size_t gen = 0;
126+
{
127+
auto locked = data->counter.lock();
128+
if (val > ev_max - *locked)
129+
{
130+
gen = data->bell.snapshot_gen();
131+
blocked = true;
132+
}
133+
else *locked += val;
134+
}
135+
136+
if (!blocked)
137+
{
138+
data->bell.wake_all();
139+
return sizeof(val);
140+
}
141+
142+
if (nonblock)
143+
return std::unexpected { lib::err::try_again };
144+
145+
const auto res = data->bell.wait_prepared(gen);
146+
if (res.interrupted || res.killed)
147+
return std::unexpected { lib::err::interrupted };
148+
}
149+
}
150+
151+
lib::expect<void> trunc(std::shared_ptr<vfs::file> file, std::size_t size) override
152+
{
153+
lib::unused(file, size);
154+
return std::unexpected { lib::err::illegal_seek };
155+
}
156+
157+
lib::expect<std::uint16_t> poll(
158+
std::shared_ptr<vfs::file> file, vfs::poll_table *pt
159+
) override
160+
{
161+
auto data = std::static_pointer_cast<data_t>(file->private_data);
162+
if (pt)
163+
pt->add(data->bell);
164+
165+
auto locked = data->counter.lock();
166+
std::uint16_t mask = 0;
167+
168+
if (*locked > 0)
169+
mask |= pollin;
170+
if (*locked < ev_max)
171+
mask |= pollout;
172+
173+
return mask;
174+
}
175+
};
176+
} // namespace
177+
178+
int eventfd2(unsigned int count, int flags)
179+
{
180+
if (flags & ~(efd_semaphore | efd_cloexec | efd_nonblock))
181+
return -EINVAL;
182+
183+
auto ret = create_anon_fd({
184+
.name = "<[EVENTFD]>",
185+
.ops = ops::singleton(),
186+
.file_private_data = std::make_shared<data_t>(count, flags & efd_semaphore),
187+
.inode_private_data = nullptr,
188+
.st_mode = std::to_underlying(stat::s_ifreg) | s_irusr | s_iwusr,
189+
.flags = (flags & ~efd_semaphore) | o_rdwr,
190+
.skip_open = true,
191+
.inode = nullptr
192+
});
193+
if (!ret)
194+
return -lib::map_error(ret.error());
195+
196+
return ret->first;
197+
}
198+
199+
int eventfd(unsigned int count)
200+
{
201+
return eventfd2(count, 0);
202+
}
203+
} // namespace syscall::vfs

kernel/source/system/vfs/socket/socket.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ namespace vfs::socket
158158
.file_private_data = std::move(sock),
159159
.inode_private_data = nullptr,
160160
.st_mode = std::to_underlying(stat::s_ifsock) | s_irwxu | s_irwxg | s_irwxo,
161-
.flags = flags | o_rdonly,
161+
.flags = flags | o_rdwr,
162162
.skip_open = true,
163163
.inode = nullptr
164164
});

0 commit comments

Comments
 (0)