]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
tools/rbd: introduce a simple bench for read
authorDongsheng Yang <dongsheng.yang@easystack.cn>
Fri, 13 May 2016 08:57:32 +0000 (04:57 -0400)
committerDongsheng Yang <dongsheng.yang@easystack.cn>
Mon, 20 Jun 2016 05:24:44 +0000 (01:24 -0400)
Currently, we have a bench-write for in rbd tool, but we don't have
a simple bench for read.

This patch add a new subcommand of bench, with an option of --io-type.
Then we can do a simple bench for write or read.

Signed-off-by: Dongsheng Yang <dongsheng.yang@easystack.cn>
doc/man/8/rbd.rst
src/CMakeLists.txt
src/test/cli-integration/rbd/formatted-output.t
src/test/cli/rbd/help.t
src/test/cli/rbd/invalid-snap-usage.t
src/test/cli/rbd/not-enough-args.t
src/tools/Makefile-client.am
src/tools/rbd/action/Bench.cc [new file with mode: 0644]
src/tools/rbd/action/BenchWrite.cc [deleted file]

index c9136f699dabc74469bef0be7c615a0b30cd42dc..9ba5442e7a1a9afd955dad5ff650949e421852f9 100644 (file)
@@ -369,6 +369,13 @@ Commands
   Release a lock on an image. The lock id and locker are
   as output by lock ls.
 
+:command:`bench` --io-type <read | write> [--io-size *size-in-B/K/M/G/T*] [--io-threads *num-ios-in-flight*] [--io-total *total-size-for-IO-in-B/K/M/G/T*] [--io-pattern seq | rand] *image-spec*
+  Generate a series of IOs to the image and measure the IO throughput and
+  latency.  Defaults are: --io-size 4096, --io-threads 16, --io-total 1G,
+  --io-pattern seq.
+
+  NOTE: --io-type must be specified.
+
 Image and snap specs
 ====================
 
index 1aa9ddad90658ca3c6bf9c4c579539dacfa42f10..934160097223db94b0736344efb05722d0c1cc4b 100644 (file)
@@ -1261,7 +1261,7 @@ if(${WITH_RBD})
     tools/rbd/OptionPrinter.cc
     tools/rbd/Shell.cc
     tools/rbd/Utils.cc
-    tools/rbd/action/BenchWrite.cc
+    tools/rbd/action/Bench.cc
     tools/rbd/action/Children.cc
     tools/rbd/action/Clone.cc
     tools/rbd/action/Copy.cc
index af18b267422274ee32dded1302166364f7d5b915..b05cc583f55501264e5e80233d8b084785a84e11 100644 (file)
@@ -33,7 +33,7 @@ clone
   $ rbd clone --image-feature layering,exclusive-lock,object-map,fast-diff bar@snap rbd_other/child
   $ rbd snap create rbd_other/child@snap
   $ rbd flatten rbd_other/child 2> /dev/null
-  $ rbd bench-write rbd_other/child --io-pattern seq --io-total 1B > /dev/null 2>&1
+  $ rbd bench rbd_other/child --io-type write --io-pattern seq --io-total 1B > /dev/null 2>&1
   $ rbd clone bar@snap rbd_other/deep-flatten-child
   $ rbd snap create rbd_other/deep-flatten-child@snap
   $ rbd flatten rbd_other/deep-flatten-child 2> /dev/null
index 49f3f14109774af68279619a923e6d9ecb81e5d1..d26075837bc1fcada3f89ce585c43b651c94df86 100644 (file)
@@ -5,7 +5,7 @@
   
   Positional arguments:
     <command>
-      bench-write                 Simple write benchmark.
+      bench                       Simple benchmark.
       children                    Display children of snapshot.
       clone                       Clone a snapshot into a COW child image.
       copy (cp)                   Copy src image to dest.
   
   See 'rbd help <command>' for help on a specific command.
   $ rbd help | grep '^    [a-z]' | sed 's/^    \([a-z -]*[a-z]\).*/\1/g' | while read -r line; do echo rbd help $line ; rbd help $line; done
-  rbd help bench-write
-  usage: rbd bench-write [--pool <pool>] [--image <image>] [--io-size <io-size>] 
-                         [--io-threads <io-threads>] [--io-total <io-total>] 
-                         [--io-pattern <io-pattern>] 
-                         <image-spec> 
+  rbd help bench
+  usage: rbd bench [--pool <pool>] [--image <image>] [--io-size <io-size>] 
+                   [--io-threads <io-threads>] [--io-total <io-total>] 
+                   [--io-pattern <io-pattern>] --io-type <io-type> 
+                   <image-spec> 
   
