const: mark remaining super_operations const
[safe/jmp/linux-2.6] / net / sunrpc / cache.c
index b5f2786..45cdaff 100644 (file)
 #include <linux/net.h>
 #include <linux/workqueue.h>
 #include <linux/mutex.h>
+#include <linux/pagemap.h>
 #include <asm/ioctls.h>
 #include <linux/sunrpc/types.h>
 #include <linux/sunrpc/cache.h>
 #include <linux/sunrpc/stats.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
 
 #define         RPCDBG_FACILITY RPCDBG_CACHE
 
@@ -98,7 +100,7 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail,
 
        return new;
 }
-EXPORT_SYMBOL(sunrpc_cache_lookup);
+EXPORT_SYMBOL_GPL(sunrpc_cache_lookup);
 
 
 static void queue_loose(struct cache_detail *detail, struct cache_head *ch);
@@ -173,9 +175,15 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail,
        cache_put(old, detail);
        return tmp;
 }
-EXPORT_SYMBOL(sunrpc_cache_update);
+EXPORT_SYMBOL_GPL(sunrpc_cache_update);
+
+static int cache_make_upcall(struct cache_detail *cd, struct cache_head *h)
+{
+       if (!cd->cache_upcall)
+               return -EINVAL;
+       return cd->cache_upcall(cd, h);
+}
 
-static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h);
 /*
  * This is the generic cache management routine for all
  * the authentication caches.
@@ -245,7 +253,7 @@ int cache_check(struct cache_detail *detail,
                cache_put(h, detail);
        return rv;
 }
-EXPORT_SYMBOL(cache_check);
+EXPORT_SYMBOL_GPL(cache_check);
 
 /*
  * caches need to be periodically cleaned.
@@ -284,83 +292,11 @@ static DEFINE_SPINLOCK(cache_list_lock);
 static struct cache_detail *current_detail;
 static int current_index;
 
-static const struct file_operations cache_file_operations;
-static const struct file_operations content_file_operations;
-static const struct file_operations cache_flush_operations;
-
 static void do_cache_clean(struct work_struct *work);
 static DECLARE_DELAYED_WORK(cache_cleaner, do_cache_clean);
 
-static void remove_cache_proc_entries(struct cache_detail *cd)
-{
-       if (cd->proc_ent == NULL)
-               return;
-       if (cd->flush_ent)
-               remove_proc_entry("flush", cd->proc_ent);
-       if (cd->channel_ent)
-               remove_proc_entry("channel", cd->proc_ent);
-       if (cd->content_ent)
-               remove_proc_entry("content", cd->proc_ent);
-       cd->proc_ent = NULL;
-       remove_proc_entry(cd->name, proc_net_rpc);
-}
-
-#ifdef CONFIG_PROC_FS
-static int create_cache_proc_entries(struct cache_detail *cd)
-{
-       struct proc_dir_entry *p;
-
-       cd->proc_ent = proc_mkdir(cd->name, proc_net_rpc);
-       if (cd->proc_ent == NULL)
-               goto out_nomem;
-       cd->proc_ent->owner = cd->owner;
-       cd->channel_ent = cd->content_ent = NULL;
-
-       p = proc_create("flush", S_IFREG|S_IRUSR|S_IWUSR,
-                       cd->proc_ent, &cache_flush_operations);
-       cd->flush_ent = p;
-       if (p == NULL)
-               goto out_nomem;
-       p->owner = cd->owner;
-       p->data = cd;
-
-       if (cd->cache_request || cd->cache_parse) {
-               p = proc_create("channel", S_IFREG|S_IRUSR|S_IWUSR,
-                               cd->proc_ent, &cache_file_operations);
-               cd->channel_ent = p;
-               if (p == NULL)
-                       goto out_nomem;
-               p->owner = cd->owner;
-               p->data = cd;
-       }
-       if (cd->cache_show) {
-               p = proc_create("content", S_IFREG|S_IRUSR|S_IWUSR,
-                               cd->proc_ent, &content_file_operations);
-               cd->content_ent = p;
-               if (p == NULL)
-                       goto out_nomem;
-               p->owner = cd->owner;
-               p->data = cd;
-       }
-       return 0;
-out_nomem:
-       remove_cache_proc_entries(cd);
-       return -ENOMEM;
-}
-#else /* CONFIG_PROC_FS */
-static int create_cache_proc_entries(struct cache_detail *cd)
-{
-       return 0;
-}
-#endif
-
-int cache_register(struct cache_detail *cd)
+static void sunrpc_init_cache_detail(struct cache_detail *cd)
 {
-       int ret;
-
-       ret = create_cache_proc_entries(cd);
-       if (ret)
-               return ret;
        rwlock_init(&cd->hash_lock);
        INIT_LIST_HEAD(&cd->queue);
        spin_lock(&cache_list_lock);
@@ -374,11 +310,9 @@ int cache_register(struct cache_detail *cd)
 
        /* start the cleaning process */
        schedule_delayed_work(&cache_cleaner, 0);
-       return 0;
 }
