diff options
Diffstat (limited to 'src/mds/flock.h')
-rw-r--r-- | src/mds/flock.h | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/src/mds/flock.h b/src/mds/flock.h new file mode 100644 index 000000000..915d912e1 --- /dev/null +++ b/src/mds/flock.h @@ -0,0 +1,289 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +#ifndef CEPH_MDS_FLOCK_H +#define CEPH_MDS_FLOCK_H + +#include <errno.h> + +#include "common/debug.h" +#include "mdstypes.h" + +inline std::ostream& operator<<(std::ostream& out, const ceph_filelock& l) { + out << "start: " << l.start << ", length: " << l.length + << ", client: " << l.client << ", owner: " << l.owner + << ", pid: " << l.pid << ", type: " << (int)l.type + << std::endl; + return out; +} + +inline bool ceph_filelock_owner_equal(const ceph_filelock& l, const ceph_filelock& r) +{ + if (l.client != r.client || l.owner != r.owner) + return false; + // The file lock is from old client if the most significant bit of + // 'owner' is not set. Old clients use both 'owner' and 'pid' to + // identify the owner of lock. + if (l.owner & (1ULL << 63)) + return true; + return l.pid == r.pid; +} + +inline int ceph_filelock_owner_compare(const ceph_filelock& l, const ceph_filelock& r) +{ + if (l.client != r.client) + return l.client > r.client ? 1 : -1; + if (l.owner != r.owner) + return l.owner > r.owner ? 1 : -1; + if (l.owner & (1ULL << 63)) + return 0; + if (l.pid != r.pid) + return l.pid > r.pid ? 1 : -1; + return 0; +} + +inline int ceph_filelock_compare(const ceph_filelock& l, const ceph_filelock& r) +{ + int ret = ceph_filelock_owner_compare(l, r); + if (ret) + return ret; + if (l.start != r.start) + return l.start > r.start ? 1 : -1; + if (l.length != r.length) + return l.length > r.length ? 1 : -1; + if (l.type != r.type) + return l.type > r.type ? 1 : -1; + return 0; +} + +inline bool operator<(const ceph_filelock& l, const ceph_filelock& r) +{ + return ceph_filelock_compare(l, r) < 0; +} + +inline bool operator==(const ceph_filelock& l, const ceph_filelock& r) { + return ceph_filelock_compare(l, r) == 0; +} + +inline bool operator!=(const ceph_filelock& l, const ceph_filelock& r) { + return ceph_filelock_compare(l, r) != 0; +} + +class ceph_lock_state_t { +public: + explicit ceph_lock_state_t(CephContext *cct_, int type_) : cct(cct_), type(type_) {} + ~ceph_lock_state_t(); + /** + * Check if a lock is on the waiting_locks list. + * + * @param fl The filelock to check for + * @returns True if the lock is waiting, false otherwise + */ + bool is_waiting(const ceph_filelock &fl) const; + /** + * Remove a lock from the waiting_locks list + * + * @param fl The filelock to remove + */ + void remove_waiting(const ceph_filelock& fl); + /* + * Try to set a new lock. If it's blocked and wait_on_fail is true, + * add the lock to waiting_locks. + * The lock needs to be of type CEPH_LOCK_EXCL or CEPH_LOCK_SHARED. + * This may merge previous locks, or convert the type of already-owned + * locks. + * + * @param new_lock The lock to set + * @param wait_on_fail whether to wait until the lock can be set. + * Otherwise it fails immediately when blocked. + * + * @returns true if set, false if not set. + */ + bool add_lock(ceph_filelock& new_lock, bool wait_on_fail, bool replay, + bool *deadlock); + /** + * See if a lock is blocked by existing locks. If the lock is blocked, + * it will be set to the value of the first blocking lock. Otherwise, + * it will be returned unchanged, except for setting the type field + * to CEPH_LOCK_UNLOCK. + * + * @param testing_lock The lock to check for conflicts on. + */ + void look_for_lock(ceph_filelock& testing_lock); + + /* + * Remove lock(s) described in old_lock. This may involve splitting a + * previous lock or making a previous lock smaller. + * + * @param removal_lock The lock to remove + * @param activated_locks A return parameter, holding activated wait locks. + */ + void remove_lock(const ceph_filelock removal_lock, + std::list<ceph_filelock>& activated_locks); + + bool remove_all_from(client_t client); + + void encode(ceph::bufferlist& bl) const { + using ceph::encode; + encode(held_locks, bl); + encode(client_held_lock_counts, bl); + } + void decode(ceph::bufferlist::const_iterator& bl) { + using ceph::decode; + decode(held_locks, bl); + decode(client_held_lock_counts, bl); + } + bool empty() const { + return held_locks.empty() && waiting_locks.empty() && + client_held_lock_counts.empty() && + client_waiting_lock_counts.empty(); + } + + std::multimap<uint64_t, ceph_filelock> held_locks; // current locks + std::multimap<uint64_t, ceph_filelock> waiting_locks; // locks waiting for other locks + // both of the above are keyed by starting offset + std::map<client_t, int> client_held_lock_counts; + std::map<client_t, int> client_waiting_lock_counts; + +private: + static const unsigned MAX_DEADLK_DEPTH = 5; + + /** + * Check if adding the lock causes deadlock + * + * @param fl The blocking filelock + * @param overlapping_locks list of all overlapping locks + * @param first_fl + * @depth recursion call depth + */ + bool is_deadlock(const ceph_filelock& fl, + std::list<std::multimap<uint64_t, ceph_filelock>::iterator>& + overlapping_locks, + const ceph_filelock *first_fl=NULL, unsigned depth=0) const; + + /** + * Add a lock to the waiting_locks list + * + * @param fl The filelock to add + */ + void add_waiting(const ceph_filelock& fl); + + /** + * Adjust old locks owned by a single process so that process can set + * a new lock of different type. Handle any changes needed to the old locks + * (and the new lock) so that once the new lock is inserted into the + * held_locks list the process has a coherent, non-fragmented set of lock + * ranges. Make sure any overlapping locks are combined, trimmed, and removed + * as needed. + * This function should only be called once you know the lock will be + * inserted, as it DOES adjust new_lock. You can call this function + * on an empty list, in which case it does nothing. + * This function does not remove elements from old_locks, so regard the list + * as bad information following function invocation. + * + * @param new_lock The new lock the process has requested. + * @param old_locks list of all locks currently held by same + * client/process that overlap new_lock. + * @param neighbor_locks locks owned by same process that neighbor new_lock on + * left or right side. + */ + void adjust_locks(std::list<std::multimap<uint64_t, ceph_filelock>::iterator> old_locks, + ceph_filelock& new_lock, + std::list<std::multimap<uint64_t, ceph_filelock>::iterator> + neighbor_locks); + + //get last lock prior to start position + std::multimap<uint64_t, ceph_filelock>::iterator + get_lower_bound(uint64_t start, + std::multimap<uint64_t, ceph_filelock>& lock_map); + //get latest-starting lock that goes over the byte "end" + std::multimap<uint64_t, ceph_filelock>::iterator + get_last_before(uint64_t end, + std::multimap<uint64_t, ceph_filelock>& lock_map); + + /* + * See if an iterator's lock covers any of the same bounds as a given range + * Rules: locks cover "length" bytes from "start", so the last covered + * byte is at start + length - 1. + * If the length is 0, the lock covers from "start" to the end of the file. + */ + bool share_space(std::multimap<uint64_t, ceph_filelock>::iterator& iter, + uint64_t start, uint64_t end); + + bool share_space(std::multimap<uint64_t, ceph_filelock>::iterator& iter, + const ceph_filelock &lock) { + uint64_t end = lock.start; + if (lock.length) { + end += lock.length - 1; + } else { // zero length means end of file + end = uint64_t(-1); + } + return share_space(iter, lock.start, end); + } + /* + *get a list of all locks overlapping with the given lock's range + * lock: the lock to compare with. + * overlaps: an empty list, to be filled. + * Returns: true if at least one lock overlaps. + */ + bool get_overlapping_locks(const ceph_filelock& lock, + std::list<std::multimap<uint64_t, + ceph_filelock>::iterator> & overlaps, + std::list<std::multimap<uint64_t, + ceph_filelock>::iterator> *self_neighbors); + + + bool get_overlapping_locks(const ceph_filelock& lock, + std::list<std::multimap<uint64_t, ceph_filelock>::iterator>& overlaps) { + return get_overlapping_locks(lock, overlaps, NULL); + } + + /** + * Get a list of all waiting locks that overlap with the given lock's range. + * lock: specifies the range to compare with + * overlaps: an empty list, to be filled + * Returns: true if at least one waiting_lock overlaps + */ + bool get_waiting_overlaps(const ceph_filelock& lock, + std::list<std::multimap<uint64_t, + ceph_filelock>::iterator>& overlaps); + /* + * split a list of locks up by whether they're owned by same + * process as given lock + * owner: the owning lock + * locks: the list of locks (obtained from get_overlapping_locks, probably) + * Will have all locks owned by owner removed + * owned_locks: an empty list, to be filled with the locks owned by owner + */ + void split_by_owner(const ceph_filelock& owner, + std::list<std::multimap<uint64_t, + ceph_filelock>::iterator> & locks, + std::list<std::multimap<uint64_t, + ceph_filelock>::iterator> & owned_locks); + + ceph_filelock *contains_exclusive_lock(std::list<std::multimap<uint64_t, + ceph_filelock>::iterator>& locks); + + CephContext *cct; + int type; +}; +WRITE_CLASS_ENCODER(ceph_lock_state_t) + +inline std::ostream& operator<<(std::ostream &out, const ceph_lock_state_t &l) { + out << "ceph_lock_state_t. held_locks.size()=" << l.held_locks.size() + << ", waiting_locks.size()=" << l.waiting_locks.size() + << ", client_held_lock_counts -- " << l.client_held_lock_counts + << "\n client_waiting_lock_counts -- " << l.client_waiting_lock_counts + << "\n held_locks -- "; + for (auto iter = l.held_locks.begin(); + iter != l.held_locks.end(); + ++iter) + out << iter->second; + out << "\n waiting_locks -- "; + for (auto iter =l.waiting_locks.begin(); + iter != l.waiting_locks.end(); + ++iter) + out << iter->second << "\n"; + return out; +} + +#endif |