Merge branch 'nfs-for-2.6.32' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6...
authorJ. Bruce Fields <bfields@citi.umich.edu>
Fri, 21 Aug 2009 15:27:29 +0000 (11:27 -0400)
committerJ. Bruce Fields <bfields@citi.umich.edu>
Fri, 21 Aug 2009 15:27:29 +0000 (11:27 -0400)
Conflicts:
net/sunrpc/cache.c

1  2 
fs/nfsd/nfsctl.c
fs/nfsd/nfssvc.c
net/sunrpc/cache.c
net/sunrpc/svcauth_unix.c

diff --combined fs/nfsd/nfsctl.c
@@@ -25,7 -25,6 +25,6 @@@
  #include <linux/init.h>
  #include <linux/inet.h>
  #include <linux/string.h>
- #include <linux/smp_lock.h>
  #include <linux/ctype.h>
  
  #include <linux/nfs.h>
@@@ -38,6 -37,7 +37,7 @@@
  #include <linux/nfsd/xdr.h>
  #include <linux/nfsd/syscall.h>
  #include <linux/lockd/lockd.h>
+ #include <linux/sunrpc/clnt.h>
  
  #include <asm/uaccess.h>
  #include <net/ipv6.h>
@@@ -491,22 -491,18 +491,18 @@@ static ssize_t write_getfd(struct file 
   *
   * Input:
   *                    buf:    '\n'-terminated C string containing a
-  *                            presentation format IPv4 address
+  *                            presentation format IP address
   *                    size:   length of C string in @buf
   * Output:
   *    On success:     returns zero if all specified locks were released;
   *                    returns one if one or more locks were not released
   *    On error:       return code is negative errno value
   */
  static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
  {
-       struct sockaddr_in sin = {
-               .sin_family     = AF_INET,
-       };
-       int b1, b2, b3, b4;
-       char c;
+       struct sockaddr_storage address;
+       struct sockaddr *sap = (struct sockaddr *)&address;
+       size_t salen = sizeof(address);
        char *fo_path;
  
        /* sanity check */
        if (qword_get(&buf, fo_path, size) < 0)
                return -EINVAL;
  
-       /* get ipv4 address */
-       if (sscanf(fo_path, "%u.%u.%u.%u%c", &b1, &b2, &b3, &b4, &c) != 4)
-               return -EINVAL;
-       if (b1 > 255 || b2 > 255 || b3 > 255 || b4 > 255)
+       if (rpc_pton(fo_path, size, sap, salen) == 0)
                return -EINVAL;
-       sin.sin_addr.s_addr = htonl((b1 << 24) | (b2 << 16) | (b3 << 8) | b4);
  
-       return nlmsvc_unlock_all_by_ip((struct sockaddr *)&sin);
+       return nlmsvc_unlock_all_by_ip(sap);
  }
  
  /**
@@@ -784,7 -776,10 +776,7 @@@ static ssize_t write_pool_threads(struc
                size -= len;
                mesg += len;
        }
 -
 -      mutex_unlock(&nfsd_mutex);
 -      return (mesg-buf);
 -
 +      rv = mesg - buf;
  out_free:
        kfree(nthreads);
        mutex_unlock(&nfsd_mutex);
diff --combined fs/nfsd/nfssvc.c
@@@ -18,7 -18,6 +18,6 @@@
  #include <linux/unistd.h>
  #include <linux/slab.h>
  #include <linux/smp.h>
- #include <linux/smp_lock.h>
  #include <linux/freezer.h>
  #include <linux/fs_struct.h>
  #include <linux/kthread.h>
@@@ -67,16 -66,6 +66,16 @@@ struct timeval                      nfssvc_boot
  DEFINE_MUTEX(nfsd_mutex);
  struct svc_serv               *nfsd_serv;
  
 +/*
 + * nfsd_drc_lock protects nfsd_drc_max_pages and nfsd_drc_pages_used.
 + * nfsd_drc_max_pages limits the total amount of memory available for
 + * version 4.1 DRC caches.
 + * nfsd_drc_pages_used tracks the current version 4.1 DRC memory usage.
 + */
 +spinlock_t    nfsd_drc_lock;
 +unsigned int  nfsd_drc_max_mem;
 +unsigned int  nfsd_drc_mem_used;
 +
  #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
  static struct svc_stat        nfsd_acl_svcstats;
  static struct svc_version *   nfsd_acl_version[] = {
@@@ -246,12 -235,13 +245,12 @@@ void nfsd_reset_versions(void
   */
  static void set_max_drc(void)
  {
 -      /* The percent of nr_free_buffer_pages used by the V4.1 server DRC */
 -      #define NFSD_DRC_SIZE_SHIFT     7
 -      nfsd_serv->sv_drc_max_pages = nr_free_buffer_pages()
 -                                              >> NFSD_DRC_SIZE_SHIFT;
 -      nfsd_serv->sv_drc_pages_used = 0;
 -      dprintk("%s svc_drc_max_pages %u\n", __func__,
 -              nfsd_serv->sv_drc_max_pages);
 +      #define NFSD_DRC_SIZE_SHIFT     10
 +      nfsd_drc_max_mem = (nr_free_buffer_pages()
 +                                      >> NFSD_DRC_SIZE_SHIFT) * PAGE_SIZE;
 +      nfsd_drc_mem_used = 0;
 +      spin_lock_init(&nfsd_drc_lock);
 +      dprintk("%s nfsd_drc_max_mem %u \n", __func__, nfsd_drc_max_mem);
  }
  
  int nfsd_create_serv(void)
diff --combined net/sunrpc/cache.c
  #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
  
@@@ -101,7 -103,7 +103,7 @@@ struct cache_head *sunrpc_cache_lookup(
  EXPORT_SYMBOL_GPL(sunrpc_cache_lookup);
  
  
 -static void queue_loose(struct cache_detail *detail, struct cache_head *ch);
 +static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch);
  
  static int cache_fresh_locked(struct cache_head *head, time_t expiry)
  {
@@@ -117,7 -119,7 +119,7 @@@ static void cache_fresh_unlocked(struc
                cache_revisit_request(head);
        if (test_and_clear_bit(CACHE_PENDING, &head->flags)) {
                cache_revisit_request(head);
 -              queue_loose(detail, head);
 +              cache_dequeue(detail, head);
        }
  }
  
@@@ -175,23 -177,13 +177,29 @@@ struct cache_head *sunrpc_cache_update(
  }
  EXPORT_SYMBOL_GPL(sunrpc_cache_update);
  
- static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h);
+ 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 inline int cache_is_valid(struct cache_detail *detail, struct cache_head *h)
 +{
 +      if (!test_bit(CACHE_VALID, &h->flags) ||
 +          h->expiry_time < get_seconds())
 +              return -EAGAIN;
 +      else if (detail->flush_time > h->last_refresh)
 +              return -EAGAIN;
 +      else {
 +              /* entry is valid */
 +              if (test_bit(CACHE_NEGATIVE, &h->flags))
 +                      return -ENOENT;
 +              else
 +                      return 0;
 +      }
 +}
++
  /*
   * This is the generic cache management routine for all
   * the authentication caches.
   *
   *
   * Returns 0 if the cache_head can be used, or cache_puts it and returns
 - * -EAGAIN if upcall is pending,
 - * -ETIMEDOUT if upcall failed and should be retried,
 + * -EAGAIN if upcall is pending and request has been queued
 + * -ETIMEDOUT if upcall failed or request could not be queue or
 + *           upcall completed but item is still invalid (implying that
 + *           the cache item has been replaced with a newer one).
   * -ENOENT if cache entry was negative
   */
  int cache_check(struct cache_detail *detail,
        long refresh_age, age;
  
        /* First decide return status as best we can */
 -      if (!test_bit(CACHE_VALID, &h->flags) ||
 -          h->expiry_time < get_seconds())
 -              rv = -EAGAIN;
 -      else if (detail->flush_time > h->last_refresh)
 -              rv = -EAGAIN;
 -      else {
 -              /* entry is valid */
 -              if (test_bit(CACHE_NEGATIVE, &h->flags))
 -                      rv = -ENOENT;
 -              else rv = 0;
 -      }
 +      rv = cache_is_valid(detail, h);
  
        /* now see if we want to start an upcall */
        refresh_age = (h->expiry_time - h->last_refresh);
                        switch (cache_make_upcall(detail, h)) {
                        case -EINVAL:
                                clear_bit(CACHE_PENDING, &h->flags);
 +                              cache_revisit_request(h);
                                if (rv == -EAGAIN) {
                                        set_bit(CACHE_NEGATIVE, &h->flags);
                                        cache_fresh_unlocked(h, detail,
                }
        }
  
 -      if (rv == -EAGAIN)
 -              if (cache_defer_req(rqstp, h) != 0)
 -                      rv = -ETIMEDOUT;
 -
 +      if (rv == -EAGAIN) {
 +              if (cache_defer_req(rqstp, h) == 0) {
 +                      /* Request is not deferred */
 +                      rv = cache_is_valid(detail, h);
 +                      if (rv == -EAGAIN)
 +                              rv = -ETIMEDOUT;
 +              }
 +      }
        if (rv)
                cache_put(h, detail);
        return rv;
@@@ -297,76 -292,11 +305,11 @@@ static DEFINE_SPINLOCK(cache_list_lock)
  static struct cache_detail *current_detail;
  static int current_index;
  
  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->channel_ent = cd->content_ent = NULL;
-       p = proc_create_data("flush", S_IFREG|S_IRUSR|S_IWUSR,
-                            cd->proc_ent, &cache_flush_operations, cd);
-       cd->flush_ent = p;
-       if (p == NULL)
-               goto out_nomem;
-       if (cd->cache_request || cd->cache_parse) {
-               p = proc_create_data("channel", S_IFREG|S_IRUSR|S_IWUSR,
-                                    cd->proc_ent, &cache_file_operations, cd);
-               cd->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->proc_ent, &content_file_operations, cd);
-               cd->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)
+ 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);
  
        /* start the cleaning process */
        schedule_delayed_work(&cache_cleaner, 0);
-       return 0;
  }
- EXPORT_SYMBOL_GPL(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);
        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);
  out:
        printk(KERN_ERR "nfsd: failed to unregister %s cache\n", cd->name);
  }
