summaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-03-17 22:05:47 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-03-17 22:05:47 +0000
commit27cfca1ec98e91261b1a5355d10a8996464b63af (patch)
tree8e895a53e372fa682b4c0a585b9377d67ed70d0e /fs/nfs
parent6a76fb7214c477ccf6582bd79c5b4ccc4f9c41b1 (diff)
Look Ma' what I found on my harddisk ...
o New faster syscalls for 2.1.x, too o Upgrade to 2.1.89. Don't try to run this. It's flaky as hell. But feel free to debug ...
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/.cvsignore1
-rw-r--r--fs/nfs/dir.c289
-rw-r--r--fs/nfs/file.c54
-rw-r--r--fs/nfs/inode.c301
-rw-r--r--fs/nfs/nfs2xdr.c6
-rw-r--r--fs/nfs/nfs3xdr.c1
-rw-r--r--fs/nfs/nfsroot.c1350
-rw-r--r--fs/nfs/proc.c9
-rw-r--r--fs/nfs/read.c94
-rw-r--r--fs/nfs/symlink.c50
-rw-r--r--fs/nfs/write.c316
11 files changed, 760 insertions, 1711 deletions
diff --git a/fs/nfs/.cvsignore b/fs/nfs/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/nfs/.cvsignore
+++ b/fs/nfs/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index aaf17187b..8d75f3b85 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -35,6 +35,7 @@
extern void nfs_renew_times(struct dentry *);
#define NFS_PARANOIA 1
+/* #define NFS_DEBUG_VERBOSE 1 */
/*
* Head for a dircache entry. Currently still very simple; when
@@ -64,7 +65,7 @@ static int nfs_mkdir(struct inode *, struct dentry *, int);
static int nfs_rmdir(struct inode *, struct dentry *);
static int nfs_unlink(struct inode *, struct dentry *);
static int nfs_symlink(struct inode *, struct dentry *, const char *);
-static int nfs_link(struct inode *, struct inode *, struct dentry *);
+static int nfs_link(struct dentry *, struct inode *, struct dentry *);
static int nfs_mknod(struct inode *, struct dentry *, int, int);
static int nfs_rename(struct inode *, struct dentry *,
struct inode *, struct dentry *);
@@ -108,8 +109,11 @@ struct inode_operations nfs_dir_inode_operations = {
static int
nfs_dir_open(struct inode *dir, struct file *file)
{
- dfprintk(VFS, "NFS: nfs_dir_open(%x/%ld)\n", dir->i_dev, dir->i_ino);
- return nfs_revalidate_inode(NFS_SERVER(dir), dir);
+ struct dentry *dentry = file->f_dentry;
+
+ dfprintk(VFS, "NFS: nfs_dir_open(%s/%s)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ return nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
}
static ssize_t
@@ -133,7 +137,8 @@ static struct nfs_dirent dircache[NFS_MAX_DIRCACHE];
static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
- struct inode *inode = filp->f_dentry->d_inode;
+ struct dentry *dentry = filp->f_dentry;
+ struct inode *inode = dentry->d_inode;
static struct wait_queue *readdir_wait = NULL;
struct wait_queue **waitp = NULL;
struct nfs_dirent *cache, *free;
@@ -144,14 +149,17 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
__u32 *entry;
char *name, *start;
- dfprintk(VFS, "NFS: nfs_readdir(%x/%ld)\n", inode->i_dev, inode->i_ino);
+ dfprintk(VFS, "NFS: nfs_readdir(%s/%s)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ result = -EBADF;
if (!inode || !S_ISDIR(inode->i_mode)) {
printk("nfs_readdir: inode is NULL or not a directory\n");
- return -EBADF;
+ goto out;
}
- if ((result = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0)
- return result;
+ result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
+ if (result < 0)
+ goto out;
/*
* Try to find the entry in the cache
@@ -250,7 +258,7 @@ again:
goto done;
}
- result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(inode),
+ result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(dentry),
cookie, PAGE_SIZE, cache->entry);
if (result <= 0)
goto done;
@@ -300,6 +308,7 @@ done:
wake_up(&cache->wait);
wake_up(&readdir_wait);
+out:
return result;
}
@@ -411,7 +420,7 @@ static void nfs_dentry_delete(struct dentry *dentry)
int error;
dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
-#ifdef NFS_DEBUG
+#ifdef NFS_DEBUG_VERBOSE
printk("nfs_dentry_delete: unlinking %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
#endif
@@ -439,7 +448,8 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
*/
if (list_empty(&dentry->d_hash) && dentry->d_inode) {
struct inode *inode = dentry->d_inode;
- if (inode->i_count > 1) {
+ int max_count = (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink);
+ if (inode->i_count > max_count) {
printk("nfs_dentry_delete: %s/%s: ino=%ld, count=%d, nlink=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
inode->i_ino, inode->i_count, inode->i_nlink);
@@ -448,13 +458,69 @@ inode->i_ino, inode->i_count, inode->i_nlink);
#endif
}
-static struct dentry_operations nfs_dentry_operations = {
+/*
+ * Called to free the inode from the dentry. We must flush
+ * any pending writes for this dentry before freeing the inode.
+ */
+static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
+{
+ if (NFS_WRITEBACK(inode)) {
+#ifdef NFS_PARANOIA
+printk("nfs_dentry_iput: pending writes for %s/%s, i_count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count);
+#endif
+ while (nfs_find_dentry_request(inode, dentry)) {
+#ifdef NFS_PARANOIA
+printk("nfs_dentry_iput: flushing %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+ nfs_flush_dirty_pages(inode, 0, 0, 0);
+ }
+ }
+ iput(inode);
+}
+
+/*
+ * Called when the dentry is being freed to release private memory.
+ */
+static void nfs_dentry_release(struct dentry *dentry)
+{
+ if (dentry->d_fsdata)
+ kfree(dentry->d_fsdata);
+}
+
+struct dentry_operations nfs_dentry_operations = {
nfs_lookup_revalidate, /* d_validate(struct dentry *) */
- 0, /* d_hash */
- 0, /* d_compare */
- nfs_dentry_delete /* d_delete(struct dentry *) */
+ NULL, /* d_hash */
+ NULL, /* d_compare */
+ nfs_dentry_delete, /* d_delete(struct dentry *) */
+ nfs_dentry_release, /* d_release(struct dentry *) */
+ nfs_dentry_iput /* d_iput(struct dentry *, struct inode *) */
};
+#ifdef NFS_PARANOIA
+/*
+ * Display all dentries holding the specified inode.
+ */
+static void show_dentry(struct list_head * dlist)
+{
+ struct list_head *tmp = dlist;
+
+ while ((tmp = tmp->next) != dlist) {
+ struct dentry * dentry = list_entry(tmp, struct dentry, d_alias);
+ const char * unhashed = "";
+
+ if (list_empty(&dentry->d_hash))
+ unhashed = "(unhashed)";
+
+ printk("show_dentry: %s/%s, d_count=%d%s\n",
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name, dentry->d_count,
+ unhashed);
+ }
+}
+#endif
+
/*
* Whenever a lookup succeeds, we know the parent directories
* are all valid, so we want to update the dentry timestamps.
@@ -471,14 +537,13 @@ void nfs_renew_times(struct dentry * dentry)
static int nfs_lookup(struct inode *dir, struct dentry * dentry)
{
- int len = dentry->d_name.len;
struct inode *inode;
int error;
struct nfs_fh fhandle;
struct nfs_fattr fattr;
- dfprintk(VFS, "NFS: lookup(%x/%ld, %.*s)\n",
- dir->i_dev, dir->i_ino, len, dentry->d_name.name);
+ dfprintk(VFS, "NFS: lookup(%s/%s)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_lookup: inode is NULL or not a directory\n");
@@ -486,60 +551,46 @@ static int nfs_lookup(struct inode *dir, struct dentry * dentry)
}
error = -ENAMETOOLONG;
- if (len > NFS_MAXNAMLEN)
+ if (dentry->d_name.len > NFS_MAXNAMLEN)
goto out;
- error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir),
+ error = -ENOMEM;
+ if (!dentry->d_fsdata) {
+ dentry->d_fsdata = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
+ if (!dentry->d_fsdata)
+ goto out;
+ }
+ dentry->d_op = &nfs_dentry_operations;
+
+ error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, &fhandle, &fattr);
inode = NULL;
if (error == -ENOENT)
goto no_entry;
if (!error) {
error = -EACCES;
- inode = nfs_fhget(dir->i_sb, &fhandle, &fattr);
+ inode = nfs_fhget(dentry, &fhandle, &fattr);
if (inode) {
#ifdef NFS_PARANOIA
-if (inode->i_count > 1)
+if (inode->i_count > (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink)) {
printk("nfs_lookup: %s/%s ino=%ld in use, count=%d, nlink=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
inode->i_ino, inode->i_count, inode->i_nlink);
+show_dentry(&inode->i_dentry);
+}
#endif
no_entry:
- dentry->d_op = &nfs_dentry_operations;
d_add(dentry, inode);
nfs_renew_times(dentry);
error = 0;
}
}
-out:
- return error;
-}
-
-/*
- * Attempt to patch up certain errors following a create or
- * mkdir operation. We clear the original error if the new
- * lookup succeeds and has the correct mode.
- */
-static int nfs_fixup(struct inode *dir, struct dentry *dentry, int mode,
- struct nfs_fh *fhandle, struct nfs_fattr *fattr, int error)
-{
- int newerr;
-
#ifdef NFS_PARANOIA
-printk("nfs_fixup: %s/%s, error=%d, mode=%x\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, error, mode);
+if (error)
+printk("nfs_lookup: %s/%s failed, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, error);
#endif
- if (error == -EEXIST) {
- newerr = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir),
- dentry->d_name.name, fhandle, fattr);
- if (!newerr) {
-#ifdef NFS_PARANOIA
-printk("nfs_fixup: lookup OK, got mode=%x, want mode=%x\n", fattr->mode, mode);
-#endif
- if ((fattr->mode & S_IFMT) == (mode & S_IFMT))
- error = 0;
- }
- }
+out:
return error;
}
@@ -552,13 +603,15 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
struct inode *inode;
int error = -EACCES;
- inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
+ inode = nfs_fhget(dentry, fhandle, fattr);
if (inode) {
#ifdef NFS_PARANOIA
-if (inode->i_count > 1)
+if (inode->i_count > (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink)) {
printk("nfs_instantiate: %s/%s ino=%ld in use, count=%d, nlink=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
inode->i_ino, inode->i_count, inode->i_nlink);
+show_dentry(&inode->i_dentry);
+}
#endif
d_instantiate(dentry, inode);
nfs_renew_times(dentry);
@@ -600,10 +653,8 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode)
* Invalidate the dir cache before the operation to avoid a race.
*/
nfs_invalidate_dircache(dir);
- error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
+ error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, &sattr, &fhandle, &fattr);
- if (error)
- error = nfs_fixup(dir, dentry, mode, &fhandle, &fattr, error);
if (!error)
error = nfs_instantiate(dentry, &fhandle, &fattr);
if (error)
@@ -640,10 +691,8 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
nfs_invalidate_dircache(dir);
- error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
+ error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, &sattr, &fhandle, &fattr);
- if (error)
- error = nfs_fixup(dir, dentry, mode, &fhandle, &fattr, error);
if (!error)
error = nfs_instantiate(dentry, &fhandle, &fattr);
if (error)
@@ -669,39 +718,25 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
return -ENOENT;
}
+ error = -ENAMETOOLONG;
if (dentry->d_name.len > NFS_MAXNAMLEN)
- return -ENAMETOOLONG;
+ goto out;
- /* For some reason mode doesn't have the S_IFDIR flag ... */
- mode |= S_IFDIR;
- sattr.mode = mode;
+ sattr.mode = mode | S_IFDIR;
sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
+ /*
+ * Always drop the dentry, we can't always depend on
+ * the fattr returned by the server (AIX seems to be
+ * broken). We're better off doing another lookup than
+ * depending on potentially bogus information.
+ */
+ d_drop(dentry);
nfs_invalidate_dircache(dir);
- error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir),
+ error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent),
dentry->d_name.name, &sattr, &fhandle, &fattr);
- if (error)
- error = nfs_fixup(dir, dentry, mode, &fhandle, &fattr, error);
- if (!error) {
- /*
- * Some AIX servers reportedly fail to fill out the fattr.
- * Check for a bad mode value and complain, then drop the
- * dentry to force a new lookup.
- */
- if (!S_ISDIR(fattr.mode)) {
- static int complain = 0;
- if (!complain++)
- printk("NFS: buggy server! fattr mode=%x\n",
- fattr.mode);
- goto drop;
- }
- error = nfs_instantiate(dentry, &fhandle, &fattr);
- }
- if (error) {
- drop:
- d_drop(dentry);
- }
+out:
return error;
}
@@ -756,8 +791,8 @@ dentry->d_inode->i_count, dentry->d_inode->i_nlink);
dentry->d_inode->i_nlink --;
d_delete(dentry);
nfs_invalidate_dircache(dir);
- error = nfs_proc_rmdir(NFS_SERVER(dir),
- NFS_FH(dir), dentry->d_name.name);
+ error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
+ dentry->d_name.name);
if (!error) {
if (rehash)
d_add(dentry, NULL);
@@ -876,11 +911,11 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
goto out;
} while(sdentry->d_inode != NULL); /* need negative lookup */
+ nfs_invalidate_dircache(dir);
error = nfs_proc_rename(NFS_SERVER(dir),
- NFS_FH(dir), dentry->d_name.name,
- NFS_FH(dir), silly);
+ NFS_FH(dentry->d_parent), dentry->d_name.name,
+ NFS_FH(dentry->d_parent), silly);
if (!error) {
- nfs_invalidate_dircache(dir);
nfs_renew_times(dentry);
d_move(dentry, sdentry);
dentry->d_flags |= DCACHE_NFSFS_RENAMED;
@@ -953,8 +988,8 @@ inode->i_count, inode->i_nlink);
d_delete(dentry);
}
nfs_invalidate_dircache(dir);
- error = nfs_proc_remove(NFS_SERVER(dir),
- NFS_FH(dir), dentry->d_name.name);
+ error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
+ dentry->d_name.name);
/*
* Rehash the negative dentry if the operation succeeded.
*/
@@ -1036,10 +1071,10 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
* can't instantiate the new inode.
*/
d_drop(dentry);
- error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir),
+ nfs_invalidate_dircache(dir);
+ error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, symname, &sattr);
if (!error) {
- nfs_invalidate_dircache(dir);
nfs_renew_times(dentry->d_parent);
} else if (error == -EEXIST) {
printk("nfs_proc_symlink: %s/%s already exists??\n",
@@ -1051,13 +1086,14 @@ out:
}
static int
-nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentry)
+nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
{
+ struct inode *inode = old_dentry->d_inode;
int error;
- dfprintk(VFS, "NFS: link(%x/%ld -> %x/%ld, %s)\n",
- inode->i_dev, inode->i_ino,
- dir->i_dev, dir->i_ino, dentry->d_name.name);
+ dfprintk(VFS, "NFS: link(%s/%s -> %s/%s)\n",
+ old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+ dentry->d_parent->d_name.name, dentry->d_name.name);
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_link: dir is NULL or not a directory\n");
@@ -1068,13 +1104,21 @@ nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentry)
if (dentry->d_name.len > NFS_MAXNAMLEN)
goto out;
- error = nfs_proc_link(NFS_SERVER(inode), NFS_FH(inode), NFS_FH(dir),
- dentry->d_name.name);
+ /*
+ * Drop the dentry in advance to force a new lookup.
+ * Since nfs_proc_link doesn't return a filehandle,
+ * we can't use the existing dentry.
+ */
+ d_drop(dentry);
+ nfs_invalidate_dircache(dir);
+ error = nfs_proc_link(NFS_DSERVER(old_dentry), NFS_FH(old_dentry),
+ NFS_FH(dentry->d_parent), dentry->d_name.name);
if (!error) {
- nfs_invalidate_dircache(dir);
- inode->i_count ++;
- inode->i_nlink ++; /* no need to wait for nfs_refresh_inode() */
- d_instantiate(dentry, inode);
+ /*
+ * Update the link count immediately, as some apps
+ * (e.g. pine) test this after making a link.
+ */
+ inode->i_nlink++;
}
out:
return error;
@@ -1135,10 +1179,8 @@ new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
* First check whether the target is busy ... we can't
* safely do _any_ rename if the target is in use.
*/
- if (new_dentry->d_count > 1) {
- if (new_inode && S_ISDIR(new_inode->i_mode))
- shrink_dcache_parent(new_dentry);
- }
+ if (new_dentry->d_count > 1 && !list_empty(&new_dentry->d_subdirs))
+ shrink_dcache_parent(new_dentry);
error = -EBUSY;
if (new_dentry->d_count > 1) {
#ifdef NFS_PARANOIA
@@ -1202,28 +1244,33 @@ new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
do_rename:
/*
- * We must prevent any new references to the target while
- * the rename is in progress, so we unhash the dentry.
+ * To prevent any new references to the target during the rename,
+ * we unhash the dentry and free the inode in advance.
*/
+#ifdef NFS_PARANOIA
+if (new_inode &&
+ new_inode->i_count > (S_ISDIR(new_inode->i_mode) ? 1 : new_inode->i_nlink))
+printk("nfs_rename: %s/%s inode busy?? i_count=%d, i_nlink=%d\n",
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+new_inode->i_count, new_inode->i_nlink);
+#endif
if (!list_empty(&new_dentry->d_hash)) {
d_drop(new_dentry);
rehash = update;
}
- error = nfs_proc_rename(NFS_SERVER(old_dir),
- NFS_FH(old_dir), old_dentry->d_name.name,
- NFS_FH(new_dir), new_dentry->d_name.name);
- if (rehash) {
- d_add(new_dentry, new_inode);
+ if (new_inode) {
+ d_delete(new_dentry);
}
-#ifdef NFS_PARANOIA
-if (new_dentry->d_count > 1)
-printk("nfs_rename: %s/%s busy after rename, d_count=%d\n",
-new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
-#endif
+
+ nfs_invalidate_dircache(new_dir);
+ nfs_invalidate_dircache(old_dir);
+ error = nfs_proc_rename(NFS_DSERVER(old_dentry),
+ NFS_FH(old_dentry->d_parent), old_dentry->d_name.name,
+ NFS_FH(new_dentry->d_parent), new_dentry->d_name.name);
if (!error) {
- nfs_invalidate_dircache(new_dir);
- nfs_invalidate_dircache(old_dir);
/* Update the dcache if needed */
+ if (rehash)
+ d_add(new_dentry, NULL);
if (update)
d_move(old_dentry, new_dentry);
}
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 7b53bc8ef..4a575c48c 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -109,15 +109,14 @@ nfs_file_close(struct inode *inode, struct file *file)
static ssize_t
nfs_file_read(struct file * file, char * buf, size_t count, loff_t *ppos)
{
- struct inode * inode = file->f_dentry->d_inode;
+ struct dentry * dentry = file->f_dentry;
ssize_t result;
- dfprintk(VFS, "nfs: read(%x/%ld, %lu@%lu)\n",
- inode->i_dev, inode->i_ino,
- (unsigned long) count,
- (unsigned long) *ppos);
+ dfprintk(VFS, "nfs: read(%s/%s, %lu@%lu)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ (unsigned long) count, (unsigned long) *ppos);
- result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
if (!result)
result = generic_file_read(file, buf, count, ppos);
return result;
@@ -126,12 +125,13 @@ nfs_file_read(struct file * file, char * buf, size_t count, loff_t *ppos)
static int
nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
{
- struct inode *inode = file->f_dentry->d_inode;
+ struct dentry *dentry = file->f_dentry;
int status;
- dfprintk(VFS, "nfs: mmap(%x/%ld)\n", inode->i_dev, inode->i_ino);
+ dfprintk(VFS, "nfs: mmap(%s/%s)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
- status = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ status = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
if (!status)
status = generic_file_mmap(file, vma);
return status;
@@ -163,31 +163,33 @@ nfs_fsync(struct file *file, struct dentry *dentry)
static ssize_t
nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
- struct inode * inode = file->f_dentry->d_inode;
+ struct dentry * dentry = file->f_dentry;
+ struct inode * inode = dentry->d_inode;
ssize_t result;
- dfprintk(VFS, "nfs: write(%x/%ld (%d), %lu@%lu)\n",
- inode->i_dev, inode->i_ino, inode->i_count,
- (unsigned long) count, (unsigned long) *ppos);
+ dfprintk(VFS, "nfs: write(%s/%s (%d), %lu@%lu)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ inode->i_count, (unsigned long) count, (unsigned long) *ppos);
if (!inode) {
printk("nfs_file_write: inode = NULL\n");
return -EINVAL;
}
- if (IS_SWAPFILE(inode)) {
- printk("NFS: attempt to write to active swap file!\n");
- return -EBUSY;
- }
- result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ result = -EBUSY;
+ if (IS_SWAPFILE(inode))
+ goto out_swapfile;
+ result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
if (result)
goto out;
- /* N.B. This should be impossible now -- inodes can't change mode */
- if (!S_ISREG(inode->i_mode)) {
- printk("nfs_file_write: write to non-file, mode %07o\n",
- inode->i_mode);
- return -EINVAL;
- }
+#ifdef NFS_PARANOIA
+/* N.B. This should be impossible now -- inodes can't change mode */
+if (!S_ISREG(inode->i_mode)) {
+ printk("nfs_file_write: write to non-file, mode %07o\n",
+ inode->i_mode);
+ return -EINVAL;
+}
+#endif
result = count;
if (!count)
goto out;
@@ -198,6 +200,10 @@ nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
result = generic_file_write(file, buf, count, ppos);
out:
return result;
+
+out_swapfile:
+ printk("NFS: attempt to write to active swap file!\n");
+ goto out;
}
/*
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index eb56950eb..8300fee67 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -32,13 +32,16 @@
#include <asm/system.h>
#include <asm/uaccess.h>
+#define CONFIG_NFS_SNAPSHOT 1
#define NFSDBG_FACILITY NFSDBG_VFS
#define NFS_PARANOIA 1
+static struct inode * __nfs_fhget(struct super_block *, struct nfs_fattr *);
+
static void nfs_read_inode(struct inode *);
static void nfs_put_inode(struct inode *);
static void nfs_delete_inode(struct inode *);
-static int nfs_notify_change(struct inode *, struct iattr *);
+static int nfs_notify_change(struct dentry *, struct iattr *);
static void nfs_put_super(struct super_block *);
static int nfs_statfs(struct super_block *, struct statfs *, int);
@@ -138,6 +141,7 @@ nfs_put_super(struct super_block *sb)
*/
nfs_invalidate_dircache_sb(sb);
+ kfree(server->hostname);
sb->s_dev = 0;
unlock_super(sb);
MOD_DEC_USE_COUNT;
@@ -180,15 +184,16 @@ struct super_block *
nfs_read_super(struct super_block *sb, void *raw_data, int silent)
{
struct nfs_mount_data *data = (struct nfs_mount_data *) raw_data;
- struct sockaddr_in srvaddr;
struct nfs_server *server;
- struct rpc_timeout timeparms;
struct rpc_xprt *xprt;
struct rpc_clnt *clnt;
+ struct nfs_fh *root_fh;
+ struct inode *root_inode;
unsigned int authflavor;
int tcp;
- kdev_t dev = sb->s_dev;
- struct inode *root_inode;
+ struct sockaddr_in srvaddr;
+ struct rpc_timeout timeparms;
+ struct nfs_fattr fattr;
MOD_INC_USE_COUNT;
if (!data)
@@ -211,7 +216,6 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
lock_super(sb);
sb->s_magic = NFS_SUPER_MAGIC;
- sb->s_dev = dev;
sb->s_op = &nfs_sops;
sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
sb->u.nfs_sb.s_root = data->root;
@@ -223,6 +227,10 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
server->acregmax = data->acregmax*HZ;
server->acdirmin = data->acdirmin*HZ;
server->acdirmax = data->acdirmax*HZ;
+
+ server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL);
+ if (!server->hostname)
+ goto out_unlock;
strcpy(server->hostname, data->hostname);
/* Which protocol do we use? */
@@ -234,21 +242,19 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
timeparms.to_maxval = tcp? RPC_MAX_TCP_TIMEOUT : RPC_MAX_UDP_TIMEOUT;
timeparms.to_exponential = 1;
- /* Choose authentication flavor */
- if (data->flags & NFS_MOUNT_SECURE) {
- authflavor = RPC_AUTH_DES;
- } else if (data->flags & NFS_MOUNT_KERBEROS) {
- authflavor = RPC_AUTH_KRB;
- } else {
- authflavor = RPC_AUTH_UNIX;
- }
-
/* Now create transport and client */
xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP,
&srvaddr, &timeparms);
if (xprt == NULL)
goto out_no_xprt;
+ /* Choose authentication flavor */
+ authflavor = RPC_AUTH_UNIX;
+ if (data->flags & NFS_MOUNT_SECURE)
+ authflavor = RPC_AUTH_DES;
+ else if (data->flags & NFS_MOUNT_KERBEROS)
+ authflavor = RPC_AUTH_KRB;
+
clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
NFS_VERSION, authflavor);
if (clnt == NULL)
@@ -260,23 +266,30 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
server->client = clnt;
/* Fire up rpciod if not yet running */
-#ifdef RPCIOD_RESULT
- if (rpciod_up())
+ if (rpciod_up() != 0)
goto out_no_iod;
-#else
- rpciod_up();
-#endif
/*
* Keep the super block locked while we try to get
* the root fh attributes.
*/
- root_inode = nfs_fhget(sb, &data->root, NULL);
+ root_fh = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
+ if (!root_fh)
+ goto out_no_fh;
+ *root_fh = data->root;
+
+ if (nfs_proc_getattr(server, root_fh, &fattr) != 0)
+ goto out_no_fattr;
+
+ root_inode = __nfs_fhget(sb, &fattr);
if (!root_inode)
goto out_no_root;
sb->s_root = d_alloc_root(root_inode, NULL);
if (!sb->s_root)
goto out_no_root;
+ sb->s_root->d_op = &nfs_dentry_operations;
+ sb->s_root->d_fsdata = root_fh;
+
/* We're airborne */
unlock_super(sb);
@@ -289,14 +302,19 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
out_no_root:
printk("nfs_read_super: get root inode failed\n");
iput(root_inode);
+ goto out_free_fh;
+
+out_no_fattr:
+ printk("nfs_read_super: get root fattr failed\n");
+out_free_fh:
+ kfree(root_fh);
+out_no_fh:
rpciod_down();
-#ifdef RPCIOD_RESULT
goto out_shutdown;
out_no_iod:
- printk("nfs_read_super: couldn't start rpciod!\n");
+ printk("NFS: couldn't start rpciod!\n");
out_shutdown:
-#endif
rpc_shutdown_client(server->client);
goto out_unlock;
@@ -307,6 +325,7 @@ out_no_client:
out_no_xprt:
printk("NFS: cannot create RPC transport.\n");
+ kfree(server->hostname);
out_unlock:
unlock_super(sb);
goto out_fail;
@@ -349,46 +368,39 @@ nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
}
/*
- * This is our own version of iget that looks up inodes by file handle
- * instead of inode number. We use this technique instead of using
- * the vfs read_inode function because there is no way to pass the
- * file handle or current attributes into the read_inode function.
+ * Free all unused dentries in an inode's alias list.
+ *
+ * Subtle note: we have to be very careful not to cause
+ * any IO operations with the stale dentries, as this
+ * could cause file corruption. But since the dentry
+ * count is 0 and all pending IO for a dentry has been
+ * flushed when the count went to 0, we're safe here.
*/
-struct inode *
-nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle,
- struct nfs_fattr *fattr)
+void nfs_free_dentries(struct inode *inode)
{
- struct nfs_fattr newfattr;
- int error;
- struct inode *inode;
-
- if (!sb) {
- printk("nfs_fhget: super block is NULL\n");
- return NULL;
- }
- if (!fattr) {
- error = nfs_proc_getattr(&sb->u.nfs_sb.s_server, fhandle,
- &newfattr);
- if (error) {
- printk("nfs_fhget: getattr error = %d\n", -error);
- return NULL;
+ struct list_head *tmp, *head = &inode->i_dentry;
+
+restart:
+ tmp = head;
+ while ((tmp = tmp->next) != head) {
+ struct dentry *dentry = list_entry(tmp, struct dentry, d_alias);
+ if (!dentry->d_count) {
+printk("nfs_free_dentries: freeing %s/%s, i_count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count);
+ dget(dentry);
+ d_drop(dentry);
+ dput(dentry);
+ goto restart;
}
- fattr = &newfattr;
- }
- if (!(inode = iget(sb, fattr->fileid))) {
- printk("nfs_fhget: iget failed\n");
- return NULL;
- }
-#ifdef NFS_PARANOIA
-if (inode->i_dev != sb->s_dev)
-printk("nfs_fhget: impossible\n");
-#endif
-
- if (inode->i_ino != fattr->fileid) {
- printk("nfs_fhget: unexpected inode from iget\n");
- return inode;
}
+}
+/*
+ * Fill in inode information from the fattr.
+ */
+static void
+nfs_fill_inode(struct inode *inode, struct nfs_fattr *fattr)
+{
/*
* Check whether the mode has been set, as we only want to
* do this once. (We don't allow inodes to change types.)
@@ -418,29 +430,140 @@ printk("nfs_fhget: impossible\n");
inode->i_size = fattr->size;
inode->i_mtime = fattr->mtime.seconds;
NFS_OLDMTIME(inode) = fattr->mtime.seconds;
- *NFS_FH(inode) = *fhandle;
}
- if (memcmp(NFS_FH(inode), fhandle, sizeof(struct nfs_fh)))
- printk("nfs_fhget: fhandle changed!\n");
nfs_refresh_inode(inode, fattr);
- dprintk("NFS: fhget(%x/%ld ct=%d)\n",
- inode->i_dev, inode->i_ino,
- inode->i_count);
+}
+/*
+ * This is our own version of iget that looks up inodes by file handle
+ * instead of inode number. We use this technique instead of using
+ * the vfs read_inode function because there is no way to pass the
+ * file handle or current attributes into the read_inode function.
+ *
+ * We provide a special check for NetApp .snapshot directories to avoid
+ * inode aliasing problems. All snapshot inodes are anonymous (unhashed).
+ */
+struct inode *
+nfs_fhget(struct dentry *dentry, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr)
+{
+ struct super_block *sb = dentry->d_sb;
+
+ dprintk("NFS: nfs_fhget(%s/%s fileid=%d)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ fattr->fileid);
+
+ /* Install the filehandle in the dentry */
+ *((struct nfs_fh *) dentry->d_fsdata) = *fhandle;
+
+#ifdef CONFIG_NFS_SNAPSHOT
+ /*
+ * Check for NetApp snapshot dentries, and get an
+ * unhashed inode to avoid aliasing problems.
+ */
+ if ((dentry->d_parent->d_inode->u.nfs_i.flags & NFS_IS_SNAPSHOT) ||
+ (IS_ROOT(dentry->d_parent) && dentry->d_name.len == 9 &&
+ memcmp(dentry->d_name.name, ".snapshot", 9) == 0)) {
+ struct inode *inode = get_empty_inode();
+ if (!inode)
+ goto out;
+ inode->i_sb = sb;
+ inode->i_dev = sb->s_dev;
+ inode->i_ino = fattr->fileid;
+ nfs_read_inode(inode);
+ nfs_fill_inode(inode, fattr);
+ inode->u.nfs_i.flags |= NFS_IS_SNAPSHOT;
+ dprintk("NFS: nfs_fhget(snapshot ino=%ld)\n", inode->i_ino);
+ out:
+ return inode;
+ }
+#endif
+ return __nfs_fhget(sb, fattr);
+}
+
+/*
+ * Look up the inode by super block and fattr->fileid.
+ *
+ * Note carefully the special handling of busy inodes (i_count > 1).
+ * With the kernel 2.1.xx dcache all inodes except hard links must
+ * have i_count == 1 after iget(). Otherwise, it indicates that the
+ * server has reused a fileid (i_ino) and we have a stale inode.
+ */
+static struct inode *
+__nfs_fhget(struct super_block *sb, struct nfs_fattr *fattr)
+{
+ struct inode *inode;
+ int max_count;
+
+retry:
+ inode = iget(sb, fattr->fileid);
+ if (!inode)
+ goto out_no_inode;
+ /* N.B. This should be impossible ... */
+ if (inode->i_ino != fattr->fileid)
+ goto out_bad_id;
+
+ /*
+ * Check for busy inodes, and attempt to get rid of any
+ * unused local references. If successful, we release the
+ * inode and try again.
+ *
+ * Note that the busy test uses the values in the fattr,
+ * as the inode may have become a different object.
+ * (We can probably handle modes changes here, too.)
+ */
+ max_count = S_ISDIR(fattr->mode) ? 1 : fattr->nlink;
+ if (inode->i_count > max_count) {
+printk("__nfs_fhget: inode %ld busy, i_count=%d, i_nlink=%d\n",
+inode->i_ino, inode->i_count, inode->i_nlink);
+ nfs_free_dentries(inode);
+ if (inode->i_count > max_count) {
+printk("__nfs_fhget: inode %ld still busy, i_count=%d\n",
+inode->i_ino, inode->i_count);
+ if (!list_empty(&inode->i_dentry)) {
+ struct dentry *dentry;
+ dentry = list_entry(inode->i_dentry.next,
+ struct dentry, d_alias);
+printk("__nfs_fhget: killing %s/%s filehandle\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+ memset(dentry->d_fsdata, 0,
+ sizeof(struct nfs_fh));
+ } else
+ printk("NFS: inode %ld busy, no aliases?\n",
+ inode->i_ino);
+ make_bad_inode(inode);
+ remove_inode_hash(inode);
+ }
+ iput(inode);
+ goto retry;
+ }
+ nfs_fill_inode(inode, fattr);
+ dprintk("NFS: __nfs_fhget(%x/%ld ct=%d)\n",
+ inode->i_dev, inode->i_ino, inode->i_count);
+
+out:
return inode;
+
+out_no_inode:
+ printk("__nfs_fhget: iget failed\n");
+ goto out;
+out_bad_id:
+ printk("__nfs_fhget: unexpected inode from iget\n");
+ goto out;
}
int
-nfs_notify_change(struct inode *inode, struct iattr *attr)
+nfs_notify_change(struct dentry *dentry, struct iattr *attr)
{
+ struct inode *inode = dentry->d_inode;
+ int error;
struct nfs_sattr sattr;
struct nfs_fattr fattr;
- int error;
/*
* Make sure the inode is up-to-date.
*/
- error = nfs_revalidate(inode);
+ error = nfs_revalidate(dentry);
if (error) {
#ifdef NFS_PARANOIA
printk("nfs_notify_change: revalidate failed, error=%d\n", error);
@@ -476,7 +599,7 @@ printk("nfs_notify_change: revalidate failed, error=%d\n", error);
sattr.atime.useconds = 0;
}
- error = nfs_proc_setattr(NFS_SERVER(inode), NFS_FH(inode),
+ error = nfs_proc_setattr(NFS_DSERVER(dentry), NFS_FH(dentry),
&sattr, &fattr);
if (error)
goto out;
@@ -503,9 +626,9 @@ out:
* Externally visible revalidation function
*/
int
-nfs_revalidate(struct inode *inode)
+nfs_revalidate(struct dentry *dentry)
{
- return nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ return nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
}
/*
@@ -513,38 +636,43 @@ nfs_revalidate(struct inode *inode)
* the cached attributes have to be refreshed.
*/
int
-_nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
+_nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry)
{
- struct nfs_fattr fattr;
+ struct inode *inode = dentry->d_inode;
int status = 0;
+ struct nfs_fattr fattr;
if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode))
goto out;
- dfprintk(PAGECACHE, "NFS: revalidating %x/%ld inode\n",
- inode->i_dev, inode->i_ino);
- status = nfs_proc_getattr(server, NFS_FH(inode), &fattr);
+ dfprintk(PAGECACHE, "NFS: revalidating %s/%s, ino=%ld\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ inode->i_ino);
+ status = nfs_proc_getattr(server, NFS_FH(dentry), &fattr);
if (status) {
#ifdef NFS_PARANOIA
-printk("nfs_revalidate_inode: getattr failed, error=%d\n", status);
+printk("nfs_revalidate_inode: %s/%s getattr failed, ino=%ld, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, status);
#endif
- goto done;
+ goto out;
}
status = nfs_refresh_inode(inode, &fattr);
- if (status)
- goto done;
+ if (status) {
+#ifdef NFS_PARANOIA
+printk("nfs_revalidate_inode: %s/%s refresh failed, ino=%ld, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, status);
+#endif
+ goto out;
+ }
if (fattr.mtime.seconds == NFS_OLDMTIME(inode)) {
/* Update attrtimeo value */
if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode))
NFS_ATTRTIMEO(inode) = NFS_MAXATTRTIMEO(inode);
}
NFS_OLDMTIME(inode) = fattr.mtime.seconds;
-
-done:
- dfprintk(PAGECACHE,
- "NFS: inode %x/%ld revalidation complete (status %d).\n",
- inode->i_dev, inode->i_ino, status);
+ dfprintk(PAGECACHE, "NFS: %s/%s revalidation complete\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
out:
return status;
}
@@ -575,7 +703,8 @@ nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
goto out;
}
if (inode->i_ino != fattr->fileid) {
- printk("nfs_refresh_inode: inode number mismatch\n");
+ printk("nfs_refresh_inode: mismatch, ino=%ld, fattr=%d\n",
+ inode->i_ino, fattr->fileid);
goto out;
}
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c
index 0311b7d0b..216aafb80 100644
--- a/fs/nfs/nfs2xdr.c
+++ b/fs/nfs/nfs2xdr.c
@@ -406,8 +406,9 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
len = ntohl(*p++);
if ((p + QUADLEN(len) + 3) > end) {
- printk(KERN_NOTICE
- "NFS: short packet in readdir reply!\n");
+ printk(KERN_WARNING "NFS: short readdir reply! "
+ "nr=%d, slots=%d, len=%d\n",
+ nr, (end - p), len);
break;
}
if (len > NFS_MAXNAMLEN) {
@@ -564,6 +565,7 @@ static struct {
{ NFSERR_EAGAIN, EAGAIN },
{ NFSERR_ACCES, EACCES },
{ NFSERR_EXIST, EEXIST },
+ { NFSERR_XDEV, EXDEV },
{ NFSERR_NODEV, ENODEV },
{ NFSERR_NOTDIR, ENOTDIR },
{ NFSERR_ISDIR, EISDIR },
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index 94096d928..6ef7b9282 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -584,6 +584,7 @@ static struct {
{ NFSERR_EAGAIN, EAGAIN },
{ NFSERR_ACCES, EACCES },
{ NFSERR_EXIST, EEXIST },
+ { NFSERR_XDEV, EXDEV },
{ NFSERR_NODEV, ENODEV },
{ NFSERR_NOTDIR, ENOTDIR },
{ NFSERR_ISDIR, EISDIR },
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c
index b7c7dfba7..f12cdfd6b 100644
--- a/fs/nfs/nfsroot.c
+++ b/fs/nfs/nfsroot.c
@@ -1,14 +1,10 @@
/*
- * $Id: nfsroot.c,v 1.38 1997/07/17 03:21:06 davem Exp $
+ * $Id: nfsroot.c,v 1.43 1997/10/16 19:55:27 mj Exp $
*
* Copyright (C) 1995, 1996 Gero Kuhlmann <gero@gkminix.han.de>
*
- * For parts of this file:
- * Copyright (C) 1996, 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
- *
* Allow an NFS filesystem to be mounted as root. The way this works is:
- * (1) Determine the local IP address via RARP or BOOTP or from the
- * kernel command line.
+ * (1) Use the IP autoconfig mechanism to set local IP addresses and routes.
* (2) Handle RPC negotiation with the system which replied to RARP or
* was reported as a boot server by BOOTP or manually.
* (3) The actual mounting is done later, when init() is running.
@@ -47,7 +43,7 @@
* from being used (thanks to Leo Spiekman)
* Andy Walker : Allow to specify the NFS server in nfs_root
* without giving a path name
- * Swen Th=FCmmler : Allow to specify the NFS options in nfs_root
+ * Swen Thümmler : Allow to specify the NFS options in nfs_root
* without giving a path name. Fix BOOTP request
* for domainname (domainname is NIS domain, not
* DNS domain!). Skip dummy devices for BOOTP.
@@ -57,1004 +53,60 @@
* Jakub Jelinek : Free used code segment.
* Marko Kohtala : Fixed some bugs.
* Martin Mares : Debug message cleanup
- *
+ * Martin Mares : Changed to use the new generic IP layer autoconfig
+ * code. BOOTP and RARP moved there.
+ * Martin Mares : Default path now contains host name instead of
+ * host IP address (but host name defaults to IP
+ * address anyway).
+ * Martin Mares : Use root_server_addr appropriately during setup.
*/
-
-/* Define this to allow debugging output */
-#undef NFSROOT_DEBUG
-#undef NFSROOT_BOOTP_DEBUG
-
-
-#include <linux/config.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
-#include <linux/random.h>
-#include <linux/fcntl.h>
#include <linux/init.h>
-
-#include <asm/param.h>
-#include <linux/utsname.h>
-#include <linux/in.h>
-#include <linux/if.h>
-#include <linux/inet.h>
-#include <linux/net.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#ifdef CONFIG_AX25
-#include <net/ax25.h> /* For AX25_P_IP */
-#endif
-#include <linux/skbuff.h>
-#include <linux/ip.h>
-#include <linux/socket.h>
-#include <linux/route.h>
#include <linux/sunrpc/clnt.h>
#include <linux/nfs.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_mount.h>
#include <linux/in.h>
-#include <net/route.h>
-#include <net/sock.h>
-
-#include <asm/segment.h>
-#include <asm/uaccess.h>
-
-#define NFSDBG_FACILITY NFSDBG_ROOT
-/* Range of privileged ports */
-#define STARTPORT 600
-#define ENDPORT 1023
-#define NPORTS (ENDPORT - STARTPORT + 1)
-
-
-/* Define the timeout for waiting for a RARP/BOOTP reply */
-#define CONF_BASE_TIMEOUT (HZ*5) /* Initial timeout: 5 seconds */
-#define CONF_RETRIES 10 /* 10 retries */
-#define CONF_TIMEOUT_RANDOM (HZ) /* Maximum amount of randomization */
-#define CONF_TIMEOUT_MULT *5/4 /* Speed of timeout growth */
-#define CONF_TIMEOUT_MAX (HZ*30) /* Maximum allowed timeout */
-
-
-/* List of open devices */
-struct open_dev {
- struct device *dev;
- unsigned short old_flags;
- struct open_dev *next;
-};
-
-static struct open_dev *open_base __initdata = NULL;
-
-
-/* IP configuration */
-static struct device *root_dev __initdata = NULL; /* Device selected for booting */
-static char user_dev_name[IFNAMSIZ] __initdata = { 0, };/* Name of user-selected boot device */
-static __u32 myaddr __initdata = 0; /* My IP address */
-static __u32 servaddr __initdata = 0; /* Server IP address */
-static __u32 gateway __initdata = 0; /* Gateway IP address */
-static __u32 netmask __initdata = 0; /* Netmask for local subnet */
+#include <linux/inet.h>
+#include <linux/major.h>
+#include <linux/utsname.h>
+#include <net/ipconfig.h>
+/* Define this to allow debugging output */
+#undef NFSROOT_DEBUG
+#define NFSDBG_FACILITY NFSDBG_ROOT
-/* BOOTP/RARP variables */
-static int bootp_flag __initdata = 0; /* User said: Use BOOTP! */
-static int rarp_flag __initdata = 0; /* User said: Use RARP! */
-static int bootp_dev_count __initdata = 0; /* Number of devices allowing BOOTP */
-static int rarp_dev_count __initdata = 0; /* Number of devices allowing RARP */
-static __u32 rarp_serv __initdata = 0; /* IP address of RARP server */
+/* Default path we try to mount. "%s" gets replaced by our IP address */
+#define NFS_ROOT "/tftpboot/%s"
+#define NFS_ROOT_NAME_LEN 256
-#if defined(CONFIG_RNFS_BOOTP) || defined(CONFIG_RNFS_RARP)
-#define CONFIG_RNFS_DYNAMIC /* Enable dynamic IP config */
-static volatile int pkt_arrived __initdata = 0; /* BOOTP/RARP packet detected */
+/* Parameters passed from the kernel command line */
+static char nfs_root_name[NFS_ROOT_NAME_LEN] __initdata = "default";
+static int nfs_params_parsed = 0;
-#define ARRIVED_BOOTP 1
-#define ARRIVED_RARP 2
-#endif
+/* Address of NFS server */
+static __u32 servaddr __initdata = 0;
+/* Name of directory to mount */
+static char nfs_path[NFS_MAXPATHLEN] __initdata = { 0, };
/* NFS-related data */
static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */
-static char nfs_path[NFS_MAXPATHLEN] __initdata = { 0, };/* Name of directory to mount */
-static int nfs_port __initdata = 0; /* Port to connect to for NFS */
-static int mount_port __initdata = 0; /* Mount daemon port number */
-
-
-/* Yes, we use sys_socket, but there's no include file for it */
-extern asmlinkage int sys_socket(int family, int type, int protocol);
-
-
-
-/***************************************************************************
-
- Device Handling Subroutines
-
- ***************************************************************************/
-
-/*
- * Setup and initialize all network devices. If there is a user-preferred
- * interface, ignore all other interfaces.
- */
-__initfunc(static int root_dev_open(void))
-{
- struct open_dev *openp, **last;
- struct device *dev;
- unsigned short old_flags;
-
- last = &open_base;
- for (dev = dev_base; dev != NULL; dev = dev->next) {
- if (dev->type < ARPHRD_SLIP &&
- dev->family == AF_INET &&
- !(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) &&
- (0 != strncmp(dev->name, "dummy", 5)) &&
- (!user_dev_name[0] || !strcmp(dev->name, user_dev_name))) {
- /* First up the interface */
- old_flags = dev->flags;
- dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
- if (!(old_flags & IFF_UP) && dev_open(dev)) {
- dev->flags = old_flags;
- continue;
- }
- openp = (struct open_dev *) kmalloc(sizeof(struct open_dev),
- GFP_ATOMIC);
- if (openp == NULL)
- continue;
- openp->dev = dev;
- openp->old_flags = old_flags;
- *last = openp;
- last = &openp->next;
- bootp_dev_count++;
- if (!(dev->flags & IFF_NOARP))
- rarp_dev_count++;
- dprintk("Root-NFS: Opened %s\n", dev->name);
- }
- }
- *last = NULL;
-
- if (!bootp_dev_count && !rarp_dev_count) {
- printk(KERN_ERR "Root-NFS: Unable to open at least one network device\n");
- return -1;
- }
- return 0;
-}
-
-static inline void
-set_sockaddr(struct sockaddr_in *sin, __u32 addr, __u16 port)
-{
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = addr;
- sin->sin_port = port;
-}
-
-__initfunc(static int
-root_dev_chg_route(int op, struct device *dev, __u32 dest, __u32 mask, __u32 gw))
-{
- struct rtentry route;
- mm_segment_t oldfs;
- int err;
-
- memset(&route, 0, sizeof(struct rtentry)); /* or else! */
-
- route.rt_dev = dev->name;
- route.rt_mtu = dev->mtu;
- route.rt_flags = RTF_UP;
- set_sockaddr((struct sockaddr_in *) &route.rt_dst, dest & mask, 0);
- set_sockaddr((struct sockaddr_in *) &route.rt_genmask, mask, 0);
-
- if (gw != 0) {
- set_sockaddr((struct sockaddr_in *) &route.rt_gateway, gw, 0);
- route.rt_flags |= RTF_GATEWAY;
- if ((gw ^ myaddr) & netmask) {
- printk(KERN_ERR "Root-NFS: Gateway not on local network!\n");
- return -ENETUNREACH;
- }
- }
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
- err = ip_rt_ioctl(op, &route);
- set_fs(oldfs);
-
-#ifdef NFSROOT_DEBUG
- /* in_ntoa in ipv4/utils.c uses a single static buffer, so
- * must make multiple printk calls, one for each in_ntoa
- * invocation...
- */
- printk(KERN_NOTICE "%s route ", (op == SIOCADDRT ? "add" : "del"));
- printk("%s ", in_ntoa(dest));
- printk("%s ", in_ntoa(mask));
- printk("%s: res %d\n", in_ntoa(gw), err);
-#endif
-
- return err;
-}
-
-__initfunc(static int
-root_dev_add_route(struct device *dev, __u32 dest, __u32 mask, __u32 gateway))
-{
- return root_dev_chg_route(SIOCADDRT, dev, dest, mask, gateway);
-}
-
-__initfunc(static int
-root_dev_del_route(struct device *dev, __u32 dest, __u32 mask, __u32 gateway))
-{
- return root_dev_chg_route(SIOCDELRT, dev, dest, mask, gateway);
-}
-
-/*
- * Restore the state of all devices. However, keep the root device open
- * for the upcoming mount.
- */
-__initfunc(static void root_dev_close(void))
-{
- struct open_dev *openp;
- struct open_dev *nextp;
-
- openp = open_base;
- while (openp != NULL) {
- nextp = openp->next;
- openp->next = NULL;
- if (openp->dev != root_dev) {
- if (!(openp->old_flags & IFF_UP)) {
- dev_close(openp->dev);
- }
-
- openp->dev->flags = openp->old_flags;
- }
- kfree_s(openp, sizeof(struct open_dev));
- openp = nextp;
- }
-}
-
-
-
-/***************************************************************************
-
- RARP Subroutines
-
- ***************************************************************************/
-
-#ifdef CONFIG_RNFS_RARP
-
-extern void arp_send(int type, int ptype, unsigned long target_ip,
- struct device *dev, unsigned long src_ip,
- unsigned char *dest_hw, unsigned char *src_hw,
- unsigned char *target_hw);
-
-static int root_rarp_recv(struct sk_buff *skb, struct device *dev,
- struct packet_type *pt);
-
-
-static struct packet_type rarp_packet_type __initdata = {
- 0, /* Should be: __constant_htons(ETH_P_RARP)
- * - but this _doesn't_ come out constant! */
- NULL, /* Listen to all devices */
- root_rarp_recv,
- NULL,
- NULL
-};
-
-
-/*
- * Register the packet type for RARP
- */
-__initfunc(static void root_rarp_open(void))
-{
- rarp_packet_type.type = htons(ETH_P_RARP);
- dev_add_pack(&rarp_packet_type);
-}
-
-
-/*
- * Deregister the RARP packet type
- */
-__initfunc(static void root_rarp_close(void))
-{
- rarp_packet_type.type = htons(ETH_P_RARP);
- dev_remove_pack(&rarp_packet_type);
-}
-
-
-/*
- * Receive RARP packets.
- */
-__initfunc(static int
-root_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt))
-{
- struct arphdr *rarp = (struct arphdr *)skb->h.raw;
- unsigned char *rarp_ptr = (unsigned char *) (rarp + 1);
- unsigned long sip, tip;
- unsigned char *sha, *tha; /* s for "source", t for "target" */
-
- /* If this test doesn't pass, it's not IP, or we should ignore it anyway */
- if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)) {
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-
- /* If it's not a RARP reply, delete it. */
- if (rarp->ar_op != htons(ARPOP_RREPLY)) {
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-
- /* If it's not ethernet or AX25, delete it. */
- if ((rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) ||
-#ifdef CONFIG_AX25
- (rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
-#endif
- rarp->ar_pln != 4) {
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-
- /* Extract variable width fields */
- sha = rarp_ptr;
- rarp_ptr += dev->addr_len;
- memcpy(&sip, rarp_ptr, 4);
- rarp_ptr += 4;
- tha = rarp_ptr;
- rarp_ptr += dev->addr_len;
- memcpy(&tip, rarp_ptr, 4);
-
- /* Discard packets which are not meant for us. */
- if (memcmp(tha, dev->dev_addr, dev->addr_len)) {
- kfree_skb(skb, FREE_READ);
- return 0;
- }
- /* Discard packets which are not from specified server. */
- if (rarp_flag && !bootp_flag &&
- rarp_serv != INADDR_NONE &&
- rarp_serv != sip) {
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-
- /*
- * The packet is what we were looking for. Setup the global
- * variables.
- */
- cli();
- if (pkt_arrived) {
- sti();
- kfree_skb(skb, FREE_READ);
- return 0;
- }
- pkt_arrived = ARRIVED_RARP;
- sti();
- root_dev = dev;
-
- if (myaddr == INADDR_NONE)
- myaddr = tip;
- if (servaddr == INADDR_NONE)
- servaddr = sip;
- kfree_skb(skb, FREE_READ);
- return 0;
-}
-
-
-/*
- * Send RARP request packet over all devices which allow RARP.
- */
-__initfunc(static void root_rarp_send(void))
-{
- struct open_dev *openp;
- struct device *dev;
- int num = 0;
-
- for (openp = open_base; openp != NULL; openp = openp->next) {
- dev = openp->dev;
- if (!(dev->flags & IFF_NOARP)) {
- arp_send(ARPOP_RREQUEST, ETH_P_RARP, 0, dev, 0, NULL,
- dev->dev_addr, dev->dev_addr);
- num++;
- }
- }
-}
-#endif
-
-
-
-/***************************************************************************
-
- BOOTP Subroutines
-
- ***************************************************************************/
-
-#ifdef CONFIG_RNFS_BOOTP
-
-static struct device *bootp_dev __initdata = NULL; /* Device selected as best BOOTP target */
-
-static struct socket *bootp_xmit_sock __initdata = NULL;/* BOOTP send socket */
-static struct socket *bootp_recv_sock __initdata = NULL;/* BOOTP receive socket */
-
-struct bootp_pkt { /* BOOTP packet format */
- u8 op; /* 1=request, 2=reply */
- u8 htype; /* HW address type */
- u8 hlen; /* HW address length */
- u8 hops; /* Used only by gateways */
- u32 xid; /* Transaction ID */
- u16 secs; /* Seconds since we started */
- u16 flags; /* Just what is says */
- u32 client_ip; /* Client's IP address if known */
- u32 your_ip; /* Assigned IP address */
- u32 server_ip; /* Server's IP address */
- u32 relay_ip; /* IP address of BOOTP relay */
- u8 hw_addr[16]; /* Client's HW address */
- u8 serv_name[64]; /* Server host name */
- u8 boot_file[128]; /* Name of boot file */
- u8 vendor_area[128]; /* Area for extensions */
-};
-
-#define BOOTP_REQUEST 1
-#define BOOTP_REPLY 2
-
-static struct bootp_pkt *xmit_bootp __initdata = NULL; /* Packet being transmitted */
-static struct bootp_pkt *recv_bootp __initdata = NULL; /* Packet being received */
-
-static int bootp_have_route __initdata = 0; /* BOOTP route installed */
-
-
-/*
- * Free BOOTP packet buffers
- */
-__initfunc(static void root_free_bootp(void))
-{
- if (xmit_bootp) {
- kfree_s(xmit_bootp, sizeof(struct bootp_pkt));
- xmit_bootp = NULL;
- }
- if (recv_bootp) {
- kfree_s(recv_bootp, sizeof(struct bootp_pkt));
- recv_bootp = NULL;
- }
-}
-
-
-/*
- * Allocate memory for BOOTP packet buffers
- */
-static inline int root_alloc_bootp(void)
-{
- if (!(xmit_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL)) ||
- !(recv_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL))) {
- printk(KERN_ERR "BOOTP: Out of memory!\n");
- return -1;
- }
- return 0;
-}
-
-
-/*
- * Create default route for BOOTP sending
- */
-__initfunc(static int root_add_bootp_route(void))
-{
- if (root_dev_add_route(bootp_dev, 0, 0, 0) < 0) {
- printk(KERN_ERR "BOOTP: Failed to add route\n");
- return -1;
- }
- bootp_have_route = 1;
- return 0;
-}
-
-
-/*
- * Delete default route for BOOTP sending
- */
-__initfunc(static int root_del_bootp_route(void))
-{
- if (bootp_have_route && root_dev_del_route(bootp_dev, 0, 0, 0) < 0) {
- printk(KERN_ERR "BOOTP: Deleting of route failed!\n");
- return -1;
- }
- bootp_have_route = 0;
- return 0;
-}
-
-
-/*
- * Open UDP socket.
- */
-__initfunc(static int root_open_udp_sock(struct socket **sock))
-{
- int err;
-
- if ((err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sock)) < 0)
- printk(KERN_ERR "BOOTP: Cannot open UDP socket!\n");
- return err;
-}
-
-
-/*
- * Connect UDP socket.
- */
-__initfunc(static int
-root_connect_udp_sock(struct socket *sock, u32 addr, u16 port))
-{
- struct sockaddr_in sa;
- int result;
-
- set_sockaddr(&sa, htonl(addr), htons(port));
- result = sock->ops->connect(sock, (struct sockaddr *) &sa, sizeof(sa), 0);
- if (result < 0) {
- printk(KERN_ERR "BOOTP: connect() failed\n");
- return -1;
- }
- return 0;
-}
-
-
-/*
- * Bind UDP socket.
- */
-__initfunc(static int
-root_bind_udp_sock(struct socket *sock, u32 addr, u16 port))
-{
- struct sockaddr_in sa;
- int result;
-
- set_sockaddr(&sa, htonl(addr), htons(port));
- result = sock->ops->bind(sock, (struct sockaddr *) &sa, sizeof(sa));
- if (result < 0) {
- printk(KERN_ERR "BOOTP: bind() failed\n");
- return -1;
- }
- return 0;
-}
-
-
-/*
- * Send UDP packet.
- */
-static inline int root_send_udp(struct socket *sock, void *buf, int size)
-{
- mm_segment_t oldfs;
- int result;
- struct msghdr msg;
- struct iovec iov;
-
- oldfs = get_fs();
- set_fs(get_ds());
- iov.iov_base = buf;
- iov.iov_len = size;
- memset(&msg, 0, sizeof(msg));
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- result = sock_sendmsg(sock, &msg, size);
- set_fs(oldfs);
-
- return (result != size);
-}
-
-
-/*
- * Try to receive UDP packet.
- */
-static inline int root_recv_udp(struct socket *sock, void *buf, int size)
-{
- mm_segment_t oldfs;
- int result;
- struct msghdr msg;
- struct iovec iov;
-
- oldfs = get_fs();
- set_fs(get_ds());
- iov.iov_base = buf;
- iov.iov_len = size;
- memset(&msg, 0, sizeof(msg));
- msg.msg_flags = MSG_DONTWAIT;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- result = sock_recvmsg(sock, &msg, size, MSG_DONTWAIT);
- set_fs(oldfs);
- return result;
-}
-
-
-/*
- * Initialize BOOTP extension fields in the request.
- */
-__initfunc(static void root_bootp_init_ext(u8 *e))
-{
- *e++ = 99; /* RFC1048 Magic Cookie */
- *e++ = 130;
- *e++ = 83;
- *e++ = 99;
- *e++ = 1; /* Subnet mask request */
- *e++ = 4;
- e += 4;
- *e++ = 3; /* Default gateway request */
- *e++ = 4;
- e += 4;
- *e++ = 12; /* Host name request */
- *e++ = 32;
- e += 32;
- *e++ = 40; /* NIS Domain name request */
- *e++ = 32;
- e += 32;
- *e++ = 17; /* Boot path */
- *e++ = 32;
- e += 32;
- *e = 255; /* End of the list */
-}
-
-
-/*
- * Deinitialize the BOOTP mechanism.
- */
-__initfunc(static void root_bootp_close(void))
-{
- if (bootp_xmit_sock)
- sock_release(bootp_xmit_sock);
- if (bootp_recv_sock)
- sock_release(bootp_recv_sock);
- root_del_bootp_route();
- root_free_bootp();
-}
-
-
-/*
- * Initialize the BOOTP mechanism.
- */
-__initfunc(static int root_bootp_open(void))
-{
- struct open_dev *openp;
- struct device *dev, *best_dev;
-
- /*
- * Select the best interface for BOOTP. We try to select a first
- * Ethernet-like interface. It's shame I know no simple way how to send
- * BOOTP's to all interfaces, but it doesn't apply to usual diskless
- * stations as they don't have multiple interfaces.
- */
-
- best_dev = NULL;
- for (openp = open_base; openp != NULL; openp = openp->next) {
- dev = openp->dev;
- if (dev->flags & IFF_BROADCAST) {
- if (!best_dev ||
- ((best_dev->flags & IFF_NOARP) && !(dev->flags & IFF_NOARP)))
- best_dev = dev;
- }
- }
-
- if (!best_dev) {
- printk(KERN_ERR "BOOTP: This cannot happen!\n");
- return -1;
- }
- bootp_dev = best_dev;
-
- /* Allocate memory for BOOTP packets */
- if (root_alloc_bootp())
- return -1;
-
- /* Construct BOOTP request */
- memset(xmit_bootp, 0, sizeof(struct bootp_pkt));
- xmit_bootp->op = BOOTP_REQUEST;
- get_random_bytes(&xmit_bootp->xid, sizeof(xmit_bootp->xid));
- xmit_bootp->htype = best_dev->type;
- xmit_bootp->hlen = best_dev->addr_len;
- memcpy(xmit_bootp->hw_addr, best_dev->dev_addr, best_dev->addr_len);
- root_bootp_init_ext(xmit_bootp->vendor_area);
-
-#ifdef NFSROOT_BOOTP_DEBUG
- {
- int x;
- printk(KERN_NOTICE "BOOTP: XID=%08x, DE=%s, HT=%02x, HL=%02x, HA=",
- xmit_bootp->xid,
- best_dev->name,
- xmit_bootp->htype,
- xmit_bootp->hlen);
- for(x=0; x<xmit_bootp->hlen; x++)
- printk("%02x", xmit_bootp->hw_addr[x]);
- printk("\n");
- }
-#endif
-
- /* Create default route to that interface */
- if (root_add_bootp_route())
- return -1;
-
- /* Open the sockets */
- if (root_open_udp_sock(&bootp_xmit_sock) ||
- root_open_udp_sock(&bootp_recv_sock))
- return -1;
-
- /* Bind/connect the sockets */
- bootp_xmit_sock->sk->broadcast = 1;
- bootp_xmit_sock->sk->reuse = 1;
- bootp_recv_sock->sk->reuse = 1;
- if (root_bind_udp_sock(bootp_recv_sock, INADDR_ANY, 68) ||
- root_bind_udp_sock(bootp_xmit_sock, INADDR_ANY, 68) ||
- root_connect_udp_sock(bootp_xmit_sock, INADDR_BROADCAST, 67))
- return -1;
-
- return 0;
-}
-
-
-/*
- * Send BOOTP request.
- */
-__initfunc(static int root_bootp_send(u32 jiffies))
-{
- xmit_bootp->secs = htons(jiffies / HZ);
- return root_send_udp(bootp_xmit_sock, xmit_bootp, sizeof(struct bootp_pkt));
-}
-
-
-/*
- * Copy BOOTP-supplied string if not already set.
- */
-__initfunc(static int
-root_bootp_string(char *dest, char *src, int len, int max))
-{
- if (*dest || !len)
- return 0;
- if (len > max-1)
- len = max-1;
- strncpy(dest, src, len);
- dest[len] = '\0';
- return 1;
-}
-
-
-/*
- * Process BOOTP extension.
- */
-__initfunc(static void root_do_bootp_ext(u8 *ext))
-{
-#ifdef NFSROOT_BOOTP_DEBUG
- u8 *c;
-
- printk(KERN_DEBUG "BOOTP: Got extension %02x",*ext);
- for(c=ext+2; c<ext+2+ext[1]; c++)
- printk(" %02x", *c);
- printk("\n");
-#endif
-
- switch (*ext++) {
- case 1: /* Subnet mask */
- if (netmask == INADDR_NONE)
- memcpy(&netmask, ext+1, 4);
- break;
- case 3: /* Default gateway */
- if (gateway == INADDR_NONE)
- memcpy(&gateway, ext+1, 4);
- break;
- case 12: /* Host name */
- root_bootp_string(system_utsname.nodename, ext+1, *ext, __NEW_UTS_LEN);
- break;
- case 40: /* NIS Domain name */
- root_bootp_string(system_utsname.domainname, ext+1, *ext, __NEW_UTS_LEN);
- break;
- case 17: /* Root path */
- root_bootp_string(nfs_path, ext+1, *ext, NFS_MAXPATHLEN);
- break;
- }
-}
-
-
-/*
- * Receive BOOTP request.
- */
-__initfunc(static void root_bootp_recv(void))
-{
- int len;
- u8 *ext, *end, *opt;
-
- len = root_recv_udp(bootp_recv_sock, recv_bootp, sizeof(struct bootp_pkt));
- if (len < 0)
- return;
-
- /* Check consistency of incoming packet */
- if (len < 300 || /* See RFC 1542:2.1 */
- recv_bootp->op != BOOTP_REPLY ||
- recv_bootp->htype != xmit_bootp->htype ||
- recv_bootp->hlen != xmit_bootp->hlen ||
- recv_bootp->xid != xmit_bootp->xid) {
- dprintk("?");
- return;
- }
-
- /* Record BOOTP packet arrival in the global variables */
- cli();
- if (pkt_arrived) {
- sti();
- return;
- }
- pkt_arrived = ARRIVED_BOOTP;
- sti();
- root_dev = bootp_dev;
-
- /* Extract basic fields */
- myaddr = recv_bootp->your_ip;
- if (servaddr==INADDR_NONE)
- servaddr = recv_bootp->server_ip;
-
- /* Parse extensions */
- if (recv_bootp->vendor_area[0] == 99 && /* Check magic cookie */
- recv_bootp->vendor_area[1] == 130 &&
- recv_bootp->vendor_area[2] == 83 &&
- recv_bootp->vendor_area[3] == 99) {
- ext = &recv_bootp->vendor_area[4];
- end = (u8 *) recv_bootp + len;
- while (ext < end && *ext != 255) {
- if (*ext == 0) /* Padding */
- ext++;
- else {
- opt = ext;
- ext += ext[1] + 2;
- if (ext <= end)
- root_do_bootp_ext(opt);
- }
- }
- }
-}
-#endif
-
+static int nfs_port __initdata = 0; /* Port to connect to for NFS */
+static int mount_port __initdata = 0; /* Mount daemon port number */
/***************************************************************************
- Dynamic configuration of IP.
-
- ***************************************************************************/
-
-#ifdef CONFIG_RNFS_DYNAMIC
-
-/*
- * Determine client and server IP numbers and appropriate device by using
- * the RARP and BOOTP protocols.
- */
-__initfunc(static int root_auto_config(void))
-{
- int retries;
- unsigned long timeout, jiff;
- unsigned long start_jiffies;
-
- /*
- * If neither BOOTP nor RARP was selected, return with an error. This
- * routine gets only called when some pieces of information are mis-
- * sing, and without BOOTP and RARP we are not able to get that in-
- * formation.
- */
- if (!bootp_flag && !rarp_flag) {
- printk(KERN_ERR "Root-NFS: Neither RARP nor BOOTP selected.\n");
- return -1;
- }
-
-#ifdef CONFIG_RNFS_BOOTP
- if (bootp_flag && !bootp_dev_count) {
- printk(KERN_ERR "Root-NFS: No suitable device for BOOTP found.\n");
- bootp_flag = 0;
- }
-#else
- bootp_flag = 0;
-#endif
-
-#ifdef CONFIG_RNFS_RARP
- if (rarp_flag && !rarp_dev_count) {
- printk(KERN_ERR "Root-NFS: No suitable device for RARP found.\n");
- rarp_flag = 0;
- }
-#else
- rarp_flag = 0;
-#endif
-
- if (!bootp_flag && !rarp_flag)
- /* Error message already printed */
- return -1;
-
- /*
- * Setup RARP and BOOTP protocols
- */
-#ifdef CONFIG_RNFS_RARP
- if (rarp_flag)
- root_rarp_open();
-#endif
-#ifdef CONFIG_RNFS_BOOTP
- if (bootp_flag && root_bootp_open() < 0) {
- root_bootp_close();
- return -1;
- }
-#endif
-
- /*
- * Send requests and wait, until we get an answer. This loop
- * seems to be a terrible waste of CPU time, but actually there is
- * only one process running at all, so we don't need to use any
- * scheduler functions.
- * [Actually we could now, but the nothing else running note still
- * applies.. - AC]
- */
- printk(KERN_NOTICE "Sending %s%s%s requests...",
- bootp_flag ? "BOOTP" : "",
- bootp_flag && rarp_flag ? " and " : "",
- rarp_flag ? "RARP" : "");
- start_jiffies = jiffies;
- retries = CONF_RETRIES;
- get_random_bytes(&timeout, sizeof(timeout));
- timeout = CONF_BASE_TIMEOUT + (timeout % (unsigned) CONF_TIMEOUT_RANDOM);
- for(;;) {
-#ifdef CONFIG_RNFS_BOOTP
- if (bootp_flag && root_bootp_send(jiffies - start_jiffies) < 0) {
- printk(" BOOTP failed!\n");
- root_bootp_close();
- bootp_flag = 0;
- if (!rarp_flag)
- break;
- }
-#endif
-#ifdef CONFIG_RNFS_RARP
- if (rarp_flag)
- root_rarp_send();
-#endif
- printk(".");
- jiff = jiffies + timeout;
- while (jiffies < jiff && !pkt_arrived)
-#ifdef CONFIG_RNFS_BOOTP
- root_bootp_recv();
-#else
- ;
-#endif
- if (pkt_arrived) {
- printk(" OK\n");
- break;
- }
- if (! --retries) {
- printk(" timed out!\n");
- break;
- }
- timeout = timeout CONF_TIMEOUT_MULT;
- if (timeout > CONF_TIMEOUT_MAX)
- timeout = CONF_TIMEOUT_MAX;
- }
-
-#ifdef CONFIG_RNFS_RARP
- if (rarp_flag)
- root_rarp_close();
-#endif
-#ifdef CONFIG_RNFS_BOOTP
- if (bootp_flag)
- root_bootp_close();
-#endif
-
- if (!pkt_arrived)
- return -1;
-
- printk(KERN_NOTICE "Root-NFS: Got %s answer from %s, ",
- (pkt_arrived == ARRIVED_BOOTP) ? "BOOTP" : "RARP",
- in_ntoa(servaddr));
- printk("my address is %s\n", in_ntoa(myaddr));
-
- return 0;
-}
-#endif
-
-/* Get default netmask - used to be exported from net/ipv4 */
-static inline unsigned long
-ip_get_mask(unsigned long addr)
-{
- if (!addr)
- return 0;
- addr = ntohl(addr);
- if (IN_CLASSA(addr))
- return htonl(IN_CLASSA_NET);
- if (IN_CLASSB(addr))
- return htonl(IN_CLASSB_NET);
- if (IN_CLASSC(addr))
- return htonl(IN_CLASSC_NET);
- return 0;
-}
-
-/***************************************************************************
-
Parsing of options
***************************************************************************/
-
/*
* The following integer options are recognized
*/
@@ -1107,6 +159,9 @@ __initfunc(static int root_nfs_name(char *name))
char *cp, *cq, *options, *val;
int octets = 0;
+ if (nfs_params_parsed)
+ return nfs_params_parsed;
+
/* It is possible to override the server IP number here */
cp = cq = name;
while (octets < 4) {
@@ -1123,33 +178,29 @@ __initfunc(static int root_nfs_name(char *name))
if (octets == 4 && (*cp == ':' || *cp == '\0')) {
if (*cp == ':')
*cp++ = '\0';
- servaddr = in_aton(name);
+ root_server_addr = in_aton(name);
name = cp;
}
/* Clear the nfs_data structure and setup the server hostname */
memset(&nfs_data, 0, sizeof(nfs_data));
- strncpy(nfs_data.hostname, in_ntoa(servaddr),
- sizeof(nfs_data.hostname)-1);
- nfs_data.namlen = strlen(nfs_data.hostname);
/* Set the name of the directory to mount */
- if (nfs_path[0] == '\0' || strncmp(name, "default", 7))
- strncpy(buf, name, NFS_MAXPATHLEN);
+ if (root_server_path[0] && !strcmp(name, "default"))
+ strncpy(buf, root_server_path, NFS_MAXPATHLEN-1);
else
- strncpy(buf, nfs_path, NFS_MAXPATHLEN);
+ strncpy(buf, name, NFS_MAXPATHLEN-1);
+ buf[NFS_MAXPATHLEN-1] = '\0';
if ((options = strchr(buf, ',')))
*options++ = '\0';
if (!strcmp(buf, "default"))
strcpy(buf, NFS_ROOT);
- cp = in_ntoa(myaddr);
+ cp = system_utsname.nodename;
if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) {
printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n");
return -1;
}
- /* update nfs_path with path from nfsroot=... command line parameter */
- if (*buf)
- sprintf(nfs_path, buf, cp);
+ sprintf(nfs_path, buf, cp);
/* Set some default values */
nfs_port = -1;
@@ -1188,27 +239,31 @@ __initfunc(static int root_nfs_name(char *name))
cp = strtok(NULL, ",");
}
}
- return 0;
+ return 1;
}
/*
+ * Get NFS server address.
+ */
+__initfunc(static int root_nfs_addr(void))
+{
+ if ((servaddr = root_server_addr) == INADDR_NONE) {
+ printk(KERN_ERR "Root-NFS: No NFS server available, giving up.\n");
+ return -1;
+ }
+
+ strncpy(nfs_data.hostname, in_ntoa(servaddr), sizeof(nfs_data.hostname)-1);
+ nfs_data.namlen = strlen(nfs_data.hostname);
+ return 0;
+}
+
+/*
* Tell the user what's going on.
*/
#ifdef NFSROOT_DEBUG
__initfunc(static void root_nfs_print(void))
{
-#define IN_NTOA(x) (((x) == INADDR_NONE) ? "none" : in_ntoa(x))
-
- printk(KERN_NOTICE "Root-NFS: IP config: dev=%s, ",
- root_dev ? root_dev->name : "none");
- printk("local=%s, ", IN_NTOA(myaddr));
- printk("server=%s, ", IN_NTOA(servaddr));
- printk("gw=%s, ", IN_NTOA(gateway));
- printk("mask=%s, ", IN_NTOA(netmask));
- printk("host=%s, domain=%s\n",
- system_utsname.nodename[0] ? system_utsname.nodename : "none",
- system_utsname.domainname[0] ? system_utsname.domainname : "none");
printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n",
nfs_path, nfs_data.hostname);
printk(KERN_NOTICE "Root-NFS: rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
@@ -1216,245 +271,26 @@ __initfunc(static void root_nfs_print(void))
printk(KERN_NOTICE "Root-NFS: acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
nfs_data.acregmin, nfs_data.acregmax,
nfs_data.acdirmin, nfs_data.acdirmax);
- printk(KERN_NOTICE "Root-NFS: port = %d, flags = %08x\n",
- nfs_port, nfs_data.flags);
-
-#undef IN_NTOA
+ printk(KERN_NOTICE "Root-NFS: nfsd port = %d, mountd port = %d, flags = %08x\n",
+ nfs_port, mount_port, nfs_data.flags);
}
#endif
-/*
- * Decode any IP configuration options in the "nfsaddrs" kernel command
- * line parameter. It consists of option fields separated by colons in
- * the following order:
- *
- * <client-ip>:<server-ip>:<gw-ip>:<netmask>:<host name>:<device>:<bootp|rarp>
- *
- * Any of the fields can be empty which means to use a default value:
- * <client-ip> - address given by BOOTP or RARP
- * <server-ip> - address of host returning BOOTP or RARP packet
- * <gw-ip> - none, or the address returned by BOOTP
- * <netmask> - automatically determined from <client-ip>, or the
- * one returned by BOOTP
- * <host name> - <client-ip> in ASCII notation, or the name returned
- * by BOOTP
- * <device> - use all available devices for RARP and the first
- * one for BOOTP
- * <bootp|rarp> - use both protocols to determine my own address
- */
-__initfunc(static void root_nfs_addrs(char *addrs))
-{
- char *cp, *ip, *dp;
- int num = 0;
-
- /* Clear all addresses and strings */
- myaddr = servaddr = rarp_serv = gateway = netmask = INADDR_NONE;
- system_utsname.nodename[0] = '\0';
- system_utsname.domainname[0] = '\0';
- user_dev_name[0] = '\0';
- bootp_flag = rarp_flag = 1;
-
- /* The following is just a shortcut for automatic IP configuration */
- if (!strcmp(addrs, "bootp")) {
- rarp_flag = 0;
- return;
- } else if (!strcmp(addrs, "rarp")) {
- bootp_flag = 0;
- return;
- } else if (!strcmp(addrs, "both")) {
- return;
- }
-
- /* Parse the whole string */
- ip = addrs;
- while (ip && *ip) {
- if ((cp = strchr(ip, ':')))
- *cp++ = '\0';
- if (strlen(ip) > 0) {
- dprintk("Root-NFS: Config string num %d is \"%s\"\n",
- num, ip);
- switch (num) {
- case 0:
- if ((myaddr = in_aton(ip)) == INADDR_ANY)
- myaddr = INADDR_NONE;
- break;
- case 1:
- if ((servaddr = in_aton(ip)) == INADDR_ANY)
- servaddr = INADDR_NONE;
- break;
- case 2:
- if ((gateway = in_aton(ip)) == INADDR_ANY)
- gateway = INADDR_NONE;
- break;
- case 3:
- if ((netmask = in_aton(ip)) == INADDR_ANY)
- netmask = INADDR_NONE;
- break;
- case 4:
- if ((dp = strchr(ip, '.'))) {
- *dp++ = '\0';
- strncpy(system_utsname.domainname, dp, __NEW_UTS_LEN);
- system_utsname.domainname[__NEW_UTS_LEN] = '\0';
- }
- strncpy(system_utsname.nodename, ip, __NEW_UTS_LEN);
- system_utsname.nodename[__NEW_UTS_LEN] = '\0';
- break;
- case 5:
- strncpy(user_dev_name, ip, IFNAMSIZ);
- user_dev_name[IFNAMSIZ-1] = '\0';
- break;
- case 6:
- if (!strcmp(ip, "rarp"))
- bootp_flag = 0;
- else if (!strcmp(ip, "bootp"))
- rarp_flag = 0;
- else if (strcmp(ip, "both"))
- bootp_flag = rarp_flag = 0;
- break;
- default:
- break;
- }
- }
- ip = cp;
- num++;
- }
- rarp_serv = servaddr;
-}
-
-
-/*
- * Set the interface address and configure a route to the server.
- */
-__initfunc(static int root_nfs_setup(void))
-{
- /* Set the default system name in case none was previously found */
- if (!system_utsname.nodename[0]) {
- strncpy(system_utsname.nodename, in_ntoa(myaddr), __NEW_UTS_LEN);
- system_utsname.nodename[__NEW_UTS_LEN] = '\0';
- }
-
- /* Set the correct netmask */
- if (netmask == INADDR_NONE)
- netmask = ip_get_mask(myaddr);
-
- /* Setup the device correctly */
- root_dev->family = AF_INET;
- root_dev->pa_addr = myaddr;
- root_dev->pa_mask = netmask;
- root_dev->pa_brdaddr = root_dev->pa_addr | ~root_dev->pa_mask;
- root_dev->pa_dstaddr = 0;
-
- /* Sticky situation, but it has a solution. We opened it earlier,
- * but before we knew what pa_addr etc. to give to it, thus the
- * routing code did not add a RTF_LOCAL route for it (how could
- * it?) so we send the pseudo device state change event now. -DaveM
- */
- ip_rt_event(NETDEV_CHANGE, root_dev);
-
- /*
- * Now add a route to the server. If there is no gateway given,
- * the server is on the same subnet, so we establish only a route to
- * the local network. Otherwise we create a route to the gateway (the
- * same local network router as in the former case) and then setup a
- * gatewayed default route. Note that this gives sufficient network
- * setup even for full system operation in all common cases.
- */
- if (root_dev_add_route(root_dev, myaddr, netmask, 0))
- {
- printk(KERN_ERR "Root-NFS: Adding of local route failed!\n");
- return -1;
- }
-
- if (gateway != INADDR_NONE) { /* Default route */
- if (root_dev_add_route(root_dev, INADDR_ANY, INADDR_ANY, gateway)) {
- printk(KERN_ERR "Root-NFS: Adding of default route failed!\n");
- return -1;
- }
- } else if ((servaddr ^ myaddr) & netmask) {
- printk(KERN_ERR "Root-NFS: Boot server not on local network and no default gateway configured!\n");
- return -1;
- }
-
- return 0;
-}
-
-
-/*
- * Get the necessary IP addresses and prepare for mounting the required
- * NFS filesystem.
- */
-__initfunc(int nfs_root_init(char *nfsname, char *nfsaddrs))
+__initfunc(int root_nfs_init(void))
{
#ifdef NFSROOT_DEBUG
nfs_debug |= NFSDBG_ROOT;
#endif
/*
- * Decode IP addresses and other configuration info contained
- * in the nfsaddrs string (which came from the kernel command
- * line).
- */
- root_nfs_addrs(nfsaddrs);
-
- /*
- * Setup all network devices
- */
- if (root_dev_open() < 0)
- return -1;
-
- /*
- * If the config information is insufficient (e.g., our IP address or
- * IP address of the boot server is missing or we have multiple network
- * interfaces and no default was set), use BOOTP or RARP to get the
- * missing values.
- *
- * Note that we don't try to set up correct routes for multiple
- * interfaces (could be solved by trying icmp echo requests), because
- * it's only necessary in the rare case of multiple ethernet devices
- * in the (diskless) system and if the server is on another subnet.
- * If only one interface is installed, the routing is obvious.
- */
- if ((myaddr == INADDR_NONE ||
- servaddr == INADDR_NONE ||
- (open_base != NULL && open_base->next != NULL))
-#ifdef CONFIG_RNFS_DYNAMIC
- && root_auto_config() < 0
-#endif
- ) {
- root_dev_close();
- return -1;
- }
- if (root_dev == NULL) {
- if (open_base != NULL && open_base->next == NULL) {
- root_dev = open_base->dev;
- } else {
- printk(KERN_ERR "Root-NFS: Multiple devices and no server\n");
- root_dev_close();
- return -1;
- }
- }
-
- /*
- * Close all network devices except the device which connects to
- * server
- */
- root_dev_close();
-
- /*
* Decode the root directory path name and NFS options from
* the kernel command line. This has to go here in order to
* be able to use the client IP address for the remote root
* directory (necessary for pure RARP booting).
*/
- if (root_nfs_name(nfsname) < 0)
- return -1;
-
- /*
- * Setup devices and routes. The server directory is actually
- * mounted after init() has been started.
- */
- if (root_nfs_setup() < 0)
+ if (root_nfs_name(nfs_root_name) < 0 ||
+ root_nfs_addr() < 0)
return -1;
#ifdef NFSROOT_DEBUG
@@ -1465,34 +301,65 @@ __initfunc(int nfs_root_init(char *nfsname, char *nfsaddrs))
}
+/*
+ * Parse NFS server and directory information passed on the kernel
+ * command line.
+ */
+__initfunc(void nfs_root_setup(char *line, int *ints))
+{
+ ROOT_DEV = MKDEV(UNNAMED_MAJOR, 255);
+ if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) {
+ strncpy(nfs_root_name, line, sizeof(nfs_root_name));
+ nfs_root_name[sizeof(nfs_root_name)-1] = '\0';
+ } else {
+ int n = strlen(line) + strlen(NFS_ROOT);
+ if (n >= sizeof(nfs_root_name))
+ line[sizeof(nfs_root_name) - strlen(NFS_ROOT) - 1] = '\0';
+ sprintf(nfs_root_name, NFS_ROOT, line);
+ }
+ nfs_params_parsed = root_nfs_name(nfs_root_name);
+}
+
+
/***************************************************************************
Routines to actually mount the root directory
***************************************************************************/
+
/*
- * Query server portmapper for the port of a daemon program
+ * Construct sockaddr_in from address and port number.
+ */
+static inline void
+set_sockaddr(struct sockaddr_in *sin, __u32 addr, __u16 port)
+{
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = addr;
+ sin->sin_port = port;
+}
+
+/*
+ * Query server portmapper for the port of a daemon program.
*/
__initfunc(static int root_nfs_getport(int program, int version))
{
- struct sockaddr_in sin;
+ struct sockaddr_in sin;
printk(KERN_NOTICE "Looking up port of RPC %d/%d on %s\n",
program, version, in_ntoa(servaddr));
- set_sockaddr(&sin, servaddr, 0);
- return rpc_getport_external(&sin, program, version, IPPROTO_UDP);
+ set_sockaddr(&sin, servaddr, 0);
+ return rpc_getport_external(&sin, program, version, IPPROTO_UDP);
}
/*
- * Get portnumbers for mountd and nfsd from server
- * The RPC layer does support portmapper queries; the only reason to
- * keep this code is that we may want to use fallback ports. But is there
- * actually someone who does not run portmap?
+ * Use portmapper to find mountd and nfsd port numbers if not overriden
+ * by the user. Use defaults if portmapper is not available.
+ * XXX: Is there any nfs server with no portmapper?
*/
__initfunc(static int root_nfs_ports(void))
{
- int port;
+ int port;
if (nfs_port < 0) {
if ((port = root_nfs_getport(NFS_PROGRAM, NFS_VERSION)) < 0) {
@@ -1510,10 +377,8 @@ __initfunc(static int root_nfs_ports(void))
"number from server, using default\n");
port = NFS_MNT_PORT;
}
-
mount_port = htons(port);
- dprintk("Root-NFS: Portmapper on server returned %d "
- "as mountd port\n", port);
+ dprintk("Root-NFS: mountd port is %d\n", port);
return 0;
}
@@ -1521,12 +386,12 @@ __initfunc(static int root_nfs_ports(void))
/*
* Get a file handle from the server for the directory which is to be
- * mounted
+ * mounted.
*/
__initfunc(static int root_nfs_get_handle(void))
{
struct sockaddr_in sin;
- int status;
+ int status;
set_sockaddr(&sin, servaddr, mount_port);
status = nfs_mount(&sin, nfs_path, &nfs_data.root);
@@ -1539,7 +404,7 @@ __initfunc(static int root_nfs_get_handle(void))
/*
- * Now actually mount the given directory
+ * Now actually mount the given directory.
*/
__initfunc(static int root_nfs_do_mount(struct super_block *sb))
{
@@ -1559,11 +424,10 @@ __initfunc(static int root_nfs_do_mount(struct super_block *sb))
*/
__initfunc(int nfs_root_mount(struct super_block *sb))
{
- if (root_nfs_ports() < 0)
- return -1;
- if (root_nfs_get_handle() < 0)
- return -1;
- if (root_nfs_do_mount(sb) < 0)
+ if (root_nfs_init() < 0
+ || root_nfs_ports() < 0
+ || root_nfs_get_handle() < 0
+ || root_nfs_do_mount(sb) < 0)
return -1;
return 0;
}
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index 416ed294e..38a9513dc 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -43,11 +43,6 @@
#include <asm/segment.h>
-/*
- * If NFS_DEBUG is defined, you can toggle NFS debugging by causing
- * a lookup of "xyzzy". Just cd to an NFS-mounted filesystem and type
- * 'ls xyzzy' to turn on debugging.
- */
#ifdef NFS_DEBUG
# define NFSDBG_FACILITY NFSDBG_PROC
#endif
@@ -90,10 +85,6 @@ nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *name,
int status;
dprintk("NFS call lookup %s\n", name);
-#ifdef RPC_DEBUG
- if (!strcmp(name, "xyzzy"))
- nfs_debug = ~nfs_debug;
-#endif
status = rpc_call(server->client, NFSPROC_LOOKUP, &arg, &res, 0);
dprintk("NFS reply lookup: %d\n", status);
return status;
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 4ce61f731..6f1fdd7ff 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -65,8 +65,8 @@ nfs_readreq_setup(struct nfs_rreq *req, struct nfs_fh *fh,
/*
* Read a page synchronously.
*/
-int
-nfs_readpage_sync(struct inode *inode, struct page *page)
+static int
+nfs_readpage_sync(struct dentry *dentry, struct inode *inode, struct page *page)
{
struct nfs_rreq rqst;
unsigned long offset = page->offset;
@@ -83,12 +83,13 @@ nfs_readpage_sync(struct inode *inode, struct page *page)
if (count < rsize)
rsize = count;
- dprintk("NFS: nfs_proc_read(%s, (%x,%lx), %ld, %d, %p)\n",
- NFS_SERVER(inode)->hostname, inode->i_dev,
- inode->i_ino, offset, rsize, buffer);
+ dprintk("NFS: nfs_proc_read(%s, (%s/%s), %ld, %d, %p)\n",
+ NFS_SERVER(inode)->hostname,
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ offset, rsize, buffer);
/* Set up arguments and perform rpc call */
- nfs_readreq_setup(&rqst, NFS_FH(inode), offset, buffer, rsize);
+ nfs_readreq_setup(&rqst, NFS_FH(dentry), offset, buffer, rsize);
result = rpc_call(NFS_CLIENT(inode), NFSPROC_READ,
&rqst.ra_args, &rqst.ra_res, flags);
@@ -114,8 +115,10 @@ nfs_readpage_sync(struct inode *inode, struct page *page)
result = 0;
io_error:
- if (refresh)
+ /* Note: we don't refresh if the call returned error */
+ if (refresh && result >= 0)
nfs_refresh_inode(inode, &rqst.ra_fattr);
+ /* N.B. Use nfs_unlock_page here? */
clear_bit(PG_locked, &page->flags);
wake_up(&page->wait);
return result;
@@ -130,17 +133,17 @@ nfs_readpage_result(struct rpc_task *task)
{
struct nfs_rreq *req = (struct nfs_rreq *) task->tk_calldata;
struct page *page = req->ra_page;
+ unsigned long address = page_address(page);
int result = task->tk_status;
static int succ = 0, fail = 0;
dprintk("NFS: %4d received callback for page %lx, result %d\n",
- task->tk_pid, page_address(page), result);
+ task->tk_pid, address, result);
if (result >= 0) {
result = req->ra_res.count;
if (result < PAGE_SIZE) {
- memset((char *) page_address(page) + result, 0,
- PAGE_SIZE - result);
+ memset((char *) address + result, 0, PAGE_SIZE - result);
}
nfs_refresh_inode(req->ra_inode, &req->ra_fattr);
set_bit(PG_uptodate, &page->flags);
@@ -150,50 +153,59 @@ nfs_readpage_result(struct rpc_task *task)
fail++;
dprintk("NFS: %d successful reads, %d failures\n", succ, fail);
}
+ /* N.B. Use nfs_unlock_page here? */
clear_bit(PG_locked, &page->flags);
wake_up(&page->wait);
- free_page(page_address(page));
+ free_page(address);
rpc_release_task(task);
kfree(req);
}
static inline int
-nfs_readpage_async(struct inode *inode, struct page *page)
+nfs_readpage_async(struct dentry *dentry, struct inode *inode,
+ struct page *page)
{
+ unsigned long address = page_address(page);
struct nfs_rreq *req;
- int result, flags;
+ int result = -1, flags;
dprintk("NFS: nfs_readpage_async(%p)\n", page);
- flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
+ if (NFS_CONGESTED(inode))
+ goto out_defer;
- if (NFS_CONGESTED(inode)
- || !(req = (struct nfs_rreq *) rpc_allocate(flags, sizeof(*req)))) {
- dprintk("NFS: deferring async READ request.\n");
- return -1;
- }
+ /* N.B. Do we need to test? Never called for swapfile inode */
+ flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
+ req = (struct nfs_rreq *) rpc_allocate(flags, sizeof(*req));
+ if (!req)
+ goto out_defer;
/* Initialize request */
- nfs_readreq_setup(req, NFS_FH(inode), page->offset,
- (void *) page_address(page), PAGE_SIZE);
+ /* N.B. Will the dentry remain valid for life of request? */
+ nfs_readreq_setup(req, NFS_FH(dentry), page->offset,
+ (void *) address, PAGE_SIZE);
req->ra_inode = inode;
- req->ra_page = page;
+ req->ra_page = page; /* count has been incremented by caller */
/* Start the async call */
dprintk("NFS: executing async READ request.\n");
result = rpc_do_call(NFS_CLIENT(inode), NFSPROC_READ,
&req->ra_args, &req->ra_res, flags,
nfs_readpage_result, req);
+ if (result < 0)
+ goto out_free;
+ result = 0;
+out:
+ return result;
- if (result >= 0) {
- atomic_inc(&page->count);
- return 0;
- }
-
+out_defer:
+ dprintk("NFS: deferring async READ request.\n");
+ goto out;
+out_free:
dprintk("NFS: failed to enqueue async READ request.\n");
kfree(req);
- return -1;
+ goto out;
}
/*
@@ -209,22 +221,24 @@ nfs_readpage_async(struct inode *inode, struct page *page)
* - The server is congested.
*/
int
-nfs_readpage(struct inode *inode, struct page *page)
+nfs_readpage(struct file *file, struct page *page)
{
- unsigned long address;
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = dentry->d_inode;
int error = -1;
- dprintk("NFS: nfs_readpage %08lx\n", page_address(page));
+ dprintk("NFS: nfs_readpage (%p %ld@%ld)\n",
+ page, PAGE_SIZE, page->offset);
set_bit(PG_locked, &page->flags);
- address = page_address(page);
atomic_inc(&page->count);
- if (!IS_SWAPFILE(inode) && !PageError(page)
- && NFS_SERVER(inode)->rsize >= PAGE_SIZE)
- error = nfs_readpage_async(inode, page);
- if (error < 0) /* couldn't enqueue */
- error = nfs_readpage_sync(inode, page);
- if (error < 0 && IS_SWAPFILE(inode))
- printk("Aiee.. nfs swap-in of page failed!\n");
- free_page(address);
+ if (!IS_SWAPFILE(inode) && !PageError(page) &&
+ NFS_SERVER(inode)->rsize >= PAGE_SIZE)
+ error = nfs_readpage_async(dentry, inode, page);
+ if (error < 0) { /* couldn't enqueue */
+ error = nfs_readpage_sync(dentry, inode, page);
+ if (error < 0 && IS_SWAPFILE(inode))
+ printk("Aiee.. nfs swap-in of page failed!\n");
+ free_page(page_address(page));
+ }
return error;
}
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index c739ebe6d..3ae490c37 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -18,8 +18,8 @@
#include <asm/uaccess.h>
-static int nfs_readlink(struct inode *, char *, int);
-static struct dentry *nfs_follow_link(struct inode *, struct dentry *);
+static int nfs_readlink(struct dentry *, char *, int);
+static struct dentry *nfs_follow_link(struct dentry *, struct dentry *);
/*
* symlinks can't do much...
@@ -44,19 +44,20 @@ struct inode_operations nfs_symlink_inode_operations = {
NULL /* permission */
};
-static int nfs_readlink(struct inode *inode, char *buffer, int buflen)
+static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
{
int error;
unsigned int len;
char *res;
void *mem;
- dfprintk(VFS, "nfs: readlink(%x/%ld)\n", inode->i_dev, inode->i_ino);
+ dfprintk(VFS, "nfs: readlink(%s/%s)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
if (buflen > NFS_MAXPATHLEN)
buflen = NFS_MAXPATHLEN;
- error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem,
- &res, &len, buflen);
+ error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry),
+ &mem, &res, &len, buflen);
if (! error) {
copy_to_user(buffer, res, len);
put_user('\0', buffer + len);
@@ -66,34 +67,41 @@ static int nfs_readlink(struct inode *inode, char *buffer, int buflen)
return error;
}
-static struct dentry * nfs_follow_link(struct inode * inode, struct dentry *base)
+static struct dentry *
+nfs_follow_link(struct dentry * dentry, struct dentry *base)
{
int error;
unsigned int len;
char *res;
void *mem;
char *path;
+ struct dentry *result;
- dfprintk(VFS, "nfs: follow_link(%x/%ld)\n", inode->i_dev, inode->i_ino);
+ dfprintk(VFS, "nfs: follow_link(%s/%s)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
- error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem,
- &res, &len, NFS_MAXPATHLEN);
+ error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry),
+ &mem, &res, &len, NFS_MAXPATHLEN);
+ result = ERR_PTR(error);
+ if (error)
+ goto out_dput;
- if (error) {
- dput(base);
- return ERR_PTR(error);
- }
+ result = ERR_PTR(-ENOMEM);
path = kmalloc(len + 1, GFP_KERNEL);
- if (!path) {
- dput(base);
- kfree(mem);
- return ERR_PTR(-ENOMEM);
- }
+ if (!path)
+ goto out_mem;
memcpy(path, res, len);
path[len] = 0;
kfree(mem);
- base = lookup_dentry(path, base, 1);
+ result = lookup_dentry(path, base, 1);
kfree(path);
- return base;
+out:
+ return result;
+
+out_mem:
+ kfree(mem);
+out_dput:
+ dput(base);
+ goto out;
}
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 53c227e58..71bdcf645 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -46,12 +46,12 @@
* Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de>
*/
-#define NFS_NEED_XDR_TYPES
#include <linux/config.h>
#include <linux/types.h>
#include <linux/malloc.h>
#include <linux/swap.h>
#include <linux/pagemap.h>
+
#include <linux/sunrpc/clnt.h>
#include <linux/nfs_fs.h>
#include <asm/uaccess.h>
@@ -66,51 +66,13 @@
*/
#define IS_SOFT 0
+#define NFS_PARANOIA 1
#define NFSDBG_FACILITY NFSDBG_PAGECACHE
static void nfs_wback_lock(struct rpc_task *task);
static void nfs_wback_result(struct rpc_task *task);
/*
- * This struct describes a file region to be written.
- * It's kind of a pity we have to keep all these lists ourselves, rather
- * than sticking an extra pointer into struct page.
- */
-struct nfs_wreq {
- struct rpc_listitem wb_list; /* linked list of req's */
- struct rpc_task wb_task; /* RPC task */
- struct inode * wb_inode; /* inode referenced */
- struct page * wb_page; /* page to be written */
- unsigned int wb_offset; /* offset within page */
- unsigned int wb_bytes; /* dirty range */
- pid_t wb_pid; /* owner process */
- unsigned short wb_flags; /* status flags */
-
- struct nfs_writeargs * wb_args; /* NFS RPC stuff */
- struct nfs_fattr * wb_fattr; /* file attributes */
-};
-#define wb_status wb_task.tk_status
-
-#define WB_NEXT(req) ((struct nfs_wreq *) ((req)->wb_list.next))
-
-/*
- * Various flags for wb_flags
- */
-#define NFS_WRITE_WANTLOCK 0x0001 /* needs to lock page */
-#define NFS_WRITE_LOCKED 0x0002 /* holds lock on page */
-#define NFS_WRITE_CANCELLED 0x0004 /* has been cancelled */
-#define NFS_WRITE_UNCOMMITTED 0x0008 /* written but uncommitted (NFSv3) */
-#define NFS_WRITE_INVALIDATE 0x0010 /* invalidate after write */
-#define NFS_WRITE_INPROGRESS 0x0020 /* RPC call in progress */
-
-#define WB_INPROGRESS(req) ((req)->wb_flags & NFS_WRITE_INPROGRESS)
-#define WB_WANTLOCK(req) ((req)->wb_flags & NFS_WRITE_WANTLOCK)
-#define WB_HAVELOCK(req) ((req)->wb_flags & NFS_WRITE_LOCKED)
-#define WB_CANCELLED(req) ((req)->wb_flags & NFS_WRITE_CANCELLED)
-#define WB_UNCOMMITTED(req) ((req)->wb_flags & NFS_WRITE_UNCOMMITTED)
-#define WB_INVALIDATE(req) ((req)->wb_flags & NFS_WRITE_INVALIDATE)
-
-/*
* Cache parameters
*/
#define NFS_WRITEBACK_DELAY (10 * HZ)
@@ -143,8 +105,13 @@ nfs_unlock_page(struct page *page)
/* async swap-out support */
if (test_and_clear_bit(PG_decr_after, &page->flags))
atomic_dec(&page->count);
- if (test_and_clear_bit(PG_swap_unlock_after, &page->flags))
- swap_after_unlock_page(page->pg_swap_entry);
+ if (test_and_clear_bit(PG_swap_unlock_after, &page->flags)) {
+ /*
+ * We're doing a swap, so check that this page is
+ * swap-cached and do the necessary cleanup.
+ */
+ swap_after_unlock_page(page->offset);
+ }
#endif
}
@@ -160,7 +127,7 @@ transfer_page_lock(struct nfs_wreq *req)
req->wb_flags |= NFS_WRITE_LOCKED;
rpc_wake_up_task(&req->wb_task);
- dprintk("nfs: wake up task %d (flags %x)\n",
+ dprintk("NFS: wake up task %d (flags %x)\n",
req->wb_task.tk_pid, req->wb_flags);
}
@@ -169,17 +136,17 @@ transfer_page_lock(struct nfs_wreq *req)
* Offset is the data offset within the page.
*/
static int
-nfs_writepage_sync(struct inode *inode, struct page *page,
- unsigned long offset, unsigned int count)
+nfs_writepage_sync(struct dentry *dentry, struct inode *inode,
+ struct page *page, unsigned long offset, unsigned int count)
{
- struct nfs_fattr fattr;
unsigned int wsize = NFS_SERVER(inode)->wsize;
int result, refresh = 0, written = 0;
u8 *buffer;
+ struct nfs_fattr fattr;
- dprintk("NFS: nfs_writepage_sync(%x/%ld %d@%ld)\n",
- inode->i_dev, inode->i_ino,
- count, page->offset + offset);
+ dprintk("NFS: nfs_writepage_sync(%s/%s %d@%ld)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ count, page->offset + offset);
buffer = (u8 *) page_address(page) + offset;
offset += page->offset;
@@ -188,7 +155,7 @@ nfs_writepage_sync(struct inode *inode, struct page *page,
if (count < wsize && !IS_SWAPFILE(inode))
wsize = count;
- result = nfs_proc_write(NFS_SERVER(inode), NFS_FH(inode),
+ result = nfs_proc_write(NFS_DSERVER(dentry), NFS_FH(dentry),
IS_SWAPFILE(inode), offset, wsize,
buffer, &fattr);
@@ -214,8 +181,8 @@ nfs_writepage_sync(struct inode *inode, struct page *page,
} while (count);
io_error:
- /* N.B. do we want to refresh if there was an error?? (fattr valid?) */
- if (refresh) {
+ /* Note: we don't refresh if the call failed (fattr invalid) */
+ if (refresh && result >= 0) {
/* See comments in nfs_wback_result */
/* N.B. I don't think this is right -- sync writes in order */
if (fattr.size < inode->i_size)
@@ -281,6 +248,27 @@ find_write_request(struct inode *inode, struct page *page)
}
/*
+ * Find any requests for the specified dentry.
+ */
+int
+nfs_find_dentry_request(struct inode *inode, struct dentry *dentry)
+{
+ struct nfs_wreq *head, *req;
+ int found = 0;
+
+ req = head = NFS_WRITEBACK(inode);
+ while (req != NULL) {
+ if (req->wb_dentry == dentry) {
+ found = 1;
+ break;
+ }
+ if ((req = WB_NEXT(req)) == head)
+ break;
+ }
+ return found;
+}
+
+/*
* Find a failed write request by pid
*/
static struct nfs_wreq *
@@ -380,16 +368,16 @@ update_write_request(struct nfs_wreq *req, unsigned int first,
* Create and initialize a writeback request
*/
static inline struct nfs_wreq *
-create_write_request(struct inode *inode, struct page *page,
- unsigned int offset, unsigned int bytes)
+create_write_request(struct dentry *dentry, struct inode *inode,
+ struct page *page, unsigned int offset, unsigned int bytes)
{
- struct nfs_wreq *wreq;
struct rpc_clnt *clnt = NFS_CLIENT(inode);
+ struct nfs_wreq *wreq;
struct rpc_task *task;
- dprintk("NFS: create_write_request(%x/%ld, %ld+%d)\n",
- inode->i_dev, inode->i_ino,
- page->offset + offset, bytes);
+ dprintk("NFS: create_write_request(%s/%s, %ld+%d)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ page->offset + offset, bytes);
/* FIXME: Enforce hard limit on number of concurrent writes? */
@@ -399,7 +387,7 @@ create_write_request(struct inode *inode, struct page *page,
memset(wreq, 0, sizeof(*wreq));
task = &wreq->wb_task;
- rpc_init_task(task, clnt, nfs_wback_result, 0);
+ rpc_init_task(task, clnt, nfs_wback_result, RPC_TASK_NFSWRITE);
task->tk_calldata = wreq;
task->tk_action = nfs_wback_lock;
@@ -408,6 +396,7 @@ create_write_request(struct inode *inode, struct page *page,
goto out_req;
/* Put the task on inode's writeback request list. */
+ wreq->wb_dentry = dentry;
wreq->wb_inode = inode;
wreq->wb_pid = current->pid;
wreq->wb_page = page;
@@ -434,7 +423,7 @@ out_fail:
* Schedule a writeback RPC call.
* If the server is congested, don't add to our backlog of queued
* requests but call it synchronously.
- * The function returns true if the page has been unlocked as the
+ * The function returns false if the page has been unlocked as the
* consequence of a synchronous write call.
*
* FIXME: Here we could walk the inode's lock list to see whether the
@@ -504,9 +493,10 @@ wait_on_write_request(struct nfs_wreq *req)
* (for now), and we currently do this synchronously only.
*/
int
-nfs_writepage(struct inode *inode, struct page *page)
+nfs_writepage(struct file * file, struct page *page)
{
- return nfs_writepage_sync(inode, page, 0, PAGE_SIZE);
+ struct dentry *dentry = file->f_dentry;
+ return nfs_writepage_sync(dentry, dentry->d_inode, page, 0, PAGE_SIZE);
}
/*
@@ -516,27 +506,20 @@ nfs_writepage(struct inode *inode, struct page *page)
* things with a page scheduled for an RPC call (e.g. invalidate it).
*/
int
-nfs_updatepage(struct inode *inode, struct page *page, const char *buffer,
+nfs_updatepage(struct file *file, struct page *page, const char *buffer,
unsigned long offset, unsigned int count, int sync)
{
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ u8 *page_addr = (u8 *) page_address(page);
struct nfs_wreq *req;
int status = 0, page_locked = 1;
- u8 *page_addr;
- dprintk("NFS: nfs_updatepage(%x/%ld %d@%ld, sync=%d)\n",
- inode->i_dev, inode->i_ino,
- count, page->offset+offset, sync);
+ dprintk("NFS: nfs_updatepage(%s/%s %d@%ld, sync=%d)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ count, page->offset+offset, sync);
set_bit(PG_locked, &page->flags);
- page_addr = (u8 *) page_address(page);
-
- /* If wsize is smaller than page size, update and write
- * page synchronously.
- */
- if (NFS_SERVER(inode)->wsize < PAGE_SIZE) {
- copy_from_user(page_addr + offset, buffer, count);
- return nfs_writepage_sync(inode, page, offset, count);
- }
/*
* Try to find a corresponding request on the writeback queue.
@@ -550,6 +533,7 @@ nfs_updatepage(struct inode *inode, struct page *page, const char *buffer,
*/
if ((req = find_write_request(inode, page)) != NULL) {
if (update_write_request(req, offset, count)) {
+ /* N.B. check for a fault here and cancel the req */
copy_from_user(page_addr + offset, buffer, count);
goto updated;
}
@@ -558,16 +542,23 @@ nfs_updatepage(struct inode *inode, struct page *page, const char *buffer,
return 0;
}
+ /* Copy data to page buffer. */
+ status = -EFAULT;
+ if (copy_from_user(page_addr + offset, buffer, count))
+ goto done;
+
+ /* If wsize is smaller than page size, update and write
+ * page synchronously.
+ */
+ if (NFS_SERVER(inode)->wsize < PAGE_SIZE)
+ return nfs_writepage_sync(dentry, inode, page, offset, count);
+
/* Create the write request. */
status = -ENOBUFS;
- req = create_write_request(inode, page, offset, count);
+ req = create_write_request(dentry, inode, page, offset, count);
if (!req)
goto done;
- /* Copy data to page buffer. */
- /* N.B. should check for fault here ... */
- copy_from_user(page_addr + offset, buffer, count);
-
/* Schedule request */
page_locked = schedule_write_request(req, sync);
@@ -597,8 +588,14 @@ done:
if ((count = nfs_write_error(inode)) < 0)
status = count;
}
- } else
+ } else {
+ if (status < 0) {
+printk("NFS: %s/%s write failed, clearing bit\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+ clear_bit(PG_uptodate, &page->flags);
+ }
nfs_unlock_page(page);
+ }
}
dprintk("NFS: nfs_updatepage returns %d (isize %ld)\n",
@@ -609,14 +606,18 @@ done:
/*
* Flush out a dirty page.
*/
-static inline void
+static void
nfs_flush_request(struct nfs_wreq *req)
{
struct page *page = req->wb_page;
- dprintk("NFS: nfs_flush_request(%x/%ld, @%ld)\n",
- page->inode->i_dev, page->inode->i_ino,
- page->offset);
+#ifdef NFS_DEBUG_VERBOSE
+if (req->wb_inode != page->inode)
+printk("NFS: inode %ld no longer has page %p\n", req->wb_inode->i_ino, page);
+#endif
+ dprintk("NFS: nfs_flush_request(%s/%s, @%ld)\n",
+ req->wb_dentry->d_parent->d_name.name,
+ req->wb_dentry->d_name.name, page->offset);
req->wb_flags |= NFS_WRITE_WANTLOCK;
if (!test_and_set_bit(PG_locked, &page->flags)) {
@@ -642,30 +643,24 @@ nfs_flush_pages(struct inode *inode, pid_t pid, off_t offset, off_t len,
req = head = NFS_WRITEBACK(inode);
while (req != NULL) {
- dprintk("NFS: %4d nfs_flush inspect %x/%ld @%ld fl %x\n",
- req->wb_task.tk_pid,
- req->wb_inode->i_dev, req->wb_inode->i_ino,
- req->wb_page->offset, req->wb_flags);
- if (!WB_INPROGRESS(req)) {
- rqoffset = req->wb_page->offset + req->wb_offset;
- rqend = rqoffset + req->wb_bytes;
-
- if (rqoffset < end && offset < rqend
- && (pid == 0 || req->wb_pid == pid)) {
- if (!WB_HAVELOCK(req)) {
-#ifdef NFS_PARANOIA
+ dprintk("NFS: %4d nfs_flush inspect %s/%s @%ld fl %x\n",
+ req->wb_task.tk_pid,
+ req->wb_dentry->d_parent->d_name.name,
+ req->wb_dentry->d_name.name,
+ req->wb_page->offset, req->wb_flags);
+
+ rqoffset = req->wb_page->offset + req->wb_offset;
+ rqend = rqoffset + req->wb_bytes;
+ if (rqoffset < end && offset < rqend &&
+ (pid == 0 || req->wb_pid == pid)) {
+ if (!WB_INPROGRESS(req) && !WB_HAVELOCK(req)) {
+#ifdef NFS_DEBUG_VERBOSE
printk("nfs_flush: flushing inode=%ld, %d @ %lu\n",
req->wb_inode->i_ino, req->wb_bytes, rqoffset);
#endif
- nfs_flush_request(req);
- }
- last = req;
+ nfs_flush_request(req);
}
- } else {
-#ifdef NFS_PARANOIA
-printk("nfs_flush_pages: in progress inode=%ld, %d @ %lu\n",
-req->wb_inode->i_ino, req->wb_bytes, rqoffset);
-#endif
+ last = req;
}
if (invalidate)
req->wb_flags |= NFS_WRITE_INVALIDATE;
@@ -677,11 +672,23 @@ req->wb_inode->i_ino, req->wb_bytes, rqoffset);
}
/*
+ * Cancel a write request. We always mark it cancelled,
+ * but if it's already in progress there's no point in
+ * calling rpc_exit, and we don't want to overwrite the
+ * tk_status field.
+ */
+static void
+nfs_cancel_request(struct nfs_wreq *req)
+{
+ req->wb_flags |= NFS_WRITE_CANCELLED;
+ if (!WB_INPROGRESS(req)) {
+ rpc_exit(&req->wb_task, 0);
+ rpc_wake_up_task(&req->wb_task);
+ }
+}
+
+/*
* Cancel all writeback requests, both pending and in progress.
- *
- * N.B. This doesn't seem to wake up the tasks -- are we sure
- * they will eventually complete? Also, this could overwrite a
- * failed status code from an already-completed task.
*/
static void
nfs_cancel_dirty(struct inode *inode, pid_t pid)
@@ -690,11 +697,8 @@ nfs_cancel_dirty(struct inode *inode, pid_t pid)
req = head = NFS_WRITEBACK(inode);
while (req != NULL) {
- /* N.B. check for task already finished? */
- if (pid == 0 || req->wb_pid == pid) {
- req->wb_flags |= NFS_WRITE_CANCELLED;
- rpc_exit(&req->wb_task, 0);
- }
+ if (pid == 0 || req->wb_pid == pid)
+ nfs_cancel_request(req);
if ((req = WB_NEXT(req)) == head)
break;
}
@@ -715,8 +719,7 @@ nfs_flush_dirty_pages(struct inode *inode, pid_t pid, off_t offset, off_t len)
int result = 0, cancel = 0;
dprintk("NFS: flush_dirty_pages(%x/%ld for pid %d %ld/%ld)\n",
- inode->i_dev, inode->i_ino, current->pid,
- offset, len);
+ inode->i_dev, inode->i_ino, current->pid, offset, len);
if (IS_SOFT && signalled()) {
nfs_cancel_dirty(inode, pid);
@@ -769,16 +772,15 @@ nfs_truncate_dirty_pages(struct inode *inode, unsigned long offset)
struct nfs_wreq *req, *head;
unsigned long rqoffset;
- dprintk("NFS: truncate_dirty_pages(%x/%ld, %ld)\n",
- inode->i_dev, inode->i_ino, offset);
+ dprintk("NFS: truncate_dirty_pages(%d/%ld, %ld)\n",
+ inode->i_dev, inode->i_ino, offset);
req = head = NFS_WRITEBACK(inode);
while (req != NULL) {
rqoffset = req->wb_page->offset + req->wb_offset;
if (rqoffset >= offset) {
- req->wb_flags |= NFS_WRITE_CANCELLED;
- rpc_exit(&req->wb_task, 0);
+ nfs_cancel_request(req);
} else if (rqoffset + req->wb_bytes >= offset) {
req->wb_bytes = offset - rqoffset;
}
@@ -823,47 +825,37 @@ nfs_wback_lock(struct rpc_task *task)
{
struct nfs_wreq *req = (struct nfs_wreq *) task->tk_calldata;
struct page *page = req->wb_page;
- struct inode *inode = req->wb_inode;
+ struct dentry *dentry = req->wb_dentry;
- dprintk("NFS: %4d nfs_wback_lock (status %d flags %x)\n",
- task->tk_pid, task->tk_status, req->wb_flags);
+ dprintk("NFS: %4d nfs_wback_lock (%s/%s, status=%d flags=%x)\n",
+ task->tk_pid, dentry->d_parent->d_name.name,
+ dentry->d_name.name, task->tk_status, req->wb_flags);
if (!WB_HAVELOCK(req))
req->wb_flags |= NFS_WRITE_WANTLOCK;
- if (WB_WANTLOCK(req) && test_and_set_bit(PG_locked, &page->flags)) {
- dprintk("NFS: page already locked in writeback_lock!\n");
- task->tk_timeout = 2 * HZ;
- rpc_sleep_on(&write_queue, task, NULL, NULL);
- return;
- }
- task->tk_status = 0;
+ if (WB_WANTLOCK(req) && test_and_set_bit(PG_locked, &page->flags))
+ goto out_locked;
req->wb_flags &= ~NFS_WRITE_WANTLOCK;
req->wb_flags |= NFS_WRITE_LOCKED;
-
- if (req->wb_args == 0) {
- size_t size = sizeof(struct nfs_writeargs)
- + sizeof(struct nfs_fattr);
- void *ptr;
-
- if (!(ptr = kmalloc(size, GFP_KERNEL))) {
- task->tk_timeout = HZ;
- rpc_sleep_on(&write_queue, task, NULL, NULL);
- return;
- }
- req->wb_args = (struct nfs_writeargs *) ptr;
- req->wb_fattr = (struct nfs_fattr *) (req->wb_args + 1);
- }
+ task->tk_status = 0;
/* Setup the task struct for a writeback call */
- req->wb_args->fh = NFS_FH(inode);
- req->wb_args->offset = page->offset + req->wb_offset;
- req->wb_args->count = req->wb_bytes;
- req->wb_args->buffer = (void *) (page_address(page) + req->wb_offset);
+ req->wb_args.fh = NFS_FH(dentry);
+ req->wb_args.offset = page->offset + req->wb_offset;
+ req->wb_args.count = req->wb_bytes;
+ req->wb_args.buffer = (void *) (page_address(page) + req->wb_offset);
- rpc_call_setup(task, NFSPROC_WRITE, req->wb_args, req->wb_fattr, 0);
+ rpc_call_setup(task, NFSPROC_WRITE, &req->wb_args, &req->wb_fattr, 0);
req->wb_flags |= NFS_WRITE_INPROGRESS;
+ return;
+
+out_locked:
+ printk("NFS: page already locked in writeback_lock!\n");
+ task->tk_timeout = 2 * HZ;
+ rpc_sleep_on(&write_queue, task, NULL, NULL);
+ return;
}
/*
@@ -873,17 +865,16 @@ static void
nfs_wback_result(struct rpc_task *task)
{
struct nfs_wreq *req = (struct nfs_wreq *) task->tk_calldata;
- struct inode *inode;
- struct page *page;
- int status;
-
- dprintk("NFS: %4d nfs_wback_result (status %d)\n",
- task->tk_pid, task->tk_status);
+ struct inode *inode = req->wb_inode;
+ struct page *page = req->wb_page;
+ int status = task->tk_status;
- inode = req->wb_inode;
- page = req->wb_page;
- status = task->tk_status;
+ dprintk("NFS: %4d nfs_wback_result (%s/%s, status=%d, flags=%x)\n",
+ task->tk_pid, req->wb_dentry->d_parent->d_name.name,
+ req->wb_dentry->d_name.name, status, req->wb_flags);
+ /* Set the WRITE_COMPLETE flag, but leave WRITE_INPROGRESS set */
+ req->wb_flags |= NFS_WRITE_COMPLETE;
if (status < 0) {
/*
* An error occurred. Report the error back to the
@@ -894,7 +885,7 @@ nfs_wback_result(struct rpc_task *task)
status = 0;
clear_bit(PG_uptodate, &page->flags);
} else if (!WB_CANCELLED(req)) {
- struct nfs_fattr *fattr = req->wb_fattr;
+ struct nfs_fattr *fattr = &req->wb_fattr;
/* Update attributes as result of writeback.
* Beware: when UDP replies arrive out of order, we
* may end up overwriting a previous, bigger file size.
@@ -930,11 +921,6 @@ nfs_wback_result(struct rpc_task *task)
if (WB_HAVELOCK(req))
nfs_unlock_page(page);
- if (req->wb_args) {
- kfree(req->wb_args);
- req->wb_args = 0;
- }
-
/*
* Now it's safe to remove the request from the inode's
* writeback list and wake up any tasks sleeping on it.