]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
neorados: Go through linger_cancel on `io_context` shutdown
authorAdam C. Emerson <aemerson@redhat.com>
Fri, 6 Mar 2026 00:53:17 +0000 (19:53 -0500)
committerAdam C. Emerson <aemerson@redhat.com>
Thu, 14 May 2026 23:17:47 +0000 (19:17 -0400)
Rather than just dropping the reference, clean up the linger operation
properly within Objecter. Also, clear out handlers before
relinquishing reference to avoid use-after-free.

Fixes: https://tracker.ceph.com/issues/75164
Signed-off-by: Adam C. Emerson <aemerson@redhat.com>
(cherry picked from commit f5affe06676bec2f32f856bce85b5d78932d807f)

Conflicts:
qa/workunits/rados/test.sh
 - Just drop the `watch_leak` test on teuthology since it's of
   marginal utility and not worth backporting all of
   <https://github.com/ceph/ceph/pull/64219>.

Fixes: https://tracker.ceph.com/issues/76434
Signed-off-by: Adam C. Emerson <aemerson@redhat.com>
src/neorados/RADOS.cc
src/test/neorados/CMakeLists.txt
src/test/neorados/leak_watch_notify.cc [new file with mode: 0644]

index 8959754c15563816eb76d025ef8986379a97e264..6aeaddbf7df175e61f1f23669fffdee454b60040 100644 (file)
@@ -1408,12 +1408,23 @@ class Notifier : public async::service_list_base_hook {
   std::shared_ptr<detail::Client> neoref;
 
   void service_shutdown() {
-    if (neoref) {
-      neoref = nullptr;
-    }
-    linger_op.reset();
+    // Ensure neorados object and operation live to the end of the
+    // function.
+    auto localop = std::move(linger_op);
+    [[maybe_unused]] auto localneo = std::move(neoref);
     std::unique_lock l(m);
     handlers.clear();
+    l.unlock();
+    if (localop) {
+      // We are being taken down and will execute no more
+      // handlers. Call `linger_cancel` to clean up properly in
+      // Objecter. (It doesn't call out to the OSD or anything.)
+      //
+      // Removal and decrement are guarded by `linger_op->canceled`
+      // so there's no risk of an underflow.
+      localop->objecter->linger_cancel(localop.get());
+    }
+    // The Notifier object is freed by the `linger_cancel` above.
   }
 
 public:
index 968ef609cdca7d546ff9696c7b859a3b65698c76..432b0336425f9037cc1c76198df73d75c67c4409 100644 (file)
@@ -233,3 +233,10 @@ target_link_libraries(ceph_test_neorados_watch_notify
 install(TARGETS
   ceph_test_neorados_watch_notify
   DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+add_executable(ceph_test_neorados_leak_watch_notify leak_watch_notify.cc)
+target_link_libraries(ceph_test_neorados_leak_watch_notify global libneorados
+  neoradostest-support ${unittest_libs})
+install(TARGETS
+  ceph_test_neorados_leak_watch_notify
+  DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/test/neorados/leak_watch_notify.cc b/src/test/neorados/leak_watch_notify.cc
new file mode 100644 (file)
index 0000000..407e622
--- /dev/null
@@ -0,0 +1,50 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
+// vim: ts=8 sw=2 sts=2 expandtab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2026 Contributors to the Ceph Project
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ *
+ */
+
+#include <iostream>
+#include <optional>
+
+#include <boost/asio.hpp>
+#include <boost/system/error_code.hpp>
+
+#include "include/neorados/RADOS.hpp"
+
+#include "common_tests.h"
+
+namespace asio = boost::asio;
+
+using boost::system::error_code;
+
+// Intentionally get a watch and don't unwatch it to make sure we call
+// `linger_cancel` and don't leak.
+
+int main()
+{    
+  asio::io_context c;
+  std::string_view oid = "obj";
+
+  std::optional<neorados::RADOS> rados;
+  neorados::IOContext pool;
+  neorados::RADOS::Builder{}.build(c, [&](error_code ec, neorados::RADOS r_) {
+    rados = std::move(r_);
+    create_pool(*rados, get_temp_pool_name(),
+               [&](error_code ec, int64_t poolid) {
+                 pool.set_pool(poolid);
+                 rados->watch(oid, pool, [&](error_code, std::uint64_t cookie) {
+                   std::cout << "Got watch: " << cookie << std::endl;
+                 });
+               });
+  });
+  c.run();
+}