blob: ada8027c5870548bd97c4e6d90ad86eda6f8efed [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "driver_host_context.h"
#include <stdio.h>
#include <zircon/status.h>
#include <fbl/auto_lock.h>
#include <fs/vfs.h>
#include "composite_device.h"
#include "src/devices/lib/log/log.h"
DriverHostContext::~DriverHostContext() {
while (!dead_devices_.is_empty()) {
delete dead_devices_.pop_front();
}
}
void DriverHostContext::PushWorkItem(const fbl::RefPtr<zx_device_t>& dev, Callback callback) {
auto work_item = std::make_unique<WorkItem>(dev, std::move(callback));
fbl::AutoLock al(&lock_);
work_items_.push_back(std::move(work_item));
// TODO(surajmalhotra): Only signal if not being run in main driver_host thread as a slight
// optimization (assuming we will run work items before going back to waiting on the port).
if (!event_waiter_->signaled()) {
event_waiter_->signal();
}
}
void DriverHostContext::InternalRunWorkItems(size_t how_many_to_run) {
{
fbl::AutoLock al(&lock_);
if (event_waiter_->signaled()) {
event_waiter_->designal();
}
}
size_t work_items_run = 0;
auto keep_running = [&]() { return work_items_run < how_many_to_run || how_many_to_run == 0; };
do {
fbl::DoublyLinkedList<std::unique_ptr<WorkItem>> work_items;
{
fbl::AutoLock al(&lock_);
work_items = std::move(work_items_);
}
if (work_items.is_empty()) {
return;
}
std::unique_ptr<WorkItem> work_item;
while (keep_running() && (work_item = work_items.pop_front())) {
work_item->callback();
work_items_run++;
}
if (!work_items.is_empty()) {
fbl::AutoLock al(&lock_);
work_items_.splice(work_items_.begin(), work_items);
}
} while (keep_running());
fbl::AutoLock al(&lock_);
if (!work_items_.is_empty() && !event_waiter_->signaled()) {
event_waiter_->signal();
}
}
void DriverHostContext::RunWorkItems(size_t how_many_to_run) {
std::unique_ptr<EventWaiter> event_waiter;
{
fbl::AutoLock al(&lock_);
ZX_DEBUG_ASSERT(event_waiter_ != nullptr);
if (work_items_.is_empty()) {
return;
}
event_waiter = event_waiter_->Cancel();
}
InternalRunWorkItems(how_many_to_run);
EventWaiter::BeginWait(std::move(event_waiter), loop_.dispatcher());
}
zx_status_t DriverHostContext::SetupEventWaiter() {
zx::event event;
if (zx_status_t status = zx::event::create(0, &event); status != ZX_OK) {
return status;
}
// TODO(surajmalhotra): Tune this value.
constexpr uint32_t kBatchSize = 5;
auto event_waiter = std::make_unique<EventWaiter>(std::move(event),
[this]() { InternalRunWorkItems(kBatchSize); });
{
fbl::AutoLock al(&lock_);
event_waiter_ = event_waiter.get();
}
return EventWaiter::BeginWait(std::move(event_waiter), loop_.dispatcher());
}
void DriverHostContext::EventWaiter::HandleEvent(std::unique_ptr<EventWaiter> event_waiter,
async_dispatcher_t* dispatcher,
async::WaitBase* wait, zx_status_t status,
const zx_packet_signal_t* signal) {
if (status != ZX_OK) {
LOGF(ERROR, "Failed to wait for event: %s", zx_status_get_string(status));
return;
}
if (signal->observed & ZX_USER_SIGNAL_0) {
event_waiter->InvokeCallback();
BeginWait(std::move(event_waiter), dispatcher);
} else {
LOGF(FATAL, "Unexpected signal state %#08x", signal->observed);
}
}
zx_status_t DriverHostContext::DeviceConnect(const fbl::RefPtr<zx_device_t>& dev, uint32_t flags,
zx::channel c) {
auto options = fs::VnodeConnectionOptions::FromIoV1Flags(flags);
fbl::RefPtr<fs::Vnode> target;
if (!options.flags.node_reference) {
zx_status_t status = dev->vnode->OpenValidating(options, &target);
if (status != ZX_OK) {
return status;
}
}
if (target == nullptr) {
target = dev->vnode;
}
return vfs_.Serve(std::move(target), std::move(c), options);
}