sys_fs: Optimize concurrent file reads

This commit is contained in:
Eladash 2023-10-02 18:23:21 +03:00 committed by Elad Ashkenazi
parent f07e17f6aa
commit 4b827a8d9c
3 changed files with 36 additions and 14 deletions

View file

@ -1854,8 +1854,9 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
} }
const auto file = std::as_const(all_files).find(file_path); const auto file = std::as_const(all_files).find(file_path);
const u64 pos = fileSet->fileOffset;
if (file == all_files.cend() || file->second.size() <= fileSet->fileOffset) if (file == all_files.cend() || file->second.size() <= pos)
{ {
cellSaveData.error("Failed to open file %s%s (size=%d, fileOffset=%d)", dir_path, file_path, file == all_files.cend() ? -1 : file->second.size(), fileSet->fileOffset); cellSaveData.error("Failed to open file %s%s (size=%d, fileOffset=%d)", dir_path, file_path, file == all_files.cend() ? -1 : file->second.size(), fileSet->fileOffset);
savedata_result = CELL_SAVEDATA_ERROR_FAILURE; savedata_result = CELL_SAVEDATA_ERROR_FAILURE;
@ -1863,8 +1864,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
} }
// Read from memory file to vm // Read from memory file to vm
file->second.seek(fileSet->fileOffset); const u64 rr = lv2_file::op_read(file->second, fileSet->fileBuf, fileSet->fileSize, pos);
const u64 rr = lv2_file::op_read(file->second, fileSet->fileBuf, fileSet->fileSize);
fileGet->excSize = ::narrow<u32>(rr); fileGet->excSize = ::narrow<u32>(rr);
break; break;
} }

View file

@ -16,6 +16,7 @@
#include <filesystem> #include <filesystem>
#include <span> #include <span>
#include <shared_mutex>
LOG_CHANNEL(sys_fs); LOG_CHANNEL(sys_fs);
@ -371,7 +372,7 @@ lv2_fs_object::lv2_fs_object(utils::serial& ar, bool)
{ {
} }
u64 lv2_file::op_read(const fs::file& file, vm::ptr<void> buf, u64 size) u64 lv2_file::op_read(const fs::file& file, vm::ptr<void> buf, u64 size, u64 opt_pos)
{ {
// Copy data from intermediate buffer (avoid passing vm pointer to a native API) // Copy data from intermediate buffer (avoid passing vm pointer to a native API)
std::vector<uchar> local_buf(std::min<u64>(size, 65536)); std::vector<uchar> local_buf(std::min<u64>(size, 65536));
@ -381,7 +382,7 @@ u64 lv2_file::op_read(const fs::file& file, vm::ptr<void> buf, u64 size)
while (result < size) while (result < size)
{ {
const u64 block = std::min<u64>(size - result, local_buf.size()); const u64 block = std::min<u64>(size - result, local_buf.size());
const u64 nread = file.read(+local_buf.data(), block); const u64 nread = (opt_pos == umax ? file.read(local_buf.data(), block) : file.read_at(opt_pos + result, local_buf.data(), block));
std::memcpy(static_cast<uchar*>(buf.get_ptr()) + result, local_buf.data(), nread); std::memcpy(static_cast<uchar*>(buf.get_ptr()) + result, local_buf.data(), nread);
result += nread; result += nread;
@ -1930,7 +1931,19 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
sys_fs.error("%s type: Writing %u bytes to FD=%d (path=%s)", file->type, arg->size, file->name.data()); sys_fs.error("%s type: Writing %u bytes to FD=%d (path=%s)", file->type, arg->size, file->name.data());
} }
std::lock_guard lock(file->mp->mutex); std::unique_lock wlock(file->mp->mutex, std::defer_lock);
std::shared_lock rlock(file->mp->mutex, std::defer_lock);
if (op == 0x8000000b)
{
// Writer lock
wlock.lock();
}
else
{
// Reader lock (not needing exclusivity in this special case because the state should not change)
rlock.lock();
}
if (!file->file) if (!file->file)
{ {
@ -1947,14 +1960,23 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
return CELL_EBUSY; return CELL_EBUSY;
} }
const u64 old_pos = file->file.pos(); u64 old_pos = umax;
file->file.seek(arg->offset); const u64 op_pos = arg->offset;
if (op == 0x8000000b)
{
old_pos = file->file.pos();
file->file.seek(op_pos);
}
arg->out_size = op == 0x8000000a arg->out_size = op == 0x8000000a
? file->op_read(arg->buf, arg->size) ? file->op_read(arg->buf, arg->size, op_pos)
: file->op_write(arg->buf, arg->size); : file->op_write(arg->buf, arg->size);
ensure(old_pos == file->file.seek(old_pos)); if (op == 0x8000000b)
{
ensure(old_pos == file->file.seek(old_pos));
}
// TODO: EDATA corruption detection // TODO: EDATA corruption detection

View file

@ -157,7 +157,7 @@ struct lv2_fs_mount_point
const bs_t<lv2_mp_flag> flags{}; const bs_t<lv2_mp_flag> flags{};
lv2_fs_mount_point* const next = nullptr; lv2_fs_mount_point* const next = nullptr;
mutable std::recursive_mutex mutex; mutable shared_mutex mutex;
}; };
extern lv2_fs_mount_point g_mp_sys_dev_hdd0; extern lv2_fs_mount_point g_mp_sys_dev_hdd0;
@ -340,11 +340,11 @@ struct lv2_file final : lv2_fs_object
static open_result_t open(std::string_view vpath, s32 flags, s32 mode, const void* arg = {}, u64 size = 0); static open_result_t open(std::string_view vpath, s32 flags, s32 mode, const void* arg = {}, u64 size = 0);
// File reading with intermediate buffer // File reading with intermediate buffer
static u64 op_read(const fs::file& file, vm::ptr<void> buf, u64 size); static u64 op_read(const fs::file& file, vm::ptr<void> buf, u64 size, u64 opt_pos = umax);
u64 op_read(vm::ptr<void> buf, u64 size) const u64 op_read(vm::ptr<void> buf, u64 size, u64 opt_pos = umax) const
{ {
return op_read(file, buf, size); return op_read(file, buf, size, opt_pos);
} }
// File writing with intermediate buffer // File writing with intermediate buffer