-  Simple write benchmark.
+  Simple benchmark.
   
   Positional arguments
     <image-spec>         image specification
   Optional arguments
     -p [ --pool ] arg    pool name
     --image arg          image name
-    --io-size arg        write size (in B/K/M/G/T)
+    --io-size arg        IO size (in B/K/M/G/T)
     --io-threads arg     ios in flight
-    --io-total arg       total size to write (in B/K/M/G/T)
-    --io-pattern arg     write pattern (rand or seq)
+    --io-total arg       total size for IO (in B/K/M/G/T)
+    --io-pattern arg     IO pattern (rand or seq)
+    --io-type arg        IO type (read or write)
   
   rbd help children
   usage: rbd children [--pool <pool>] [--image <image>] [--snap <snap>] 
index 9b8c594646136b011b7f71c0b0ac052359dfe1d9..5f98c1a243e8b5bcc26d27b8e8a10fb1de5c8089 100644 (file)
@@ -88,7 +88,7 @@
   $ rbd lock remove foo@snap id client.1234
   rbd: snapname specified for a command that doesn't use it
   [22]
-  $ rbd bench-write foo@snap
+  $ rbd bench foo@snap --io-type write
   rbd: snapname specified for a command that doesn't use it
   [22]
 
index 72adf186a973662198ee1840d8e3edef10cba982..3cc98b2899677e9be7ed9f6651a60f8c205e82dc 100644 (file)
   $ rbd lock remove foo id
   rbd: locker was not specified
   [22]
-  $ rbd bench-write
+  $ rbd bench --io-type write
   rbd: image name was not specified
   [22]
   $ rbd mirror pool enable rbd
index 906ea48439fbe821e05e2e1e767b9bcb3229c00a..6bb86a6ab46159dbefb165bdf475ae77cb3bda62 100644 (file)
@@ -30,7 +30,7 @@ rbd_SOURCES = \
        tools/rbd/OptionPrinter.cc \
        tools/rbd/Shell.cc \
        tools/rbd/Utils.cc \
-       tools/rbd/action/BenchWrite.cc \
+       tools/rbd/action/Bench.cc \
        tools/rbd/action/Children.cc \
        tools/rbd/action/Clone.cc \
        tools/rbd/action/Group.cc \
