// NOTE: state order reversal, warning comes after prepping
case EXPORT_WARNING:
dout(10) << "export state=warning : unpinning bounds, unfreezing, notifying" << dendl;
+ it->second.state = EXPORT_CANCELLING;
// fall-thru
case EXPORT_PREPPING:
- if (state != EXPORT_WARNING)
+ if (state != EXPORT_WARNING) {
dout(10) << "export state=prepping : unpinning bounds, unfreezing" << dendl;
+ it->second.state = EXPORT_CANCELLED;
+ }
- it->second.state = EXPORT_CANCELLED;
{
// unpin bounds
set<CDir*> bounds;
case EXPORT_EXPORTING:
dout(10) << "export state=exporting : reversing, and unfreezing" << dendl;
- it->second.state = EXPORT_CANCELLED;
+ it->second.state = EXPORT_CANCELLING;
export_reverse(dir);
break;
dout(10) << "export state=loggingfinish|notifying : ignoring dest failure, we were successful." << dendl;
// leave export_state, don't clean up now.
break;
+ case EXPORT_CANCELLING:
+ break;
default:
ceph_abort();
}
// finish clean-up?
- if (it->second.state == EXPORT_CANCELLED) {
- MutationRef mut = it->second.mut;
-
- export_state.erase(it);
- dir->state_clear(CDir::STATE_EXPORTING);
-
- // send pending import_maps? (these need to go out when all exports have finished.)
- cache->maybe_send_pending_resolves();
+ if (it->second.state == EXPORT_CANCELLING ||
+ it->second.state == EXPORT_CANCELLED) {
+ MutationRef mut;
+ mut.swap(it->second.mut);
+
+ if (it->second.state == EXPORT_CANCELLED) {
+ export_state.erase(it);
+ dir->state_clear(CDir::STATE_EXPORTING);
+ // send pending import_maps?
+ cache->maybe_send_pending_resolves();
+ }
// drop locks
if (state == EXPORT_LOCKING || state == EXPORT_DISCOVERING) {
}
}
+void Migrator::export_cancel_finish(CDir *dir)
+{
+ assert(dir->state_test(CDir::STATE_EXPORTING));
+ dir->state_clear(CDir::STATE_EXPORTING);
+
+ // pinned by Migrator::export_notify_abort()
+ dir->auth_unpin(this);
+ // send pending import_maps? (these need to go out when all exports have finished.)
+ cache->maybe_send_pending_resolves();
+}
+
// ==========================================================
// mds failure handling
// - that are going to the failed node
// - that aren't frozen yet (to avoid auth_pin deadlock)
// - they havne't prepped yet (they may need to discover bounds to do that)
- if (p->second.peer == who ||
+ if ((p->second.peer == who &&
+ p->second.state != EXPORT_CANCELLING) ||
p->second.state == EXPORT_LOCKING ||
p->second.state == EXPORT_DISCOVERING ||
p->second.state == EXPORT_FREEZING ||
dout(10) << "cleaning up export state (" << p->second.state << ")"
<< get_export_statename(p->second.state) << " of " << *dir << dendl;
export_try_cancel(dir);
- } else {
+ } else if (p->second.peer != who) {
// bystander failed.
if (p->second.warning_ack_waiting.erase(who)) {
- p->second.notify_ack_waiting.erase(who); // they won't get a notify either.
if (p->second.state == EXPORT_WARNING) {
+ p->second.notify_ack_waiting.erase(who); // they won't get a notify either.
// exporter waiting for warning acks, let's fake theirs.
dout(10) << "faking export_warning_ack from mds." << who
<< " on " << *dir << " to mds." << p->second.peer
}
}
if (p->second.notify_ack_waiting.erase(who)) {
+ // exporter is waiting for notify acks, fake it
+ dout(10) << "faking export_notify_ack from mds." << who
+ << " on " << *dir << " to mds." << p->second.peer
+ << dendl;
if (p->second.state == EXPORT_NOTIFYING) {
- // exporter is waiting for notify acks, fake it
- dout(10) << "faking export_notify_ack from mds." << who
- << " on " << *dir << " to mds." << p->second.peer
- << dendl;
if (p->second.notify_ack_waiting.empty())
export_finish(dir);
+ } else if (p->second.state == EXPORT_CANCELLING) {
+ if (p->second.notify_ack_waiting.empty()) {
+ export_state.erase(p);
+ export_cancel_finish(dir);
+ }
}
}
}
CDir *dir = p->first;
if (p->second.state == EXPORT_LOCKING ||
p->second.state == EXPORT_DISCOVERING ||
- p->second.state == EXPORT_FREEZING) continue;
+ p->second.state == EXPORT_FREEZING ||
+ p->second.state == EXPORT_CANCELLING)
+ continue;
assert(dir->is_ambiguous_dir_auth());
assert(dir->authority().first == mds->get_nodeid() ||
dir->authority().second == mds->get_nodeid());
dout(7) << "export_sessions_flushed " << *dir << dendl;
map<CDir*,export_state_t>::iterator it = export_state.find(dir);
- if (it == export_state.end() || it->second.tid != tid) {
+ if (it == export_state.end() ||
+ it->second.state == EXPORT_CANCELLING ||
+ it->second.tid != tid) {
// export must have aborted.
dout(7) << "export must have aborted on " << dir << dendl;
return;
map<CDir*,export_state_t>::iterator it = export_state.find(dir);
if (it == export_state.end() ||
+ it->second.state == EXPORT_CANCELLING ||
it->second.tid != tid) {
// export must have aborted.
dout(7) << "export must have aborted on " << dir << dendl;
dout(7) << "export_notify_abort " << *dir << dendl;
export_state_t& stat = export_state[dir];
+ assert(stat.state == EXPORT_CANCELLING);
+
+ if (stat.notify_ack_waiting.empty()) {
+ stat.state = EXPORT_CANCELLED;
+ return;
+ }
+
+ dir->auth_pin(this);
for (set<mds_rank_t>::iterator p = stat.notify_ack_waiting.begin();
p != stat.notify_ack_waiting.end();
++p) {
- MExportDirNotify *notify = new MExportDirNotify(dir->dirfrag(),stat.tid,false,
+ MExportDirNotify *notify = new MExportDirNotify(dir->dirfrag(),stat.tid, true,
pair<int,int>(mds->get_nodeid(),stat.peer),
pair<int,int>(mds->get_nodeid(),CDIR_AUTH_UNKNOWN));
for (set<CDir*>::iterator i = bounds.begin(); i != bounds.end(); ++i)
auto export_state_entry = export_state.find(dir);
if (export_state_entry != export_state.end()) {
export_state_t& stat = export_state_entry->second;
- if (stat.state == EXPORT_WARNING) {
+ if (stat.state == EXPORT_WARNING &&
+ stat.warning_ack_waiting.erase(from)) {
// exporting. process warning.
dout(7) << "handle_export_notify_ack from " << m->get_source()
<< ": exporting, processing warning on " << *dir << dendl;
- stat.warning_ack_waiting.erase(from);
-
if (stat.warning_ack_waiting.empty())
export_go(dir); // start export.
- } else if (stat.state == EXPORT_NOTIFYING) {
+ } else if (stat.state == EXPORT_NOTIFYING &&
+ stat.notify_ack_waiting.erase(from)) {
// exporting. process notify.
dout(7) << "handle_export_notify_ack from " << m->get_source()
<< ": exporting, processing notify on " << *dir << dendl;
- stat.notify_ack_waiting.erase(from);
-
if (stat.notify_ack_waiting.empty())
export_finish(dir);
+ } else if (stat.state == EXPORT_CANCELLING &&
+ m->get_new_auth().second == CDIR_AUTH_UNKNOWN && // not warning ack
+ stat.notify_ack_waiting.erase(from)) {
+ dout(7) << "handle_export_notify_ack from " << m->get_source()
+ << ": cancelling export, processing notify on " << *dir << dendl;
+ if (stat.notify_ack_waiting.empty()) {
+ export_state.erase(export_state_entry);
+ export_cancel_finish(dir);
+ }
}
}
else {
// send ack
if (m->wants_ack()) {
- mds->send_message_mds(new MExportDirNotifyAck(m->get_dirfrag(), m->get_tid()), from);
+ mds->send_message_mds(new MExportDirNotifyAck(m->get_dirfrag(), m->get_tid(), m->get_new_auth()), from);
} else {
// aborted. no ack.
dout(7) << "handle_export_notify no ack requested" << dendl;
public:
// export stages. used to clean up intelligently if there's a failure.
const static int EXPORT_CANCELLED = 0; // cancelled
- const static int EXPORT_LOCKING = 1; // acquiring locks
- const static int EXPORT_DISCOVERING = 2; // dest is disovering export dir
- const static int EXPORT_FREEZING = 3; // we're freezing the dir tree
- const static int EXPORT_PREPPING = 4; // sending dest spanning tree to export bounds
- const static int EXPORT_WARNING = 5; // warning bystanders of dir_auth_pending
- const static int EXPORT_EXPORTING = 6; // sent actual export, waiting for ack
- const static int EXPORT_LOGGINGFINISH = 7; // logging EExportFinish
- const static int EXPORT_NOTIFYING = 8; // waiting for notifyacks
+ const static int EXPORT_CANCELLING = 1; // waiting for cancel notifyacks
+ const static int EXPORT_LOCKING = 2; // acquiring locks
+ const static int EXPORT_DISCOVERING = 3; // dest is disovering export dir
+ const static int EXPORT_FREEZING = 4; // we're freezing the dir tree
+ const static int EXPORT_PREPPING = 5; // sending dest spanning tree to export bounds
+ const static int EXPORT_WARNING = 6; // warning bystanders of dir_auth_pending
+ const static int EXPORT_EXPORTING = 7; // sent actual export, waiting for ack
+ const static int EXPORT_LOGGINGFINISH = 8; // logging EExportFinish
+ const static int EXPORT_NOTIFYING = 9; // waiting for notifyacks
static const char *get_export_statename(int s) {
switch (s) {
+ case EXPORT_CANCELLING: return "cancelling";
case EXPORT_LOCKING: return "locking";
case EXPORT_DISCOVERING: return "discovering";
case EXPORT_FREEZING: return "freezing";
void export_go(CDir *dir);
void export_go_synced(CDir *dir, uint64_t tid);
void export_try_cancel(CDir *dir, bool notify_peer=true);
+ void export_cancel_finish(CDir *dir);
void export_reverse(CDir *dir);
void export_notify_abort(CDir *dir, set<CDir*>& bounds);
void handle_export_ack(MExportDirAck *m);