Linux 4.15: check for 2nd argument to pagevec_init
[openafs.git] / src / afs / LINUX / osi_vnodeops.c
index 97b5486..5e2818b 100644 (file)
 #define MAX_ERRNO 1000L
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)
+/* Enable our workaround for a race with d_splice_alias. The race was fixed in
+ * 2.6.34, so don't do it after that point. */
+# define D_SPLICE_ALIAS_RACE
+#endif
+
 int cachefs_noreadpage = 0;
 
 extern struct backing_dev_info *afs_backing_dev_info;
@@ -819,7 +825,7 @@ struct file_operations afs_file_fops = {
 #ifdef STRUCT_FILE_OPERATIONS_HAS_READ_ITER
   .read_iter = afs_linux_read_iter,
   .write_iter =        afs_linux_write_iter,
-# if !defined(HAVE_LINUX___VFS_READ)
+# if !defined(HAVE_LINUX___VFS_WRITE) && !defined(HAVE_LINUX_KERNEL_WRITE)
   .read =      new_sync_read,
   .write =     new_sync_write,
 # endif
@@ -1130,6 +1136,44 @@ parent_vcache_dv(struct inode *inode, cred_t *credp)
     return hgetlo(pvcp->f.m.DataVersion);
 }
 
+#ifdef D_SPLICE_ALIAS_RACE
+/* Leave some trace that this code is enabled; otherwise it's pretty hard to
+ * tell. */
+static __attribute__((used)) const char dentry_race_marker[] = "d_splice_alias race workaround enabled";
+
+static int
+check_dentry_race(struct dentry *dp)
+{
+    int raced = 0;
+    if (!dp->d_inode) {
+        struct dentry *parent = dget_parent(dp);
+
+        /* In Linux, before commit 4919c5e45a91b5db5a41695fe0357fbdff0d5767,
+         * d_splice_alias can momentarily hash a dentry before it's fully
+         * populated. This only happens for a moment, since it's unhashed again
+         * right after (in d_move), but this can make the dentry be found by
+         * __d_lookup, and then given to us.
+         *
+         * So check if the dentry is unhashed; if it is, then the dentry is not
+         * valid. We lock the parent inode to ensure that d_splice_alias is no
+         * longer running (the inode mutex will be held during
+         * afs_linux_lookup). Locking d_lock is required to check the dentry's
+         * flags, so lock that, too.
+         */
+        afs_linux_lock_inode(parent->d_inode);
+        spin_lock(&dp->d_lock);
+        if (d_unhashed(dp)) {
+            raced = 1;
+        }
+        spin_unlock(&dp->d_lock);
+        afs_linux_unlock_inode(parent->d_inode);
+
+        dput(parent);
+    }
+    return raced;
+}
+#endif /* D_SPLICE_ALIAS_RACE */
+
 /* Validate a dentry. Return 1 if unchanged, 0 if VFS layer should re-evaluate.
  * In kernels 2.2.10 and above, we are passed an additional flags var which
  * may have either the LOOKUP_FOLLOW OR LOOKUP_DIRECTORY set in which case
@@ -1166,6 +1210,13 @@ afs_linux_dentry_revalidate(struct dentry *dp, int flags)
        return -ECHILD;
 #endif
 
+#ifdef D_SPLICE_ALIAS_RACE
+    if (check_dentry_race(dp)) {
+        valid = 0;
+        return valid;
+    }
+#endif
+
     AFS_GLOCK();
     afs_InitFakeStat(&fakestate);
 
@@ -1525,9 +1576,21 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
     int code;
 
     AFS_GLOCK();
+
     code = afs_lookup(VTOAFS(dip), (char *)comp, &vcp, credp);
+    if (code == ENOENT) {
+        /* It's ok for the file to not be found. That's noted by the caller by
+         * seeing that the dp->d_inode field is NULL (set by d_splice_alias or
+         * d_add, below). */
+        code = 0;
+        osi_Assert(vcp == NULL);
+    }
+    if (code) {
+        AFS_GUNLOCK();
+        goto done;
+    }
 
-    if (!code) {
+    if (vcp) {
        struct vattr *vattr = NULL;
        struct vcache *parent_vc = VTOAFS(dip);
 
@@ -1603,9 +1666,7 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
         return NULL;
     }
 
-    /* It's ok for the file to not be found (ENOENT). That's noted by the
-     * caller by seeing that the dp->d_inode field is NULL. */
-    if (code && code != ENOENT) {
+    if (code) {
        if (ip)
            iput(ip);
        return ERR_PTR(afs_convert_code(code));
@@ -2016,7 +2077,7 @@ afs_linux_read_cache(struct file *cachefp, struct page *page,
         cachepage = find_get_page(cachemapping, pageindex);
        if (!cachepage) {
            if (!newpage)
-               newpage = page_cache_alloc_cold(cachemapping);
+               newpage = page_cache_alloc(cachemapping);
            if (!newpage) {
                code = -ENOMEM;
                goto out;
@@ -2174,7 +2235,11 @@ afs_linux_readpage_fastpath(struct file *fp, struct page *pp, int *codep)
        AFS_GLOCK();
        goto out;
     }
+#if defined(PAGEVEC_INIT_COLD_ARG)
     pagevec_init(&lrupv, 0);
+#else
+    pagevec_init(&lrupv);
+#endif
 
     code = afs_linux_read_cache(cacheFp, pp, tdc->f.chunk, &lrupv, NULL);
 
@@ -2334,7 +2399,11 @@ afs_linux_bypass_readpages(struct file *fp, struct address_space *mapping,
     ancr->offset = auio->uio_offset;
     ancr->length = auio->uio_resid;
 
+#if defined(PAGEVEC_INIT_COLD_ARG)
     pagevec_init(&lrupv, 0);
+#else
+    pagevec_init(&lrupv);
+#endif
 
     for(page_ix = 0; page_ix < num_pages; ++page_ix) {
 
@@ -2555,7 +2624,11 @@ afs_linux_readpages(struct file *fp, struct address_space *mapping,
     task = afs_pagecopy_init_task();
 
     tdc = NULL;
+#if defined(PAGEVEC_INIT_COLD_ARG)
     pagevec_init(&lrupv, 0);
+#else
+    pagevec_init(&lrupv);
+#endif
     for (page_idx = 0; page_idx < num_pages; page_idx++) {
        struct page *page = list_entry(page_list->prev, struct page, lru);
        list_del(&page->lru);