| // Copyright 2018 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 <threads.h> |
| |
| #include <virtio/balloon.h> |
| |
| #include "garnet/bin/guest/vmm/device/test_with_device.h" |
| #include "garnet/bin/guest/vmm/device/virtio_queue_fake.h" |
| |
| static constexpr char kVirtioBalloonUrl[] = |
| "fuchsia-pkg://fuchsia.com/virtio_balloon#meta/virtio_balloon.cmx"; |
| static constexpr uint16_t kNumQueues = 3; |
| static constexpr uint16_t kQueueSize = 16; |
| |
| class VirtioBalloonTest : public TestWithDevice { |
| protected: |
| VirtioBalloonTest() |
| : inflate_queue_(phys_mem_, PAGE_SIZE * kNumQueues, kQueueSize), |
| deflate_queue_(phys_mem_, inflate_queue_.end(), kQueueSize), |
| stats_queue_(phys_mem_, deflate_queue_.end(), 1) {} |
| |
| void SetUp() override { |
| // Launch device process. |
| fuchsia::guest::device::StartInfo start_info; |
| zx_status_t status = |
| LaunchDevice(kVirtioBalloonUrl, stats_queue_.end(), &start_info); |
| ASSERT_EQ(ZX_OK, status); |
| |
| // Start device execution. |
| services.ConnectToService(balloon_.NewRequest()); |
| status = balloon_->Start(std::move(start_info)); |
| ASSERT_EQ(ZX_OK, status); |
| status = balloon_->Ready(VIRTIO_BALLOON_F_STATS_VQ); |
| ASSERT_EQ(ZX_OK, status); |
| |
| // Configure device queues. |
| VirtioQueueFake* queues[kNumQueues] = {&inflate_queue_, &deflate_queue_, |
| &stats_queue_}; |
| for (size_t i = 0; i < kNumQueues; i++) { |
| auto q = queues[i]; |
| q->Configure(PAGE_SIZE * i, PAGE_SIZE); |
| status = balloon_->ConfigureQueue(i, q->size(), q->desc(), q->avail(), |
| q->used()); |
| ASSERT_EQ(ZX_OK, status); |
| } |
| } |
| |
| public: |
| fuchsia::guest::device::VirtioBalloonSyncPtr balloon_; |
| VirtioQueueFake inflate_queue_; |
| VirtioQueueFake deflate_queue_; |
| VirtioQueueFake stats_queue_; |
| using TestWithDevice::WaitOnInterrupt; |
| }; |
| |
| TEST_F(VirtioBalloonTest, Inflate) { |
| uint32_t pfns[] = {0, 1, 2}; |
| zx_status_t status = DescriptorChainBuilder(inflate_queue_) |
| .AppendReadableDescriptor(pfns, sizeof(pfns)) |
| .Build(); |
| ASSERT_EQ(ZX_OK, status); |
| |
| status = balloon_->NotifyQueue(0); |
| ASSERT_EQ(ZX_OK, status); |
| status = WaitOnInterrupt(); |
| ASSERT_EQ(ZX_OK, status); |
| } |
| |
| TEST_F(VirtioBalloonTest, Deflate) { |
| uint32_t pfns[] = {2, 1, 0}; |
| zx_status_t status = DescriptorChainBuilder(deflate_queue_) |
| .AppendReadableDescriptor(pfns, sizeof(pfns)) |
| .Build(); |
| ASSERT_EQ(ZX_OK, status); |
| |
| status = balloon_->NotifyQueue(1); |
| ASSERT_EQ(ZX_OK, status); |
| status = WaitOnInterrupt(); |
| ASSERT_EQ(ZX_OK, status); |
| } |
| |
| TEST_F(VirtioBalloonTest, Stats) { |
| zx_status_t status = DescriptorChainBuilder(stats_queue_) |
| .AppendReadableDescriptor(nullptr, 0) |
| .Build(); |
| ASSERT_EQ(ZX_OK, status); |
| |
| auto entry = [](void* arg) { |
| auto test = static_cast<VirtioBalloonTest*>(arg); |
| zx_status_t status = test->WaitOnInterrupt(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| virtio_balloon_stat_t stat = {.tag = 2301, .val = 1985}; |
| status = DescriptorChainBuilder(test->stats_queue_) |
| .AppendReadableDescriptor(&stat, sizeof(stat)) |
| .Build(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| return test->balloon_->NotifyQueue(2); |
| }; |
| thrd_t thread; |
| int ret = thrd_create_with_name(&thread, entry, this, "balloon-stats"); |
| ASSERT_EQ(thrd_success, ret); |
| |
| zx_status_t stats_status; |
| fidl::VectorPtr<fuchsia::guest::MemStat> mem_stats; |
| status = balloon_->GetMemStats(&stats_status, &mem_stats); |
| ASSERT_EQ(ZX_OK, status); |
| ASSERT_EQ(ZX_OK, stats_status); |
| |
| ASSERT_EQ(1u, mem_stats->size()); |
| EXPECT_EQ(2301u, (*mem_stats)[0].tag); |
| EXPECT_EQ(1985u, (*mem_stats)[0].val); |
| |
| ret = thrd_join(thread, &status); |
| ASSERT_EQ(thrd_success, ret); |
| ASSERT_EQ(ZX_OK, status); |
| } |
| |
| TEST_F(VirtioBalloonTest, StatsShouldWait) { |
| zx_status_t stats_status; |
| fidl::VectorPtr<fuchsia::guest::MemStat> mem_stats; |
| zx_status_t status = balloon_->GetMemStats(&stats_status, &mem_stats); |
| ASSERT_EQ(ZX_OK, status); |
| ASSERT_EQ(ZX_ERR_SHOULD_WAIT, stats_status); |
| } |