Re: NFSD dentry manipulation (was Re: d_move())

2000-11-23 Thread Chip Salzenberg

According to Neil Brown:
>  You suggest that d_move (or it's caller) must be able to deal with
>  the [second parameter] being an "root" dentry (x->d_parent == x).
>  However, I cannot see that this could possibly happen.

Hmph.  It seems you're right; my d_move changes [1][2] were apparently
not required after all.  However...

The sum of my recent NFS patches incontrovertably turned a totally
unstable server into the very model of stability.  Why?

Let's assume that d_move was a red herring.  What else in the old code
could have cause dentry bugs?  My first thought on that is that the
old code (1) created multiple disconnected dentries for a given inode,
and yet (2) assumed that multiple disconnected dentries would never be
found.  I invite others' opinions.  And I've included a copy of my
recent patches (less d_move) below.

[1] I still think it would be reasonable for d_move() to handle root
dentries, or at least oops on them for debugging.
[2] One bit of the d_splice patches is unrelated to d_move, namely,
the move of 'dput(tdentry)' to the bottom of d_splice, allowing
the 'parent' flag manipulation to finish before calling dput(),
which can sleep.  But knfsd uses the big kernel lock, so I guess
that's probably no issue either.

Index: fs/nfsd/nfsfh.c
--- fs/nfsd/nfsfh.c.prev
+++ fs/nfsd/nfsfh.c Mon Nov 20 22:31:52 2000
@@ -108,4 +108,15 @@ static int get_ino_name(struct dentry *d
}
 
+   if (!error) {
+   /*
+* Check for a fs-specific hash function. Note that we must
+* calculate the standard hash first, as the d_op->d_hash()
+* routine may choose to leave the hash value unchanged.
+*/
+   name->hash = full_name_hash(name->name, name->len);
+   if (dentry->d_op && dentry->d_op->d_hash)
+   error = dentry->d_op->d_hash(dentry, name);
+   }
+
 out_close:
if (file.f_op->release)
@@ -115,4 +126,35 @@ out:
 }
 
+/* Arrange a dentry for the given inode:
+ *  1. Prefer an existing connected dentry.
+ *  2. Settle for an existing disconnected dentry.
+ *  3. If necessary, create a (disconnected) dentry.
+ */
+static struct dentry *nfsd_arrange_dentry(struct inode *inode)
+{
+   struct list_head *lp;
+   struct dentry *result;
+
+   result = NULL;
+   for (lp = inode->i_dentry.next; lp != >i_dentry ; lp=lp->next) {
+   result = list_entry(lp,struct dentry, d_alias);
+   if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED))
+   break;
+   }
+   if (result) {
+   dget(result);
+   iput(inode);
+   } else {
+   result = d_alloc_root(inode, NULL);
+   if (!result) {
+   iput(inode);
+   return ERR_PTR(-ENOMEM);
+   }
+   result->d_flags |= DCACHE_NFSD_DISCONNECTED;
+   d_rehash(result); /* so a dput won't loose it */
+   }
+   return result;
+}
+
 /* this should be provided by each filesystem in an nfsd_operations interface;
  * iget isn't really the right interface
@@ -121,6 +163,4 @@ static struct dentry *nfsd_iget(struct s
 {
struct inode *inode;
-   struct list_head *lp;
-   struct dentry *result;
 
inode = iget(sb, ino);
@@ -149,23 +189,6 @@ static struct dentry *nfsd_iget(struct s
return ERR_PTR(-ESTALE);
}
-   /* now to find a dentry.
-* If possible, get a well-connected one
-*/
-   for (lp = inode->i_dentry.next; lp != >i_dentry ; lp=lp->next) {
-   result = list_entry(lp,struct dentry, d_alias);
-   if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
-   dget(result);
-   iput(inode);
-   return result;
-   }
-   }
-   result = d_alloc_root(inode, NULL);
-   if (result == NULL) {
-   iput(inode);
-   return ERR_PTR(-ENOMEM);
-   }
-   result->d_flags |= DCACHE_NFSD_DISCONNECTED;
-   d_rehash(result); /* so a dput won't loose it */
-   return result;
+
+   return nfsd_arrange_dentry(inode);
 }
 
