From ef90f93a19b482ae38e09c20c737ea3c6c1d4171 Mon Sep 17 00:00:00 2001
From: Marc Dionne <marc.dionne@your-file-system.com>
Date: Tue, 9 Sep 2014 10:39:55 -0300
Subject: [PATCH 1/8] Linux 3.17: No more typedef for ctl_table

The typedef has been removed so we need to use the structure
directly.

Note that the API for register_sysctl_table has also changed
with 3.17, but it reverted back to a form that existed
before and the configure tests handle it correctly.

Reviewed-on: http://gerrit.openafs.org/11455
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Perry Ruiter <pruiter@sinenomine.net>
Reviewed-by: Andrew Deason <adeason@sinenomine.net>
Reviewed-by: D Brashear <shadow@your-file-system.com>
(cherry picked from commit 6a23ca5b6e8bcaf881be7a4c50bfba72d001e6cd)

Change-Id: Ifb8fc0b9b01d2578c65407608f0e1b3f3b254459
---
 src/afs/LINUX/osi_sysctl.c | 4 ++--
 src/cf/linux-test4.m4      | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/afs/LINUX/osi_sysctl.c b/src/afs/LINUX/osi_sysctl.c
index a8f7fac..834e8ad 100644
--- a/src/afs/LINUX/osi_sysctl.c
+++ b/src/afs/LINUX/osi_sysctl.c
@@ -34,7 +34,7 @@ extern afs_int32 afs_pct2;
 #ifdef CONFIG_SYSCTL
 static struct ctl_table_header *afs_sysctl = NULL;
 