-EXPORT_SYMBOL(cache_register);
 
-void cache_unregister(struct cache_detail *cd)
+static void sunrpc_destroy_cache_detail(struct cache_detail *cd)
 {
        cache_purge(cd);
        spin_lock(&cache_list_lock);
@@ -393,7 +327,6 @@ void cache_unregister(struct cache_detail *cd)
        list_del_init(&cd->others);
        write_unlock(&cd->hash_lock);
        spin_unlock(&cache_list_lock);
-       remove_cache_proc_entries(cd);
        if (list_empty(&cache_list)) {
                /* module must be being unloaded so its safe to kill the worker */
                cancel_delayed_work_sync(&cache_cleaner);
@@ -402,7 +335,6 @@ void cache_unregister(struct cache_detail *cd)
 out:
        printk(KERN_ERR "nfsd: failed to unregister %s cache\n", cd->name);
 }
-EXPORT_SYMBOL(cache_unregister);
 
 /* clean cache tries to find something to clean
  * and cleans it.
@@ -495,7 +427,7 @@ static void do_cache_clean(struct work_struct *work)
 {
        int delay = 5;
        if (cache_clean() == -1)
-               delay = 30*HZ;
+               delay = round_jiffies_relative(30*HZ);
 
        if (list_empty(&cache_list))
                delay = 0;
@@ -517,7 +449,7 @@ void cache_flush(void)
        while (cache_clean() != -1)
                cond_resched();
 }
-EXPORT_SYMBOL(cache_flush);
+EXPORT_SYMBOL_GPL(cache_flush);
 
 void cache_purge(struct cache_detail *detail)
 {
@@ -526,7 +458,7 @@ void cache_purge(struct cache_detail *detail)
        cache_flush();
        detail->flush_time = 1;
 }
-EXPORT_SYMBOL(cache_purge);
+EXPORT_SYMBOL_GPL(cache_purge);
 
 
 /*
@@ -571,7 +503,6 @@ static int cache_defer_req(struct cache_req *req, struct cache_head *item)
                return -ETIMEDOUT;
 
        dreq->item = item;
-       dreq->recv_time = get_seconds();
 
        spin_lock(&cache_defer_lock);
 
@@ -695,18 +626,18 @@ struct cache_reader {
        int                     offset; /* if non-0, we have a refcnt on next request */
 };
 
-static ssize_t
-cache_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
+static ssize_t cache_read(struct file *filp, char __user *buf, size_t count,
+                         loff_t *ppos, struct cache_detail *cd)
 {
        struct cache_reader *rp = filp->private_data;
        struct cache_request *rq;
-       struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
+       struct inode *inode = filp->f_path.dentry->d_inode;
        int err;
 
        if (count == 0)
                return 0;
 
-       mutex_lock(&queue_io_mutex); /* protect against multiple concurrent
+       mutex_lock(&inode->i_mutex); /* protect against multiple concurrent
                              * readers on this file */
  again:
        spin_lock(&queue_lock);
@@ -719,7 +650,7 @@ cache_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
        }
        if (rp->q.list.next == &cd->queue) {
                spin_unlock(&queue_lock);
-               mutex_unlock(&queue_io_mutex);
+               mutex_unlock(&inode->i_mutex);
                BUG_ON(rp->offset);
                return 0;
        }
@@ -766,49 +697,90 @@ cache_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
        }
        if (err == -EAGAIN)
                goto again;
-       mutex_unlock(&queue_io_mutex);
+       mutex_unlock(&inode->i_mutex);
        return err ? err :  count;
 }
 
