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