Re: [PATCH] mm, memcg: fix memory.peak reset clobbering other fds' watermark
Ridong Chen <[email protected]> Fri, 31 Jul 2026 10:36:42 +0800
| Newsgroups | dev.linux.lists.sashiko-reviews |
|---|---|
| Message-ID | <[email protected]> |
On 7/30/2026 8:06 PM, [email protected] wrote: > Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: > - [Medium] Lockless readers in `peak_show` can transiently observe a lost peak due to an incorrect multi-variable update order and missing memory barriers in `peak_write`. > -- > > --- Patch [1]: [PATCH] mm, memcg: fix memory.peak reset clobbering other fds' watermark --- > commit 15d8c2c42e3dd5de2e4db8178cc6e094af81f2fe > Author: Ridong Chen <[email protected]> > > mm, memcg: fix memory.peak reset clobbering other fds' watermark > > Writing to memory.peak resets the peak for that fd only. Each fd is a > watcher and reads back max(its own value, the shared local_watermark). > > peak_write() resets by lowering local_watermark to the current usage. > To keep the other watchers' peaks it then walks the watcher list, but it > stores the current usage into them instead of the old watermark. So once > usage has dropped from a peak, a reset on one fd wrongly drags every > other fd's peak down too, even fds that never reset. > >> diff --git a/mm/memcontrol.c b/mm/memcontrol.c >> index 8ff285e872ca9..7d7b6bf200805 100644 >> --- a/mm/memcontrol.c >> +++ b/mm/memcontrol.c >> @@ -4740,7 +4740,7 @@ static ssize_t peak_write(struct kernfs_open_file *of, char *buf, size_t nbytes, >> loff_t off, struct page_counter *pc, >> struct list_head *watchers) >> { >> - unsigned long usage; >> + unsigned long usage, old_watermark; >> struct cgroup_of_peak *peer_ctx; >> struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of)); >> struct cgroup_of_peak *ofp = of_peak(of); >> @@ -4748,11 +4748,12 @@ static ssize_t peak_write(struct kernfs_open_file *of, char *buf, size_t nbytes, >> spin_lock(&memcg->peaks_lock); >> >> usage = page_counter_read(pc); >> + old_watermark = READ_ONCE(pc->local_watermark); >> WRITE_ONCE(pc->local_watermark, usage); >> >> list_for_each_entry(peer_ctx, watchers, list) >> - if (usage > peer_ctx->value) >> - WRITE_ONCE(peer_ctx->value, usage); >> + if (peer_ctx != ofp && old_watermark > peer_ctx->value) >> + WRITE_ONCE(peer_ctx->value, old_watermark); > > [Severity: Medium] > Will this update order create a race window where readers can transiently > observe a lower peak? > > In mm/memcontrol.c:peak_show(), readers locklessly check these values: > > u64 fd_peak = READ_ONCE(ofp->value), peak; > > /* User wants global or local peak? */ > if (fd_peak == OFP_PEAK_UNSET) > peak = pc->watermark; > else > peak = max(fd_peak, READ_ONCE(pc->local_watermark)); > > If pc->local_watermark is lowered to usage here in peak_write() before the > watchers' peer_ctx->value fields are updated to old_watermark, can a > concurrent lockless reader in peak_show() interleave to observe the lowered > local_watermark while still reading their old lower fd_peak value? > It seems this issue exsit already, maybe we should add spin_lock(&memcg->peaks_lock) in peak_show()? > If they observe both lower values, this would temporarily violate the > monotonic property of the peak metric and return a lower peak than expected. > > Should the watchers be updated with old_watermark before lowering > pc->local_watermark to usage, or are memory barriers needed to ensure the > correct order? > >> >> /* initial write, register watcher */ >> if (ofp->value == OFP_PEAK_UNSET) > -- Best regards Ridong