- EXPORT_SYMBOL_GPL(cache_unregister);
  
  /* clean cache tries to find something to clean
   * and cleans it.
@@@ -470,7 -396,7 +409,7 @@@ static int cache_clean(void
                                )
                                continue;
                        if (test_and_clear_bit(CACHE_PENDING, &ch->flags))
 -                              queue_loose(current_detail, ch);
 +                              cache_dequeue(current_detail, ch);
  
                        if (atomic_read(&ch->ref.refcount) == 1)
                                break;
                if (!ch)
                        current_index ++;
                spin_unlock(&cache_list_lock);
 -              if (ch)
 +              if (ch) {
 +                      cache_revisit_request(ch);
                        cache_put(ch, d);
 +              }
        } else
                spin_unlock(&cache_list_lock);
  
@@@ -572,11 -496,11 +511,11 @@@ static int cache_defer_req(struct cache
                 * or continue and drop the oldest below
                 */
                if (net_random()&1)
 -                      return -ETIMEDOUT;
 +                      return 0;
        }
        dreq = req->defer(req);
        if (dreq == NULL)
 -              return -ETIMEDOUT;
 +              return 0;
  
        dreq->item = item;
  
        if (!test_bit(CACHE_PENDING, &item->flags)) {
                /* must have just been validated... */
                cache_revisit_request(item);
 +              return 0;
        }
 -      return 0;
 +      return 1;
  }
  
  static void cache_revisit_request(struct cache_head *item)
