]> git.proxmox.com Git - ceph.git/blob - ceph/src/crimson/osd/ops_executer.h
import 15.2.0 Octopus source
[ceph.git] / ceph / src / crimson / osd / ops_executer.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #pragma once
5
6 #include <memory>
7 #include <type_traits>
8 #include <boost/intrusive_ptr.hpp>
9 #include <boost/smart_ptr/intrusive_ref_counter.hpp>
10 #include <boost/smart_ptr/local_shared_ptr.hpp>
11 #include <seastar/core/chunked_fifo.hh>
12 #include <seastar/core/future.hh>
13 #include <seastar/core/shared_future.hh>
14
15 #include "common/dout.h"
16 #include "crimson/net/Fwd.h"
17 #include "os/Transaction.h"
18 #include "osd/osd_types.h"
19 #include "crimson/osd/object_context.h"
20
21 #include "crimson/common/errorator.h"
22 #include "crimson/common/type_helpers.h"
23 #include "crimson/osd/osd_operations/client_request.h"
24 #include "crimson/osd/osd_operations/peering_event.h"
25 #include "crimson/osd/shard_services.h"
26 #include "crimson/osd/osdmap_gate.h"
27
28 #include "crimson/osd/pg.h"
29 #include "crimson/osd/pg_backend.h"
30 #include "crimson/osd/exceptions.h"
31
32 #include "messages/MOSDOp.h"
33
34 class PGLSFilter;
35 class OSDOp;
36
37 namespace crimson::osd {
38 class OpsExecuter {
39 using call_errorator = crimson::errorator<
40 crimson::stateful_ec,
41 crimson::ct_error::enoent,
42 crimson::ct_error::invarg,
43 crimson::ct_error::permission_denied,
44 crimson::ct_error::operation_not_supported,
45 crimson::ct_error::input_output_error>;
46 using read_errorator = PGBackend::read_errorator;
47 using get_attr_errorator = PGBackend::get_attr_errorator;
48 using watch_errorator = crimson::errorator<
49 crimson::ct_error::enoent,
50 crimson::ct_error::invarg,
51 crimson::ct_error::not_connected,
52 crimson::ct_error::timed_out>;
53
54 public:
55 // because OpsExecuter is pretty heavy-weight object we want to ensure
56 // it's not copied nor even moved by accident. Performance is the sole
57 // reason for prohibiting that.
58 OpsExecuter(OpsExecuter&&) = delete;
59 OpsExecuter(const OpsExecuter&) = delete;
60
61 using osd_op_errorator = crimson::compound_errorator_t<
62 call_errorator,
63 read_errorator,
64 get_attr_errorator,
65 watch_errorator,
66 PGBackend::stat_errorator>;
67
68 private:
69 // an operation can be divided into two stages: main and effect-exposing
70 // one. The former is performed immediately on call to `do_osd_op()` while
71 // the later on `submit_changes()` – after successfully processing main
72 // stages of all involved operations. When any stage fails, none of all
73 // scheduled effect-exposing stages will be executed.
74 // when operation requires this division, some variant of `with_effect()`
75 // should be used.
76 struct effect_t {
77 virtual osd_op_errorator::future<> execute() = 0;
78 virtual ~effect_t() = default;
79 };
80
81 ObjectContextRef obc;
82 PG& pg;
83 PGBackend& backend;
84 Ref<MOSDOp> msg;
85 ceph::os::Transaction txn;
86
87 size_t num_read = 0; ///< count read ops
88 size_t num_write = 0; ///< count update ops
89
90 // this gizmo could be wrapped in std::optional for the sake of lazy
91 // initialization. we don't need it for ops that doesn't have effect
92 // TODO: verify the init overhead of chunked_fifo
93 seastar::chunked_fifo<std::unique_ptr<effect_t>> op_effects;
94
95 template <class Context, class MainFunc, class EffectFunc>
96 auto with_effect_on_obc(
97 Context&& ctx,
98 MainFunc&& main_func,
99 EffectFunc&& effect_func);
100
101 call_errorator::future<> do_op_call(class OSDOp& osd_op);
102 watch_errorator::future<> do_op_watch(
103 class OSDOp& osd_op,
104 class ObjectState& os,
105 ceph::os::Transaction& txn);
106 watch_errorator::future<> do_op_watch_subop_watch(
107 class OSDOp& osd_op,
108 class ObjectState& os,
109 ceph::os::Transaction& txn);
110 watch_errorator::future<> do_op_watch_subop_reconnect(
111 class OSDOp& osd_op,
112 class ObjectState& os,
113 ceph::os::Transaction& txn);
114 watch_errorator::future<> do_op_watch_subop_unwatch(
115 class OSDOp& osd_op,
116 class ObjectState& os,
117 ceph::os::Transaction& txn);
118 watch_errorator::future<> do_op_watch_subop_ping(
119 class OSDOp& osd_op,
120 class ObjectState& os,
121 ceph::os::Transaction& txn);
122 watch_errorator::future<> do_op_notify(
123 class OSDOp& osd_op,
124 const class ObjectState& os);
125 watch_errorator::future<> do_op_notify_ack(
126 class OSDOp& osd_op,
127 const class ObjectState& os);
128
129 hobject_t &get_target() const {
130 return obc->obs.oi.soid;
131 }
132
133 template <class Func>
134 auto do_const_op(Func&& f) {
135 // TODO: pass backend as read-only
136 return std::forward<Func>(f)(backend, std::as_const(obc->obs));
137 }
138
139 template <class Func>
140 auto do_read_op(Func&& f) {
141 ++num_read;
142 // TODO: pass backend as read-only
143 return do_const_op(std::forward<Func>(f));
144 }
145
146 template <class Func>
147 auto do_write_op(Func&& f) {
148 ++num_write;
149 return std::forward<Func>(f)(backend, obc->obs, txn);
150 }
151
152 // PG operations are being provided with pg instead of os.
153 template <class Func>
154 auto do_pg_op(Func&& f) {
155 return std::forward<Func>(f)(std::as_const(pg),
156 std::as_const(msg->get_hobj().nspace));
157 }
158
159 decltype(auto) dont_do_legacy_op() {
160 return crimson::ct_error::operation_not_supported::make();
161 }
162
163 public:
164 OpsExecuter(ObjectContextRef obc, PG& pg, Ref<MOSDOp> msg)
165 : obc(std::move(obc)),
166 pg(pg),
167 backend(pg.get_backend()),
168 msg(std::move(msg)) {
169 }
170 OpsExecuter(PG& pg, Ref<MOSDOp> msg)
171 : OpsExecuter{ObjectContextRef(), pg, std::move(msg)}
172 {}
173
174 osd_op_errorator::future<> execute_osd_op(class OSDOp& osd_op);
175 seastar::future<> execute_pg_op(class OSDOp& osd_op);
176
177 template <typename Func>
178 osd_op_errorator::future<> submit_changes(Func&& f) &&;
179
180 const auto& get_message() const {
181 return *msg;
182 }
183 };
184
185 template <class Context, class MainFunc, class EffectFunc>
186 auto OpsExecuter::with_effect_on_obc(
187 Context&& ctx,
188 MainFunc&& main_func,
189 EffectFunc&& effect_func)
190 {
191 using context_t = std::decay_t<Context>;
192 // the language offers implicit conversion to pointer-to-function for
193 // lambda only when it's closureless. We enforce this restriction due
194 // the fact that `submit_changes()` std::moves many executer's parts.
195 using allowed_effect_func_t =
196 seastar::future<> (*)(context_t&&, ObjectContextRef);
197 static_assert(std::is_convertible_v<EffectFunc, allowed_effect_func_t>,
198 "with_effect function is not allowed to capture");
199 struct task_t final : effect_t {
200 context_t ctx;
201 EffectFunc effect_func;
202 ObjectContextRef obc;
203
204 task_t(Context&& ctx, EffectFunc&& effect_func, ObjectContextRef obc)
205 : ctx(std::move(ctx)),
206 effect_func(std::move(effect_func)),
207 obc(std::move(obc)) {
208 }
209 osd_op_errorator::future<> execute() final {
210 return std::move(effect_func)(std::move(ctx), std::move(obc));
211 }
212 };
213 auto task =
214 std::make_unique<task_t>(std::move(ctx), std::move(effect_func), obc);
215 auto& ctx_ref = task->ctx;
216 op_effects.emplace_back(std::move(task));
217 return std::forward<MainFunc>(main_func)(ctx_ref);
218 }
219
220 template <typename Func>
221 OpsExecuter::osd_op_errorator::future<> OpsExecuter::submit_changes(Func&& f) && {
222 if (__builtin_expect(op_effects.empty(), true)) {
223 return std::forward<Func>(f)(std::move(txn), std::move(obc));
224 }
225 return std::forward<Func>(f)(std::move(txn), std::move(obc)).safe_then([this] {
226 // let's do the cleaning of `op_effects` in destructor
227 return crimson::do_for_each(op_effects, [] (auto& op_effect) {
228 return op_effect->execute();
229 });
230 });
231 }
232
233 } // namespace crimson::osd