// 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 "proxy-iostate.h" #include <fbl/auto_lock.h> #include "../shared/log.h" #include "connection-destroyer.h" #include "zx-device.h" namespace devmgr { ProxyIostate::~ProxyIostate() { fbl::AutoLock guard(&dev->proxy_ios_lock); ZX_ASSERT(dev->proxy_ios != this); } // Handling RPC From Proxy Devices to BusDevs void ProxyIostate::HandleRpc(fbl::unique_ptr<ProxyIostate> conn, async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status, const zx_packet_signal_t* signal) { auto handle_destroy = [&conn]() { fbl::AutoLock guard(&conn->dev->proxy_ios_lock); // If proxy_ios is not |conn|, then it's had a packet queued already to // destroy it, so we should let the queued destruction handle things. // Otherwise we should destroy it. if (conn->dev->proxy_ios == conn.get()) { // Mark proxy_ios as disconnected, so that CancelLocked doesn't try to // destroy it too conn->dev->proxy_ios = nullptr; // The actual destruction will happen when |conn| goes out of scope. } else { __UNUSED auto ptr = conn.release(); } }; if (status != ZX_OK) { return handle_destroy(); } if (conn->dev == nullptr) { log(RPC_SDW, "proxy-rpc: stale rpc? (ios=%p)\n", conn.get()); // Do not re-issue the wait here return handle_destroy(); } if (signal->observed & ZX_CHANNEL_READABLE) { log(RPC_SDW, "proxy-rpc: rpc readable (ios=%p,dev=%p)\n", conn.get(), conn->dev.get()); zx_status_t r = conn->dev->ops->rxrpc(conn->dev->ctx, wait->object()); if (r != ZX_OK) { log(RPC_SDW, "proxy-rpc: rpc cb error %d (ios=%p,dev=%p)\n", r, conn.get(), conn->dev.get()); return handle_destroy(); } BeginWait(std::move(conn), dispatcher); return; } if (signal->observed & ZX_CHANNEL_PEER_CLOSED) { log(RPC_SDW, "proxy-rpc: peer closed (ios=%p,dev=%p)\n", conn.get(), conn->dev.get()); return handle_destroy(); } log(ERROR, "devhost: no work? %08x\n", signal->observed); BeginWait(std::move(conn), dispatcher); } zx_status_t ProxyIostate::Create(const fbl::RefPtr<zx_device_t>& dev, zx::channel rpc, async_dispatcher_t* dispatcher) { // This must be held for the adding of the channel to the port, since the // async loop may run immediately after that point. fbl::AutoLock guard(&dev->proxy_ios_lock); if (dev->proxy_ios) { dev->proxy_ios->CancelLocked(dispatcher); } auto ios = std::make_unique<ProxyIostate>(dev); if (ios == nullptr) { return ZX_ERR_NO_MEMORY; } ios->set_channel(std::move(rpc)); // |ios| is will be owned by the async loop. |dev| holds a reference that will be // cleared prior to destruction. dev->proxy_ios = ios.get(); zx_status_t status = BeginWait(std::move(ios), dispatcher); if (status != ZX_OK) { dev->proxy_ios = nullptr; return status; } return ZX_OK; } void ProxyIostate::CancelLocked(async_dispatcher_t* dispatcher) { ZX_ASSERT(this->dev->proxy_ios == this); this->dev->proxy_ios = nullptr; // TODO(teisenbe): We should probably check the return code in case the // queue was full ConnectionDestroyer::Get()->QueueProxyConnection(dispatcher, this); } } // namespace devmgr