aboutsummaryrefslogtreecommitdiffstats
path: root/mm/swap.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/swap.c')
-rw-r--r--mm/swap.c77
1 files changed, 52 insertions, 25 deletions
diff --git a/mm/swap.c b/mm/swap.c
index 62b78a6e224f..c899502d3e36 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -31,6 +31,7 @@
31#include <linux/memcontrol.h> 31#include <linux/memcontrol.h>
32#include <linux/gfp.h> 32#include <linux/gfp.h>
33#include <linux/uio.h> 33#include <linux/uio.h>
34#include <linux/hugetlb.h>
34 35
35#include "internal.h" 36#include "internal.h"
36 37
@@ -81,6 +82,19 @@ static void __put_compound_page(struct page *page)
81 82
82static void put_compound_page(struct page *page) 83static void put_compound_page(struct page *page)
83{ 84{
85 /*
86 * hugetlbfs pages cannot be split from under us. If this is a
87 * hugetlbfs page, check refcount on head page and release the page if
88 * the refcount becomes zero.
89 */
90 if (PageHuge(page)) {
91 page = compound_head(page);
92 if (put_page_testzero(page))
93 __put_compound_page(page);
94
95 return;
96 }
97
84 if (unlikely(PageTail(page))) { 98 if (unlikely(PageTail(page))) {
85 /* __split_huge_page_refcount can run under us */ 99 /* __split_huge_page_refcount can run under us */
86 struct page *page_head = compound_trans_head(page); 100 struct page *page_head = compound_trans_head(page);
@@ -184,38 +198,51 @@ bool __get_page_tail(struct page *page)
184 * proper PT lock that already serializes against 198 * proper PT lock that already serializes against
185 * split_huge_page(). 199 * split_huge_page().
186 */ 200 */
187 unsigned long flags;
188 bool got = false; 201 bool got = false;
189 struct page *page_head = compound_trans_head(page); 202 struct page *page_head;
190 203
191 if (likely(page != page_head && get_page_unless_zero(page_head))) { 204 /*
205 * If this is a hugetlbfs page it cannot be split under us. Simply
206 * increment refcount for the head page.
207 */
208 if (PageHuge(page)) {
209 page_head = compound_head(page);
210 atomic_inc(&page_head->_count);
211 got = true;
212 } else {
213 unsigned long flags;
214
215 page_head = compound_trans_head(page);
216 if (likely(page != page_head &&
217 get_page_unless_zero(page_head))) {
218
219 /* Ref to put_compound_page() comment. */
220 if (PageSlab(page_head)) {
221 if (likely(PageTail(page))) {
222 __get_page_tail_foll(page, false);
223 return true;
224 } else {
225 put_page(page_head);
226 return false;
227 }
228 }
192 229
193 /* Ref to put_compound_page() comment. */ 230 /*
194 if (PageSlab(page_head)) { 231 * page_head wasn't a dangling pointer but it
232 * may not be a head page anymore by the time
233 * we obtain the lock. That is ok as long as it
234 * can't be freed from under us.
235 */
236 flags = compound_lock_irqsave(page_head);
237 /* here __split_huge_page_refcount won't run anymore */
195 if (likely(PageTail(page))) { 238 if (likely(PageTail(page))) {
196 __get_page_tail_foll(page, false); 239 __get_page_tail_foll(page, false);
197 return true; 240 got = true;
198 } else {
199 put_page(page_head);
200 return false;
201 } 241 }
242 compound_unlock_irqrestore(page_head, flags);
243 if (unlikely(!got))
244 put_page(page_head);
202 } 245 }
203
204 /*
205 * page_head wasn't a dangling pointer but it
206 * may not be a head page anymore by the time
207 * we obtain the lock. That is ok as long as it
208 * can't be freed from under us.
209 */
210 flags = compound_lock_irqsave(page_head);
211 /* here __split_huge_page_refcount won't run anymore */
212 if (likely(PageTail(page))) {
213 __get_page_tail_foll(page, false);
214 got = true;
215 }
216 compound_unlock_irqrestore(page_head, flags);
217 if (unlikely(!got))
218 put_page(page_head);
219 } 246 }
220 return got; 247 return got;
221} 248}