LWN.net Weekly Edition for July 30, 2026
Welcome to the LWN.net Weekly Edition for July 30, 2026
This edition contains the following feature content:
- Hazard pointers for the kernel: an alternative approach to lockless data updates that may offer better performance.
- A report from Debian's new DFSG team: how the ftpmaster team split is working out for the project so far.
- An operations structure for swap devices: different approaches to an abstraction layer for swap storage.
- Additional coverage from the 2026 Linux Storage, Filesystem, Memory Management, and BPF Summit:
- An update on netkit and the use of BPF in user space: the work to allow zero-copy networking within virtual machines continues.
- Debugging information for inlined functions: adding information about inlined functions to BTF.
- Fedora approves a smaller GRUB: FESCo gives the go-ahead to a slimmed-down GRUB package for confidential computing and other use cases.
- Progress toward compiling Linux with gccrs: a look at how the Rust frontend for GCC is progressing.
This week's edition also includes these inner pages:
- Brief items: Brief news items from throughout the community.
- Announcements: Newsletters, conferences, security updates, patches, and more.
Please enjoy this week's edition, and, as always, thank you for supporting LWN.net.
Hazard pointers for the kernel
The kernel's read-copy-update (RCU) subsystem ensures that data will not be deleted until it is known that there are no threads holding references to it. RCU works well and is widely used throughout the kernel, but it can increase memory use and add significant delays before unused kernel objects are cleaned up. Hazard pointers are an alternative approach to lockless data updates that offers better performance, for some situations at least. The kernel community is currently considering a hazard-pointer implementation by Mathieu Desnoyers and Paul McKenney.Like RCU, hazard pointers are meant to be a way to hold a short-lived reference to an immutable object that may disappear once all references are gone. Code holding references to RCU-protected data must disable preemption; hazard pointers, instead, appear to be designed to allow preemption, though such use may not be entirely optimal.
The hazard-pointer API
At the API level, code using hazard pointers must allocate a context (struct hazptr_ctx) for each pointer that will be in use at the same time; this structure is normally placed on the stack. If there is a pointer (we'll call it resource) to an object to be protected by a hazard pointer, and code needs to access that object, the pointer must first be acquired with a call to:
void *hazptr_acquire(struct hazptr_ctx *ctx, void * const *address);
Where ctx is the above-described context, and address, in this case, would be the address of resource. This call will return the current value of resource (the address of the protected object) and ensure that this object will not be changed or deleted for as long as the reference remains. When work with the protected object is complete, the code must call:
void hazptr_release(struct hazptr_ctx *ctx, void *address);
Where address is the address of the old copy of the resource; after this call, the previously protected object can no longer be used.
Normally, a hazard pointer must be released in the same execution context in which it was obtained — in the same thread or interrupt handler, in other words. There may be times when it is necessary to release the pointer in a different setting, though. That can be done, but only if the hazard-pointer context is passed to this function:
void hazptr_detach(struct hazptr_ctx *ctx);
This call must, clearly, be made before the pointer is actually released.
On the producer side, when the time comes to replace the protected object with a new one, code should create and initialize the new object, aim the resource pointer at this new copy, then call:
void hazptr_synchronize(void *address);
This call, which must be made in a preemptible context, will wait until there are no more hazard-pointer references to the given address, then return to the caller. At that point, the object at that address can be freed.
The implementation
The core idea behind hazard pointers is relatively simple: a call to hazptr_acquire() adds the pointer to a special list, while hazptr_release() removes it from that list. When a call to hazptr_synchronize() is made, that list is scanned for the address in question; if the address is found there, the function will wait until it is removed. This algorithm could be implemented with a simple linked list protected by a lock, but the whole purpose is to maximize performance, so the actual implementation is somewhat more complicated.
The hazard-pointer code maintains a global per-CPU array, with four slots on each CPU. The oversimplified explanation of the algorithm is that, on a call to hazptr_acquire(), an empty slot is found, and the relevant address is stored there. Calls to hazptr_synchronize() can then simply scan those slots (on each CPU) and wait until none of them contain the protected address. But, once again, there are complications.
One of those is an ordering problem. hazptr_acquire() must read the pointer to acquire, then store it into the slot. On the synchronize side, that pointer must be changed, then the slots searched for the previous value. If the synchronization code runs between the two acquire operations — after the pointer is read, but before it is stored into a slot — it will conclude that there are no references and release an object that is still in use. That is not the sort of hazard the authors of this code care to face.
To address this problem, the slots are maintained in three different states. If the address stored there is NULL, the slot is free and not protecting a pointer. If it contains a non-NULL pointer value, the slot is occupied protecting that pointer. But there is a third value, HAZPTR_WILDCARD (which happens to have the value 0x1UL) to indicate that the slot is in the process of being assigned. hazptr_acquire() starts by finding a free slot and setting its value to HAZPTR_WILDCARD; only then does it read the address value and, subsequently, store it in the slot. hazptr_synchronize() treats any slot containing HAZPTR_WILDCARD as if it contained the pointer it is looking for, so it will wait until the real pointer value appears in that slot before returning. That extra check prevents the race described above.
The other complication is: what happens if there is a need for more than four slots? Any given function may not need so many slots, but there is no knowing how many will be used by functions further down the call chain. The hazard-pointer API could just return an error in that case, but that seems like a sure way to create hard-to-find bugs. Instead, handling this case is what the hazptr_ctx structure is for.
That structure contains a spare slot that can be used to hold the hazard pointer if none of the per-CPU slots are available. In that case, the code uses the slot in the context structure, then links that structure into a per-CPU list. When a hazptr_synchronize() call happens, it must search those per-CPU lists as well as the per-CPU slots to ensure that the address is not under protection. As an added twist, there are actually two per-CPU linked lists; one is available for adding to while the other is available for searching, again to prevent race conditions. The list traversal risks slowing everything down, but those lists should almost always be empty.
The overflow slot has a couple of uses beyond extending the four per-CPU slots. The hazptr_detach() call described above will immediately move the given pointer into the overflow slot (if it is not already there), freeing the per-CPU slot for other uses. There is also a special callback added to the scheduler that is called on context switches; that one moves all of the per-CPU slots to their corresponding overflow slots. In this way, if a thread is preempted while using hazard-pointer slots, it will free the faster per-CPU slots for whoever runs next.
This code is still in a relatively early state, and could yet evolve somewhat before finding its way into the mainline. Importantly, the patch series does not include any users of the API, which is normally a requirement for a new subsystem like this. The creation of those users may well reveal API shortcomings that can be resolved before merging upstream. So it is hard to hazard a guess as to when hazard pointers will be available for use by kernel developers.
A report from Debian's new DFSG team
The DFSG, Licensing & New Packages Team (usually shortened to "DFSG team") was created in October 2025 as part of the ftpmaster team split. Its job is to review packages in the new queue for compliance with the Debian Free Software Guidelines (DFSG), among other things, before the packages are allowed to enter the Debian archive. The change was long in coming, and some questions remained after the split whether it was the right move. Andrew McMillan provided an overview of the team's activities and its current status during DebConf26. While it may be too early to say with certainty, his report suggests that the new division of duties is working out well.
I did not attend DebConf26, but I did watch the recording of McMillan's talk, which is available in WebM format (HD quality, low-resolution video). Slides from the presentation have not yet been made available.
Ftpmaster split
For more than two decades, Debian's ftpmaster team was responsible for allowing new packages to enter Debian, removing old packages, and otherwise maintaining Debian's archive. At times, packages languished in the new queue longer than Debian developers thought that they should, and the team was seen by some as a bottleneck that was in need of attention.
The idea of refactoring the team was brought up in the "meet the ftpteam" BoF (notes) at DebConf24. On October 3, 2025, Debian Project Leader (DPL) Andreas Tille announced his plan to split the ftpmaster team into two parts: the Archive Operations Team, which handles the infrastructure supporting the Debian archives, and the DFSG team. Tille pulled the trigger on the change on October 26, 2025; he created delegations for the new teams and revoked the delegation for the ftpmaster team, thereby dissolving it.
The first delegation for the DFSG team was composed entirely of former members of the ftpmaster team: Thorsten Alteholz, Ansgar Burchardt, Joerg Jaspert, and Luke Faraone, all of whom were also appointed to the new Archive team. On January 19, 2026, Tille appointed a new delegation to the DFSG team consisting of McMillan, Emmanuel Arias, Nicolas Mora, Mechtilde Stehmann, and Reinhard Tartler.
Team introduction
McMillan said that the talk was supposed to be delivered jointly with Arias
and Tartler; however, Tartler had needed to fly back to New York, and Arias was
in attendance but not presenting as he is "famously a very shy person
",
though he would be happy to field questions in Spanish if there were any. "In
English, I'll do the talking, I guess.
" Mora and Stehmann were going to join
remotely, he said later, but the network-connectivity problems in the
presentation room made that impossible.
The team name was quite a mouthful, he acknowledged, but while "DFSG team" is easier to say it also misses some of the team's responsibilities. He thought it was important to point out that the team reviews packages for compliance with packaging standards and so forth, in addition to ensuring that the packaged software meets the Debian Free Software Guidelines.
The DFSG team does not replace the ftpmaster team, he said: "We just do a very
specific task, which was largely done by Thorsten Alteholz in the past.
" Alteholz
had done a great job, "but it was more than one person can handle
". The DFSG
team does things much more in parallel, and tries not to get blocked on any
particular package. "And if somebody's not available, then we can work around
that.
" McMillan also talked about what the DFSG team does not do: it does not fully
replace the ftpmaster team. Processing of packages into Debian's stable release, the
security and backports repositories, and other archives
are handled by the Archive team.
He displayed a slide with the text: "Thesis: a package isn't free until someone
checks
". McMillan said that the DFSG team has to check that a package is free
software before it can enter the archive; the team also reviews packages headed for
Debian's contrib
and non-free
repositories. "We review those as well with less-stringent requirements, as is
appropriate for non-free.
"
There are two types of packages that pass through the hands of the
DFSG team: genuinely new source packages that have never been or are
not currently in Debian. There are also "binary new" packages which,
he explained, which have new names due to a version increment or
similar changes. "We tend to review the binary new packages much
more leniently, but we still find packages that have been around in
Debian for 15 years that have issues with their licensing
".
In those cases, the team often accepts the package with a note asking
the packager to make the needed changes. "We would rather accept a package which
has some minor changes needed, and we'll just ask you to make the minor changes after
it's entered the archive.
" That saves a lot of round-tripping, he added.
McMillan detailed some of the things that the DFSG team looks for during a review;
licensing in the debian/copyright
file that differs from the headers in the package's source files, non-free assets
bundled into the package, or other problems. Sometimes, he said, "people put
something into the Debian copyright [file] that seems to be a license, but it actually is
just verbiage about the license that's put on a web page or something
". In those
cases, the team has to ask the packager to include the actual license in debian/copyright.
Most packages come through using the copyright
format 1.0 specification, which defines a standard, machine-interpretable format for
the debian/copyright file. The format is better known as DEP-5, as it was
proposed as a Debian Enhancement
Proposal (DEP) in 2007: "DEP-5:
Machine-readable debian/copyright". It was published
as an optional format in 2012. McMillan hoped that everyone in the audience was
familiar with the DEP-5 format; after the audience seemed to indicate that it
was, he said, "good, we want to make it mandatory for new packages to be
DEP-5
".
He said that the DEP-5 requirement was likely to be introduced before the
end of this year. "We might actually make it actually apply retroactively that it
is a bug for a package not to have the copyright in DEP-5 format.
" That would
help people who are managing large sets of computers who want to know the licensing
that's being used in an organization. "Software bill of materials people, and that
sort of thing.
" It's unclear how many packages are not using DEP-5 format or
might not be fully compliant.
Dashboard
Developers who are interested in the progress of their packages, or everyone's packages, through the new queue can follow along using the DFSG team's Dashboard. McMillan said that the dashboard is a simple web presentation of the queue with an iceberg's worth of complexity underneath it.
The Archive team uses the Debian
Archive Kit (dak) collection of scripts to manage the archive. The DFSG team
discussed using those tools as well, he said, but decided that it would be better to
have its own software with a separate interface. "So we interact with dak, our
software interacts with dak, but we don't have to learn how dak works. That can be
somebody else's problem.
"
Instead of using the dak tools, he said that the DFSG team uses
software that is focused on its process. He displayed a slide with
some example uses of dnq,
the command-line client the team has developed to process new
packages. For instance dnq list displays packages that
are ready to be reviewed, dnq fetch is used to grab the
package, dnq assign packagename will
assign a package to the reviewer, etc. The
dnq complete accept command finalizes the review
and turns it over to dak, which sends out the final email to the
packager. "Or, now and again, somebody might get a reject
[email]. Hopefully nobody in this room.
"
The team is still learning from one another, he said: "We kind of all have
slightly different workflows because we're a new team and we're evolving.
"
Members of the team have different kinds of skills, he said: Stehmann, for example,
has a strong legal background and is good at reading through licenses to assess what is,
or is not, compatible with the DFSG.
Priorities and results
Each team member chooses which packages to look at, McMillan said: "The ones
that come to the top of my list tend to be languages that I'm more familiar with,
like Go. And the ones that go to the bottom of my list tend to be languages that I'm
less familiar with, like Python.
" Conversely, Arias is more familiar with Python,
so he is more likely to review those packages. A package can also be
designated for team review if it happens to be tricky in some way, such as big
package that has been around since the 1990s with "a very hairy set of authors and
copyrights
".
There were more than 750 packages in the queue when the new DFSG team took over,
he said; now there are normally fewer than 30. The team started by
prioritizing the oldest packages first, "but also with a balance to get
important packages
". New binary packages, for example, are given a higher
priority than new source packages. Packages with high popularity contest (popcon) scores are also
prioritized.
The team normally processes packages within two or three days, McMillan said,
"if it takes more than a week for your package to get through, you've hopefully
had some communication with the reviewer in that process
". He encouraged
packagers to email the team if it seemed that a review was taking too long.
Even though the team is processing the new queue more quickly, McMillan said
that the team could use more hands and invited people to talk to the team if they were
interested in joining. He said he would ultimately like to have a system that put more of the
workload of accepting new packages onto Debian developers in general. He envisioned
"some kind of quorum sort of structure
" that would allow anyone with upload
rights to the Debian archive to endorse a package as being good. If it received
enough votes, it would just be accepted.
There was a lot of discussion last year at DebConf, McMillan said, "about the
difficulty with getting [new] packages accepted into Debian, and I think this year
we're not talking about that
". He opened the floor for questions, but the audience
did not offer any, so he ended the session a bit early.
An operations structure for swap devices
One of the ideas raised at the 2026 Linux Storage, Filesystem, Memory Management, and BPF Summit (LSFMM+BPF) was the creation of an operations structure for the swap subsystem. Like many parts of the kernel, the swap layer evolved over time, with pieces being added as needed; the end result of this evolution is rarely what one would expect had the subsystem been designed today. The interface between the swap layer and the devices it uses is just one example. It appears that one result of the swap subsystem's evolution — the lack of an abstraction layer to interface with underlying storage — will soon be addressed, but in a different way than was initially envisioned.In the early days, the kernel was only able to swap to a dedicated partition on a local disk drive. If that partition turned out to be too small — and it always turned out that way in the end — the only way to fix it was, normally, to repartition the entire disk. The eventual addition of the ability to use a file within a filesystem as swap space made life easier for system administrators; later still, the kernel gained the ability to swap to networked filesystems. Within the swap layer, each of these possible swap backends is coded as a set of special cases where their behavior differs. This lack of abstraction at that level makes the swap subsystem harder to maintain and improve.
A a patch series meant to improve this situation, created by Baoquan He, had been in the works for some time. The interface between the swap layer and the backend would be defined by this structure:
struct swap_ops {
void (*read_folio)(struct swap_info_struct *sis, struct folio *folio,
struct swap_iocb **plug);
void (*write_folio)(struct swap_info_struct *sis, struct folio *folio,
struct swap_iocb **plug);
void (*unplug)(struct swap_iocb *sio);
};
The purpose of the read_folio() and write_folio() functions is fairly obvious: they are called to move one or more pages between memory and the underlying swap device. The unplug() function existed to start a batch of accumulated I/O operations. Together, this set of operations was able to hide the differences between swap backends from the rest of the swap subsystem.
In He's series, there were three swap_ops structures defined, for three different backend cases:
- bdev_async_swap_ops is for the normal case of swapping to a block device. Operations happen asynchronously, since block devices can take some time to carry them out. It is worth noting that the case of swapping to a file within a local filesystem is also normally handled by these operations. When a swap file is added, the kernel asks the filesystem to specify where the file's blocks have been placed, then bypasses the filesystem for I/O thereafter.
- bdev_sync_swap_ops is also for a local block device, but the operations are synchronous; the functions will not return until the requested operations are complete. This is the "swap bypass" case, which was described in this article; in short, for fast, memory-based devices, the operations come down to memory copies, so there is no point in performing them asynchronously.
- bdev_fs_swap_ops is a set of operations that call into a filesystem to perform the actual I/O. As noted above, this case is not for local filesystems. It is needed, though, for swapping to files on network filesystems.
This series had been through seven revisions and appeared to be on track for merging into the mainline. After LSFMM+BPF, though, Christoph Hellwig showed up with a somewhat different approach to the problem. His series is focused on increasing the batching of swap-I/O operations for better performance. As part of that effort, it adds an operations structure that is inspired by He's work, but which takes a different approach:
struct swap_ops {
unsigned int flags;
bool (*can_merge)(struct folio *folio, struct folio *prev_folio,
size_t prev_folio_size, int rw);
void (*submit_write)(struct swap_io_ctx *ctx);
void (*submit_read)(struct swap_io_ctx *ctx);
};
The can_merge() function determines whether the I/O operations for two folios can be merged into a single, larger operation. It has to determine whether the storage for the two folios is logically adjacent on the backing store, so the calculation must be different for different backing devices. submit_write() and submit_read() are for initiating I/O, with the details of the operation gathered together into the ctx argument. There is only one possible flags value, SWAP_OPS_F_REQUIRE_NOFS, which indicates that any reclaim operations must not call into the filesystem, since that could generate a recursive call into the filesystem hosting the swap file.
In Hellwig's patch set, there are only two operations structures defined: swap_bdev_ops for block-device backends, and swap_fs_ops for filesystem-backed backends. The synchronous swap-bypass case is handled with tests in the block-device implementation, as with current kernels.
This series is on its fifth revision, and seems to be mostly ready, though it may not come together in time for the 7.3 merge window. This work, it seems, has run into a problem that is showing up with increasing frequency in the kernel community: the Sashiko review tool has few complaints about the patches themselves, but finds a couple of pre-existing problems in the code that the patches change. There is a natural desire to see those problems fixed, but delaying new work to fix them is not always a popular decision. Whether that will happen in this case is yet to be determined.
An update on netkit and the use of BPF in user space
Daniel Borkmann led a session at the 2026 Linux Filesystem, Memory-Management, and BPF Summit about the progress that has been made with netkit, the subsystem that allows virtual machines (VMs) running on Linux to perform networking efficiently. When that did not fill the full time, he went on to discuss his idea for using BPF to live-patch user-space applications. While netkit is making progress, and can now support zero-copy receipt of packets into a VM in a network namespace, the idea of using BPF for patching user-space programs remains entirely speculative.
There have long been ways for VMs to accelerate their networking or access physical networking devices. Those approaches, however, have not been compatible with the use of network namespaces. That combination is needed by KubeVirt, a Kubernetes setup that uses virtual machines for isolation and network namespaces to set networking policies. Eventually, Borkmann would like to let virtual machines do true zero-copy networking. A critical step toward that is the recently merged support for queue leasing.
Queue leasing enables zero-copy networking by allowing the virtual machine manager (VMM) to create a queue on the VM's virtual networking device, and then bind it to a real queue on a host device. Right now, that is only working for receive queues — meaning that when a packet arrives to the physical network interface card (NIC), the driver can place it directly in the memory that corresponds to the virtual NIC's queue, and therefore have it be accessible to the VM directly.
The support for receive-queue leasing is fairly robust; it already works with other performance optimizations such as huge pages or BIG TCP, provided that the physical NIC supports them. There is a need for some self-tests to ensure that these things keep working, Borkmann said. On the transmit side, there are some additional complications that will be more work to address.
The underlying design for transmit-queue leasing is nearly identical: allowing the VM to place outgoing packets directly in memory accessible to the physical NIC. John Fastabend asked whether the transmit side would need a BPF program to enforce policy. Borkmann replied that, for netkit, there would be such a BPF program installed even in the absence of queue leasing. The thing is, not all physical devices support the kind of direct memory access that would be needed for transmit-queue leasing. There was a patch from Bobby Eshleman (merged on May 18), Borkmann said, that would extend the device memory transmit configuration property to indicate whether the physical device supports this kind of access; then netkit just needs to be extended to pass through that property from the underlying physical device.
Allowing VMs to pass data directly to devices like this would not allow them to circumvent network namespace policies because the packet header still goes through the normal process, including any BPF programs that have been set up to filter or redirect packets. It is only the data part of the packet — the part that is expensive to copy — that is provided directly. Once transmit-queue leasing can be made to work, it should be suitable to most use cases.
One audience member asked about which component was supposed to be able to set up receive-queue leasing, and what was preventing a VM from abusing the ability to exhaust the host device's number of supported queues. For his use case, Borkmann said, it is Cillium that sets up the queue leases. The correspondence is always set up by some software running on the VMM; the VM can't simply decide that it gets to have a queue. The same person also asked why it was necessary to have a separate transmit queue per VM; for the receive-queue side, it's needed because incoming packets may need to be steered to different VMs, but he did not think that it was necessary for transmit queues.
It might be possible to use a shared transmit queue, Borkmann said, but he had previously seen bugs where, when express data path (XDP) assigned a queue mapping, it caused problems with the state of queues on the physical NIC. Keeping things separate prevents interference of that kind from occurring. The audience member then had some additional suggestions for how the transmit side could be improved which Borkmann promised to look into.
BPF for user-space live patching
The other topic that Borkmann wanted to discuss with the assembled kernel contributors was the possibility of using BPF to live-patch user-space programs. He got the idea from Fastabend's earlier talk, noting that sometimes there are user-space applications that are just as hard to patch against security vulnerabilities in a timely manner as the kernel. What if the kernel could generate trampolines or just-in-time compiled code that runs directly in user-space? That could allow the same kind of function-argument validation or return-value alteration. Such a facility could also be used to optimize the performance of uprobes.
Of course, it is already possible to patch user-space programs by directly modifying the text pages in memory. That is risky, however, because a misplaced patch can wreak havoc on a program. BPF might be useful here for the same reason it is in the kernel, Borkmann thought: BPF programs can be verified to ensure that they do not cause a crash. There are plenty of critical user-space applications where a form of live-patching that was guaranteed not to cause a crash would be welcome.
Borkmann envisioned a system where the kernel would verify a BPF program, compile it to position-independent code, and then inject it into the executable memory of a running user-space application. The user-space process would run the compiled BPF program directly. One complication would be that the BPF program would not have access to kernel-only interfaces, such as the majority of BPF maps, but for simple patches access to BPF arenas should be sufficient.
Song Liu thought that many important application properties could not be verified, and that therefore BPF programs in user-space might still crash user-space applications. Borkmann didn't think that small programs similar to Fastabend's "shields" would cause problems. In the kernel, the verifier has a clearly defined set of things that programs are and aren't allowed to do, Liu said, which is not true in user-space, so it's not clear that the verifier would be sufficient, or even helpful.
Jakub Sitnicki asked what advantage a BPF-patching mechanism would have over uprobes. Borkmann said that, because BPF would run directly in user space, it would avoid the overhead of a trap into the kernel. Andrii Nakryiko agreed, noting that uprobes have limited performance. Liu suggested that some of uprobe's functionality could be moved into user-space without involving BPF.
There is also the question of how to hook the generated code into the user-space
executable, Nakryiko pointed out. Uprobes use a small breakpoint instruction, and
then the kernel emulates the overwritten instruction. But patching in a longer
jump to a BPF trampoline would be more complicated: "this rewriting of
generic instructions is very hard
". I asked about the possibility of
including special target locations in user-space binaries that would be simpler
to patch, but Nakryiko said that the benefit of uprobes was that they could run
on any function.
At that point, the session was running out of time, but not before one more audience member asked for the verifier to be pulled out into a separate library. A lot of the benefit of the verifier, especially type-system-related checks, carries over between contexts, he said; having the verifier as a separate library would let user-space applications add in their own rules on top of that.
Overall, the assembled developers seemed to feel that using BPF for user-space patching would be difficult for several reasons, although none were opposed to the idea in principle, if it could be made to work. Whether anyone judges the ability to patch user-space programs in this way to be worth the hassle remains to be seen.
Debugging information for inlined functions
BPF programs use BPF type format (BTF) debugging information in order to determine how to interact with functions in the kernel. Specifically, tracing a kernel function involves finding its address in the kernel's BTF section — but that doesn't work for functions that have been inlined, and therefore don't have a single, specific address. Alan Maguire wants to add information about inlined functions to BTF in order to allow them to be traced, and led a session on that topic at the 2026 Linux Storage, Filesystem, Memory-Management, and BPF Summit.
There are more than 100,000 inlined functions in the kernel, Maguire said, spread across five times as many locations. Worse, some of them are partially inlined: called normally in some places and inlined in others. That can lead to cases today where it appears that a function was traced successfully, but some invocations were not seen.
The good news is that the rest of the infrastructure for tracing inlined
functions is already in place to enable
kprobes, which can be attached to arbitrary
locations. It is just a matter of getting the data about where functions have
been inlined into a usable format, Maguire stated. "The story is actually
pretty complete.
"
So, what is needed to store this information in BTF? The DWARF debugging format already has a way to indicate inlining information, but DWARF is also difficult to work with, and doesn't have a simple way to represent the common cases. A solution for BTF should be compact, and permit deduplication, to keep the memory overhead low, Maguire said. Ideally, inlining information could be stored in a separate section of the kernel binary, or even be distributed as a separate kernel module, so that it is not loaded until it is needed.
Concretely, Maguire proposed three new pieces of information be added to BTF. The first was inline-site-specific information about which function was inlined at each call site and how it would have been called if it had not been inlined, called the "location section". That data can't be easily deduplicated because it's specific to a given call site, so as much as possible the information should consist of pointers to data that can be deduplicated.
The second and third pieces of information he wants to add would be the pointed-to data: a "location prototype" and "location parameter". The location prototype specifies how the inlined function's arguments are represented at the call site, as a list of pointers to location parameters, which each store how to access a single function argument. In theory, the compiler could store a given function parameter at a different location for every inlined call site (of which there are 538,090); in practice, there are a limited number of ways that the compiler will transform parameters, and many functions have compatible signatures that result in the compiler making the same choices. In the current kernel, deduplicating location prototypes results in just 57,141 distinct entries referencing only 17,535 location parameter entries in total.
This means that the location sections take up most of the added data. Overall, the additions to BTF that Maguire proposes would come to approximately 11MB of additional data, or about 21 bytes per inlined call site. When pulled out into a separate kernel module and compressed, the total amount of data goes down to 3.5MB.
Maguire then went through an example of how information about a specific function would be stored. Consider this function:
int foo(int a, void *b, bool c);
If the compiler chose to inline foo() in a way that eliminates a as unused, promotes b to be passed in a register, and determines that c is a constant, the BTF representation would be a single location section entry storing the BTF type ID of foo(), the offset of the call site from the kernel's base address in memory, and a pointer to the location-prototype entry. That entry becomes a length-tagged array of references to location-parameter entries. The first entry is null, indicating that a cannot be recovered. The second points to a location parameter entry with a flag that indicates the value is contained in a register, and then the specific register number of b. The last location-parameter entry has a different flag specifying that it is a constant, and then the value of the constant.
Alexei Starovoitov asked what order the location-section entries would be stored in, since the kernel will have to search through them to find the right entry when setting up tracing. At first, Maguire thought that sorting by the address of the call site would be best, but after looking at how the data would be used, he decided to sort the entries by function name, instead. That way, tracers that are looking for information on a particular function can find it quickly with a binary search.
There were two prerequisite problems with adding inlining information to BTF that Maguire covered. For one thing, the number of location-section entries required increasing the size of the length field of the containing structure to 24 bits. More seriously, some of the existing tooling for processing BTF did not cope well with the presence of new tags that it doesn't know how to read. That is a problem that comes up any time BTF is extended with new kinds of information, and part of how DWARF ended up becoming so brittle. To address that, Maguire added information on how to parse BTF into BTF itself. Now, any tool that can read that meta-information can correctly skip past any new tags that it does not understand.
Maguire also had to update the poke-a-hole (pahole) utility to handle properly sorting the new kinds of BTF tags. That ended up being a bit complicated, but it should be working for normal kernel builds. Andrii Nakryiko asked about how it would handle out-of-tree modules. Those modules would need resilient module BTF that could be explicitly relocated when loaded into a running kernel, Maguire explained. His design allows that, but it complicates the build somewhat. There was a bit of back-and-forth about the changes that would be needed to support out-of-tree modules elegantly, but ultimately no changes were agreed upon.
At the time of writing, Maguire's patch set has not been merged. It is clearly useful to be able to trace inlined and partially inlined functions; the question is whether it is worth the complexity and memory overhead. Time will tell.
Fedora approves a smaller GRUB
Leo Sandoval and Marta Lewandowska have put forward a change proposal for Fedora 45, which is expected in October, to provide a separate, slimmed-down version of GRUB for a niche use case. The new package would be in addition to the main GRUB package and would not replace it for the majority of Fedora users. The idea met with some resistance from Fedora contributors who thought that it would be better to use systemd-boot, or another modern bootloader, rather than trying to wrangle GRUB into a suitable state for the use case. The Fedora Engineering Steering Council (FESCo), however, voted to accept the change on July 7.
Signed, sealed, booted
The change proposal specifies the creation of a UEFI-only build of GRUB with a minimal set of modules for booting unified kernel images (UKIs) using boot loader specification (BLS) files. The target use case is for booting "sealed bootable container" images in virtual environments (e.g., running virtual machines in public clouds) for confidential computing.
LWN has covered bootable containers (bootc) in the past, but sealed bootc images are relatively new. A sealed image is one with all of the components for a fully verified boot chain: that includes firmware, the UKI, and a composefs repository, with fs-verity enabled, for the filesystem rather than OSTree, which has traditionally been used for bootc. A sealed image relies on Secure Boot and currently is only supported on x86_64 and aarch64 systems with UEFI.
The main users of the new GRUB package, according to the
proposal, will be the Fedora
CoreOS developers. Since CoreOS is meant to be a minimal, automatically
updated operating system, the hope is that a stripped-down GRUB with fewer
built-in modules will have a smaller attack surface and require less-frequent
updates. CoreOS may be the first user of the new GRUB build, but the proposal
envisions "a minimal UEFI bootloader for virtual environments that can be
further tailored for use in those environments
".
It is interesting to see Lewandowska, who is a Red Hat quality engineer, now endorsing GRUB for this use case after arguing that it should be replaced not long ago. She gave a a talk at DevConf.cz, in 2024, about a project called nmbl (for "no more bootloader", pronounced "nimble") that would replace GRUB for use with UKIs. Instead of using a separate bootloader, nmbl would use the Linux kernel as its own bootloader. Her blog post that was published to coincide with the talk gives a good overview of nmbl, as well as its anticipated advantages over GRUB.
She pointed out at the time that Red Hat was carrying "hundreds
of downstream patches
" for the project and had argued that GRUB
was too complex. Lewandowska also pointed out the large
number of vulnerabilities it had been subject to over time, as
well as all of the filesystem, storage, and memory-allocation bugs
that she said GRUB's developers did not have the time to fix.
During the proposal discussion,
Lewandowska explained
her change of heart regarding GRUB, at least in part: "This year GRUB
upstream moved to GitLab, has much better [continuous integration], become more
accessible and much more lively.
" It would appear that work on nmbl has
stalled as GRUB has had a revival: the
repository that seems to have the most up-to-date work on the project, a dracut plugin for working with
nmbl, was last updated in August 2025. The proposal does not mention nmbl at
all.
Why not systemd-boot?
The original vision for sealed bootable containers called for using
systemd-boot rather than GRUB. The proposal mentions that systemd-boot was considered,
but was rejected for several reasons. It argued that systemd-boot
"has not been widely tested or fuzzed, like GRUB has been
", and
that long-term maintenance of more than one bootloader would add
technical debt. An early
version of the proposal claimed that the systemd team would
"view any additional features [for systemd-boot] as a
no-go
".
Fedora change discussions are still conducted on both its discussion forum
and the Fedora development mailing list, though a
proposal to change that and move discussions solely to the mailing list is
currently being entertained. This meant that the discussion was, naturally, a
bit fragmented. On the forum, Zbigniew Jędrzejewski-Szmek said
that the arguments against systemd-boot were "divorced from reality
". He
said that the project was "fairly widely used
", its code was being fuzzed
through OSS-Fuzz (albeit "not
as extensively as it should be
"), and added that systemd-boot received new
features regularly.
On the mailing list, Lennart Poettering commented
that he was "really irritated
" by the comment that systemd
developers were not adding features. "We are adding new stuff to
systemd-boot/systemd-stub all the time, including stuff contributed by
Red Hat's [confidential computing] folks
". On the forum,
Lewandowska said
that proposal sponsors were told by "RHEL/Fedora systemd
maintainers
" that new features were not being added to
systemd-boot. The bullet point about new features has since been removed from
the proposal by Kashyap Chamarthy.
Michael Gruber said
that he was not against the proposal, per se, but complained that it
was "giving up on the idea of systemd-boot (or something else) as a
new and lean boot loader
". GRUB does too many things, he said, and
he was not convinced that "chopping off parts
" of the software
would be the right way to go. Alberto Ruiz also
thought that the reasons given for rejecting systemd-boot were
faulty: "the burden of maintaining a GRUB variant is likely higher
than the burden of supporting systemd-boot in an additional use case
that is very limited in scope
".
If GRUB were not so widely used, Lewandowska said,
"I would agree adopting it for this use case does not make sense
". But,
given the amount of investment it has already received, she believed
that it made sense to use GRUB rather than to adopt something new.
The proposal provided instructions
and links to packages for early testing. Gerd Hoffman observed
that the test GRUB package "does not look very stripped down to me
", and
provided output comparing the size of the standard GRUB binary against the test
version. The smaller GRUB package was only about 64KB smaller than the standard
binary, which is a bit larger than 4MB. Sandoval acknowledged
that it needed more work and linked to the tracking ticket for
the work of removing non-essential modules from GRUB.
Neal Gompa said he was generally in favor of the proposal, though he wanted to see support for other common filesystems such as Btrfs, ext4, and XFS. Currently the slimmed-down build only includes the FAT filesystem module. Timothée Ravier replied that adding more supported filesystems would contradict the goal of including as few modules as possible to reduce the attack surface and reduce the need for updates. That led to an extended debate about other cloud use cases and the problems with UEFI.
Gompa also said that he had been "exploring and engaging with
upstream
" GRUB developers on the topic of providing additional security
features around data integrity. "With the revived upstream and our patches
getting merged at a rapid clip now, I'm very optimistic about the future of the
GRUB project
." Poettering, however, was less
enthusiastic about expanding GRUB's filesystem features. "It's hard
enough for the Linux storage people to maintain that as part of the Linux
kernel, and now you expect the handful of Grub maintainers to keep up with this
in their own codebase?
"
Oron Peled suggested
another strategy, highly reminiscent of nmbl, for dealing with the various
filesystems that bootable components might live on. Specifically, he thought the project should consider using an
intermediate kernel to boot another kernel, thereby reusing the Linux kernel's
filesystem drivers. Jędrzejewski-Szmek pointed out that
Peled's suggested approach "has been discussed extensively in the past
"
but had a number of downsides. For example, he said that the kernel's
"'embrace all CVEs' approach
" that has resulted in a constant stream of
CVEs meant "we'd be updating that smaller kernel all the time
".
Accepted
In considering the change, FESCo member Simon de Vlieger said
that he found it difficult to vote on; he did not see the value in having the
stripped-down GRUB in virtual environments, but thought it might be useful in
other situations that were not being considered or targeted. "This stripped
down version is at odds though with what I've seen people expect this to turn
into (having filesystem drivers, that sort of stuff).
" But, since it was
only the introduction of a new package, he cast a "weak-ish +1
" in favor
of the proposal. He said that he expected any deliverables, such as CoreOS, that
might use the new package by default would need to go through the change process
for approval.
Ravier said
that he was wearing multiple hats in the discussion since he was involved with
CoreOS, Fedora's Atomic
desktops, Red Hat Enterprise Linux (RHEL) CoreOS, as well as being a member
of FESCo. He was in favor of the change since it would be used in RHEL and
CentOS CoreOS for confidential-computing setups. But, from the FESCo perspective
"I don't think this change brings a lot of value to Fedora and we should
focus on using systemd-boot instead
". He said he was working on using
systemd-boot for the Atomic desktops, but: "In the end, I'm +1 as this
is an additive change that is useful for some groups of Fedora and does not
prevent us from doing something else in the future.
"
In the end, none of the FESCo members expressed much enthusiasm for the proposal, but none of the members seemed inclined to oppose it. It passed with five votes in favor (out of nine possible) and none against. Assuming all goes well, the new package should appear in Fedora 45. It will be interesting to see how the experiment goes, and whether there is a change proposal in time for Fedora 46 to make this the default bootloader for Fedora CoreOS.
Progress toward compiling Linux with gccrs
The gccrs project, which is creating a Rust frontend for the GCC compiler, has spent the first half of 2026 focusing on compiling the Linux kernel. By testing the compiler against the kernel crates, the development team has made significant progress toward generating correct code for other Rust programs. As detailed in the project's weekly and monthly reports, this effort has uncovered and resolved problems in areas such as attribute handling (described in the report for February), name resolution, and resource management (both detailed in the May report). Currently, the compiler can only handle simple standalone programs, but that situation could change rapidly in the coming months.
The drive to compile the Rust components of the Linux kernel stems from the new toolchain requirements brought by Rust's introduction into the kernel. Currently, developers must use the LLVM-based rustc compiler (although rustc does have experimental, in-progress support for using GCC as a backend via rust_codegen_gcc). While LLVM is supported by the kernel, a GCC-based alternative is necessary to support architectures not targeted by LLVM and to integrate with GCC's existing plugin ecosystem. As the kernel's Rust integration matures, toolchain flexibility and the availability of a GCC-based compiler have become priorities for Linux distributions.
Reorganizing milestones
Compiler frontend projects often track their progress against the release cycle of their target backend. In its March 2026 report, the gccrs team announced a change in project management, opting to organize its work into three capability-based milestones rather than targeting specific GCC versions.
The first milestone is an "embedded Rust compiler" capable of
compiling
no_std programs that depend only on the
core crate. The second is a "Rust for Linux compiler" that
supports the
alloc crate alongside focused on supporting the specific crates used
by the kernel. The final milestone is a "general purpose compiler" aimed
at handling broader Rust applications beyond the kernel environment.
The first milestone is not completely implemented, but it is close.
Progress toward the Rust for Linux milestone is underway. In March, the team
added support for compiler_builtins,
a key low-level crate required by kernel builds, and focused on
resolving problems within the kernel's ffi crate the kernel's
custom, minimal implementations of the compiler_builtins and
ffi crates. To
support this effort, Zhi Heng joined the project in May
2026 for an Open Source Security
internship. His work is dedicated to fixing bugs encountered when
gccrs compiles kernel crates and establishing
continuous-integration testing to prevent regressions.
However, simply testing to ensure the compiler can process Rust code without crashing is only part of the task. The generated code must also be correct. The implementation of Rust's destructor semantics is key to the generation of correct code, because idiomatic Rust code uses them more heavily than traditional C code, so that has been another area of focus.
The Drop infrastructure
Rust manages resources using a scope-based model known as resource acquisition is initialization (RAII). When a value goes out of scope, the compiler automatically inserts a call to its destructor, which is defined by the Drop trait.
In Rust, tracking when variables must be cleaned up is complex
because a variable's initialization state can change depending on the
control flow within a function. If a variable is conditionally moved or
only partially initialized, the compiler cannot simply drop it at the end of the
enclosing scope. To solve this, the frontend must analyze the
control-flow graph and generate dynamic "drop flags"—boolean variables
tracked at run time—to record whether a value needs to be destroyed
before passing this representation to the GCC backend. That analysis was missing
from the initial implementation of Drop in gccrs, causing some
Drop::drop() calls to be omitted or incorrect. Prior to the recent
work on the compiler, gccrs lacked Drop elaboration entirely.
In the context of the Linux kernel, missing Drop calls lead to severe run-time failures, such as memory leaks or unreleased system resources. A primary example is lock management. When kernel code acquires a lock, the Rust for Linux API returns a MutexGuard. The Drop implementation for this guard is responsible for releasing the lock.
As the team noted in May, without proper Drop calls, the lock is never released. This results in miscompiled code where locks remain held after their guard goes out of scope, which can cause synchronization failures or deadlocks. Google Summer of Code (GSoC) participant Janet Chien joined the project in May to focus specifically on building the gccrs Drop infrastructure.
Name-resolution work
Testing the compiler against the standard library and kernel crates also exposed fundamental bugs in how gccrs handled name resolution.
Rust maintains three four distinct namespaces: the value namespace for
functions and static variables, the macro namespace, the type
namespace, and a namespace for lifetimes and control-flow labels. When the compiler
encounters a "path"—a sequence of identifiers like
crate::foo::bar used to refer to an item—it must identify
the correct namespace for each segment of the path.
The development team discovered a flaw in its processing pipeline. Previously, when gccrs looked for the definition of an item, it resolved paths within the namespace of the target item type. For instance, when looking for a function, it resolved the path segments in the value namespace. This approach is incorrect because modules and publicly visible imports actually live in the type namespace; the compiler cannot successfully traverse a path to find a function without first resolving the module structure itself in the type namespace.
Fixing this problem required a rewrite of internal data structures and a refactoring of the visitor implementations used throughout the code. By May, these changes allowed the deeply nested imports in the core crate to resolve correctly. Modules and imports are now properly inserted into the types namespace, aligning gccrs more closely with rustc behavior.
Metadata and attribute handling
The shift to compiling kernel crates highlighted further problems in how gccrs processes compiler attributes and crate metadata.
Rust relies on attributes, such as #[cfg()], for conditional compilation. The February 2026 report described how lead developer Pierre-Emmanuel Patry had reworked the attribute-handling pipeline. His work split, into two distinct passes, the compiler pass that removes items excluded by the cfg attribute. This separation was necessary to support unstable features within the kernel; some of these features rely on macro expansion or conditional attributes that must be stripped before the main attribute-validation pass can safely evaluate them without triggering compiler errors.
In March, gccrs added a command-line option equivalent to rustc's -Zcrate-attr, called -frust-crate-attr. This option allows the build system to inject attributes during compiler invocation without modifying the underlying source files. This is particularly useful for passing the #![no_core] attribute, which is necessary to compile code without depending on the standard core library. That feature is relied upon by developers who are fuzzing the compiler to locate edge-case bugs.
Linking the kernel's Rust crates eventually revealed a new bug. Rust crates export metadata, typically bundled in .rlib files, to communicate their public APIs to other crates. When attempting to link kernel code, the developers found that certain modules and exports were simply missing from the emitted metadata. Because the compiler was omitting nested module exports during metadata generation, gccrs was unable to resolve external dependencies.
This problem was not caught by the project's existing metadata test cases, which relied on flatter module structures. Identifying this bug required compiling real-world code. Consequently, the team began a significant rework of the metadata handling system to ensure the GNU toolchain can successfully link the kernel's dependency tree.
Current capabilities and the challenges of upstreaming
So, what is possible for gccrs today? Currently, the compiler can successfully handle standalone no_core programs, and has made significant strides in processing the core crate and implementing compiler builtins. However, fully compiling the kernel's complex Rust abstractions remains a work in progress. gccrs is currently able to parse the kernel's code and the project is focused on correctly implementing the run-time semantics.
Beyond technical hurdles, gccrs has also had to navigate the
organization challenges of the GNU toolchain. Historically, the project has
faced problems landing its patches in the upstream GCC tree. Integrating an
entirely new, rapidly evolving language frontend into GCC is a large
undertaking, and the size of the patch sets has occasionally overwhelmed
the limited bandwidth of upstream GCC reviewers. While the situation has
improved as the frontend's architecture has stabilized, merging sweeping
changes—such as the recent name-resolution rewrites and Drop
infrastructure—still requires significant coordination and patience to clear the
upstream review process the bigger change is the recent elevation of two
gccrs developers to the status of GCC maintainers, which allows them
to stage updates in their own tree and then push them wholesale..
Next steps
Work continues on the remaining components required to compile the
kernel. GSoC participant Enes Çevik, who also joined the project in May, is
implementing support for the alloc crate.
This crate handles dynamic memory allocation types like Box,
Rc,
and Vec.
Although kernel development avoids many standard library abstractions,
several core kernel Rust abstractions rely on allocation types making
support for the alloc crate a hard prerequisite for the
"Rust for Linux" milestone. While earlier versions of the kernel's Rust
integration relied on a fork of the standard alloc crate, the kernel
has recently transitioned to using its own custom alloc module.
Consequently, compiling the standard alloc crate is no longer a strict
prerequisite for the "Rust for Linux" milestone, though it remains an important
step for the compiler's broader capabilities.
The broader development community will soon receive a closer look at this progress. Patry and Arthur Cohen plan to present a talk titled "Compiling the Linux kernel with gccrs" at RustConf in Montreal and EuroRust in Barcelona later this year. By systematically addressing the specific requirements of kernel code, the project is steadily building the foundation for using GCC to compile Rust code within the Linux kernel ecosystem.
[ Note: several corrections about the state of the alloc and compiler_builtins libraries have been added to the article. ]
Page editor: Joe Brockmeier
Inside this week's LWN.net Weekly Edition
- Briefs: RIP Dan Williams; Debian LLM resolution; Fedora 45 process; Codeberg LLM policy; GCC LLM policy; GNU Binutils 2.47; GNU C Library 2.44; Wayfire 0.11; Quotes; ...
- Announcements: Newsletters, conferences, security updates, patches, and more.
