// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Ceph - scalable distributed file system * * Copyright (C) 2004-2006 Sage Weil * * This is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software * Foundation. See file COPYING. * */ #ifndef CEPH_SCATTERLOCK_H #define CEPH_SCATTERLOCK_H #include "SimpleLock.h" class ScatterLock : public SimpleLock { struct more_bits_t { int state_flags; utime_t last_scatter; xlist::item item_updated; utime_t update_stamp; explicit more_bits_t(ScatterLock *lock) : state_flags(0), item_updated(lock) {} bool empty() const { return !state_flags && !item_updated.is_on_list(); } }; more_bits_t *_more; bool have_more() const { return _more ? true : false; } void try_clear_more() { if (_more && _more->empty()) { delete _more; _more = NULL; } } more_bits_t *more() { if (!_more) _more = new more_bits_t(this); return _more; } enum flag_values { // flag values for more_bits_t state SCATTER_WANTED = 1 << 0, UNSCATTER_WANTED = 1 << 1, DIRTY = 1 << 2, FLUSHING = 1 << 3, FLUSHED = 1 << 4, REJOIN_MIX = 1 << 5, // no rdlock until the recovering mds become active }; public: ScatterLock(MDSCacheObject *o, LockType *lt) : SimpleLock(o, lt), _more(NULL) {} ~ScatterLock() override { if (_more) { _more->item_updated.remove_myself(); // FIXME this should happen sooner, i think... delete _more; } } bool is_scatterlock() const override { return true; } bool is_sync_and_unlocked() const { return SimpleLock::is_sync_and_unlocked() && !is_dirty() && !is_flushing(); } bool can_scatter_pin(client_t loner) { /* LOCK : NOT okay because it can MIX and force replicas to journal something TSYN : also not okay for same reason EXCL : also not okay MIX : okay, replica can stall before sending AC_SYNCACK SYNC : okay, replica can stall before sending AC_MIXACK or AC_LOCKACK */ return get_state() == LOCK_SYNC || get_state() == LOCK_MIX; } void set_xlock_snap_sync(MDSInternalContextBase *c) { assert(get_type() == CEPH_LOCK_IFILE); assert(state == LOCK_XLOCK || state == LOCK_XLOCKDONE); state = LOCK_XLOCKSNAP; add_waiter(WAIT_STABLE, c); } xlist::item *get_updated_item() { return &more()->item_updated; } utime_t get_update_stamp() { return more()->update_stamp; } void set_update_stamp(utime_t t) { more()->update_stamp = t; } void set_scatter_wanted() { more()->state_flags |= SCATTER_WANTED; } void set_unscatter_wanted() { more()->state_flags |= UNSCATTER_WANTED; } void clear_scatter_wanted() { if (have_more()) _more->state_flags &= ~SCATTER_WANTED; try_clear_more(); } void clear_unscatter_wanted() { if (have_more()) _more->state_flags &= ~UNSCATTER_WANTED; try_clear_more(); } bool get_scatter_wanted() const { return have_more() ? _more->state_flags & SCATTER_WANTED : false; } bool get_unscatter_wanted() const { return have_more() ? _more->state_flags & UNSCATTER_WANTED : false; } bool is_dirty() const override { return have_more() ? _more->state_flags & DIRTY : false; } bool is_flushing() const override { return have_more() ? _more->state_flags & FLUSHING: false; } bool is_flushed() const override { return have_more() ? _more->state_flags & FLUSHED: false; } bool is_dirty_or_flushing() const { return have_more() ? (is_dirty() || is_flushing()) : false; } bool is_rejoin_mix() const { return have_more() ? _more->state_flags & REJOIN_MIX : false; } void mark_dirty() { if (!is_dirty()) { if (!is_flushing()) parent->get(MDSCacheObject::PIN_DIRTYSCATTERED); set_dirty(); } } void start_flush() { if (is_dirty()) { set_flushing(); clear_dirty(); } } void finish_flush() { if (is_flushing()) { clear_flushing(); set_flushed(); if (!is_dirty()) { parent->put(MDSCacheObject::PIN_DIRTYSCATTERED); parent->clear_dirty_scattered(get_type()); } } } void remove_dirty() { start_flush(); finish_flush(); } void clear_flushed() override { if (have_more()) { _more->state_flags &= ~FLUSHED; try_clear_more(); } } void clear_rejoin_mix() { if (have_more()) { _more->state_flags &= ~REJOIN_MIX; try_clear_more(); } } void set_last_scatter(utime_t t) { more()->last_scatter = t; } utime_t get_last_scatter() { return more()->last_scatter; } void infer_state_from_strong_rejoin(int rstate, bool locktoo) { if (rstate == LOCK_MIX || rstate == LOCK_MIX_LOCK || // replica still has wrlocks? rstate == LOCK_MIX_SYNC) state = LOCK_MIX; else if (locktoo && rstate == LOCK_LOCK) state = LOCK_LOCK; } void encode_state_for_rejoin(bufferlist& bl, int rep) { __s16 s = get_replica_state(); if (is_gathering(rep)) { // the recovering mds may hold rejoined wrlocks if (state == LOCK_MIX_SYNC) s = LOCK_MIX_SYNC; else s = LOCK_MIX_LOCK; } if (s == LOCK_MIX || s == LOCK_MIX_LOCK || s == LOCK_MIX_SYNC) more()->state_flags |= REJOIN_MIX; ::encode(s, bl); } void decode_state_rejoin(bufferlist::iterator& p, list& waiters) { SimpleLock::decode_state_rejoin(p, waiters); if (is_flushing()) { set_dirty(); clear_flushing(); } } bool remove_replica(int from, bool rejoin) { if (rejoin && (state == LOCK_MIX || state == LOCK_MIX_SYNC || state == LOCK_MIX_LOCK2 || state == LOCK_MIX_TSYN || state == LOCK_MIX_EXCL)) return false; return SimpleLock::remove_replica(from); } void print(ostream& out) const override { out << "("; _print(out); if (is_dirty()) out << " dirty"; if (is_flushing()) out << " flushing"; if (is_flushed()) out << " flushed"; if (get_scatter_wanted()) out << " scatter_wanted"; out << ")"; } private: void set_flushing() { more()->state_flags |= FLUSHING; } void clear_flushing() { if (have_more()) { _more->state_flags &= ~FLUSHING; } } void set_flushed() { more()->state_flags |= FLUSHED; } void set_dirty() { more()->state_flags |= DIRTY; } void clear_dirty() { if (have_more()) { _more->state_flags &= ~DIRTY; } } }; #endif