Skip to content

Commit b87d8ce

Browse files
rgushchintorvalds
authored andcommitted
mm, memcg: rework remote charging API to support nesting
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 . Signed-off-by: Roman Gushchin <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Reviewed-by: Shakeel Butt <[email protected]> Cc: Johannes Weiner <[email protected]> Cc: Dan Schatzberg <[email protected]> Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Linus Torvalds <[email protected]>
1 parent 7404840 commit b87d8ce

File tree

5 files changed

+22
-30
lines changed

5 files changed

+22
-30
lines changed

fs/buffer.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -842,13 +842,13 @@ struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size,
842842
struct buffer_head *bh, *head;
843843
gfp_t gfp = GFP_NOFS | __GFP_ACCOUNT;
844844
long offset;
845-
struct mem_cgroup *memcg;
845+
struct mem_cgroup *memcg, *old_memcg;
846846

847847
if (retry)
848848
gfp |= __GFP_NOFAIL;
849849

850850
memcg = get_mem_cgroup_from_page(page);
851-
memalloc_use_memcg(memcg);
851+
old_memcg = set_active_memcg(memcg);
852852

853853
head = NULL;
854854
offset = PAGE_SIZE;
@@ -867,7 +867,7 @@ struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size,
867867
set_bh_page(bh, page, offset);
868868
}
869869
out:
870-
memalloc_unuse_memcg();
870+
set_active_memcg(old_memcg);
871871
mem_cgroup_put(memcg);
872872
return head;
873873
/*

fs/notify/fanotify/fanotify.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
531531
struct inode *dirid = fanotify_dfid_inode(mask, data, data_type, dir);
532532
const struct path *path = fsnotify_data_path(data, data_type);
533533
unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
534+
struct mem_cgroup *old_memcg;
534535
struct inode *child = NULL;
535536
bool name_event = false;
536537

@@ -580,7 +581,7 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
580581
gfp |= __GFP_RETRY_MAYFAIL;
581582

582583
/* Whoever is interested in the event, pays for the allocation. */
583-
memalloc_use_memcg(group->memcg);
584+
old_memcg = set_active_memcg(group->memcg);
584585

585586
if (fanotify_is_perm_event(mask)) {
586587
event = fanotify_alloc_perm_event(path, gfp);
@@ -608,7 +609,7 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
608609
event->pid = get_pid(task_tgid(current));
609610

610611
out:
611-
memalloc_unuse_memcg();
612+
set_active_memcg(old_memcg);
612613
return event;
613614
}
614615

fs/notify/inotify/inotify_fsnotify.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ static int inotify_one_event(struct fsnotify_group *group, u32 mask,
6666
int ret;
6767
int len = 0;
6868
int alloc_len = sizeof(struct inotify_event_info);
69+
struct mem_cgroup *old_memcg;
6970

7071
if ((inode_mark->mask & FS_EXCL_UNLINK) &&
7172
path && d_unlinked(path->dentry))
@@ -87,9 +88,9 @@ static int inotify_one_event(struct fsnotify_group *group, u32 mask,
8788
* trigger OOM killer in the target monitoring memcg as it may have
8889
* security repercussion.
8990
*/
90-
memalloc_use_memcg(group->memcg);
91+
old_memcg = set_active_memcg(group->memcg);
9192
event = kmalloc(alloc_len, GFP_KERNEL_ACCOUNT | __GFP_RETRY_MAYFAIL);
92-
memalloc_unuse_memcg();
93+
set_active_memcg(old_memcg);
9394

9495
if (unlikely(!event)) {
9596
/*

include/linux/sched/mm.h

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -280,38 +280,28 @@ static inline void memalloc_nocma_restore(unsigned int flags)
280280

281281
#ifdef CONFIG_MEMCG
282282
/**
283-
* memalloc_use_memcg - Starts the remote memcg charging scope.
283+
* set_active_memcg - Starts the remote memcg charging scope.
284284
* @memcg: memcg to charge.
285285
*
286286
* This function marks the beginning of the remote memcg charging scope. All the
287287
* __GFP_ACCOUNT allocations till the end of the scope will be charged to the
288288
* given memcg.
289289
*
290-
* NOTE: This function is not nesting safe.
290+
* NOTE: This function can nest. Users must save the return value and
291+
* reset the previous value after their own charging scope is over.
291292
*/
292-
static inline void memalloc_use_memcg(struct mem_cgroup *memcg)
293+
static inline struct mem_cgroup *
294+
set_active_memcg(struct mem_cgroup *memcg)
293295
{
294-
WARN_ON_ONCE(current->active_memcg);
296+
struct mem_cgroup *old = current->active_memcg;
295297
current->active_memcg = memcg;
296-
}
297-
298-
/**
299-
* memalloc_unuse_memcg - Ends the remote memcg charging scope.
300-
*
301-
* This function marks the end of the remote memcg charging scope started by
302-
* memalloc_use_memcg().
303-
*/
304-
static inline void memalloc_unuse_memcg(void)
305-
{
306-
current->active_memcg = NULL;
298+
return old;
307299
}
308300
#else
309-
static inline void memalloc_use_memcg(struct mem_cgroup *memcg)
310-
{
311-
}
312-
313-
static inline void memalloc_unuse_memcg(void)
301+
static inline struct mem_cgroup *
302+
set_active_memcg(struct mem_cgroup *memcg)
314303
{
304+
return NULL;
315305
}
316306
#endif
317307

mm/memcontrol.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5290,12 +5290,12 @@ static struct cgroup_subsys_state * __ref
52905290
mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
52915291
{
52925292
struct mem_cgroup *parent = mem_cgroup_from_css(parent_css);
5293-
struct mem_cgroup *memcg;
5293+
struct mem_cgroup *memcg, *old_memcg;
52945294
long error = -ENOMEM;
52955295

5296-
memalloc_use_memcg(parent);
5296+
old_memcg = set_active_memcg(parent);
52975297
memcg = mem_cgroup_alloc();
5298-
memalloc_unuse_memcg();
5298+
set_active_memcg(old_memcg);
52995299
if (IS_ERR(memcg))
53005300
return ERR_CAST(memcg);
53015301

0 commit comments

Comments
 (0)