@@@ -703,18 -626,18 +642,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);
        }
        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;
        }
        }
        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;
  
- static ssize_t
- cache_write(struct file *filp, const char __user *buf, size_t count,
-           loff_t *ppos)
+       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_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);
  
        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;
        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;
        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);
                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)
 +static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch)
  {
        struct cache_queue *cq;
        spin_lock(&queue_lock);
@@@ -1036,15 -987,21 +1003,21 @@@ static void warn_no_listener(struct cac
        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;
        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);
  
        bp = buf; len = PAGE_SIZE;
  
-       detail->cache_request(detail, h, &bp, &len);
+       cache_request(detail, h, &bp, &len);
  
        if (len < 0) {
                kfree(buf);
        wake_up(&queue_wait);
        return 0;
  }
+ EXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall);
  
  /*
   * parse a message from user-space and pass it
@@@ -1258,11 -1213,13 +1229,13 @@@ static const struct seq_operations cach
        .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;
        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;
        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;
        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);
@@@ -171,6 -171,11 +171,11 @@@ static void ip_map_request(struct cache
        (*bpp)[-1] = '\n';
  }
  
+ static int ip_map_upcall(struct cache_detail *cd, struct cache_head *h)
+ {
+       return sunrpc_cache_pipe_upcall(cd, h, ip_map_request);
+ }
  static struct ip_map *ip_map_lookup(char *class, struct in6_addr *addr);
  static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t expiry);
  
@@@ -289,7 -294,7 +294,7 @@@ struct cache_detail ip_map_cache = 
        .hash_table     = ip_table,
        .name           = "auth.unix.ip",
        .cache_put      = ip_map_put,
-       .cache_request  = ip_map_request,
+       .cache_upcall   = ip_map_upcall,
        .cache_parse    = ip_map_parse,
        .cache_show     = ip_map_show,
        .match          = ip_map_match,
@@@ -523,6 -528,11 +528,11 @@@ static void unix_gid_request(struct cac
        (*bpp)[-1] = '\n';
  }
  
+ static int unix_gid_upcall(struct cache_detail *cd, struct cache_head *h)
+ {
+       return sunrpc_cache_pipe_upcall(cd, h, unix_gid_request);
+ }
  static struct unix_gid *unix_gid_lookup(uid_t uid);
  extern struct cache_detail unix_gid_cache;
  
@@@ -622,7 -632,7 +632,7 @@@ struct cache_detail unix_gid_cache = 
        .hash_table     = gid_table,
        .name           = "auth.unix.gid",
        .cache_put      = unix_gid_put,
-       .cache_request  = unix_gid_request,
+       .cache_upcall   = unix_gid_upcall,
        .cache_parse    = unix_gid_parse,
        .cache_show     = unix_gid_show,
        .match          = unix_gid_match,
@@@ -658,7 -668,6 +668,7 @@@ static int unix_gid_find(uid_t uid, str
        case 0:
                *gip = ug->gi;
                get_group_info(*gip);
 +              cache_put(&ug->h, &unix_gid_cache);
                return 0;
        default:
                return -EAGAIN;