mount_cond.Signal();
}
-
void Client::handle_osd_map(MOSDMap *m)
{
if (objecter->osdmap_full_flag()) {
ldout(cct, 1) << __func__ << ": FULL: cancelling outstanding operations" << dendl;
-
- // For all inodes with a pending flush write op (i.e. one of the ones we
- // will cancel), we've got to purge_set their data from ObjectCacher
- // so that it doesn't re-issue the write in response to the ENOSPC error.
-
- // We can *only* do this if there is a file handle open, because otherwise
- // there is nobody to surface the error code to, and we would be silently
- // dropping data
-
// Cancel all outstanding ops with -ENOSPC: it is necessary to do this rather than blocking,
// because otherwise when we fill up we potentially lock caps forever on files with
// dirty pages, and we need to be able to release those caps to the MDS so that it can
// delete files and free up space.
epoch_t cancelled_epoch = objecter->op_cancel_writes(-ENOSPC);
+ // For all inodes with a pending flush write op (i.e. one of the ones we
+ // will cancel), we've got to purge_set their data from ObjectCacher
+ // so that it doesn't re-issue the write in response to the ENOSPC error.
+ // Fortunately since we're cancelling *everything*, we don't need to know
+ // which ops belong to which ObjectSet, we can just blow all the un-flushed
+ // cached data away and mark any dirty inodes' async_err field with -ENOSPC
+ // (i.e. we only need to know which inodes had outstanding ops, not the exact
+ // op-to-inode relation)
+ for (unordered_map<vinodeno_t,Inode*>::iterator i = inode_map.begin();
+ i != inode_map.end(); i++)
+ {
+ Inode *inode = i->second;
+ if (inode->oset.dirty_or_tx) {
+ ldout(cct, 4) << __func__ << ": FULL: inode 0x" << std::hex << i->first << std::dec
+ << " has dirty objects, purging and setting ENOSPC" << dendl;
+ objectcacher->purge_set(&inode->oset);
+ inode->async_err = -ENOSPC;
+ }
+ }
set_cap_epoch_barrier(cancelled_epoch);
}
}
}
-
-class C_Client_PutInode : public Context {
- Client *client;
- Inode *in;
-public:
- C_Client_PutInode(Client *c, Inode *i) : client(c), in(i) {
- in->get();
- }
- void finish(int) {
- // I am used via ObjectCacher, which is responsible for taking
- // the client lock before calling me back.
- assert(client->client_lock.is_locked_by_me());
- client->put_inode(in);
- }
-};
-
bool Client::_flush(Inode *in, Context *onfinish)
{
ldout(cct, 10) << "_flush " << *in << dendl;
if (!in->oset.dirty_or_tx) {
ldout(cct, 10) << " nothing to flush" << dendl;
- if (onfinish)
- onfinish->complete(0);
+ onfinish->complete(0);
return true;
}
- if (!onfinish) {
- onfinish = new C_Client_PutInode(this, in);
- }
-
if (objecter->osdmap_full_flag()) {
ldout(cct, 1) << __func__ << ": FULL, purging for ENOSPC" << dendl;
objectcacher->purge_set(&in->oset);
}
}
+/**
+ * For asynchronous flushes, check for errors from the IO and
+ * update the inode if necessary
+ */
+class C_Client_FlushComplete : public Context {
+ private:
+ Client *client;
+ Inode *inode;
+
+ public:
+ C_Client_FlushComplete(Client *c, Inode *in) : client(c), inode(in)
+ {
+ inode->get();
+ }
+
+ void finish(int r) {
+ assert(client->client_lock.is_locked_by_me());
+ if (r != 0) {
+ client_t const whoami = client->whoami; // For the benefit of ldout prefix
+ ldout(client->cct, 1) << "I/O error from flush on inode " << inode
+ << " 0x" << std::hex << inode->ino << std::dec
+ << ": " << r << "(" << cpp_strerror(r) << ")" << dendl;
+ inode->async_err = r;
+ }
+ client->put_inode(inode);
+ }
+};
+
void Client::handle_cap_grant(MetaSession *session, Inode *in, Cap *cap, MClientCaps *m)
{
mds_rank_t mds = session->mds_num;
cap->issued = new_caps;
cap->implemented |= new_caps;
-
- if (((used & ~new_caps) & CEPH_CAP_FILE_BUFFER) &&
- !_flush(in)) {
+ if (((used & ~new_caps) & CEPH_CAP_FILE_BUFFER)
+ && !_flush(in, new C_Client_FlushComplete(this, in))) {
// waitin' for flush
} else if ((old_caps & ~new_caps) & CEPH_CAP_FILE_CACHE) {
_release(in);
while (!fd_map.empty()) {
Fh *fh = fd_map.begin()->second;
fd_map.erase(fd_map.begin());
- ldout(cct, 0) << " destroying lost open file " << fh << " on " << *fh->inode << dendl;
- _release_fh(fh);
+ int release_err = _release_fh(fh);
+ ldout(cct, 0) << " destroyed lost open file " << fh << " on " << *fh->inode << "(async_err = " << release_err << ")" << dendl;
+
}
_ll_drop_pins();
if (!in->caps.empty()) {
in->get();
_release(in);
- _flush(in);
+ _flush(in, new C_Client_FlushComplete(this, in));
put_inode(in);
}
}
if (in->snapid == CEPH_NOSNAP) {
if (in->put_open_ref(f->mode)) {
- _flush(in);
+ _flush(in, new C_Client_FlushComplete(this, in));
// release clean pages too, if we dont want RDCACHE
if (in->cap_refs[CEPH_CAP_FILE_CACHE] == 0 &&
!(in->caps_wanted() & CEPH_CAP_FILE_CACHE) &&
_release_filelocks(f);
+ // Finally, read any async err (i.e. from flushes) from the inode
+ int err = in->async_err;
+ if (err != 0) {
+ ldout(cct, 1) << "_release_fh " << f << " on inode " << *in << " caught async_err = "
+ << cpp_strerror(err) << dendl;
+ } else {
+ ldout(cct, 10) << "_release_fh " << f << " on inode " << *in << " no async_err state" << dendl;
+ }
+
put_inode(in);
delete f;
- return 0;
+ return err;
}
int Client::_open(Inode *in, int flags, mode_t mode, Fh **fhp, int uid, int gid)
Fh *fh = get_filehandle(fd);
if (!fh)
return -EBADF;
- _release_fh(fh);
+ int err = _release_fh(fh);
fd_map.erase(fd);
ldout(cct, 3) << "close exit(" << fd << ")" << dendl;
- return 0;
+ return err;
}
int Client::_flush(Fh *f)
{
- // no-op, for now. hrm.
- return 0;
+ Inode *in = f->inode;
+ int err = in->async_err;
+ if (err != 0) {
+ ldout(cct, 1) << __func__ << ": " << f << " on inode " << *in << " caught async_err = "
+ << cpp_strerror(err) << dendl;
+ } else {
+ ldout(cct, 10) << __func__ << ": " << f << " on inode " << *in << " no async_err state" << dendl;
+ }
+
+ return err;
}
int Client::truncate(const char *relpath, loff_t length)
ldout(cct, 1) << "ino " << in->ino << " failed to commit to disk! "
<< cpp_strerror(-r) << dendl;
}
+
+ if (in->async_err) {
+ ldout(cct, 1) << "ino " << in->ino << " marked with error from background flush! "
+ << cpp_strerror(in->async_err) << dendl;
+ r = in->async_err;
+ }
+
return r;
}
r = check_permissions(in, flags, uid, gid);
if (r < 0) {
if (fhp && *fhp) {
- _release_fh(*fhp);
+ int release_r = _release_fh(*fhp);
+ assert(release_r == 0); // during create, no async data ops should have happened
}
goto out;
}
tout(cct) << "ll_release (fh)" << std::endl;
tout(cct) << (unsigned long)fh << std::endl;
- _release_fh(fh);
- return 0;
+ return _release_fh(fh);
}
int Client::ll_getlk(Fh *fh, struct flock *fl, uint64_t owner)