From cf254ae8e423f4e7a964b650e5e7c1a64d886fe1 Mon Sep 17 00:00:00 2001 From: Abdulla Kamar <abdulla@google.com> Date: Fri, 10 May 2019 02:36:10 +0000 Subject: [PATCH] [devhost] Wait for BindDriver in reply to Bind Calls to fuchsia.device.Controller/Bind should only return once the operation completes, which is when fuchsia.device.manager.DeviceController/BindDriver is called. ZX-3991 #done Change-Id: I10607640072c8f779ac1d5be79d5a2da6de6288b --- zircon/system/core/devmgr/devhost/BUILD.gn | 1 + zircon/system/core/devmgr/devhost/devhost.cpp | 22 ++++++++++++++++ .../system/core/devmgr/devhost/rpc-server.cpp | 14 +++++++--- .../system/core/devmgr/devhost/zx-device.cpp | 15 +++++++++++ zircon/system/core/devmgr/devhost/zx-device.h | 9 +++++++ zircon/system/core/devmgr/dmctl/BUILD.gn | 1 + zircon/system/dev/test/ddk-test/ddk-test.c | 26 ++++++++++++++++++- zircon/system/ulib/ddktl/test/ddktl-test.cpp | 23 ++++++++++++++-- zircon/system/ulib/fs/BUILD.gn | 2 +- zircon/system/utest/driver-test/main.cpp | 9 +++++++ 10 files changed, 114 insertions(+), 8 deletions(-) diff --git a/zircon/system/core/devmgr/devhost/BUILD.gn b/zircon/system/core/devmgr/devhost/BUILD.gn index 3660c415568..b57ac813f92 100644 --- a/zircon/system/core/devmgr/devhost/BUILD.gn +++ b/zircon/system/core/devmgr/devhost/BUILD.gn @@ -42,6 +42,7 @@ library("driver") { "$zx/system/ulib/fbl", "$zx/system/ulib/fdio", "$zx/system/ulib/fidl", + "$zx/system/ulib/fit", "$zx/system/ulib/fs:fs-for-driver", "$zx/system/ulib/sync", "$zx/system/ulib/trace:trace-driver", diff --git a/zircon/system/core/devmgr/devhost/devhost.cpp b/zircon/system/core/devmgr/devhost/devhost.cpp index 97b14bb99e0..9a69ebddd8d 100644 --- a/zircon/system/core/devmgr/devhost/devhost.cpp +++ b/zircon/system/core/devmgr/devhost/devhost.cpp @@ -29,6 +29,7 @@ #include <fbl/auto_lock.h> #include <fbl/function.h> #include <fs/handler.h> +#include <fuchsia/device/c/fidl.h> #include <fuchsia/device/manager/c/fidl.h> #include <fuchsia/io/c/fidl.h> #include <lib/async-loop/cpp/loop.h> @@ -36,6 +37,7 @@ #include <lib/async/cpp/wait.h> #include <lib/fdio/fdio.h> #include <lib/fidl/coding.h> +#include <lib/fit/defer.h> #include <lib/zx/debuglog.h> #include <lib/zx/resource.h> #include <lib/zx/vmo.h> @@ -547,6 +549,20 @@ static zx_status_t fidl_CreateCompositeDevice(void* raw_ctx, zx_handle_t raw_rpc return fuchsia_device_manager_DevhostControllerCreateCompositeDevice_reply(txn, ZX_OK); } +// Handles outstanding calls to fuchsia.device.Controller/Bind. +static void BindReply(const fbl::RefPtr<zx_device_t>& dev) { + fs::FidlConnection conn(fidl_txn_t{}, ZX_HANDLE_INVALID, 0); + if (dev->PopBindConn(&conn)) { + // TODO(DNO-492): additional logging for debugging what looks like a + // deadlock. Remove once bug is resolved. + printf("DeviceControllerBind finished\n"); + // TODO(ZX-3431): We ignore the status from device_bind() for + // bug-compatibility reasons. Once this bug is resolved, we can + // return the actual status. + fuchsia_device_ControllerBind_reply(conn.Txn(), ZX_OK); + } +} + static zx_status_t fidl_BindDriver(void* raw_ctx, const char* driver_path_data, size_t driver_path_size, zx_handle_t raw_driver_vmo, fidl_txn_t* txn) { @@ -554,6 +570,12 @@ static zx_status_t fidl_BindDriver(void* raw_ctx, const char* driver_path_data, zx::vmo driver_vmo(raw_driver_vmo); fbl::StringPiece driver_path(driver_path_data, driver_path_size); + // We can now reply to calls to fuchsia.device.Controller/Bind, as we have + // completed the request to bind the driver. + auto defer = fit::defer([dev = ctx->conn->dev] { + BindReply(dev); + }); + // TODO: added to help debug DNO-492, remove when done if (driver_path == "/boot/driver/zxcrypt.so") { log(ERROR, "devhost[%s] bind zxcrypt\n", ctx->path); diff --git a/zircon/system/core/devmgr/devhost/rpc-server.cpp b/zircon/system/core/devmgr/devhost/rpc-server.cpp index 7951a87d8ee..8fe32008e70 100644 --- a/zircon/system/core/devmgr/devhost/rpc-server.cpp +++ b/zircon/system/core/devmgr/devhost/rpc-server.cpp @@ -27,6 +27,7 @@ #include <zircon/syscalls.h> #include <zircon/types.h> +#include <fbl/auto_lock.h> #include <fs/connection.h> #include <fs/handler.h> #include <fuchsia/device/c/fidl.h> @@ -561,6 +562,14 @@ static zx_status_t fidl_DeviceControllerBind(void* ctx, const char* driver_data, memcpy(drv_libname, driver_data, driver_count); drv_libname[driver_count] = 0; + if (!strcmp(drv_libname, "/boot/driver/zxcrypt.so")) { + // TODO(DNO-492): Workaround, due to racing attempts to bind zxcrypt.so + // that results in a deadlock. + fuchsia_device_ControllerBind_reply(txn, ZX_OK); + } else { + conn->dev->PushBindConn(fs::FidlConnection::CopyTxn(txn)); + } + // TODO(DNO-492): additional logging for debugging what looks like a // deadlock. Remove once bug is resolved. printf("DeviceControllerBind running: %s\n", drv_libname); @@ -568,10 +577,7 @@ static zx_status_t fidl_DeviceControllerBind(void* ctx, const char* driver_data, // bug-compatibility reasons. Once this bug is resolved, we can return the // actual status. __UNUSED zx_status_t status = device_bind(conn->dev, drv_libname); - // TODO(DNO-492): additional logging for debugging what looks like a - // deadlock. Remove once bug is resolved. - printf("DeviceControllerBind finished: %s %s\n", drv_libname, zx_status_get_string(status)); - return fuchsia_device_ControllerBind_reply(txn, ZX_OK); + return ZX_OK; } static zx_status_t fidl_DeviceControllerUnbind(void* ctx, fidl_txn_t* txn) { diff --git a/zircon/system/core/devmgr/devhost/zx-device.cpp b/zircon/system/core/devmgr/devhost/zx-device.cpp index d00ea30d266..1201f89a95d 100644 --- a/zircon/system/core/devmgr/devhost/zx-device.cpp +++ b/zircon/system/core/devmgr/devhost/zx-device.cpp @@ -14,6 +14,21 @@ zx_status_t zx_device::Create(fbl::RefPtr<zx_device>* out_dev) { return ZX_OK; } +void zx_device::PushBindConn(const fs::FidlConnection& conn) { + fbl::AutoLock<fbl::Mutex> lock(&bind_conn_lock_); + bind_conn_.push_back(conn); +} + +bool zx_device::PopBindConn(fs::FidlConnection* conn) { + fbl::AutoLock<fbl::Mutex> lock(&bind_conn_lock_); + if (bind_conn_.is_empty()) { + return false; + } + *conn = bind_conn_[0]; + bind_conn_.erase(0); + return true; +} + // We must disable thread-safety analysis due to not being able to statically // guarantee the lock holding invariant. Instead, we acquire the lock if // it's not already being held by the current thread. diff --git a/zircon/system/core/devmgr/devhost/zx-device.h b/zircon/system/core/devmgr/devhost/zx-device.h index 26152085fc3..90bf1a4bc8f 100644 --- a/zircon/system/core/devmgr/devhost/zx-device.h +++ b/zircon/system/core/devmgr/devhost/zx-device.h @@ -12,6 +12,8 @@ #include <fbl/recycler.h> #include <fbl/ref_counted_upgradeable.h> #include <fbl/ref_ptr.h> +#include <fbl/vector.h> +#include <fs/handler.h> #include <lib/zx/channel.h> #include <lib/zx/eventpair.h> #include <zircon/compiler.h> @@ -80,6 +82,9 @@ struct zx_device : fbl::RefCountedUpgradeable<zx_device>, fbl::Recyclable<zx_dev return Dispatch(ops->message, ZX_ERR_NOT_SUPPORTED, msg, txn); } + void PushBindConn(const fs::FidlConnection& conn); + bool PopBindConn(fs::FidlConnection* conn); + // Check if this devhost has a device with the given ID, and if so returns a // reference to it. static fbl::RefPtr<zx_device> GetDeviceFromLocalId(uint64_t local_id); @@ -180,6 +185,10 @@ private: // Identifier assigned by devmgr that can be used to assemble composite // devices. uint64_t local_id_ = 0; + + // The connection associated with a fuchsia.device.Controller/Bind. + fbl::Mutex bind_conn_lock_; + fbl::Vector<fs::FidlConnection> bind_conn_ TA_GUARDED(bind_conn_lock_); }; // zx_device_t objects must be created or initialized by the driver manager's diff --git a/zircon/system/core/devmgr/dmctl/BUILD.gn b/zircon/system/core/devmgr/dmctl/BUILD.gn index d702904df07..010be79855a 100644 --- a/zircon/system/core/devmgr/dmctl/BUILD.gn +++ b/zircon/system/core/devmgr/dmctl/BUILD.gn @@ -11,6 +11,7 @@ driver("dmctl") { "$zx/system/ulib/ddk", "$zx/system/ulib/ddktl", "$zx/system/ulib/fbl", + "$zx/system/ulib/fs:fs-for-driver", "$zx/system/ulib/zircon", "$zx/system/ulib/zx", "$zx/system/ulib/zxcpp", diff --git a/zircon/system/dev/test/ddk-test/ddk-test.c b/zircon/system/dev/test/ddk-test/ddk-test.c index b1e0927b3e1..957d794d1bc 100644 --- a/zircon/system/dev/test/ddk-test/ddk-test.c +++ b/zircon/system/dev/test/ddk-test/ddk-test.c @@ -6,6 +6,7 @@ #include <ddk/driver.h> #include <ddk/binding.h> #include <ddk/protocol/test.h> +#include <zircon/assert.h> #include <unittest/unittest.h> #include <stddef.h> @@ -57,9 +58,32 @@ static zx_status_t ddk_test_func(void* cookie, test_report_t* report) { return report->n_failed == 0 ? ZX_OK : ZX_ERR_INTERNAL; } +static zx_device_t* child_dev = NULL; + +static void child_unbind(void* ctx) { + device_remove(child_dev); +} + +static zx_protocol_device_t child_device_ops = { + .version = DEVICE_OPS_VERSION, + .unbind = child_unbind, +}; + zx_status_t ddk_test_bind(void* ctx, zx_device_t* parent) { + device_add_args_t args = { + .version = DEVICE_ADD_ARGS_VERSION, + .name = "child", + .ops = &child_device_ops, + .flags = DEVICE_ADD_NON_BINDABLE, + }; + ZX_ASSERT(child_dev == NULL); + zx_status_t status = device_add(parent, &args, &child_dev); + if (status != ZX_OK) { + return status; + } + test_protocol_t proto; - zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_TEST, &proto); + status = device_get_protocol(parent, ZX_PROTOCOL_TEST, &proto); if (status != ZX_OK) { return status; } diff --git a/zircon/system/ulib/ddktl/test/ddktl-test.cpp b/zircon/system/ulib/ddktl/test/ddktl-test.cpp index 15cfa061583..bad3ff2c813 100644 --- a/zircon/system/ulib/ddktl/test/ddktl-test.cpp +++ b/zircon/system/ulib/ddktl/test/ddktl-test.cpp @@ -6,9 +6,11 @@ #include <ddk/device.h> #include <ddk/driver.h> #include <ddk/protocol/test.h> +#include <ddktl/device.h> #include <lib/zx/socket.h> #include <limits.h> +#include <memory> #include <stddef.h> #include <stdio.h> #include <stdlib.h> @@ -62,12 +64,29 @@ zx_status_t ddktl_test_func(void* cookie, test_report_t* report) { return report->n_failed == 0 ? ZX_OK : ZX_ERR_INTERNAL; } +class ChildDevice; +using ChildDeviceType = ddk::Device<ChildDevice, ddk::Unbindable>; + +class ChildDevice : public ChildDeviceType { +public: + ChildDevice(zx_device_t* parent) : ChildDeviceType(parent) {} + void DdkUnbind() { DdkRemove(); } + void DdkRelease() { delete this; } +}; + } // namespace extern "C" zx_status_t ddktl_test_bind(void* ctx, zx_device_t* parent) { + auto device = std::make_unique<ChildDevice>(parent); + zx_status_t status = device->DdkAdd("child", DEVICE_ADD_NON_BINDABLE); + if (status != ZX_OK) { + return status; + } + // devmgr now owns this + __UNUSED auto ptr = device.release(); + test_protocol_t proto; - auto status = - device_get_protocol(parent, ZX_PROTOCOL_TEST, reinterpret_cast<void*>(&proto)); + status = device_get_protocol(parent, ZX_PROTOCOL_TEST, &proto); if (status != ZX_OK) { return status; } diff --git a/zircon/system/ulib/fs/BUILD.gn b/zircon/system/ulib/fs/BUILD.gn index c6535041c8f..b0da9c3a736 100644 --- a/zircon/system/ulib/fs/BUILD.gn +++ b/zircon/system/ulib/fs/BUILD.gn @@ -113,7 +113,7 @@ if (is_fuchsia) { library("fs-for-driver") { visibility = [ ":*", - "$zx/system/core/devmgr/devhost:*", + "$zx/system/core/devmgr/*", ] sources = [ "block-txn.cpp", diff --git a/zircon/system/utest/driver-test/main.cpp b/zircon/system/utest/driver-test/main.cpp index 6e0ad8a51ec..1e95ba22de9 100644 --- a/zircon/system/utest/driver-test/main.cpp +++ b/zircon/system/utest/driver-test/main.cpp @@ -105,6 +105,15 @@ void do_one_test(const IsolatedDevmgr& devmgr, const zx::channel& test_root, return; } + // Check that Bind was synchronous by looking for the child device. + char child_devpath[fuchsia_device_test_MAX_DEVICE_PATH_LEN+1]; + snprintf(child_devpath, sizeof(child_devpath), "%s/child", relative_devpath); + fd.reset(openat(devmgr.devfs_root().get(), child_devpath, O_RDWR)); + if (!fd.is_valid()) { + printf("driver-tests: error binding device %s %s\n", devpath, relative_devpath); + return; + } + zx::socket output_copy; status = output.duplicate(ZX_RIGHT_SAME_RIGHTS, &output_copy); if (status != ZX_OK) { -- GitLab