]> Git Repo - J-linux.git/commitdiff
Merge tag 'nfsd-6.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux
authorLinus Torvalds <[email protected]>
Sat, 18 Nov 2023 19:23:32 +0000 (11:23 -0800)
committerLinus Torvalds <[email protected]>
Sat, 18 Nov 2023 19:23:32 +0000 (11:23 -0800)
Pull nfsd fixes from Chuck Lever:

 - Fix several long-standing bugs in the duplicate reply cache

 - Fix a memory leak

* tag 'nfsd-6.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux:
  NFSD: Fix checksum mismatches in the duplicate reply cache
  NFSD: Fix "start of NFS reply" pointer passed to nfsd_cache_update()
  NFSD: Update nfsd_cache_append() to use xdr_stream
  nfsd: fix file memleak on client_opens_release

1  2 
fs/nfsd/nfs4state.c
fs/nfsd/nfscache.c

diff --combined fs/nfsd/nfs4state.c
index 4045c852a450e7ab172f26ecfcfc5e2805bfb6df,3709e58f0a4a6ea57eba3d6d2f58f8b59a09395e..40415929e2aef385fce9b658ba68b32425a6462f
@@@ -2804,7 -2804,7 +2804,7 @@@ static int client_opens_release(struct 
  
        /* XXX: alternatively, we could get/drop in seq start/stop */
        drop_client(clp);
-       return 0;
+       return seq_release(inode, file);
  }
  
  static const struct file_operations client_states_fops = {
@@@ -4452,7 -4452,8 +4452,7 @@@ static unsigned lon
  nfsd4_state_shrinker_count(struct shrinker *shrink, struct shrink_control *sc)
  {
        int count;
 -      struct nfsd_net *nn = container_of(shrink,
 -                      struct nfsd_net, nfsd_client_shrinker);
 +      struct nfsd_net *nn = shrink->private_data;
  
        count = atomic_read(&nn->nfsd_courtesy_clients);
        if (!count)
@@@ -8234,16 -8235,12 +8234,16 @@@ static int nfs4_state_create_net(struc
        INIT_WORK(&nn->nfsd_shrinker_work, nfsd4_state_shrinker_worker);
        get_net(net);
  
 -      nn->nfsd_client_shrinker.scan_objects = nfsd4_state_shrinker_scan;
 -      nn->nfsd_client_shrinker.count_objects = nfsd4_state_shrinker_count;
 -      nn->nfsd_client_shrinker.seeks = DEFAULT_SEEKS;
 -
 -      if (register_shrinker(&nn->nfsd_client_shrinker, "nfsd-client"))
 +      nn->nfsd_client_shrinker = shrinker_alloc(0, "nfsd-client");
 +      if (!nn->nfsd_client_shrinker)
                goto err_shrinker;
 +
 +      nn->nfsd_client_shrinker->scan_objects = nfsd4_state_shrinker_scan;
 +      nn->nfsd_client_shrinker->count_objects = nfsd4_state_shrinker_count;
 +      nn->nfsd_client_shrinker->private_data = nn;
 +
 +      shrinker_register(nn->nfsd_client_shrinker);
 +
        return 0;
  
  err_shrinker:
@@@ -8341,7 -8338,7 +8341,7 @@@ nfs4_state_shutdown_net(struct net *net
        struct list_head *pos, *next, reaplist;
        struct nfsd_net *nn = net_generic(net, nfsd_net_id);
  
 -      unregister_shrinker(&nn->nfsd_client_shrinker);
 +      shrinker_free(nn->nfsd_client_shrinker);
        cancel_work(&nn->nfsd_shrinker_work);
        cancel_delayed_work_sync(&nn->laundromat_work);
        locks_end_grace(&nn->nfsd4_manager);
diff --combined fs/nfsd/nfscache.c
index fd56a52aa5fb678859e5fd12eb8e00492e671372,6cd36af2f97e10610cdd1ba4821c5bb01de19bcb..d3273a3966598b83e46679102fa9b7e00d879184
@@@ -201,29 -201,26 +201,29 @@@ int nfsd_reply_cache_init(struct nfsd_n
  {
        unsigned int hashsize;
        unsigned int i;
 -      int status = 0;
  
        nn->max_drc_entries = nfsd_cache_size_limit();
        atomic_set(&nn->num_drc_entries, 0);
        hashsize = nfsd_hashsize(nn->max_drc_entries);
        nn->maskbits = ilog2(hashsize);
  
 -      nn->nfsd_reply_cache_shrinker.scan_objects = nfsd_reply_cache_scan;
 -      nn->nfsd_reply_cache_shrinker.count_objects = nfsd_reply_cache_count;
 -      nn->nfsd_reply_cache_shrinker.seeks = 1;
 -      status = register_shrinker(&nn->nfsd_reply_cache_shrinker,
 -                                 "nfsd-reply:%s", nn->nfsd_name);
 -      if (status)
 -              return status;
 -
        nn->drc_hashtbl = kvzalloc(array_size(hashsize,
                                sizeof(*nn->drc_hashtbl)), GFP_KERNEL);
        if (!nn->drc_hashtbl)
 +              return -ENOMEM;
 +
 +      nn->nfsd_reply_cache_shrinker = shrinker_alloc(0, "nfsd-reply:%s",
 +                                                     nn->nfsd_name);
 +      if (!nn->nfsd_reply_cache_shrinker)
                goto out_shrinker;
  
 +      nn->nfsd_reply_cache_shrinker->scan_objects = nfsd_reply_cache_scan;
 +      nn->nfsd_reply_cache_shrinker->count_objects = nfsd_reply_cache_count;
 +      nn->nfsd_reply_cache_shrinker->seeks = 1;
 +      nn->nfsd_reply_cache_shrinker->private_data = nn;
 +
 +      shrinker_register(nn->nfsd_reply_cache_shrinker);
 +
        for (i = 0; i < hashsize; i++) {
                INIT_LIST_HEAD(&nn->drc_hashtbl[i].lru_head);
                spin_lock_init(&nn->drc_hashtbl[i].cache_lock);
  
        return 0;
  out_shrinker:
 -      unregister_shrinker(&nn->nfsd_reply_cache_shrinker);
 +      kvfree(nn->drc_hashtbl);
        printk(KERN_ERR "nfsd: failed to allocate reply cache\n");
        return -ENOMEM;
  }
@@@ -242,7 -239,7 +242,7 @@@ void nfsd_reply_cache_shutdown(struct n
        struct nfsd_cacherep *rp;
        unsigned int i;
  
 -      unregister_shrinker(&nn->nfsd_reply_cache_shrinker);
 +      shrinker_free(nn->nfsd_reply_cache_shrinker);
  
        for (i = 0; i < nn->drc_hashsize; i++) {
                struct list_head *head = &nn->drc_hashtbl[i].lru_head;
@@@ -326,7 -323,8 +326,7 @@@ nfsd_prune_bucket_locked(struct nfsd_ne
  static unsigned long
  nfsd_reply_cache_count(struct shrinker *shrink, struct shrink_control *sc)
  {
 -      struct nfsd_net *nn = container_of(shrink,
 -                              struct nfsd_net, nfsd_reply_cache_shrinker);
 +      struct nfsd_net *nn = shrink->private_data;
  
        return atomic_read(&nn->num_drc_entries);
  }
  static unsigned long
  nfsd_reply_cache_scan(struct shrinker *shrink, struct shrink_control *sc)
  {
 -      struct nfsd_net *nn = container_of(shrink,
 -                              struct nfsd_net, nfsd_reply_cache_shrinker);
 +      struct nfsd_net *nn = shrink->private_data;
        unsigned long freed = 0;
        LIST_HEAD(dispose);
        unsigned int i;
        return freed;
  }
  
- /*
-  * Walk an xdr_buf and get a CRC for at most the first RC_CSUMLEN bytes
+ /**
+  * nfsd_cache_csum - Checksum incoming NFS Call arguments
+  * @buf: buffer containing a whole RPC Call message
+  * @start: starting byte of the NFS Call header
+  * @remaining: size of the NFS Call header, in bytes
+  *
+  * Compute a weak checksum of the leading bytes of an NFS procedure
+  * call header to help verify that a retransmitted Call matches an
+  * entry in the duplicate reply cache.
+  *
+  * To avoid assumptions about how the RPC message is laid out in
+  * @buf and what else it might contain (eg, a GSS MIC suffix), the
+  * caller passes us the exact location and length of the NFS Call
+  * header.
+  *
+  * Returns a 32-bit checksum value, as defined in RFC 793.
   */
- static __wsum
nfsd_cache_csum(struct svc_rqst *rqstp)
+ static __wsum nfsd_cache_csum(struct xdr_buf *buf, unsigned int start,
                            unsigned int remaining)
  {
+       unsigned int base, len;
+       struct xdr_buf subbuf;
+       __wsum csum = 0;
+       void *p;
        int idx;
-       unsigned int base;
-       __wsum csum;
-       struct xdr_buf *buf = &rqstp->rq_arg;
-       const unsigned char *p = buf->head[0].iov_base;
-       size_t csum_len = min_t(size_t, buf->head[0].iov_len + buf->page_len,
-                               RC_CSUMLEN);
-       size_t len = min(buf->head[0].iov_len, csum_len);
+       if (remaining > RC_CSUMLEN)
+               remaining = RC_CSUMLEN;
+       if (xdr_buf_subsegment(buf, &subbuf, start, remaining))
+               return csum;
  
        /* rq_arg.head first */
-       csum = csum_partial(p, len, 0);
-       csum_len -= len;
+       if (subbuf.head[0].iov_len) {
+               len = min_t(unsigned int, subbuf.head[0].iov_len, remaining);
+               csum = csum_partial(subbuf.head[0].iov_base, len, csum);
+               remaining -= len;
+       }
  
        /* Continue into page array */
-       idx = buf->page_base / PAGE_SIZE;
-       base = buf->page_base & ~PAGE_MASK;
-       while (csum_len) {
-               p = page_address(buf->pages[idx]) + base;
-               len = min_t(size_t, PAGE_SIZE - base, csum_len);
+       idx = subbuf.page_base / PAGE_SIZE;
+       base = subbuf.page_base & ~PAGE_MASK;
+       while (remaining) {
+               p = page_address(subbuf.pages[idx]) + base;
+               len = min_t(unsigned int, PAGE_SIZE - base, remaining);
                csum = csum_partial(p, len, csum);
-               csum_len -= len;
+               remaining -= len;
                base = 0;
                ++idx;
        }
@@@ -466,6 -484,8 +485,8 @@@ out
  /**
   * nfsd_cache_lookup - Find an entry in the duplicate reply cache
   * @rqstp: Incoming Call to find
+  * @start: starting byte in @rqstp->rq_arg of the NFS Call header
+  * @len: size of the NFS Call header, in bytes
   * @cacherep: OUT: DRC entry for this request
   *
   * Try to find an entry matching the current call in the cache. When none
   *   %RC_REPLY: Reply from cache
   *   %RC_DROPIT: Do not process the request further
   */
- int nfsd_cache_lookup(struct svc_rqst *rqstp, struct nfsd_cacherep **cacherep)
+ int nfsd_cache_lookup(struct svc_rqst *rqstp, unsigned int start,
+                     unsigned int len, struct nfsd_cacherep **cacherep)
  {
        struct nfsd_net         *nn;
        struct nfsd_cacherep    *rp, *found;
                goto out;
        }
  
-       csum = nfsd_cache_csum(rqstp);
+       csum = nfsd_cache_csum(&rqstp->rq_arg, start, len);
  
        /*
         * Since the common case is a cache miss followed by an insert,
@@@ -641,24 -662,17 +663,17 @@@ void nfsd_cache_update(struct svc_rqst 
        return;
  }
  
- /*
-  * Copy cached reply to current reply buffer. Should always fit.
-  * FIXME as reply is in a page, we should just attach the page, and
-  * keep a refcount....
-  */
  static int
  nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *data)
  {
-       struct kvec     *vec = &rqstp->rq_res.head[0];
-       if (vec->iov_len + data->iov_len > PAGE_SIZE) {
-               printk(KERN_WARNING "nfsd: cached reply too large (%zd).\n",
-                               data->iov_len);
-               return 0;
-       }
-       memcpy((char*)vec->iov_base + vec->iov_len, data->iov_base, data->iov_len);
-       vec->iov_len += data->iov_len;
-       return 1;
+       __be32 *p;
+       p = xdr_reserve_space(&rqstp->rq_res_stream, data->iov_len);
+       if (unlikely(!p))
+               return false;
+       memcpy(p, data->iov_base, data->iov_len);
+       xdr_commit_encode(&rqstp->rq_res_stream);
+       return true;
  }
  
  /*
This page took 0.084535 seconds and 4 git commands to generate.