include/linux/futex.h | 22 + include/linux/futex_types.h | 1 + include/linux/sched.h | 53 ++- include/uapi/linux/futex.h | 3 + init/init_task.c | 1 + kernel/fork.c | 3 +- kernel/futex/Makefile | 2 +- kernel/futex/core.c | 102 ++++- kernel/futex/futex.h | 27 +- kernel/futex/pi.c | 132 +++--- kernel/futex/ping.c | 707 +++++++++++++++++++++++++++++++ kernel/futex/syscalls.c | 6 + kernel/locking/mutex.c | 8 +- kernel/sched/core.c | 69 ++- kernel/sched/sched.h | 2 +- tools/testing/futex/Makefile | 13 + tools/testing/futex/ping_bench.c | 428 +++++++++++++++++++ 17 files changed, 1484 insertions(+), 95 deletions(-) create mode 100644 kernel/futex/ping.c create mode 100644 tools/testing/futex/Makefile create mode 100644 tools/testing/futex/ping_bench.c
Hello,
This patch series adds a new type of PI futexes, PI Next Generation,
or PING (name coined by Steven Rostedt) (but other name suggestions
are welcome!), that differs from classic PI futexes in that they can
be stolen from the top waiter, and use Proxy Execution instead of
rtmutexes internally.
The reason to allow the futexes to be stolen is that with classic PI
futex's strict handoff to the top waiter, new contending lockers are
now forced to wait in queue, which means that any locking operation
now becomes a scheduling event. With stealing, a contending locker has
the chance of taking the lock without blocking. The longer wait time
of blocked tasks can be mitigated by forcing the lock to be handed
off to them in a way that can't be stolen, when they've been stolen
from too much, to ensure they don't get starved.
The use of Proxy Execution lets us also get Priority Inheritance for
for fair tasks, which PI futexes don't really allow.
PING futexes are used similarly to FUTEX_*_PI, where the owner is
expected to write its TID in the futex.
The main user-visible difference with regular PI futexes is that the
kernel is allowed to set the FUTEX_WAITERS bit with an empty TID.
This is to allow for a new locker to steal the lock from the top
waiter, which is supposed to improve performance in workloads that
don't need strict RT handling.
The user is also allowed to notice that the futex is unlocked but
has waiters, and steal it without going to the kernel.
An example of how they're meant to be used:
Lock:
static __thread pid_t tid = gettid();
uint32_t oldval = 0;
if (atomic_compare_exchange_strong(ftx, &oldval, tid)
return;
oldval = FUTEX_WAITERS;
// The user could also spin here a bit.
if (atomic_compare_exchange_strong(ftx, &oldval,
tid | FUTEX_WAITERS))
return;
if (futex(ftx, FUTEX_LOCK_PING, 0, NULL) != 0)
err(1, "FUTEX_LOCK_PING");
Unlock:
static __thread pid_t tid = gettid();
uint32_t oldval = tid;
if (atomic_compare_exchange_strong(ftx, &oldval, 0))
return;
if (futex(ftx, FUTEX_UNLOCK_PING, 0, NULL) != 0)
err(1, "FUTEX_UNLOCK_PING");
Why did we opt for a new futex type instead of making PI futexes
and rtmutex use Proxy Execution?
PI futexes have strict rtmutex semantics, which while important
for RT workloads, could also potentially result in performance
penalties in workloads where maintaining those semantics isn't
as important. Since we don't want to break existing applications,
creating a new futex type seems appropriate.
Making a new futex type also lets us change the user interface a bit,
to allow for userspace stealing.
We do hope to eventually enable rtmutexes to use Proxy Execution
but the rtmutex wait_lock and pi_lock ordering is backwards for
Proxy Execution, so a potentially complicated rework might be needed
before rtmutexes can use it.
In the meantime, Proxy Execution boosting and rtmutex boosting
can continue to coexist.
Some features of PING futexes:
- Uses Proxy Execution.
- Optimistic spinning (can also be done in userspace, similarly to
FUTEX_WAIT futexes).
- Allows for userspace stealing.
- Starvation prevention with handoff.
Some performance numbers (lock acquisition time, in nsec, lower is better):
(The numbers were kindly gathered by John on a machine with 11th Gen
Intel(R) Core(TM) i5.)
(How PING performs compared to PI and FUTEX_WAIT seems to vary a lot
based on hardware and ping_bench parameters, and the results look a
bit more impressive on my device :-), but I did not include them since
they are based on an older kernel version.)
With all fair tasks:
"ping_bench -a -w 50000 -s 1000 -t 16"
x FUTEX_PING
+ FUTEX_PI
* glibc pthread_mutex_t (using FUTEX_WAIT)
N Mean Stddev Min 25p 50p 75p Max
x 159984 214377.3 859183.23 21 13703 15580 75343 1.15e+07
+ 159984 240690.75 23090.391 234635 237833 239962 241565 1.07e+06
Difference at 95.0% confidence
26313.4 +/- 4211.65 [22101.8 30525.1]
12.274363% +/- 1.9646%
(z 12.2454 p-val 1.77893e-34 crit val 1.95996 se 2148.84)
* 159984 203941.44 14257763 17 20 21 21 1.21e+09
No difference proven at 95.0% confidence
(z -0.292232 p-val 0.770109 crit val 1.95996 se 35710.9)
With priority inversions with a RT foreground task:
"ping_bench -t 8 -b 16 -r 1 -p -w 10000 -S 500"
x FUTEX_PING
+ FUTEX_PI
* glibc pthread_mutex_t (using FUTEX_WAIT)
N Mean Stddev Min 25p 50p 75p Max
x 9999 615.55326 3387.2647 21 50 52 54 8.2e+04
+ 9999 701.18222 3450.3964 21 49 52 53 3.34e+04
No difference proven at 95.0% confidence
(z 1.77087 p-val 0.0765815 crit val 1.95996 se 48.354)
* 9999 205935.36 2881211.5 24 53 54 55 1.24e+08
Difference at 95.0% confidence
205320 +/- 56473.6 [148846 261793]
33355.327862% +/- 9174.44%
(z 7.1258 p-val 1.03486e-12 crit val 1.95996 se 28813.6)
The patches apply on top of Linus' HEAD.
Any feedback is welcome!
Suleiman Souhlal (12):
sched: Abstract task_struct->blocked_on by locking primitive.
futex: Switch PI futex to use p->pi_futex_lock instead of p->pi_lock.
futex: Add "ping" parameter to pi_state management functions and
export them.
futex: Introduce stealable PI futex, FUTEX_*_PING.
futex: Implement exit_ping_state_list().
futex: Address aborting from futex_lock_ping() while owning
ping_state.
futex: Make FUTEX_*_PING use Proxy Execution.
futex: Implement PING futex handoff.
futex: Wake up donor in PING futex unlock.
futex: Optimistic spinning for PING futexes.
futex: Allow userspace stealing for PING futexes.
tools/testing/futex: Add ping_bench, a tool for benchmarking futexes.
include/linux/futex.h | 22 +
include/linux/futex_types.h | 1 +
include/linux/sched.h | 53 ++-
include/uapi/linux/futex.h | 3 +
init/init_task.c | 1 +
kernel/fork.c | 3 +-
kernel/futex/Makefile | 2 +-
kernel/futex/core.c | 102 ++++-
kernel/futex/futex.h | 27 +-
kernel/futex/pi.c | 132 +++---
kernel/futex/ping.c | 707 +++++++++++++++++++++++++++++++
kernel/futex/syscalls.c | 6 +
kernel/locking/mutex.c | 8 +-
kernel/sched/core.c | 69 ++-
kernel/sched/sched.h | 2 +-
tools/testing/futex/Makefile | 13 +
tools/testing/futex/ping_bench.c | 428 +++++++++++++++++++
17 files changed, 1484 insertions(+), 95 deletions(-)
create mode 100644 kernel/futex/ping.c
create mode 100644 tools/testing/futex/Makefile
create mode 100644 tools/testing/futex/ping_bench.c
--
2.55.0.1082.g2b9226bbc0-goog
On Thu, Sep 17, 2026 at 04:33:24AM +0000, Suleiman Souhlal wrote: > Hello, > > This patch series adds a new type of PI futexes, PI Next Generation, > or PING (name coined by Steven Rostedt) (but other name suggestions > are welcome!), that differs from classic PI futexes in that they can > be stolen from the top waiter, and use Proxy Execution instead of > rtmutexes internally. > > The reason to allow the futexes to be stolen is that with classic PI > futex's strict handoff to the top waiter, new contending lockers are > now forced to wait in queue, which means that any locking operation > now becomes a scheduling event. With stealing, a contending locker has > the chance of taking the lock without blocking. The longer wait time > of blocked tasks can be mitigated by forcing the lock to be handed > off to them in a way that can't be stolen, when they've been stolen > from too much, to ensure they don't get starved. > > The use of Proxy Execution lets us also get Priority Inheritance for > for fair tasks, which PI futexes don't really allow. https://patch.msgid.link/1490204338-1856-1-git-send-email-longman%40redhat.com Also, the goal is to eventually delete rt_mutex and have it be the normal mutex, in which case the existing FUTEX_*_PI things will automagically work. So I'm thinking all of this is way premature.
On Thu, Sep 17, 2026 at 5:58 PM Peter Zijlstra <peterz@infradead.org> wrote: > > On Thu, Sep 17, 2026 at 04:33:24AM +0000, Suleiman Souhlal wrote: > > Hello, > > > > This patch series adds a new type of PI futexes, PI Next Generation, > > or PING (name coined by Steven Rostedt) (but other name suggestions > > are welcome!), that differs from classic PI futexes in that they can > > be stolen from the top waiter, and use Proxy Execution instead of > > rtmutexes internally. > > > > The reason to allow the futexes to be stolen is that with classic PI > > futex's strict handoff to the top waiter, new contending lockers are > > now forced to wait in queue, which means that any locking operation > > now becomes a scheduling event. With stealing, a contending locker has > > the chance of taking the lock without blocking. The longer wait time > > of blocked tasks can be mitigated by forcing the lock to be handed > > off to them in a way that can't be stolen, when they've been stolen > > from too much, to ensure they don't get starved. > > > > The use of Proxy Execution lets us also get Priority Inheritance for > > for fair tasks, which PI futexes don't really allow. > > https://patch.msgid.link/1490204338-1856-1-git-send-email-longman%40redhat.com I was not aware of this, thanks! It actually seems very similar to this patchset (but much better written). The main functional differences that I can see from a cursory look are that on unlock it doesn't preserve the waiters bit, while PING does, and that TP futex also supports userspace rwlocks, unlike PING. And of course the fact that PING also uses Proxy Execution. So now I know that I'm not completely crazy for wanting this. :-) > > Also, the goal is to eventually delete rt_mutex and have it be the > normal mutex, in which case the existing FUTEX_*_PI things will > automagically work. I considered using a mutex but I was under the impression that some RT workloads really want strict RT handling such that the highest priority RT task always has to take the lock before any other contender, which mutex doesn't currently allow. For these workloads, the existing rtmutex behavior of handing off the lock to the top waiter is desirable. Other workloads still have RT tasks, but are ok with slightly less strict handling, as long as RT tasks still get serviced reasonably fast (so they still want priority inheritance, but are ok with the occasional case where a fair task steals the lock from the RT top waiter). I suspect Android is in this later bucket, so for them using a mutex as it's currently implemented would be ok. But others might not be. On the other hand, the current rtmutex behavior would not be desirable for users like Android, due to the performance with fair tasks that Steven brings up in his reply. These different needs are why I wanted to add a new futex type. But maybe the different needs could be selected through a CONFIG option, but with the same user API (FUTEX_*_PI)? I also had some other probably not insurmountable minor issues, like the fact that we probably wouldn't want to get kernel lockdep warnings for userspace deadlocks. -- Suleiman
On Fri, Sep 18, 2026 at 03:07:56PM +0900, Suleiman Souhlal wrote: > On Thu, Sep 17, 2026 at 5:58 PM Peter Zijlstra <peterz@infradead.org> wrote: > > > > On Thu, Sep 17, 2026 at 04:33:24AM +0000, Suleiman Souhlal wrote: > > > Hello, > > > > > > This patch series adds a new type of PI futexes, PI Next Generation, > > > or PING (name coined by Steven Rostedt) (but other name suggestions > > > are welcome!), that differs from classic PI futexes in that they can > > > be stolen from the top waiter, and use Proxy Execution instead of > > > rtmutexes internally. > > > > > > The reason to allow the futexes to be stolen is that with classic PI > > > futex's strict handoff to the top waiter, new contending lockers are > > > now forced to wait in queue, which means that any locking operation > > > now becomes a scheduling event. With stealing, a contending locker has > > > the chance of taking the lock without blocking. The longer wait time > > > of blocked tasks can be mitigated by forcing the lock to be handed > > > off to them in a way that can't be stolen, when they've been stolen > > > from too much, to ensure they don't get starved. > > > > > > The use of Proxy Execution lets us also get Priority Inheritance for > > > for fair tasks, which PI futexes don't really allow. > > > > https://patch.msgid.link/1490204338-1856-1-git-send-email-longman%40redhat.com > > I was not aware of this, thanks! > It actually seems very similar to this patchset (but much better written). > The main functional differences that I can see from a cursory look are > that on unlock it doesn't preserve the waiters bit, while PING does, > and that TP futex also supports userspace rwlocks, unlike PING. > And of course the fact that PING also uses Proxy Execution. > So now I know that I'm not completely crazy for wanting this. :-) Well, you all seem to be in violent disagreement with yourself. On the one hand you call your feature Priority Inheritance Next-Gen, while at the same time you're arguing you do NOT in fact want the strictness of PI/RT. You cannot have it both ways. But yes, FUTEX_LOCK/FUTEX_UNLOCK is desirable for a fair number of reasons, and Waiman's earlier attempt basically died because neither Thomas nor me found time to do a proper review :-( One of the very few things that I remember being annoyed with is that it did handoff in the futex code, while mutex_unlock() already has this. But other than it being a annoyance, I never had enough time to dig in to understand if this was fixable or not. Anyway, back then the motivation for FUTEX_LOCK was to get optimistic spinning for 'free' by using mutex, much like how FUTEX_LOCK_PI uses rt_mutex. These days, you'd get proxy exec as well. But like I mentioned elsewhere, the whole futex/proxy thing needs a deadlock detector, something we don't really need for in-kernel code, since it is a hard requirement for kernel code not to have lock cycles (barring ww_mutex, which will resolve them etc.). But we cannot trust userspace to play nice. Doing it at schedule() time is very much not ideal, this is something much better done at block time, not least because it is the ideal context to return -EDEADLK, but also because you but the cost of verifying it in the right context.
On Thu, Sep 17, 2026 at 1:58 AM Peter Zijlstra <peterz@infradead.org> wrote: > On Thu, Sep 17, 2026 at 04:33:24AM +0000, Suleiman Souhlal wrote: > > > > This patch series adds a new type of PI futexes, PI Next Generation, > > or PING (name coined by Steven Rostedt) (but other name suggestions > > are welcome!), that differs from classic PI futexes in that they can > > be stolen from the top waiter, and use Proxy Execution instead of > > rtmutexes internally. > > > > The reason to allow the futexes to be stolen is that with classic PI > > futex's strict handoff to the top waiter, new contending lockers are > > now forced to wait in queue, which means that any locking operation > > now becomes a scheduling event. With stealing, a contending locker has > > the chance of taking the lock without blocking. The longer wait time > > of blocked tasks can be mitigated by forcing the lock to be handed > > off to them in a way that can't be stolen, when they've been stolen > > from too much, to ensure they don't get starved. > > > > The use of Proxy Execution lets us also get Priority Inheritance for > > for fair tasks, which PI futexes don't really allow. > > https://patch.msgid.link/1490204338-1856-1-git-send-email-longman%40redhat.com Thanks for that reference! I'm sure we can learn a lot from that series. (I do like the straightforward FUTEX_LOCK/UNLOCK over the PING name here :) > Also, the goal is to eventually delete rt_mutex and have it be the > normal mutex, in which case the existing FUTEX_*_PI things will > automagically work. Indeed, moving rt_mutexes to proxy is a goal. Though the performance concerns from FUTEX_*_PI have to do with the semantics it (and rt_mutex) promises: strict RT prio order handoff - esentially FIFO for SCHED_NORMAL. Not so much the mechanism it uses for boosting. That's why it seems a new FUTEX op is needed. Since we 1) need to communicate the owner to the kernel 2) want different semantics for lock-handoff and stealing. Even when we get to moving rt_mutexes to proxy, it seems to me we're likely going to have to keep the rt_mutex itself (even if the PI boosting is proxy based) as a distinguisher, since the users likely desire the strict semantics (or at least we will need to add some behavior flags to the mutex struct so it can still satisfy those semantics). > So I'm thinking all of this is way premature. So it is just an initial RFC, and we are looking for input. But there is a strong appetite to solve this, as we're already seeing vendors doing less ideal things, like passing who the waiter thinks the owner is (racy) in via unused fields of FUTEX_WAIT, so that out-of-tree custom scheduler boosting can (temporarily - as again it can be wrong) try to get that owner to release the lock. The longer we go without a usable upstream solution, the more entrenched these less ideal solutions become. thanks -john
On Thu, Sep 17, 2026 at 10:53:47AM -0700, John Stultz wrote: > > Also, the goal is to eventually delete rt_mutex and have it be the > > normal mutex, in which case the existing FUTEX_*_PI things will > > automagically work. > > Indeed, moving rt_mutexes to proxy is a goal. Though the performance > concerns from FUTEX_*_PI have to do with the semantics it (and > rt_mutex) promises: strict RT prio order handoff - esentially FIFO for > SCHED_NORMAL. Not so much the mechanism it uses for boosting. Yeah, mutex will probably need to grow a few feature when doing that. > That's why it seems a new FUTEX op is needed. Since we 1) need to > communicate the owner to the kernel 2) want different semantics for > lock-handoff and stealing. So I'm not convinced. One could argue the strict requirements are dependent on the scheduling class, not the lock type. It should be feasible to make stealing behaviour depend on the lock owner or waiter class for instance. > > So I'm thinking all of this is way premature. > > So it is just an initial RFC, and we are looking for input. > > But there is a strong appetite to solve this, as we're already seeing > vendors doing less ideal things, like passing who the waiter thinks > the owner is (racy) in via unused fields of FUTEX_WAIT, so that > out-of-tree custom scheduler boosting can (temporarily - as again it > can be wrong) try to get that owner to release the lock. The longer we > go without a usable upstream solution, the more entrenched these less > ideal solutions become. So this does sound a little like blackmail. If vendors want this, then vendors had better help out upstream. If vendors don't give a toss about upstream, then why should I give a flying fuck about them? It is then on Google to tell vendors to put up or shut up. Just tell em their hacks are no longer allowed once we have something upstream. Break their toys and tell em to kindly go cry in the corner. So no, we don't rush this.
On Thu, 17 Sep 2026 10:53:47 -0700 John Stultz <jstultz@google.com> wrote: > Indeed, moving rt_mutexes to proxy is a goal. Though the performance > concerns from FUTEX_*_PI have to do with the semantics it (and > rt_mutex) promises: strict RT prio order handoff - esentially FIFO for > SCHED_NORMAL. Not so much the mechanism it uses for boosting. I started working on this while still at Google on the ChromeOS/Android-on-chrome team. The biggest issue we found with FUTEX_PI was that it forced all users of it to be fair. Even SCHED_OTHER which did not even benefit from the PI code. The result, it killed performance, and nobody wanted to use it. What I recommended was to have a new futex to allow SCHED_OTHER tasks to be unfair (just like rt_mutex is in PREEMPT_RT), and also to allow more to be done in user space and not require every contention to go into the kernel. We had a test (Suleiman, can you share that test) which emulated the code in Chrome and by switching to FUTEX_PI the performance dropped by a large percentage (I don't recall the actual numbers but I posted them internally at Google). By switching SCHED_OTHER to be non fair (that is, when the lock was released, if the next waiting task was SCHED_OTHER, and the new SCHED_OTHER task coming in could steal the lock), and the performance almost went back to what normal FUTEX had (I was assuming that going into the kernel on all contention was the cause of not getting closer to normal FUTEX). That alone was a big improvement over FUTEX_PI. There was talk about changing FUTEX_PI to allow SCHED_OTHER to be unfair and to steal, but that would change the semantics of it and there may be some application that requires FUTEX_PI to be fair for all tasks, even SCHED_OTHER. This is when we decided that we need a new FUTEX_PI (new generation, which I coined FUTEX_PING, but kinda of a joke so feel free to change), so that we could change the semantics without breaking backward compatibility of applications requiring the current behavior of FUTEX_PI. But FUTEX_PI requires all contention to be handled in the kernel, where as we can get even more performance if we could change it to allow the SCHED_OTHER case (which can steal the lock) to be handled in user space. I haven't looked at Suleiman's code yet (I'm currently traveling and don't have time until after Oct 10th). But I would expect this new futex to keep RT tasks being fair. Otherwise no RT task will use it. In summary, the motivation of this patch was that we had a few RT tasks suffering from priority inversion from hundreds of SCHED_OTHER tasks over a shared mutex. The problem was, if we switched it to FUTEX_PI, the few RT tasks would perform correctly, but the slowdown from the hundreds of SCHED_OTHER tasks made it a show stopper. The goal was to have a futex that allowed nice PI with RT tasks, but still allowed SCHED_OTHER being unfair and stealing from each other. -- Steve
On Fri, Sep 18, 2026 at 3:51 AM Steven Rostedt <rostedt@goodmis.org> wrote: > > On Thu, 17 Sep 2026 10:53:47 -0700 > John Stultz <jstultz@google.com> wrote: > > > Indeed, moving rt_mutexes to proxy is a goal. Though the performance > > concerns from FUTEX_*_PI have to do with the semantics it (and > > rt_mutex) promises: strict RT prio order handoff - esentially FIFO for > > SCHED_NORMAL. Not so much the mechanism it uses for boosting. > > I started working on this while still at Google on the > ChromeOS/Android-on-chrome team. The biggest issue we found with > FUTEX_PI was that it forced all users of it to be fair. Even > SCHED_OTHER which did not even benefit from the PI code. The result, it > killed performance, and nobody wanted to use it. > > What I recommended was to have a new futex to allow SCHED_OTHER tasks > to be unfair (just like rt_mutex is in PREEMPT_RT), and also to allow > more to be done in user space and not require every contention to go > into the kernel. > > We had a test (Suleiman, can you share that test) which emulated the > code in Chrome and by switching to FUTEX_PI the performance dropped by > a large percentage (I don't recall the actual numbers but I posted them > internally at Google). By switching SCHED_OTHER to be non fair (that > is, when the lock was released, if the next waiting task was > SCHED_OTHER, and the new SCHED_OTHER task coming in could steal the > lock), and the performance almost went back to what normal FUTEX had (I > was assuming that going into the kernel on all contention was the cause > of not getting closer to normal FUTEX). That alone was a big > improvement over FUTEX_PI. Your test is here: https://rostedt.org/private/lock-test.c Your PING implementation is here: https://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace.git/log/?h=ping/test > > There was talk about changing FUTEX_PI to allow SCHED_OTHER to be > unfair and to steal, but that would change the semantics of it and > there may be some application that requires FUTEX_PI to be fair for all > tasks, even SCHED_OTHER. This is when we decided that we need a new > FUTEX_PI (new generation, which I coined FUTEX_PING, but kinda of a > joke so feel free to change), so that we could change the semantics > without breaking backward compatibility of applications requiring the > current behavior of FUTEX_PI. > > But FUTEX_PI requires all contention to be handled in the kernel, where > as we can get even more performance if we could change it to allow the > SCHED_OTHER case (which can steal the lock) to be handled in user space. > > I haven't looked at Suleiman's code yet (I'm currently traveling and > don't have time until after Oct 10th). But I would expect this new > futex to keep RT tasks being fair. Otherwise no RT task will use it. Currently, my implementation allows a fair task to steal from an RT blocking task. However, I think that we could easily change it. I suspect that for our use case, we are ok with fair tasks occasionally stealing from RT tasks. > > In summary, the motivation of this patch was that we had a few RT tasks > suffering from priority inversion from hundreds of SCHED_OTHER tasks > over a shared mutex. The problem was, if we switched it to FUTEX_PI, > the few RT tasks would perform correctly, but the slowdown from the > hundreds of SCHED_OTHER tasks made it a show stopper. The goal was to > have a futex that allowed nice PI with RT tasks, but still allowed > SCHED_OTHER being unfair and stealing from each other. I agree with everything you've said, but I should also add that we would also like PI behavior between fair tasks, not just when RT tasks are present, because priority inversions can still occur between fair tasks. For example when using sched group shares. The existing rtmutex based PI futexes can't address those, as far as I can tell. This is why I opted for a whole new implementation rather than your initial prototype that still used rtmutexes. -- Suleiman
© 2016 - 2026 Red Hat, Inc.