]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd-nbd: add quiesce/unquiesce hooks
authorMykola Golub <mgolub@suse.com>
Mon, 27 Apr 2020 06:32:19 +0000 (07:32 +0100)
committerMykola Golub <mgolub@suse.com>
Sat, 9 May 2020 11:37:36 +0000 (12:37 +0100)
Signed-off-by: Mykola Golub <mgolub@suse.com>
src/include/config-h.in.cmake
src/test/cli/rbd/help.t
src/tools/rbd/action/Device.cc
src/tools/rbd/action/Ggate.cc
src/tools/rbd/action/Kernel.cc
src/tools/rbd/action/Nbd.cc
src/tools/rbd_nbd/rbd-nbd.cc

index ea550c81e3a0e018828a61f1d6d8093655f72cd9..e26671d3a005df4e770158e39fe9780590cbaf49 100644 (file)
 /* Shared library extension, such as .so, .dll or .dylib */
 #cmakedefine CMAKE_SHARED_LIBRARY_SUFFIX "@CMAKE_SHARED_LIBRARY_SUFFIX@"
 
+/* libexec directory path */
+#cmakedefine CMAKE_INSTALL_LIBEXECDIR "@CMAKE_INSTALL_LIBEXECDIR@"
+
 #endif /* CONFIG_H */
index fc2bc57749cfd7415d049473b755c688a8d5d927..18d40de6724363951488247bfbf6cc56b1788830 100644 (file)
   rbd help device map
   usage: rbd device map [--device-type <device-type>] [--pool <pool>] 
                         [--namespace <namespace>] [--image <image>] 
-                        [--snap <snap>] [--read-only] [--exclusive] 
-                        [--options <options>] 
+                        [--snap <snap>] [--read-only] [--exclusive] [--quiesce] 
+                        [--quiesce-hook <quiesce-hook>] [--options <options>] 
                         <image-or-snap-spec> 
   
   Map an image to a block device.
     --snap arg               snapshot name
     --read-only              map read-only
     --exclusive              disable automatic exclusive lock transitions
+    --quiesce                use quiesce callbacks
+    --quiesce-hook arg       quiesce hook path
     -o [ --options ] arg     device specific options
   
   rbd help device unmap
index 3fdf2ef50aab6695fe9cef9561cd1057c2818160..94864d04c63ee14808d29ca3411d17d635b27353 100644 (file)
@@ -136,7 +136,9 @@ void get_map_arguments(po::options_description *positional,
                                      at::ARGUMENT_MODIFIER_NONE);
   options->add_options()
     ("read-only", po::bool_switch(), "map read-only")
-    ("exclusive", po::bool_switch(), "disable automatic exclusive lock transitions");
+    ("exclusive", po::bool_switch(), "disable automatic exclusive lock transitions")
+    ("quiesce", po::bool_switch(), "use quiesce callbacks")
+    ("quiesce-hook", po::value<std::string>(), "quiesce hook path");
   add_device_specific_options(options);
 }
 
index 61f77be2db3e13cb18ccc0da8929c8602d072f01..74cd57dd5a2d0fc7dd0c9eedc0f9972ae2101c28 100644 (file)
@@ -130,6 +130,10 @@ int execute_map(const po::variables_map &vm,
   }
   args.push_back(img);
 
+  if (vm["quiesce"].as<bool>()) {
+    std::cerr << "rbd: warning: quiesce is not supported" << std::endl;
+  }
+
   if (vm["read-only"].as<bool>()) {
     args.push_back("--read-only");
   }
@@ -138,6 +142,10 @@ int execute_map(const po::variables_map &vm,
     args.push_back("--exclusive");
   }
 