-static ctl_table afs_sysctl_table[] = {
+static struct ctl_table afs_sysctl_table[] = {
     {
 #if defined(STRUCT_CTL_TABLE_HAS_CTL_NAME)
 #if defined(CTL_UNNUMBERED)
@@ -234,7 +234,7 @@ static ctl_table afs_sysctl_table[] = {
     {0}
 };
 
-static ctl_table fs_sysctl_table[] = {
+static struct ctl_table fs_sysctl_table[] = {
     {
 #if defined(STRUCT_CTL_TABLE_HAS_CTL_NAME)
 #if defined(CTL_UNNUMBERED)
diff --git a/src/cf/linux-test4.m4 b/src/cf/linux-test4.m4
index b068af5..1759d9e 100644
--- a/src/cf/linux-test4.m4
+++ b/src/cf/linux-test4.m4
@@ -395,7 +395,7 @@ AC_DEFUN([LINUX_REGISTER_SYSCTL_TABLE_NOFLAG], [
   AC_CHECK_LINUX_BUILD([whether register_sysctl_table has an insert_at_head argument],
 		       [ac_cv_linux_register_sysctl_table_noflag],
 		       [#include <linux/sysctl.h>],
-		       [ctl_table *t; register_sysctl_table (t);],
+		       [struct ctl_table *t; register_sysctl_table (t);],
 		       [REGISTER_SYSCTL_TABLE_NOFLAG],
 		       [define if register_sysctl_table has no insert_at head flag],
 		       [])
-- 
2.2.0

From 542e7d8f05bd8d23d6a1e0a2a172123b55c14780 Mon Sep 17 00:00:00 2001
From: Marc Dionne <marc.dionne@your-file-system.com>
Date: Thu, 25 Sep 2014 07:52:12 -0300
Subject: [PATCH 2/8] Linux 3.17: Deal with d_splice_alias errors

In 3.17 the logic in d_splice_alias has changed.  Of interest to
us is the fact that it will now return an EIO error if it finds
an existing connected directory for the dentry, where it would
previously have added a new alias for it.  As a result the end
user can get EIO errors when accessing any file in a volume
if the volume was first accessed through a different path (ex:
RO path vs RW path).

This commit just restores the old behaviour, adding the directory
alias manually in the error case, which is what older versions
of d_splice_alias used to do.

Reviewed-on: http://gerrit.openafs.org/11492
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Perry Ruiter <pruiter@sinenomine.net>
Reviewed-by: Andrew Deason <adeason@sinenomine.net>
Reviewed-by: D Brashear <shadow@your-file-system.com>
(cherry picked from commit 5815ee92a41cdcf105741d834042a5617dc4c219)

Change-Id: Ie86009ede93255c85fcf640af14c598fe1e42ca9
---
 src/afs/LINUX/osi_vnodeops.c | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/src/afs/LINUX/osi_vnodeops.c b/src/afs/LINUX/osi_vnodeops.c
index 7e5cdd1..3ddcf42 100644
--- a/src/afs/LINUX/osi_vnodeops.c
+++ b/src/afs/LINUX/osi_vnodeops.c
@@ -1529,9 +1529,18 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
     /* 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.
      */
-    if (!code || code == ENOENT)
-	return newdp;
-    else 
+    if (!code || code == ENOENT) {
+	/*
+	 * d_splice_alias can return an error (EIO) if there is an existing
+	 * connected directory alias for this dentry.
+	 */
+	if (!IS_ERR(newdp))
+	    return newdp;
+	else {
+	    d_add(dp, ip);
+	    return NULL;
+	}
+    } else
 	return ERR_PTR(afs_convert_code(code));
 }
 
-- 
2.2.0

From b5262ef13852032cfa4415ab0e9888d2c392b3b7 Mon Sep 17 00:00:00 2001
From: Marc Dionne <marc.dionne@your-file-system.com>
Date: Thu, 23 Oct 2014 11:12:57 -0400
Subject: [PATCH 3/8] Linux 3.18: d_invalidate can no longer return an error

d_invalidate is now defined as void and does not have
a return value to check.

Reviewed-on: http://gerrit.openafs.org/11562
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: D Brashear <shadow@your-file-system.com>
(cherry picked from commit a42f01d5ebb13da575b3123800ee6990743155ab)

Change-Id: I8542404771c4a7962238efd9a53d7dfcf4011c96
Reviewed-on: http://gerrit.openafs.org/11569
Reviewed-by: Anders Kaseorg <andersk@mit.edu>
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Reviewed-by: Andrew Deason <adeason@sinenomine.net>
Reviewed-by: Stephan Wiesand <stephan.wiesand@desy.de>
(cherry picked from commit b3527c802ad82cdecc0df6dfa42b228710ad5fd4)
---
 acinclude.m4               |  1 +
 src/afs/LINUX/osi_compat.h | 11 +++++++++++
 src/afs/LINUX/osi_vcache.c |  4 +++-
 src/cf/linux-test4.m4      | 13 +++++++++++++
 4 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/acinclude.m4 b/acinclude.m4
index 13d70db..b10cfb6 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -1024,6 +1024,7 @@ case $AFS_SYSNAME in *_linux* | *_umlinux*)
 		 LINUX_IOP_I_CREATE_TAKES_BOOL
 		 LINUX_DOP_D_REVALIDATE_TAKES_UNSIGNED
 		 LINUX_IOP_LOOKUP_TAKES_UNSIGNED
+		 LINUX_D_INVALIDATE_IS_VOID
 
 		 dnl If we are guaranteed that keyrings will work - that is
 		 dnl  a) The kernel has keyrings enabled
diff --git a/src/afs/LINUX/osi_compat.h b/src/afs/LINUX/osi_compat.h
index c500c61..26673a7 100644
--- a/src/afs/LINUX/osi_compat.h
+++ b/src/afs/LINUX/osi_compat.h
@@ -557,4 +557,15 @@ afs_maybe_shrink_dcache(struct dentry *dp)
 #endif
 }
 
+static inline int
+afs_d_invalidate(struct dentry *dp)
+{
+#if defined(D_INVALIDATE_IS_VOID)
+    d_invalidate(dp);
+    return 0;
+#else
+    return d_invalidate(dp);
+#endif
+}
+
 #endif /* AFS_LINUX_OSI_COMPAT_H */
diff --git a/src/afs/LINUX/osi_vcache.c b/src/afs/LINUX/osi_vcache.c
index 99aab91..1d0db82 100644
--- a/src/afs/LINUX/osi_vcache.c
+++ b/src/afs/LINUX/osi_vcache.c
@@ -13,6 +13,8 @@
 #include "afs/sysincludes.h"    /*Standard vendor system headers */
 #include "afsincludes.h"        /*AFS-based standard headers */
 
+#include "osi_compat.h"
+
 int
 osi_TryEvictVCache(struct vcache *avc, int *slept, int defersleep) {
     int code;
@@ -71,7 +73,7 @@ restart:
 	    dget(dentry);
 
 	    spin_unlock(&inode->i_lock);
-	    if (d_invalidate(dentry) == -EBUSY) {
+	    if (afs_d_invalidate(dentry) == -EBUSY) {
 		dput(dentry);
 		/* perhaps lock and try to continue? (use cur as head?) */
 		goto inuse;
diff --git a/src/cf/linux-test4.m4 b/src/cf/linux-test4.m4
index 1759d9e..34c5d4d 100644
--- a/src/cf/linux-test4.m4
+++ b/src/cf/linux-test4.m4
@@ -786,3 +786,16 @@ AC_DEFUN([LINUX_IOP_LOOKUP_TAKES_UNSIGNED], [
 		       [define if your iops.lookup takes an unsigned int argument],
 		       [-Werror])
 ])
+
+
+AC_DEFUN([LINUX_D_INVALIDATE_IS_VOID], [
+  AC_CHECK_LINUX_BUILD([whether d_invalidate returns void],
+			[ac_cv_linux_func_d_invalidate_returns_void],
+			[#include <linux/fs.h>],
+			[
+			void d_invalidate(struct dentry *);
+			],
+		       [D_INVALIDATE_IS_VOID],
+		       [define if your d_invalidate returns void],
+		       [])
+])
-- 
2.2.0

From 8a1de84bb7b7d4b53e007cf7001c7dd509cc6078 Mon Sep 17 00:00:00 2001
From: Marc Dionne <marc.dionne@your-file-system.com>
Date: Thu, 23 Oct 2014 11:27:55 -0400
Subject: [PATCH 4/8] Linux 3.18: key_type no longer has a match op

Structure key_type no longer has a match op, and
overriding the default matching has to be done
differently.

Our current match op doesn't do anything special so there's
no need to try to override the defaults; just remove the
assignment of .match and the associated function.

Reviewed-on: http://gerrit.openafs.org/11563
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: D Brashear <shadow@your-file-system.com>
(cherry picked from commit b5de4a9f42bb83ae03f2f647b11a1200a502d013)

Change-Id: I7baca4a7f02eac45671e1e9ebf48534cdd5830be
Reviewed-on: http://gerrit.openafs.org/11570
Reviewed-by: Anders Kaseorg <andersk@mit.edu>
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Reviewed-by: Andrew Deason <adeason@sinenomine.net>
Reviewed-by: Stephan Wiesand <stephan.wiesand@desy.de>
(cherry picked from commit 9d653bd966b47223eaaf6f8a5d983b6634bdf0d5)
---
 acinclude.m4               | 3 ++-
 src/afs/LINUX/osi_groups.c | 4 ++++
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/acinclude.m4 b/acinclude.m4
index b10cfb6..72dd6c1 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -840,8 +840,9 @@ case $AFS_SYSNAME in *_linux* | *_umlinux*)
 		 AC_CHECK_LINUX_STRUCT([file_operations], [sendfile], [fs.h])
 		 AC_CHECK_LINUX_STRUCT([file_system_type], [mount], [fs.h])
 		 AC_CHECK_LINUX_STRUCT([inode_operations], [truncate], [fs.h])
-		 AC_CHECK_LINUX_STRUCT([key_type], [preparse], [key-type.h])
 		 AC_CHECK_LINUX_STRUCT([key_type], [instantiate_prep], [key-type.h])
+		 AC_CHECK_LINUX_STRUCT([key_type], [match], [key-type.h])
+		 AC_CHECK_LINUX_STRUCT([key_type], [preparse], [key-type.h])
 		 AC_CHECK_LINUX_STRUCT([nameidata], [path], [namei.h])
 		 AC_CHECK_LINUX_STRUCT([proc_dir_entry], [owner], [proc_fs.h])
 		 AC_CHECK_LINUX_STRUCT([super_block], [s_bdi], [fs.h])
diff --git a/src/afs/LINUX/osi_groups.c b/src/afs/LINUX/osi_groups.c
index f3f87c2..f1d97a6 100644
--- a/src/afs/LINUX/osi_groups.c
+++ b/src/afs/LINUX/osi_groups.c
@@ -498,10 +498,12 @@ error:
     return code;
 }
 
+#if defined(STRUCT_KEY_TYPE_HAS_MATCH)
 static int afs_pag_match(const struct key *key, const void *description)
 {
 	return strcmp(key->description, description) == 0;
 }
+#endif
 
 static void afs_pag_destroy(struct key *key)
 {
@@ -527,7 +529,9 @@ struct key_type key_type_afs_pag =
 #else
     .instantiate = afs_pag_instantiate,
 #endif
+#if defined(STRUCT_KEY_TYPE_HAS_MATCH)
     .match       = afs_pag_match,
+#endif
     .destroy     = afs_pag_destroy,
 };
 
-- 
2.2.0

From 0ef8ea06ee83885f3b22d3426864ebdded072976 Mon Sep 17 00:00:00 2001
From: Andrew Deason <adeason@sinenomine.net>
Date: Wed, 5 Nov 2014 10:22:00 -0600
Subject: [PATCH 5/8] LINUX: Avoid check for key_type.match existence

Commit b5de4a9f removed our key_type 'match' function for kernels that
do not have such a 'match' function pointer. However, this added a
configure test where we are supposed to fail for the "new" behavior,
which is discouraged.

This causes an actual problem, because this test will fail on at least
RHEL5, due to arguably unrelated reasons (the header file for the
relevant struct is in key.h instead of key-type.h). And so, in that
situation we avoid defining a 'match' function callback, meaning our
'match' function callback is NULL, which causes a panic when we try to
actually look up keys for a PAG.

To fix this, transform the 'match' config test into one where we
succeed for the "new" behavior. We do this by testing for the
existence of the new functionality that replaced the old 'match'
function, which is the match_preparse function (specifically, the
'cmp' field in the structure accepted by match_preparse). This should
cause unrelated compilation errors to cause us to revert to the "old"
behavior instead of the "new" behavior. At worst, this should cause
build issues if we get the config test wrong (since we will try to use
the 'match' function definition that does not exist), instead of
panicing at runtime.

Note that while we test for key_type.match_preparse, we don't actually
use that function, since our 'match' functionality is the same as the
default behavior (according to b5de4a9f). So, we can avoid defining
any such function for newer kernels.

Thanks to Stephan Wiesand for bisecting this issue.

Reviewed-on: http://gerrit.openafs.org/11589
Reviewed-by: Marc Dionne <marc.c.dionne@gmail.com>
Reviewed-by: Stephan Wiesand <stephan.wiesand@desy.de>
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Jeffrey Altman <jaltman@your-file-system.com>
(cherry picked from commit a9a3cb2efff7e6c020be4687b004d157bc070ac6)

Change-Id: I59f40258c5ea35a59681f436095922d111e344f6
Reviewed-on: http://gerrit.openafs.org/11595
Tested-by: Anders Kaseorg <andersk@mit.edu>
Reviewed-by: Anders Kaseorg <andersk@mit.edu>
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Marc Dionne <marc.c.dionne@gmail.com>
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Reviewed-by: Andrew Deason <adeason@sinenomine.net>
Reviewed-by: Stephan Wiesand <stephan.wiesand@desy.de>
(cherry picked from commit 9ff1cd92d023f9a949b499891f552b41cb0c52e4)
---
 acinclude.m4               |  2 +-
 src/afs/LINUX/osi_groups.c | 10 ++++++++--
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/acinclude.m4 b/acinclude.m4
index 72dd6c1..d324dc1 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -841,7 +841,7 @@ case $AFS_SYSNAME in *_linux* | *_umlinux*)
 		 AC_CHECK_LINUX_STRUCT([file_system_type], [mount], [fs.h])
 		 AC_CHECK_LINUX_STRUCT([inode_operations], [truncate], [fs.h])
 		 AC_CHECK_LINUX_STRUCT([key_type], [instantiate_prep], [key-type.h])
-		 AC_CHECK_LINUX_STRUCT([key_type], [match], [key-type.h])
+		 AC_CHECK_LINUX_STRUCT([key_type], [match_preparse], [key-type.h])
 		 AC_CHECK_LINUX_STRUCT([key_type], [preparse], [key-type.h])
 		 AC_CHECK_LINUX_STRUCT([nameidata], [path], [namei.h])
 		 AC_CHECK_LINUX_STRUCT([proc_dir_entry], [owner], [proc_fs.h])
diff --git a/src/afs/LINUX/osi_groups.c b/src/afs/LINUX/osi_groups.c
index f1d97a6..3b068e5 100644
--- a/src/afs/LINUX/osi_groups.c
+++ b/src/afs/LINUX/osi_groups.c
@@ -498,7 +498,13 @@ error:
     return code;
 }
 
-#if defined(STRUCT_KEY_TYPE_HAS_MATCH)
+#if !defined(STRUCT_KEY_TYPE_HAS_MATCH_PREPARSE)
+/* Note that we only define a ->match function if struct
+ * key_type.match_preparse does _not_ exist. If key_type.match_preparse does
+ * exist, we would use that to specify an alternative comparison function; but
+ * since we just rely on default behavior, we don't need to actually specify
+ * one. But for kernels with no such match_preparse function, we need to
+ * specify a 'match' function, since there is no default. */
 static int afs_pag_match(const struct key *key, const void *description)
 {
 	return strcmp(key->description, description) == 0;
@@ -529,7 +535,7 @@ struct key_type key_type_afs_pag =
 #else
     .instantiate = afs_pag_instantiate,
 #endif
-#if defined(STRUCT_KEY_TYPE_HAS_MATCH)
+#if !defined(STRUCT_KEY_TYPE_HAS_MATCH_PREPARSE)
     .match       = afs_pag_match,
 #endif
     .destroy     = afs_pag_destroy,
-- 
2.2.0

From faf71b805d41aa435def8f5e48cfb8900f41a315 Mon Sep 17 00:00:00 2001
From: Marc Dionne <marc.dionne@your-file-system.com>
Date: Thu, 18 Dec 2014 06:57:22 -0500
Subject: [PATCH 6/8] Linux: Move code to reset the root to afs/LINUX

Move the Linux specific bit of code to reset the root to
afs/LINUX platform specific files.  Things that play with
the Linux vfs internals should not be exposed here.

No functional change, but this helps cleanup some ifdef
mess.

Change-Id: Ia27fca3d8052ead45783cb2332c04fe6e99e5d9f
---
 src/afs/LINUX/osi_prototypes.h |  3 ++
 src/afs/LINUX/osi_vcache.c     | 52 ++++++++++++++++++++++++++++++++
 src/afs/afs_daemons.c          | 67 ++++--------------------------------------
 3 files changed, 60 insertions(+), 62 deletions(-)

diff --git a/src/afs/LINUX/osi_prototypes.h b/src/afs/LINUX/osi_prototypes.h
index 9002882..1d2ca0d 100644
--- a/src/afs/LINUX/osi_prototypes.h
+++ b/src/afs/LINUX/osi_prototypes.h
@@ -79,6 +79,9 @@ extern void osi_VM_FlushPages(struct vcache *avc, afs_ucred_t *credp);
 extern void osi_VM_Truncate(struct vcache *avc, int alen,
 			    afs_ucred_t *acred);
 
+/* osi_vcache.c */
+extern void osi_ResetRootVCache(afs_uint32 volid);
+
 /* osi_vfsops.c */
 extern void vattr2inode(struct inode *ip, struct vattr *vp);
 extern int afs_init_inodecache(void);
diff --git a/src/afs/LINUX/osi_vcache.c b/src/afs/LINUX/osi_vcache.c
index 1d0db82..2ccd060 100644
--- a/src/afs/LINUX/osi_vcache.c
+++ b/src/afs/LINUX/osi_vcache.c
@@ -143,3 +143,55 @@ osi_PostPopulateVCache(struct vcache *avc) {
     vSetType(avc, VREG);
 }
 
+void
+osi_ResetRootVCache(afs_uint32 volid)
+{
+    struct vrequest treq;
+    struct vattr vattr;
+    cred_t *credp;
+    struct dentry *dp;
+    struct vcache *vcp;
+    struct inode *root = AFSTOV(afs_globalVp);
+
+    afs_rootFid.Fid.Volume = volid;
+    afs_rootFid.Fid.Vnode = 1;
+    afs_rootFid.Fid.Unique = 1;
+
+    credp = crref();
+    if (afs_InitReq(&treq, credp))
+	goto out;
+    vcp = afs_GetVCache(&afs_rootFid, &treq, NULL, NULL);
+    if (!vcp)
+	goto out;
+    afs_getattr(vcp, &vattr, credp);
+    afs_fill_inode(AFSTOV(vcp), &vattr);
+
+    dp = d_find_alias(root);
+
+#if defined(HAVE_DCACHE_LOCK)
+    spin_lock(&dcache_lock);
+#else
+    spin_lock(&AFSTOV(vcp)->i_lock);
+#endif
+    spin_lock(&dp->d_lock);
+#if defined(D_ALIAS_IS_HLIST)
+    hlist_del_init(&dp->d_alias);
+    hlist_add_head(&dp->d_alias, &(AFSTOV(vcp)->i_dentry));
+#else
+    list_del_init(&dp->d_alias);
+    list_add(&dp->d_alias, &(AFSTOV(vcp)->i_dentry));
+#endif
+    dp->d_inode = AFSTOV(vcp);
+    spin_unlock(&dp->d_lock);
+#if defined(HAVE_DCACHE_LOCK)
+    spin_unlock(&dcache_lock);
+#else
+    spin_unlock(&AFSTOV(vcp)->i_lock);
+#endif
+    dput(dp);
+
+    AFS_RELE(root);
+    afs_globalVp = vcp;
+out:
+    crfree(credp);
+}
diff --git a/src/afs/afs_daemons.c b/src/afs/afs_daemons.c
index 8e5f6ad..cd7d378 100644
--- a/src/afs/afs_daemons.c
+++ b/src/afs/afs_daemons.c
@@ -363,71 +363,14 @@ afs_CheckRootVolume(void)
 		 * count to zero and fs checkv is executed when the current
 		 * directory is /afs.
 		 */
-#ifdef AFS_LINUX20_ENV
-		{
-		    struct vrequest *treq = NULL;
-		    struct vattr vattr;
-		    cred_t *credp;
-		    struct dentry *dp;
-		    struct vcache *vcp;
-
-		    afs_rootFid.Fid.Volume = volid;
-		    afs_rootFid.Fid.Vnode = 1;
-		    afs_rootFid.Fid.Unique = 1;
-
-		    credp = crref();
-		    if (afs_CreateReq(&treq, credp))
-			goto out;
-		    vcp = afs_GetVCache(&afs_rootFid, treq, NULL, NULL);
-		    if (!vcp)
-			goto out;
-		    afs_getattr(vcp, &vattr, credp);
-		    afs_fill_inode(AFSTOV(vcp), &vattr);
-
-		    dp = d_find_alias(AFSTOV(afs_globalVp));
-
-#if defined(AFS_LINUX24_ENV)
-#if defined(HAVE_DCACHE_LOCK)
-		    spin_lock(&dcache_lock);
+#ifdef AFS_LINUX26_ENV
+		osi_ResetRootVCache(volid);
 #else
-		    spin_lock(&AFSTOV(vcp)->i_lock);
-#endif
-#if defined(AFS_LINUX26_ENV)
-		    spin_lock(&dp->d_lock);
-#endif
-#endif
-#if defined(D_ALIAS_IS_HLIST)
-		    hlist_del_init(&dp->d_alias);
-		    hlist_add_head(&dp->d_alias, &(AFSTOV(vcp)->i_dentry));
-#else
-		    list_del_init(&dp->d_alias);
-		    list_add(&dp->d_alias, &(AFSTOV(vcp)->i_dentry));
-#endif
-		    dp->d_inode = AFSTOV(vcp);
-#if defined(AFS_LINUX24_ENV)
-#if defined(AFS_LINUX26_ENV)
-		    spin_unlock(&dp->d_lock);
-#endif
-#if defined(HAVE_DCACHE_LOCK)
-		    spin_unlock(&dcache_lock);
-#else
-		    spin_unlock(&AFSTOV(vcp)->i_lock);
-#endif
-#endif
-		    dput(dp);
-
-		    AFS_FAST_RELE(afs_globalVp);
-		    afs_globalVp = vcp;
-		out:
-		    crfree(credp);
-		    afs_DestroyReq(treq);
-		}
-#else
-#ifdef AFS_DARWIN80_ENV
+# ifdef AFS_DARWIN80_ENV
 		afs_PutVCache(afs_globalVp);
-#else
+# else
 		AFS_FAST_RELE(afs_globalVp);
-#endif
+# endif
 		afs_globalVp = 0;
 #endif
 	    }
-- 
2.2.0

From 4e188d785b735f2d43a831f9af123f896b55b77e Mon Sep 17 00:00:00 2001
From: Marc Dionne <marc.dionne@your-file-system.com>
Date: Thu, 18 Dec 2014 07:13:46 -0500
Subject: [PATCH 7/8] Linux: d_alias becomes d_u.d_alias

The fields in struct dentry are re-arranged so that d_alias
shares space wth d_rcu inside the d_u union.  Some references
need to change from d_alias to d_u.d_alias.

The kernel change was introduced for 3.19 but was also backported
to the 3.18 stable series in 3.18.1, so this commit is required
for 3.19 and current 3.18 kernels.

Change-Id: I711a5a3a89af6e0055381dfd4474ddca2868bb9c
---
 acinclude.m4               | 1 +
 src/afs/LINUX/osi_compat.h | 4 ++++
 src/cf/linux-test4.m4      | 9 ++++++++-
 3 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/acinclude.m4 b/acinclude.m4
index d324dc1..fa429c7 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -828,6 +828,7 @@ case $AFS_SYSNAME in *_linux* | *_umlinux*)
 				       [backing-dev.h])
 		 AC_CHECK_LINUX_STRUCT([cred], [session_keyring], [cred.h])
 		 AC_CHECK_LINUX_STRUCT([ctl_table], [ctl_name], [sysctl.h])
+		 AC_CHECK_LINUX_STRUCT([dentry], [d_u.d_alias], [dcache.h])
 		 AC_CHECK_LINUX_STRUCT([dentry_operations], [d_automount], [dcache.h])
 		 AC_CHECK_LINUX_STRUCT([inode], [i_alloc_sem], [fs.h])
 		 AC_CHECK_LINUX_STRUCT([inode], [i_blkbits], [fs.h])
diff --git a/src/afs/LINUX/osi_compat.h b/src/afs/LINUX/osi_compat.h
index 26673a7..b98e980 100644
--- a/src/afs/LINUX/osi_compat.h
+++ b/src/afs/LINUX/osi_compat.h
@@ -37,6 +37,10 @@ typedef struct vfs_path afs_linux_path_t;
 typedef struct path afs_linux_path_t;
 #endif
 
+#if defined(STRUCT_DENTRY_HAS_D_U_D_ALIAS)
+# define d_alias d_u.d_alias
+#endif
+
 #ifndef HAVE_LINUX_DO_SYNC_READ
 static inline int
 do_sync_read(struct file *fp, char *buf, size_t count, loff_t *offp) {
diff --git a/src/cf/linux-test4.m4 b/src/cf/linux-test4.m4
index 34c5d4d..5f5ec5c 100644
--- a/src/cf/linux-test4.m4
+++ b/src/cf/linux-test4.m4
@@ -723,7 +723,11 @@ AC_DEFUN([LINUX_D_ALIAS_IS_HLIST], [
 			[#include <linux/fs.h>],
 			[struct dentry *d = NULL;
 			struct hlist_node *hn = NULL;
-			d->d_alias = *hn;],
+			#if defined(STRUCT_DENTRY_HAS_D_U_D_ALIAS)
+			d->d_u.d_alias = *hn;
+			#else
+			d->d_alias = *hn;
+			#endif],
 			[D_ALIAS_IS_HLIST],
 			[define if dentry->d_alias is an hlist],
 			[])
@@ -737,6 +741,9 @@ AC_DEFUN([LINUX_HLIST_ITERATOR_NO_NODE], [
 			#include <linux/fs.h>],
 			[struct dentry *d = NULL, *cur;
 			struct inode *ip;
+			#if defined(STRUCT_DENTRY_HAS_D_U_D_ALIAS)
+			# define d_alias d_u.d_alias
+			#endif
 			hlist_for_each_entry(cur, &ip->i_dentry, d_alias) { }
 			],
 			[HLIST_ITERATOR_NO_NODE],
-- 
2.2.0

From 2808f4aefd17e0e69cd0cd0637af0ecfd329bcc8 Mon Sep 17 00:00:00 2001
From: Marc Dionne <marc.dionne@your-file-system.com>
Date: Thu, 18 Dec 2014 08:43:22 -0500
Subject: [PATCH 8/8] Linux: d_splice_alias may drop inode reference on error

d_splice_alias now drops the inode reference on error, so we
need to grab an extra one to make sure that the inode doesn't
go away, and release it when done if there was no error.

For kernels that may not drop the reference, provide an
additional iput() within an ifdef.  This could be hooked up
to a configure option to allow building a module for a kernel
that is known not to drop the reference on error.  That hook
is not provided here.  Affected kernels should be the early
3.17 ones (3.17 - 3.17.2); 3.16 and older kernels should not
return errors here.

Change-Id: Id1786ac2227b4d8e0ae801fe59c15a0ecd975bed
---
 src/afs/LINUX/osi_vnodeops.c | 25 ++++++++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)

diff --git a/src/afs/LINUX/osi_vnodeops.c b/src/afs/LINUX/osi_vnodeops.c
index 3ddcf42..b2090d7 100644
--- a/src/afs/LINUX/osi_vnodeops.c
+++ b/src/afs/LINUX/osi_vnodeops.c
@@ -1521,6 +1521,13 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
 	ip->i_flags |= S_AUTOMOUNT;
 #endif
     }
+    /*
+     * Take an extra reference so the inode doesn't go away if
+     * d_splice_alias drops our reference on error.
+     */
+    if (ip)
+	igrab(ip);
+
     newdp = d_splice_alias(ip, dp);
 
  done:
@@ -1534,14 +1541,26 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
 	 * d_splice_alias can return an error (EIO) if there is an existing
 	 * connected directory alias for this dentry.
 	 */
-	if (!IS_ERR(newdp))
+	if (!IS_ERR(newdp)) {
+	    iput(ip);
 	    return newdp;
-	else {
+	} else {
 	    d_add(dp, ip);
+	    /*
+	     * Depending on the kernel version, d_splice_alias may or may
+	     * not drop the inode reference on error.  If it didn't, do it
+	     * here.
+	     */
+#if defined(D_SPLICE_ALIAS_LEAK_ON_ERROR)
+	    iput(ip);
+#endif
 	    return NULL;
 	}
-    } else
+    } else {
+	if (ip)
+	    iput(ip);
 	return ERR_PTR(afs_convert_code(code));
+    }
 }
 
 static int
-- 
2.2.0