-static char write_buf[8192]; /* protected by queue_io_mutex */
+static ssize_t cache_do_downcall(char *kaddr, const char __user *buf,
+                                size_t count, struct cache_detail *cd)
+{
+       ssize_t ret;
+
+       if (copy_from_user(kaddr, buf, count))
+               return -EFAULT;
+       kaddr[count] = '\0';
+       ret = cd->cache_parse(cd, kaddr, count);
+       if (!ret)
+               ret = count;
+       return ret;
+}
 
-static ssize_t
-cache_write(struct file *filp, const char __user *buf, size_t count,
-           loff_t *ppos)
+static ssize_t cache_slow_downcall(const char __user *buf,
+                                  size_t count, struct cache_detail *cd)
 {
-       int err;
-       struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
+       static char write_buf[8192]; /* protected by queue_io_mutex */
+       ssize_t ret = -EINVAL;
 
-       if (count == 0)
-               return 0;
        if (count >= sizeof(write_buf))
-               return -EINVAL;
-
+               goto out;
        mutex_lock(&queue_io_mutex);
+       ret = cache_do_downcall(write_buf, buf, count, cd);
+       mutex_unlock(&queue_io_mutex);
+out:
+       return ret;
+}
 
-       if (copy_from_user(write_buf, buf, count)) {
-               mutex_unlock(&queue_io_mutex);
-               return -EFAULT;
-       }
-       write_buf[count] = '\0';
-       if (cd->cache_parse)
-               err = cd->cache_parse(cd, write_buf, count);
-       else
-               err = -EINVAL;
+static ssize_t cache_downcall(struct address_space *mapping,
+                             const char __user *buf,
+                             size_t count, struct cache_detail *cd)
+{
+       struct page *page;
+       char *kaddr;
+       ssize_t ret = -ENOMEM;
+
+       if (count >= PAGE_CACHE_SIZE)
+               goto out_slow;
+
+       page = find_or_create_page(mapping, 0, GFP_KERNEL);
+       if (!page)
+               goto out_slow;
+
+       kaddr = kmap(page);
+       ret = cache_do_downcall(kaddr, buf, count, cd);
+       kunmap(page);
+       unlock_page(page);
+       page_cache_release(page);
+       return ret;
+out_slow:
+       return cache_slow_downcall(buf, count, cd);
+}
 
-       mutex_unlock(&queue_io_mutex);
-       return err ? err : count;
+static ssize_t cache_write(struct file *filp, const char __user *buf,
+                          size_t count, loff_t *ppos,
+                          struct cache_detail *cd)
+{
+       struct address_space *mapping = filp->f_mapping;
+       struct inode *inode = filp->f_path.dentry->d_inode;
+       ssize_t ret = -EINVAL;
+
+       if (!cd->cache_parse)
+               goto out;
+
+       mutex_lock(&inode->i_mutex);
+       ret = cache_downcall(mapping, buf, count, cd);
+       mutex_unlock(&inode->i_mutex);
+out:
+       return ret;
 }
 
 static DECLARE_WAIT_QUEUE_HEAD(queue_wait);
 
-static unsigned int
-cache_poll(struct file *filp, poll_table *wait)
+static unsigned int cache_poll(struct file *filp, poll_table *wait,
+                              struct cache_detail *cd)
 {
        unsigned int mask;
        struct cache_reader *rp = filp->private_data;
        struct cache_queue *cq;
-       struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
 
        poll_wait(filp, &queue_wait, wait);
 
@@ -830,14 +802,13 @@ cache_poll(struct file *filp, poll_table *wait)
        return mask;
 }
 
