[hwstress] Add deletion of flash test partitions.

In the case of an unexpected exit of the flash stress test some
partitions may persist on the device. Add a command to clear out any
existing flash test partitions and flag the partitions to be destroyed
on reboot.

Change-Id: I1c3b6881b8b950cc27b94419c0e01a9de19c99e1
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/405554
Reviewed-by: David Greenaway <dgreenaway@google.com>
Testability-Review: David Greenaway <dgreenaway@google.com>
Commit-Queue: Sarah Pham <smpham@google.com>
diff --git a/garnet/bin/hwstress/args.cc b/garnet/bin/hwstress/args.cc
index fdfcb3cb..1cde501 100644
--- a/garnet/bin/hwstress/args.cc
+++ b/garnet/bin/hwstress/args.cc
@@ -21,6 +21,8 @@
 
 std::unique_ptr<cmdline::ArgsParser<CommandLineArgs>> GetParser() {
   auto parser = std::make_unique<cmdline::ArgsParser<CommandLineArgs>>();
+  parser->AddSwitch("cleanup-test-partitions", 'c', "Cleanup all existing flash test partitions.",
+                    &CommandLineArgs::destroy_partitions);
   parser->AddSwitch("duration", 'd', "Test duration in seconds.",
                     &CommandLineArgs::test_duration_seconds);
   parser->AddSwitch("fvm-path", 'f', "Path to Fuchsia Volume Manager.", &CommandLineArgs::fvm_path);
@@ -70,7 +72,12 @@
                          than 0, and no more than 100.
 
 Flash test options:
-  -f, --fvm-path=<path>  Path to Fuchsia Volume Manager
+  -c, --cleanup-test-partitions
+                         Cleanup all existing flash test partitions in the
+                         system, and then exit without testing. Can be used
+                         to clean up persistent test partitions left over from
+                         previous flash tests which did not exit cleanly.
+  -f, --fvm-path=<path>  Path to Fuchsia Volume Manager.
   -m, --memory=<size>    Amount of flash memory to test, in megabytes.
 
 Memory test options:
@@ -154,8 +161,12 @@
   }
 
   // Ensure mandatory flash test argument is provided
-  if (result.subcommand == StressTest::kFlash && result.fvm_path.empty()) {
-    return fitx::error(fxl::StringPrintf("Path to Fuchsia Volume Manager must be specified"));
+  if (result.subcommand == StressTest::kFlash) {
+    if (result.destroy_partitions && !result.fvm_path.empty()) {
+      return fitx::error(fxl::StringPrintf("Path to Fuchsia Volume Manager invalid with cleanup"));
+    } else if (!result.destroy_partitions && result.fvm_path.empty()) {
+      return fitx::error(fxl::StringPrintf("Path to Fuchsia Volume Manager must be specified"));
+    }
   }
 
   // Ensure no more parameters were given.
diff --git a/garnet/bin/hwstress/args.h b/garnet/bin/hwstress/args.h
index b36b331..2e39d8e 100644
--- a/garnet/bin/hwstress/args.h
+++ b/garnet/bin/hwstress/args.h
@@ -67,6 +67,9 @@
   // Path to the Fuchsia Volume Manager
   std::string fvm_path;
 
+  // Destroy any existing flash test partitions.
+  bool destroy_partitions = false;
+
   //
   // Memory-specific arguments.
   //
diff --git a/garnet/bin/hwstress/flash_stress.cc b/garnet/bin/hwstress/flash_stress.cc
index e27927d..9fc8d52 100644
--- a/garnet/bin/hwstress/flash_stress.cc
+++ b/garnet/bin/hwstress/flash_stress.cc
@@ -4,15 +4,12 @@
 
 #include "flash_stress.h"
 
-#include <fcntl.h>
 #include <fuchsia/hardware/block/cpp/fidl.h>
 #include <fuchsia/hardware/block/volume/cpp/fidl.h>
 #include <lib/fdio/directory.h>
 #include <lib/zx/fifo.h>
-#include <lib/zx/time.h>
 #include <lib/zx/vmar.h>
 #include <lib/zx/vmo.h>
-#include <threads.h>
 #include <zircon/status.h>
 
 #include <string>
@@ -33,9 +30,6 @@
 constexpr uint32_t kMinFvmFreeSpace = 16 * 1024 * 1024;
 constexpr uint32_t kMinPartitionFreeSpace = 2 * 1024 * 1024;
 
-constexpr uuid::Uuid kTestPartGUID = uuid::Uuid({0xC6, 0x24, 0xF5, 0xDD, 0x9D, 0x88, 0x4C, 0x81,
-                                                 0x99, 0x87, 0xCA, 0x92, 0xD1, 0x1B, 0x28, 0x89});
-
 struct BlockDevice {
   fuchsia::hardware::block::BlockSyncPtr device;  // Connection to the block device.
   zx::fifo fifo;                                  // FIFO used to read/write to the block device.
@@ -247,10 +241,9 @@
 
   uuid::Uuid unique_guid = uuid::Uuid::Generate();
 
-  alloc_req_t request{
-      .slice_count = num_slices,
-      .name = "flash-test-fs",
-  };
+  alloc_req_t request{.slice_count = num_slices,
+                      .name = "flash-test-fs",
+                      .flags = fuchsia::hardware::block::volume::ALLOCATE_PARTITION_FLAG_INACTIVE};
   memcpy(request.guid, unique_guid.bytes(), sizeof(request.guid));
   memcpy(request.type, kTestPartGUID.bytes(), sizeof(request.type));
 
@@ -341,4 +334,13 @@
   return true;
 }
 
+void DestroyFlashTestPartitions(StatusLine* status) {
+  uint32_t count = 0;
+  // Remove any partitions from previous tests
+  while (destroy_partition(nullptr, kTestPartGUID.bytes()) == ZX_OK) {
+    count++;
+  }
+  status->Log("Deleted %u partitions", count);
+}
+
 }  // namespace hwstress
