patch-2.3.6 linux/fs/nfs/symlink.c
Next file: linux/fs/nfs/write.c
Previous file: linux/fs/nfs/read.c
Back to the patch index
Back to the overall index
- Lines: 198
- Date:
Tue Jun 8 22:11:58 1999
- Orig file:
v2.3.5/linux/fs/nfs/symlink.c
- Orig date:
Mon Apr 12 09:57:11 1999
diff -u --recursive --new-file v2.3.5/linux/fs/nfs/symlink.c linux/fs/nfs/symlink.c
@@ -5,12 +5,18 @@
*
* Optimization changes Copyright (C) 1994 Florian La Roche
*
+ * Jun 7 1999, cache symlink lookups in the page cache. -DaveM
+ *
* nfs symlink handling code
*/
+#define NFS_NEED_XDR_TYPES
#include <linux/sched.h>
#include <linux/errno.h>
+#include <linux/sunrpc/clnt.h>
#include <linux/nfs_fs.h>
+#include <linux/nfs.h>
+#include <linux/pagemap.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/malloc.h>
@@ -44,63 +50,128 @@
NULL /* permission */
};
-static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
+/* Symlink caching in the page cache is even more simplistic
+ * and straight-forward than readdir caching.
+ */
+static struct page *try_to_get_symlink_page(struct dentry *dentry, struct inode *inode)
{
- int error;
- unsigned int len;
- char *res;
- void *mem;
-
- dfprintk(VFS, "nfs: readlink(%s/%s)\n",
- dentry->d_parent->d_name.name, dentry->d_name.name);
-
- error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry),
- &mem, &res, &len, NFS_MAXPATHLEN);
- if (! error) {
- if (len > buflen)
- len = buflen;
- copy_to_user(buffer, res, len);
- error = len;
- kfree(mem);
+ struct nfs_readlinkargs rl_args;
+ struct page *page, **hash;
+ unsigned long page_cache;
+
+ page = NULL;
+ page_cache = page_cache_alloc();
+ if (!page_cache)
+ goto out;
+
+ hash = page_hash(inode, 0);
+ page = __find_page(inode, 0, *hash);
+ if (page) {
+ page_cache_free(page_cache);
+ goto out;
}
- return error;
+
+ page = page_cache_entry(page_cache);
+ atomic_inc(&page->count);
+ page->flags = ((page->flags &
+ ~((1 << PG_uptodate) | (1 << PG_error))) |
+ ((1 << PG_referenced) | (1 << PG_locked)));
+ page->offset = 0;
+ add_page_to_inode_queue(inode, page);
+ __add_page_to_hash_queue(page, hash);
+
+ /* We place the length at the beginning of the page,
+ * in host byte order, followed by the string. The
+ * XDR response verification will NULL terminate it.
+ */
+ rl_args.fh = NFS_FH(dentry);
+ rl_args.buffer = (const void *)page_cache;
+ if (rpc_call(NFS_CLIENT(inode), NFSPROC_READLINK,
+ &rl_args, NULL, 0) < 0)
+ goto error;
+ set_bit(PG_uptodate, &page->flags);
+unlock_out:
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
+out:
+ return page;
+
+error:
+ set_bit(PG_error, &page->flags);
+ goto unlock_out;
+}
+
+static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ struct inode *inode = dentry->d_inode;
+ struct page *page, **hash;
+ u32 *p, len;
+
+ /* Caller revalidated the directory inode already. */
+ hash = page_hash(inode, 0);
+ page = __find_page(inode, 0, *hash);
+ if (!page)
+ goto no_readlink_page;
+ if (PageLocked(page))
+ goto readlink_locked_wait;
+ if (!PageUptodate(page))
+ goto readlink_read_error;
+success:
+ p = (u32 *) page_address(page);
+ len = *p++;
+ if (len > buflen)
+ len = buflen;
+ copy_to_user(buffer, p, len);
+ page_cache_release(page);
+ return len;
+
+no_readlink_page:
+ page = try_to_get_symlink_page(dentry, inode);
+ if (!page)
+ goto no_page;
+readlink_locked_wait:
+ wait_on_page(page);
+ if (PageUptodate(page))
+ goto success;
+readlink_read_error:
+ page_cache_release(page);
+no_page:
+ return -EIO;
}
static struct dentry *
-nfs_follow_link(struct dentry * dentry, struct dentry *base, unsigned int follow)
+nfs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow)
{
- int error;
- unsigned int len;
- char *res;
- void *mem;
- char *path;
struct dentry *result;
-
- dfprintk(VFS, "nfs: follow_link(%s/%s)\n",
- dentry->d_parent->d_name.name, dentry->d_name.name);
-
- error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry),
- &mem, &res, &len, NFS_MAXPATHLEN);
- result = ERR_PTR(error);
- if (error)
- goto out_dput;
-
- result = ERR_PTR(-ENOMEM);
- path = kmalloc(len + 1, GFP_KERNEL);
- if (!path)
- goto out_mem;
- memcpy(path, res, len);
- path[len] = 0;
- kfree(mem);
-
- result = lookup_dentry(path, base, follow);
- kfree(path);
-out:
+ struct inode *inode = dentry->d_inode;
+ struct page *page, **hash;
+ u32 *p;
+
+ /* Caller revalidated the directory inode already. */
+ hash = page_hash(inode, 0);
+ page = __find_page(inode, 0, *hash);
+ if (!page)
+ goto no_followlink_page;
+ if (PageLocked(page))
+ goto followlink_locked_wait;
+ if (!PageUptodate(page))
+ goto followlink_read_error;
+success:
+ p = (u32 *) page_address(page);
+ result = lookup_dentry((char *) (p + 1), base, follow);
+ page_cache_release(page);
return result;
-out_mem:
- kfree(mem);
-out_dput:
- dput(base);
- goto out;
+no_followlink_page:
+ page = try_to_get_symlink_page(dentry, inode);
+ if (!page)
+ goto no_page;
+followlink_locked_wait:
+ wait_on_page(page);
+ if (PageUptodate(page))
+ goto success;
+followlink_read_error:
+ page_cache_release(page);
+no_page:
+ return ERR_PTR(-EIO);
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)