]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "librbd/managed_lock/BreakRequest.h" | |
5 | #include "common/dout.h" | |
6 | #include "common/errno.h" | |
7 | #include "common/WorkQueue.h" | |
8 | #include "include/stringify.h" | |
9 | #include "cls/lock/cls_lock_client.h" | |
10 | #include "cls/lock/cls_lock_types.h" | |
11 | #include "librbd/ImageCtx.h" | |
12 | #include "librbd/Utils.h" | |
13 | ||
14 | #define dout_subsys ceph_subsys_rbd | |
15 | #undef dout_prefix | |
16 | #define dout_prefix *_dout << "librbd::managed_lock::BreakRequest: " << this \ | |
17 | << " " << __func__ << ": " | |
18 | ||
19 | namespace librbd { | |
20 | namespace managed_lock { | |
21 | ||
22 | using util::create_context_callback; | |
23 | using util::create_rados_callback; | |
24 | ||
25 | namespace { | |
26 | ||
27 | struct C_BlacklistClient : public Context { | |
28 | librados::IoCtx &ioctx; | |
29 | std::string locker_address; | |
30 | uint32_t expire_seconds; | |
31 | Context *on_finish; | |
32 | ||
33 | C_BlacklistClient(librados::IoCtx &ioctx, const std::string &locker_address, | |
34 | uint32_t expire_seconds, Context *on_finish) | |
35 | : ioctx(ioctx), locker_address(locker_address), | |
36 | expire_seconds(expire_seconds), on_finish(on_finish) { | |
37 | } | |
38 | ||
39 | void finish(int r) override { | |
40 | librados::Rados rados(ioctx); | |
41 | r = rados.blacklist_add(locker_address, expire_seconds); | |
42 | on_finish->complete(r); | |
43 | } | |
44 | }; | |
45 | ||
46 | } // anonymous namespace | |
47 | ||
48 | template <typename I> | |
49 | BreakRequest<I>::BreakRequest(librados::IoCtx& ioctx, ContextWQ *work_queue, | |
50 | const std::string& oid, const Locker &locker, | |
51 | bool blacklist_locker, | |
52 | uint32_t blacklist_expire_seconds, | |
53 | bool force_break_lock, Context *on_finish) | |
54 | : m_ioctx(ioctx), m_cct(reinterpret_cast<CephContext *>(m_ioctx.cct())), | |
55 | m_work_queue(work_queue), m_oid(oid), m_locker(locker), | |
56 | m_blacklist_locker(blacklist_locker), | |
57 | m_blacklist_expire_seconds(blacklist_expire_seconds), | |
58 | m_force_break_lock(force_break_lock), m_on_finish(on_finish) { | |
59 | } | |
60 | ||
61 | template <typename I> | |
62 | void BreakRequest<I>::send() { | |
63 | send_get_watchers(); | |
64 | } | |
65 | ||
66 | template <typename I> | |
67 | void BreakRequest<I>::send_get_watchers() { | |
68 | ldout(m_cct, 10) << dendl; | |
69 | ||
70 | librados::ObjectReadOperation op; | |
71 | op.list_watchers(&m_watchers, &m_watchers_ret_val); | |
72 | ||
73 | using klass = BreakRequest<I>; | |
74 | librados::AioCompletion *rados_completion = | |
75 | create_rados_callback<klass, &klass::handle_get_watchers>(this); | |
76 | m_out_bl.clear(); | |
77 | int r = m_ioctx.aio_operate(m_oid, rados_completion, &op, &m_out_bl); | |
78 | assert(r == 0); | |
79 | rados_completion->release(); | |
80 | } | |
81 | ||
82 | template <typename I> | |
83 | void BreakRequest<I>::handle_get_watchers(int r) { | |
84 | ldout(m_cct, 10) << "r=" << r << dendl; | |
85 | ||
86 | if (r == 0) { | |
87 | r = m_watchers_ret_val; | |
88 | } | |
89 | if (r < 0) { | |
90 | lderr(m_cct) << "failed to retrieve watchers: " << cpp_strerror(r) | |
91 | << dendl; | |
92 | finish(r); | |
93 | return; | |
94 | } | |
95 | ||
96 | bool found_alive_locker = false; | |
97 | for (auto &watcher : m_watchers) { | |
98 | ldout(m_cct, 20) << "watcher=[" | |
99 | << "addr=" << watcher.addr << ", " | |
100 | << "entity=client." << watcher.watcher_id << "]" << dendl; | |
101 | ||
102 | if ((strncmp(m_locker.address.c_str(), | |
103 | watcher.addr, sizeof(watcher.addr)) == 0) && | |
104 | (m_locker.handle == watcher.cookie)) { | |
105 | ldout(m_cct, 10) << "lock owner is still alive" << dendl; | |
106 | found_alive_locker = true; | |
107 | } | |
108 | } | |
109 | ||
110 | if (!m_force_break_lock && found_alive_locker) { | |
111 | finish(-EAGAIN); | |
112 | return; | |
113 | } | |
114 | ||
115 | send_blacklist(); | |
116 | } | |
117 | ||
118 | template <typename I> | |
119 | void BreakRequest<I>::send_blacklist() { | |
120 | if (!m_blacklist_locker) { | |
121 | send_break_lock(); | |
122 | return; | |
123 | } | |
124 | ||
125 | entity_name_t entity_name = entity_name_t::CLIENT(m_ioctx.get_instance_id()); | |
126 | ldout(m_cct, 10) << "local entity=" << entity_name << ", " | |
127 | << "locker entity=" << m_locker.entity << dendl; | |
128 | ||
129 | if (m_locker.entity == entity_name) { | |
130 | lderr(m_cct) << "attempting to self-blacklist" << dendl; | |
131 | finish(-EINVAL); | |
132 | return; | |
133 | } | |
134 | ||
135 | // TODO: need async version of RadosClient::blacklist_add | |
136 | using klass = BreakRequest<I>; | |
137 | Context *ctx = create_context_callback<klass, &klass::handle_blacklist>( | |
138 | this); | |
139 | m_work_queue->queue(new C_BlacklistClient(m_ioctx, m_locker.address, | |
140 | m_blacklist_expire_seconds, ctx), | |
141 | 0); | |
142 | } | |
143 | ||
144 | template <typename I> | |
145 | void BreakRequest<I>::handle_blacklist(int r) { | |
146 | ldout(m_cct, 10) << "r=" << r << dendl; | |
147 | ||
148 | if (r < 0) { | |
149 | lderr(m_cct) << "failed to blacklist lock owner: " << cpp_strerror(r) | |
150 | << dendl; | |
151 | finish(r); | |
152 | return; | |
153 | } | |
154 | send_break_lock(); | |
155 | } | |
156 | ||
157 | template <typename I> | |
158 | void BreakRequest<I>::send_break_lock() { | |
159 | ldout(m_cct, 10) << dendl; | |
160 | ||
161 | librados::ObjectWriteOperation op; | |
162 | rados::cls::lock::break_lock(&op, RBD_LOCK_NAME, m_locker.cookie, | |
163 | m_locker.entity); | |
164 | ||
165 | using klass = BreakRequest<I>; | |
166 | librados::AioCompletion *rados_completion = | |
167 | create_rados_callback<klass, &klass::handle_break_lock>(this); | |
168 | int r = m_ioctx.aio_operate(m_oid, rados_completion, &op); | |
169 | assert(r == 0); | |
170 | rados_completion->release(); | |
171 | } | |
172 | ||
173 | template <typename I> | |
174 | void BreakRequest<I>::handle_break_lock(int r) { | |
175 | ldout(m_cct, 10) << "r=" << r << dendl; | |
176 | ||
177 | if (r < 0 && r != -ENOENT) { | |
178 | lderr(m_cct) << "failed to break lock: " << cpp_strerror(r) << dendl; | |
179 | finish(r); | |
180 | return; | |
181 | } | |
182 | ||
183 | finish(0); | |
184 | } | |
185 | ||
186 | template <typename I> | |
187 | void BreakRequest<I>::finish(int r) { | |
188 | ldout(m_cct, 10) << "r=" << r << dendl; | |
189 | ||
190 | m_on_finish->complete(r); | |
191 | delete this; | |
192 | } | |
193 | ||
194 | } // namespace managed_lock | |
195 | } // namespace librbd | |
196 | ||
197 | template class librbd::managed_lock::BreakRequest<librbd::ImageCtx>; |