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/inode.c | |
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/inode.c')
-rw-r--r-- | fs/nfs/inode.c | 301 |
1 files changed, 215 insertions, 86 deletions
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; } |