static const int PIN_OPENINGSNAPPARENTS = 17;
static const int PIN_TRUNCATING = 18;
static const int PIN_STRAY = 19; // we pin our stray inode while active
+ static const int PIN_NEEDSNAPFLUSH = 20;
const char *pin_name(int p) {
switch (p) {
case PIN_OPENINGSNAPPARENTS: return "openingsnapparents";
case PIN_TRUNCATING: return "truncating";
case PIN_STRAY: return "stray";
+ case PIN_NEEDSNAPFLUSH: return "needsnapflush";
default: return generic_pin_name(p);
}
}
utime_t replica_caps_wanted_keep_until;
map<int, set<client_t> > client_snap_caps; // [auth] [snap] dirty metadata we still need from the head
+public:
+ map<snapid_t, set<client_t> > client_need_snapflush;
+protected:
ceph_lock_state_t fcntl_locks;
ceph_lock_state_t flock_locks;
mut->cleanup();
delete mut;
- if (!in->is_head()) {
+ if (!in->is_head() && in->client_snap_caps.size()) {
dout(10) << " client_snap_caps " << in->client_snap_caps << dendl;
// check for snap writeback completion
bool gather = false;
dout(10) << " completing client_snap_caps for " << ccap_string(p->first)
<< " lock " << *lock << " on " << *in << dendl;
lock->put_wrlock();
+
p->second.erase(client);
if (p->second.empty()) {
gather = true;
// flushsnap?
if (op == CEPH_CAP_OP_FLUSHSNAP) {
- if (in->is_auth()) {
+ if (!in->is_auth()) {
+ dout(7) << " not auth, ignoring flushsnap on " << *in << dendl;
+ goto out;
+ }
+
+ if (in == head_in ||
+ (head_in->client_need_snapflush.count(in->last) &&
+ head_in->client_need_snapflush[in->last].count(client))) {
dout(7) << " flushsnap follows " << follows
<< " client" << client << " on " << *in << dendl;
// this cap now follows a later snap (i.e. the one initiating this flush, or later)
}
_do_snap_update(in, m->get_dirty(), follows, m, ack);
+
+ if (in != head_in) {
+ head_in->client_need_snapflush[in->last].erase(client);
+ if (head_in->client_need_snapflush[in->last].empty()) {
+ head_in->client_need_snapflush.erase(in->last);
+ if (head_in->client_need_snapflush.empty())
+ head_in->put(CInode::PIN_NEEDSNAPFLUSH);
+ }
+ }
+
} else
- dout(7) << " not auth, ignoring flushsnap on " << *in << dendl;
+ dout(7) << " not expecting flushsnap from client" << client << " on " << *in << dendl;
goto out;
}
}
in = mdcache->pick_inode_snap(in, in->last);
}
-
+
// head inode, and cap
MClientCaps *ack = 0;
<< " retains " << ccap_string(m->get_caps())
<< " dirty " << ccap_string(m->get_caps())
<< " on " << *in << dendl;
+
+
+ // missing/skipped snapflush?
+ // ** only if the client has completed all revocations. if we
+ // are still revoking, it's possible the client is waiting for
+ // data writeback and hasn't sent the flushsnap yet. **
+ if (head_in->client_need_snapflush.size()) {
+ if ((cap->pending() & ~cap->issued()) == 0) {
+ map<snapid_t, set<client_t> >::iterator p = head_in->client_need_snapflush.begin();
+ while (p != head_in->client_need_snapflush.end()) {
+ // p->first is the snap inode's ->last
+ if (follows > p->first)
+ break;
+ if (p->second.count(client)) {
+ dout(10) << " doing async NULL snapflush on " << p->first << " from client" << p->second << dendl;
+ CInode *sin = mdcache->pick_inode_snap(head_in, p->first - 1);
+ assert(sin != head_in);
+ _do_snap_update(sin, 0, sin->first - 1, m, NULL);
+ head_in->client_need_snapflush[in->last].erase(client);
+ if (head_in->client_need_snapflush[in->last].empty()) {
+ head_in->client_need_snapflush.erase(p++);
+ if (head_in->client_need_snapflush.empty())
+ head_in->put(CInode::PIN_NEEDSNAPFLUSH);
+ continue;
+ }
+ }
+ p++;
+ }
+ } else {
+ dout(10) << " revocation in progress, not making any conclusions about null snapflushes" << dendl;
+ }
+ }
if (m->get_dirty() && in->is_auth()) {
dout(7) << " flush client" << client << " dirty " << ccap_string(m->get_dirty())