Skip to content

Commit 248832f

Browse files
committed
Kernel: Convert OpenFileDescriptor from mutex to spinlock
A mutex is useful when we need to be able to block the current thread until it's available. This is overkill for OpenFileDescriptor. First off, this patch wraps the main state member variables inside a SpinlockProtected<State> to enforce synchronized access. This also avoids "free locking" where figuring out which variables are guarded by which lock is left as an unamusing exercise for the reader. Then we remove mutex locking from the functions that simply call through to the underlying File or Inode, since those fields never change anyway, and the target objects perform their own synchronization.
1 parent 35e24bc commit 248832f

File tree

2 files changed

+163
-91
lines changed

2 files changed

+163
-91
lines changed

Kernel/FileSystem/OpenFileDescription.cpp

Lines changed: 132 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
/*
2-
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
2+
* Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
33
* Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
44
*
55
* SPDX-License-Identifier: BSD-2-Clause
66
*/
77

88
#include <AK/MemoryStream.h>
99
#include <Kernel/API/POSIX/errno.h>
10-
#include <Kernel/Debug.h>
1110
#include <Kernel/Devices/BlockDevice.h>
1211
#include <Kernel/FileSystem/Custody.h>
1312
#include <Kernel/FileSystem/FIFO.h>
14-
#include <Kernel/FileSystem/FileSystem.h>
1513
#include <Kernel/FileSystem/InodeFile.h>
1614
#include <Kernel/FileSystem/InodeWatcher.h>
1715
#include <Kernel/FileSystem/OpenFileDescription.h>
@@ -47,14 +45,15 @@ OpenFileDescription::OpenFileDescription(File& file)
4745
if (file.is_inode())
4846
m_inode = static_cast<InodeFile&>(file).inode();
4947

50-
m_is_directory = metadata().is_directory();
48+
auto metadata = this->metadata();
49+
m_state.with([&](auto& state) { state.is_directory = metadata.is_directory(); });
5150
}
5251

