patch-2.3.6 linux/fs/nfs/dir.c
Next file: linux/fs/nfs/inode.c
Previous file: linux/fs/ncpfs/inode.c
Back to the patch index
Back to the overall index
- Lines: 734
- Date:
Tue Jun 8 17:58:03 1999
- Orig file:
v2.3.5/linux/fs/nfs/dir.c
- Orig date:
Fri May 14 18:55:26 1999
diff -u --recursive --new-file v2.3.5/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
@@ -14,8 +14,10 @@
* Following Linus comments on my original hack, this version
* depends only on the dcache stuff and doesn't touch the inode
* layer (iput() and friends).
+ * 6 Jun 1999 Cache readdir lookups in the page cache. -DaveM
*/
+#define NFS_NEED_XDR_TYPES
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/stat.h>
@@ -24,31 +26,16 @@
#include <linux/kernel.h>
#include <linux/malloc.h>
#include <linux/mm.h>
-#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/clnt.h>
#include <linux/nfs_fs.h>
+#include <linux/nfs.h>
+#include <linux/pagemap.h>
#include <asm/segment.h> /* for fs functions */
#define NFS_PARANOIA 1
/* #define NFS_DEBUG_VERBOSE 1 */
-/*
- * Head for a dircache entry. Currently still very simple; when
- * the cache grows larger, we will need a LRU list.
- */
-struct nfs_dirent {
- dev_t dev; /* device number */
- ino_t ino; /* inode number */
- u32 cookie; /* cookie of first entry */
- unsigned short valid : 1, /* data is valid */
- locked : 1; /* entry locked */
- unsigned int size; /* # of entries */
- unsigned long age; /* last used */
- unsigned long mtime; /* last attr stamp */
- wait_queue_head_t wait;
- __u32 * entry; /* three __u32's per entry */
-};
-
static int nfs_safe_remove(struct dentry *);
static ssize_t nfs_dir_read(struct file *, char *, size_t, loff_t *);
@@ -107,253 +94,326 @@
return -EISDIR;
}
-static struct nfs_dirent dircache[NFS_MAX_DIRCACHE];
+/* Each readdir response is composed of entries which look
+ * like the following, as per the NFSv2 RFC:
+ *
+ * __u32 not_end zero if end of response
+ * __u32 file ID opaque ino_t
+ * __u32 namelen size of name string
+ * VAR name string the string, padded to modulo 4 bytes
+ * __u32 cookie opaque ID of next entry
+ *
+ * When you hit not_end being zero, the next __u32 is non-zero if
+ * this is the end of the complete set of readdir entires for this
+ * directory. This can be used, for example, to initiate pre-fetch.
+ *
+ * In order to know what to ask the server for, we only need to know
+ * the final cookie of the previous page, and offset zero has cookie
+ * zero, so we cache cookie to page offset translations in chunks.
+ */
+#define COOKIES_PER_CHUNK (8 - ((sizeof(void *) / sizeof(__u32))))
+struct nfs_cookie_table {
+ struct nfs_cookie_table *next;
+ __u32 cookies[COOKIES_PER_CHUNK];
+};
+static kmem_cache_t *nfs_cookie_cachep;
-/*
- * We need to do caching of directory entries to prevent an
- * incredible amount of RPC traffic. Only the most recent open
- * directory is cached. This seems sufficient for most purposes.
- * Technically, we ought to flush the cache on close but this is
- * not a problem in practice.
+/* Since a cookie of zero is declared special by the NFS
+ * protocol, we easily can tell if a cookie in an existing
+ * table chunk is valid or not.
*
- * XXX: Do proper directory caching by stuffing data into the
- * page cache (may require some fiddling for rsize < PAGE_SIZE).
+ * NOTE: The cookies are indexed off-by-one because zero
+ * need not an entry.
*/
+static __inline__ __u32 *find_cookie(struct inode *inode, unsigned long off)
+{
+ static __u32 cookie_zero = 0;
+ struct nfs_cookie_table *p;
+ __u32 *ret;
+
+ if (!off)
+ return &cookie_zero;
+ off -= 1;
+ p = NFS_COOKIES(inode);
+ while(off >= COOKIES_PER_CHUNK && p) {
+ off -= COOKIES_PER_CHUNK;
+ p = p->next;
+ }
+ ret = NULL;
+ if (p) {
+ ret = &p->cookies[off];
+ if (!*ret)
+ ret = NULL;
+ }
+ return ret;
+}
-static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+/* Now we cache directories properly, by stuffing the dirent
+ * data directly in the page cache.
+ *
+ * Inode invalidation due to refresh etc. takes care of
+ * _everything_, no sloppy entry flushing logic, no extraneous
+ * copying, network direct to page cache, the way it was meant
+ * to be.
+ *
+ * NOTE: Dirent information verification is done always by the
+ * page-in of the RPC reply, nowhere else, this simplies
+ * things substantially.
+ */
+#define NFS_NAMELEN_ALIGN(__len) ((((__len)+3)>>2)<<2)
+static u32 find_midpoint(__u32 *p, u32 doff)
{
- struct dentry *dentry = filp->f_dentry;
- struct inode *inode = dentry->d_inode;
- static DECLARE_WAIT_QUEUE_HEAD(readdir_wait);
- wait_queue_head_t *waitp = NULL;
- struct nfs_dirent *cache, *free;
- unsigned long age, dead;
- u32 cookie;
- int ismydir, result;
- int i, j, index = 0;
- __u32 *entry;
- char *name, *start;
+ u32 walk = doff & PAGE_MASK;
- dfprintk(VFS, "NFS: nfs_readdir(%s/%s)\n",
- dentry->d_parent->d_name.name, dentry->d_name.name);
+ while(*p++ != 0) {
+ __u32 skip;
- result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
- if (result < 0)
- goto out;
+ p++; /* skip fileid */
- /*
- * Try to find the entry in the cache
- */
-again:
- if (waitp) {
- interruptible_sleep_on(waitp);
- if (signal_pending(current))
- return -ERESTARTSYS;
- waitp = NULL;
+ /* Skip len, name, and cookie. */
+ skip = NFS_NAMELEN_ALIGN(*p++);
+ p += (skip >> 2) + 1;
+ walk += skip + (4 * sizeof(__u32));
+ if (walk >= doff)
+ break;
}
+ return walk;
+}
- cookie = filp->f_pos;
- entry = NULL;
- free = NULL;
- age = ~(unsigned long) 0;
- dead = jiffies - NFS_ATTRTIMEO(inode);
+static int create_cookie(__u32 cookie, unsigned long off, struct inode *inode)
+{
+ struct nfs_cookie_table **cpp;
- for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) {
- /*
- dprintk("NFS: dircache[%d] valid %d locked %d\n",
- i, cache->valid, cache->locked);
- */
- ismydir = (cache->dev == inode->i_dev
- && cache->ino == inode->i_ino);
- if (cache->locked) {
- if (!ismydir || cache->cookie != cookie)
- continue;
- dfprintk(DIRCACHE, "NFS: waiting on dircache entry\n");
- waitp = &cache->wait;
- goto again;
+ cpp = (struct nfs_cookie_table **) &NFS_COOKIES(inode);
+ while (off >= COOKIES_PER_CHUNK && *cpp) {
+ off -= COOKIES_PER_CHUNK;
+ cpp = &(*cpp)->next;
+ }
+ if (*cpp) {
+ (*cpp)->cookies[off] = cookie;
+ } else {
+ struct nfs_cookie_table *new;
+ int i;
+
+ new = kmem_cache_alloc(nfs_cookie_cachep, SLAB_ATOMIC);
+ if(!new)
+ return -1;
+ *cpp = new;
+ new->next = NULL;
+ for(i = 0; i < COOKIES_PER_CHUNK; i++) {
+ if (i == off) {
+ new->cookies[i] = cookie;
+ } else {
+ new->cookies[i] = 0;
+ }
}
+ }
+ return 0;
+}
- if (ismydir && cache->mtime != inode->i_mtime)
- cache->valid = 0;
-
- if (!cache->valid || cache->age < dead) {
- free = cache;
- age = 0;
- } else if (cache->age < age) {
- free = cache;
- age = cache->age;
- }
+static struct page *try_to_get_dirent_page(struct file *, unsigned long, int);
- if (!ismydir || !cache->valid)
- continue;
+/* Recover from a revalidation flush. The case here is that
+ * the inode for the directory got invalidated somehow, and
+ * all of our cached information is lost. In order to get
+ * a correct cookie for the current readdir request from the
+ * user, we must (re-)fetch older readdir page cache entries.
+ */
+static int refetch_to_readdir_off(struct file *file, struct inode *inode, u32 off)
+{
+ u32 cur_off, goal_off = off & PAGE_MASK;
- if (cache->cookie == cookie && cache->size > 0) {
- entry = cache->entry + (index = 0);
- cache->locked = 1;
- break;
- }
- for (j = 0; j < cache->size; j++) {
- __u32 *this_ent = cache->entry + j*3;
+again:
+ cur_off = 0;
+ while (cur_off < goal_off) {
+ struct page *page;
+
+ page = find_page(inode, cur_off);
+ if (page) {
+ if (PageLocked(page))
+ __wait_on_page(page);
+ if (!PageUptodate(page))
+ return -1;
+ } else {
+ page = try_to_get_dirent_page(file, cur_off, 0);
+ if (!page) {
+ if (!cur_off)
+ return -1;
- if (*(this_ent+1) != cookie)
- continue;
- if (j < cache->size - 1) {
- index = j + 1;
- entry = this_ent + 3;
- } else if (*(this_ent+2) & (1 << 15)) {
- /* eof */
- return 0;
+ /* Someone touched the dir on us. */
+ goto again;
}
- break;
- }
- if (entry) {
- dfprintk(DIRCACHE, "NFS: found dircache entry %d\n",
- (int)(cache - dircache));
- cache->locked = 1;
- break;
- }
- }
-
- /*
- * Okay, entry not present in cache, or locked and inaccessible.
- * Set up the cache entry and attempt a READDIR call.
- */
- if (entry == NULL) {
- if ((cache = free) == NULL) {
- dfprintk(DIRCACHE, "NFS: dircache contention\n");
- waitp = &readdir_wait;
- goto again;
- }
- dfprintk(DIRCACHE, "NFS: using free dircache entry %d\n",
- (int)(free - dircache));
- cache->cookie = cookie;
- cache->locked = 1;
- cache->valid = 0;
- cache->dev = inode->i_dev;
- cache->ino = inode->i_ino;
- init_waitqueue_head(&cache->wait);
- if (!cache->entry) {
- result = -ENOMEM;
- cache->entry = (__u32 *) get_free_page(GFP_KERNEL);
- if (!cache->entry)
- goto done;
+ page_cache_release(page);
}
- result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(dentry),
- cookie, PAGE_SIZE, cache->entry);
- if (result <= 0)
- goto done;
- cache->size = result;
- cache->valid = 1;
- entry = cache->entry + (index = 0);
+ cur_off += PAGE_SIZE;
}
- cache->mtime = inode->i_mtime;
- cache->age = jiffies;
- /*
- * Yowza! We have a cache entry...
- */
- start = (char *) cache->entry;
- while (index < cache->size) {
- __u32 fileid = *entry++;
- __u32 nextpos = *entry++; /* cookie */
- __u32 length = *entry++;
+ return 0;
+}
- /*
- * Unpack the eof flag, offset, and length
- */
- result = length & (1 << 15); /* eof flag */
- name = start + ((length >> 16) & 0xFFFF);
- length &= 0x7FFF;
- /*
- dprintk("NFS: filldir(%p, %.*s, %d, %d, %x, eof %x)\n", entry,
- (int) length, name, length,
- (unsigned int) filp->f_pos,
- fileid, result);
- */
+static struct page *try_to_get_dirent_page(struct file *file, unsigned long offset, int refetch_ok)
+{
+ struct nfs_readdirargs rd_args;
+ struct nfs_readdirres rd_res;
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ struct page *page, **hash;
+ unsigned long page_cache;
+ __u32 *cookiep;
+
+ page = NULL;
+ page_cache = page_cache_alloc();
+ if (!page_cache)
+ goto out;
- if (filldir(dirent, name, length, cookie, fileid) < 0)
- break;
- cookie = nextpos;
- index++;
+ while ((cookiep = find_cookie(inode, offset)) == NULL) {
+ if (!refetch_ok ||
+ refetch_to_readdir_off(file, inode, file->f_pos))
+ goto out;
}
- filp->f_pos = cookie;
- result = 0;
-
- /* XXX: May want to kick async readdir-ahead here. Not too hard
- * to do. */
-done:
- dfprintk(DIRCACHE, "NFS: nfs_readdir complete\n");
- cache->locked = 0;
- wake_up(&cache->wait);
- wake_up(&readdir_wait);
+ hash = page_hash(inode, offset);
+ page = __find_page(inode, offset, *hash);
+ if (page) {
+ page_cache_free(page_cache);
+ goto out;
+ }
+ 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 = offset;
+ add_page_to_inode_queue(inode, page);
+ __add_page_to_hash_queue(page, hash);
+
+ rd_args.fh = NFS_FH(dentry);
+ rd_res.buffer = (char *)page_cache;
+ rd_res.bufsiz = PAGE_CACHE_SIZE;
+ rd_res.cookie = *cookiep;
+ do {
+ rd_args.buffer = rd_res.buffer;
+ rd_args.bufsiz = rd_res.bufsiz;
+ rd_args.cookie = rd_res.cookie;
+ if (rpc_call(NFS_CLIENT(inode),
+ NFSPROC_READDIR, &rd_args, &rd_res, 0) < 0)
+ goto error;
+ } while(rd_res.bufsiz > 0);
+
+ if (rd_res.bufsiz < 0)
+ NFS_DIREOF(inode) =
+ (offset << PAGE_CACHE_SHIFT) + -(rd_res.bufsiz);
+ else if (create_cookie(rd_res.cookie, offset, inode))
+ goto error;
+
+ set_bit(PG_uptodate, &page->flags);
+unlock_out:
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
out:
- return result;
+ return page;
+
+error:
+ set_bit(PG_error, &page->flags);
+ goto unlock_out;
}
-/*
- * Invalidate dircache entries for an inode.
- */
-void
-nfs_invalidate_dircache(struct inode *inode)
+static __inline__ u32 nfs_do_filldir(__u32 *p, u32 doff,
+ void *dirent, filldir_t filldir)
{
- struct nfs_dirent *cache = dircache;
- dev_t dev = inode->i_dev;
- ino_t ino = inode->i_ino;
- int i;
-
- dfprintk(DIRCACHE, "NFS: invalidate dircache for %x/%ld\n", dev, (long)ino);
- for (i = NFS_MAX_DIRCACHE; i--; cache++) {
- if (cache->ino != ino)
- continue;
- if (cache->dev != dev)
- continue;
- if (cache->locked) {
- printk("NFS: cache locked for %s/%ld\n",
- kdevname(dev), (long) ino);
- continue;
- }
- cache->valid = 0; /* brute force */
+ u32 end;
+
+ if (doff & ~PAGE_CACHE_MASK) {
+ doff = find_midpoint(p, doff);
+ p += (doff & ~PAGE_CACHE_MASK) >> 2;
+ }
+ while((end = *p++) != 0) {
+ __u32 fileid = *p++;
+ __u32 len = *p++;
+ __u32 skip = NFS_NAMELEN_ALIGN(len);
+ char *name = (char *) p;
+
+ /* Skip the cookie. */
+ p = ((__u32 *) (name + skip)) + 1;
+ if (filldir(dirent, name, len, doff, fileid) < 0)
+ goto out;
+ doff += (skip + (4 * sizeof(__u32)));
}
+ if (!*p)
+ doff = PAGE_CACHE_ALIGN(doff);
+out:
+ return doff;
}
-/*
- * Invalidate the dircache for a super block (or all caches),
- * and release the cache memory.
+/* The file offset position is represented in pure bytes, to
+ * make the page cache interface straight forward.
+ *
+ * However, some way is needed to make the connection between the
+ * opaque NFS directory entry cookies and our offsets, so a per-inode
+ * cookie cache table is used.
*/
-void
-nfs_invalidate_dircache_sb(struct super_block *sb)
+static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
- struct nfs_dirent *cache = dircache;
- int i;
+ struct dentry *dentry = filp->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ struct page *page, **hash;
+ unsigned long offset;
+ int res;
+
+ res = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
+ if (res < 0)
+ return res;
+
+ if (NFS_DIREOF(inode) && filp->f_pos >= NFS_DIREOF(inode))
+ return 0;
+
+ offset = filp->f_pos >> PAGE_CACHE_SHIFT;
+ hash = page_hash(inode, offset);
+ page = __find_page(inode, offset, *hash);
+ if (!page)
+ goto no_dirent_page;
+ if (PageLocked(page))
+ goto dirent_locked_wait;
+ if (!PageUptodate(page))
+ goto dirent_read_error;
+success:
+ filp->f_pos = nfs_do_filldir((__u32 *) page_address(page),
+ filp->f_pos, dirent, filldir);
+ page_cache_release(page);
+ return 0;
- for (i = NFS_MAX_DIRCACHE; i--; cache++) {
- if (sb && sb->s_dev != cache->dev)
- continue;
- if (cache->locked) {
- printk("NFS: cache locked at umount %s\n",
- (cache->entry ? "(lost a page!)" : ""));
- continue;
- }
- cache->valid = 0; /* brute force */
- if (cache->entry) {
- free_page((unsigned long) cache->entry);
- cache->entry = NULL;
- }
- }
+no_dirent_page:
+ page = try_to_get_dirent_page(filp, offset, 1);
+ if (!page)
+ goto no_page;
+
+dirent_locked_wait:
+ wait_on_page(page);
+ if (PageUptodate(page))
+ goto success;
+dirent_read_error:
+ page_cache_release(page);
+no_page:
+ return -EIO;
}
-/*
- * Free directory cache memory
- * Called from cleanup_module
+/* Invalidate directory cookie caches and EOF marker
+ * for an inode.
*/
-void
-nfs_free_dircache(void)
+__inline__ void nfs_invalidate_dircache(struct inode *inode)
{
- dfprintk(DIRCACHE, "NFS: freeing dircache\n");
- nfs_invalidate_dircache_sb(NULL);
+ struct nfs_cookie_table *p = NFS_COOKIES(inode);
+
+ if (p != NULL) {
+ NFS_COOKIES(inode) = NULL;
+ do { struct nfs_cookie_table *next = p->next;
+ kmem_cache_free(nfs_cookie_cachep, p);
+ p = next;
+ } while (p != NULL);
+ }
+ NFS_DIREOF(inode) = 0;
}
/*
@@ -475,10 +535,15 @@
out_valid:
return 1;
out_bad:
- if (dentry->d_parent->d_inode)
+ /* Purge readdir caches. */
+ if (dentry->d_parent->d_inode) {
+ invalidate_inode_pages(dentry->d_parent->d_inode);
nfs_invalidate_dircache(dentry->d_parent->d_inode);
- if (inode && S_ISDIR(inode->i_mode))
+ }
+ if (inode && S_ISDIR(inode->i_mode)) {
+ invalidate_inode_pages(inode);
nfs_invalidate_dircache(inode);
+ }
return 0;
}
@@ -522,13 +587,25 @@
#endif
}
+static kmem_cache_t *nfs_fh_cachep;
+
+__inline__ struct nfs_fh *nfs_fh_alloc(void)
+{
+ return kmem_cache_alloc(nfs_fh_cachep, SLAB_KERNEL);
+}
+
+__inline__ void nfs_fh_free(struct nfs_fh *p)
+{
+ kmem_cache_free(nfs_fh_cachep, p);
+}
+
/*
* 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);
+ nfs_fh_free(dentry->d_fsdata);
}
struct dentry_operations nfs_dentry_operations = {
@@ -579,7 +656,7 @@
error = -ENOMEM;
if (!dentry->d_fsdata) {
- dentry->d_fsdata = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
+ dentry->d_fsdata = nfs_fh_alloc();
if (!dentry->d_fsdata)
goto out;
}
@@ -661,6 +738,7 @@
/*
* Invalidate the dir cache before the operation to avoid a race.
*/
+ invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir);
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, &sattr, &fhandle, &fattr);
@@ -690,6 +768,7 @@
sattr.size = rdev; /* get out your barf bag */
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
+ invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir);
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, &sattr, &fhandle, &fattr);
@@ -724,6 +803,7 @@
* depending on potentially bogus information.
*/
d_drop(dentry);
+ invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir);
error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent),
dentry->d_name.name, &sattr, &fhandle, &fattr);
@@ -744,6 +824,7 @@
dentry->d_inode->i_count, dentry->d_inode->i_nlink);
#endif
+ invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir);
error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name);
@@ -871,6 +952,7 @@
goto out;
} while(sdentry->d_inode != NULL); /* need negative lookup */
+ invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir);
error = nfs_proc_rename(NFS_SERVER(dir),
NFS_FH(dentry->d_parent), dentry->d_name.name,
@@ -940,6 +1022,7 @@
inode->i_nlink --;
d_delete(dentry);
}
+ invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir);
error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name);
@@ -1006,6 +1089,7 @@
* can't instantiate the new inode.
*/
d_drop(dentry);
+ invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir);
error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, symname, &sattr);
@@ -1036,6 +1120,7 @@
* we can't use the existing dentry.
*/
d_drop(dentry);
+ invalidate_inode_pages(dir);
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);
@@ -1181,7 +1266,9 @@
d_delete(new_dentry);
}
+ invalidate_inode_pages(new_dir);
nfs_invalidate_dircache(new_dir);
+ invalidate_inode_pages(old_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,
@@ -1199,6 +1286,25 @@
if (dentry)
dput(dentry);
return error;
+}
+
+int nfs_init_fhcache(void)
+{
+ nfs_fh_cachep = kmem_cache_create("nfs_fh",
+ sizeof(struct nfs_fh),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (nfs_fh_cachep == NULL)
+ return -ENOMEM;
+
+ nfs_cookie_cachep = kmem_cache_create("nfs_dcookie",
+ sizeof(struct nfs_cookie_table),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (nfs_cookie_cachep == NULL)
+ return -ENOMEM;
+
+ return 0;
}
/*
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)