@@ -228,43 +245,21 @@ int d_splice(struct dentry *target, stru
 struct dentry *nfsd_findparent(struct dentry *child)
 {
-   struct dentry *tdentry, *pdentry;
-   tdentry = d_alloc(child, &(const struct qstr) {"..", 2, 0});
-   if (!tdentry)
+   struct dentry *dotdot, *parent;
+
+   dotdot = d_alloc(child, &(const struct qstr) {"..", 2, 0});
+   if (!dotdot)
return ERR_PTR(-ENOMEM);
+   parent = child->d_inode->i_op->lookup(child->d_inode, dotdot);
+   d_drop(dotdot); /* we never want ".." hashed */
 
-   /* I'm going to assume that if the returned dentry is different, then
-* it is well connected.  But nobody returns different dentrys do they?
-*/
-   pdentry = 

Re: NFSD dentry manipulation (was Re: d_move())

2000-11-23 Thread Chip Salzenberg

According to Neil Brown:
  You suggest that d_move (or it's caller) must be able to deal with
  the [second parameter] being an "root" dentry (x-d_parent == x).
  However, I cannot see that this could possibly happen.

Hmph.  It seems you're right; my d_move changes [1][2] were apparently
not required after all.  However...

The sum of my recent NFS patches incontrovertably turned a totally
unstable server into the very model of stability.  Why?

Let's assume that d_move was a red herring.  What else in the old code
could have cause dentry bugs?  My first thought on that is that the
old code (1) created multiple disconnected dentries for a given inode,
and yet (2) assumed that multiple disconnected dentries would never be
found.  I invite others' opinions.  And I've included a copy of my
recent patches (less d_move) below.

[1] I still think it would be reasonable for d_move() to handle root
dentries, or at least oops on them for debugging.
[2] One bit of the d_splice patches is unrelated to d_move, namely,
the move of 'dput(tdentry)' to the bottom of d_splice, allowing
the 'parent' flag manipulation to finish before calling dput(),
which can sleep.  But knfsd uses the big kernel lock, so I guess
that's probably no issue either.

Index: fs/nfsd/nfsfh.c
--- fs/nfsd/nfsfh.c.prev
+++ fs/nfsd/nfsfh.c Mon Nov 20 22:31:52 2000
@@ -108,4 +108,15 @@ static int get_ino_name(struct dentry *d
}
 
+   if (!error) {
+   /*
+* Check for a fs-specific hash function. Note that we must
+* calculate the standard hash first, as the d_op-d_hash()
+* routine may choose to leave the hash value unchanged.
+*/
+   name-hash = full_name_hash(name-name, name-len);
+   if (dentry-d_op  dentry-d_op-d_hash)
+   error = dentry-d_op-d_hash(dentry, name);
+   }
+
 out_close:
if (file.f_op-release)
@@ -115,4 +126,35 @@ out:
 }
 
+/* Arrange a dentry for the given inode:
+ *  1. Prefer an existing connected dentry.
+ *  2. Settle for an existing disconnected dentry.
+ *  3. If necessary, create a (disconnected) dentry.
+ */
+static struct dentry *nfsd_arrange_dentry(struct inode *inode)
+{
+   struct list_head *lp;
+   struct dentry *result;
+
+   result = NULL;
+   for (lp = inode-i_dentry.next; lp != inode-i_dentry ; lp=lp-next) {
+   result = list_entry(lp,struct dentry, d_alias);
+   if (! (result-d_flags  DCACHE_NFSD_DISCONNECTED))
+   break;
+   }
+   if (result) {
+   dget(result);
+   iput(inode);
+   } else {
+   result = d_alloc_root(inode, NULL);
+   if (!result) {
+   iput(inode);
+   return ERR_PTR(-ENOMEM);
+   }
+   result-d_flags |= DCACHE_NFSD_DISCONNECTED;
+   d_rehash(result); /* so a dput won't loose it */
+   }
+   return result;
+}
+
 /* this should be provided by each filesystem in an nfsd_operations interface;
  * iget isn't really the right interface
@@ -121,6 +163,4 @@ static struct dentry *nfsd_iget(struct s
 {
struct inode *inode;
-   struct list_head *lp;
-   struct dentry *result;
 
inode = iget(sb, ino);
@@ -149,23 +189,6 @@ static struct dentry *nfsd_iget(struct s
return ERR_PTR(-ESTALE);
}
-   /* now to find a dentry.
-* If possible, get a well-connected one
-*/
-   for (lp = inode-i_dentry.next; lp != inode-i_dentry ; lp=lp-next) {
-   result = list_entry(lp,struct dentry, d_alias);
-   if (! (result-d_flags  DCACHE_NFSD_DISCONNECTED)) {
-   dget(result);
-   iput(inode);
-   return result;
-   }
-   }
-   result = d_alloc_root(inode, NULL);
-   if (result == NULL) {
-   iput(inode);
-   return ERR_PTR(-ENOMEM);
-   }
-   result-d_flags |= DCACHE_NFSD_DISCONNECTED;
-   d_rehash(result); /* so a dput won't loose it */
-   return result;
+
+   return nfsd_arrange_dentry(inode);
 }
 
@@ -228,43 +245,21 @@ int d_splice(struct dentry *target, stru
 struct dentry *nfsd_findparent(struct dentry *child)
 {
-   struct dentry *tdentry, *pdentry;
-   tdentry = d_alloc(child, (const struct qstr) {"..", 2, 0});
-   if (!tdentry)
+   struct dentry *dotdot, *parent;
+
+   dotdot = d_alloc(child, (const struct qstr) {"..", 2, 0});
+   if (!dotdot)
return ERR_PTR(-ENOMEM);
+   parent = child-d_inode-i_op-lookup(child-d_inode, dotdot);
+   d_drop(dotdot); /* we never want ".." hashed */
 
-   /* I'm going to assume that if the returned dentry is different, then
-* it is well connected.  But nobody returns different dentrys do they?
-*/
-   pdentry = child-d_inode-i_op-lookup(child-d_inode, tdentry);
-