--- /dev/null
+// -*- 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 <sage@newdream.net>
+ *
+ * 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<ScatterLock*>::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<ScatterLock*>::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<MDSInternalContextBase*>& 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