diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-03-17 22:05:47 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-03-17 22:05:47 +0000 |
commit | 27cfca1ec98e91261b1a5355d10a8996464b63af (patch) | |
tree | 8e895a53e372fa682b4c0a585b9377d67ed70d0e /fs/nfs | |
parent | 6a76fb7214c477ccf6582bd79c5b4ccc4f9c41b1 (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/.cvsignore | 1 | ||||
-rw-r--r-- | fs/nfs/dir.c | 289 | ||||
-rw-r--r-- | fs/nfs/file.c | 54 | ||||
-rw-r--r-- | fs/nfs/inode.c | 301 | ||||
-rw-r--r-- | fs/nfs/nfs2xdr.c | 6 | ||||
-rw-r--r-- | fs/nfs/nfs3xdr.c | 1 | ||||
-rw-r--r-- | fs/nfs/nfsroot.c | 1350 | ||||
-rw-r--r-- | fs/nfs/proc.c | 9 | ||||
-rw-r--r-- | fs/nfs/read.c | 94 | ||||
-rw-r--r-- | fs/nfs/symlink.c | 50 | ||||
-rw-r--r-- | fs/nfs/write.c | 316 |
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. |