5352
OpenFileDescription::~OpenFileDescription()
5453
{
5554
m_file->detach(*this);
5655
if (is_fifo())
57-
static_cast<FIFO*>(m_file.ptr())->detach(m_fifo_direction);
56+
static_cast<FIFO*>(m_file.ptr())->detach(fifo_direction());
5857
// FIXME: Should this error path be observed somehow?
5958
(void)m_file->close();
6059
if (m_inode)
@@ -99,7 +98,6 @@ Thread::FileBlocker::BlockFlags OpenFileDescription::should_unblock(Thread::File
9998

10099
ErrorOr<struct stat> OpenFileDescription::stat()
101100
{
102-
MutexLocker locker(m_lock);
103101
// FIXME: This is due to the Device class not overriding File::stat().
104102
if (m_inode)
105103
return m_inode->metadata().stat();
@@ -108,43 +106,45 @@ ErrorOr<struct stat> OpenFileDescription::stat()
108106

109107
ErrorOr<off_t> OpenFileDescription::seek(off_t offset, int whence)
110108
{
111-
MutexLocker locker(m_lock);
112109
if (!m_file->is_seekable())
113110
return ESPIPE;
114111

115-
off_t new_offset;
112+
auto metadata = this->metadata();
116113

117-
switch (whence) {
118-
case SEEK_SET:
119-
new_offset = offset;
120-
break;
121-
case SEEK_CUR:
122-
if (Checked<off_t>::addition_would_overflow(m_current_offset, offset))
123-
return EOVERFLOW;
124-
new_offset = m_current_offset + offset;
125-
break;
126-
case SEEK_END:
127-
if (!metadata().is_valid())
128-
return EIO;
129-
if (Checked<off_t>::addition_would_overflow(metadata().size, offset))
130-
return EOVERFLOW;
131-
new_offset = metadata().size + offset;
132-
break;
133-
default:
134-
return EINVAL;
135-
}
114+
auto new_offset = TRY(m_state.with([&](auto& state) -> ErrorOr<off_t> {
115+
off_t new_offset;
116+
switch (whence) {
117+
case SEEK_SET:
118+
new_offset = offset;
119+
break;
120+
case SEEK_CUR:
121+
if (Checked<off_t>::addition_would_overflow(state.current_offset, offset))
122+
return EOVERFLOW;
123+
new_offset = state.current_offset + offset;
124+
break;
125+
case SEEK_END:
126+
if (!metadata.is_valid())
127+
return EIO;
128+
if (Checked<off_t>::addition_would_overflow(metadata.size, offset))
129+
return EOVERFLOW;
130+
new_offset = metadata.size + offset;
131+
break;
132+
default:
133+
return EINVAL;
134+
}
135+
if (new_offset < 0)
136+
return EINVAL;
137+
state.current_offset = new_offset;
138+
return new_offset;
139+
}));
136140

137-
if (new_offset < 0)
138-
return EINVAL;
139141
// FIXME: Return EINVAL if attempting to seek past the end of a seekable device.
140142

141-
m_current_offset = new_offset;
142-
143143
m_file->did_seek(*this, new_offset);
144144
if (m_inode)
145145
m_inode->did_seek(*this, new_offset);
146146
evaluate_block_conditions();
147-
return m_current_offset;
147+
return new_offset;
148148
}
149149

150150
ErrorOr<size_t> OpenFileDescription::read(UserOrKernelBuffer& buffer, u64 offset, size_t count)
@@ -163,24 +163,30 @@ ErrorOr<size_t> OpenFileDescription::write(u64 offset, UserOrKernelBuffer const&
163163

164164
ErrorOr<size_t> OpenFileDescription::read(UserOrKernelBuffer& buffer, size_t count)
165165
{
166-
MutexLocker locker(m_lock);
167-
if (Checked<off_t>::addition_would_overflow(m_current_offset, count))
168-
return EOVERFLOW;
169-
auto nread = TRY(m_file->read(*this, offset(), buffer, count));
166+
auto offset = TRY(m_state.with([&](auto& state) -> ErrorOr<off_t> {
167+
if (Checked<off_t>::addition_would_overflow(state.current_offset, count))
168+
return EOVERFLOW;
169+
return state.current_offset;
170+
}));
171+
auto nread = TRY(m_file->read(*this, offset, buffer, count));
170172
if (m_file->is_seekable())
171-
m_current_offset += nread;
173+
m_state.with([&](auto& state) { state.current_offset = offset + nread; });
172174
evaluate_block_conditions();
173175
return nread;
174176
}
175177

176178
ErrorOr<size_t> OpenFileDescription::write(const UserOrKernelBuffer& data, size_t size)
177179
{
178-
MutexLocker locker(m_lock);
179-
if (Checked<off_t>::addition_would_overflow(m_current_offset, size))
180-
return EOVERFLOW;
181-
auto nwritten = TRY(m_file->write(*this, offset(), data, size));
180+
auto offset = TRY(m_state.with([&](auto& state) -> ErrorOr<off_t> {
181+
if (Checked<off_t>::addition_would_overflow(state.current_offset, size))
182+
return EOVERFLOW;
183+
return state.current_offset;
184+
}));
185+
auto nwritten = TRY(m_file->write(*this, offset, data, size));
186+
182187
if (m_file->is_seekable())
183-
m_current_offset += nwritten;
188+
m_state.with([&](auto& state) { state.current_offset = offset + nwritten; });
189+
184190
evaluate_block_conditions();
185191
return nwritten;
186192
}
@@ -205,7 +211,6 @@ ErrorOr<NonnullOwnPtr<KBuffer>> OpenFileDescription::read_entire_file()
205211

206212
ErrorOr<size_t> OpenFileDescription::get_dir_entries(UserOrKernelBuffer& output_buffer, size_t size)
207213
{
208-
MutexLocker locker(m_lock, Mutex::Mode::Shared);
209214
if (!is_directory())
210215
return ENOTDIR;
211216

@@ -371,19 +376,16 @@ InodeMetadata OpenFileDescription::metadata() const
371376

372377
ErrorOr<Memory::Region*> OpenFileDescription::mmap(Process& process, Memory::VirtualRange const& range, u64 offset, int prot, bool shared)
373378
{
374-
MutexLocker locker(m_lock);
375379
return m_file->mmap(process, *this, range, offset, prot, shared);
376380
}
377381

378382
ErrorOr<void> OpenFileDescription::truncate(u64 length)
379383
{
380-
MutexLocker locker(m_lock);
381384
return m_file->truncate(length);
382385
}
383386

384387
ErrorOr<void> OpenFileDescription::sync()
385388
{
386-
MutexLocker locker(m_lock);
387389
return m_file->sync();
388390
}
389391

@@ -420,22 +422,21 @@ const Socket* OpenFileDescription::socket() const
420422

421423
void OpenFileDescription::set_file_flags(u32 flags)
422424
{
423-
MutexLocker locker(m_lock);
424-
m_is_blocking = !(flags & O_NONBLOCK);
425-
m_should_append = flags & O_APPEND;
426-
m_direct = flags & O_DIRECT;
427-
m_file_flags = flags;
425+
m_state.with([&](auto& state) {
426+
state.is_blocking = !(flags & O_NONBLOCK);
427+
state.should_append = flags & O_APPEND;
428+
state.direct = flags & O_DIRECT;
429+
state.file_flags = flags;
430+
});
428431
}
429432

430433
ErrorOr<void> OpenFileDescription::chmod(mode_t mode)
431434
{
432-
MutexLocker locker(m_lock);
433435
return m_file->chmod(*this, mode);
434436
}
435437

436438
ErrorOr<void> OpenFileDescription::chown(UserID uid, GroupID gid)
437439
{
438-
MutexLocker locker(m_lock);
439440
return m_file->chown(*this, uid, gid);
440441
}
441442

@@ -459,4 +460,82 @@ ErrorOr<void> OpenFileDescription::get_flock(Userspace<flock*> lock) const
459460

460461
return m_inode->get_flock(*this, lock);
461462
}
463+
bool OpenFileDescription::is_readable() const
464+
{
465+
return m_state.with([](auto& state) { return state.readable; });
466+
}
467+
468+
bool OpenFileDescription::is_writable() const
469+
{
470+
return m_state.with([](auto& state) { return state.writable; });
471+
}
472+
473+
void OpenFileDescription::set_readable(bool b)
474+
{
475+
m_state.with([&](auto& state) { state.readable = b; });
476+
}
477+
478+
void OpenFileDescription::set_writable(bool b)
479+
{
480+
m_state.with([&](auto& state) { state.writable = b; });
481+
}
482+
483+
void OpenFileDescription::set_rw_mode(int options)
484+
{
485+
m_state.with([&](auto& state) {
486+
state.readable = (options & O_RDONLY) == O_RDONLY;
487+
state.writable = (options & O_WRONLY) == O_WRONLY;
488+
});
489+
}
490+
491+
bool OpenFileDescription::is_direct() const
492+
{
493+
return m_state.with([](auto& state) { return state.direct; });
494+
}
495+
496+
bool OpenFileDescription::is_directory() const
497+
{
498+
return m_state.with([](auto& state) { return state.is_directory; });
499+
}
500+
501+
bool OpenFileDescription::is_blocking() const
502+
{
503+
return m_state.with([](auto& state) { return state.is_blocking; });
504+
}
505+
506+
void OpenFileDescription::set_blocking(bool b)
507+
{
508+
m_state.with([&](auto& state) { state.is_blocking = b; });
509+
}
510+
511+
bool OpenFileDescription::should_append() const
512+
{
513+
return m_state.with([](auto& state) { return state.should_append; });
514+
}
515+
516+
u32 OpenFileDescription::file_flags() const
517+
{
518+
return m_state.with([](auto& state) { return state.file_flags; });
519+
}
520+
521+
FIFO::Direction OpenFileDescription::fifo_direction() const
522+
{
523+
return m_state.with([](auto& state) { return state.fifo_direction; });
524+
}
525+
526+
void OpenFileDescription::set_fifo_direction(Badge<FIFO>, FIFO::Direction direction)
527+
{
528+
m_state.with([&](auto& state) { state.fifo_direction = direction; });
529+
}
530+
531+
OwnPtr<OpenFileDescriptionData>& OpenFileDescription::data()
532+
{
533+
return m_state.with([](auto& state) -> OwnPtr<OpenFileDescriptionData>& { return state.data; });
534+
}
535+
536+
off_t OpenFileDescription::offset() const
537+
{
538+
return m_state.with([](auto& state) { return state.current_offset; });
539+
}
540+
462541
}

0 commit comments

Comments
 (0)