diff --git a/src/include/ceph_fs.h b/src/include/ceph_fs.h index 769f51966a4..08ef460bfe0 100644 --- a/src/include/ceph_fs.h +++ b/src/include/ceph_fs.h @@ -409,6 +409,7 @@ union ceph_mds_request_args { } __attribute__ ((packed)) open; struct { __le32 flags; + __le32 osdmap_epoch; /* use for set file/dir layout */ } __attribute__ ((packed)) setxattr; struct { struct ceph_file_layout layout; diff --git a/src/mds/Server.cc b/src/mds/Server.cc index be3a9269975..2b7554cf09a 100644 --- a/src/mds/Server.cc +++ b/src/mds/Server.cc @@ -4019,16 +4019,21 @@ void Server::handle_set_vxattr(MDRequestRef& mdr, CInode *cur, rest = name.substr(name.find("layout")); const OSDMap *osdmap = mds->objecter->get_osdmap_read(); int r = parse_layout_vxattr(rest, value, osdmap, &layout); + epoch_t epoch = osdmap->get_epoch(); mds->objecter->put_osdmap_read(); if (r < 0) { if (r == -ENOENT) { - if (!mdr->waited_for_osdmap) { - // make sure we have the latest map. - // FIXME: we should get the client's osdmap epoch and just - // make sure we have *that*. + epoch_t req_epoch = req->get_osdmap_epoch(); + if (req_epoch > epoch) { + if (!mds->objecter->wait_for_map(req_epoch, + new C_OnFinisher(new C_IO_Wrapper(mds, new C_MDS_RetryRequest(mdcache, mdr)), mds->finisher))) + return; + } else if (req_epoch == 0 && !mdr->waited_for_osdmap) { + // For compatibility with client w/ old code, we still need get the latest map. + // One day if COMPACT_VERSION of MClientRequest >=3, we can remove those code. mdr->waited_for_osdmap = true; mds->objecter->wait_for_latest_osdmap( - new C_OnFinisher(new C_IO_Wrapper(mds, new C_MDS_RetryRequest(mdcache, mdr)), mds->finisher)); + new C_OnFinisher(new C_IO_Wrapper(mds, new C_MDS_RetryRequest(mdcache, mdr)), mds->finisher)); return; } r = -EINVAL; @@ -4057,13 +4062,18 @@ void Server::handle_set_vxattr(MDRequestRef& mdr, CInode *cur, rest = name.substr(name.find("layout")); const OSDMap *osdmap = mds->objecter->get_osdmap_read(); int r = parse_layout_vxattr(rest, value, osdmap, &layout); + epoch_t epoch = osdmap->get_epoch(); mds->objecter->put_osdmap_read(); if (r < 0) { if (r == -ENOENT) { - if (!mdr->waited_for_osdmap) { - // make sure we have the latest map. - // FIXME: we should get the client's osdmap epoch and just - // make sure we have *that*. + epoch_t req_epoch = req->get_osdmap_epoch(); + if (req_epoch > epoch) { + if (!mds->objecter->wait_for_map(req_epoch, + new C_OnFinisher(new C_IO_Wrapper(mds, new C_MDS_RetryRequest(mdcache, mdr)), mds->finisher))) + return; + } else if (req_epoch == 0 && !mdr->waited_for_osdmap) { + // For compatibility with client w/ old code, we still need get the latest map. + // One day if COMPACT_VERSION of MClientRequest >=3, we can remove those code. mdr->waited_for_osdmap = true; mds->objecter->wait_for_latest_osdmap( new C_OnFinisher(new C_IO_Wrapper(mds, new C_MDS_RetryRequest(mdcache, mdr)), mds->finisher)); diff --git a/src/messages/MClientRequest.h b/src/messages/MClientRequest.h index 35dbb175766..1c374591057 100644 --- a/src/messages/MClientRequest.h +++ b/src/messages/MClientRequest.h @@ -46,7 +46,7 @@ // metadata ops. class MClientRequest : public Message { - static const int HEAD_VERSION = 2; + static const int HEAD_VERSION = 3; static const int COMPAT_VERSION = 1; public: @@ -93,6 +93,17 @@ private: public: void set_mdsmap_epoch(epoch_t e) { head.mdsmap_epoch = e; } epoch_t get_mdsmap_epoch() { return head.mdsmap_epoch; } + epoch_t get_osdmap_epoch() const { + assert(head.op == CEPH_MDS_OP_SETXATTR); + if (header.version >= 3) + return head.args.setxattr.osdmap_epoch; + else + return 0; + } + void set_osdmap_epoch(epoch_t e) { + assert(head.op == CEPH_MDS_OP_SETXATTR); + head.args.setxattr.osdmap_epoch = e; + } metareqid_t get_reqid() { // FIXME: for now, assume clients always have 1 incarnation