-static int
-cache_ioctl(struct inode *ino, struct file *filp,
-           unsigned int cmd, unsigned long arg)
+static int cache_ioctl(struct inode *ino, struct file *filp,
+                      unsigned int cmd, unsigned long arg,
+                      struct cache_detail *cd)
 {
        int len = 0;
        struct cache_reader *rp = filp->private_data;
        struct cache_queue *cq;
-       struct cache_detail *cd = PDE(ino)->data;
 
        if (cmd != FIONREAD || !rp)
                return -EINVAL;
@@ -860,15 +831,15 @@ cache_ioctl(struct inode *ino, struct file *filp,
        return put_user(len, (int __user *)arg);
 }
 
-static int
-cache_open(struct inode *inode, struct file *filp)
+static int cache_open(struct inode *inode, struct file *filp,
+                     struct cache_detail *cd)
 {
        struct cache_reader *rp = NULL;
 
+       if (!cd || !try_module_get(cd->owner))
+               return -EACCES;
        nonseekable_open(inode, filp);
        if (filp->f_mode & FMODE_READ) {
-               struct cache_detail *cd = PDE(inode)->data;
-
                rp = kmalloc(sizeof(*rp), GFP_KERNEL);
                if (!rp)
                        return -ENOMEM;
@@ -883,11 +854,10 @@ cache_open(struct inode *inode, struct file *filp)
        return 0;
 }
 
-static int
-cache_release(struct inode *inode, struct file *filp)
+static int cache_release(struct inode *inode, struct file *filp,
+                        struct cache_detail *cd)
 {
        struct cache_reader *rp = filp->private_data;
-       struct cache_detail *cd = PDE(inode)->data;
 
        if (rp) {
                spin_lock(&queue_lock);
@@ -911,23 +881,12 @@ cache_release(struct inode *inode, struct file *filp)
                cd->last_close = get_seconds();
                atomic_dec(&cd->readers);
        }
+       module_put(cd->owner);
        return 0;
 }
 
 
 
-static const struct file_operations cache_file_operations = {
-       .owner          = THIS_MODULE,
-       .llseek         = no_llseek,
-       .read           = cache_read,
-       .write          = cache_write,
-       .poll           = cache_poll,
-       .ioctl          = cache_ioctl, /* for FIONREAD */
-       .open           = cache_open,
-       .release        = cache_release,
-};
-
-
 static void queue_loose(struct cache_detail *detail, struct cache_head *ch)
 {
        struct cache_queue *cq;
@@ -992,7 +951,7 @@ void qword_add(char **bpp, int *lp, char *str)
        *bpp = bp;
        *lp = len;
 }
-EXPORT_SYMBOL(qword_add);
+EXPORT_SYMBOL_GPL(qword_add);
 
 void qword_addhex(char **bpp, int *lp, char *buf, int blen)
 {
@@ -1021,22 +980,28 @@ void qword_addhex(char **bpp, int *lp, char *buf, int blen)
        *bpp = bp;
        *lp = len;
 }
-EXPORT_SYMBOL(qword_addhex);
+EXPORT_SYMBOL_GPL(qword_addhex);
 
 static void warn_no_listener(struct cache_detail *detail)
 {
        if (detail->last_warn != detail->last_close) {
                detail->last_warn = detail->last_close;
                if (detail->warn_no_listener)
-                       detail->warn_no_listener(detail);
+                       detail->warn_no_listener(detail, detail->last_close != 0);
        }
 }
 
 /*
- * register an upcall request to user-space.
+ * register an upcall request to user-space and queue it up for read() by the
+ * upcall daemon.
+ *
  * Each request is at most one page long.
  */
-static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h)
+int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h,
+               void (*cache_request)(struct cache_detail *,
+                                     struct cache_head *,
+                                     char **,
+                                     int *))
 {
 
        char *buf;
@@ -1044,9 +1009,6 @@ static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h)
        char *bp;
        int len;
 
-       if (detail->cache_request == NULL)
-               return -EINVAL;
-
        if (atomic_read(&detail->readers) == 0 &&
            detail->last_close < get_seconds() - 30) {
                        warn_no_listener(detail);
@@ -1065,7 +1027,7 @@ static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h)
 
        bp = buf; len = PAGE_SIZE;
 
-       detail->cache_request(detail, h, &bp, &len);
+       cache_request(detail, h, &bp, &len);
 
        if (len < 0) {
                kfree(buf);
@@ -1083,6 +1045,7 @@ static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h)
        wake_up(&queue_wait);
        return 0;
 }
+EXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall);
 
 /*
  * parse a message from user-space and pass it
@@ -1144,7 +1107,7 @@ int qword_get(char **bpp, char *dest, int bufsize)
        *dest = '\0';
        return len;
 }
-EXPORT_SYMBOL(qword_get);
+EXPORT_SYMBOL_GPL(qword_get);
 
 
 /*
@@ -1250,11 +1213,13 @@ static const struct seq_operations cache_content_op = {
        .show   = c_show,
 };
 
-static int content_open(struct inode *inode, struct file *file)
+static int content_open(struct inode *inode, struct file *file,
+                       struct cache_detail *cd)
 {
        struct handle *han;
-       struct cache_detail *cd = PDE(inode)->data;
 
+       if (!cd || !try_module_get(cd->owner))
+               return -EACCES;
        han = __seq_open_private(file, &cache_content_op, sizeof(*han));
        if (han == NULL)
                return -ENOMEM;
@@ -1263,17 +1228,33 @@ static int content_open(struct inode *inode, struct file *file)
        return 0;
 }
 
-static const struct file_operations content_file_operations = {
-       .open           = content_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = seq_release_private,
-};
+static int content_release(struct inode *inode, struct file *file,
+               struct cache_detail *cd)
+{
+       int ret = seq_release_private(inode, file);
+       module_put(cd->owner);
+       return ret;
+}
+
+static int open_flush(struct inode *inode, struct file *file,
+                       struct cache_detail *cd)
+{
+       if (!cd || !try_module_get(cd->owner))
+               return -EACCES;
+       return nonseekable_open(inode, file);
+}
+
+static int release_flush(struct inode *inode, struct file *file,
+                       struct cache_detail *cd)
+{
+       module_put(cd->owner);
+       return 0;
+}
 
 static ssize_t read_flush(struct file *file, char __user *buf,
-                           size_t count, loff_t *ppos)
+                         size_t count, loff_t *ppos,
+                         struct cache_detail *cd)
 {
-       struct cache_detail *cd = PDE(file->f_path.dentry->d_inode)->data;
        char tbuf[20];
        unsigned long p = *ppos;
        size_t len;
@@ -1291,10 +1272,10 @@ static ssize_t read_flush(struct file *file, char __user *buf,
        return len;
 }
 
-static ssize_t write_flush(struct file * file, const char __user * buf,
-                            size_t count, loff_t *ppos)
+static ssize_t write_flush(struct file *file, const char __user *buf,
+                          size_t count, loff_t *ppos,
+                          struct cache_detail *cd)
 {
-       struct cache_detail *cd = PDE(file->f_path.dentry->d_inode)->data;
        char tbuf[20];
        char *ep;
        long flushtime;
@@ -1315,8 +1296,343 @@ static ssize_t write_flush(struct file * file, const char __user * buf,
        return count;
 }
 
-static const struct file_operations cache_flush_operations = {
-       .open           = nonseekable_open,
-       .read           = read_flush,
-       .write          = write_flush,
+static ssize_t cache_read_procfs(struct file *filp, char __user *buf,
+                                size_t count, loff_t *ppos)
+{
+       struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
+
+       return cache_read(filp, buf, count, ppos, cd);
+}
+
+static ssize_t cache_write_procfs(struct file *filp, const char __user *buf,
+                                 size_t count, loff_t *ppos)
+{
+       struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
+
+       return cache_write(filp, buf, count, ppos, cd);
+}
+
+static unsigned int cache_poll_procfs(struct file *filp, poll_table *wait)
+{
+       struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
+
+       return cache_poll(filp, wait, cd);
+}
+
+static int cache_ioctl_procfs(struct inode *inode, struct file *filp,
+                             unsigned int cmd, unsigned long arg)
+{
+       struct cache_detail *cd = PDE(inode)->data;
+
+       return cache_ioctl(inode, filp, cmd, arg, cd);
+}
+
+static int cache_open_procfs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = PDE(inode)->data;
+
+       return cache_open(inode, filp, cd);
+}
+
+static int cache_release_procfs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = PDE(inode)->data;
+
+       return cache_release(inode, filp, cd);
+}
+
+static const struct file_operations cache_file_operations_procfs = {
+       .owner          = THIS_MODULE,
+       .llseek         = no_llseek,
+       .read           = cache_read_procfs,
+       .write          = cache_write_procfs,
+       .poll           = cache_poll_procfs,
+       .ioctl          = cache_ioctl_procfs, /* for FIONREAD */
+       .open           = cache_open_procfs,
+       .release        = cache_release_procfs,
 };
