aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-06-17 22:05:36 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-06-17 22:24:11 -0400
commitdd34739c03f2f9a79403d33419c2e61e11b4c403 (patch)
tree74e4e43b78d54994e88aef7df96f0f8f4b080934 /mm
parenteee2acbae95555006307395d8a6c91452d62851d (diff)
mm: avoid anon_vma_chain allocation under anon_vma lock
Hugh Dickins points out that lockdep (correctly) spots a potential deadlock on the anon_vma lock, because we now do a GFP_KERNEL allocation of anon_vma_chain while doing anon_vma_clone(). The problem is that page reclaim will want to take the anon_vma lock of any anonymous pages that it will try to reclaim. So re-organize the code in anon_vma_clone() slightly: first do just a GFP_NOWAIT allocation, which will usually work fine. But if that fails, let's just drop the lock and re-do the allocation, now with GFP_KERNEL. End result: not only do we avoid the locking problem, this also ends up getting better concurrency in case the allocation does need to block. Tim Chen reports that with all these anon_vma locking tweaks, we're now almost back up to the spinlock performance. Reported-and-tested-by: Hugh Dickins <hughd@google.com> Tested-by: Tim Chen <tim.c.chen@linux.intel.com> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Andi Kleen <ak@linux.intel.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/rmap.c20
1 files changed, 12 insertions, 8 deletions
diff --git a/mm/rmap.c b/mm/rmap.c
index 68756a77ef87..27dfd3b82b0f 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -112,9 +112,9 @@ static inline void anon_vma_free(struct anon_vma *anon_vma)
112 kmem_cache_free(anon_vma_cachep, anon_vma); 112 kmem_cache_free(anon_vma_cachep, anon_vma);
113} 113}
114 114
115static inline struct anon_vma_chain *anon_vma_chain_alloc(void) 115static inline struct anon_vma_chain *anon_vma_chain_alloc(gfp_t gfp)
116{ 116{
117 return kmem_cache_alloc(anon_vma_chain_cachep, GFP_KERNEL); 117 return kmem_cache_alloc(anon_vma_chain_cachep, gfp);
118} 118}
119 119
120static void anon_vma_chain_free(struct anon_vma_chain *anon_vma_chain) 120static void anon_vma_chain_free(struct anon_vma_chain *anon_vma_chain)
@@ -159,7 +159,7 @@ int anon_vma_prepare(struct vm_area_struct *vma)
159 struct mm_struct *mm = vma->vm_mm; 159 struct mm_struct *mm = vma->vm_mm;
160 struct anon_vma *allocated; 160 struct anon_vma *allocated;
161 161
162 avc = anon_vma_chain_alloc(); 162 avc = anon_vma_chain_alloc(GFP_KERNEL);
163 if (!avc) 163 if (!avc)
164 goto out_enomem; 164 goto out_enomem;
165 165
@@ -253,9 +253,14 @@ int anon_vma_clone(struct vm_area_struct *dst, struct vm_area_struct *src)
253 list_for_each_entry_reverse(pavc, &src->anon_vma_chain, same_vma) { 253 list_for_each_entry_reverse(pavc, &src->anon_vma_chain, same_vma) {
254 struct anon_vma *anon_vma; 254 struct anon_vma *anon_vma;
255 255
256 avc = anon_vma_chain_alloc(); 256 avc = anon_vma_chain_alloc(GFP_NOWAIT | __GFP_NOWARN);
257 if (!avc) 257 if (unlikely(!avc)) {
258 goto enomem_failure; 258 unlock_anon_vma_root(root);
259 root = NULL;
260 avc = anon_vma_chain_alloc(GFP_KERNEL);
261 if (!avc)
262 goto enomem_failure;
263 }
259 anon_vma = pavc->anon_vma; 264 anon_vma = pavc->anon_vma;
260 root = lock_anon_vma_root(root, anon_vma); 265 root = lock_anon_vma_root(root, anon_vma);
261 anon_vma_chain_link(dst, avc, anon_vma); 266 anon_vma_chain_link(dst, avc, anon_vma);
@@ -264,7 +269,6 @@ int anon_vma_clone(struct vm_area_struct *dst, struct vm_area_struct *src)
264 return 0; 269 return 0;
265 270
266 enomem_failure: 271 enomem_failure:
267 unlock_anon_vma_root(root);
268 unlink_anon_vmas(dst); 272 unlink_anon_vmas(dst);
269 return -ENOMEM; 273 return -ENOMEM;
270} 274}
@@ -294,7 +298,7 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma)
294 anon_vma = anon_vma_alloc(); 298 anon_vma = anon_vma_alloc();
295 if (!anon_vma) 299 if (!anon_vma)
296 goto out_error; 300 goto out_error;
297 avc = anon_vma_chain_alloc(); 301 avc = anon_vma_chain_alloc(GFP_KERNEL);
298 if (!avc) 302 if (!avc)
299 goto out_error_free_anon_vma; 303 goto out_error_free_anon_vma;
300 304