diff --git a/src/tools/rbd/action/Bench.cc b/src/tools/rbd/action/Bench.cc
new file mode 100644 (file)
index 0000000..8454337
--- /dev/null
@@ -0,0 +1,414 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include "common/strtol.h"
+#include "common/Cond.h"
+#include "common/Mutex.h"
+#include <iostream>
+#include <boost/accumulators/accumulators.hpp>
+#include <boost/accumulators/statistics/stats.hpp>
+#include <boost/accumulators/statistics/rolling_sum.hpp>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace bench {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+namespace {
+
+enum io_type_t {
+  IO_TYPE_READ = 0,
+  IO_TYPE_WRITE,
+
+  IO_TYPE_NUM,
+};
+
+struct IOType {};
+struct Size {};
+struct IOPattern {};
+
+void validate(boost::any& v, const std::vector<std::string>& values,
+              Size *target_type, int) {
+  po::validators::check_first_occurrence(v);
+  const std::string &s = po::validators::get_single_string(values);
+
+  std::string parse_error;
+  uint64_t size = strict_sistrtoll(s.c_str(), &parse_error);
+  if (!parse_error.empty()) {
+    throw po::validation_error(po::validation_error::invalid_option_value);
+  }
+  v = boost::any(size);
+}
+
+void validate(boost::any& v, const std::vector<std::string>& values,
+              IOPattern *target_type, int) {
+  po::validators::check_first_occurrence(v);
+  const std::string &s = po::validators::get_single_string(values);
+  if (s == "rand") {
+    v = boost::any(true);
+  } else if (s == "seq") {
+    v = boost::any(false);
+  } else {
+    throw po::validation_error(po::validation_error::invalid_option_value);
+  }
+}
+
+io_type_t get_io_type(string io_type_string) {
+  if (io_type_string == "read")
+    return IO_TYPE_READ;
+  else if (io_type_string == "write")
+    return IO_TYPE_WRITE;
+  else
+    return IO_TYPE_NUM;
+}
+
+void validate(boost::any& v, const std::vector<std::string>& values,
+              IOType *target_type, int) {
+  po::validators::check_first_occurrence(v);
+  const std::string &s = po::validators::get_single_string(values);
+  io_type_t io_type = get_io_type(s);
+  if (io_type >= IO_TYPE_NUM)
+    throw po::validation_error(po::validation_error::invalid_option_value);
+  else
+    v = boost::any(io_type);
+}
+
+} // anonymous namespace
+
+static void rbd_bencher_completion(void *c, void *pc);
+struct rbd_bencher;
+
+struct bencher_completer {
+  rbd_bencher *bencher;
+  bufferlist *bl;
+
+public:
+  bencher_completer(rbd_bencher *bencher, bufferlist *bl)
+    : bencher(bencher), bl(bl)
+  { }
+
+  ~bencher_completer()
+  {
+    if (bl)
+      delete bl;
+  }
+};
+
+struct rbd_bencher {
+  librbd::Image *image;
+  Mutex lock;
+  Cond cond;
+  int in_flight;
+  io_type_t io_type;
+  uint64_t io_size;
+  bufferlist write_bl;
+
+  explicit rbd_bencher(librbd::Image *i, io_type_t io_type, uint64_t io_size)
+    : image(i),
+      lock("rbd_bencher::lock"),
+      in_flight(0),
+      io_type(io_type),
+      io_size(io_size)
+  {
+    if (io_type == IO_TYPE_WRITE) {
+      bufferptr bp(io_size);
+      memset(bp.c_str(), rand() & 0xff, io_size);
+      write_bl.push_back(bp);
+    }
+  }
+
+
+  bool start_io(int max, uint64_t off, uint64_t len, int op_flags)
+  {
+    {
+      Mutex::Locker l(lock);
+      if (in_flight >= max)
+        return false;
+      in_flight++;
+    }
+
+    librbd::RBD::AioCompletion *c;
+    if (io_type == IO_TYPE_READ) {
+      bufferlist *read_bl = new bufferlist();
+      c = new librbd::RBD::AioCompletion((void *)(new bencher_completer(this, read_bl)),
+                                        rbd_bencher_completion);
+      image->aio_read2(off, len, *read_bl, c, op_flags);
+    } else if (io_type == IO_TYPE_WRITE) {
+      c = new librbd::RBD::AioCompletion((void *)(new bencher_completer(this, NULL)),
+                                        rbd_bencher_completion);
+      image->aio_write2(off, len, write_bl, c, op_flags);
+    } else {
+      assert(0 == "Invalid io_type");
+    }
+    //cout << "start " << c << " at " << off << "~" << len << std::endl;
+    return true;
+  }
+
+  void wait_for(int max) {
+    Mutex::Locker l(lock);
+    while (in_flight > max) {
+      utime_t dur;
+      dur.set_from_double(.2);
+      cond.WaitInterval(g_ceph_context, lock, dur);
+    }
+  }
+
+};
+
+void rbd_bencher_completion(void *vc, void *pc)
+{
+  librbd::RBD::AioCompletion *c = (librbd::RBD::AioCompletion *)vc;
+  bencher_completer *bc = static_cast<bencher_completer *>(pc);
+  rbd_bencher *b = bc->bencher;
+  //cout << "complete " << c << std::endl;
+  int ret = c->get_return_value();
+  if (b->io_type == IO_TYPE_WRITE && ret != 0) {
+    cout << "write error: " << cpp_strerror(ret) << std::endl;
+    exit(ret < 0 ? -ret : ret);
+  } else if (b->io_type == IO_TYPE_READ && (unsigned int)ret != b->io_size) {
+    cout << "read error: " << cpp_strerror(ret) << std::endl;
+    exit(ret < 0 ? -ret : ret);
+  }
+  b->lock.Lock();
+  b->in_flight--;
+  b->cond.Signal();
+  b->lock.Unlock();
+  c->release();
+  delete bc;
+}
+
+int do_bench(librbd::Image& image, io_type_t io_type,
+                  uint64_t io_size, uint64_t io_threads,
+                  uint64_t io_bytes, bool random)
+{
+  rbd_bencher b(&image, io_type, io_size);
+
+  std::cout << "bench "
+       << " type " << (io_type == IO_TYPE_READ ? "read" : "write")
+       << " io_size " << io_size
+       << " io_threads " << io_threads
+       << " bytes " << io_bytes
+       << " pattern " << (random ? "random" : "sequential")
+       << std::endl;
+
+  srand(time(NULL) % (unsigned long) -1);
+
+  utime_t start = ceph_clock_now(NULL);
+  utime_t last;
+  unsigned ios = 0;
+
+  uint64_t size = 0;
+  image.size(&size);
+
+  vector<uint64_t> thread_offset;
+  uint64_t i;
+  uint64_t start_pos;
+
+  // disturb all thread's offset, used by seq IO
+  for (i = 0; i < io_threads; i++) {
+    start_pos = (rand() % (size / io_size)) * io_size;
+    thread_offset.push_back(start_pos);
+  }
+
+  const int WINDOW_SIZE = 5;
+  typedef boost::accumulators::accumulator_set<
+    double, boost::accumulators::stats<
+      boost::accumulators::tag::rolling_sum> > RollingSum;
+
+  RollingSum time_acc(
+    boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE);
+  RollingSum ios_acc(
+    boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE);
+  RollingSum off_acc(
+    boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE);
+  uint64_t cur_ios = 0;
+  uint64_t cur_off = 0;
+
+  int op_flags;
+  if  (random) {
+    op_flags = LIBRADOS_OP_FLAG_FADVISE_RANDOM;
+  } else {
+    op_flags = LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL;
+  }
+
+  printf("  SEC       OPS   OPS/SEC   BYTES/SEC\n");
+  uint64_t off;
+  for (off = 0; off < io_bytes; ) {
+    b.wait_for(io_threads - 1);
+    i = 0;
+    while (i < io_threads && off < io_bytes) {
+      if (random) {
+        thread_offset[i] = (rand() % (size / io_size)) * io_size;
+      } else {
+        thread_offset[i] += io_size;
+        if (thread_offset[i] + io_size > size)
+          thread_offset[i] = 0;
+      }
+
+      if (!b.start_io(io_threads, thread_offset[i], io_size, op_flags))
+        break;
+
+      ++i;
+      ++ios;
+      off += io_size;
+
+      ++cur_ios;
+      cur_off += io_size;
+    }
+
+    utime_t now = ceph_clock_now(NULL);
+    utime_t elapsed = now - start;
+    if (last.is_zero()) {
+      last = elapsed;
+    } else if (elapsed.sec() != last.sec()) {
+      time_acc(elapsed - last);
+      ios_acc(static_cast<double>(cur_ios));
+      off_acc(static_cast<double>(cur_off));
+      cur_ios = 0;
+      cur_off = 0;
+
+      double time_sum = boost::accumulators::rolling_sum(time_acc);
+      printf("%5d  %8d  %8.2lf  %8.2lf\n",
+             (int)elapsed,
+             (int)(ios - io_threads),
+             boost::accumulators::rolling_sum(ios_acc) / time_sum,
+             boost::accumulators::rolling_sum(off_acc) / time_sum);
+      last = elapsed;
+    }
+  }
+  b.wait_for(0);
+  int r = image.flush();
+  if (r < 0) {
+    std::cerr << "Error flushing data at the end: " << cpp_strerror(r)
+              << std::endl;
+  }
+
+  utime_t now = ceph_clock_now(NULL);
+  double elapsed = now - start;
+
+  printf("elapsed: %5d  ops: %8d  ops/sec: %8.2lf  bytes/sec: %8.2lf\n",
+         (int)elapsed, ios, (double)ios / elapsed, (double)off / elapsed);
+
+  return 0;
+}
+
+void add_bench_common_options(po::options_description *positional,
+                             po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+
+  options->add_options()
+    ("io-size", po::value<Size>(), "IO size (in B/K/M/G/T)")
+    ("io-threads", po::value<uint32_t>(), "ios in flight")
+    ("io-total", po::value<Size>(), "total size for IO (in B/K/M/G/T)")
+    ("io-pattern", po::value<IOPattern>(), "IO pattern (rand or seq)");
+}
+
+void get_arguments_for_write(po::options_description *positional,
+                             po::options_description *options) {
+  add_bench_common_options(positional, options);
+}
+
+void get_arguments_for_bench(po::options_description *positional,
+                             po::options_description *options) {
+  add_bench_common_options(positional, options);
+
+  options->add_options()
+    ("io-type", po::value<IOType>()->required(), "IO type (read or write)");
+}
+
+int bench_execute(const po::variables_map &vm, io_type_t bench_io_type) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  utils::SnapshotPresence snap_presence = utils::SNAPSHOT_PRESENCE_NONE;
+  if (bench_io_type == IO_TYPE_READ)
+    snap_presence = utils::SNAPSHOT_PRESENCE_PERMITTED;
+
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, snap_presence, utils::SPEC_VALIDATION_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  uint64_t bench_io_size;
+  if (vm.count("io-size")) {
+    bench_io_size = vm["io-size"].as<uint64_t>();
+  } else {
+    bench_io_size = 4096;
+  }
+
+  uint32_t bench_io_threads;
+  if (vm.count("io-threads")) {
+    bench_io_threads = vm["io-threads"].as<uint32_t>();
+  } else {
+    bench_io_threads = 16;
+  }
+
+  uint64_t bench_bytes;
+  if (vm.count("io-total")) {
+    bench_bytes = vm["io-total"].as<uint64_t>();
+  } else {
+    bench_bytes = 1 << 30;
+  }
+
+  bool bench_random;
+  if (vm.count("io-pattern")) {
+    bench_random = vm["io-pattern"].as<bool>();
+  } else {
+    bench_random = false;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false, &rados,
+                                 &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  r = do_bench(image, bench_io_type, bench_io_size, bench_io_threads,
+                    bench_bytes, bench_random);
+  if (r < 0) {
+    std::cerr << "do_bench failed: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
+  return 0;
+}
+
+int execute_for_write(const po::variables_map &vm) {
+  std::cerr << "rbd: bench-write is deprecated, use rbd bench --io-type write ..." << std::endl;
+  return bench_execute(vm, IO_TYPE_WRITE);
+}
+
+int execute_for_bench(const po::variables_map &vm) {
+  io_type_t bench_io_type;
+  if (vm.count("io-type")) {
+    bench_io_type = vm["io-type"].as<io_type_t>();
+  } else {
+    std::cerr << "rbd: --io-type must be specified." << std::endl;
+    return -EINVAL;
+  }
+
+  return bench_execute(vm, bench_io_type);
+}
+
+Shell::Action action_write(
+  {"bench-write"}, {}, "Simple write benchmark. (Deprecated, please use `rbd bench --io-type write` instead.)",
+                  "", &get_arguments_for_write, &execute_for_write, false);
+
+Shell::Action action_bench(
+  {"bench"}, {}, "Simple benchmark.", "", &get_arguments_for_bench, &execute_for_bench);
+
+} // namespace bench
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/BenchWrite.cc b/src/tools/rbd/action/BenchWrite.cc
deleted file mode 100644 (file)
index 6691a28..0000000
+++ /dev/null
@@ -1,310 +0,0 @@
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-
-#include "tools/rbd/ArgumentTypes.h"
-#include "tools/rbd/Shell.h"
-#include "tools/rbd/Utils.h"
-#include "common/errno.h"
-#include "common/strtol.h"
-#include "common/Cond.h"
-#include "common/Mutex.h"
-#include <iostream>
-#include <boost/accumulators/accumulators.hpp>
-#include <boost/accumulators/statistics/stats.hpp>
-#include <boost/accumulators/statistics/rolling_sum.hpp>
-#include <boost/program_options.hpp>
-
-namespace rbd {
-namespace action {
-namespace bench_write {
-
-namespace at = argument_types;
-namespace po = boost::program_options;
-
-namespace {
-
-struct Size {};
-struct IOPattern {};
-
-void validate(boost::any& v, const std::vector<std::string>& values,
-              Size *target_type, int) {
-  po::validators::check_first_occurrence(v);
-  const std::string &s = po::validators::get_single_string(values);
-
-  std::string parse_error;
-  uint64_t size = strict_sistrtoll(s.c_str(), &parse_error);
-  if (!parse_error.empty()) {
-    throw po::validation_error(po::validation_error::invalid_option_value);
-  }
-  v = boost::any(size);
-}
-
-void validate(boost::any& v, const std::vector<std::string>& values,
-              IOPattern *target_type, int) {
-  po::validators::check_first_occurrence(v);
-  const std::string &s = po::validators::get_single_string(values);
-  if (s == "rand") {
-    v = boost::any(true);
-  } else if (s == "seq") {
-    v = boost::any(false);
-  } else {
-    throw po::validation_error(po::validation_error::invalid_option_value);
-  }
-
-}
-
-} // anonymous namespace
-
-static void rbd_bencher_completion(void *c, void *pc);
-struct rbd_bencher;
-
-struct rbd_bencher {
-  librbd::Image *image;
-  Mutex lock;
-  Cond cond;
-  int in_flight;
-
-  explicit rbd_bencher(librbd::Image *i)
-    : image(i),
-      lock("rbd_bencher::lock"),
-      in_flight(0)
-  { }
-
-  bool start_write(int max, uint64_t off, uint64_t len, bufferlist& bl,
-                   int op_flags)
-  {
-    {
-      Mutex::Locker l(lock);
-      if (in_flight >= max)
-        return false;
-      in_flight++;
-    }
-    librbd::RBD::AioCompletion *c =
-      new librbd::RBD::AioCompletion((void *)this, rbd_bencher_completion);
-    image->aio_write2(off, len, bl, c, op_flags);
-    //cout << "start " << c << " at " << off << "~" << len << std::endl;
-    return true;
-  }
-
-  void wait_for(int max) {
-    Mutex::Locker l(lock);
-    while (in_flight > max) {
-      utime_t dur;
-      dur.set_from_double(.2);
-      cond.WaitInterval(g_ceph_context, lock, dur);
-    }
-  }
-
-};
-
-void rbd_bencher_completion(void *vc, void *pc)
-{
-  librbd::RBD::AioCompletion *c = (librbd::RBD::AioCompletion *)vc;
-  rbd_bencher *b = static_cast<rbd_bencher *>(pc);
-  //cout << "complete " << c << std::endl;
-  int ret = c->get_return_value();
-  if (ret != 0) {
-    cout << "write error: " << cpp_strerror(ret) << std::endl;
-    exit(ret < 0 ? -ret : ret);
-  }
-  b->lock.Lock();
-  b->in_flight--;
-  b->cond.Signal();
-  b->lock.Unlock();
-  c->release();
-}
-
-int do_bench_write(librbd::Image& image, uint64_t io_size,
-                   uint64_t io_threads, uint64_t io_bytes,
-                   bool random)
-{
-  rbd_bencher b(&image);
-
-  std::cout << "bench-write "
-       << " io_size " << io_size
-       << " io_threads " << io_threads
-       << " bytes " << io_bytes
-       << " pattern " << (random ? "random" : "sequential")
-       << std::endl;
-
-  srand(time(NULL) % (unsigned long) -1);
-
-  bufferptr bp(io_size);
-  memset(bp.c_str(), rand() & 0xff, io_size);
-  bufferlist bl;
-  bl.push_back(bp);
-
-  utime_t start = ceph_clock_now(NULL);
-  utime_t last;
-  unsigned ios = 0;
-
-  uint64_t size = 0;
-  image.size(&size);
-
-  vector<uint64_t> thread_offset;
-  uint64_t i;
-  uint64_t start_pos;
-
-  // disturb all thread's offset, used by seq write
-  for (i = 0; i < io_threads; i++) {
-    start_pos = (rand() % (size / io_size)) * io_size;
-    thread_offset.push_back(start_pos);
-  }
-
-  const int WINDOW_SIZE = 5;
-  typedef boost::accumulators::accumulator_set<
-    double, boost::accumulators::stats<
-      boost::accumulators::tag::rolling_sum> > RollingSum;
-
-  RollingSum time_acc(
-    boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE);
-  RollingSum ios_acc(
-    boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE);
-  RollingSum off_acc(
-    boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE);
-  uint64_t cur_ios = 0;
-  uint64_t cur_off = 0;
-
-  int op_flags;
-  if  (random) {
-    op_flags = LIBRADOS_OP_FLAG_FADVISE_RANDOM;
-  } else {
-    op_flags = LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL;
-  }
-
-  printf("  SEC       OPS   OPS/SEC   BYTES/SEC\n");
-  uint64_t off;
-  for (off = 0; off < io_bytes; ) {
-    b.wait_for(io_threads - 1);
-    i = 0;
-    while (i < io_threads && off < io_bytes) {
-      if (random) {
-        thread_offset[i] = (rand() % (size / io_size)) * io_size;
-      } else {
-        thread_offset[i] += io_size;
-        if (thread_offset[i] + io_size > size)
-          thread_offset[i] = 0;
-      }
-
-      if (!b.start_write(io_threads, thread_offset[i], io_size, bl, op_flags))
-        break;
-
-      ++i;
-      ++ios;
-      off += io_size;
-
-      ++cur_ios;
-      cur_off += io_size;
-    }
-
-    utime_t now = ceph_clock_now(NULL);
-    utime_t elapsed = now - start;
-    if (last.is_zero()) {
-      last = elapsed;
-    } else if (elapsed.sec() != last.sec()) {
-      time_acc(elapsed - last);
-      ios_acc(static_cast<double>(cur_ios));
-      off_acc(static_cast<double>(cur_off));
-      cur_ios = 0;
-      cur_off = 0;
-
-      double time_sum = boost::accumulators::rolling_sum(time_acc);
-      printf("%5d  %8d  %8.2lf  %8.2lf\n",
-             (int)elapsed,
-             (int)(ios - io_threads),
-             boost::accumulators::rolling_sum(ios_acc) / time_sum,
-             boost::accumulators::rolling_sum(off_acc) / time_sum);
-      last = elapsed;
-    }
-  }
-  b.wait_for(0);
-  int r = image.flush();
-  if (r < 0) {
-    std::cerr << "Error flushing data at the end: " << cpp_strerror(r)
-              << std::endl;
-  }
-
-  utime_t now = ceph_clock_now(NULL);
-  double elapsed = now - start;
-
-  printf("elapsed: %5d  ops: %8d  ops/sec: %8.2lf  bytes/sec: %8.2lf\n",
-         (int)elapsed, ios, (double)ios / elapsed, (double)off / elapsed);
-
-  return 0;
-}
-
-void get_arguments(po::options_description *positional,
-                   po::options_description *options) {
-  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
-  // TODO
-  options->add_options()
-    ("io-size", po::value<Size>(), "write size (in B/K/M/G/T)")
-    ("io-threads", po::value<uint32_t>(), "ios in flight")
-    ("io-total", po::value<Size>(), "total size to write (in B/K/M/G/T)")
-    ("io-pattern", po::value<IOPattern>(), "write pattern (rand or seq)");
-}
-
-int execute(const po::variables_map &vm) {
-  size_t arg_index = 0;
-  std::string pool_name;
-  std::string image_name;
-  std::string snap_name;
-  int r = utils::get_pool_image_snapshot_names(
-    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
-    &snap_name, utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_NONE);
-  if (r < 0) {
-    return r;
-  }
-
-  uint64_t bench_io_size;
-  if (vm.count("io-size")) {
-    bench_io_size = vm["io-size"].as<uint64_t>();
-  } else {
-    bench_io_size = 4096;
-  }
-
-  uint32_t bench_io_threads;
-  if (vm.count("io-threads")) {
-    bench_io_threads = vm["io-threads"].as<uint32_t>();
-  } else {
-    bench_io_threads = 16;
-  }
-
-  uint64_t bench_bytes;
-  if (vm.count("io-total")) {
-    bench_bytes = vm["io-total"].as<uint64_t>();
-  } else {
-    bench_bytes = 1 << 30;
-  }
-
-  bool bench_random;
-  if (vm.count("io-pattern")) {
-    bench_random = vm["io-pattern"].as<bool>();
-  } else {
-    bench_random = false;
-  }
-
-  librados::Rados rados;
-  librados::IoCtx io_ctx;
-  librbd::Image image;
-  r = utils::init_and_open_image(pool_name, image_name, "", false, &rados,
-                                 &io_ctx, &image);
-  if (r < 0) {
-    return r;
-  }
-
-  r = do_bench_write(image, bench_io_size, bench_io_threads, bench_bytes,
-                     bench_random);
-  if (r < 0) {
-    std::cerr << "bench-write failed: " << cpp_strerror(r) << std::endl;
-    return r;
-  }
-  return 0;
-}
-
-Shell::Action action(
-  {"bench-write"}, {}, "Simple write benchmark.", "", &get_arguments, &execute, false);
-
-} // namespace bench_write
-} // namespace action
-} // namespace rbd