+  if (vm.count("quiesce-hook")) {
+    std::cerr << "rbd: warning: quiesce-hook is not supported" << std::endl;
+  }
+
   if (vm.count("options")) {
     r = parse_options(vm["options"].as<std::vector<std::string>>(), &args);
     if (r < 0) {
index 3ebb5bc1de40b4e3bdae235640b81f607efc59f8..3b6bfd10523dbc05ec7baa6a1aab9dbdbd58f8a4 100644 (file)
@@ -434,6 +434,9 @@ int execute_map(const po::variables_map &vm,
     return r;
   }
 
+  if (vm["quiesce"].as<bool>()) {
+    std::cerr << "rbd: warning: quiesce is not supported" << std::endl;
+  }
   if (vm["read-only"].as<bool>()) {
     put_map_option("rw", "ro");
   }
@@ -441,6 +444,10 @@ int execute_map(const po::variables_map &vm,
     put_map_option("exclusive", "exclusive");
   }
 
+  if (vm.count("quiesce-hook")) {
+    std::cerr << "rbd: warning: quiesce-hook is not supported" << std::endl;
+  }
+
   // parse default options first so they can be overwritten by cli options
   r = parse_map_options(
       g_conf().get_val<std::string>("rbd_default_map_options"));
index 5c55adea4e765f54e67505ec7375d1db41868712..ab62e9a7bf83599a22733a3cbbc2dd2c84fae8dd 100644 (file)
@@ -134,6 +134,10 @@ int execute_map(const po::variables_map &vm,
   }
   args.push_back(img);
 
+  if (vm["quiesce"].as<bool>()) {
+    args.push_back("--quiesce");
+  }
+
   if (vm["read-only"].as<bool>()) {
     args.push_back("--read-only");
   }
@@ -142,6 +146,11 @@ int execute_map(const po::variables_map &vm,
     args.push_back("--exclusive");
   }
 
+  if (vm.count("quiesce-hook")) {
+    args.push_back("--quiesce-hook");
+    args.push_back(vm["quiesce-hook"].as<std::string>());
+  }
+
   if (vm.count("options")) {
     r = parse_options(vm["options"].as<std::vector<std::string>>(), &args);
     if (r < 0) {
index 77d8e82e29979ec64d76708641105fa90c9d4483..a9af90922c98e137d09ccf10ec4c5d8f78d0449d 100644 (file)
@@ -16,6 +16,7 @@
  *
 */
 
+#include "acconfig.h"
 #include "include/int_types.h"
 
 #include <libgen.h>
@@ -46,6 +47,7 @@
 
 #include "common/Formatter.h"
 #include "common/Preforker.h"
+#include "common/SubProcess.h"
 #include "common/TextTable.h"
 #include "common/ceph_argparse.h"
 #include "common/config.h"
@@ -76,6 +78,7 @@ struct Config {
   int timeout = -1;
 
   bool exclusive = false;
+  bool quiesce = false;
   bool readonly = false;
   bool set_max_part = false;
   bool try_netlink = false;
@@ -85,6 +88,7 @@ struct Config {
   std::string imgname;
   std::string snapname;
   std::string devpath;
+  std::string quiesce_hook = CMAKE_INSTALL_LIBEXECDIR "/rbd-nbd/rbd-nbd_quiesce";
 
   std::string format;
   bool pretty_format = false;
@@ -97,10 +101,13 @@ static void usage()
             << "               [options] list-mapped               List mapped nbd devices\n"
             << "Map options:\n"
             << "  --device <device path>  Specify nbd device path (/dev/nbd{num})\n"
-            << "  --read-only             Map read-only\n"
-            << "  --nbds_max <limit>      Override for module param nbds_max\n"
-            << "  --max_part <limit>      Override for module param max_part\n"
             << "  --exclusive             Forbid writes by other clients\n"
+            << "  --max_part <limit>      Override for module param max_part\n"
+            << "  --nbds_max <limit>      Override for module param nbds_max\n"
+            << "  --quiesce               Use quiesce callbacks\n"
+            << "  --quiesce_hook <path>   Specify quiesce hook path\n"
+            << "                          (default: " << Config().quiesce_hook << ")\n"
+            << "  --read-only             Map read-only\n"
             << "  --timeout <seconds>     Set nbd request timeout\n"
             << "  --try-netlink           Use the nbd netlink interface\n"
             << "\n"
@@ -141,18 +148,25 @@ static int parse_args(vector<const char*>& args, std::ostream *err_msg,
                       Command *command, Config *cfg);
 static int netlink_resize(int nbd_index, uint64_t size);
 
+static void run_quiesce_hook(const std::string &quiesce_hook,
+                             const std::string &devpath,
+                             const std::string &command);
+
 class NBDServer
 {
 private:
   int fd;
   librbd::Image &image;
+  Config *cfg;
 
 public:
-  NBDServer(int _fd, librbd::Image& _image)
-    : fd(_fd)
-    , image(_image)
+  NBDServer(int fd, librbd::Image& image, Config *cfg)
+    : fd(fd)
+    , image(image)
+    , cfg(cfg)
     , reader_thread(*this, &NBDServer::reader_entry)
     , writer_thread(*this, &NBDServer::writer_entry)
+    , quiesce_thread(*this, &NBDServer::quiesce_entry)
     , started(false)
   {}
 
@@ -380,6 +394,66 @@ signal:
     dout(20) << __func__ << ": terminated" << dendl;
   }
 
+  bool wait_quiesce() {
+    dout(20) << __func__ << dendl;
+
+    std::unique_lock locker{lock};
+    cond.wait(locker, [this] { return quiesce || terminated; });
+
+    if (terminated) {
+      return false;
+    }
+
+    dout(20) << __func__ << ": got quiesce request" << dendl;
+    return true;
+  }
+
+  void wait_unquiesce() {
+    dout(20) << __func__ << dendl;
+
+    std::unique_lock locker{lock};
+    cond.wait(locker, [this] { return !quiesce || terminated; });
+
+    dout(20) << __func__ << ": got unquiesce request" << dendl;
+  }
+
+  void wait_inflight_io() {
+    uint64_t features = 0;
+    image.features(&features);
+    if ((features & RBD_FEATURE_EXCLUSIVE_LOCK) != 0) {
+      bool is_owner = false;
+      image.is_exclusive_lock_owner(&is_owner);
+      if (!is_owner) {
+        return;
+      }
+    }
+
+    dout(20) << __func__ << dendl;
+
+    int r = image.flush();
+    if (r < 0) {
+      derr << "flush failed: " << cpp_strerror(r) << dendl;
+    }
+  }
+
+  void quiesce_entry()
+  {
+    ceph_assert(cfg->quiesce);
+
+    while (wait_quiesce()) {
+
+      run_quiesce_hook(cfg->quiesce_hook, cfg->devpath, "quiesce");
+
+      wait_inflight_io();
+
+      image.quiesce_complete();
+
+      wait_unquiesce();
+
+      run_quiesce_hook(cfg->quiesce_hook, cfg->devpath, "unquiesce");
+    }
+  }
+
   class ThreadHelper : public Thread
   {
   public:
@@ -399,9 +473,11 @@ signal:
       server.shutdown();
       return NULL;
     }
-  } reader_thread, writer_thread;
+  } reader_thread, writer_thread, quiesce_thread;
 
   bool started;
+  bool quiesce;
+
 public:
   void start()
   {
@@ -412,6 +488,9 @@ public:
 
       reader_thread.create("rbd_reader");
       writer_thread.create("rbd_writer");
+      if (cfg->quiesce) {
+        quiesce_thread.create("rbd_quiesce");
+      }
     }
   }
 
@@ -424,6 +503,28 @@ public:
     disconnect_cond.wait(l);
   }
 
+  void notify_quiesce() {
+    dout(10) << __func__ << dendl;
+
+    ceph_assert(cfg->quiesce);
+
+    std::unique_lock locker{lock};
+    ceph_assert(quiesce == false);
+    quiesce = true;
+    cond.notify_all();
+  }
+
+  void notify_unquiesce() {
+    dout(10) << __func__ << dendl;
+
+    ceph_assert(cfg->quiesce);
+
+    std::unique_lock locker{lock};
+    ceph_assert(quiesce == true);
+    quiesce = false;
+    cond.notify_all();
+  }
+
   ~NBDServer()
   {
     if (started) {
@@ -433,6 +534,9 @@ public:
 
       reader_thread.join();
       writer_thread.join();
+      if (cfg->quiesce) {
+        quiesce_thread.join();
+      }
 
       wait_clean();
 
@@ -470,6 +574,24 @@ std::ostream &operator<<(std::ostream &os, const NBDServer::IOContext &ctx) {
   return os;
 }
 
+class NBDQuiesceWatchCtx : public librbd::QuiesceWatchCtx
+{
+public:
+  NBDQuiesceWatchCtx(NBDServer *server) : server(server) {
+  }
+
+  void handle_quiesce() override {
+    server->notify_quiesce();
+  }
+
+  void handle_unquiesce() override {
+    server->notify_unquiesce();
+  }
+
+private:
+  NBDServer *server;
+};
+
 class NBDWatchCtx : public librbd::UpdateWatchCtx
 {
 private:
@@ -1047,6 +1169,31 @@ static int try_netlink_setup(Config *cfg, int fd, uint64_t size, uint64_t flags)
   return 0;
 }
 
+static void run_quiesce_hook(const std::string &quiesce_hook,
+                             const std::string &devpath,
+                             const std::string &command) {
+  dout(10) << __func__ << ": " << quiesce_hook << " " << devpath << " "
+           << command << dendl;
+
+  SubProcess hook(quiesce_hook.c_str(), SubProcess::CLOSE, SubProcess::PIPE,
+                  SubProcess::PIPE);
+  hook.add_cmd_args(devpath.c_str(), command.c_str(), NULL);
+  bufferlist err;
+  int r = hook.spawn();
+  if (r != 0) {
+    err.append("subprocess spawn failed");
+  } else {
+    err.read_fd(hook.get_stderr(), 16384);
+    r = hook.join();
+  }
+  if (r != 0) {
+    derr << __func__ << ": " << quiesce_hook << " " << devpath << " "
+         << command << " failed: " << err.to_str() << dendl;
+  } else {
+    dout(10) << " succeeded: " << err.to_str() << dendl;
+  }
+}
+
 static void handle_signal(int signum)
 {
   int ret;
@@ -1071,11 +1218,11 @@ static void handle_signal(int signum)
   }
 }
 
-static NBDServer *start_server(int fd, librbd::Image& image)
+static NBDServer *start_server(int fd, librbd::Image& image, Config *cfg)
 {
   NBDServer *server;
 
-  server = new NBDServer(fd, image);
+  server = new NBDServer(fd, image, cfg);
   server->start();
 
   init_async_signal_handler();
@@ -1221,7 +1368,7 @@ static int do_map(int argc, const char *argv[], Config *cfg)
   if (r < 0)
     goto close_fd;
 
-  server = start_server(fd[1], image);
+  server = start_server(fd[1], image, cfg);
 
   use_netlink = cfg->try_netlink;
   if (use_netlink) {
@@ -1250,6 +1397,15 @@ static int do_map(int argc, const char *argv[], Config *cfg)
   }
 
   {
+    NBDQuiesceWatchCtx quiesce_watch_ctx(server);
+    uint64_t quiesce_watch_handle;
+    if (cfg->quiesce) {
+      r = image.quiesce_watch(&quiesce_watch_ctx, &quiesce_watch_handle);
+      if (r < 0) {
+        goto close_nbd;
+      }
+    }
+
     uint64_t handle;
 
     NBDWatchCtx watch_ctx(nbd, nbd_index, use_netlink, io_ctx, image,
@@ -1262,6 +1418,11 @@ static int do_map(int argc, const char *argv[], Config *cfg)
 
     run_server(forker, server, use_netlink);
 
+    if (cfg->quiesce) {
+      r = image.quiesce_unwatch(quiesce_watch_handle);
+      ceph_assert(r == 0);
+    }
+
     r = image.update_unwatch(handle);
     ceph_assert(r == 0);
   }
@@ -1466,6 +1627,10 @@ static int parse_args(vector<const char*>& args, std::ostream *err_msg,
         return -EINVAL;
       }
       cfg->set_max_part = true;
+    } else if (ceph_argparse_flag(args, i, "--quiesce", (char *)NULL)) {
+      cfg->quiesce = true;
+    } else if (ceph_argparse_witharg(args, i, &cfg->quiesce_hook,
+                                     "--quiesce-hook", (char *)NULL)) {
     } else if (ceph_argparse_flag(args, i, "--read-only", (char *)NULL)) {
       cfg->readonly = true;
     } else if (ceph_argparse_flag(args, i, "--exclusive", (char *)NULL)) {