Re: [PATCH 1/2] mm: introduce memory.min

Roman Gushchin
2018-04-23T12:44:43Z

Hi Johannes!

Thanks for the review. I've just sent v2, which closely
follows your advice, really nothing contradictory.

Plus fixed some comments on top of mem_cgroup_protected
and fixed !CONFIG_MEMCG build.

Thank you!

Roman

On Fri, Apr 20, 2018 at 04:44:29PM -0400, Johannes Weiner wrote:
> Hi Roman,
>
> this looks cool, and the implementation makes sense to me, so the
> feedback I have is just smaller stuff.
>
> On Fri, Apr 20, 2018 at 05:36:31PM +0100, Roman Gushchin wrote:
> > Memory controller implements the memory.low best-effort memory
> > protection mechanism, which works perfectly in many cases and
> > allows protecting working sets of important workloads from
> > sudden reclaim.
> >
> > But it's semantics has a significant limitation: it works
>
> its
>
> > only until there is a supply of reclaimable memory.
>
> s/until/as long as/
>
> Maybe "as long as there is an unprotected supply of reclaimable memory
> from other groups"?
>
> > This makes it pretty useless against any sort of slow memory
> > leaks or memory usage increases. This is especially true
> > for swapless systems. If swap is enabled, memory soft protection
> > effectively postpones problems, allowing a leaking application
> > to fill all swap area, which makes no sense.
> > The only effective way to guarantee the memory protection
> > in this case is to invoke the OOM killer.
>
> This makes it sound like it has no purpose at all. But like with
> memory.high, the point is to avoid the kernel OOM killer, which knows
> jack about how the system is structured, and instead allow userspace
> management software to monitor MEMCG_LOW and make informed decisions.
>
> memory.min again is the fail-safe for memory.low, not the primary way
> of implementing guarantees.
>
> > @@ -297,7 +297,14 @@ static inline bool mem_cgroup_disabled(void)
> > return !cgroup_subsys_enabled(memory_cgrp_subsys);
> > }
> >
> > -bool mem_cgroup_low(struct mem_cgroup *root, struct mem_cgroup *memcg);
> > +enum mem_cgroup_protection {
> > + MEM_CGROUP_UNPROTECTED,
> > + MEM_CGROUP_PROTECTED_LOW,
> > + MEM_CGROUP_PROTECTED_MIN,
> > +};
>
> These look a bit unwieldy. How about
>
> MEMCG_PROT_NONE,
> MEMCG_PROT_LOW,
> MEMCG_PROT_HIGH,
>
> > +enum mem_cgroup_protection
> > +mem_cgroup_protected(struct mem_cgroup *root, struct mem_cgroup *memcg);
>
> Please don't wrap at the return type, wrap the parameter list instead.
>
> > int mem_cgroup_try_charge(struct page *page, struct mm_struct *mm,
> > gfp_t gfp_mask, struct mem_cgroup **memcgp,
> > @@ -756,6 +763,12 @@ static inline void memcg_memory_event(struct mem_cgroup *memcg,
> > {
> > }
> >
> > +static inline bool mem_cgroup_min(struct mem_cgroup *root,
> > + struct mem_cgroup *memcg)
> > +{
> > + return false;
> > +}
> > +
> > static inline bool mem_cgroup_low(struct mem_cgroup *root,
> > struct mem_cgroup *memcg)
> > {
>
> The real implementation has these merged into the single
> mem_cgroup_protected(). Probably a left-over from earlier versions?
>
> It's always a good idea to build test the !CONFIG_MEMCG case too.
>
> > @@ -5685,44 +5723,71 @@ struct cgroup_subsys memory_cgrp_subsys = {
> > * for next usage. This part is intentionally racy, but it's ok,
> > * as memory.low is a best-effort mechanism.
> > */
> > -bool mem_cgroup_low(struct mem_cgroup *root, struct mem_cgroup *memcg)
> > +enum mem_cgroup_protection
> > +mem_cgroup_protected(struct mem_cgroup *root, struct mem_cgroup *memcg)
>
> Please fix the wrapping here too.
>
> > @@ -2525,12 +2525,29 @@ static bool shrink_node(pg_data_t *pgdat, struct scan_control *sc)
> > unsigned long reclaimed;
> > unsigned long scanned;
> >
> > - if (mem_cgroup_low(root, memcg)) {
> > + switch (mem_cgroup_protected(root, memcg)) {
> > + case MEM_CGROUP_PROTECTED_MIN:
> > + /*
> > + * Hard protection.
> > + * If there is no reclaimable memory, OOM.
> > + */
> > + continue;
> > +
> > + case MEM_CGROUP_PROTECTED_LOW:
>
> Please drop that newline after continue.
>
> > + /*
> > + * Soft protection.
> > + * Respect the protection only until there is
> > + * a supply of reclaimable memory.
>
> Same feedback here as in the changelog:
>
> s/until/as long as/
>
> Maybe "as long as there is an unprotected supply of reclaimable memory
> from other groups"?
>
> > + */
> > if (!sc->memcg_low_reclaim) {
> > sc->memcg_low_skipped = 1;
> > continue;
> > }
> > memcg_memory_event(memcg, MEMCG_LOW);
> > + break;
> > +
> > + case MEM_CGROUP_UNPROTECTED:
>
> Please drop that newline after break, too.
>
> Thanks!
> Johannes