|
|
Log in / Subscribe / Register

The interaction between memory reclaim and RCU

Please consider subscribing to LWN

Subscriptions are the lifeblood of LWN.net. If you appreciate this content and would like to see more of it, your subscription will help to ensure that LWN continues to thrive. Please visit this page to join up and keep LWN on the net.

By Jonathan Corbet
May 22, 2024

LSFMM+BPF
The 2024 Linux Storage, Filesystem, Memory-Management and BPF Summit was a development conference, where discussion was prioritized and presentations with a lot of slides were discouraged. Paul McKenney seemingly flouted this convention in a joint session of the storage, filesystem, and memory-management tracks where he presented about 50 slides — in five minutes, twice. The subject was the use of the read-copy-update (RCU) mechanism in the memory-reclaim process, and whether changes to RCU would be needed for that purpose.

Readers who are unfamiliar with RCU may want to have a look at this article for a refresher.

After the slide deluge (for which it was not possible to take effective notes without severe keyboard damage), McKenney got to his real point: before making complicated changes to the RCU subsystem (which does not lack complexity already), a real problem with the current code will need to be demonstrated. The concern seems to be that RCU is simply too slow in getting around to freeing memory, causing the system to go into an out-of-memory state. What can be done about that?

[Paul McKenney] The question of whether RCU can keep up with the work presented to it is, he said, dependent on the workload. There can be a few reasons why it would fail; perhaps the grace periods (the delay before RCU callbacks can be run) are too infrequent, or there may be readers holding the RCU lock for too long. Kent Overstreet tried to give some background for the current topic, which is a spin-off from a lengthy pre-conference discussion on buffered-I/O performance. The buffered read path is fast now, he said, but it can be made faster by using RCU. But that means using RCU to free page-cache pages, which is a critical cleanup path in the kernel.

McKenney suggested that, as an experiment, developers could try just forcing page-free operations through RCU for no particular reason, just to see if anything breaks. Josef Bacik said that, while there are places where RCU can be improved, this use case is pushing for a solution to a problem that is not RCU's fault. Writeback from page-cache pages can take a long time; page reclaim is an unpredictable process in general.

Overstreet agreed that reclaim is a hard problem, and that a lot of different developers have responsibility for parts of it. He is a filesystem developer who finds himself having to solve reclaim problems, but the kernel lacks the sort of introspection that would help him to see where the problems are. Thus, he said, there is a need for a wider discussion about the interactions around the reclaim problem.

James Bottomley asked whether it was appropriate to use RCU in this way; perhaps there is a need to invent a new mechanism instead? McKenney answered that, instead, developers could use a different flavor of RCU, such as sleepable RCU. Steven Rostedt asked whether a new RCU flavor aimed at the reclaim problem is needed, but McKenney said he did not think that was the case.

It was this far into the session before Dave Chinner got up to ask what the problem to be solved was. The short answer is "making the buffer cache faster". Matthew Wilcox said that taking folio references for small reads is simply too expensive; RCU can be used instead to keep pages around while data is copied from them without the need to take a reference. McKenney suggested that perhaps hazard pointers could be used for this purpose. That would allow the immediate freeing of any object that is not currently referenced; RCU, instead, must wait for all readers to complete their work.

Bottomley said that the reference-count problem comes down to the cost of converting cache lines to exclusive access. If there is not actually a lot of contention for those reference counts, perhaps a different solution is called for. Overstreet answered that, even in the no-contention case, the reference-counting overhead is a problem; Wilcox suggested that Bottomley was underestimating the number of places in the kernel that take references.

McKenney tried to direct the conversation toward an understanding of the performance problem; Overstreet answered that better numbers are needed. He would like to be able to track just how much memory is waiting in the RCU system to be freed. McKenney answered that, while kvfree_call_rcu() is aware of the size of the memory block it has been asked to free, it is used infrequently. Most memory is freed using call_rcu(), and that function has no idea of how much memory it will eventually free (or whether it is freeing memory at all). There is also no per-subsystem accounting in RCU. Hannes Reinecke said that he would like to see subsystem-level accounting, along with the ability to force a grace period for a specific subsystem. The problem there, as somebody pointed out, is that the ability to free a specific range of memory may depend on other subsystems, and there is no way to know for sure.

Chinner said that this is a problem of tracking objects in flight. It is possible to count slab objects, since they know which slab they belong to and their size; it's just a matter of adding the tracking. Calls to kfree_rcu() could recognize slab objects and account for them. McKenney said that he would like to see kfree_rcu() merged into the slab allocator; slab maintainer Vlastimil Babka said that he had plans to do exactly that. Now that the SLOB allocator has been removed, he said, kernel code can pass any memory pointer to kfree() (and thus kfree_rcu()) and the right thing will happen.

As this somewhat inconclusive session came to a close, McKenney said that there were two problems to be solved. If the system is loaded with memory demands, how are those to be accounted for? And, for memory freed with call_rcu(), more information will need to be provided somehow. Overstreet got in the last word by saying that, if a kernel subsystem is using call_rcu(), the duty of performing the accounting is also there. kfree_rcu() should be used instead whenever possible.

Index entries for this article
KernelMemory management/Scalability
KernelRead-copy-update
ConferenceStorage, Filesystem, Memory-Management and BPF Summit/2024


to post comments

The interaction between memory reclaim and RCU

Posted May 22, 2024 21:02 UTC (Wed) by koverstreet (subscriber, #4296) [Link] (1 responses)

My ask is just to add a global percpu counter for "amount of memory stranded waiting for kfree_rcu() et. all to complete"

Paul mentioned the idea of per subsystem accounting; instead of doing it per subsystem, we can add it to the (just merged) memory allocation profiling - per callsite.

The interaction between memory reclaim and RCU

Posted May 23, 2024 18:19 UTC (Thu) by PaulMcKenney (✭ supporter ✭, #9624) [Link]

On your ask, understood.

Please see the "Tracking Memory Waiting on RCU" section of this document for a list of approaches that have been considered and a number of the resulting complications.

Thoughts?

Slides

Posted May 23, 2024 18:09 UTC (Thu) by PaulMcKenney (✭ supporter ✭, #9624) [Link]

The slides may be found here.

I am glad that neither Jon nor Jake injured either themselves or their keyboards during those two leisurely strolls through the slides. ;–)

There were several hours worth of discussion in the cited email thread, so the purpose of the slides was for me to quickly figure out what people wanted to focus on.

Those wanting to know more about the possibility of hazard pointers in the Linux kernel might find something of interest here. Boqun Feng is working on a prototype, in which a number of people expressed interest during subsequent hallway-track sessions. TL;DR: Situations in which performance and/or scalability is being limited by a not-zero atomic increment of a reference count might be well served by hazard pointers. (In addition to the memory-footprint motivation called out in the article.)

SRCU latency?

Posted Jun 5, 2024 21:44 UTC (Wed) by pbonzini (subscriber, #60935) [Link]

A few years ago each SRCU instance was effectively independent and synchronize_srcu_expedited (which could busy wait but is nowhere as bad as its _rcu counterpart) was guaranteed to be basically instantaneous if there was no long running usage of srcu_read_lock/unlock. This was a bit counter to the "sleepable" RCU name, but in fact I always thought of it more as a "subsystem" RCU.

Since then, SRCU has been rewritten to be more scalable in the tracking of grace periods, but the current code is... a bit impenetrable. How true is the above guarantee these days?


Copyright © 2024, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds