OPTION(mds_snap_max_uid, OPT_U32, 65536) // The maximum UID allowed to create a snapshot
OPTION(mds_verify_backtrace, OPT_U32, 1)
+OPTION(mds_action_on_write_error, OPT_U32, 1) // 0: ignore; 1: force readonly; 2: crash
+
// If true, compact leveldb store on mount
OPTION(osd_compact_leveldb_on_mount, OPT_BOOL, false)
public:
C_IO_Dir_Committed(CDir *d, version_t v) : CDirIOContext(d), version(v) { }
void finish(int r) {
- assert(r == 0);
- dir->_committed(version);
+ dir->_committed(r, version);
}
};
*
* @param v version i just committed
*/
-void CDir::_committed(version_t v)
+void CDir::_committed(int r, version_t v)
{
+ if (r < 0) {
+ dout(1) << "commit error " << r << " v " << v << dendl;
+ cache->mds->clog->error() << "failed to commit dir " << dirfrag() << " object,"
+ << " errno " << r << "\n";
+ cache->mds->handle_write_error(r);
+ return;
+ }
+
dout(10) << "_committed v " << v << " on " << *this << dendl;
assert(is_auth());
void _commit(version_t want, int op_prio);
void _omap_commit(int op_prio);
void _encode_dentry(CDentry *dn, bufferlist& bl, const std::set<snapid_t> *snaps);
- void _committed(version_t v);
+ void _committed(int r, version_t v);
public:
#if 0 // unused?
void wait_for_commit(Context *c, version_t v=0);
Context *fin;
C_IO_Inode_Stored(CInode *i, version_t v, Context *f) : CInodeIOContext(i), version(v), fin(f) {}
void finish(int r) {
- assert(r == 0);
- in->_stored(version, fin);
+ in->_stored(r, version, fin);
}
};
NULL, newfin);
}
-void CInode::_stored(version_t v, Context *fin)
+void CInode::_stored(int r, version_t v, Context *fin)
{
- dout(10) << "_stored " << v << " " << *this << dendl;
+ if (r < 0) {
+ dout(1) << "store error " << r << " v " << v << " on " << *this << dendl;
+ mdcache->mds->clog->error() << "failed to store ino " << ino() << " object,"
+ << " errno " << r << "\n";
+ mdcache->mds->handle_write_error(r);
+ return;
+ }
+
+ dout(10) << "_stored " << v << " on " << *this << dendl;
if (v == get_projected_version())
mark_clean();
Context *fin;
C_IO_Inode_StoredBacktrace(CInode *i, version_t v, Context *f) : CInodeIOContext(i), version(v), fin(f) {}
void finish(int r) {
- assert(r == 0);
- in->_stored_backtrace(version, fin);
+ in->_stored_backtrace(r, version, fin);
}
};
gather.activate();
}
-void CInode::_stored_backtrace(version_t v, Context *fin)
+void CInode::_stored_backtrace(int r, version_t v, Context *fin)
{
- dout(10) << "_stored_backtrace" << dendl;
+ if (r < 0) {
+ dout(1) << "store backtrace error " << r << " v " << v << dendl;
+ mdcache->mds->clog->error() << "failed to store backtrace on dir ino "
+ << ino() << " object, errno " << r << "\n";
+ mdcache->mds->handle_write_error(r);
+ return;
+ }
+
+ dout(10) << "_stored_backtrace v " << v << dendl;
auth_unpin(this);
if (v == inode.backtrace_version)
void mark_clean();
void store(MDSInternalContextBase *fin);
- void _stored(version_t cv, Context *fin);
+ void _stored(int r, version_t cv, Context *fin);
/**
* Flush a CInode to disk. This includes the backtrace, the parent
* directory's link, and the Inode object itself (if a base directory).
void build_backtrace(int64_t pool, inode_backtrace_t& bt);
void store_backtrace(MDSInternalContextBase *fin, int op_prio=-1);
- void _stored_backtrace(version_t v, Context *fin);
+ void _stored_backtrace(int r, version_t v, Context *fin);
void fetch_backtrace(Context *fin, bufferlist *backtrace);
void _mark_dirty_parent(LogSegment *ls, bool dirty_pool=false);
void clear_dirty_parent();
void finish(int r) {
MDS *mds = get_mds();
-
+ // assume journal is reliable, so don't choose action based on
+ // g_conf->mds_action_on_write_error.
if (r == -EBLACKLISTED) {
derr << "we have been blacklisted (fenced), respawning..." << dendl;
mds->respawn();
_trim_expired_segments();
}
+class C_MaybeExpiredSegment : public MDSInternalContext {
+ MDLog *mdlog;
+ LogSegment *ls;
+ int op_prio;
+ public:
+ C_MaybeExpiredSegment(MDLog *mdl, LogSegment *s, int p) :
+ MDSInternalContext(mdl->mds), mdlog(mdl), ls(s), op_prio(p) {}
+ void finish(int res) {
+ if (res < 0)
+ mdlog->mds->handle_write_error(res);
+ mdlog->_maybe_expired(ls, op_prio);
+ }
+};
/**
* Like ::trim, but instead of trimming to max_segments, trim all but the latest
}
private:
- class C_MaybeExpiredSegment : public MDSInternalContext {
- MDLog *mdlog;
- LogSegment *ls;
- int op_prio;
- public:
- C_MaybeExpiredSegment(MDLog *mdl, LogSegment *s, int p) : MDSInternalContext(mdl->mds), mdlog(mdl), ls(s), op_prio(p) {}
- void finish(int res) {
- mdlog->_maybe_expired(ls, op_prio);
- }
- };
-
void try_expire(LogSegment *ls, int op_prio);
void _maybe_expired(LogSegment *ls, int op_prio);
void _expired(LogSegment *ls);
void _trim_expired_segments();
+ friend class C_MaybeExpiredSegment;
+
public:
void trim_expired_segments();
void trim(int max=-1);
suicide();
}
+void MDS::handle_write_error(int err)
+{
+ if (err == -EBLACKLISTED) {
+ derr << "we have been blacklisted (fenced), respawning..." << dendl;
+ respawn();
+ return;
+ }
-
+ if (g_conf->mds_action_on_write_error >= 2) {
+ derr << "unhandled write error " << cpp_strerror(err) << ", suicide..." << dendl;
+ suicide();
+ } else if (g_conf->mds_action_on_write_error == 1) {
+ derr << "unhandled write error " << cpp_strerror(err) << ", force readonly..." << dendl;
+ mdcache->force_readonly();
+ } else {
+ // ignore;
+ derr << "unhandled write error " << cpp_strerror(err) << ", ignore..." << dendl;
+ }
+}
bool MDS::ms_dispatch(Message *m)
{
void suicide();
void respawn();
+ void handle_write_error(int err);
void tick();
void MDSTable::save_2(int r, version_t v)
{
- dout(10) << "save_2 v " << v << dendl;
- if (r == -EBLACKLISTED) {
- mds->suicide();
- return;
- }
if (r < 0) {
- dout(10) << "save_2 could not write table: " << r << dendl;
- assert(r >= 0);
+ dout(1) << "save error " << r << " v " << v << dendl;
+ mds->clog->error() << "failed to store table " << table_name << " object,"
+ << " errno " << r << "\n";
+ mds->handle_write_error(r);
+ return;
}
- assert(r >= 0);
+
+ dout(10) << "save_2 v " << v << dendl;
committed_version = v;
list<MDSInternalContextBase*> ls;