| From: Greg Thelen <gthelen@google.com> |
| Date: Tue, 30 Apr 2013 15:26:48 -0700 |
| Subject: fs/dcache.c: add cond_resched() to shrink_dcache_parent() |
| |
| commit 421348f1ca0bf17769dee0aed4d991845ae0536d upstream. |
| |
| Call cond_resched() in shrink_dcache_parent() to maintain interactivity. |
| |
| Before this patch: |
| |
| void shrink_dcache_parent(struct dentry * parent) |
| { |
| while ((found = select_parent(parent, &dispose)) != 0) |
| shrink_dentry_list(&dispose); |
| } |
| |
| select_parent() populates the dispose list with dentries which |
| shrink_dentry_list() then deletes. select_parent() carefully uses |
| need_resched() to avoid doing too much work at once. But neither |
| shrink_dcache_parent() nor its called functions call cond_resched(). So |
| once need_resched() is set select_parent() will return single dentry |
| dispose list which is then deleted by shrink_dentry_list(). This is |
| inefficient when there are a lot of dentry to process. This can cause |
| softlockup and hurts interactivity on non preemptable kernels. |
| |
| This change adds cond_resched() in shrink_dcache_parent(). The benefit |
| of this is that need_resched() is quickly cleared so that future calls |
| to select_parent() are able to efficiently return a big batch of dentry. |
| |
| These additional cond_resched() do not seem to impact performance, at |
| least for the workload below. |
| |
| Here is a program which can cause soft lockup if other system activity |
| sets need_resched(). |
| |
| int main() |
| { |
| struct rlimit rlim; |
| int i; |
| int f[100000]; |
| char buf[20]; |
| struct timeval t1, t2; |
| double diff; |
| |
| /* cleanup past run */ |
| system("rm -rf x"); |
| |
| /* boost nfile rlimit */ |
| rlim.rlim_cur = 200000; |
| rlim.rlim_max = 200000; |
| if (setrlimit(RLIMIT_NOFILE, &rlim)) |
| err(1, "setrlimit"); |
| |
| /* make directory for files */ |
| if (mkdir("x", 0700)) |
| err(1, "mkdir"); |
| |
| if (gettimeofday(&t1, NULL)) |
| err(1, "gettimeofday"); |
| |
| /* populate directory with open files */ |
| for (i = 0; i < 100000; i++) { |
| snprintf(buf, sizeof(buf), "x/%d", i); |
| f[i] = open(buf, O_CREAT); |
| if (f[i] == -1) |
| err(1, "open"); |
| } |
| |
| /* close some of the files */ |
| for (i = 0; i < 85000; i++) |
| close(f[i]); |
| |
| /* unlink all files, even open ones */ |
| system("rm -rf x"); |
| |
| if (gettimeofday(&t2, NULL)) |
| err(1, "gettimeofday"); |
| |
| diff = (((double)t2.tv_sec * 1000000 + t2.tv_usec) - |
| ((double)t1.tv_sec * 1000000 + t1.tv_usec)); |
| |
| printf("done: %g elapsed\n", diff/1e6); |
| return 0; |
| } |
| |
| Signed-off-by: Greg Thelen <gthelen@google.com> |
| Signed-off-by: Dave Chinner <david@fromorbit.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| fs/dcache.c | 4 +++- |
| 1 file changed, 3 insertions(+), 1 deletion(-) |
| |
| --- a/fs/dcache.c |
| +++ b/fs/dcache.c |
| @@ -1176,8 +1176,10 @@ void shrink_dcache_parent(struct dentry |
| LIST_HEAD(dispose); |
| int found; |
| |
| - while ((found = select_parent(parent, &dispose)) != 0) |
| + while ((found = select_parent(parent, &dispose)) != 0) { |
| shrink_dentry_list(&dispose); |
| + cond_resched(); |
| + } |
| } |
| EXPORT_SYMBOL(shrink_dcache_parent); |
| |