1    	// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 
2    	// vim: ts=8 sw=2 smarttab
3    	/*
4    	 * Ceph - scalable distributed file system
5    	 *
6    	 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
7    	 *
8    	 * This is free software; you can redistribute it and/or
9    	 * modify it under the terms of the GNU Lesser General Public
10   	 * License version 2.1, as published by the Free Software 
11   	 * Foundation.  See file COPYING.
12   	 * 
13   	 */
14   	
15   	
16   	#ifndef CEPH_SCATTERLOCK_H
17   	#define CEPH_SCATTERLOCK_H
18   	
19   	#include "SimpleLock.h"
20   	
21   	#include "MDSContext.h"
22   	
23   	class ScatterLock : public SimpleLock {
24   	
25   	  struct more_bits_t {
26   	    xlist<ScatterLock*>::item item_updated;
27   	    utime_t update_stamp;
28   	
29   	    explicit more_bits_t(ScatterLock *lock) :
30   	      item_updated(lock)
31   	    {}
32   	  };
33   	
34   	  mutable std::unique_ptr<more_bits_t> _more;
35   	
36   	  more_bits_t *more() {
37   	    if (!_more)
38   	      _more.reset(new more_bits_t(this));
39   	    return _more.get();
40   	  }
41   	
42   	  enum {
43   	    SCATTER_WANTED   = 1 << 8,
44   	    UNSCATTER_WANTED = 1 << 9,
45   	    DIRTY            = 1 << 10,
46   	    FLUSHING         = 1 << 11,
47   	    FLUSHED          = 1 << 12,
48   	  };
49   	
50   	public:
51   	  ScatterLock(MDSCacheObject *o, LockType *lt) :
52   	    SimpleLock(o, lt) {}
(1) Event exn_spec_violation: An exception of type "_ZN5boost16exception_detail10clone_implINS0_19error_info_injectorINSt8ios_base7failureB5cxx11EEEEE" is thrown but the throw list "throw()" doesn't allow it to be thrown. This will cause a call to unexpected() which usually calls terminate().
Also see events: [fun_call_w_exception]
53   	  ~ScatterLock() override {
(2) Event fun_call_w_exception: Called function throws an exception of type "_ZN5boost16exception_detail10clone_implINS0_19error_info_injectorINSt8ios_base7failureB5cxx11EEEEE". [details]
Also see events: [exn_spec_violation]
54   	    ceph_assert(!_more);
55   	  }
56   	
57   	  bool is_scatterlock() const override {
58   	    return true;
59   	  }
60   	
61   	  bool is_sync_and_unlocked() const {
62   	    return
63   	      SimpleLock::is_sync_and_unlocked() && 
64   	      !is_dirty() &&
65   	      !is_flushing();
66   	  }
67   	
68   	  bool can_scatter_pin(client_t loner) {
69   	    /*
70   	      LOCK : NOT okay because it can MIX and force replicas to journal something
71   	      TSYN : also not okay for same reason
72   	      EXCL : also not okay
73   	
74   	      MIX  : okay, replica can stall before sending AC_SYNCACK
75   	      SYNC : okay, replica can stall before sending AC_MIXACK or AC_LOCKACK
76   	    */   
77   	    return
78   	      get_state() == LOCK_SYNC ||
79   	      get_state() == LOCK_MIX;
80   	  }
81   	
82   	  void set_xlock_snap_sync(MDSContext *c)
83   	  {
84   	    ceph_assert(get_type() == CEPH_LOCK_IFILE);
85   	    ceph_assert(state == LOCK_XLOCK || state == LOCK_XLOCKDONE);
86   	    state = LOCK_XLOCKSNAP;
87   	    add_waiter(WAIT_STABLE, c);
88   	  }
89   	
90   	  xlist<ScatterLock*>::item *get_updated_item() { return &more()->item_updated; }
91   	
92   	  utime_t get_update_stamp() {
93   	    return _more ? _more->update_stamp : utime_t();
94   	  }
95   	
96   	  void set_update_stamp(utime_t t) { more()->update_stamp = t; }
97   	
98   	  void set_scatter_wanted() {
99   	    state_flags |= SCATTER_WANTED;
100  	  }
101  	  void set_unscatter_wanted() {
102  	    state_flags |= UNSCATTER_WANTED;
103  	  }
104  	  void clear_scatter_wanted() {
105  	    state_flags &= ~SCATTER_WANTED;
106  	  }
107  	  void clear_unscatter_wanted() {
108  	    state_flags &= ~UNSCATTER_WANTED;
109  	  }
110  	  bool get_scatter_wanted() const {
111  	    return state_flags & SCATTER_WANTED;
112  	  }
113  	  bool get_unscatter_wanted() const {
114  	    return state_flags & UNSCATTER_WANTED;
115  	  }
116  	
117  	  bool is_dirty() const override {
118  	    return state_flags & DIRTY;
119  	  }
120  	  bool is_flushing() const override {
121  	    return state_flags & FLUSHING;
122  	  }
123  	  bool is_flushed() const override {
124  	    return state_flags & FLUSHED;
125  	  }
126  	  bool is_dirty_or_flushing() const {
127  	    return is_dirty() || is_flushing();
128  	  }
129  	
130  	  void mark_dirty() { 
131  	    if (!is_dirty()) {
132  	      if (!is_flushing())
133  		parent->get(MDSCacheObject::PIN_DIRTYSCATTERED);
134  	      set_dirty();
135  	    }
136  	  }
137  	  void start_flush() {
138  	    if (is_dirty()) {
139  	      set_flushing();
140  	      clear_dirty();
141  	    }
142  	  }
143  	  void finish_flush() {
144  	    if (is_flushing()) {
145  	      clear_flushing();
146  	      set_flushed();
147  	      if (!is_dirty()) {
148  		parent->put(MDSCacheObject::PIN_DIRTYSCATTERED);
149  		parent->clear_dirty_scattered(get_type());
150  	      }
151  	    }
152  	  }
153  	  void clear_flushed() override {
154  	    state_flags &= ~FLUSHED;
155  	  }
156  	  void remove_dirty() {
157  	    start_flush();
158  	    finish_flush();
159  	    clear_flushed();
160  	  }
161  	
162  	  void infer_state_from_strong_rejoin(int rstate, bool locktoo) {
163  	    if (rstate == LOCK_MIX || 
164  		rstate == LOCK_MIX_LOCK || // replica still has wrlocks?
165  		rstate == LOCK_MIX_SYNC)
166  	      state = LOCK_MIX;
167  	    else if (locktoo && rstate == LOCK_LOCK)
168  	      state = LOCK_LOCK;
169  	  }
170  	
171  	  void encode_state_for_rejoin(bufferlist& bl, int rep) {
172  	    __s16 s = get_replica_state();
173  	    if (is_gathering(rep)) {
174  	      // the recovering mds may hold rejoined wrlocks
175  	      if (state == LOCK_MIX_SYNC)
176  		s = LOCK_MIX_SYNC;
177  	      else
178  		s = LOCK_MIX_LOCK;
179  	    }
180  	
181  	    // If there is a recovering mds who replcated an object when it failed
182  	    // and scatterlock in the object was in MIX state, It's possible that
183  	    // the recovering mds needs to take wrlock on the scatterlock when it
184  	    // replays unsafe requests. So this mds should delay taking rdlock on
185  	    // the scatterlock until the recovering mds finishes replaying unsafe.
186  	    // Otherwise unsafe requests may get replayed after current request.
187  	    //
188  	    // For example:
189  	    // The recovering mds is auth mds of a dirfrag, this mds is auth mds
190  	    // of corresponding inode. when 'rm -rf' the direcotry, this mds should
191  	    // delay the rmdir request until the recovering mds has replayed unlink
192  	    // requests.
193  	    if (s == LOCK_MIX || s == LOCK_MIX_LOCK || s == LOCK_MIX_SYNC)
194  	      mark_need_recover();
195  	
196  	    using ceph::encode;
197  	    encode(s, bl);
198  	  }
199  	
200  	  void decode_state_rejoin(bufferlist::const_iterator& p, MDSContext::vec& waiters, bool survivor) {
201  	    SimpleLock::decode_state_rejoin(p, waiters, survivor);
202  	    if (is_flushing()) {
203  	      set_dirty();
204  	      clear_flushing();
205  	    }
206  	  }
207  	
208  	  bool remove_replica(int from, bool rejoin) {
209  	    if (rejoin &&
210  		(state == LOCK_MIX ||
211  		 state == LOCK_MIX_SYNC ||
212  		 state == LOCK_MIX_LOCK2 ||
213  		 state == LOCK_MIX_TSYN ||
214  		 state == LOCK_MIX_EXCL))
215  	      return false;
216  	    return SimpleLock::remove_replica(from);
217  	  }
218  	
219  	  void print(ostream& out) const override {
220  	    out << "(";
221  	    _print(out);
222  	    if (is_dirty())
223  	      out << " dirty";
224  	    if (is_flushing())
225  	      out << " flushing";
226  	    if (is_flushed())
227  	      out << " flushed";
228  	    if (get_scatter_wanted())
229  	      out << " scatter_wanted";
230  	    out << ")";
231  	  }
232  	
233  	private:
234  	  void set_flushing() {
235  	    state_flags |= FLUSHING;
236  	  }
237  	  void clear_flushing() {
238  	    state_flags &= ~FLUSHING;
239  	  }
240  	  void set_flushed() {
241  	    state_flags |= FLUSHED;
242  	  }
243  	  void set_dirty() {
244  	    state_flags |= DIRTY;
245  	  }
246  	  void clear_dirty() {
247  	    state_flags &= ~DIRTY;
248  	    if (_more) {
249  	      _more->item_updated.remove_myself();
250  	      _more.reset();
251  	    }
252  	  }
253  	};
254  	
255  	#endif
256