Re: [RFC-PATCH 2/4] mm: Add __rcu_alloc_page_lockless() func.
From: Mel Gorman
Date: Wed Sep 23 2020 - 19:23:03 EST
On Wed, Sep 23, 2020 at 08:41:05AM -0700, Paul E. McKenney wrote:
> > Fundamentally, this is simply shifting the problem from RCU to the page
> > allocator because of the locking arrangements and hazard of acquiring zone
> > lock is a raw spinlock is held on RT. It does not even make the timing
> > predictable as an empty PCU list (for example, a full drain in low memory
> > situations) may mean the emergency path is hit anyway. About all it changes
> > is the timing of when the emergency path is hit in some circumstances --
> > it's not fixing the problem, it's simply changing the shape.
>
> All good points!
>
> On the other hand, duplicating a portion of the allocator functionality
> within RCU increases the amount of reserved memory, and needlessly most
> of the time.
>
But it's very similar to what mempools are for.
> Is there some way that we can locklessly allocate memory, but return
> failure instead of running down the emergency pool? A change to the loop
> that iterates over the migration types? Or to the loop that iterates
> over the zones? Something else?
>
Only by duplicating some of the logic in get_page_from_freelist would
protect the reserves. Even if you had that, the contents of the pcp
pools can easily be zero for the local CPU and you still get stuck.
> > > > Mimicing a similar implementation shouldn't be all that hard
> > > > and you will get your own pool which doesn't affect other page allocator
> > > > users as much as a bonus.
> > > >
> > > I see your point Michal. As i mentioned before, it is important to avoid of
> > > having such own pools, because the aim is not to waste memory resources. A
> > > page will be returned back to "page allocator" as soon as a scheduler place
> > > our reclaim thread on a CPU and grace period is passed. So, the resource
> > > can be used for other needs. What is important.
> >
> > As the emergency path and synchronising can be hit no matter what, why
> > not increase the pool temporarily after the emergency path is hit and
> > shrink it again later if necessary?
>
> If I understand what you are suggesting, this is in fact what Uladzislau's
> prototyped commit 8c0a1269709d ("rcu/tree: Add a work to allocate
> pages from regular context") on the -rcu "dev" branch is intended to do.
> The issue, as Uladislau noted above, is that scheduler delays can prevent
> these pool-increase actions until the point at which there is no memory.
>
Scheduler latency would be a problem. You would have to keep an emergency
"rescue" pool that is enough to make slow progress even if no other memory
is available until the worker takes action. The worker that allocates
pages from regular context would be to increase the pool size enough so
the emergency reserve does not have to be used again in the near future.
> > > Otherwise a memory footprint is increased what is bad for low memory
> > > conditions when OOM is involved.
> >
> > OOM would only be a major factor if the size of the pools meant the
> > machine could not even operate or at least was severely degraded. However,
> > depleting the PCPU lists for RCU may slow kswapd making reclaim progress
> > and cause an OOM in itself, or at least an intervention by a userspace
> > monitor that kills non-critical applications in the background when memory
> > pressure exists.
>
> When under emergency conditions, we have one page allocated per 500
> objects passed to kvfree_rcu(). So the increase in total allocated
> memory load due to this emergency path is quite small.
>
Fair enough but any solution that depends on the PCP having even one
page available is going to need a fallback option of some sort.
> > > > > As for memory overhead, it is important to reduce it because of
> > > > > embedded devices like phones, where a low memory condition is a
> > > > > big issue. In that sense pre-allocating is something that we strongly
> > > > > would like to avoid.
> > > >
> > > > How big "machines" are we talking about here? I would expect that really
> > > > tiny machines would have hard times to really fill up thousands of pages
> > > > with pointers to free...
> > > >
> > > I mentioned above. We can not rely on static model. We would like to
> > > have a mechanism that gives back ASAP used pages to page allocator
> > > for other needs.
> >
> > After an emergency, temporarily increase the size of the pool to avoid
> > hitting the emergency path again in the near future.
>
> By which time we might well already be in OOM territory. The emergency
> situations can ramp up very quickly.
>
And if the pcp lists are empty, this problem still exists -- pretty much
anything that depends on the pcp lists always having pages is going to
have a failure case.
> > > > Would a similar scaling as the page allocator feasible. Really I mostly
> > > > do care about shared nature of the pcp allocator list that one user can
> > > > easily monopolize with this API.
> > > >
> > > I see your concern. pcplist can be monopolized by already existing API:
> > >
> > > while (i < 100)
> > > __get_free_page(GFP_NOWAIT | __GFP_NOWARN);
> >
> > That's not the same class of abuse as it can go to the buddy lists to
> > refill the correct PCP lists, avoid fragmentation issues, obeys watermarks
> > and wakes kswapd if it's not awake already.
>
> Good point, and we did try doing it this way. Unfortunately, in current
> !PREEMPT kernels, this approach can deadlock on one of the allocator
> locks via call_rcu(). This experience caused us to look at lockless
> allocator access. It also inspired the unconditional PREEMPT_COUNT
> approach, but that has its own detractors. (Yes, we are of course still
> persuing it as well.)
>
I didn't keep up to date unfortunately but pretty much anything that
needs guaranteed access to pages has to maintain a mempool of some sort.
Even if the page allocator had a common pool for emergency use, it would
be subject to abuse because all it would take is one driver or subsystem
deciding that it was special enough to deplete it. For example, the ml4
driver decided that it should declare itself to be a "memory allocator"
like kswapd for iSCSI (it claims swap-over-nfs, but swap-over-nfs was
only meant to apply when a swapfile was backed by a network fs).
--
Mel Gorman
SUSE Labs