]> git.proxmox.com Git - ceph.git/blame - ceph/src/cls/lock/cls_lock.cc
import 15.2.0 Octopus source
[ceph.git] / ceph / src / cls / lock / cls_lock.cc
CommitLineData
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/** \file
5 *
6 * This is an OSD class that implements methods for object
7 * advisory locking.
8 *
9 */
10
7c673cae 11#include <errno.h>
7c673cae 12#include <map>
11fdf7f2 13#include <sstream>
7c673cae
FG
14
15#include "include/types.h"
16#include "include/utime.h"
17#include "objclass/objclass.h"
18
19#include "common/errno.h"
20#include "common/Clock.h"
21
22#include "cls/lock/cls_lock_types.h"
23#include "cls/lock/cls_lock_ops.h"
24
25#include "global/global_context.h"
26
27#include "include/compat.h"
28
29
30using namespace rados::cls::lock;
31
32
33CLS_VER(1,0)
34CLS_NAME(lock)
35
36#define LOCK_PREFIX "lock."
37
f64942e4
AA
38static int clean_lock(cls_method_context_t hctx)
39{
40 int r = cls_cxx_remove(hctx);
41 if (r < 0)
42 return r;
43
44 return 0;
45}
46
47static int read_lock(cls_method_context_t hctx,
48 const string& name,
49 lock_info_t *lock)
7c673cae
FG
50{
51 bufferlist bl;
52 string key = LOCK_PREFIX;
53 key.append(name);
54
55 int r = cls_cxx_getxattr(hctx, key.c_str(), &bl);
56 if (r < 0) {
57 if (r == -ENODATA) {
58 *lock = lock_info_t();
59 return 0;
60 }
61 if (r != -ENOENT) {
62 CLS_ERR("error reading xattr %s: %d", key.c_str(), r);
63 }
64 return r;
65 }
66
67 try {
11fdf7f2
TL
68 auto it = bl.cbegin();
69 decode(*lock, it);
7c673cae
FG
70 } catch (const buffer::error &err) {
71 CLS_ERR("error decoding %s", key.c_str());
72 return -EIO;
73 }
74
75 /* now trim expired locks */
76
77 utime_t now = ceph_clock_now();
78
79 map<locker_id_t, locker_info_t>::iterator iter = lock->lockers.begin();
80
81 while (iter != lock->lockers.end()) {
7c673cae
FG
82 struct locker_info_t& info = iter->second;
83 if (!info.expiration.is_zero() && info.expiration < now) {
84 CLS_LOG(20, "expiring locker");
f64942e4
AA
85 iter = lock->lockers.erase(iter);
86 } else {
87 ++iter;
7c673cae 88 }
f64942e4 89 }
7c673cae 90
f64942e4
AA
91 if (lock->lockers.empty() && cls_lock_is_ephemeral(lock->lock_type)) {
92 r = clean_lock(hctx);
93 if (r < 0) {
94 CLS_ERR("error, on read, cleaning lock object %s", cpp_strerror(r).c_str());
95 }
7c673cae
FG
96 }
97
98 return 0;
99}
100
101static int write_lock(cls_method_context_t hctx, const string& name, const lock_info_t& lock)
102{
11fdf7f2 103 using ceph::encode;
7c673cae
FG
104 string key = LOCK_PREFIX;
105 key.append(name);
106
107 bufferlist lock_bl;
11fdf7f2 108 encode(lock, lock_bl, cls_get_client_features(hctx));
7c673cae
FG
109
110 int r = cls_cxx_setxattr(hctx, key.c_str(), &lock_bl);
111 if (r < 0)
112 return r;
113
114 return 0;
115}
116
117/**
118 * helper function to add a lock and update disk state.
119 *
120 * Input:
121 * @param name Lock name
122 * @param lock_type Type of lock (exclusive / shared)
123 * @param duration Duration of lock (in seconds). Zero means it doesn't expire.
124 * @param flags lock flags
125 * @param cookie The cookie to set in the lock
126 * @param tag The tag to match with the lock (can only lock with matching tags)
127 * @param lock_description The lock description to set (if not empty)
128 * @param locker_description The locker description
129 *
130 * @return 0 on success, or -errno on failure
131 */
132static int lock_obj(cls_method_context_t hctx,
133 const string& name,
134 ClsLockType lock_type,
135 utime_t duration,
136 const string& description,
137 uint8_t flags,
138 const string& cookie,
139 const string& tag)
140{
f64942e4 141 bool exclusive = cls_lock_is_exclusive(lock_type);
7c673cae 142 lock_info_t linfo;
f64942e4
AA
143 bool fail_if_exists = (flags & LOCK_FLAG_MAY_RENEW) == 0;
144 bool fail_if_does_not_exist = flags & LOCK_FLAG_MUST_RENEW;
7c673cae 145
f64942e4
AA
146 CLS_LOG(20,
147 "requested lock_type=%s fail_if_exists=%d fail_if_does_not_exist=%d",
148 cls_lock_type_str(lock_type), fail_if_exists, fail_if_does_not_exist);
149 if (!cls_lock_is_valid(lock_type)) {
7c673cae 150 return -EINVAL;
f64942e4 151 }
7c673cae
FG
152
153 if (name.empty())
154 return -EINVAL;
155
f64942e4
AA
156 if (!fail_if_exists && fail_if_does_not_exist) {
157 // at most one of LOCK_FLAG_MAY_RENEW and LOCK_FLAG_MUST_RENEW may
158 // be set since they have different implications if the lock does
159 // not already exist
160 return -EINVAL;
161 }
162
7c673cae
FG
163 // see if there's already a locker
164 int r = read_lock(hctx, name, &linfo);
165 if (r < 0 && r != -ENOENT) {
166 CLS_ERR("Could not read lock info: %s", cpp_strerror(r).c_str());
167 return r;
168 }
f64942e4 169
7c673cae
FG
170 map<locker_id_t, locker_info_t>& lockers = linfo.lockers;
171 map<locker_id_t, locker_info_t>::iterator iter;
172
173 locker_id_t id;
174 id.cookie = cookie;
175 entity_inst_t inst;
176 r = cls_get_request_origin(hctx, &inst);
177 id.locker = inst.name;
11fdf7f2 178 ceph_assert(r == 0);
7c673cae
FG
179
180 /* check this early, before we check fail_if_exists, otherwise we might
181 * remove the locker entry and not check it later */
182 if (lockers.size() && tag != linfo.tag) {
183 CLS_LOG(20, "cannot take lock on object, conflicting tag");
184 return -EBUSY;
185 }
186
187 ClsLockType existing_lock_type = linfo.lock_type;
188 CLS_LOG(20, "existing_lock_type=%s", cls_lock_type_str(existing_lock_type));
189 iter = lockers.find(id);
190 if (iter != lockers.end()) {
f64942e4 191 if (fail_if_exists && !fail_if_does_not_exist) {
7c673cae
FG
192 return -EEXIST;
193 } else {
194 lockers.erase(iter); // remove old entry
195 }
f64942e4
AA
196 } else if (fail_if_does_not_exist) {
197 return -ENOENT;
7c673cae
FG
198 }
199
200 if (!lockers.empty()) {
201 if (exclusive) {
81eedcae
TL
202 auto locker_lister =
203 [&lockers]() -> std::string {
204 std::stringstream locker_list;
205 locker_list << lockers;
206 return locker_list.str();
207 };
208 CLS_LOG(20, "could not exclusive-lock object, already locked by %s",
209 locker_lister().c_str());
7c673cae
FG
210 return -EBUSY;
211 }
212
213 if (existing_lock_type != lock_type) {
214 CLS_LOG(20, "cannot take lock on object, conflicting lock type");
215 return -EBUSY;
216 }
217 }
218
219 linfo.lock_type = lock_type;
220 linfo.tag = tag;
221 utime_t expiration;
222 if (!duration.is_zero()) {
223 expiration = ceph_clock_now();
224 expiration += duration;
225
226 }
11fdf7f2
TL
227 // make all addrs of type legacy, because v2 clients speak v2 or v1,
228 // even depending on which OSD they are talking to, and the type
229 // isn't what uniquely identifies them. also, storing a v1 addr
230 // here means that old clients who get this locker_info won't see an
231 // old "msgr2:" prefix.
232 inst.addr.set_type(entity_addr_t::TYPE_LEGACY);
233
7c673cae
FG
234 struct locker_info_t info(expiration, inst.addr, description);
235
236 linfo.lockers[id] = info;
237
238 r = write_lock(hctx, name, linfo);
239 if (r < 0)
240 return r;
241
242 return 0;
243}
244
245/**
246 * Set an exclusive lock on an object for the activating client, if possible.
247 *
248 * Input:
249 * @param cls_lock_lock_op request input
250 *
251 * @returns 0 on success, -EINVAL if it can't decode the lock_cookie,
252 * -EBUSY if the object is already locked, or -errno on (unexpected) failure.
253 */
254static int lock_op(cls_method_context_t hctx,
255 bufferlist *in, bufferlist *out)
256{
257 CLS_LOG(20, "lock_op");
258 cls_lock_lock_op op;
259 try {
11fdf7f2
TL
260 auto iter = in->cbegin();
261 decode(op, iter);
7c673cae
FG
262 } catch (const buffer::error &err) {
263 return -EINVAL;
264 }
265
266 return lock_obj(hctx,
267 op.name, op.type, op.duration, op.description,
268 op.flags, op.cookie, op.tag);
269}
270
271/**
272 * helper function to remove a lock from on disk and clean up state.
273 *
274 * @param name The lock name
275 * @param locker The locker entity name
276 * @param cookie The user-defined cookie associated with the lock.
277 *
278 * @return 0 on success, -ENOENT if there is no such lock (either
279 * entity or cookie is wrong), or -errno on other error.
280 */
281static int remove_lock(cls_method_context_t hctx,
f64942e4
AA
282 const string& name,
283 entity_name_t& locker,
284 const string& cookie)
7c673cae
FG
285{
286 // get current lockers
287 lock_info_t linfo;
288 int r = read_lock(hctx, name, &linfo);
289 if (r < 0) {
290 CLS_ERR("Could not read list of current lockers off disk: %s", cpp_strerror(r).c_str());
291 return r;
292 }
293
294 map<locker_id_t, locker_info_t>& lockers = linfo.lockers;
295 struct locker_id_t id(locker, cookie);
296
297 // remove named locker from set
298 map<locker_id_t, locker_info_t>::iterator iter = lockers.find(id);
299 if (iter == lockers.end()) { // no such key
9f95a23c
TL
300 CLS_LOG(10, "locker %s [name: %s.%ld, cookie: %s] does not exist", name.c_str(),
301 locker.type_str(), locker.num(), cookie.c_str());
7c673cae
FG
302 return -ENOENT;
303 }
304 lockers.erase(iter);
305
f64942e4
AA
306 if (cls_lock_is_ephemeral(linfo.lock_type)) {
307 ceph_assert(lockers.empty());
308 r = clean_lock(hctx);
309 } else {
310 r = write_lock(hctx, name, linfo);
311 }
7c673cae
FG
312
313 return r;
314}
315
316/**
317 * Unlock an object which the activating client currently has locked.
318 *
319 * Input:
320 * @param cls_lock_unlock_op request input
321 *
322 * @return 0 on success, -EINVAL if it can't decode the cookie, -ENOENT
323 * if there is no such lock (either entity or cookie is wrong), or
324 * -errno on other (unexpected) error.
325 */
326static int unlock_op(cls_method_context_t hctx,
327 bufferlist *in, bufferlist *out)
328{
329 CLS_LOG(20, "unlock_op");
330 cls_lock_unlock_op op;
331 try {
11fdf7f2
TL
332 auto iter = in->cbegin();
333 decode(op, iter);
7c673cae
FG
334 } catch (const buffer::error& err) {
335 return -EINVAL;
336 }
337
338 entity_inst_t inst;
339 int r = cls_get_request_origin(hctx, &inst);
11fdf7f2 340 ceph_assert(r == 0);
7c673cae
FG
341 return remove_lock(hctx, op.name, inst.name, op.cookie);
342}
343
344/**
345 * Break the lock on an object held by any client.
346 *
347 * Input:
348 * @param cls_lock_break_op request input
349 *
350 * @return 0 on success, -EINVAL if it can't decode the locker and
351 * cookie, -ENOENT if there is no such lock (either entity or cookie
352 * is wrong), or -errno on other (unexpected) error.
353 */
354static int break_lock(cls_method_context_t hctx,
f64942e4 355 bufferlist *in, bufferlist *out)
7c673cae
FG
356{
357 CLS_LOG(20, "break_lock");
358 cls_lock_break_op op;
359 try {
11fdf7f2
TL
360 auto iter = in->cbegin();
361 decode(op, iter);
7c673cae
FG
362 } catch (const buffer::error& err) {
363 return -EINVAL;
364 }
365
366 return remove_lock(hctx, op.name, op.locker, op.cookie);
367}
368
369
370/**
371 * Retrieve lock info: lockers, tag, exclusive
372 *
373 * Input:
374 * @param cls_lock_list_lockers_op request input
375 *
376 * Output:
377 * @param cls_lock_list_lockers_reply result
378 *
379 * @return 0 on success, -errno on failure.
380 */
381static int get_info(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
382{
383 CLS_LOG(20, "get_info");
384 cls_lock_get_info_op op;
385 try {
11fdf7f2
TL
386 auto iter = in->cbegin();
387 decode(op, iter);
7c673cae
FG
388 } catch (const buffer::error& err) {
389 return -EINVAL;
390 }
391
392 // get current lockers
393 lock_info_t linfo;
394 int r = read_lock(hctx, op.name, &linfo);
395 if (r < 0) {
396 CLS_ERR("Could not read lock info: %s", cpp_strerror(r).c_str());
397 return r;
398 }
399
400 struct cls_lock_get_info_reply ret;
401
402 map<locker_id_t, locker_info_t>::iterator iter;
403 for (iter = linfo.lockers.begin(); iter != linfo.lockers.end(); ++iter) {
404 ret.lockers[iter->first] = iter->second;
405 }
406 ret.lock_type = linfo.lock_type;
407 ret.tag = linfo.tag;
408
11fdf7f2 409 encode(ret, *out, cls_get_client_features(hctx));
7c673cae
FG
410
411 return 0;
412}
413
414
415/**
416 * Retrieve a list of locks for this object
417 *
418 * Input:
419 * @param in is ignored.
420 *
421 * Output:
422 * @param out contains encoded cls_list_locks_reply
423 *
424 * @return 0 on success, -errno on failure.
425 */
426static int list_locks(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
427{
428 CLS_LOG(20, "list_locks");
429
430 map<string, bufferlist> attrs;
431
432 int r = cls_cxx_getxattrs(hctx, &attrs);
433 if (r < 0)
434 return r;
435
436 cls_lock_list_locks_reply ret;
437
438 map<string, bufferlist>::iterator iter;
439 size_t pos = sizeof(LOCK_PREFIX) - 1;
440 for (iter = attrs.begin(); iter != attrs.end(); ++iter) {
441 const string& attr = iter->first;
442 if (attr.substr(0, pos).compare(LOCK_PREFIX) == 0) {
443 ret.locks.push_back(attr.substr(pos));
444 }
445 }
446
11fdf7f2 447 encode(ret, *out);
7c673cae
FG
448
449 return 0;
450}
451
452/**
453 * Assert that the object is currently locked
454 *
455 * Input:
456 * @param cls_lock_assert_op request input
457 *
458 * Output:
459 * @param none
460 *
461 * @return 0 on success, -errno on failure.
462 */
463int assert_locked(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
464{
465 CLS_LOG(20, "assert_locked");
466
467 cls_lock_assert_op op;
468 try {
11fdf7f2
TL
469 auto iter = in->cbegin();
470 decode(op, iter);
7c673cae
FG
471 } catch (const buffer::error& err) {
472 return -EINVAL;
473 }
474
f64942e4 475 if (!cls_lock_is_valid(op.type)) {
7c673cae
FG
476 return -EINVAL;
477 }
478
479 if (op.name.empty()) {
480 return -EINVAL;
481 }
482
483 // see if there's already a locker
484 lock_info_t linfo;
485 int r = read_lock(hctx, op.name, &linfo);
486 if (r < 0) {
487 CLS_ERR("Could not read lock info: %s", cpp_strerror(r).c_str());
488 return r;
489 }
490
491 if (linfo.lockers.empty()) {
492 CLS_LOG(20, "object not locked");
493 return -EBUSY;
494 }
495
496 if (linfo.lock_type != op.type) {
497 CLS_LOG(20, "lock type mismatch: current=%s, assert=%s",
498 cls_lock_type_str(linfo.lock_type), cls_lock_type_str(op.type));
499 return -EBUSY;
500 }
501
502 if (linfo.tag != op.tag) {
503 CLS_LOG(20, "lock tag mismatch: current=%s, assert=%s", linfo.tag.c_str(),
504 op.tag.c_str());
505 return -EBUSY;
506 }
507
508 entity_inst_t inst;
509 r = cls_get_request_origin(hctx, &inst);
11fdf7f2 510 ceph_assert(r == 0);
7c673cae
FG
511
512 locker_id_t id;
513 id.cookie = op.cookie;
514 id.locker = inst.name;
515
516 map<locker_id_t, locker_info_t>::iterator iter = linfo.lockers.find(id);
517 if (iter == linfo.lockers.end()) {
518 CLS_LOG(20, "not locked by assert client");
519 return -EBUSY;
520 }
521 return 0;
522}
523
524/**
525 * Update the cookie associated with an object lock
526 *
527 * Input:
528 * @param cls_lock_set_cookie_op request input
529 *
530 * Output:
531 * @param none
532 *
533 * @return 0 on success, -errno on failure.
534 */
535int set_cookie(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
536{
537 CLS_LOG(20, "set_cookie");
538
539 cls_lock_set_cookie_op op;
540 try {
11fdf7f2
TL
541 auto iter = in->cbegin();
542 decode(op, iter);
7c673cae
FG
543 } catch (const buffer::error& err) {
544 return -EINVAL;
545 }
546
f64942e4 547 if (!cls_lock_is_valid(op.type)) {
7c673cae
FG
548 return -EINVAL;
549 }
550
551 if (op.name.empty()) {
552 return -EINVAL;
553 }
554
555 // see if there's already a locker
556 lock_info_t linfo;
557 int r = read_lock(hctx, op.name, &linfo);
558 if (r < 0) {
559 CLS_ERR("Could not read lock info: %s", cpp_strerror(r).c_str());
560 return r;
561 }
562
563 if (linfo.lockers.empty()) {
564 CLS_LOG(20, "object not locked");
565 return -EBUSY;
566 }
567
568 if (linfo.lock_type != op.type) {
569 CLS_LOG(20, "lock type mismatch: current=%s, assert=%s",
570 cls_lock_type_str(linfo.lock_type), cls_lock_type_str(op.type));
571 return -EBUSY;
572 }
573
574 if (linfo.tag != op.tag) {
575 CLS_LOG(20, "lock tag mismatch: current=%s, assert=%s", linfo.tag.c_str(),
576 op.tag.c_str());
577 return -EBUSY;
578 }
579
580 entity_inst_t inst;
581 r = cls_get_request_origin(hctx, &inst);
11fdf7f2 582 ceph_assert(r == 0);
7c673cae
FG
583
584 locker_id_t id;
585 id.cookie = op.cookie;
586 id.locker = inst.name;
587
588 map<locker_id_t, locker_info_t>::iterator iter = linfo.lockers.find(id);
589 if (iter == linfo.lockers.end()) {
590 CLS_LOG(20, "not locked by client");
591 return -EBUSY;
592 }
593
594 id.cookie = op.new_cookie;
595 if (linfo.lockers.count(id) != 0) {
596 CLS_LOG(20, "lock cookie in-use");
597 return -EBUSY;
598 }
599
600 locker_info_t locker_info(iter->second);
601 linfo.lockers.erase(iter);
602
603 linfo.lockers[id] = locker_info;
604 r = write_lock(hctx, op.name, linfo);
605 if (r < 0) {
606 CLS_ERR("Could not update lock info: %s", cpp_strerror(r).c_str());
607 return r;
608 }
609 return 0;
610}
611
612CLS_INIT(lock)
613{
614 CLS_LOG(20, "Loaded lock class!");
615
616 cls_handle_t h_class;
617 cls_method_handle_t h_lock_op;
618 cls_method_handle_t h_unlock_op;
619 cls_method_handle_t h_break_lock;
620 cls_method_handle_t h_get_info;
621 cls_method_handle_t h_list_locks;
622 cls_method_handle_t h_assert_locked;
623 cls_method_handle_t h_set_cookie;
624
625 cls_register("lock", &h_class);
626 cls_register_cxx_method(h_class, "lock",
627 CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PROMOTE,
628 lock_op, &h_lock_op);
629 cls_register_cxx_method(h_class, "unlock",
630 CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PROMOTE,
631 unlock_op, &h_unlock_op);
632 cls_register_cxx_method(h_class, "break_lock",
633 CLS_METHOD_RD | CLS_METHOD_WR,
634 break_lock, &h_break_lock);
635 cls_register_cxx_method(h_class, "get_info",
636 CLS_METHOD_RD,
637 get_info, &h_get_info);
638 cls_register_cxx_method(h_class, "list_locks",
639 CLS_METHOD_RD,
640 list_locks, &h_list_locks);
641 cls_register_cxx_method(h_class, "assert_locked",
642 CLS_METHOD_RD | CLS_METHOD_PROMOTE,
643 assert_locked, &h_assert_locked);
644 cls_register_cxx_method(h_class, "set_cookie",
645 CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PROMOTE,
646 set_cookie, &h_set_cookie);
647
648 return;
649}