diff --git a/garnet/bin/hwstress/flash_stress.h b/garnet/bin/hwstress/flash_stress.h
index c556fde..bbe7753 100644
--- a/garnet/bin/hwstress/flash_stress.h
+++ b/garnet/bin/hwstress/flash_stress.h
@@ -13,6 +13,10 @@
 
 namespace hwstress {
 
+// The GPT partition type used for partitions created by the flash test.
+constexpr uuid::Uuid kTestPartGUID = uuid::Uuid({0xC6, 0x24, 0xF5, 0xDD, 0x9D, 0x88, 0x4C, 0x81,
+                                                 0x99, 0x87, 0xCA, 0x92, 0xD1, 0x1B, 0x28, 0x89});
+
 // Creates and manages the lifetime of a new partition backed by a
 // Fuchsia Volume Manager instance.
 class TemporaryFvmPartition {
@@ -55,6 +59,9 @@
 // Start a stress test.
 bool StressFlash(StatusLine* status, const std::string& fvm_path, uint64_t bytes_to_test);
 
+// Delete any persistent flash test partitions
+void DestroyFlashTestPartitions(StatusLine* status);
+
 }  // namespace hwstress
 
 #endif  // GARNET_BIN_HWSTRESS_FLASH_STRESS_H_
diff --git a/garnet/bin/hwstress/flash_stress_test.cc b/garnet/bin/hwstress/flash_stress_test.cc
index 00ac7e8..20c8607 100644
--- a/garnet/bin/hwstress/flash_stress_test.cc
+++ b/garnet/bin/hwstress/flash_stress_test.cc
@@ -7,6 +7,8 @@
 #include <lib/zx/status.h>
 #include <lib/zx/vmo.h>
 
+#include <fbl/unique_fd.h>
+#include <fs-management/fvm.h>
 #include <gtest/gtest.h>
 
 #include "src/lib/isolated_devmgr/v2_component/fvm.h"
@@ -36,5 +38,33 @@
   ASSERT_TRUE(StressFlash(&status, fvm_path.value(), /*bytes_to_test=*/16 * 1024 * 1024));
 }
 
+TEST(Flash, DeletePartition) {
+  // Create a RAM disk.
+  zx::status<isolated_devmgr::RamDisk> ramdisk = isolated_devmgr::RamDisk::Create(
+      /*block_size=*/kBlockSize, /*block_count=*/kDefaultRamDiskSize / kBlockSize);
+  ASSERT_TRUE(ramdisk.is_ok());
+
+  // Instantiate it as a FVM device.
+  zx::status<std::string> fvm_path =
+      isolated_devmgr::CreateFvmInstance(ramdisk->path(), kDefaultFvmSliceSize);
+  ASSERT_TRUE(fvm_path.is_ok());
+
+  // Access FVM.
+  fbl::unique_fd fvm_fd(open(fvm_path.value().c_str(), O_RDWR));
+  ASSERT_TRUE(fvm_fd);
+
+  alloc_req_t request{.slice_count = 1, .name = "test-fs"};
+  memcpy(request.guid, uuid::Uuid::Generate().bytes(), sizeof(request.guid));
+  memcpy(request.type, kTestPartGUID.bytes(), sizeof(request.type));
+
+  // Create a partition.
+  fbl::unique_fd fd(fvm_allocate_partition(fvm_fd.get(), &request));
+  ASSERT_TRUE(fd);
+
+  StatusLine status;
+  DestroyFlashTestPartitions(&status);
+  ASSERT_TRUE(open_partition(nullptr, kTestPartGUID.bytes(), 0, nullptr) != ZX_OK);
+}
+
 }  // namespace
 }  // namespace hwstress
diff --git a/garnet/bin/hwstress/hwstress.cc b/garnet/bin/hwstress/hwstress.cc
index ea5d67e..1ede4d2 100644
--- a/garnet/bin/hwstress/hwstress.cc
+++ b/garnet/bin/hwstress/hwstress.cc
@@ -69,7 +69,12 @@
       success = StressCpu(&status, args, duration, sensor.get());
       break;
     case StressTest::kFlash:
-      success = StressFlash(&status, args.fvm_path, flash_to_test);
+      if (args.destroy_partitions) {
+        DestroyFlashTestPartitions(&status);
+        success = true;
+      } else {
+        success = StressFlash(&status, args.fvm_path, flash_to_test);
+      }
       break;
     case StressTest::kLight:
       success = StressLight(&status, args, duration);