+
+static int content_open_procfs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = PDE(inode)->data;
+
+       return content_open(inode, filp, cd);
+}
+
+static int content_release_procfs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = PDE(inode)->data;
+
+       return content_release(inode, filp, cd);
+}
+
+static const struct file_operations content_file_operations_procfs = {
+       .open           = content_open_procfs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = content_release_procfs,
+};
+
+static int open_flush_procfs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = PDE(inode)->data;
+
+       return open_flush(inode, filp, cd);
+}
+
+static int release_flush_procfs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = PDE(inode)->data;
+
+       return release_flush(inode, filp, cd);
+}
+
+static ssize_t read_flush_procfs(struct file *filp, char __user *buf,
+                           size_t count, loff_t *ppos)
+{
+       struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
+
+       return read_flush(filp, buf, count, ppos, cd);
+}
+
+static ssize_t write_flush_procfs(struct file *filp,
+                                 const char __user *buf,
+                                 size_t count, loff_t *ppos)
+{
+       struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
+
+       return write_flush(filp, buf, count, ppos, cd);
+}
+
+static const struct file_operations cache_flush_operations_procfs = {
+       .open           = open_flush_procfs,
+       .read           = read_flush_procfs,
+       .write          = write_flush_procfs,
+       .release        = release_flush_procfs,
+};
+
+static void remove_cache_proc_entries(struct cache_detail *cd)
+{
+       if (cd->u.procfs.proc_ent == NULL)
+               return;
+       if (cd->u.procfs.flush_ent)
+               remove_proc_entry("flush", cd->u.procfs.proc_ent);
+       if (cd->u.procfs.channel_ent)
+               remove_proc_entry("channel", cd->u.procfs.proc_ent);
+       if (cd->u.procfs.content_ent)
+               remove_proc_entry("content", cd->u.procfs.proc_ent);
+       cd->u.procfs.proc_ent = NULL;
+       remove_proc_entry(cd->name, proc_net_rpc);
+}
+
+#ifdef CONFIG_PROC_FS
+static int create_cache_proc_entries(struct cache_detail *cd)
+{
+       struct proc_dir_entry *p;
+
+       cd->u.procfs.proc_ent = proc_mkdir(cd->name, proc_net_rpc);
+       if (cd->u.procfs.proc_ent == NULL)
+               goto out_nomem;
+       cd->u.procfs.channel_ent = NULL;
+       cd->u.procfs.content_ent = NULL;
+
+       p = proc_create_data("flush", S_IFREG|S_IRUSR|S_IWUSR,
+                            cd->u.procfs.proc_ent,
+                            &cache_flush_operations_procfs, cd);
+       cd->u.procfs.flush_ent = p;
+       if (p == NULL)
+               goto out_nomem;
+
+       if (cd->cache_upcall || cd->cache_parse) {
+               p = proc_create_data("channel", S_IFREG|S_IRUSR|S_IWUSR,
+                                    cd->u.procfs.proc_ent,
+                                    &cache_file_operations_procfs, cd);
+               cd->u.procfs.channel_ent = p;
+               if (p == NULL)
+                       goto out_nomem;
+       }
+       if (cd->cache_show) {
+               p = proc_create_data("content", S_IFREG|S_IRUSR|S_IWUSR,
+                               cd->u.procfs.proc_ent,
+                               &content_file_operations_procfs, cd);
+               cd->u.procfs.content_ent = p;
+               if (p == NULL)
+                       goto out_nomem;
+       }
+       return 0;
+out_nomem:
+       remove_cache_proc_entries(cd);
+       return -ENOMEM;
+}
+#else /* CONFIG_PROC_FS */
+static int create_cache_proc_entries(struct cache_detail *cd)
+{
+       return 0;
+}
+#endif
+
+int cache_register(struct cache_detail *cd)
+{
+       int ret;
+
+       sunrpc_init_cache_detail(cd);
+       ret = create_cache_proc_entries(cd);
+       if (ret)
+               sunrpc_destroy_cache_detail(cd);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(cache_register);
+
+void cache_unregister(struct cache_detail *cd)
+{
+       remove_cache_proc_entries(cd);
+       sunrpc_destroy_cache_detail(cd);
+}
+EXPORT_SYMBOL_GPL(cache_unregister);
+
+static ssize_t cache_read_pipefs(struct file *filp, char __user *buf,
+                                size_t count, loff_t *ppos)
+{
+       struct cache_detail *cd = RPC_I(filp->f_path.dentry->d_inode)->private;
+
+       return cache_read(filp, buf, count, ppos, cd);
+}
+
+static ssize_t cache_write_pipefs(struct file *filp, const char __user *buf,
+                                 size_t count, loff_t *ppos)
+{
+       struct cache_detail *cd = RPC_I(filp->f_path.dentry->d_inode)->private;
+
+       return cache_write(filp, buf, count, ppos, cd);
+}
+
+static unsigned int cache_poll_pipefs(struct file *filp, poll_table *wait)
+{
+       struct cache_detail *cd = RPC_I(filp->f_path.dentry->d_inode)->private;
+
+       return cache_poll(filp, wait, cd);
+}
+
+static int cache_ioctl_pipefs(struct inode *inode, struct file *filp,
+                             unsigned int cmd, unsigned long arg)
+{
+       struct cache_detail *cd = RPC_I(inode)->private;
+
+       return cache_ioctl(inode, filp, cmd, arg, cd);
+}
+
+static int cache_open_pipefs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = RPC_I(inode)->private;
+
+       return cache_open(inode, filp, cd);
+}
+
+static int cache_release_pipefs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = RPC_I(inode)->private;
+
+       return cache_release(inode, filp, cd);
+}
+
+const struct file_operations cache_file_operations_pipefs = {
+       .owner          = THIS_MODULE,
+       .llseek         = no_llseek,
+       .read           = cache_read_pipefs,
+       .write          = cache_write_pipefs,
+       .poll           = cache_poll_pipefs,
+       .ioctl          = cache_ioctl_pipefs, /* for FIONREAD */
+       .open           = cache_open_pipefs,
+       .release        = cache_release_pipefs,
+};
+
+static int content_open_pipefs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = RPC_I(inode)->private;
+
+       return content_open(inode, filp, cd);
+}
+
+static int content_release_pipefs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = RPC_I(inode)->private;
+
+       return content_release(inode, filp, cd);
+}
+
+const struct file_operations content_file_operations_pipefs = {
+       .open           = content_open_pipefs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = content_release_pipefs,
+};
+
+static int open_flush_pipefs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = RPC_I(inode)->private;
+
+       return open_flush(inode, filp, cd);
+}
+
+static int release_flush_pipefs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = RPC_I(inode)->private;
+
+       return release_flush(inode, filp, cd);
+}
+
+static ssize_t read_flush_pipefs(struct file *filp, char __user *buf,
+                           size_t count, loff_t *ppos)
+{
+       struct cache_detail *cd = RPC_I(filp->f_path.dentry->d_inode)->private;
+
+       return read_flush(filp, buf, count, ppos, cd);
+}
+
+static ssize_t write_flush_pipefs(struct file *filp,
+                                 const char __user *buf,
+                                 size_t count, loff_t *ppos)
+{
+       struct cache_detail *cd = RPC_I(filp->f_path.dentry->d_inode)->private;
+
+       return write_flush(filp, buf, count, ppos, cd);
+}
+
+const struct file_operations cache_flush_operations_pipefs = {
+       .open           = open_flush_pipefs,
+       .read           = read_flush_pipefs,
+       .write          = write_flush_pipefs,
+       .release        = release_flush_pipefs,
+};
+
+int sunrpc_cache_register_pipefs(struct dentry *parent,
+                                const char *name, mode_t umode,
+                                struct cache_detail *cd)
+{
+       struct qstr q;
+       struct dentry *dir;
+       int ret = 0;
+
+       sunrpc_init_cache_detail(cd);
+       q.name = name;
+       q.len = strlen(name);
+       q.hash = full_name_hash(q.name, q.len);
+       dir = rpc_create_cache_dir(parent, &q, umode, cd);
+       if (!IS_ERR(dir))
+               cd->u.pipefs.dir = dir;
+       else {
+               sunrpc_destroy_cache_detail(cd);
+               ret = PTR_ERR(dir);
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sunrpc_cache_register_pipefs);
+
+void sunrpc_cache_unregister_pipefs(struct cache_detail *cd)
+{
+       rpc_remove_cache_dir(cd->u.pipefs.dir);
+       cd->u.pipefs.dir = NULL;
+       sunrpc_destroy_cache_detail(cd);
+}
+EXPORT_SYMBOL_GPL(sunrpc_cache_unregister_pipefs);
+