aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLee Schermerhorn <Lee.Schermerhorn@hp.com>2008-03-10 14:43:45 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-03-10 21:01:19 -0400
commit69682d852f5c94ee94e21174b3e8b719626c98db (patch)
treeed3c7c0681ef7658132ae32f1cd8a56bb6451621
parent9afa802ff568d935dab33dd207dc25d9849f35d4 (diff)
mempolicy: fix reference counting bugs
Address 3 known bugs in the current memory policy reference counting method. I have a series of patches to rework the reference counting to reduce overhead in the allocation path. However, that series will require testing in -mm once I repost it. 1) alloc_page_vma() does not release the extra reference taken for vma/shared mempolicy when the mode == MPOL_INTERLEAVE. This can result in leaking mempolicy structures. This is probably occurring, but not being noticed. Fix: add the conditional release of the reference. 2) hugezonelist unconditionally releases a reference on the mempolicy when mode == MPOL_INTERLEAVE. This can result in decrementing the reference count for system default policy [should have no ill effect] or premature freeing of task policy. If this occurred, the next allocation using task mempolicy would use the freed structure and probably BUG out. Fix: add the necessary check to the release. 3) The current reference counting method assumes that vma 'get_policy()' methods automatically add an extra reference a non-NULL returned mempolicy. This is true for shmem_get_policy() used by tmpfs mappings, including regular page shm segments. However, SHM_HUGETLB shm's, backed by hugetlbfs, just use the vma policy without the extra reference. This results in freeing of the vma policy on the first allocation, with reuse of the freed mempolicy structure on subsequent allocations. Fix: Rather than add another condition to the conditional reference release, which occur in the allocation path, just add a reference when returning the vma policy in shm_get_policy() to match the assumptions. Signed-off-by: Lee Schermerhorn <lee.schermerhorn@hp.com> Cc: Greg KH <greg@kroah.com> Cc: Andi Kleen <ak@suse.de> Cc: Christoph Lameter <clameter@sgi.com> Cc: Mel Gorman <mel@csn.ul.ie> Cc: David Rientjes <rientjes@google.com> Cc: <eric.whitney@hp.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--ipc/shm.c5
-rw-r--r--mm/mempolicy.c7
2 files changed, 9 insertions, 3 deletions
diff --git a/ipc/shm.c b/ipc/shm.c
index c47e87278a92..cc63fae02f06 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -271,9 +271,10 @@ static struct mempolicy *shm_get_policy(struct vm_area_struct *vma,
271 271
272 if (sfd->vm_ops->get_policy) 272 if (sfd->vm_ops->get_policy)
273 pol = sfd->vm_ops->get_policy(vma, addr); 273 pol = sfd->vm_ops->get_policy(vma, addr);
274 else if (vma->vm_policy) 274 else if (vma->vm_policy) {
275 pol = vma->vm_policy; 275 pol = vma->vm_policy;
276 else 276 mpol_get(pol); /* get_vma_policy() expects this */
277 } else
277 pol = current->mempolicy; 278 pol = current->mempolicy;
278 return pol; 279 return pol;
279} 280}
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 6c7ba1a63d23..3c3601121509 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -1296,7 +1296,9 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
1296 unsigned nid; 1296 unsigned nid;
1297 1297
1298 nid = interleave_nid(pol, vma, addr, HPAGE_SHIFT); 1298 nid = interleave_nid(pol, vma, addr, HPAGE_SHIFT);
1299 __mpol_free(pol); /* finished with pol */ 1299 if (unlikely(pol != &default_policy &&
1300 pol != current->mempolicy))
1301 __mpol_free(pol); /* finished with pol */
1300 return NODE_DATA(nid)->node_zonelists + gfp_zone(gfp_flags); 1302 return NODE_DATA(nid)->node_zonelists + gfp_zone(gfp_flags);
1301 } 1303 }
1302 1304
@@ -1360,6 +1362,9 @@ alloc_page_vma(gfp_t gfp, struct vm_area_struct *vma, unsigned long addr)
1360 unsigned nid; 1362 unsigned nid;
1361 1363
1362 nid = interleave_nid(pol, vma, addr, PAGE_SHIFT); 1364 nid = interleave_nid(pol, vma, addr, PAGE_SHIFT);
1365 if (unlikely(pol != &default_policy &&
1366 pol != current->mempolicy))
1367 __mpol_free(pol); /* finished with pol */
1363 return alloc_page_interleave(gfp, 0, nid); 1368 return alloc_page_interleave(gfp, 0, nid);
1364 } 1369 }
1365 zl = zonelist_policy(gfp, pol); 1370 zl = zonelist_policy(gfp, pol);