[qemu_edu] Use device interrupt in compute.
Update the qemu_edu driver to respond to the device interrupt instead
of doing a busy wait on the status flag.
Bug: 109425
Change-Id: I9467193db339bbd9353a9b477792c041d39d9893
Reviewed-on: https://fuchsia-review.googlesource.com/c/sdk-samples/drivers/+/727882
Commit-Queue: Dave Smith <smithdave@google.com>
Reviewed-by: Jocelyn Dang <jocelyndang@google.com>
Reviewed-by: Suraj Malhotra <surajmalhotra@google.com>
diff --git a/src/qemu_edu/drivers/BUILD.bazel b/src/qemu_edu/drivers/BUILD.bazel
index 0b49122..f7e833a 100644
--- a/src/qemu_edu/drivers/BUILD.bazel
+++ b/src/qemu_edu/drivers/BUILD.bazel
@@ -41,6 +41,7 @@
"@fuchsia_sdk//fidl/fuchsia.driver.compat:fuchsia.driver.compat_llcpp_cc",
"@fuchsia_sdk//fidl/fuchsia.hardware.pci:fuchsia.hardware.pci_llcpp_cc",
"@fuchsia_sdk//fidl/zx:zx_cc",
+ "@fuchsia_sdk//pkg/async-cpp",
"@fuchsia_sdk//pkg/driver2-llcpp",
"@fuchsia_sdk//pkg/driver_runtime_cpp",
"@fuchsia_sdk//pkg/fidl_cpp_wire",
diff --git a/src/qemu_edu/drivers/edu_device.cc b/src/qemu_edu/drivers/edu_device.cc
index 8ab28f3..4663b90 100644
--- a/src/qemu_edu/drivers/edu_device.cc
+++ b/src/qemu_edu/drivers/edu_device.cc
@@ -40,7 +40,7 @@
mmio_ = *std::move(mmio);
}
- // Configure interrupt handling for the device
+ // Configure interrupt handling for the device using INTx
auto result = pci_->SetInterruptMode(fuchsia_hardware_pci::wire::InterruptMode::kLegacy, 1);
if (!result.ok()) {
FDF_SLOG(ERROR, "failed configure interrupt mode", KV("status", result.status()));
@@ -62,39 +62,79 @@
return zx::error(interrupt->error_value());
}
irq_ = std::move(interrupt->value()->interrupt);
+ // Start listening for interrupts.
+ irq_method_.set_object(irq_.get());
+ irq_method_.Begin(dispatcher_);
return zx::ok();
}
// [END interrupt_mmio]
// [START compute_factorial]
-// Write data into the factorial register and return the result.
-uint32_t QemuEduDevice::ComputeFactorial(uint32_t input) {
- // Write a value into the factorial register.
- mmio_->Write32(input, kFactorialComputationOffset);
-
- // Busy wait on the factorial status bit.
- while (true) {
- const auto status = StatusRegister();
- if (!status.busy())
- break;
+// Write data into the factorial register wait for an interrupt.
+void QemuEduDevice::ComputeFactorial(uint32_t input,
+ fit::callback<void(zx::status<uint32_t>)> callback) {
+ if (pending_callback_.has_value()) {
+ callback(zx::error(ZX_ERR_SHOULD_WAIT));
}
- // Return the result.
+ // Tell the device to raise an interrupt after computation.
+ auto status = StatusRegister();
+ status.set_irq_enable(true);
+ status.WriteTo(&*mmio_);
+
+ // Write the value into the factorial register to start computation.
+ mmio_->Write32(input, kFactorialComputationOffset);
+
+ // We will receive an interrupt when the computation completes.
+ pending_callback_ = std::move(callback);
+}
+
+// Respond to INTx interrupts triggered by the device, and return the compute result.
+void QemuEduDevice::HandleIrq(async_dispatcher_t* dispatcher, async::IrqBase* irq,
+ zx_status_t status, const zx_packet_interrupt_t* interrupt) {
+ irq_.ack();
+ if (!pending_callback_.has_value()) {
+ FDF_LOG(ERROR, "Received unexpected interrupt!");
+ return;
+ }
+ auto callback = std::move(*pending_callback_);
+ pending_callback_ = std::nullopt;
+ if (status != ZX_OK) {
+ FDF_SLOG(ERROR, "Failed to wait for interrupt", KV("status", zx_status_get_string(status)));
+ callback(zx::error(status));
+ return;
+ }
+
+ // Acknowledge the interrupt with the edu device.
+ auto int_status = mmio_->Read32(kInterruptStatusRegisterOffset);
+ mmio_->Write32(int_status, kInterruptAcknowledgeRegisterOffset);
+
+ // Deassert the legacy INTx interrupt on the PCI bus.
+ auto irq_result = pci_->AckInterrupt();
+ if (!irq_result.ok() || irq_result->is_error()) {
+ FDF_SLOG(ERROR, "Failed to ack PCI interrupt",
+ KV("status", irq_result.ok() ? irq_result->error_value() : irq_result.status()));
+ callback(zx::error(ZX_ERR_IO));
+ return;
+ }
+
+ // Reply with the result.
uint32_t factorial = mmio_->Read32(kFactorialComputationOffset);
- return factorial;
+ FDF_SLOG(INFO, "Replying with", KV("factorial", factorial));
+ callback(zx::ok(factorial));
}
// [END compute_factorial]
// [START liveness_check]
// Write a challenge value to the liveness check register and return the result.
-uint32_t QemuEduDevice::LivenessCheck(uint32_t challenge) {
+zx::status<uint32_t> QemuEduDevice::LivenessCheck(uint32_t challenge) {
// Write the challenge value to the liveness check register.
mmio_->Write32(challenge, kLivenessCheckOffset);
// Return the result.
auto value = mmio_->Read32(kLivenessCheckOffset);
- return value;
+ return zx::ok(value);
}
// [END liveness_check]
diff --git a/src/qemu_edu/drivers/edu_device.h b/src/qemu_edu/drivers/edu_device.h
index 11636b8..8aa9130 100644
--- a/src/qemu_edu/drivers/edu_device.h
+++ b/src/qemu_edu/drivers/edu_device.h
@@ -11,6 +11,7 @@
// [START hw_imports]
#include <fidl/fuchsia.hardware.pci/cpp/wire.h>
+#include <lib/async/cpp/irq.h>
#include <lib/driver2/namespace.h>
#include <lib/driver2/structured_logger.h>
#include <lib/mmio/mmio.h>
@@ -61,30 +62,38 @@
public:
explicit QemuEduDevice(driver::Namespace& ns, async_dispatcher_t* dispatcher,
fidl::WireSyncClient<fuchsia_hardware_pci::Device> pci)
- : pci_(std::move(pci)) {
+ : dispatcher_(dispatcher), pci_(std::move(pci)) {
auto logger_result = driver::Logger::Create(ns, dispatcher, "qemu-edu");
ZX_ASSERT(logger_result.is_ok());
logger_ = std::move(*logger_result);
}
zx::status<> MapInterruptAndMmio();
-
- uint32_t ComputeFactorial(uint32_t input);
- uint32_t LivenessCheck(uint32_t challenge);
// [END public_main]
// [START public_registers]
+ void ComputeFactorial(uint32_t input, fit::callback<void(zx::status<uint32_t>)> callback);
+ zx::status<uint32_t> LivenessCheck(uint32_t challenge);
+
Identification IdentificationRegister() { return Identification::Get().ReadFrom(&*mmio_); }
Status StatusRegister() { return Status::Get().ReadFrom(&*mmio_); }
// [END public_registers]
// [START private_main]
private:
+ void HandleIrq(async_dispatcher_t* dispatcher, async::IrqBase* irq, zx_status_t status,
+ const zx_packet_interrupt_t* interrupt);
+
+ async_dispatcher_t* const dispatcher_;
+
driver::Logger logger_;
+ fidl::WireSyncClient<fuchsia_hardware_pci::Device> pci_;
std::optional<fdf::MmioBuffer> mmio_;
zx::interrupt irq_;
- fidl::WireSyncClient<fuchsia_hardware_pci::Device> pci_;
+ async::IrqMethod<QemuEduDevice, &QemuEduDevice::HandleIrq> irq_method_{this};
+ std::optional<fit::callback<void(zx::status<uint32_t>)>> pending_callback_;
// [END private_main]
+
// [START class_footer]
};
// [END class_footer]
diff --git a/src/qemu_edu/drivers/qemu_edu.cc b/src/qemu_edu/drivers/qemu_edu.cc
index fdaae86..34a2b22 100644
--- a/src/qemu_edu/drivers/qemu_edu.cc
+++ b/src/qemu_edu/drivers/qemu_edu.cc
@@ -144,16 +144,22 @@
ComputeFactorialCompleter::Sync& completer) {
auto edu_device = device_.lock();
if (!edu_device) {
- FDF_LOG(ERROR, "Unable to access device resources");
+ FDF_LOG(ERROR, "Unable to access device resources.");
+ completer.ReplyError(ZX_ERR_BAD_STATE);
return;
}
uint32_t input = request->input;
- uint32_t factorial = edu_device->ComputeFactorial(input);
-
- FDF_SLOG(INFO, "Replying with", KV("factorial", factorial));
- completer.Reply(factorial);
+ edu_device->ComputeFactorial(
+ input, [completer = completer.ToAsync()](zx::status<uint32_t> result_status) mutable {
+ if (result_status.is_error()) {
+ completer.ReplyError(result_status.error_value());
+ return;
+ }
+ uint32_t factorial = result_status.value();
+ completer.ReplySuccess(factorial);
+ });
}
// [END compute_factorial]
@@ -163,18 +169,24 @@
LivenessCheckCompleter::Sync& completer) {
auto edu_device = device_.lock();
if (!edu_device) {
- FDF_LOG(ERROR, "Unable to access device resources");
+ FDF_LOG(ERROR, "Unable to access device resources.");
+ completer.ReplyError(ZX_ERR_BAD_STATE);
return;
}
constexpr uint32_t kChallenge = 0xdeadbeef;
constexpr uint32_t kExpectedResponse = ~(kChallenge);
- uint32_t value = edu_device->LivenessCheck(kChallenge);
- const bool alive = (value == kExpectedResponse);
+ auto status = edu_device->LivenessCheck(kChallenge);
+ if (status.is_error()) {
+ FDF_LOG(ERROR, "Unable to send liveness check request.");
+ completer.ReplyError(status.error_value());
+ return;
+ }
+ const bool alive = (status.value() == kExpectedResponse);
FDF_SLOG(INFO, "Replying with", KV("result", alive));
- completer.Reply(alive);
+ completer.ReplySuccess(alive);
}
// [END liveness_check]
diff --git a/src/qemu_edu/drivers/qemu_edu.h b/src/qemu_edu/drivers/qemu_edu.h
index 73d5bca..0070f5c 100644
--- a/src/qemu_edu/drivers/qemu_edu.h
+++ b/src/qemu_edu/drivers/qemu_edu.h
@@ -47,7 +47,9 @@
// This method is called when a server connection is torn down.
void OnUnbound(fidl::UnbindInfo info,
fidl::ServerEnd<fuchsia_examples_qemuedu::Device> server_end) {
- if (!info.is_user_initiated()) {
+ if (info.is_peer_closed()) {
+ FDF_LOG(DEBUG, "Client disconnected");
+ } else if (!info.is_user_initiated()) {
FDF_LOG(ERROR, "Client connection unbound: %s", info.status_string());
}
}
diff --git a/src/qemu_edu/fidl/BUILD.bazel b/src/qemu_edu/fidl/BUILD.bazel
index 17f5df6..98aa50f 100644
--- a/src/qemu_edu/fidl/BUILD.bazel
+++ b/src/qemu_edu/fidl/BUILD.bazel
@@ -18,6 +18,9 @@
],
library = "fuchsia.examples.qemuedu",
visibility = ["//visibility:public"],
+ deps = [
+ "@fuchsia_sdk//fidl/zx",
+ ],
)
fuchsia_fidl_llcpp_library(
diff --git a/src/qemu_edu/fidl/qemu_edu.fidl b/src/qemu_edu/fidl/qemu_edu.fidl
index 045e9a3..72f95cb 100644
--- a/src/qemu_edu/fidl/qemu_edu.fidl
+++ b/src/qemu_edu/fidl/qemu_edu.fidl
@@ -5,6 +5,8 @@
// [START example_snippet]
library fuchsia.examples.qemuedu;
+using zx;
+
/// The Device protocol provides access to the functions of the QEMU edu hardware.
protocol Device {
/// Computes the factorial of 'input' using the edu device and returns the
@@ -13,12 +15,12 @@
input uint32;
}) -> (struct {
output uint32;
- });
+ }) error zx.status;
/// Performs a liveness check and return true if the device passes.
LivenessCheck() -> (struct {
result bool;
- });
+ }) error zx.status;
};
/// The Service enables the driver framework to offer the Device protocol to
diff --git a/src/qemu_edu/tests/qemu_edu_system_test.cc b/src/qemu_edu/tests/qemu_edu_system_test.cc
index 22dec21..07d028e 100644
--- a/src/qemu_edu/tests/qemu_edu_system_test.cc
+++ b/src/qemu_edu/tests/qemu_edu_system_test.cc
@@ -39,7 +39,7 @@
TEST_F(QemuEduSystemTest, LivenessCheck) {
fidl::WireResult result = device()->LivenessCheck();
ASSERT_EQ(result.status(), ZX_OK);
- ASSERT_TRUE(result->result);
+ ASSERT_TRUE(result->value()->result);
}
TEST_F(QemuEduSystemTest, ComputeFactorial) {
@@ -49,7 +49,7 @@
for (int i = 0; i < kExpected.size(); i++) {
fidl::WireResult result = device()->ComputeFactorial(i);
ASSERT_EQ(result.status(), ZX_OK);
- EXPECT_EQ(result->output, kExpected[i]);
+ EXPECT_EQ(result->value()->output, kExpected[i]);
}
}
diff --git a/src/qemu_edu/tools/eductl.cc b/src/qemu_edu/tools/eductl.cc
index 971bb36..3123029 100644
--- a/src/qemu_edu/tools/eductl.cc
+++ b/src/qemu_edu/tools/eductl.cc
@@ -94,7 +94,7 @@
return -1;
}
- if (liveness_check_result->result) {
+ if (liveness_check_result->value()->result) {
printf("Liveness check passed!\n");
return 0;
} else {
@@ -126,7 +126,7 @@
return -1;
}
- printf("Factorial(%u) = %u\n", input, compute_factorial_result->output);
+ printf("Factorial(%u) = %u\n", input, compute_factorial_result->value()->output);
return 0;
}