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