From 8385c7ad23809d7b987b38b69e727fadaf8bc825 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Wed, 30 Nov 2016 04:51:33 -0500 Subject: [PATCH] Add shake-harder patches Signed-off-by: Anders Kaseorg --- debian/changelog | 6 + ...t-use-d_invalidate-to-evict-dentries.patch | 63 +++++++ ...try-eviction-from-osi_TryEvictVCache.patch | 164 ++++++++++++++++++ .../Reformat-src-afs-LINUX-osi_vcache.c.patch | 89 ++++++++++ ...-shake-harder-in-shake-loose-vcaches.patch | 109 ++++++++++++ debian/patches/series | 4 + 6 files changed, 435 insertions(+) create mode 100644 debian/patches/LINUX-do-not-use-d_invalidate-to-evict-dentries.patch create mode 100644 debian/patches/LINUX-split-dentry-eviction-from-osi_TryEvictVCache.patch create mode 100644 debian/patches/Reformat-src-afs-LINUX-osi_vcache.c.patch create mode 100644 debian/patches/afs-shake-harder-in-shake-loose-vcaches.patch diff --git a/debian/changelog b/debian/changelog index 5b402a452..5ada66794 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,12 @@ openafs (1.6.20-2) UNRELEASED; urgency=medium * 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 Thu, 01 Dec 2016 00:13:01 -0500 diff --git a/debian/patches/LINUX-do-not-use-d_invalidate-to-evict-dentries.patch b/debian/patches/LINUX-do-not-use-d_invalidate-to-evict-dentries.patch new file mode 100644 index 000000000..3b754ce89 --- /dev/null +++ b/debian/patches/LINUX-do-not-use-d_invalidate-to-evict-dentries.patch @@ -0,0 +1,63 @@ +From 83a174de588c56cedbf13e6abab786fb56e8082f Mon Sep 17 00:00:00 2001 +From: Mark Vitale +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 +Tested-by: Benjamin Kaduk +(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 + diff --git a/debian/patches/LINUX-split-dentry-eviction-from-osi_TryEvictVCache.patch b/debian/patches/LINUX-split-dentry-eviction-from-osi_TryEvictVCache.patch new file mode 100644 index 000000000..9de17f1db --- /dev/null +++ b/debian/patches/LINUX-split-dentry-eviction-from-osi_TryEvictVCache.patch @@ -0,0 +1,164 @@ +From cb2e2c26dc841bb4cab2d3b2ff376936f6c39e68 Mon Sep 17 00:00:00 2001 +From: Mark Vitale +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 +Tested-by: BuildBot +Reviewed-by: Joe Gorse +(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 + diff --git a/debian/patches/Reformat-src-afs-LINUX-osi_vcache.c.patch b/debian/patches/Reformat-src-afs-LINUX-osi_vcache.c.patch new file mode 100644 index 000000000..ee03ba9e1 --- /dev/null +++ b/debian/patches/Reformat-src-afs-LINUX-osi_vcache.c.patch @@ -0,0 +1,89 @@ +From f31b673509664714b146f23be069b69be7e1ac89 Mon Sep 17 00:00:00 2001 +From: Benjamin Kaduk +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 +Tested-by: BuildBot +(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 + #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 + diff --git a/debian/patches/afs-shake-harder-in-shake-loose-vcaches.patch b/debian/patches/afs-shake-harder-in-shake-loose-vcaches.patch new file mode 100644 index 000000000..5ca1f283a --- /dev/null +++ b/debian/patches/afs-shake-harder-in-shake-loose-vcaches.patch @@ -0,0 +1,109 @@ +From 163fcc59b919877ee01d4fd47be46009e97a5e60 Mon Sep 17 00:00:00 2001 +From: Michael Meffie +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 +Reviewed-by: Andrew Deason +Reviewed-by: Benjamin Kaduk +(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 + diff --git a/debian/patches/series b/debian/patches/series index 58bc02e5f..722ab5864 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1 +1,5 @@ 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 -- 2.39.5