[PATCH] A kernel NFSD patch too long.

G. Allen Morris III (gam3@harpo.ixlabs.com)
Fri, 19 Feb 1999 15:06:11 -0800


nfsd-2.2.1-1.patch

CHANGES:

1. Expire cache periodicly. Before the cache was only expired on
access from a client.
(expire_all())

2. Expire cache for deleted files. Before these would stay in the
cache until expired normally.
(expire_by_dentry())

3. Change to fs/nfs/dir.c to allow

mkdir zappa
(mv frank zappa; echo This did not work) > frank

to work. THIS MAY BREAK RENAME FOR UNFSD.

4. Change of buffering for readdir from RC_REPLSTAT to RC_REPLBUFF.
(This is already in Alan's `ac' code.)

5. The `nfsd_create: dentry filename not negative!' message should
only be seen if there was a race to lock the parent.

6. Added fb_version field to nfs_filehandle. This will be used in the
near future. (The file handle is now 32 bits long.)

7. Change to fs/ext2/ialloc.c

8. Miscellaneous whitespace changes.

TODO:

1. return STALE if fh_version is not correct.

2. cache dentries to handle RENAME.

Allen Morris <gam3@acm.org>

---------------------- nfsd-2.2.1-1.patch ------------------------------
diff -u -X exclude linux-2.2.1/include/linux/nfsd/nfsfh.h linux/include/linux/nfsd/nfsfh.h
--- linux-2.2.1/include/linux/nfsd/nfsfh.h Thu Oct 8 21:32:12 1998
+++ linux/include/linux/nfsd/nfsfh.h Fri Feb 19 12:34:50 1999
@@ -33,6 +33,7 @@
__u32 fb_dev; /* our device */
__u32 fb_xdev;
__u32 fb_xino;
+ __u32 fb_version;
};

#define NFS_FH_PADDING (NFS_FHSIZE - sizeof(struct nfs_fhbase))
@@ -47,6 +48,7 @@
#define fh_dev fh_base.fb_dev
#define fh_xdev fh_base.fb_xdev
#define fh_xino fh_base.fb_xino
+#define fh_version fh_base.fb_version

#ifdef __KERNEL__

@@ -106,6 +108,9 @@
void nfsd_fh_flush(kdev_t);
void nfsd_fh_init(void);
void nfsd_fh_free(void);
+void expire_all(void);
+void expire_by_dentry(struct dentry *);
+

static __inline__ struct svc_fh *
fh_copy(struct svc_fh *dst, struct svc_fh *src)
@@ -176,34 +181,6 @@
up(&inode->i_sem);
}
}
-
-/*
- * Release an inode
- */
-#if 0
-#define fh_put(fhp) __fh_put(fhp, __FILE__, __LINE__)
-
-static inline void
-__fh_put(struct svc_fh *fhp, char *file, int line)
-{
- struct dentry *dentry;
-
- if (!fhp->fh_dverified)
- return;
-
- dentry = fhp->fh_dentry;
- if (!dentry->d_count) {
- printk("nfsd: trying to free free dentry in %s:%d\n"
- " file %s/%s\n",
- file, line,
- dentry->d_parent->d_name.name, dentry->d_name.name);
- } else {
- fh_unlock(fhp);
- fhp->fh_dverified = 0;
- dput(dentry);
- }
-}
-#endif

#endif /* __KERNEL__ */

diff -u -X exclude linux-2.2.1/fs/nfs/dir.c linux/fs/nfs/dir.c
--- linux-2.2.1/fs/nfs/dir.c Tue Jan 19 11:22:18 1999
+++ linux/fs/nfs/dir.c Fri Feb 19 12:34:50 1999
@@ -1158,8 +1158,9 @@
*/
if (old_dentry->d_count > 1) {
nfs_wb_all(old_inode);
- shrink_dcache_parent(old_dentry);
+/* shrink_dcache_parent(old_dentry); FIXME */
}
+ goto do_rename;

/*
* Now check the use counts ... we can't safely do the
diff -u -X exclude linux-2.2.1/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
--- linux-2.2.1/fs/nfsd/nfsfh.c Tue Jan 19 11:22:18 1999
+++ linux/fs/nfsd/nfsfh.c Fri Feb 19 14:52:30 1999
@@ -210,9 +210,9 @@
if (new) {
new->users = 0;
new->reftime = jiffies;
- new->ino = inode->i_ino;
- new->dev = inode->i_dev;
- result = copy_path(new->name, dentry, len);
+ new->ino = inode->i_ino;
+ new->dev = inode->i_dev;
+ result = copy_path(new->name, dentry, len);
if (!result)
goto retry;
list_add(&new->lru, &path_inuse);
@@ -299,7 +299,7 @@
int result = 0;

buf->sequence++;
-#ifdef NFSD_DEBUG_VERBOSE
+#ifdef NFSD_DEBUG_VERY_VERBOSE
printk("filldir_one: seq=%d, ino=%ld, name=%s\n", buf->sequence, ino, name);
#endif
if (buf->sequence == 2) {
@@ -309,7 +309,7 @@
if (dirent->ino == ino) {
dirent->len = len;
memcpy(dirent->name, name, len);
- dirent->name[len] = 0;
+ dirent->name[len] = '\0';
buf->found = 1;
result = -1;
}
@@ -519,7 +519,8 @@
struct dentry *dentry = empty->dentry;

#ifdef NFSD_DEBUG_VERBOSE
-printk("expire_fhe: expiring %s/%s, d_count=%d, ino=%ld\n",
+printk("expire_fhe: expiring %s %s/%s, d_count=%d, ino=%ld\n",
+(cache == NFSD_FILE_CACHE) ? "file" : "dir",
dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count,empty->ino);
#endif
empty->dentry = NULL; /* no dentry */
@@ -578,7 +579,7 @@
struct fh_entry *fhe;
int i;

-#ifdef NFSD_DEBUG_VERBOSE
+#ifdef NFSD_DEBUG_VERY_VERBOSE
printk("expire_old: expiring %s older than %d\n",
(cache == NFSD_FILE_CACHE) ? "file" : "dir", age);
#endif
@@ -749,6 +750,21 @@
return dentry;
}

+static u32
+get_version(struct inode *inode)
+{
+ u32 ret = 0;
+
+/* FIXME: there should be a flag s_type */
+ if (inode && !strcmp("ext2", inode->i_sb->s_type->name)) {
+ ret = ino_t_to_u32(inode->u.ext2_i.i_version);
+#if 0
+ printk("ext2 %u %u\n", inode->i_ino, inode->u.ext2_i.i_version);
+#endif
+ }
+ return ret;
+}
+
/*
* Look for an entry in the file cache matching the dentry pointer,
* and verify that the (dev, inode) numbers are correct. If found,
@@ -777,6 +793,11 @@
goto out;
if (inode->i_dev != u32_to_kdev_t(fh->fh_dev))
goto out;
+ if (get_version(inode) != fh->fh_version) {
+printk("find_dentry_in_fhcache: Bad version %u %u\n",
+ get_version(inode), fh->fh_version);
+/* goto out; */
+ }

fhe->dentry = NULL;
fhe->ino = 0;
@@ -841,7 +862,6 @@
dentry = NULL;
out:
return dentry;
-
}

/*
@@ -999,14 +1019,34 @@

/*
* Perform any needed housekeeping ...
- * N.B. move this into one of the daemons ...
*/
+ expire_all();
+ return dentry;
+}
+
+void
+expire_all(void)
+{
if (time_after_eq(jiffies, nfsd_next_expire)) {
expire_old(NFSD_FILE_CACHE, 5*HZ);
expire_old(NFSD_DIR_CACHE , 60*HZ);
nfsd_next_expire = jiffies + 5*HZ;
}
- return dentry;
+}
+
+/*
+ * Free cache after unlink.
+ * FIXME: (gam3) only do files now, should directories also be done?
+ */
+void
+expire_by_dentry(struct dentry *dentry)
+{
+ struct fh_entry *fhe;
+
+ fhe = find_fhe(dentry, NFSD_FILE_CACHE, NULL);
+ if (fhe) {
+ expire_fhe(fhe, NFSD_FILE_CACHE);
+ }
}

/*
@@ -1106,13 +1146,13 @@
if (exp->ex_dentry == tdentry)
break;
/* executable only by root and we can't be root */
- if (current->fsuid &&
- !(tdentry->d_inode->i_uid &&
- (tdentry->d_inode->i_mode & S_IXUSR)) &&
- !(tdentry->d_inode->i_gid &&
- (tdentry->d_inode->i_mode & S_IXGRP)) &&
- !(tdentry->d_inode->i_mode & S_IXOTH) &&
- (exp->ex_flags & NFSEXP_ROOTSQUASH)) {
+ if (current->fsuid
+ && !(tdentry->d_inode->i_uid
+ && (tdentry->d_inode->i_mode & S_IXUSR))
+ && !(tdentry->d_inode->i_gid
+ && (tdentry->d_inode->i_mode & S_IXGRP))
+ && !(tdentry->d_inode->i_mode & S_IXOTH)
+ && (exp->ex_flags & NFSEXP_ROOTSQUASH)) {
error = nfserr_stale;
dprintk("fh_verify: no root_squashed access.\n");
}
@@ -1171,6 +1211,7 @@
fhp->fh_handle.fh_dcookie = dentry;
if (inode) {
fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino);
+ fhp->fh_handle.fh_version = get_version(inode);
}
fhp->fh_handle.fh_dirino = ino_t_to_u32(parent->d_inode->i_ino);
fhp->fh_handle.fh_dev = kdev_t_to_u32(parent->d_inode->i_dev);
@@ -1202,6 +1243,7 @@
if (!inode)
goto out_negative;
fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino);
+ fhp->fh_handle.fh_version = get_version(inode);
out:
return;

diff -u -X exclude linux-2.2.1/fs/nfsd/nfsproc.c linux/fs/nfsd/nfsproc.c
--- linux-2.2.1/fs/nfsd/nfsproc.c Wed Jul 22 14:37:36 1998
+++ linux/fs/nfsd/nfsproc.c Fri Feb 19 12:34:50 1999
@@ -515,7 +515,7 @@
PROC(symlink, symlinkargs, void, none, RC_REPLSTAT),
PROC(mkdir, createargs, diropres, fhandle, RC_REPLBUFF),
PROC(rmdir, diropargs, void, none, RC_REPLSTAT),
- PROC(readdir, readdirargs, readdirres, none, RC_REPLSTAT),
+ PROC(readdir, readdirargs, readdirres, none, RC_REPLBUFF),
PROC(statfs, fhandle, statfsres, none, RC_NOCACHE),
};

diff -u -X exclude linux-2.2.1/fs/nfsd/nfssvc.c linux/fs/nfsd/nfssvc.c
--- linux-2.2.1/fs/nfsd/nfssvc.c Fri Jan 1 23:03:09 1999
+++ linux/fs/nfsd/nfssvc.c Fri Feb 19 12:34:50 1999
@@ -5,7 +5,7 @@
*
* Authors: Olaf Kirch (okir@monad.swb.de)
*
- * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995-1999 Olaf Kirch <okir@monad.swb.de>
*/

#define __NO_VERSION__
@@ -101,7 +101,7 @@
nfsd(struct svc_rqst *rqstp)
{
struct svc_serv *serv = rqstp->rq_server;
- int oldumask, err;
+ int oldumask, err, first = 0;

/* Lock module and set up kernel thread */
MOD_INC_USE_COUNT;
@@ -115,8 +115,10 @@

oldumask = current->fs->umask; /* Set umask to 0. */
current->fs->umask = 0;
- if (!nfsd_active++)
+ if (!nfsd_active++) {
nfssvc_boot = xtime; /* record boot time */
+ first = 1;
+ }
lockd_up(); /* start lockd */

/*
@@ -133,8 +135,14 @@
* Find a socket with data available and call its
* recvfrom routine.
*/
- while ((err = svc_recv(serv, rqstp, MAX_SCHEDULE_TIMEOUT)) == -EAGAIN)
- ;
+ while ((err = svc_recv(serv, rqstp,
+ first?5*HZ:MAX_SCHEDULE_TIMEOUT)) == -EAGAIN) {
+ if (first && 1) {
+ exp_readlock();
+ expire_all();
+ exp_unlock();
+ }
+ }
if (err < 0)
break;

diff -u -X exclude linux-2.2.1/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- linux-2.2.1/fs/nfsd/vfs.c Tue Feb 2 09:53:31 1999
+++ linux/fs/nfsd/vfs.c Fri Feb 19 12:34:50 1999
@@ -658,10 +658,6 @@
if (IS_ERR(dchild))
goto out_nfserr;
fh_compose(resfhp, fhp->fh_export, dchild);
- /* Lock the parent and check for errors ... */
- err = fh_lock_parent(fhp, dchild);
- if (err)
- goto out;
} else {
dchild = resfhp->fh_dentry;
if (!fhp->fh_locked)
@@ -670,6 +666,15 @@
dentry->d_parent->d_name.name,
dentry->d_name.name);
}
+ err = nfserr_exist;
+ if (dchild->d_inode)
+ goto out;
+ if (!fhp->fh_locked) {
+ /* Lock the parent and check for errors ... */
+ err = fh_lock_parent(fhp, dchild);
+ if (err)
+ goto out;
+ }
/*
* Make sure the child dentry is still negative ...
*/
@@ -695,8 +700,7 @@
case S_IFCHR:
case S_IFBLK:
/* The client is _NOT_ required to do security enforcement */
- if(!capable(CAP_SYS_ADMIN))
- {
+ if(!capable(CAP_SYS_ADMIN)) {
err = -EPERM;
goto out;
}
@@ -734,7 +738,7 @@
* directories via NFS.
*/
err = 0;
- if ((iap->ia_valid &= (ATTR_UID|ATTR_GID|ATTR_MODE)) != 0)
+ if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID|ATTR_MODE)) != 0)
err = nfsd_setattr(rqstp, resfhp, iap);
out:
return err;
@@ -899,6 +903,7 @@
/* Compose the fh so the dentry will be freed ... */
out_compose:
fh_compose(resfhp, fhp->fh_export, dnew);
+
out:
return err;

@@ -1132,7 +1137,6 @@
err = PTR_ERR(rdentry);
if (IS_ERR(rdentry))
goto out_nfserr;
-
if (!rdentry->d_inode) {
dput(rdentry);
err = nfserr_noent;
@@ -1151,7 +1155,7 @@
fh_unlock(fhp);

dput(rdentry);
-
+ expire_by_dentry(rdentry);
} else {
/* It's RMDIR */
/* See comments in fs/namei.c:do_rmdir */
@@ -1179,6 +1183,7 @@
goto out_nfserr;
if (EX_ISSYNC(fhp->fh_export))
write_inode_now(dirp);
+
out:
return err;

@@ -1330,7 +1335,7 @@
inode->i_uid, inode->i_gid, current->fsuid, current->fsgid);
#endif
#ifndef CONFIG_NFSD_SUN
- if (dentry->d_mounts != dentry) {
+ if (dentry->d_mounts != dentry) {
return nfserr_perm;
}
#endif
diff -u -X exclude linux-2.2.1/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
--- linux-2.2.1/fs/ext2/ialloc.c Thu Nov 12 08:32:58 1998
+++ linux/fs/ext2/ialloc.c Fri Feb 19 14:47:55 1999
@@ -268,15 +268,16 @@
}

/*
- * This function increments the inode version number
+ * This function sets the inode version number
*
- * This may be used one day by the NFS server
+ * This is used by the NFS server
*/
-static void inc_inode_version (struct inode * inode,
+static void get_inode_version(struct inode * inode,
struct ext2_group_desc *gdp,
int mode)
{
- inode->u.ext2_i.i_version++;
+ get_random_bytes(&inode->u.ext2_i.i_version,
+ sizeof(inode->u.ext2_i.i_version));
mark_inode_dirty(inode);

return;
@@ -494,10 +495,10 @@
inode->i_flags |= MS_SYNCHRONOUS;
insert_inode_hash(inode);
mark_inode_dirty(inode);
- inc_inode_version (inode, gdp, mode);
+ get_inode_version(inode, gdp, mode);

unlock_super (sb);
- if(DQUOT_ALLOC_INODE(sb, inode)) {
+ if (DQUOT_ALLOC_INODE(sb, inode)) {
sb->dq_op->drop(inode);
inode->i_nlink = 0;
iput(inode);

--
G. Allen Morris III <gam3@acm.org>

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/