* Build-Depend debhelper (>= 9.20160114) for dh_strip
--dbgsym-migration.
+ * debian/patches:
+ - LINUX-split-dentry-eviction-from-osi_TryEvictVCache.patch,
+ Reformat-src-afs-LINUX-osi_vcache.c.patch,
+ LINUX-do-not-use-d_invalidate-to-evict-dentries.patch,
+ afs-shake-harder-in-shake-loose-vcaches.patch: Avoid system hangs on
+ busy machines due to vcache deallocation failures.
-- Anders Kaseorg <andersk@mit.edu> Thu, 01 Dec 2016 00:13:01 -0500
--- /dev/null
+From 83a174de588c56cedbf13e6abab786fb56e8082f Mon Sep 17 00:00:00 2001
+From: Mark Vitale <mvitale@sinenomine.net>
+Date: Thu, 4 Aug 2016 18:42:27 -0400
+Subject: LINUX: do not use d_invalidate to evict dentries
+
+When working within the AFS filespace, commands which access large
+numbers of OpenAFS files (e.g., git operations and builds) may result in
+active files (e.g., the current working directory) being evicted from the
+dentry cache. One symptom of this is the following message upon return
+to the shell prompt:
+
+"fatal: unable to get current working directory: No such file or
+directory"
+
+Starting with Linux 3.18, d_invalidate returns void because it always
+succeeds. Commit a42f01d5ebb13da575b3123800ee6990743155ab adapted
+OpenAFS to cope with the new return type, but not with the changed
+semantics of d_invalidate. Because d_invalidate can no longer fail with
+-EBUSY when invoked on an in-use dentry. OpenAFS must no longer trust it
+to preserve in-use dentries.
+
+Modify the dentry eviction code to use a method (d_prune_aliases) that
+does not evict in-use dentries.
+
+Reviewed-on: https://gerrit.openafs.org/12363
+Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
+Tested-by: Benjamin Kaduk <kaduk@mit.edu>
+(cherry picked from commit c3bbf0b4444db88192eea4580ac9e9ca3de0d286)
+
+Change-Id: Ic72a280f136cc414b54d4b8ec280f225290df122
+---
+ src/afs/LINUX/osi_vcache.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/src/afs/LINUX/osi_vcache.c b/src/afs/LINUX/osi_vcache.c
+index bc74b6744..23040b12c 100644
+--- a/src/afs/LINUX/osi_vcache.c
++++ b/src/afs/LINUX/osi_vcache.c
+@@ -24,6 +24,13 @@ TryEvictDentries(struct vcache *avc)
+ struct hlist_node *p;
+ #endif
+
++#if defined(D_INVALIDATE_IS_VOID)
++ /* At this kernel level, d_invalidate always succeeds;
++ * that is, it will now invalidate even an active directory,
++ * Therefore we must use a different method to evict dentries.
++ */
++ d_prune_aliases(inode);
++#else
+ #if defined(HAVE_DCACHE_LOCK)
+ spin_lock(&dcache_lock);
+
+@@ -78,6 +85,7 @@ restart:
+ spin_unlock(&inode->i_lock);
+ #endif /* HAVE_DCACHE_LOCK */
+ inuse:
++#endif /* D_INVALIDATE_IS_VOID */
+ return;
+ }
+
+--
+2.11.0
+
--- /dev/null
+From cb2e2c26dc841bb4cab2d3b2ff376936f6c39e68 Mon Sep 17 00:00:00 2001
+From: Mark Vitale <mvitale@sinenomine.net>
+Date: Thu, 4 Aug 2016 18:18:15 -0400
+Subject: LINUX: split dentry eviction from osi_TryEvictVCache
+
+To make osi_TryEvictVCache clearer, and to prepare for a future change
+in dentry eviction, split the dentry eviction logic into its own routine
+osi_TryEvictDentries.
+
+No functional difference should be incurred by this commit.
+
+Reviewed-on: https://gerrit.openafs.org/12362
+Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
+Tested-by: BuildBot <buildbot@rampaginggeek.com>
+Reviewed-by: Joe Gorse <jhgorse@gmail.com>
+(cherry picked from commit 742643e306929ac979ab69515a33ee2a3f2fa3fa)
+
+Change-Id: I750fc7606ca56e784a60bdbc13a32d21fe307429
+---
+ src/afs/LINUX/osi_vcache.c | 103 +++++++++++++++++++++++++--------------------
+ 1 file changed, 57 insertions(+), 46 deletions(-)
+
+diff --git a/src/afs/LINUX/osi_vcache.c b/src/afs/LINUX/osi_vcache.c
+index 8a0c57899..3682bdc29 100644
+--- a/src/afs/LINUX/osi_vcache.c
++++ b/src/afs/LINUX/osi_vcache.c
+@@ -15,77 +15,88 @@
+
+ #include "osi_compat.h"
+
+-int
+-osi_TryEvictVCache(struct vcache *avc, int *slept, int defersleep) {
+- int code;
+-
++void
++osi_TryEvictDentries(struct vcache *avc)
++{
+ struct dentry *dentry;
+ struct inode *inode = AFSTOV(avc);
+ #if defined(D_ALIAS_IS_HLIST) && !defined(HLIST_ITERATOR_NO_NODE)
+ struct hlist_node *p;
+ #endif
+
+- /* First, see if we can evict the inode from the dcache */
+- if (defersleep && avc != afs_globalVp && VREFCOUNT(avc) > 1 && avc->opens == 0) {
+- *slept = 1;
+- AFS_FAST_HOLD(avc);
+- ReleaseWriteLock(&afs_xvcache);
+- AFS_GUNLOCK();
+-
+ #if defined(HAVE_DCACHE_LOCK)
+- spin_lock(&dcache_lock);
++ spin_lock(&dcache_lock);
+
+ restart:
+- list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
+- if (d_unhashed(dentry))
+- continue;
+- dget_locked(dentry);
+-
+- spin_unlock(&dcache_lock);
+- if (d_invalidate(dentry) == -EBUSY) {
+- dput(dentry);
+- /* perhaps lock and try to continue? (use cur as head?) */
+- goto inuse;
+- }
++ list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
++ if (d_unhashed(dentry))
++ continue;
++ dget_locked(dentry);
++
++ spin_unlock(&dcache_lock);
++ if (d_invalidate(dentry) == -EBUSY) {
+ dput(dentry);
+- spin_lock(&dcache_lock);
+- goto restart;
++ /* perhaps lock and try to continue? (use cur as head?) */
++ goto inuse;
+ }
+- spin_unlock(&dcache_lock);
++ dput(dentry);
++ spin_lock(&dcache_lock);
++ goto restart;
++ }
++ spin_unlock(&dcache_lock);
+ #else /* HAVE_DCACHE_LOCK */
+- spin_lock(&inode->i_lock);
++ spin_lock(&inode->i_lock);
+
+ restart:
+ #if defined(D_ALIAS_IS_HLIST)
+ # if defined(HLIST_ITERATOR_NO_NODE)
+- hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) {
++ hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) {
+ # else
+- hlist_for_each_entry(dentry, p, &inode->i_dentry, d_alias) {
++ hlist_for_each_entry(dentry, p, &inode->i_dentry, d_alias) {
+ # endif
+ #else
+- list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
++ list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
+ #endif
+- spin_lock(&dentry->d_lock);
+- if (d_unhashed(dentry)) {
+- spin_unlock(&dentry->d_lock);
+- continue;
+- }
++ spin_lock(&dentry->d_lock);
++ if (d_unhashed(dentry)) {
+ spin_unlock(&dentry->d_lock);
+- dget(dentry);
+-
+- spin_unlock(&inode->i_lock);
+- if (afs_d_invalidate(dentry) == -EBUSY) {
+- dput(dentry);
+- /* perhaps lock and try to continue? (use cur as head?) */
+- goto inuse;
+- }
+- dput(dentry);
+- spin_lock(&inode->i_lock);
+- goto restart;
++ continue;
+ }
++ spin_unlock(&dentry->d_lock);
++ dget(dentry);
++
+ spin_unlock(&inode->i_lock);
++ if (afs_d_invalidate(dentry) == -EBUSY) {
++ dput(dentry);
++ /* perhaps lock and try to continue? (use cur as head?) */
++ goto inuse;
++ }
++ dput(dentry);
++ spin_lock(&inode->i_lock);
++ goto restart;
++ }
++ spin_unlock(&inode->i_lock);
+ #endif /* HAVE_DCACHE_LOCK */
+ inuse:
++ return;
++}
++
++
++int
++osi_TryEvictVCache(struct vcache *avc, int *slept, int defersleep)
++{
++ int code;
++
++ /* First, see if we can evict the inode from the dcache */
++ if (defersleep && avc != afs_globalVp && VREFCOUNT(avc) > 1
++ && avc->opens == 0) {
++ *slept = 1;
++ AFS_FAST_HOLD(avc);
++ ReleaseWriteLock(&afs_xvcache);
++ AFS_GUNLOCK();
++
++ osi_TryEvictDentries(avc);
++
+ AFS_GLOCK();
+ ObtainWriteLock(&afs_xvcache, 733);
+ AFS_FAST_RELE(avc);
+--
+2.11.0
+
--- /dev/null
+From f31b673509664714b146f23be069b69be7e1ac89 Mon Sep 17 00:00:00 2001
+From: Benjamin Kaduk <kaduk@mit.edu>
+Date: Thu, 27 Oct 2016 17:27:26 -0500
+Subject: Reformat src/afs/LINUX/osi_vcache.c
+
+Apply the GNU indent options from CODING, with manual adjustments
+to leave jump labels in column zero.
+
+Also rename and mark static a function-local helper function.
+
+Reviewed-on: https://gerrit.openafs.org/12422
+Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
+Tested-by: BuildBot <buildbot@rampaginggeek.com>
+(cherry picked from commit 22933e02e2510f25b79230964f135571c7bfe710)
+
+Change-Id: I9fb2886ae2213218ae80ea9d5b80540b9c79077b
+---
+ src/afs/LINUX/osi_vcache.c | 23 ++++++++++++++---------
+ 1 file changed, 14 insertions(+), 9 deletions(-)
+
+diff --git a/src/afs/LINUX/osi_vcache.c b/src/afs/LINUX/osi_vcache.c
+index 3682bdc29..bc74b6744 100644
+--- a/src/afs/LINUX/osi_vcache.c
++++ b/src/afs/LINUX/osi_vcache.c
+@@ -10,13 +10,13 @@
+ #include <afsconfig.h>
+ #include "afs/param.h"
+
+-#include "afs/sysincludes.h" /*Standard vendor system headers */
+-#include "afsincludes.h" /*AFS-based standard headers */
++#include "afs/sysincludes.h" /*Standard vendor system headers */
++#include "afsincludes.h" /*AFS-based standard headers */
+
+ #include "osi_compat.h"
+
+-void
+-osi_TryEvictDentries(struct vcache *avc)
++static void
++TryEvictDentries(struct vcache *avc)
+ {
+ struct dentry *dentry;
+ struct inode *inode = AFSTOV(avc);
+@@ -95,7 +95,7 @@ osi_TryEvictVCache(struct vcache *avc, int *slept, int defersleep)
+ ReleaseWriteLock(&afs_xvcache);
+ AFS_GUNLOCK();
+
+- osi_TryEvictDentries(avc);
++ TryEvictDentries(avc);
+
+ AFS_GLOCK();
+ ObtainWriteLock(&afs_xvcache, 733);
+@@ -103,7 +103,7 @@ osi_TryEvictVCache(struct vcache *avc, int *slept, int defersleep)
+ }
+
+ /* See if we can evict it from the VLRUQ */
+- if (VREFCOUNT_GT(avc,0) && !VREFCOUNT_GT(avc,1) && avc->opens == 0
++ if (VREFCOUNT_GT(avc, 0) && !VREFCOUNT_GT(avc, 1) && avc->opens == 0
+ && (avc->f.states & CUnlinkedDel) == 0) {
+ int didsleep = *slept;
+
+@@ -145,17 +145,22 @@ osi_NewVnode(void)
+ }
+
+ void
+-osi_PrePopulateVCache(struct vcache *avc) {
++osi_PrePopulateVCache(struct vcache *avc)
++{
+ avc->uncred = 0;
+ memset(&(avc->f), 0, sizeof(struct fvcache));
+ avc->cred = NULL;
+ }
+
+ void
+-osi_AttachVnode(struct vcache *avc, int seq) { /* Nada */ }
++osi_AttachVnode(struct vcache *avc, int seq)
++{
++ /* Nada */
++}
+
+ void
+-osi_PostPopulateVCache(struct vcache *avc) {
++osi_PostPopulateVCache(struct vcache *avc)
++{
+ vSetType(avc, VREG);
+ }
+
+--
+2.11.0
+
--- /dev/null
+From 163fcc59b919877ee01d4fd47be46009e97a5e60 Mon Sep 17 00:00:00 2001
+From: Michael Meffie <mmeffie@sinenomine.net>
+Date: Thu, 27 Aug 2015 13:06:05 -0400
+Subject: afs: shake harder in shake-loose-vcaches
+
+Linux based cache managers will allocate vcaches on demand and
+deallocate batches of vcaches in the background. This feature is called
+dynamic vcaches.
+
+Vcaches to be deallocated are found by traversing the vcache LRU list
+(VLRU) from the oldest vcache to the newest. Up to a target number of
+vcaches are attempted to be evicted. The afs_xvcache lock protecting
+the VLRU may be dropped and re-acquired while attempting to evict a
+vcache. When this happens, it is possible the VLRU may have changed, so
+the traversal of the VLRU is restarted. This restarting of the VLRU
+transversal is limited to 100 iterations to avoid looping indefinitely.
+
+Vcaches which are busy cannot be evicted and remain in the VLRU. When a
+busy cache was not evicted and the afs_xvache lock was dropped, the VLRU
+traversal is restarted from the end of the VLRU. When the busy vcache is
+encountered on the retry, it will trigger additional retries until the
+loop limit is reached, at which point the target number of vcaches will
+not be deallocated.
+
+This can leave a very large number of unbusy vcaches which are never
+deallocated. On a busy machine, tens of millions of unused vcaches can
+remain in memory. When the busy vcache at the end of the VLRU is finally
+evicted, the log jam is broken, and the background deamon will hold the
+afs_xvcache lock for an excessively long time, hanging the system.
+
+Fix this by moving busy vcaches to the head of the VLRU before
+restarting the VLRU traversal. These busy vcaches will be skipped when
+retrying the VLRU traversal, allowing the cache manager to make progress
+deallocating vcaches down to the target level.
+
+This was already done on the mac osx platform while attempting to evict
+vcaches. Move the code to move busy vcaches to the head of the VLRU up
+the the platform agnostic caller.
+
+Thanks to Andrew Deason for the initial version of this patch.
+
+Reviewed-on: https://gerrit.openafs.org/11654
+Tested-by: BuildBot <buildbot@rampaginggeek.com>
+Reviewed-by: Andrew Deason <adeason@dson.org>
+Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
+(cherry picked from commit 5c136c7d93ed97166f39bf716cc7f5d579b70677)
+
+Change-Id: If60b1889d012a739aa5b43e842abb80a6ebfdb6a
+---
+ src/afs/DARWIN/osi_vcache.c | 5 +----
+ src/afs/afs_vcache.c | 14 +++++++++++++-
+ 2 files changed, 14 insertions(+), 5 deletions(-)
+
+diff --git a/src/afs/DARWIN/osi_vcache.c b/src/afs/DARWIN/osi_vcache.c
+index 18d8d9a08..1a1199c4c 100644
+--- a/src/afs/DARWIN/osi_vcache.c
++++ b/src/afs/DARWIN/osi_vcache.c
+@@ -53,10 +53,7 @@ osi_TryEvictVCache(struct vcache *avc, int *slept, int defersleep) {
+ * this out, since the iocount we have to hold makes it
+ * always "fail" */
+ if (AFSTOV(avc) == tvp) {
+- if (*slept) {
+- QRemove(&avc->vlruq);
+- QAdd(&VLRU, &avc->vlruq);
+- }
++ /* Caller will move this vcache to the head of the VLRU. */
+ return 0;
+ } else
+ return 1;
+diff --git a/src/afs/afs_vcache.c b/src/afs/afs_vcache.c
+index d751a564c..ca5a956ea 100644
+--- a/src/afs/afs_vcache.c
++++ b/src/afs/afs_vcache.c
+@@ -725,6 +725,7 @@ int
+ afs_ShakeLooseVCaches(afs_int32 anumber)
+ {
+ afs_int32 i, loop;
++ int evicted;
+ struct vcache *tvc;
+ struct afs_q *tq, *uq;
+ int fv_slept, defersleep = 0;
+@@ -752,12 +753,23 @@ afs_ShakeLooseVCaches(afs_int32 anumber)
+ }
+
+ fv_slept = 0;
+- if (osi_TryEvictVCache(tvc, &fv_slept, defersleep))
++ evicted = osi_TryEvictVCache(tvc, &fv_slept, defersleep);
++ if (evicted) {
+ anumber--;
++ }
+
+ if (fv_slept) {
+ if (loop++ > 100)
+ break;
++ if (!evicted) {
++ /*
++ * This vcache was busy and we slept while trying to evict it.
++ * Move this busy vcache to the head of the VLRU so vcaches
++ * following this busy vcache can be evicted during the retry.
++ */
++ QRemove(&tvc->vlruq);
++ QAdd(&VLRU, &tvc->vlruq);
++ }
+ goto retry; /* start over - may have raced. */
+ }
+ if (uq == &VLRU) {
+--
+2.11.0
+
0003-Add-dummy-exit-command-for-afsd-to-do-nothing.patch
+LINUX-split-dentry-eviction-from-osi_TryEvictVCache.patch
+Reformat-src-afs-LINUX-osi_vcache.c.patch
+LINUX-do-not-use-d_invalidate-to-evict-dentries.patch
+afs-shake-harder-in-shake-loose-vcaches.patch