diff options
author | Mel Gorman <mgorman@suse.de> | 2012-12-05 17:01:41 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-06 14:56:43 -0500 |
commit | 18a2f371f5edf41810f6469cb9be39931ef9deb9 (patch) | |
tree | 4e4ec26f13273b36fc7203d2084ea09f14c5f0f7 | |
parent | c702418f8a2fa6cc92e84a39880d458faf7af9cc (diff) |
tmpfs: fix shared mempolicy leak
This fixes a regression in 3.7-rc, which has since gone into stable.
Commit 00442ad04a5e ("mempolicy: fix a memory corruption by refcount
imbalance in alloc_pages_vma()") changed get_vma_policy() to raise the
refcount on a shmem shared mempolicy; whereas shmem_alloc_page() went
on expecting alloc_page_vma() to drop the refcount it had acquired.
This deserves a rework: but for now fix the leak in shmem_alloc_page().
Hugh: shmem_swapin() did not need a fix, but surely it's clearer to use
the same refcounting there as in shmem_alloc_page(), delete its onstack
mempolicy, and the strange mpol_cond_copy() and __mpol_cond_copy() -
those were invented to let swapin_readahead() make an unknown number of
calls to alloc_pages_vma() with one mempolicy; but since 00442ad04a5e,
alloc_pages_vma() has kept refcount in balance, so now no problem.
Reported-and-tested-by: Tommi Rantala <tt.rantala@gmail.com>
Signed-off-by: Mel Gorman <mgorman@suse.de>
Signed-off-by: Hugh Dickins <hughd@google.com>
Cc: stable@vger.kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | include/linux/mempolicy.h | 16 | ||||
-rw-r--r-- | mm/mempolicy.c | 22 | ||||
-rw-r--r-- | mm/shmem.c | 26 |
3 files changed, 16 insertions, 48 deletions
diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h index e5ccb9ddd90e..dbd212723b74 100644 --- a/include/linux/mempolicy.h +++ b/include/linux/mempolicy.h | |||
@@ -82,16 +82,6 @@ static inline void mpol_cond_put(struct mempolicy *pol) | |||
82 | __mpol_put(pol); | 82 | __mpol_put(pol); |
83 | } | 83 | } |
84 | 84 | ||
85 | extern struct mempolicy *__mpol_cond_copy(struct mempolicy *tompol, | ||
86 | struct mempolicy *frompol); | ||
87 | static inline struct mempolicy *mpol_cond_copy(struct mempolicy *tompol, | ||
88 | struct mempolicy *frompol) | ||
89 | { | ||
90 | if (!frompol) | ||
91 | return frompol; | ||
92 | return __mpol_cond_copy(tompol, frompol); | ||
93 | } | ||
94 | |||
95 | extern struct mempolicy *__mpol_dup(struct mempolicy *pol); | 85 | extern struct mempolicy *__mpol_dup(struct mempolicy *pol); |
96 | static inline struct mempolicy *mpol_dup(struct mempolicy *pol) | 86 | static inline struct mempolicy *mpol_dup(struct mempolicy *pol) |
97 | { | 87 | { |
@@ -215,12 +205,6 @@ static inline void mpol_cond_put(struct mempolicy *pol) | |||
215 | { | 205 | { |
216 | } | 206 | } |
217 | 207 | ||
218 | static inline struct mempolicy *mpol_cond_copy(struct mempolicy *to, | ||
219 | struct mempolicy *from) | ||
220 | { | ||
221 | return from; | ||
222 | } | ||
223 | |||
224 | static inline void mpol_get(struct mempolicy *pol) | 208 | static inline void mpol_get(struct mempolicy *pol) |
225 | { | 209 | { |
226 | } | 210 | } |
diff --git a/mm/mempolicy.c b/mm/mempolicy.c index d04a8a54c294..4ea600da8940 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c | |||
@@ -2037,28 +2037,6 @@ struct mempolicy *__mpol_dup(struct mempolicy *old) | |||
2037 | return new; | 2037 | return new; |
2038 | } | 2038 | } |
2039 | 2039 | ||
2040 | /* | ||
2041 | * If *frompol needs [has] an extra ref, copy *frompol to *tompol , | ||
2042 | * eliminate the * MPOL_F_* flags that require conditional ref and | ||
2043 | * [NOTE!!!] drop the extra ref. Not safe to reference *frompol directly | ||
2044 | * after return. Use the returned value. | ||
2045 | * | ||
2046 | * Allows use of a mempolicy for, e.g., multiple allocations with a single | ||
2047 | * policy lookup, even if the policy needs/has extra ref on lookup. | ||
2048 | * shmem_readahead needs this. | ||
2049 | */ | ||
2050 | struct mempolicy *__mpol_cond_copy(struct mempolicy *tompol, | ||
2051 | struct mempolicy *frompol) | ||
2052 | { | ||
2053 | if (!mpol_needs_cond_ref(frompol)) | ||
2054 | return frompol; | ||
2055 | |||
2056 | *tompol = *frompol; | ||
2057 | tompol->flags &= ~MPOL_F_SHARED; /* copy doesn't need unref */ | ||
2058 | __mpol_put(frompol); | ||
2059 | return tompol; | ||
2060 | } | ||
2061 | |||
2062 | /* Slow path of a mempolicy comparison */ | 2040 | /* Slow path of a mempolicy comparison */ |
2063 | bool __mpol_equal(struct mempolicy *a, struct mempolicy *b) | 2041 | bool __mpol_equal(struct mempolicy *a, struct mempolicy *b) |
2064 | { | 2042 | { |
diff --git a/mm/shmem.c b/mm/shmem.c index 89341b658bd0..50c5b8f3a359 100644 --- a/mm/shmem.c +++ b/mm/shmem.c | |||
@@ -910,25 +910,29 @@ static struct mempolicy *shmem_get_sbmpol(struct shmem_sb_info *sbinfo) | |||
910 | static struct page *shmem_swapin(swp_entry_t swap, gfp_t gfp, | 910 | static struct page *shmem_swapin(swp_entry_t swap, gfp_t gfp, |
911 | struct shmem_inode_info *info, pgoff_t index) | 911 | struct shmem_inode_info *info, pgoff_t index) |
912 | { | 912 | { |
913 | struct mempolicy mpol, *spol; | ||
914 | struct vm_area_struct pvma; | 913 | struct vm_area_struct pvma; |
915 | 914 | struct page *page; | |
916 | spol = mpol_cond_copy(&mpol, | ||
917 | mpol_shared_policy_lookup(&info->policy, index)); | ||
918 | 915 | ||
919 | /* Create a pseudo vma that just contains the policy */ | 916 | /* Create a pseudo vma that just contains the policy */ |
920 | pvma.vm_start = 0; | 917 | pvma.vm_start = 0; |
921 | /* Bias interleave by inode number to distribute better across nodes */ | 918 | /* Bias interleave by inode number to distribute better across nodes */ |
922 | pvma.vm_pgoff = index + info->vfs_inode.i_ino; | 919 | pvma.vm_pgoff = index + info->vfs_inode.i_ino; |
923 | pvma.vm_ops = NULL; | 920 | pvma.vm_ops = NULL; |
924 | pvma.vm_policy = spol; | 921 | pvma.vm_policy = mpol_shared_policy_lookup(&info->policy, index); |
925 | return swapin_readahead(swap, gfp, &pvma, 0); | 922 | |
923 | page = swapin_readahead(swap, gfp, &pvma, 0); | ||
924 | |||
925 | /* Drop reference taken by mpol_shared_policy_lookup() */ | ||
926 | mpol_cond_put(pvma.vm_policy); | ||
927 | |||
928 | return page; | ||
926 | } | 929 | } |
927 | 930 | ||
928 | static struct page *shmem_alloc_page(gfp_t gfp, | 931 | static struct page *shmem_alloc_page(gfp_t gfp, |
929 | struct shmem_inode_info *info, pgoff_t index) | 932 | struct shmem_inode_info *info, pgoff_t index) |
930 | { | 933 | { |
931 | struct vm_area_struct pvma; | 934 | struct vm_area_struct pvma; |
935 | struct page *page; | ||
932 | 936 | ||
933 | /* Create a pseudo vma that just contains the policy */ | 937 | /* Create a pseudo vma that just contains the policy */ |
934 | pvma.vm_start = 0; | 938 | pvma.vm_start = 0; |
@@ -937,10 +941,12 @@ static struct page *shmem_alloc_page(gfp_t gfp, | |||
937 | pvma.vm_ops = NULL; | 941 | pvma.vm_ops = NULL; |
938 | pvma.vm_policy = mpol_shared_policy_lookup(&info->policy, index); | 942 | pvma.vm_policy = mpol_shared_policy_lookup(&info->policy, index); |
939 | 943 | ||
940 | /* | 944 | page = alloc_page_vma(gfp, &pvma, 0); |
941 | * alloc_page_vma() will drop the shared policy reference | 945 | |
942 | */ | 946 | /* Drop reference taken by mpol_shared_policy_lookup() */ |
943 | return alloc_page_vma(gfp, &pvma, 0); | 947 | mpol_cond_put(pvma.vm_policy); |
948 | |||
949 | return page; | ||
944 | } | 950 | } |
945 | #else /* !CONFIG_NUMA */ | 951 | #else /* !CONFIG_NUMA */ |
946 | #ifdef CONFIG_TMPFS | 952 | #ifdef CONFIG_TMPFS |