|
@@ -1611,9 +1611,13 @@ cleanup:
|
|
|
* @page: the page
|
|
|
*
|
|
|
* This function protects unlocked LRU pages from being moved to
|
|
|
- * another cgroup and stabilizes their page->mem_cgroup binding.
|
|
|
+ * another cgroup.
|
|
|
+ *
|
|
|
+ * It ensures lifetime of the returned memcg. Caller is responsible
|
|
|
+ * for the lifetime of the page; __unlock_page_memcg() is available
|
|
|
+ * when @page might get freed inside the locked section.
|
|
|
*/
|
|
|
-void lock_page_memcg(struct page *page)
|
|
|
+struct mem_cgroup *lock_page_memcg(struct page *page)
|
|
|
{
|
|
|
struct mem_cgroup *memcg;
|
|
|
unsigned long flags;
|
|
@@ -1622,18 +1626,24 @@ void lock_page_memcg(struct page *page)
|
|
|
* The RCU lock is held throughout the transaction. The fast
|
|
|
* path can get away without acquiring the memcg->move_lock
|
|
|
* because page moving starts with an RCU grace period.
|
|
|
- */
|
|
|
+ *
|
|
|
+ * The RCU lock also protects the memcg from being freed when
|
|
|
+ * the page state that is going to change is the only thing
|
|
|
+ * preventing the page itself from being freed. E.g. writeback
|
|
|
+ * doesn't hold a page reference and relies on PG_writeback to
|
|
|
+ * keep off truncation, migration and so forth.
|
|
|
+ */
|
|
|
rcu_read_lock();
|
|
|
|
|
|
if (mem_cgroup_disabled())
|
|
|
- return;
|
|
|
+ return NULL;
|
|
|
again:
|
|
|
memcg = page->mem_cgroup;
|
|
|
if (unlikely(!memcg))
|
|
|
- return;
|
|
|
+ return NULL;
|
|
|
|
|
|
if (atomic_read(&memcg->moving_account) <= 0)
|
|
|
- return;
|
|
|
+ return memcg;
|
|
|
|
|
|
spin_lock_irqsave(&memcg->move_lock, flags);
|
|
|
if (memcg != page->mem_cgroup) {
|
|
@@ -1649,18 +1659,18 @@ again:
|
|
|
memcg->move_lock_task = current;
|
|
|
memcg->move_lock_flags = flags;
|
|
|
|
|
|
- return;
|
|
|
+ return memcg;
|
|
|
}
|
|
|
EXPORT_SYMBOL(lock_page_memcg);
|
|
|
|
|
|
/**
|
|
|
- * unlock_page_memcg - unlock a page->mem_cgroup binding
|
|
|
- * @page: the page
|
|
|
+ * __unlock_page_memcg - unlock and unpin a memcg
|
|
|
+ * @memcg: the memcg
|
|
|
+ *
|
|
|
+ * Unlock and unpin a memcg returned by lock_page_memcg().
|
|
|
*/
|
|
|
-void unlock_page_memcg(struct page *page)
|
|
|
+void __unlock_page_memcg(struct mem_cgroup *memcg)
|
|
|
{
|
|
|
- struct mem_cgroup *memcg = page->mem_cgroup;
|
|
|
-
|
|
|
if (memcg && memcg->move_lock_task == current) {
|
|
|
unsigned long flags = memcg->move_lock_flags;
|
|
|
|
|
@@ -1672,6 +1682,15 @@ void unlock_page_memcg(struct page *page)
|
|
|
|
|
|
rcu_read_unlock();
|
|
|
}
|
|
|
+
|
|
|
+/**
|
|
|
+ * unlock_page_memcg - unlock a page->mem_cgroup binding
|
|
|
+ * @page: the page
|
|
|
+ */
|
|
|
+void unlock_page_memcg(struct page *page)
|
|
|
+{
|
|
|
+ __unlock_page_memcg(page->mem_cgroup);
|
|
|
+}
|
|
|
EXPORT_SYMBOL(unlock_page_memcg);
|
|
|
|
|
|
/*
|