|
@@ -1614,7 +1614,7 @@ void mem_cgroup_update_page_stat(struct page *page,
|
|
|
if (unlikely(!mem || !PageCgroupUsed(pc)))
|
|
|
goto out;
|
|
|
/* pc->mem_cgroup is unstable ? */
|
|
|
- if (unlikely(mem_cgroup_stealed(mem))) {
|
|
|
+ if (unlikely(mem_cgroup_stealed(mem)) || PageTransHuge(page)) {
|
|
|
/* take a lock against to access pc->mem_cgroup */
|
|
|
move_lock_page_cgroup(pc, &flags);
|
|
|
need_unlock = true;
|
|
@@ -2083,14 +2083,27 @@ struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page)
|
|
|
return mem;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * commit a charge got by __mem_cgroup_try_charge() and makes page_cgroup to be
|
|
|
- * USED state. If already USED, uncharge and return.
|
|
|
- */
|
|
|
-static void ____mem_cgroup_commit_charge(struct mem_cgroup *mem,
|
|
|
- struct page_cgroup *pc,
|
|
|
- enum charge_type ctype)
|
|
|
+static void __mem_cgroup_commit_charge(struct mem_cgroup *mem,
|
|
|
+ struct page_cgroup *pc,
|
|
|
+ enum charge_type ctype,
|
|
|
+ int page_size)
|
|
|
{
|
|
|
+ int nr_pages = page_size >> PAGE_SHIFT;
|
|
|
+
|
|
|
+ /* try_charge() can return NULL to *memcg, taking care of it. */
|
|
|
+ if (!mem)
|
|
|
+ return;
|
|
|
+
|
|
|
+ lock_page_cgroup(pc);
|
|
|
+ if (unlikely(PageCgroupUsed(pc))) {
|
|
|
+ unlock_page_cgroup(pc);
|
|
|
+ mem_cgroup_cancel_charge(mem, page_size);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * we don't need page_cgroup_lock about tail pages, becase they are not
|
|
|
+ * accessed by any other context at this point.
|
|
|
+ */
|
|
|
pc->mem_cgroup = mem;
|
|
|
/*
|
|
|
* We access a page_cgroup asynchronously without lock_page_cgroup().
|
|
@@ -2114,35 +2127,7 @@ static void ____mem_cgroup_commit_charge(struct mem_cgroup *mem,
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- mem_cgroup_charge_statistics(mem, PageCgroupCache(pc), 1);
|
|
|
-}
|
|
|
-
|
|
|
-static void __mem_cgroup_commit_charge(struct mem_cgroup *mem,
|
|
|
- struct page_cgroup *pc,
|
|
|
- enum charge_type ctype,
|
|
|
- int page_size)
|
|
|
-{
|
|
|
- int i;
|
|
|
- int count = page_size >> PAGE_SHIFT;
|
|
|
-
|
|
|
- /* try_charge() can return NULL to *memcg, taking care of it. */
|
|
|
- if (!mem)
|
|
|
- return;
|
|
|
-
|
|
|
- lock_page_cgroup(pc);
|
|
|
- if (unlikely(PageCgroupUsed(pc))) {
|
|
|
- unlock_page_cgroup(pc);
|
|
|
- mem_cgroup_cancel_charge(mem, page_size);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * we don't need page_cgroup_lock about tail pages, becase they are not
|
|
|
- * accessed by any other context at this point.
|
|
|
- */
|
|
|
- for (i = 0; i < count; i++)
|
|
|
- ____mem_cgroup_commit_charge(mem, pc + i, ctype);
|
|
|
-
|
|
|
+ mem_cgroup_charge_statistics(mem, PageCgroupCache(pc), nr_pages);
|
|
|
unlock_page_cgroup(pc);
|
|
|
/*
|
|
|
* "charge_statistics" updated event counter. Then, check it.
|
|
@@ -2152,6 +2137,34 @@ static void __mem_cgroup_commit_charge(struct mem_cgroup *mem,
|
|
|
memcg_check_events(mem, pc->page);
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
|
|
+
|
|
|
+#define PCGF_NOCOPY_AT_SPLIT ((1 << PCG_LOCK) | (1 << PCG_MOVE_LOCK) |\
|
|
|
+ (1 << PCG_ACCT_LRU) | (1 << PCG_MIGRATION))
|
|
|
+/*
|
|
|
+ * Because tail pages are not marked as "used", set it. We're under
|
|
|
+ * zone->lru_lock, 'splitting on pmd' and compund_lock.
|
|
|
+ */
|
|
|
+void mem_cgroup_split_huge_fixup(struct page *head, struct page *tail)
|
|
|
+{
|
|
|
+ struct page_cgroup *head_pc = lookup_page_cgroup(head);
|
|
|
+ struct page_cgroup *tail_pc = lookup_page_cgroup(tail);
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We have no races witch charge/uncharge but will have races with
|
|
|
+ * page state accounting.
|
|
|
+ */
|
|
|
+ move_lock_page_cgroup(head_pc, &flags);
|
|
|
+
|
|
|
+ tail_pc->mem_cgroup = head_pc->mem_cgroup;
|
|
|
+ smp_wmb(); /* see __commit_charge() */
|
|
|
+ /* we don't need to copy all flags...*/
|
|
|
+ tail_pc->flags = head_pc->flags & ~PCGF_NOCOPY_AT_SPLIT;
|
|
|
+ move_unlock_page_cgroup(head_pc, &flags);
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
/**
|
|
|
* __mem_cgroup_move_account - move account of the page
|
|
|
* @pc: page_cgroup of the page.
|
|
@@ -2545,7 +2558,6 @@ direct_uncharge:
|
|
|
static struct mem_cgroup *
|
|
|
__mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
|
|
|
{
|
|
|
- int i;
|
|
|
int count;
|
|
|
struct page_cgroup *pc;
|
|
|
struct mem_cgroup *mem = NULL;
|
|
@@ -2595,8 +2607,7 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- for (i = 0; i < count; i++)
|
|
|
- mem_cgroup_charge_statistics(mem, PageCgroupCache(pc), -1);
|
|
|
+ mem_cgroup_charge_statistics(mem, PageCgroupCache(pc), -count);
|
|
|
|
|
|
ClearPageCgroupUsed(pc);
|
|
|
/*
|