Remove vfs dependency on kernel, implement I/O completion ports
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
#include "xenia/kernel/util/shim_utils.h"
|
||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
||||
#include "xenia/kernel/xevent.h"
|
||||
#include "xenia/kernel/xiocompletion.h"
|
||||
#include "xenia/kernel/xfile.h"
|
||||
#include "xenia/kernel/xthread.h"
|
||||
#include "xenia/vfs/device.h"
|
||||
@@ -120,15 +121,17 @@ dword_result_t NtCreateFile(lpdword_t handle_out, dword_t desired_access,
|
||||
}
|
||||
|
||||
// Attempt open (or create).
|
||||
object_ref<XFile> file;
|
||||
xe::vfs::FileAction file_action;
|
||||
vfs::File* vfs_file;
|
||||
vfs::FileAction file_action;
|
||||
X_STATUS result = kernel_state()->file_system()->OpenFile(
|
||||
kernel_state(), target_path,
|
||||
xe::vfs::FileDisposition((uint32_t)creation_disposition), desired_access,
|
||||
&file, &file_action);
|
||||
target_path, vfs::FileDisposition((uint32_t)creation_disposition),
|
||||
desired_access, &vfs_file, &file_action);
|
||||
object_ref<XFile> file = nullptr;
|
||||
|
||||
X_HANDLE handle = X_INVALID_HANDLE_VALUE;
|
||||
if (XSUCCEEDED(result)) {
|
||||
file = object_ref<XFile>(new XFile(kernel_state(), vfs_file));
|
||||
|
||||
// Handle ref is incremented, so return that.
|
||||
handle = file->handle();
|
||||
}
|
||||
@@ -178,7 +181,8 @@ dword_result_t NtReadFile(dword_t file_handle, dword_t event_handle,
|
||||
// Synchronous.
|
||||
size_t bytes_read = 0;
|
||||
result = file->Read(buffer, buffer_length,
|
||||
byte_offset_ptr ? *byte_offset_ptr : -1, &bytes_read);
|
||||
byte_offset_ptr ? *byte_offset_ptr : -1, &bytes_read,
|
||||
apc_context);
|
||||
if (io_status_block) {
|
||||
io_status_block->status = result;
|
||||
io_status_block->information = (uint32_t)bytes_read;
|
||||
@@ -261,9 +265,9 @@ dword_result_t NtWriteFile(dword_t file_handle, dword_t event_handle,
|
||||
if (true) {
|
||||
// Synchronous request.
|
||||
size_t bytes_written = 0;
|
||||
result =
|
||||
file->Write(buffer, buffer_length,
|
||||
byte_offset_ptr ? *byte_offset_ptr : -1, &bytes_written);
|
||||
result = file->Write(buffer, buffer_length,
|
||||
byte_offset_ptr ? *byte_offset_ptr : -1,
|
||||
&bytes_written, apc_context);
|
||||
if (XSUCCEEDED(result)) {
|
||||
info = (int32_t)bytes_written;
|
||||
}
|
||||
@@ -291,13 +295,54 @@ dword_result_t NtWriteFile(dword_t file_handle, dword_t event_handle,
|
||||
}
|
||||
DECLARE_XBOXKRNL_EXPORT(NtWriteFile, ExportTag::kImplemented);
|
||||
|
||||
dword_result_t NtCreateIoCompletion(lpvoid_t out_handle, dword_t desired_access,
|
||||
dword_result_t NtCreateIoCompletion(lpdword_t out_handle,
|
||||
dword_t desired_access,
|
||||
lpvoid_t object_attribs,
|
||||
dword_t num_concurrent_threads) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
auto completion = new XIOCompletion(kernel_state());
|
||||
if (out_handle) {
|
||||
*out_handle = completion->handle();
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
DECLARE_XBOXKRNL_EXPORT(NtCreateIoCompletion, ExportTag::kStub);
|
||||
|
||||
// Dequeues a packet from the completion port.
|
||||
dword_result_t NtRemoveIoCompletion(
|
||||
dword_t handle, lpdword_t key_context, lpdword_t apc_context,
|
||||
pointer_t<X_IO_STATUS_BLOCK> io_status_block, lpqword_t timeout) {
|
||||
X_STATUS status = X_STATUS_SUCCESS;
|
||||
uint32_t info = 0;
|
||||
|
||||
auto port =
|
||||
kernel_state()->object_table()->LookupObject<XIOCompletion>(handle);
|
||||
if (!port) {
|
||||
status = X_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
uint64_t timeout_ticks = timeout ? *timeout : 0;
|
||||
if (port->WaitForNotification(timeout_ticks)) {
|
||||
auto notification = port->DequeueNotification();
|
||||
if (key_context) {
|
||||
*key_context = notification.key_context;
|
||||
}
|
||||
if (apc_context) {
|
||||
*apc_context = notification.apc_context;
|
||||
}
|
||||
|
||||
if (io_status_block) {
|
||||
io_status_block->status = notification.status;
|
||||
io_status_block->information = notification.num_bytes;
|
||||
}
|
||||
} else {
|
||||
status = X_STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
DECLARE_XBOXKRNL_EXPORT(NtRemoveIoCompletion, ExportTag::kStub);
|
||||
|
||||
dword_result_t NtSetInformationFile(
|
||||
dword_t file_handle, pointer_t<X_IO_STATUS_BLOCK> io_status_block,
|
||||
lpvoid_t file_info, dword_t length, dword_t file_info_class) {
|
||||
@@ -335,9 +380,21 @@ dword_result_t NtSetInformationFile(
|
||||
info = 8;
|
||||
XELOGW("NtSetInformationFile ignoring alloc/eof");
|
||||
break;
|
||||
case XFileCompletionInformation:
|
||||
// Games appear to call NtCreateIoCompletion right before this
|
||||
case XFileCompletionInformation: {
|
||||
// Info contains IO Completion handle and completion key
|
||||
assert_true(length == 8);
|
||||
|
||||
auto handle = xe::load_and_swap<uint32_t>(file_info + 0x0);
|
||||
auto key = xe::load_and_swap<uint32_t>(file_info + 0x4);
|
||||
auto port =
|
||||
kernel_state()->object_table()->LookupObject<XIOCompletion>(handle);
|
||||
if (!port) {
|
||||
return X_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
file->RegisterIOCompletionPort(key, port);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Unsupported, for now.
|
||||
assert_always();
|
||||
|
||||
@@ -15,10 +15,8 @@
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
XFile::XFile(KernelState* kernel_state, uint32_t file_access, vfs::Entry* entry)
|
||||
: XObject(kernel_state, kTypeFile),
|
||||
entry_(entry),
|
||||
file_access_(file_access) {
|
||||
XFile::XFile(KernelState* kernel_state, vfs::File* file)
|
||||
: XObject(kernel_state, kTypeFile), file_(file) {
|
||||
async_event_ = new XEvent(kernel_state);
|
||||
async_event_->Initialize(false, false);
|
||||
}
|
||||
@@ -27,6 +25,8 @@ XFile::~XFile() {
|
||||
// TODO(benvanik): signal that the file is closing?
|
||||
async_event_->Set(0, false);
|
||||
async_event_->Delete();
|
||||
|
||||
file_->Destroy();
|
||||
}
|
||||
|
||||
X_STATUS XFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info,
|
||||
@@ -44,7 +44,7 @@ X_STATUS XFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info,
|
||||
|
||||
// Always restart the search?
|
||||
find_index_ = 0;
|
||||
entry = entry_->IterateChildren(find_engine_, &find_index_);
|
||||
entry = file_->entry()->IterateChildren(find_engine_, &find_index_);
|
||||
if (!entry) {
|
||||
return X_STATUS_NO_SUCH_FILE;
|
||||
}
|
||||
@@ -53,7 +53,7 @@ X_STATUS XFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info,
|
||||
find_index_ = 0;
|
||||
}
|
||||
|
||||
entry = entry_->IterateChildren(find_engine_, &find_index_);
|
||||
entry = file_->entry()->IterateChildren(find_engine_, &find_index_);
|
||||
if (!entry) {
|
||||
return X_STATUS_NO_SUCH_FILE;
|
||||
}
|
||||
@@ -83,34 +83,92 @@ X_STATUS XFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info,
|
||||
}
|
||||
|
||||
X_STATUS XFile::Read(void* buffer, size_t buffer_length, size_t byte_offset,
|
||||
size_t* out_bytes_read) {
|
||||
size_t* out_bytes_read, uint32_t apc_context) {
|
||||
if (byte_offset == -1) {
|
||||
// Read from current position.
|
||||
byte_offset = position_;
|
||||
}
|
||||
|
||||
size_t bytes_read = 0;
|
||||
X_STATUS result =
|
||||
ReadSync(buffer, buffer_length, byte_offset, out_bytes_read);
|
||||
file_->ReadSync(buffer, buffer_length, byte_offset, &bytes_read);
|
||||
if (XSUCCEEDED(result)) {
|
||||
position_ += *out_bytes_read;
|
||||
position_ += bytes_read;
|
||||
}
|
||||
|
||||
XIOCompletion::IONotification notify;
|
||||
notify.apc_context = apc_context;
|
||||
notify.num_bytes = uint32_t(bytes_read);
|
||||
notify.status = result;
|
||||
|
||||
NotifyIOCompletionPorts(notify);
|
||||
|
||||
if (out_bytes_read) {
|
||||
*out_bytes_read = bytes_read;
|
||||
}
|
||||
|
||||
async_event_->Set(0, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
X_STATUS XFile::Write(const void* buffer, size_t buffer_length,
|
||||
size_t byte_offset, size_t* out_bytes_written) {
|
||||
size_t byte_offset, size_t* out_bytes_written,
|
||||
uint32_t apc_context) {
|
||||
if (byte_offset == -1) {
|
||||
// Write from current position.
|
||||
byte_offset = position_;
|
||||
}
|
||||
|
||||
size_t bytes_written = 0;
|
||||
X_STATUS result =
|
||||
WriteSync(buffer, buffer_length, byte_offset, out_bytes_written);
|
||||
file_->WriteSync(buffer, buffer_length, byte_offset, &bytes_written);
|
||||
if (XSUCCEEDED(result)) {
|
||||
position_ += *out_bytes_written;
|
||||
position_ += bytes_written;
|
||||
}
|
||||
|
||||
XIOCompletion::IONotification notify;
|
||||
notify.apc_context = apc_context;
|
||||
notify.num_bytes = uint32_t(bytes_written);
|
||||
notify.status = result;
|
||||
|
||||
NotifyIOCompletionPorts(notify);
|
||||
|
||||
if (out_bytes_written) {
|
||||
*out_bytes_written = bytes_written;
|
||||
}
|
||||
|
||||
async_event_->Set(0, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
void XFile::RegisterIOCompletionPort(uint32_t key,
|
||||
object_ref<XIOCompletion> port) {
|
||||
std::lock_guard<std::mutex> lock(completion_port_lock_);
|
||||
|
||||
completion_ports_.push_back({key, port});
|
||||
}
|
||||
|
||||
void XFile::RemoveIOCompletionPort(uint32_t key) {
|
||||
std::lock_guard<std::mutex> lock(completion_port_lock_);
|
||||
|
||||
for (auto it = completion_ports_.begin(); it != completion_ports_.end();
|
||||
it++) {
|
||||
if (it->first == key) {
|
||||
completion_ports_.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XFile::NotifyIOCompletionPorts(
|
||||
XIOCompletion::IONotification& notification) {
|
||||
std::lock_guard<std::mutex> lock(completion_port_lock_);
|
||||
|
||||
for (auto port : completion_ports_) {
|
||||
notification.key_context = port.first;
|
||||
port.second->QueueNotification(notification);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
|
||||
#include "xenia/base/filesystem.h"
|
||||
#include "xenia/kernel/xevent.h"
|
||||
#include "xenia/kernel/xiocompletion.h"
|
||||
#include "xenia/kernel/xobject.h"
|
||||
#include "xenia/vfs/device.h"
|
||||
#include "xenia/vfs/entry.h"
|
||||
#include "xenia/vfs/file.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
namespace xe {
|
||||
@@ -79,14 +81,16 @@ class XFile : public XObject {
|
||||
public:
|
||||
static const Type kType = kTypeFile;
|
||||
|
||||
XFile(KernelState* kernel_state, vfs::File* file);
|
||||
~XFile() override;
|
||||
|
||||
vfs::Device* device() const { return entry_->device(); }
|
||||
vfs::Entry* entry() const { return entry_; }
|
||||
vfs::Device* device() const { return file_->entry()->device(); }
|
||||
vfs::Entry* entry() const { return file_->entry(); }
|
||||
vfs::File* file() const { return file_; }
|
||||
uint32_t file_access() const { return file_access_; }
|
||||
|
||||
const std::string& path() const { return entry_->path(); }
|
||||
const std::string& name() const { return entry_->name(); }
|
||||
const std::string& path() const { return file_->entry()->path(); }
|
||||
const std::string& name() const { return file_->entry()->name(); }
|
||||
|
||||
size_t position() const { return position_; }
|
||||
void set_position(size_t value) { position_ = value; }
|
||||
@@ -95,29 +99,29 @@ class XFile : public XObject {
|
||||
const char* file_name, bool restart);
|
||||
|
||||
X_STATUS Read(void* buffer, size_t buffer_length, size_t byte_offset,
|
||||
size_t* out_bytes_read);
|
||||
size_t* out_bytes_read, uint32_t apc_context);
|
||||
|
||||
X_STATUS Write(const void* buffer, size_t buffer_length, size_t byte_offset,
|
||||
size_t* out_bytes_written);
|
||||
size_t* out_bytes_written, uint32_t apc_context);
|
||||
|
||||
xe::threading::WaitHandle* GetWaitHandle() override {
|
||||
return async_event_->GetWaitHandle();
|
||||
}
|
||||
|
||||
void RegisterIOCompletionPort(uint32_t key, object_ref<XIOCompletion> port);
|
||||
void RemoveIOCompletionPort(uint32_t key);
|
||||
|
||||
protected:
|
||||
XFile(KernelState* kernel_state, uint32_t file_access, vfs::Entry* entry);
|
||||
virtual X_STATUS ReadSync(void* buffer, size_t buffer_length,
|
||||
size_t byte_offset, size_t* out_bytes_read) = 0;
|
||||
virtual X_STATUS WriteSync(const void* buffer, size_t buffer_length,
|
||||
size_t byte_offset, size_t* out_bytes_written) {
|
||||
return X_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
void NotifyIOCompletionPorts(XIOCompletion::IONotification& notification);
|
||||
|
||||
private:
|
||||
vfs::Entry* entry_ = nullptr;
|
||||
vfs::File* file_ = nullptr;
|
||||
uint32_t file_access_ = 0;
|
||||
XEvent* async_event_ = nullptr;
|
||||
|
||||
std::mutex completion_port_lock_;
|
||||
std::vector<std::pair<uint32_t, object_ref<XIOCompletion>>> completion_ports_;
|
||||
|
||||
// TODO(benvanik): create flags, open state, etc.
|
||||
|
||||
size_t position_ = 0;
|
||||
|
||||
50
src/xenia/kernel/xiocompletion.cc
Normal file
50
src/xenia/kernel/xiocompletion.cc
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/kernel/xiocompletion.h"
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
XIOCompletion::XIOCompletion(KernelState* kernel_state)
|
||||
: XObject(kernel_state, kTypeIOCompletion) {
|
||||
notification_semaphore_ = threading::Semaphore::Create(0, kMaxNotifications);
|
||||
}
|
||||
|
||||
XIOCompletion::~XIOCompletion() = default;
|
||||
|
||||
void XIOCompletion::QueueNotification(IONotification& notification) {
|
||||
std::unique_lock<std::mutex> lock(notification_lock_);
|
||||
|
||||
notifications_.push(notification);
|
||||
notification_semaphore_->Release(1, nullptr);
|
||||
}
|
||||
|
||||
XIOCompletion::IONotification XIOCompletion::DequeueNotification() {
|
||||
std::unique_lock<std::mutex> lock(notification_lock_);
|
||||
assert_false(notifications_.empty());
|
||||
|
||||
auto notification = notifications_.front();
|
||||
notifications_.pop();
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
bool XIOCompletion::WaitForNotification(uint64_t wait_ticks) {
|
||||
auto ms = std::chrono::milliseconds(TimeoutTicksToMs(wait_ticks));
|
||||
auto res = threading::Wait(notification_semaphore_.get(), false, ms);
|
||||
if (res == threading::WaitResult::kSuccess) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
54
src/xenia/kernel/xiocompletion.h
Normal file
54
src/xenia/kernel/xiocompletion.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_KERNEL_XIOCOMPLETION_H_
|
||||
#define XENIA_KERNEL_XIOCOMPLETION_H_
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/kernel/xobject.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
class XIOCompletion : public XObject {
|
||||
public:
|
||||
static const Type kType = kTypeIOCompletion;
|
||||
|
||||
explicit XIOCompletion(KernelState* kernel_state);
|
||||
~XIOCompletion() override;
|
||||
|
||||
struct IONotification {
|
||||
uint32_t key_context;
|
||||
uint32_t apc_context;
|
||||
uint32_t status;
|
||||
uint32_t num_bytes;
|
||||
};
|
||||
|
||||
void QueueNotification(IONotification& notification);
|
||||
IONotification DequeueNotification();
|
||||
|
||||
// If you call this and it returns true, you MUST dequeue a notification!
|
||||
// Returns true if the wait was cancelled because a notification was queued.
|
||||
bool WaitForNotification(uint64_t wait_ticks);
|
||||
|
||||
private:
|
||||
static const uint32_t kMaxNotifications = 1024;
|
||||
|
||||
std::mutex notification_lock_;
|
||||
std::queue<IONotification> notifications_;
|
||||
std::unique_ptr<threading::Semaphore> notification_semaphore_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_KERNEL_XIOCOMPLETION_H_
|
||||
Reference in New Issue
Block a user