mm, memcg: rework remote charging API to support nesting
commitb87d8cefe4
upstream. Currently the remote memcg charging API consists of two functions: memalloc_use_memcg() and memalloc_unuse_memcg(), which set and clear the memcg value, which overwrites the memcg of the current task. memalloc_use_memcg(target_memcg); <...> memalloc_unuse_memcg(); It works perfectly for allocations performed from a normal context, however an attempt to call it from an interrupt context or just nest two remote charging blocks will lead to an incorrect accounting. On exit from the inner block the active memcg will be cleared instead of being restored. memalloc_use_memcg(target_memcg); memalloc_use_memcg(target_memcg_2); <...> memalloc_unuse_memcg(); Error: allocation here are charged to the memcg of the current process instead of target_memcg. memalloc_unuse_memcg(); This patch extends the remote charging API by switching to a single function: struct mem_cgroup *set_active_memcg(struct mem_cgroup *memcg), which sets the new value and returns the old one. So a remote charging block will look like: old_memcg = set_active_memcg(target_memcg); <...> set_active_memcg(old_memcg); This patch is heavily based on the patch by Johannes Weiner, which can be found here: https://lkml.org/lkml/2020/5/28/806 . Intel-SIG: commitb87d8cefe4
mm, memcg: rework remote charging API to support nesting Backport for SGX virtualization support on Intel Xeon platform. Signed-off-by: Roman Gushchin <guro@fb.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Shakeel Butt <shakeelb@google.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Dan Schatzberg <dschatzberg@fb.com> Link: https://lkml.kernel.org/r/20200821212056.3769116-1-guro@fb.com Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> [ Zhiquan Li: amend commit log and resolve the conflict. The memalloc_use_memcg/memalloc_unuse_memcg() call in mm/memcontrol.c have not been introduced in v5.4, so discards the changes. Since the APIs have been changed to set_active_memcg(), it will be noticed if old APIs call be backported in future. ] Signed-off-by: Zhiquan Li <zhiquan1.li@intel.com>
This commit is contained in:
parent
2704b599da
commit
6eff319e75
|
@ -817,13 +817,13 @@ struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size,
|
||||||
struct buffer_head *bh, *head;
|
struct buffer_head *bh, *head;
|
||||||
gfp_t gfp = GFP_NOFS | __GFP_ACCOUNT;
|
gfp_t gfp = GFP_NOFS | __GFP_ACCOUNT;
|
||||||
long offset;
|
long offset;
|
||||||
struct mem_cgroup *memcg;
|
struct mem_cgroup *memcg, *old_memcg;
|
||||||
|
|
||||||
if (retry)
|
if (retry)
|
||||||
gfp |= __GFP_NOFAIL;
|
gfp |= __GFP_NOFAIL;
|
||||||
|
|
||||||
memcg = get_mem_cgroup_from_page(page);
|
memcg = get_mem_cgroup_from_page(page);
|
||||||
memalloc_use_memcg(memcg);
|
old_memcg = set_active_memcg(memcg);
|
||||||
|
|
||||||
head = NULL;
|
head = NULL;
|
||||||
offset = PAGE_SIZE;
|
offset = PAGE_SIZE;
|
||||||
|
@ -842,7 +842,7 @@ struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size,
|
||||||
set_bh_page(bh, page, offset);
|
set_bh_page(bh, page, offset);
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
memalloc_unuse_memcg();
|
set_active_memcg(old_memcg);
|
||||||
mem_cgroup_put(memcg);
|
mem_cgroup_put(memcg);
|
||||||
return head;
|
return head;
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -290,6 +290,7 @@ struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
|
||||||
struct fanotify_event *event = NULL;
|
struct fanotify_event *event = NULL;
|
||||||
gfp_t gfp = GFP_KERNEL_ACCOUNT;
|
gfp_t gfp = GFP_KERNEL_ACCOUNT;
|
||||||
struct inode *id = fanotify_fid_inode(inode, mask, data, data_type);
|
struct inode *id = fanotify_fid_inode(inode, mask, data, data_type);
|
||||||
|
struct mem_cgroup *old_memcg;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For queues with unlimited length lost events are not expected and
|
* For queues with unlimited length lost events are not expected and
|
||||||
|
@ -303,7 +304,7 @@ struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
|
||||||
gfp |= __GFP_RETRY_MAYFAIL;
|
gfp |= __GFP_RETRY_MAYFAIL;
|
||||||
|
|
||||||
/* Whoever is interested in the event, pays for the allocation. */
|
/* Whoever is interested in the event, pays for the allocation. */
|
||||||
memalloc_use_memcg(group->memcg);
|
old_memcg = set_active_memcg(group->memcg);
|
||||||
|
|
||||||
if (fanotify_is_perm_event(mask)) {
|
if (fanotify_is_perm_event(mask)) {
|
||||||
struct fanotify_perm_event *pevent;
|
struct fanotify_perm_event *pevent;
|
||||||
|
@ -345,7 +346,7 @@ init: __maybe_unused
|
||||||
event->path.dentry = NULL;
|
event->path.dentry = NULL;
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
memalloc_unuse_memcg();
|
set_active_memcg(old_memcg);
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,7 @@ int inotify_handle_event(struct fsnotify_group *group,
|
||||||
int ret;
|
int ret;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
int alloc_len = sizeof(struct inotify_event_info);
|
int alloc_len = sizeof(struct inotify_event_info);
|
||||||
|
struct mem_cgroup *old_memcg;
|
||||||
|
|
||||||
if (WARN_ON(fsnotify_iter_vfsmount_mark(iter_info)))
|
if (WARN_ON(fsnotify_iter_vfsmount_mark(iter_info)))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -95,9 +96,9 @@ int inotify_handle_event(struct fsnotify_group *group,
|
||||||
* trigger OOM killer in the target monitoring memcg as it may have
|
* trigger OOM killer in the target monitoring memcg as it may have
|
||||||
* security repercussion.
|
* security repercussion.
|
||||||
*/
|
*/
|
||||||
memalloc_use_memcg(group->memcg);
|
old_memcg = set_active_memcg(group->memcg);
|
||||||
event = kmalloc(alloc_len, GFP_KERNEL_ACCOUNT | __GFP_RETRY_MAYFAIL);
|
event = kmalloc(alloc_len, GFP_KERNEL_ACCOUNT | __GFP_RETRY_MAYFAIL);
|
||||||
memalloc_unuse_memcg();
|
set_active_memcg(old_memcg);
|
||||||
|
|
||||||
if (unlikely(!event)) {
|
if (unlikely(!event)) {
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -313,38 +313,28 @@ static inline void memalloc_nocma_restore(unsigned int flags)
|
||||||
|
|
||||||
#ifdef CONFIG_MEMCG
|
#ifdef CONFIG_MEMCG
|
||||||
/**
|
/**
|
||||||
* memalloc_use_memcg - Starts the remote memcg charging scope.
|
* set_active_memcg - Starts the remote memcg charging scope.
|
||||||
* @memcg: memcg to charge.
|
* @memcg: memcg to charge.
|
||||||
*
|
*
|
||||||
* This function marks the beginning of the remote memcg charging scope. All the
|
* This function marks the beginning of the remote memcg charging scope. All the
|
||||||
* __GFP_ACCOUNT allocations till the end of the scope will be charged to the
|
* __GFP_ACCOUNT allocations till the end of the scope will be charged to the
|
||||||
* given memcg.
|
* given memcg.
|
||||||
*
|
*
|
||||||
* NOTE: This function is not nesting safe.
|
* NOTE: This function can nest. Users must save the return value and
|
||||||
|
* reset the previous value after their own charging scope is over.
|
||||||
*/
|
*/
|
||||||
static inline void memalloc_use_memcg(struct mem_cgroup *memcg)
|
static inline struct mem_cgroup *
|
||||||
|
set_active_memcg(struct mem_cgroup *memcg)
|
||||||
{
|
{
|
||||||
WARN_ON_ONCE(current->active_memcg);
|
struct mem_cgroup *old = current->active_memcg;
|
||||||
current->active_memcg = memcg;
|
current->active_memcg = memcg;
|
||||||
}
|
return old;
|
||||||
|
|
||||||
/**
|
|
||||||
* memalloc_unuse_memcg - Ends the remote memcg charging scope.
|
|
||||||
*
|
|
||||||
* This function marks the end of the remote memcg charging scope started by
|
|
||||||
* memalloc_use_memcg().
|
|
||||||
*/
|
|
||||||
static inline void memalloc_unuse_memcg(void)
|
|
||||||
{
|
|
||||||
current->active_memcg = NULL;
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static inline void memalloc_use_memcg(struct mem_cgroup *memcg)
|
static inline struct mem_cgroup *
|
||||||
{
|
set_active_memcg(struct mem_cgroup *memcg)
|
||||||
}
|
|
||||||
|
|
||||||
static inline void memalloc_unuse_memcg(void)
|
|
||||||
{
|
{
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue