Merge tag 'nfs-for-6.2-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 7 Jan 2023 18:38:11 +0000 (10:38 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 7 Jan 2023 18:38:11 +0000 (10:38 -0800)
Pull NFS client fixes from Trond Myklebust:

 - Fix a race in the RPCSEC_GSS upcall code that causes hung RPC calls

 - Fix a broken coalescing test in the pNFS file layout driver

 - Ensure that the access cache rcu path also applies the login test

 - Fix up for a sparse warning

* tag 'nfs-for-6.2-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFS: Fix up a sparse warning
  NFS: Judge the file access cache's timestamp in rcu path
  pNFS/filelayout: Fix coalescing test for single DS
  SUNRPC: ensure the matching upcall is in-flight upon downcall

fs/nfs/dir.c
fs/nfs/filelayout/filelayout.c
include/linux/sunrpc/rpc_pipe_fs.h
net/sunrpc/auth_gss/auth_gss.c

index ea1ceffa1d3aa78ee4ccaeb385df5d6c910c6921..f7e4a88d5d92928a60858308cac312f87632d0bd 100644 (file)
@@ -2957,12 +2957,14 @@ static u64 nfs_access_login_time(const struct task_struct *task,
                                 const struct cred *cred)
 {
        const struct task_struct *parent;
+       const struct cred *pcred;
        u64 ret;
 
        rcu_read_lock();
        for (;;) {
                parent = rcu_dereference(task->real_parent);
-               if (parent == task || cred_fscmp(parent->cred, cred) != 0)
+               pcred = rcu_dereference(parent->cred);
+               if (parent == task || cred_fscmp(pcred, cred) != 0)
                        break;
                task = parent;
        }
@@ -3023,6 +3025,7 @@ static int nfs_access_get_cached_rcu(struct inode *inode, const struct cred *cre
         * but do it without locking.
         */
        struct nfs_inode *nfsi = NFS_I(inode);
+       u64 login_time = nfs_access_login_time(current, cred);
        struct nfs_access_entry *cache;
        int err = -ECHILD;
        struct list_head *lh;
@@ -3037,6 +3040,8 @@ static int nfs_access_get_cached_rcu(struct inode *inode, const struct cred *cre
                cache = NULL;
        if (cache == NULL)
                goto out;
+       if ((s64)(login_time - cache->timestamp) > 0)
+               goto out;
        if (nfs_check_cache_invalid(inode, NFS_INO_INVALID_ACCESS))
                goto out;
        *mask = cache->mask;
index ad34a33b0737c7611f791290b69edf9fe722efe3..4974cd18ca468ed8f7301556907c0075e0738fa2 100644 (file)
@@ -783,6 +783,12 @@ filelayout_alloc_lseg(struct pnfs_layout_hdr *layoutid,
        return &fl->generic_hdr;
 }
 
+static bool
+filelayout_lseg_is_striped(const struct nfs4_filelayout_segment *flseg)
+{
+       return flseg->num_fh > 1;
+}
+
 /*
  * filelayout_pg_test(). Called by nfs_can_coalesce_requests()
  *
@@ -803,6 +809,8 @@ filelayout_pg_test(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev,
        size = pnfs_generic_pg_test(pgio, prev, req);
        if (!size)
                return 0;
+       else if (!filelayout_lseg_is_striped(FILELAYOUT_LSEG(pgio->pg_lseg)))
+               return size;
 
        /* see if req and prev are in the same stripe */
        if (prev) {
index cd188a527d169025398caad1d21aa86db5f1b7c7..3b35b6f6533aab0553b87757ff5a73ccffa44610 100644 (file)
@@ -92,6 +92,11 @@ extern ssize_t rpc_pipe_generic_upcall(struct file *, struct rpc_pipe_msg *,
                                       char __user *, size_t);
 extern int rpc_queue_upcall(struct rpc_pipe *, struct rpc_pipe_msg *);
 
+/* returns true if the msg is in-flight, i.e., already eaten by the peer */
+static inline bool rpc_msg_is_inflight(const struct rpc_pipe_msg *msg) {
+       return (msg->copied != 0 && list_empty(&msg->list));
+}
+
 struct rpc_clnt;
 extern struct dentry *rpc_create_client_dir(struct dentry *, const char *, struct rpc_clnt *);
 extern int rpc_remove_client_dir(struct rpc_clnt *);
index 7bb247c51e2f66e7b8968c4cf054ed238aacf2b8..2d7b1e03110ae899aa156499eebac0711e61ae84 100644 (file)
@@ -302,7 +302,7 @@ __gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid, const struct gss_auth *auth
        list_for_each_entry(pos, &pipe->in_downcall, list) {
                if (!uid_eq(pos->uid, uid))
                        continue;
-               if (auth && pos->auth->service != auth->service)
+               if (pos->auth->service != auth->service)
                        continue;
                refcount_inc(&pos->count);
                return pos;
@@ -686,6 +686,21 @@ out:
        return err;
 }
 
+static struct gss_upcall_msg *
+gss_find_downcall(struct rpc_pipe *pipe, kuid_t uid)
+{
+       struct gss_upcall_msg *pos;
+       list_for_each_entry(pos, &pipe->in_downcall, list) {
+               if (!uid_eq(pos->uid, uid))
+                       continue;
+               if (!rpc_msg_is_inflight(&pos->msg))
+                       continue;
+               refcount_inc(&pos->count);
+               return pos;
+       }
+       return NULL;
+}
+
 #define MSG_BUF_MAXSIZE 1024
 
 static ssize_t
@@ -732,7 +747,7 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
        err = -ENOENT;
        /* Find a matching upcall */
        spin_lock(&pipe->lock);
-       gss_msg = __gss_find_upcall(pipe, uid, NULL);
+       gss_msg = gss_find_downcall(pipe, uid);
        if (gss_msg == NULL) {
                spin_unlock(&pipe->lock);
                goto err_put_ctx;