From 838ebddb61bc497dccf548a04fd42ebeb70d1e64 Mon Sep 17 00:00:00 2001 From: Greg Farnum Date: Tue, 4 Jan 2011 13:32:47 -0800 Subject: [PATCH] uclient: Switch how inodes link to dentries a bit. Inodes now have a set of parent dentries, rather than a single pointer. This allows the cache to accurately represent multiple hard links. Various minor adjustments were made so that this change in format works and is error checked. Signed-off-by: Greg Farnum --- src/client/Client.cc | 51 +++++++++++++++++++++++++++++--------------- src/client/Client.h | 48 ++++++++++++++++++++++++----------------- 2 files changed, 62 insertions(+), 37 deletions(-) diff --git a/src/client/Client.cc b/src/client/Client.cc index 46071dd9bd975..124ea419668aa 100644 --- a/src/client/Client.cc +++ b/src/client/Client.cc @@ -89,8 +89,12 @@ ostream& operator<<(ostream &out, Inode &in) out << " dirty_caps=" << ccap_string(in.dirty_caps); if (in.flushing_caps) out << " flushing_caps=" << ccap_string(in.flushing_caps); - out << " parent=" << in.dn - << ")"; + set::iterator i = in.dn_set.begin(); + while(i != in.dn_set.end()) { + out << " parent=" << *i; + ++i; + } + out << ")"; return out; } @@ -542,7 +546,8 @@ Inode * Client::add_update_inode(InodeStat *st, utime_t from, int mds) * insert_dentry_inode - insert + link a single dentry + inode into the metadata cache. */ Dentry *Client::insert_dentry_inode(Dir *dir, const string& dname, LeaseStat *dlease, - Inode *in, utime_t from, int mds, bool set_offset) + Inode *in, utime_t from, int mds, bool set_offset, + Dentry *old_dentry) { utime_t dttl = from; dttl += (float)dlease->duration_ms / 1000.0; @@ -572,11 +577,11 @@ Dentry *Client::insert_dentry_inode(Dir *dir, const string& dname, LeaseStat *dl if (!dn || dn->inode == 0) { // have inode linked elsewhere? -> unlink and relink! - if (in->dn) { + if (!in->dn_set.empty() && old_dentry) { dout(12) << " had vino " << in->vino() - << " not linked or linked at the right position, relinking" + << " linked at the wrong position, relinking" << dendl; - dn = relink_inode(dir, dname, in, dn); + dn = relink_inode(dir, dname, in, old_dentry, dn); } else { // link dout(12) << " had vino " << in->vino() @@ -696,7 +701,9 @@ Inode* Client::insert_trace(MetaRequest *request, utime_t from, int mds) if (in) { Dir *dir = diri->open_dir(); - insert_dentry_inode(dir, dname, &dlease, in, from, mds, true); + insert_dentry_inode(dir, dname, &dlease, in, from, mds, true, + ((request->head.op == CEPH_MDS_OP_RENAME) ? + request->old_dentry : NULL)); } else { Dentry *dn = NULL; if (diri->dir && diri->dir->dentries.count(dname)) { @@ -868,8 +875,11 @@ int Client::choose_target_mds(MetaRequest *req) while (in->snapid != CEPH_NOSNAP) { if (in->snapid == CEPH_SNAPDIR) in = in->snapdir_parent; - else if (in->dn) - in = in->dn->dir->parent_inode; + else if (!in->dn_set.empty()) + /* In most cases there will only be one dentry, so getting it + * will be the correct action. If there are multiple hard links, + * I think the MDS should be able to redirect as needed*/ + in = dentry_of(in)->dir->parent_inode; else { dout(10) << "got unlinked inode, can't look at parent" << dendl; break; @@ -3222,10 +3232,10 @@ int Client::_lookup(Inode *dir, const string& dname, Inode **target) } if (dname == "..") { - if (!dir->dn) + if (dir->dn_set.empty()) r = -ENOENT; else - *target = dir->dn->dir->parent_inode; + *target = dentry_of(dir)->dir->parent_inode; //dirs can't be hard-linked goto done; } @@ -4188,7 +4198,8 @@ int Client::readdir_r_cb(DIR *d, add_dirent_cb_t cb, void *p) if (dirp->offset == 0) { dout(15) << " including ." << dendl; - uint64_t next_off = diri->dn ? 1 : 2; + assert(diri->dn_set.size() < 2); // can't have multiple hard-links to a dir + uint64_t next_off = (!diri->dn_set.empty()) ? 1 : 2; fill_dirent(&de, ".", S_IFDIR, diri->ino, next_off); @@ -4203,8 +4214,8 @@ int Client::readdir_r_cb(DIR *d, add_dirent_cb_t cb, void *p) } if (dirp->offset == 1) { dout(15) << " including .." << dendl; - assert(diri->dn); - Inode *in = diri->dn->inode; + assert(!diri->dn_set.empty()); + Inode *in = dentry_of(diri)->inode; fill_dirent(&de, "..", S_IFDIR, in->ino, 2); fill_stat(in, &st); @@ -5191,7 +5202,8 @@ void Client::getcwd(string& dir) Inode *in = cwd; while (in->ino != CEPH_INO_ROOT) { - Dentry *dn = in->dn; + assert(in->dn_set.size() < 2); // dirs can't be hard-linked + Dentry *dn = dentry_of(in); if (!dn) { // look it up dout(10) << "getcwd looking up parent for " << *in << dendl; @@ -5707,7 +5719,11 @@ int Client::ll_readlink(vinodeno_t vino, const char **value, int uid, int gid) tout << vino.ino.val << std::endl; Inode *in = _ll_get_inode(vino); - if (in->dn) touch_dn(in->dn); + set::iterator dn = in->dn_set.begin(); + while (dn != in->dn_set.end()) { + touch_dn(*dn); + ++dn; + } int r = 0; if (in->is_symlink()) { @@ -6032,7 +6048,8 @@ int Client::_rmdir(Inode *dir, const char *name, int uid, int gid) if (res == 0) { if (dir->dir && dir->dir->dentries.count(name) ) { Dentry *dn = dir->dir->dentries[name]; - if (dn->inode->dir && dn->inode->dir->is_empty()) + if (dn->inode->dir && dn->inode->dir->is_empty() && + (dn->inode->dn_set.size() == 1)) close_dir(dn->inode->dir); // FIXME: maybe i shoudl proactively hose the whole subtree from cache? unlink(dn); } diff --git a/src/client/Client.h b/src/client/Client.h index af02733a0f85e..e7572f09ea7c6 100644 --- a/src/client/Client.h +++ b/src/client/Client.h @@ -449,7 +449,7 @@ class Inode { int ref; // ref count. 1 for each dentry, fh that links to me. int ll_ref; // separate ref count for ll client Dir *dir; // if i'm a dir. - Dentry *dn; // if i'm linked to a dentry. + set dn_set; // if i'm linked to a dentry. string symlink; // symlink content, if it's a symlink fragtree_t dirfragtree; map xattrs; @@ -461,12 +461,13 @@ class Inode { // bool hack_balance_reads; // +#define dentry_of(a) (*(a->dn_set.begin())) void make_long_path(filepath& p) { - if (dn) { - assert(dn->dir && dn->dir->parent_inode); - dn->dir->parent_inode->make_long_path(p); - p.push_dentry(dn->name); + if (!dn_set.empty()) { + assert((*dn_set.begin())->dir && (*dn_set.begin())->dir->parent_inode); + (*dn_set.begin())->dir->parent_inode->make_long_path(p); + p.push_dentry((*dn_set.begin())->name); } else if (snapdir_parent) { snapdir_parent->make_nosnap_relative_path(p); string empty; @@ -487,10 +488,10 @@ class Inode { snapdir_parent->make_nosnap_relative_path(p); string empty; p.push_dentry(empty); - } else if (dn) { - assert(dn->dir && dn->dir->parent_inode); - dn->dir->parent_inode->make_nosnap_relative_path(p); - p.push_dentry(dn->name); + } else if (!dn_set.empty()) { + assert((*dn_set.begin())->dir && (*dn_set.begin())->dir->parent_inode); + (*dn_set.begin())->dir->parent_inode->make_nosnap_relative_path(p); + p.push_dentry((*dn_set.begin())->name); } else { p = filepath(ino); } @@ -528,7 +529,7 @@ class Inode { oset((void *)this, layout->fl_pg_pool, ino), reported_size(0), wanted_max_size(0), requested_max_size(0), ref(0), ll_ref(0), - dir(0), dn(0), + dir(0), dn_set(), hack_balance_reads(false) { memset(&flushing_cap_tid, 0, sizeof(__u16)*CEPH_CAP_BITS); @@ -652,7 +653,8 @@ class Inode { // open Dir for an inode. if it's not open, allocated it (and pin dentry in memory). Dir *open_dir() { if (!dir) { - if (dn) dn->get(); // pin dentry + assert(dn_set.size() < 2); // dirs can't be hard-linked + if (!dn_set.empty()) (*dn_set.begin())->get(); // pin dentry get(); // pin inode dir = new Dir(this); } @@ -936,7 +938,8 @@ protected: assert(dir->is_empty()); Inode *in = dir->parent_inode; - if (in->dn) in->dn->put(); // unpin dentry + assert (in->dn_set.size() < 2); //dirs can't be hard-linked + if (!in->dn_set.empty()) dentry_of(in)->put(); // unpin dentry delete in->dir; in->dir = 0; @@ -966,8 +969,10 @@ protected: if (in) { // link to inode dn->inode = in; - assert(in->dn == 0); - in->dn = dn; + if(!in->dn_set.empty()) + dout(5) << "adding new hard link to " << in->vino() + << " from " << dn << dendl; + in->dn_set.insert(dn); in->get(); if (in->dir) dn->get(); // dir -> dn pin @@ -981,10 +986,9 @@ protected: // unlink from inode if (in) { - assert(in->dn == dn); if (in->dir) dn->put(); // dir -> dn pin dn->inode = 0; - in->dn = 0; + in->dn_set.erase(dn); put_inode(in); } @@ -1000,8 +1004,10 @@ protected: delete dn; } - Dentry *relink_inode(Dir *dir, const string& name, Inode *in, Dentry *newdn=NULL) { - Dentry *olddn = in->dn; + /* If an inode's been moved from one dentry to another + * (via rename, for instance), call this function to move it */ + Dentry *relink_inode(Dir *dir, const string& name, Inode *in, Dentry *olddn, + Dentry *newdn=NULL) { Dir *olddir = olddn->dir; // note: might == dir! bool made_new = false; @@ -1015,7 +1021,8 @@ protected: assert(newdn->inode == NULL); } newdn->inode = in; - in->dn = newdn; + in->dn_set.erase(olddn); + in->dn_set.insert(newdn); if (in->dir) { // dir -> dn pin newdn->get(); @@ -1150,7 +1157,8 @@ protected: int issued); Inode *add_update_inode(InodeStat *st, utime_t ttl, int mds); Dentry *insert_dentry_inode(Dir *dir, const string& dname, LeaseStat *dlease, - Inode *in, utime_t from, int mds, bool set_offset); + Inode *in, utime_t from, int mds, bool set_offset, + Dentry *old_dentry = NULL); // ---------------------- -- 2.39.5