|
@@ -31,6 +31,7 @@
|
|
|
#include <linux/memcontrol.h>
|
|
|
#include <linux/gfp.h>
|
|
|
#include <linux/uio.h>
|
|
|
+#include <linux/hugetlb.h>
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
|
@@ -81,6 +82,19 @@ static void __put_compound_page(struct page *page)
|
|
|
|
|
|
static void put_compound_page(struct page *page)
|
|
|
{
|
|
|
+ /*
|
|
|
+ * hugetlbfs pages cannot be split from under us. If this is a
|
|
|
+ * hugetlbfs page, check refcount on head page and release the page if
|
|
|
+ * the refcount becomes zero.
|
|
|
+ */
|
|
|
+ if (PageHuge(page)) {
|
|
|
+ page = compound_head(page);
|
|
|
+ if (put_page_testzero(page))
|
|
|
+ __put_compound_page(page);
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
if (unlikely(PageTail(page))) {
|
|
|
/* __split_huge_page_refcount can run under us */
|
|
|
struct page *page_head = compound_trans_head(page);
|
|
@@ -184,38 +198,51 @@ bool __get_page_tail(struct page *page)
|
|
|
* proper PT lock that already serializes against
|
|
|
* split_huge_page().
|
|
|
*/
|
|
|
- unsigned long flags;
|
|
|
bool got = false;
|
|
|
- struct page *page_head = compound_trans_head(page);
|
|
|
+ struct page *page_head;
|
|
|
|
|
|
- if (likely(page != page_head && get_page_unless_zero(page_head))) {
|
|
|
+ /*
|
|
|
+ * If this is a hugetlbfs page it cannot be split under us. Simply
|
|
|
+ * increment refcount for the head page.
|
|
|
+ */
|
|
|
+ if (PageHuge(page)) {
|
|
|
+ page_head = compound_head(page);
|
|
|
+ atomic_inc(&page_head->_count);
|
|
|
+ got = true;
|
|
|
+ } else {
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ page_head = compound_trans_head(page);
|
|
|
+ if (likely(page != page_head &&
|
|
|
+ get_page_unless_zero(page_head))) {
|
|
|
+
|
|
|
+ /* Ref to put_compound_page() comment. */
|
|
|
+ if (PageSlab(page_head)) {
|
|
|
+ if (likely(PageTail(page))) {
|
|
|
+ __get_page_tail_foll(page, false);
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ put_page(page_head);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- /* Ref to put_compound_page() comment. */
|
|
|
- if (PageSlab(page_head)) {
|
|
|
+ /*
|
|
|
+ * page_head wasn't a dangling pointer but it
|
|
|
+ * may not be a head page anymore by the time
|
|
|
+ * we obtain the lock. That is ok as long as it
|
|
|
+ * can't be freed from under us.
|
|
|
+ */
|
|
|
+ flags = compound_lock_irqsave(page_head);
|
|
|
+ /* here __split_huge_page_refcount won't run anymore */
|
|
|
if (likely(PageTail(page))) {
|
|
|
__get_page_tail_foll(page, false);
|
|
|
- return true;
|
|
|
- } else {
|
|
|
- put_page(page_head);
|
|
|
- return false;
|
|
|
+ got = true;
|
|
|
}
|
|
|
+ compound_unlock_irqrestore(page_head, flags);
|
|
|
+ if (unlikely(!got))
|
|
|
+ put_page(page_head);
|
|
|
}
|
|
|
-
|
|
|
- /*
|
|
|
- * page_head wasn't a dangling pointer but it
|
|
|
- * may not be a head page anymore by the time
|
|
|
- * we obtain the lock. That is ok as long as it
|
|
|
- * can't be freed from under us.
|
|
|
- */
|
|
|
- flags = compound_lock_irqsave(page_head);
|
|
|
- /* here __split_huge_page_refcount won't run anymore */
|
|
|
- if (likely(PageTail(page))) {
|
|
|
- __get_page_tail_foll(page, false);
|
|
|
- got = true;
|
|
|
- }
|
|
|
- compound_unlock_irqrestore(page_head, flags);
|
|
|
- if (unlikely(!got))
|
|
|
- put_page(page_head);
|
|
|
}
|
|
|
return got;
|
|
|
}
|