* Re: [PATCH RFT v3 1/5] mm: Introduce ARCH_HAS_USER_SHADOW_STACK
From: Mike Rapoport @ 2024-02-04 11:56 UTC (permalink / raw)
To: Mark Brown
Cc: Rick P. Edgecombe, Deepak Gupta, Szabolcs Nagy, H.J. Lu,
Florian Weimer, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
Mel Gorman, Daniel Bristot de Oliveira, Valentin Schneider,
Christian Brauner, Shuah Khan, linux-kernel, Catalin Marinas,
Will Deacon, Kees Cook, jannh, linux-kselftest, linux-api,
David Hildenbrand
In-Reply-To: <20231120-clone3-shadow-stack-v3-1-a7b8ed3e2acc@kernel.org>
On Mon, Nov 20, 2023 at 11:54:29PM +0000, Mark Brown wrote:
> Since multiple architectures have support for shadow stacks and we need to
> select support for this feature in several places in the generic code
> provide a generic config option that the architectures can select.
>
> Suggested-by: David Hildenbrand <david@redhat.com>
> Acked-by: David Hildenbrand <david@redhat.com>
> Signed-off-by: Mark Brown <broonie@kernel.org>
> ---
> arch/x86/Kconfig | 1 +
> fs/proc/task_mmu.c | 2 +-
> include/linux/mm.h | 2 +-
> mm/Kconfig | 6 ++++++
> 4 files changed, 9 insertions(+), 2 deletions(-)
>
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index 3762f41bb092..14b7703a9a2b 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -1952,6 +1952,7 @@ config X86_USER_SHADOW_STACK
> depends on AS_WRUSS
> depends on X86_64
> select ARCH_USES_HIGH_VMA_FLAGS
> + select ARCH_HAS_USER_SHADOW_STACK
> select X86_CET
> help
> Shadow stack protection is a hardware feature that detects function
> diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
> index ef2eb12906da..f0a904aeee8e 100644
> --- a/fs/proc/task_mmu.c
> +++ b/fs/proc/task_mmu.c
> @@ -699,7 +699,7 @@ static void show_smap_vma_flags(struct seq_file *m, struct vm_area_struct *vma)
> #ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
> [ilog2(VM_UFFD_MINOR)] = "ui",
> #endif /* CONFIG_HAVE_ARCH_USERFAULTFD_MINOR */
> -#ifdef CONFIG_X86_USER_SHADOW_STACK
> +#ifdef CONFIG_ARCH_HAS_USER_SHADOW_STACK
> [ilog2(VM_SHADOW_STACK)] = "ss",
> #endif
> };
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 418d26608ece..10462f354614 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -341,7 +341,7 @@ extern unsigned int kobjsize(const void *objp);
> #endif
> #endif /* CONFIG_ARCH_HAS_PKEYS */
>
> -#ifdef CONFIG_X86_USER_SHADOW_STACK
> +#ifdef CONFIG_ARCH_HAS_USER_SHADOW_STACK
> /*
> * VM_SHADOW_STACK should not be set with VM_SHARED because of lack of
> * support core mm.
> diff --git a/mm/Kconfig b/mm/Kconfig
> index 89971a894b60..6713bb3b0b48 100644
> --- a/mm/Kconfig
> +++ b/mm/Kconfig
> @@ -1270,6 +1270,12 @@ config LOCK_MM_AND_FIND_VMA
> bool
> depends on !STACK_GROWSUP
>
> +config ARCH_HAS_USER_SHADOW_STACK
> + bool
> + help
> + The architecture has hardware support for userspace shadow call
> + stacks (eg, x86 CET, arm64 GCS or RISC-V Zicfiss).
The whitespace looks suspicious, I think there should be a leading tab.
Otherwise
Reviewed-by: Mike Rapoport (IBM) <rppt@kernel.org>
> +
> source "mm/damon/Kconfig"
>
> endmenu
>
> --
> 2.30.2
>
--
Sincerely yours,
Mike.
^ permalink raw reply
* Re: [PATCH RFT v5 1/7] Documentation: userspace-api: Add shadow stack API documentation
From: Randy Dunlap @ 2024-02-04 1:33 UTC (permalink / raw)
To: Mark Brown, Rick P. Edgecombe, Deepak Gupta, Szabolcs Nagy,
H.J. Lu, Florian Weimer, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Peter Zijlstra,
Juri Lelli, Vincent Guittot, Dietmar Eggemann, Steven Rostedt,
Ben Segall, Mel Gorman, Daniel Bristot de Oliveira,
Valentin Schneider, Christian Brauner, Shuah Khan
Cc: linux-kernel, Catalin Marinas, Will Deacon, Kees Cook, jannh,
linux-kselftest, linux-api
In-Reply-To: <20240203-clone3-shadow-stack-v5-1-322c69598e4b@kernel.org>
Hi,
On 2/2/24 16:04, Mark Brown wrote:
> There are a number of architectures with shadow stack features which we are
> presenting to userspace with as consistent an API as we can (though there
> are some architecture specifics). Especially given that there are some
> important considerations for userspace code interacting directly with the
> feature let's provide some documentation covering the common aspects.
>
> Signed-off-by: Mark Brown <broonie@kernel.org>
> ---
> Documentation/userspace-api/index.rst | 1 +
> Documentation/userspace-api/shadow_stack.rst | 41 ++++++++++++++++++++++++++++
> 2 files changed, 42 insertions(+)
>
> diff --git a/Documentation/userspace-api/shadow_stack.rst b/Documentation/userspace-api/shadow_stack.rst
> new file mode 100644
> index 000000000000..c6e5ab795b60
> --- /dev/null
> +++ b/Documentation/userspace-api/shadow_stack.rst
> @@ -0,0 +1,41 @@
> +=============
> +Shadow Stacks
> +=============
> +
> +Introduction
> +============
> +
> +Several architectures have features which provide backward edge
> +control flow protection through a hardware maintained stack, only
> +writeable by userspace through very limited operations. This feature
> +is referred to as shadow stacks on Linux, on x86 it is part of Intel
on Linux. On x86
> +Control Enforcement Technology (CET), on arm64 it is Guarded Control
> +Stacks feature (FEAT_GCS) and for RISC-V it is the Zicfiss extension.> +It is expected that this feature will normally be managed by the
> +system dynamic linker and libc in ways broadly transparent to
> +application code, this document covers interfaces and considerations
code. This considerations.
> +
> +
> +Enabling
> +========
> +
> +Shadow stacks default to disabled when a userspace process is
> +executed, they can be enabled for the current thread with a syscall:
executed. They
> +
> + - For x86 the ARCH_SHSTK_ENABLE arch_prctl()
> +
> +It is expected that this will normally be done by the dynamic linker.
> +Any new threads created by a thread with shadow stacks enabled will
> +themsleves have shadow stacks enabled.
themselves
> +
> +
> +Enablement considerations
> +=========================
> +
> +- Returning from the function that enables shadow stacks without first
> + disabling them will cause a shadow stack exception. This includes
> + any syscall wrapper or other library functions, the syscall will need
functions. The
> + to be inlined.
> +- A lock feature allows userspace to prevent disabling of shadow stacks.
> +- This that change the stack context like longjmp() or use of ucontext
Those
?
> + changes on signal return will need support from libc.
>
--
#Randy
^ permalink raw reply
* Re: [PATCH 1/3] pidfd_poll: report POLLHUP when pid_task() == NULL
From: Christian Brauner @ 2024-02-03 16:46 UTC (permalink / raw)
To: Oleg Nesterov
Cc: Andy Lutomirski, Eric W. Biederman, Tycho Andersen, linux-api,
linux-kernel
In-Reply-To: <20240203120425.GA30029@redhat.com>
On Sat, Feb 03, 2024 at 01:04:26PM +0100, Oleg Nesterov wrote:
> Christian, I apologize for my terse and unclear emails yesterday,
> I was a bit busy.
Oh don't worry, I didn't take it badly in any way. That was all totally
ok me getting the keys api wrong is not your problem. ;)
>
> On 02/02, Oleg Nesterov wrote:
> >
> > On 02/02, Christian Brauner wrote:
> > >
> > > > I think we need a simpler patch. I was going to send it as 4/4, but I'd
> > > > like to think more, _perhaps_ we can also discriminate the PIDFD_THREAD
> > > > and non-PIDFD_THREAD waiters. I'll try to make the patch(es) tomorrow or
> > >
> > > Right, I didn't go that far.
>
> OK, so lets forget about the PIDFD_THREAD waiters for the moment.
> Then everything is trivial, please see below.
>
> > > > 1. we can't use wake_up_poll(), it passes nr_exclusive => 1
> > >
> > > Bah. So we need the same stuff we did for io_uring and use
> > > __wake_up() directly. Or we add wake_up_all_poll() and convert the other
> > > three callsites:
> >
> > ...
> >
> > > +#define wake_up_all_poll(x, m) \
> > > + __wake_up(x, TASK_NORMAL, 0, poll_to_key(m))
> >
> > Agreed, but I think this + s/wake_up/wake_up_all_poll/ conversions
> > need a separate patch.
>
> And if it was not clear I like this change! In fact I thought about
> the new helper too, but I didn't realize that it already have the
> users.
>
> > > -void do_notify_pidfd(struct task_struct *task)
> > > +void pidfd_wake_up_poll(struct task_struct *task, bool dead)
> > > {
> > > - struct pid *pid;
> > > -
> > > WARN_ON(task->exit_state == 0);
> > > - pid = task_pid(task);
> > > - wake_up_all(&pid->wait_pidfd);
> > > + WARN_ON(mask == 0);
> > > + wake_up_all_poll(&task_pid(task)->wait_pidfd,
> > > + EPOLLIN | EPOLLRDNORM | dead ? EPOLLHUP : 0);
> >
> > No...
> >
> > This is still overcomplicated and is not right.
> ^^^^^^^^^^^^^^^^
> Sorry, sorry, I misread your change, "dead" is always false so it has
> no effect and thus the change is correct.
>
> But why do we need this arg? All we need is the trivial one-liner:
>
> --- a/kernel/signal.c
> +++ b/kernel/signal.c
> @@ -2025,7 +2025,8 @@ void do_notify_pidfd(struct task_struct *task)
>
> WARN_ON(task->exit_state == 0);
> pid = task_pid(task);
> - wake_up_all(&pid->wait_pidfd);
> + __wake_up(&pid->wait_pidfd, TASK_NORMAL, 0,
> + poll_to_key(EPOLLIN | EPOLLRDNORM));
Ok, care to just send me a full patch for this?
Just paste it here and I'll pick it.
> }
>
> /*
>
> and I was going to send the patch above as 4/4, but then decided
> to delay it, see below.
>
> We can rename do_notify_pidfd() if you wish, and of course the
> new wake_up_all_poll() helper you proposed makes sense, but this
> is another story.
If at all then absolutely a separate patch.
>
> As for __change_pid(). In this case wake_up_all() is fine, we can
> change it to use wake_up_all_poll() too for consistency, but this
> is not strictly necessary and in fact "key = 0" makes a bit more
> sense imo.
Ok, I see.
>
> And just in case... previously I said that (perhaps) it can use
> __wake_up_pollfree() but no, this would be obviously wrong.
>
> ------------------------------------------------------------------
> Now let's recall about the PIDFD_THREAD waiters. exit_notify() does
>
> /*
> * sub-thread or delay_group_leader(), wake up the
> * PIDFD_THREAD waiters.
> */
> if (!thread_group_empty(tsk))
> do_notify_pidfd(tsk);
>
> and it would be nice to not wakeup the non-PIDFD_THREAD waiters.
>
> I was thinking about something like the changes below but
>
> - I am NOT sure it will work! I need to read the code
> in fs/select.c
>
> - in fact I am not sure this makes a lot of sense, and
> the hack in pidfd_poll() doesn't look very nice even
> _if_ it can work.
First, good idea to make this work. :)
Second, I agree that this looks ugly. ;/
So here's a very likely a stupid idea. To make that clean we essentially
need kernel private information that can't be set in userspace (Btw,
look at EPOLL_URING_WAKE which is similar in that it cannot be set from
userspace. It's not the same thing ofc but it is a private bit.). Which
is the gist of your proposal in a way.
So we would have to grab a new private bit in the epoll flag space. A
subsystem (e.g., pidfd, seccomp) could use that private bit to avoid
spurious wakeups such as this. I'm sure that this would be useful to
others?
It's not something we need to do right now but my point is that this
would be more palatable if this coulde be made generally useful.
The only thing that's ugly is that we'd raise that bit in ->poll() but
maybe that's acceptable through a helper? If we'd wanted to set it from
epoll itself we'd need a new fop most likely. :/
>
> --- a/kernel/fork.c
> +++ b/kernel/fork.c
> @@ -2081,6 +2081,13 @@ static __poll_t pidfd_poll(struct file *file, struct poll_table_struct *pts)
> struct task_struct *task;
> __poll_t poll_flags = 0;
>
> + if (thread && pts && pts->_qproc) {
> + // We are not registered yet. Update the key to mark
> + // us a a PIDFD_THREAD waiter, __pollwait() will copy
> + // this ->_key to poll_table_entry->key.
> + if (pts->_key & EPOLLIN) // exclude the POLLHUP-only waiters
> + pts->_key |= EPOLLMSG; // random flag
> + }
> poll_wait(file, &pid->wait_pidfd, pts);
> /*
> * Depending on PIDFD_THREAD, inform pollers when the thread
>
> Now, do_notify_pidfd() can do
>
> if (!thread_group_empty(tsk))
> mask = EPOLLMSG; // matches the hack in pidfd_poll
> else
> mask = EPOLLIN | EPOLLRDNORM;
>
> __wake_up(..., poll_to_key(mask));
>
> Yes, in this case it makes more sense to pass !thread_group_empty()
> as a "bool thread" argument.
>
> ---------------------------------------------------------------------
>
> What do you think?
>
> I am starting to think that I shouldn't have delayed the 1st trivial
> change. Feel free to push it, with or without rename, with or without
> the new wake_up_all_poll() helper, I am fine either way. But please
> don't add the new "int dead" argument, afaics it makes no sense.
I would never go above your head, don't worry. :)
^ permalink raw reply
* Re: [PATCH 1/3] pidfd_poll: report POLLHUP when pid_task() == NULL
From: Oleg Nesterov @ 2024-02-03 12:04 UTC (permalink / raw)
To: Christian Brauner
Cc: Andy Lutomirski, Eric W. Biederman, Tycho Andersen, linux-api,
linux-kernel
In-Reply-To: <20240202190529.GA28818@redhat.com>
Christian, I apologize for my terse and unclear emails yesterday,
I was a bit busy.
On 02/02, Oleg Nesterov wrote:
>
> On 02/02, Christian Brauner wrote:
> >
> > > I think we need a simpler patch. I was going to send it as 4/4, but I'd
> > > like to think more, _perhaps_ we can also discriminate the PIDFD_THREAD
> > > and non-PIDFD_THREAD waiters. I'll try to make the patch(es) tomorrow or
> >
> > Right, I didn't go that far.
OK, so lets forget about the PIDFD_THREAD waiters for the moment.
Then everything is trivial, please see below.
> > > 1. we can't use wake_up_poll(), it passes nr_exclusive => 1
> >
> > Bah. So we need the same stuff we did for io_uring and use
> > __wake_up() directly. Or we add wake_up_all_poll() and convert the other
> > three callsites:
>
> ...
>
> > +#define wake_up_all_poll(x, m) \
> > + __wake_up(x, TASK_NORMAL, 0, poll_to_key(m))
>
> Agreed, but I think this + s/wake_up/wake_up_all_poll/ conversions
> need a separate patch.
And if it was not clear I like this change! In fact I thought about
the new helper too, but I didn't realize that it already have the
users.
> > -void do_notify_pidfd(struct task_struct *task)
> > +void pidfd_wake_up_poll(struct task_struct *task, bool dead)
> > {
> > - struct pid *pid;
> > -
> > WARN_ON(task->exit_state == 0);
> > - pid = task_pid(task);
> > - wake_up_all(&pid->wait_pidfd);
> > + WARN_ON(mask == 0);
> > + wake_up_all_poll(&task_pid(task)->wait_pidfd,
> > + EPOLLIN | EPOLLRDNORM | dead ? EPOLLHUP : 0);
>
> No...
>
> This is still overcomplicated and is not right.
^^^^^^^^^^^^^^^^
Sorry, sorry, I misread your change, "dead" is always false so it has
no effect and thus the change is correct.
But why do we need this arg? All we need is the trivial one-liner:
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -2025,7 +2025,8 @@ void do_notify_pidfd(struct task_struct *task)
WARN_ON(task->exit_state == 0);
pid = task_pid(task);
- wake_up_all(&pid->wait_pidfd);
+ __wake_up(&pid->wait_pidfd, TASK_NORMAL, 0,
+ poll_to_key(EPOLLIN | EPOLLRDNORM));
}
/*
and I was going to send the patch above as 4/4, but then decided
to delay it, see below.
We can rename do_notify_pidfd() if you wish, and of course the
new wake_up_all_poll() helper you proposed makes sense, but this
is another story.
As for __change_pid(). In this case wake_up_all() is fine, we can
change it to use wake_up_all_poll() too for consistency, but this
is not strictly necessary and in fact "key = 0" makes a bit more
sense imo.
And just in case... previously I said that (perhaps) it can use
__wake_up_pollfree() but no, this would be obviously wrong.
------------------------------------------------------------------
Now let's recall about the PIDFD_THREAD waiters. exit_notify() does
/*
* sub-thread or delay_group_leader(), wake up the
* PIDFD_THREAD waiters.
*/
if (!thread_group_empty(tsk))
do_notify_pidfd(tsk);
and it would be nice to not wakeup the non-PIDFD_THREAD waiters.
I was thinking about something like the changes below but
- I am NOT sure it will work! I need to read the code
in fs/select.c
- in fact I am not sure this makes a lot of sense, and
the hack in pidfd_poll() doesn't look very nice even
_if_ it can work.
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -2081,6 +2081,13 @@ static __poll_t pidfd_poll(struct file *file, struct poll_table_struct *pts)
struct task_struct *task;
__poll_t poll_flags = 0;
+ if (thread && pts && pts->_qproc) {
+ // We are not registered yet. Update the key to mark
+ // us a a PIDFD_THREAD waiter, __pollwait() will copy
+ // this ->_key to poll_table_entry->key.
+ if (pts->_key & EPOLLIN) // exclude the POLLHUP-only waiters
+ pts->_key |= EPOLLMSG; // random flag
+ }
poll_wait(file, &pid->wait_pidfd, pts);
/*
* Depending on PIDFD_THREAD, inform pollers when the thread
Now, do_notify_pidfd() can do
if (!thread_group_empty(tsk))
mask = EPOLLMSG; // matches the hack in pidfd_poll
else
mask = EPOLLIN | EPOLLRDNORM;
__wake_up(..., poll_to_key(mask));
Yes, in this case it makes more sense to pass !thread_group_empty()
as a "bool thread" argument.
---------------------------------------------------------------------
What do you think?
I am starting to think that I shouldn't have delayed the 1st trivial
change. Feel free to push it, with or without rename, with or without
the new wake_up_all_poll() helper, I am fine either way. But please
don't add the new "int dead" argument, afaics it makes no sense.
Thanks,
Oleg.
^ permalink raw reply
* Re: [RFC PATCH v2 19/29] selftests: ntsync: Add some tests for NTSYNC_IOC_WAIT_ANY.
From: Elizabeth Figura @ 2024-02-03 2:08 UTC (permalink / raw)
To: Andi Kleen
Cc: Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet, Shuah Khan,
linux-kernel, linux-api, wine-devel, André Almeida,
Wolfram Sang, Arkadiusz Hiler, Peter Zijlstra, Andy Lutomirski,
linux-doc, linux-kselftest
In-Reply-To: <878r45khqc.fsf@linux.intel.com>
On Wednesday, 31 January 2024 02:52:11 CST Andi Kleen wrote:
> Elizabeth Figura <zfigura@codeweavers.com> writes:
>
> > +TEST(test_wait_any)
> > +{
> > + struct ntsync_mutex_args mutex_args = {0};
> > + struct ntsync_wait_args wait_args = {0};
> > + struct ntsync_sem_args sem_args = {0};
> > + __u32 owner, index, count;
> > + struct timespec timeout;
> > + int objs[2], fd, ret;
> > +
> > + clock_gettime(CLOCK_MONOTONIC, &timeout);
> > +
> > + fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
> > + ASSERT_LE(0, fd);
> > +
> > + sem_args.count = 2;
> > + sem_args.max = 3;
> > + sem_args.sem = 0xdeadbeef;
> > + ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
> > + EXPECT_EQ(0, ret);
> > + EXPECT_NE(0xdeadbeef, sem_args.sem);
> > +
> > + mutex_args.owner = 0;
> > + mutex_args.count = 0;
> > + mutex_args.mutex = 0xdeadbeef;
> > + ret = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
> > + EXPECT_EQ(0, ret);
> > + EXPECT_NE(0xdeadbeef, mutex_args.mutex);
>
> It seems your tests are missing test cases for exceeding any limits,
> especially overflow/underflow cases. Since these are the most likely
> for any security problems it would be good to have extra coverage here.
> The fuzzers will hopefully hit it too.
>
> Also some stress testing with multiple threads would be useful.
Thanks, I'll add these.
^ permalink raw reply
* Re: [net-next 0/3] Per epoll context busy poll support
From: Jakub Kicinski @ 2024-02-03 1:15 UTC (permalink / raw)
To: Joe Damato
Cc: Samudrala, Sridhar, Eric Dumazet, netdev, linux-kernel,
chuck.lever, jlayton, linux-api, brauner, davem, alexander.duyck,
Wei Wang, Amritha Nambiar
In-Reply-To: <20240202202344.GA9283@fastly.com>
On Fri, 2 Feb 2024 12:23:44 -0800 Joe Damato wrote:
> > Did you see SO_PREFER_BUSY_POLL by any chance? (In combination with
> > gro_flush_timeout IIRC). We added it a while back with Bjorn, it seems
> > like a great idea to me at the time but I'm unclear if anyone uses it
> > in production..
>
> I have seen it while reading the code, yes. I think maybe I missed
> something about its interaction with gro_flush_timeout. In my use case,
> the machine has no traffic until after the app is started.
>
> In this case, I haven't needed to worry about regular NAPI monopolizing the
> CPU and preventing busy poll from working.
>
> Maybe I am missing something more nuanced, though? I'll have another look
> at the code, just incase.
We reused the gro_flush_timeout as an existing "user doesn't care if
packets get delayed by this much in worst case" value. If you set
SO_PREFER_BUSY_POLL the next time you busy pool the NAPI will be marked
as "already scheduled" and a timer is set (to gro_flush_timeout).
If NIC IRQ fires before gro_flush_timeout it gets ignored, because NAPI
is already marked as scheduled.
If you busy poll again the timer gets postponed for another
gro_flush_timeout nsec.
If timer fires we go back to normal NAPI processing.
The idea is that you set gro_flush_timeout to some high value, like
10 msec, and expect your app to poll more often than every 10 msec.
Then the normal NAPI processing will never kick in, and there will
be only 1 NIC IRQ after which the HW IRQ remains masked.
With high coalescing timer you technically still get an IRQ every
so often and interrupt the app. Worst case (UDP flood) you may even
get into an overload where the app gets starved out completely..
^ permalink raw reply
* [PATCH RFT v5 7/7] selftests/clone3: Test shadow stack support
From: Mark Brown @ 2024-02-03 0:05 UTC (permalink / raw)
To: Rick P. Edgecombe, Deepak Gupta, Szabolcs Nagy, H.J. Lu,
Florian Weimer, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
Mel Gorman, Daniel Bristot de Oliveira, Valentin Schneider,
Christian Brauner, Shuah Khan
Cc: linux-kernel, Catalin Marinas, Will Deacon, Kees Cook, jannh,
bsegall, linux-kselftest, linux-api, Mark Brown
In-Reply-To: <20240203-clone3-shadow-stack-v5-0-322c69598e4b@kernel.org>
Add basic test coverage for specifying the shadow stack for a newly
created thread via clone3(), including coverage of the newly extended
argument structure.
In order to facilitate testing on systems without userspace shadow stack
support we manually enable shadow stacks on startup, this is architecture
specific due to the use of an arch_prctl() on x86. Due to interactions with
potential userspace locking of features we actually detect support for
shadow stacks on the running system by attempting to allocate a shadow
stack page during initialisation using map_shadow_stack(), warning if this
succeeds when the enable failed.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/clone3/clone3.c | 128 ++++++++++++++++++++++
tools/testing/selftests/clone3/clone3_selftests.h | 8 ++
2 files changed, 136 insertions(+)
diff --git a/tools/testing/selftests/clone3/clone3.c b/tools/testing/selftests/clone3/clone3.c
index 6adbfd14c841..c468d9b87bd5 100644
--- a/tools/testing/selftests/clone3/clone3.c
+++ b/tools/testing/selftests/clone3/clone3.c
@@ -3,6 +3,7 @@
/* Based on Christian Brauner's clone3() example */
#define _GNU_SOURCE
+#include <asm/mman.h>
#include <errno.h>
#include <inttypes.h>
#include <linux/types.h>
@@ -11,6 +12,7 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/un.h>
@@ -19,8 +21,12 @@
#include <sched.h>
#include "../kselftest.h"
+#include "../ksft_shstk.h"
#include "clone3_selftests.h"
+static bool shadow_stack_supported;
+static size_t max_supported_args_size;
+
enum test_mode {
CLONE3_ARGS_NO_TEST,
CLONE3_ARGS_ALL_0,
@@ -28,6 +34,10 @@ enum test_mode {
CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG,
CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG,
CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG,
+ CLONE3_ARGS_SHADOW_STACK,
+ CLONE3_ARGS_SHADOW_STACK_NO_SIZE,
+ CLONE3_ARGS_SHADOW_STACK_NO_POINTER,
+ CLONE3_ARGS_SHADOW_STACK_NO_TOKEN,
};
typedef bool (*filter_function)(void);
@@ -44,6 +54,43 @@ struct test {
filter_function filter;
};
+/*
+ * We check for shadow stack support by attempting to use
+ * map_shadow_stack() since features may have been locked by the
+ * dynamic linker resulting in spurious errors when we attempt to
+ * enable on startup. We warn if the enable failed.
+ */
+static void test_shadow_stack_supported(void)
+{
+ long ret;
+
+ ret = syscall(__NR_map_shadow_stack, 0, getpagesize(), 0);
+ if (ret == -1) {
+ ksft_print_msg("map_shadow_stack() not supported\n");
+ } else if ((void *)ret == MAP_FAILED) {
+ ksft_print_msg("Failed to map shadow stack\n");
+ } else {
+ ksft_print_msg("Shadow stack supportd\n");
+ shadow_stack_supported = true;
+
+ if (!shadow_stack_enabled)
+ ksft_print_msg("Mapped but did not enable shadow stack\n");
+ }
+}
+
+static unsigned long long get_shadow_stack_page(unsigned long flags)
+{
+ unsigned long long page;
+
+ page = syscall(__NR_map_shadow_stack, 0, getpagesize(), flags);
+ if ((void *)page == MAP_FAILED) {
+ ksft_print_msg("map_shadow_stack() failed: %d\n", errno);
+ return 0;
+ }
+
+ return page;
+}
+
static int call_clone3(uint64_t flags, size_t size, enum test_mode test_mode)
{
struct __clone_args args = {
@@ -89,6 +136,20 @@ static int call_clone3(uint64_t flags, size_t size, enum test_mode test_mode)
case CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG:
args.exit_signal = 0x00000000000000f0ULL;
break;
+ case CLONE3_ARGS_SHADOW_STACK:
+ args.shadow_stack = get_shadow_stack_page(SHADOW_STACK_SET_TOKEN);
+ args.shadow_stack_size = getpagesize();
+ break;
+ case CLONE3_ARGS_SHADOW_STACK_NO_POINTER:
+ args.shadow_stack_size = getpagesize();
+ break;
+ case CLONE3_ARGS_SHADOW_STACK_NO_SIZE:
+ args.shadow_stack = get_shadow_stack_page(SHADOW_STACK_SET_TOKEN);
+ break;
+ case CLONE3_ARGS_SHADOW_STACK_NO_TOKEN:
+ args.shadow_stack = get_shadow_stack_page(0);
+ args.shadow_stack_size = getpagesize();
+ break;
}
memcpy(&args_ext.args, &args, sizeof(struct __clone_args));
@@ -179,6 +240,26 @@ static bool no_timenamespace(void)
return true;
}
+static bool have_shadow_stack(void)
+{
+ if (shadow_stack_supported) {
+ ksft_print_msg("Shadow stack supported\n");
+ return true;
+ }
+
+ return false;
+}
+
+static bool no_shadow_stack(void)
+{
+ if (!shadow_stack_supported) {
+ ksft_print_msg("Shadow stack not supported\n");
+ return true;
+ }
+
+ return false;
+}
+
static size_t page_size_plus_8(void)
{
return getpagesize() + 8;
@@ -322,6 +403,50 @@ static const struct test tests[] = {
.expected = -EINVAL,
.test_mode = CLONE3_ARGS_NO_TEST,
},
+ {
+ .name = "Shadow stack on system with shadow stack",
+ .flags = CLONE_VM,
+ .size = 0,
+ .expected = 0,
+ .e2big_valid = true,
+ .test_mode = CLONE3_ARGS_SHADOW_STACK,
+ .filter = no_shadow_stack,
+ },
+ {
+ .name = "Shadow stack with no pointer",
+ .flags = CLONE_VM,
+ .size = 0,
+ .expected = -EINVAL,
+ .e2big_valid = true,
+ .test_mode = CLONE3_ARGS_SHADOW_STACK_NO_POINTER,
+ },
+ {
+ .name = "Shadow stack with no size",
+ .flags = CLONE_VM,
+ .size = 0,
+ .expected = -EINVAL,
+ .e2big_valid = true,
+ .test_mode = CLONE3_ARGS_SHADOW_STACK_NO_SIZE,
+ .filter = no_shadow_stack,
+ },
+ {
+ .name = "Shadow stack with no token",
+ .flags = CLONE_VM,
+ .size = 0,
+ .expected = -EINVAL,
+ .e2big_valid = true,
+ .test_mode = CLONE3_ARGS_SHADOW_STACK_NO_TOKEN,
+ .filter = no_shadow_stack,
+ },
+ {
+ .name = "Shadow stack on system without shadow stack",
+ .flags = CLONE_VM,
+ .size = 0,
+ .expected = -EINVAL,
+ .e2big_valid = true,
+ .test_mode = CLONE3_ARGS_SHADOW_STACK,
+ .filter = have_shadow_stack,
+ },
};
int main(int argc, char *argv[])
@@ -329,9 +454,12 @@ int main(int argc, char *argv[])
size_t size;
int i;
+ enable_shadow_stack();
+
ksft_print_header();
ksft_set_plan(ARRAY_SIZE(tests));
test_clone3_supported();
+ test_shadow_stack_supported();
for (i = 0; i < ARRAY_SIZE(tests); i++)
test_clone3(&tests[i]);
diff --git a/tools/testing/selftests/clone3/clone3_selftests.h b/tools/testing/selftests/clone3/clone3_selftests.h
index 3d2663fe50ba..1011dae85098 100644
--- a/tools/testing/selftests/clone3/clone3_selftests.h
+++ b/tools/testing/selftests/clone3/clone3_selftests.h
@@ -31,6 +31,14 @@ struct __clone_args {
__aligned_u64 set_tid;
__aligned_u64 set_tid_size;
__aligned_u64 cgroup;
+#ifndef CLONE_ARGS_SIZE_VER2
+#define CLONE_ARGS_SIZE_VER2 88 /* sizeof third published struct */
+#endif
+ __aligned_u64 shadow_stack;
+ __aligned_u64 shadow_stack_size;
+#ifndef CLONE_ARGS_SIZE_VER3
+#define CLONE_ARGS_SIZE_VER3 104 /* sizeof fourth published struct */
+#endif
};
static pid_t sys_clone3(struct __clone_args *args, size_t size)
--
2.30.2
^ permalink raw reply related
* [PATCH RFT v5 6/7] selftests/clone3: Allow tests to flag if -E2BIG is a valid error code
From: Mark Brown @ 2024-02-03 0:05 UTC (permalink / raw)
To: Rick P. Edgecombe, Deepak Gupta, Szabolcs Nagy, H.J. Lu,
Florian Weimer, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
Mel Gorman, Daniel Bristot de Oliveira, Valentin Schneider,
Christian Brauner, Shuah Khan
Cc: linux-kernel, Catalin Marinas, Will Deacon, Kees Cook, jannh,
bsegall, linux-kselftest, linux-api, Mark Brown
In-Reply-To: <20240203-clone3-shadow-stack-v5-0-322c69598e4b@kernel.org>
The clone_args structure is extensible, with the syscall passing in the
length of the structure. Inside the kernel we use copy_struct_from_user()
to read the struct but this has the unfortunate side effect of silently
accepting some overrun in the structure size providing the extra data is
all zeros. This means that we can't discover the clone3() features that
the running kernel supports by simply probing with various struct sizes.
We need to check this for the benefit of test systems which run newer
kselftests on old kernels.
Add a flag which can be set on a test to indicate that clone3() may return
-E2BIG due to the use of newer struct versions. Currently no tests need
this but it will become an issue for testing clone3() support for shadow
stacks, the support for shadow stacks is already present on x86.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/clone3/clone3.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/tools/testing/selftests/clone3/clone3.c b/tools/testing/selftests/clone3/clone3.c
index 1108bd8e36d6..6adbfd14c841 100644
--- a/tools/testing/selftests/clone3/clone3.c
+++ b/tools/testing/selftests/clone3/clone3.c
@@ -39,6 +39,7 @@ struct test {
size_t size;
size_function size_function;
int expected;
+ bool e2big_valid;
enum test_mode test_mode;
filter_function filter;
};
@@ -141,6 +142,11 @@ static void test_clone3(const struct test *test)
ksft_print_msg("[%d] clone3() with flags says: %d expected %d\n",
getpid(), ret, test->expected);
if (ret != test->expected) {
+ if (test->e2big_valid && ret == -E2BIG) {
+ ksft_print_msg("Test reported -E2BIG\n");
+ ksft_test_result_skip("%s\n", test->name);
+ return;
+ }
ksft_print_msg(
"[%d] Result (%d) is different than expected (%d)\n",
getpid(), ret, test->expected);
--
2.30.2
^ permalink raw reply related
* [PATCH RFT v5 5/7] selftests/clone3: Factor more of main loop into test_clone3()
From: Mark Brown @ 2024-02-03 0:05 UTC (permalink / raw)
To: Rick P. Edgecombe, Deepak Gupta, Szabolcs Nagy, H.J. Lu,
Florian Weimer, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
Mel Gorman, Daniel Bristot de Oliveira, Valentin Schneider,
Christian Brauner, Shuah Khan
Cc: linux-kernel, Catalin Marinas, Will Deacon, Kees Cook, jannh,
bsegall, linux-kselftest, linux-api, Mark Brown
In-Reply-To: <20240203-clone3-shadow-stack-v5-0-322c69598e4b@kernel.org>
In order to make it easier to add more configuration for the tests and
more support for runtime detection of when tests can be run pass the
structure describing the tests into test_clone3() rather than picking
the arguments out of it and have that function do all the per-test work.
No functional change.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/clone3/clone3.c | 77 ++++++++++++++++-----------------
1 file changed, 37 insertions(+), 40 deletions(-)
diff --git a/tools/testing/selftests/clone3/clone3.c b/tools/testing/selftests/clone3/clone3.c
index 3c9bf0cd82a8..1108bd8e36d6 100644
--- a/tools/testing/selftests/clone3/clone3.c
+++ b/tools/testing/selftests/clone3/clone3.c
@@ -30,6 +30,19 @@ enum test_mode {
CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG,
};
+typedef bool (*filter_function)(void);
+typedef size_t (*size_function)(void);
+
+struct test {
+ const char *name;
+ uint64_t flags;
+ size_t size;
+ size_function size_function;
+ int expected;
+ enum test_mode test_mode;
+ filter_function filter;
+};
+
static int call_clone3(uint64_t flags, size_t size, enum test_mode test_mode)
{
struct __clone_args args = {
@@ -104,30 +117,40 @@ static int call_clone3(uint64_t flags, size_t size, enum test_mode test_mode)
return 0;
}
-static bool test_clone3(uint64_t flags, size_t size, int expected,
- enum test_mode test_mode)
+static void test_clone3(const struct test *test)
{
+ size_t size;
int ret;
+ if (test->filter && test->filter()) {
+ ksft_test_result_skip("%s\n", test->name);
+ return;
+ }
+
+ if (test->size_function)
+ size = test->size_function();
+ else
+ size = test->size;
+
+ ksft_print_msg("Running test '%s'\n", test->name);
+
ksft_print_msg(
"[%d] Trying clone3() with flags %#" PRIx64 " (size %zu)\n",
- getpid(), flags, size);
- ret = call_clone3(flags, size, test_mode);
+ getpid(), test->flags, size);
+ ret = call_clone3(test->flags, size, test->test_mode);
ksft_print_msg("[%d] clone3() with flags says: %d expected %d\n",
- getpid(), ret, expected);
- if (ret != expected) {
+ getpid(), ret, test->expected);
+ if (ret != test->expected) {
ksft_print_msg(
"[%d] Result (%d) is different than expected (%d)\n",
- getpid(), ret, expected);
- return false;
+ getpid(), ret, test->expected);
+ ksft_test_result_fail("%s\n", test->name);
+ return;
}
- return true;
+ ksft_test_result_pass("%s\n", test->name);
}
-typedef bool (*filter_function)(void);
-typedef size_t (*size_function)(void);
-
static bool not_root(void)
{
if (getuid() != 0) {
@@ -155,16 +178,6 @@ static size_t page_size_plus_8(void)
return getpagesize() + 8;
}
-struct test {
- const char *name;
- uint64_t flags;
- size_t size;
- size_function size_function;
- int expected;
- enum test_mode test_mode;
- filter_function filter;
-};
-
static const struct test tests[] = {
{
.name = "simple clone3()",
@@ -314,24 +327,8 @@ int main(int argc, char *argv[])
ksft_set_plan(ARRAY_SIZE(tests));
test_clone3_supported();
- for (i = 0; i < ARRAY_SIZE(tests); i++) {
- if (tests[i].filter && tests[i].filter()) {
- ksft_test_result_skip("%s\n", tests[i].name);
- continue;
- }
-
- if (tests[i].size_function)
- size = tests[i].size_function();
- else
- size = tests[i].size;
-
- ksft_print_msg("Running test '%s'\n", tests[i].name);
-
- ksft_test_result(test_clone3(tests[i].flags, size,
- tests[i].expected,
- tests[i].test_mode),
- "%s\n", tests[i].name);
- }
+ for (i = 0; i < ARRAY_SIZE(tests); i++)
+ test_clone3(&tests[i]);
ksft_finished();
}
--
2.30.2
^ permalink raw reply related
* [PATCH RFT v5 4/7] fork: Add shadow stack support to clone3()
From: Mark Brown @ 2024-02-03 0:05 UTC (permalink / raw)
To: Rick P. Edgecombe, Deepak Gupta, Szabolcs Nagy, H.J. Lu,
Florian Weimer, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
Mel Gorman, Daniel Bristot de Oliveira, Valentin Schneider,
Christian Brauner, Shuah Khan
Cc: linux-kernel, Catalin Marinas, Will Deacon, Kees Cook, jannh,
bsegall, linux-kselftest, linux-api, Mark Brown
In-Reply-To: <20240203-clone3-shadow-stack-v5-0-322c69598e4b@kernel.org>
Unlike with the normal stack there is no API for configuring the the shadow
stack for a new thread, instead the kernel will dynamically allocate a new
shadow stack with the same size as the normal stack. This appears to be due
to the shadow stack series having been in development since before the more
extensible clone3() was added rather than anything more deliberate.
Add a parameter to clone3() specifying the size of a shadow stack for
the newly created process. If no shadow stack is specified then the
existing implicit allocation behaviour is maintained.
If the architecture does not support shadow stacks the shadow stack size
parameter must be zero, architectures that do support the feature are
expected to enforce the same requirement on individual systems that lack
shadow stack support.
Update the existing x86 implementation to pay attention to the newly added
arguments, in order to maintain compatibility we use the existing behaviour
if no shadow stack is specified. Minimal validation is done of the supplied
parameters, detailed enforcement is left to when the thread is executed.
Since we are now using more fields from the kernel_clone_args we pass that
into the shadow stack code rather than individual fields.
At present this implemntation does not consume the shadow stack token
atomically as would be desirable.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
arch/x86/include/asm/shstk.h | 11 ++++--
arch/x86/kernel/process.c | 2 +-
arch/x86/kernel/shstk.c | 91 +++++++++++++++++++++++++++++++++-----------
include/linux/sched/task.h | 2 +
include/uapi/linux/sched.h | 13 +++++--
kernel/fork.c | 61 +++++++++++++++++++++++------
6 files changed, 137 insertions(+), 43 deletions(-)
diff --git a/arch/x86/include/asm/shstk.h b/arch/x86/include/asm/shstk.h
index 42fee8959df7..8be7b0a909c3 100644
--- a/arch/x86/include/asm/shstk.h
+++ b/arch/x86/include/asm/shstk.h
@@ -6,6 +6,7 @@
#include <linux/types.h>
struct task_struct;
+struct kernel_clone_args;
struct ksignal;
#ifdef CONFIG_X86_USER_SHADOW_STACK
@@ -16,8 +17,8 @@ struct thread_shstk {
long shstk_prctl(struct task_struct *task, int option, unsigned long arg2);
void reset_thread_features(void);
-unsigned long shstk_alloc_thread_stack(struct task_struct *p, unsigned long clone_flags,
- unsigned long stack_size);
+unsigned long shstk_alloc_thread_stack(struct task_struct *p,
+ const struct kernel_clone_args *args);
void shstk_free(struct task_struct *p);
int setup_signal_shadow_stack(struct ksignal *ksig);
int restore_signal_shadow_stack(void);
@@ -26,8 +27,10 @@ static inline long shstk_prctl(struct task_struct *task, int option,
unsigned long arg2) { return -EINVAL; }
static inline void reset_thread_features(void) {}
static inline unsigned long shstk_alloc_thread_stack(struct task_struct *p,
- unsigned long clone_flags,
- unsigned long stack_size) { return 0; }
+ const struct kernel_clone_args *args)
+{
+ return 0;
+}
static inline void shstk_free(struct task_struct *p) {}
static inline int setup_signal_shadow_stack(struct ksignal *ksig) { return 0; }
static inline int restore_signal_shadow_stack(void) { return 0; }
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index ab49ade31b0d..d2bfcd44de05 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -207,7 +207,7 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
* is disabled, new_ssp will remain 0, and fpu_clone() will know not to
* update it.
*/
- new_ssp = shstk_alloc_thread_stack(p, clone_flags, args->stack_size);
+ new_ssp = shstk_alloc_thread_stack(p, args);
if (IS_ERR_VALUE(new_ssp))
return PTR_ERR((void *)new_ssp);
diff --git a/arch/x86/kernel/shstk.c b/arch/x86/kernel/shstk.c
index 59e15dd8d0f8..24d0e9b825da 100644
--- a/arch/x86/kernel/shstk.c
+++ b/arch/x86/kernel/shstk.c
@@ -191,44 +191,89 @@ void reset_thread_features(void)
current->thread.features_locked = 0;
}
-unsigned long shstk_alloc_thread_stack(struct task_struct *tsk, unsigned long clone_flags,
- unsigned long stack_size)
+static bool shstk_consume_token(struct task_struct *tsk,
+ unsigned long addr)
+{
+ /*
+ * SSP is aligned, so reserved bits and mode bit are a zero, just mark
+ * the token 64-bit.
+ */
+ u64 expected = (addr - SS_FRAME_SIZE) | BIT(0);
+ u64 val;
+
+ /* This should really be an atomic cpmxchg. It is not. */
+ __get_user(val, (__user u64 *)addr);
+ if (val != expected)
+ return false;
+
+ if (write_user_shstk_64((u64 __user *)addr, 0))
+ return false;
+
+ return true;
+}
+
+unsigned long shstk_alloc_thread_stack(struct task_struct *tsk,
+ const struct kernel_clone_args *args)
{
struct thread_shstk *shstk = &tsk->thread.shstk;
+ unsigned long clone_flags = args->flags;
unsigned long addr, size;
/*
* If shadow stack is not enabled on the new thread, skip any
- * switch to a new shadow stack.
+ * implicit switch to a new shadow stack and reject attempts to
+ * explciitly specify one.
*/
- if (!features_enabled(ARCH_SHSTK_SHSTK))
- return 0;
+ if (!features_enabled(ARCH_SHSTK_SHSTK)) {
+ if (args->shadow_stack || args->shadow_stack_size)
+ return (unsigned long)ERR_PTR(-EINVAL);
- /*
- * For CLONE_VFORK the child will share the parents shadow stack.
- * Make sure to clear the internal tracking of the thread shadow
- * stack so the freeing logic run for child knows to leave it alone.
- */
- if (clone_flags & CLONE_VFORK) {
- shstk->base = 0;
- shstk->size = 0;
return 0;
}
/*
- * For !CLONE_VM the child will use a copy of the parents shadow
- * stack.
+ * If the user specified a shadow stack then do some basic
+ * validation and use it, otherwise fall back to a default
+ * shadow stack size if the clone_flags don't indicate an
+ * allocation is unneeded.
*/
- if (!(clone_flags & CLONE_VM))
- return 0;
+ if (args->shadow_stack) {
+ addr = args->shadow_stack;
+ size = args->shadow_stack_size;
- size = adjust_shstk_size(stack_size);
- addr = alloc_shstk(0, size, 0, false);
- if (IS_ERR_VALUE(addr))
- return addr;
+ /* There should be a valid token at the top of the stack. */
+ if (!shstk_consume_token(tsk, addr + size - sizeof(u64)))
+ return (unsigned long)ERR_PTR(-EINVAL);
+ } else {
+ /*
+ * For CLONE_VFORK the child will share the parents
+ * shadow stack. Make sure to clear the internal
+ * tracking of the thread shadow stack so the freeing
+ * logic run for child knows to leave it alone.
+ */
+ if (clone_flags & CLONE_VFORK) {
+ shstk->base = 0;
+ shstk->size = 0;
+ return 0;
+ }
- shstk->base = addr;
- shstk->size = size;
+ /*
+ * For !CLONE_VM the child will use a copy of the
+ * parents shadow stack.
+ */
+ if (!(clone_flags & CLONE_VM))
+ return 0;
+
+ size = args->stack_size;
+ size = adjust_shstk_size(size);
+ addr = alloc_shstk(0, size, 0, false);
+ if (IS_ERR_VALUE(addr))
+ return addr;
+
+ /* We allocated the shadow stack, we should deallocate it. */
+ shstk->base = addr;
+ shstk->size = size;
+ }
return addr + size;
}
diff --git a/include/linux/sched/task.h b/include/linux/sched/task.h
index d362aacf9f89..dd577e8dc881 100644
--- a/include/linux/sched/task.h
+++ b/include/linux/sched/task.h
@@ -43,6 +43,8 @@ struct kernel_clone_args {
void *fn_arg;
struct cgroup *cgrp;
struct css_set *cset;
+ unsigned long shadow_stack;
+ unsigned long shadow_stack_size;
};
/*
diff --git a/include/uapi/linux/sched.h b/include/uapi/linux/sched.h
index 3bac0a8ceab2..8b7af52548fd 100644
--- a/include/uapi/linux/sched.h
+++ b/include/uapi/linux/sched.h
@@ -84,6 +84,10 @@
* kernel's limit of nested PID namespaces.
* @cgroup: If CLONE_INTO_CGROUP is specified set this to
* a file descriptor for the cgroup.
+ * @shadow_stack: Pointer to the memory allocated for the child
+ * shadow stack.
+ * @shadow_stack_size: Specify the size of the shadow stack for
+ * the child process.
*
* The structure is versioned by size and thus extensible.
* New struct members must go at the end of the struct and
@@ -101,12 +105,15 @@ struct clone_args {
__aligned_u64 set_tid;
__aligned_u64 set_tid_size;
__aligned_u64 cgroup;
+ __aligned_u64 shadow_stack;
+ __aligned_u64 shadow_stack_size;
};
#endif
-#define CLONE_ARGS_SIZE_VER0 64 /* sizeof first published struct */
-#define CLONE_ARGS_SIZE_VER1 80 /* sizeof second published struct */
-#define CLONE_ARGS_SIZE_VER2 88 /* sizeof third published struct */
+#define CLONE_ARGS_SIZE_VER0 64 /* sizeof first published struct */
+#define CLONE_ARGS_SIZE_VER1 80 /* sizeof second published struct */
+#define CLONE_ARGS_SIZE_VER2 88 /* sizeof third published struct */
+#define CLONE_ARGS_SIZE_VER3 104 /* sizeof fourth published struct */
/*
* Scheduling policies
diff --git a/kernel/fork.c b/kernel/fork.c
index 0d944e92a43f..fca041cc2b8a 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -123,6 +123,11 @@
*/
#define MAX_THREADS FUTEX_TID_MASK
+/*
+ * Require that shadow stacks can store at least one element
+ */
+#define SHADOW_STACK_SIZE_MIN sizeof(void *)
+
/*
* Protected counters by write_lock_irq(&tasklist_lock)
*/
@@ -3062,7 +3067,9 @@ noinline static int copy_clone_args_from_user(struct kernel_clone_args *kargs,
CLONE_ARGS_SIZE_VER1);
BUILD_BUG_ON(offsetofend(struct clone_args, cgroup) !=
CLONE_ARGS_SIZE_VER2);
- BUILD_BUG_ON(sizeof(struct clone_args) != CLONE_ARGS_SIZE_VER2);
+ BUILD_BUG_ON(offsetofend(struct clone_args, shadow_stack_size) !=
+ CLONE_ARGS_SIZE_VER3);
+ BUILD_BUG_ON(sizeof(struct clone_args) != CLONE_ARGS_SIZE_VER3);
if (unlikely(usize > PAGE_SIZE))
return -E2BIG;
@@ -3095,16 +3102,18 @@ noinline static int copy_clone_args_from_user(struct kernel_clone_args *kargs,
return -EINVAL;
*kargs = (struct kernel_clone_args){
- .flags = args.flags,
- .pidfd = u64_to_user_ptr(args.pidfd),
- .child_tid = u64_to_user_ptr(args.child_tid),
- .parent_tid = u64_to_user_ptr(args.parent_tid),
- .exit_signal = args.exit_signal,
- .stack = args.stack,
- .stack_size = args.stack_size,
- .tls = args.tls,
- .set_tid_size = args.set_tid_size,
- .cgroup = args.cgroup,
+ .flags = args.flags,
+ .pidfd = u64_to_user_ptr(args.pidfd),
+ .child_tid = u64_to_user_ptr(args.child_tid),
+ .parent_tid = u64_to_user_ptr(args.parent_tid),
+ .exit_signal = args.exit_signal,
+ .stack = args.stack,
+ .stack_size = args.stack_size,
+ .tls = args.tls,
+ .set_tid_size = args.set_tid_size,
+ .cgroup = args.cgroup,
+ .shadow_stack = args.shadow_stack,
+ .shadow_stack_size = args.shadow_stack_size,
};
if (args.set_tid &&
@@ -3145,6 +3154,34 @@ static inline bool clone3_stack_valid(struct kernel_clone_args *kargs)
return true;
}
+/**
+ * clone3_shadow_stack_valid - check and prepare shadow stack
+ * @kargs: kernel clone args
+ *
+ * Verify that shadow stacks are only enabled if supported.
+ */
+static inline bool clone3_shadow_stack_valid(struct kernel_clone_args *kargs)
+{
+ if (kargs->shadow_stack) {
+ if (!kargs->shadow_stack_size)
+ return false;
+
+ if (kargs->shadow_stack_size < SHADOW_STACK_SIZE_MIN)
+ return false;
+
+ if (kargs->shadow_stack_size > rlimit(RLIMIT_STACK))
+ return false;
+
+ /*
+ * The architecture must check support on the specific
+ * machine.
+ */
+ return IS_ENABLED(CONFIG_ARCH_HAS_USER_SHADOW_STACK);
+ } else {
+ return !kargs->shadow_stack_size;
+ }
+}
+
static bool clone3_args_valid(struct kernel_clone_args *kargs)
{
/* Verify that no unknown flags are passed along. */
@@ -3167,7 +3204,7 @@ static bool clone3_args_valid(struct kernel_clone_args *kargs)
kargs->exit_signal)
return false;
- if (!clone3_stack_valid(kargs))
+ if (!clone3_stack_valid(kargs) || !clone3_shadow_stack_valid(kargs))
return false;
return true;
--
2.30.2
^ permalink raw reply related
* [PATCH RFT v5 3/7] mm: Introduce ARCH_HAS_USER_SHADOW_STACK
From: Mark Brown @ 2024-02-03 0:04 UTC (permalink / raw)
To: Rick P. Edgecombe, Deepak Gupta, Szabolcs Nagy, H.J. Lu,
Florian Weimer, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
Mel Gorman, Daniel Bristot de Oliveira, Valentin Schneider,
Christian Brauner, Shuah Khan
Cc: linux-kernel, Catalin Marinas, Will Deacon, Kees Cook, jannh,
bsegall, linux-kselftest, linux-api, Mark Brown,
David Hildenbrand
In-Reply-To: <20240203-clone3-shadow-stack-v5-0-322c69598e4b@kernel.org>
Since multiple architectures have support for shadow stacks and we need to
select support for this feature in several places in the generic code
provide a generic config option that the architectures can select.
Suggested-by: David Hildenbrand <david@redhat.com>
Acked-by: David Hildenbrand <david@redhat.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
arch/x86/Kconfig | 1 +
fs/proc/task_mmu.c | 2 +-
include/linux/mm.h | 2 +-
mm/Kconfig | 6 ++++++
4 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 5edec175b9bf..34553911d07d 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1952,6 +1952,7 @@ config X86_USER_SHADOW_STACK
depends on AS_WRUSS
depends on X86_64
select ARCH_USES_HIGH_VMA_FLAGS
+ select ARCH_HAS_USER_SHADOW_STACK
select X86_CET
help
Shadow stack protection is a hardware feature that detects function
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 3f78ebbb795f..ff2c601f7d1c 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -700,7 +700,7 @@ static void show_smap_vma_flags(struct seq_file *m, struct vm_area_struct *vma)
#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
[ilog2(VM_UFFD_MINOR)] = "ui",
#endif /* CONFIG_HAVE_ARCH_USERFAULTFD_MINOR */
-#ifdef CONFIG_X86_USER_SHADOW_STACK
+#ifdef CONFIG_ARCH_HAS_USER_SHADOW_STACK
[ilog2(VM_SHADOW_STACK)] = "ss",
#endif
};
diff --git a/include/linux/mm.h b/include/linux/mm.h
index f5a97dec5169..c0a782eda803 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -341,7 +341,7 @@ extern unsigned int kobjsize(const void *objp);
#endif
#endif /* CONFIG_ARCH_HAS_PKEYS */
-#ifdef CONFIG_X86_USER_SHADOW_STACK
+#ifdef CONFIG_ARCH_HAS_USER_SHADOW_STACK
/*
* VM_SHADOW_STACK should not be set with VM_SHARED because of lack of
* support core mm.
diff --git a/mm/Kconfig b/mm/Kconfig
index ffc3a2ba3a8c..9119e016777a 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -1261,6 +1261,12 @@ config LOCK_MM_AND_FIND_VMA
config IOMMU_MM_DATA
bool
+config ARCH_HAS_USER_SHADOW_STACK
+ bool
+ help
+ The architecture has hardware support for userspace shadow call
+ stacks (eg, x86 CET, arm64 GCS or RISC-V Zicfiss).
+
source "mm/damon/Kconfig"
endmenu
--
2.30.2
^ permalink raw reply related
* [PATCH RFT v5 2/7] selftests: Provide helper header for shadow stack testing
From: Mark Brown @ 2024-02-03 0:04 UTC (permalink / raw)
To: Rick P. Edgecombe, Deepak Gupta, Szabolcs Nagy, H.J. Lu,
Florian Weimer, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
Mel Gorman, Daniel Bristot de Oliveira, Valentin Schneider,
Christian Brauner, Shuah Khan
Cc: linux-kernel, Catalin Marinas, Will Deacon, Kees Cook, jannh,
bsegall, linux-kselftest, linux-api, Mark Brown
In-Reply-To: <20240203-clone3-shadow-stack-v5-0-322c69598e4b@kernel.org>
While almost all users of shadow stacks should be relying on the dynamic
linker and libc to enable the feature there are several low level test
programs where it is useful to enable without any libc support, allowing
testing without full system enablement. This low level testing is helpful
during bringup of the support itself, and also in enabling coverage by
automated testing without needing all system components in the target root
filesystems to have enablement.
Provide a header with helpers for this purpose, intended for use only by
test programs directly exercising shadow stack interfaces.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/ksft_shstk.h | 63 ++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/tools/testing/selftests/ksft_shstk.h b/tools/testing/selftests/ksft_shstk.h
new file mode 100644
index 000000000000..85d0747c1802
--- /dev/null
+++ b/tools/testing/selftests/ksft_shstk.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Helpers for shadow stack enablement, this is intended to only be
+ * used by low level test programs directly exercising interfaces for
+ * working with shadow stacks.
+ *
+ * Copyright (C) 2024 ARM Ltd.
+ */
+
+#ifndef __KSFT_SHSTK_H
+#define __KSFT_SHSTK_H
+
+#include <asm/mman.h>
+
+/* This is currently only defined for x86 */
+#ifndef SHADOW_STACK_SET_TOKEN
+#define SHADOW_STACK_SET_TOKEN (1ULL << 0)
+#endif
+
+static bool shadow_stack_enabled;
+
+#ifdef __x86_64__
+#define ARCH_SHSTK_ENABLE 0x5001
+#define ARCH_SHSTK_SHSTK (1ULL << 0)
+
+#define ARCH_PRCTL(arg1, arg2) \
+({ \
+ long _ret; \
+ register long _num asm("eax") = __NR_arch_prctl; \
+ register long _arg1 asm("rdi") = (long)(arg1); \
+ register long _arg2 asm("rsi") = (long)(arg2); \
+ \
+ asm volatile ( \
+ "syscall\n" \
+ : "=a"(_ret) \
+ : "r"(_arg1), "r"(_arg2), \
+ "0"(_num) \
+ : "rcx", "r11", "memory", "cc" \
+ ); \
+ _ret; \
+})
+
+#define ENABLE_SHADOW_STACK
+static inline __attribute__((always_inline)) void enable_shadow_stack(void)
+{
+ int ret = ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK);
+ if (ret == 0)
+ shadow_stack_enabled = true;
+}
+
+#endif
+
+#ifndef __NR_map_shadow_stack
+#define __NR_map_shadow_stack 453
+#endif
+
+#ifndef ENABLE_SHADOW_STACK
+static inline void enable_shadow_stack(void) { }
+#endif
+
+#endif
+
+
--
2.30.2
^ permalink raw reply related
* [PATCH RFT v5 1/7] Documentation: userspace-api: Add shadow stack API documentation
From: Mark Brown @ 2024-02-03 0:04 UTC (permalink / raw)
To: Rick P. Edgecombe, Deepak Gupta, Szabolcs Nagy, H.J. Lu,
Florian Weimer, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
Mel Gorman, Daniel Bristot de Oliveira, Valentin Schneider,
Christian Brauner, Shuah Khan
Cc: linux-kernel, Catalin Marinas, Will Deacon, Kees Cook, jannh,
bsegall, linux-kselftest, linux-api, Mark Brown
In-Reply-To: <20240203-clone3-shadow-stack-v5-0-322c69598e4b@kernel.org>
There are a number of architectures with shadow stack features which we are
presenting to userspace with as consistent an API as we can (though there
are some architecture specifics). Especially given that there are some
important considerations for userspace code interacting directly with the
feature let's provide some documentation covering the common aspects.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
Documentation/userspace-api/index.rst | 1 +
Documentation/userspace-api/shadow_stack.rst | 41 ++++++++++++++++++++++++++++
2 files changed, 42 insertions(+)
diff --git a/Documentation/userspace-api/index.rst b/Documentation/userspace-api/index.rst
index 09f61bd2ac2e..c142183d9c98 100644
--- a/Documentation/userspace-api/index.rst
+++ b/Documentation/userspace-api/index.rst
@@ -27,6 +27,7 @@ place where this information is gathered.
iommufd
media/index
netlink/index
+ shadow_stack
sysfs-platform_profile
vduse
futex2
diff --git a/Documentation/userspace-api/shadow_stack.rst b/Documentation/userspace-api/shadow_stack.rst
new file mode 100644
index 000000000000..c6e5ab795b60
--- /dev/null
+++ b/Documentation/userspace-api/shadow_stack.rst
@@ -0,0 +1,41 @@
+=============
+Shadow Stacks
+=============
+
+Introduction
+============
+
+Several architectures have features which provide backward edge
+control flow protection through a hardware maintained stack, only
+writeable by userspace through very limited operations. This feature
+is referred to as shadow stacks on Linux, on x86 it is part of Intel
+Control Enforcement Technology (CET), on arm64 it is Guarded Control
+Stacks feature (FEAT_GCS) and for RISC-V it is the Zicfiss extension.
+It is expected that this feature will normally be managed by the
+system dynamic linker and libc in ways broadly transparent to
+application code, this document covers interfaces and considerations
+
+
+Enabling
+========
+
+Shadow stacks default to disabled when a userspace process is
+executed, they can be enabled for the current thread with a syscall:
+
+ - For x86 the ARCH_SHSTK_ENABLE arch_prctl()
+
+It is expected that this will normally be done by the dynamic linker.
+Any new threads created by a thread with shadow stacks enabled will
+themsleves have shadow stacks enabled.
+
+
+Enablement considerations
+=========================
+
+- Returning from the function that enables shadow stacks without first
+ disabling them will cause a shadow stack exception. This includes
+ any syscall wrapper or other library functions, the syscall will need
+ to be inlined.
+- A lock feature allows userspace to prevent disabling of shadow stacks.
+- This that change the stack context like longjmp() or use of ucontext
+ changes on signal return will need support from libc.
--
2.30.2
^ permalink raw reply related
* [PATCH RFT v5 0/7] fork: Support shadow stacks in clone3()
From: Mark Brown @ 2024-02-03 0:04 UTC (permalink / raw)
To: Rick P. Edgecombe, Deepak Gupta, Szabolcs Nagy, H.J. Lu,
Florian Weimer, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
Mel Gorman, Daniel Bristot de Oliveira, Valentin Schneider,
Christian Brauner, Shuah Khan
Cc: linux-kernel, Catalin Marinas, Will Deacon, Kees Cook, jannh,
bsegall, linux-kselftest, linux-api, Mark Brown,
David Hildenbrand
The kernel has recently added support for shadow stacks, currently
x86 only using their CET feature but both arm64 and RISC-V have
equivalent features (GCS and Zicfiss respectively), I am actively
working on GCS[1]. With shadow stacks the hardware maintains an
additional stack containing only the return addresses for branch
instructions which is not generally writeable by userspace and ensures
that any returns are to the recorded addresses. This provides some
protection against ROP attacks and making it easier to collect call
stacks. These shadow stacks are allocated in the address space of the
userspace process.
Our API for shadow stacks does not currently offer userspace any
flexiblity for managing the allocation of shadow stacks for newly
created threads, instead the kernel allocates a new shadow stack with
the same size as the normal stack whenever a thread is created with the
feature enabled. The stacks allocated in this way are freed by the
kernel when the thread exits or shadow stacks are disabled for the
thread. This lack of flexibility and control isn't ideal, in the vast
majority of cases the shadow stack will be over allocated and the
implicit allocation and deallocation is not consistent with other
interfaces. As far as I can tell the interface is done in this manner
mainly because the shadow stack patches were in development since before
clone3() was implemented.
Since clone3() is readily extensible let's add support for specifying a
shadow stack when creating a new thread or process in a similar manner
to how the normal stack is specified, keeping the current implicit
allocation behaviour if one is not specified either with clone3() or
through the use of clone(). The user must provide a shadow stack
address and size, this must point to memory mapped for use as a shadow
stackby map_shadow_stack() with a shadow stack token at the top of the
stack.
Please note that the x86 portions of this code are build tested only, I
don't appear to have a system that can run CET avaible to me, I have
done testing with an integration into my pending work for GCS. There is
some possibility that the arm64 implementation may require the use of
clone3() and explicit userspace allocation of shadow stacks, this is
still under discussion.
Please further note that the token consumption done by clone3() is not
currently implemented in an atomic fashion, Rick indicated that he would
look into fixing this if people are OK with the implementation.
A new architecture feature Kconfig option for shadow stacks is added as
here, this was suggested as part of the review comments for the arm64
GCS series and since we need to detect if shadow stacks are supported it
seemed sensible to roll it in here.
[1] https://lore.kernel.org/r/20231009-arm64-gcs-v6-0-78e55deaa4dd@kernel.org/
Signed-off-by: Mark Brown <broonie@kernel.org>
---
Changes in v5:
- Rebase onto v6.8-rc2.
- Rework ABI to have the user allocate the shadow stack memory with
map_shadow_stack() and a token.
- Force inlining of the x86 shadow stack enablement.
- Move shadow stack enablement out into a shared header for reuse by
other tests.
- Link to v4: https://lore.kernel.org/r/20231128-clone3-shadow-stack-v4-0-8b28ffe4f676@kernel.org
Changes in v4:
- Formatting changes.
- Use a define for minimum shadow stack size and move some basic
validation to fork.c.
- Link to v3: https://lore.kernel.org/r/20231120-clone3-shadow-stack-v3-0-a7b8ed3e2acc@kernel.org
Changes in v3:
- Rebase onto v6.7-rc2.
- Remove stale shadow_stack in internal kargs.
- If a shadow stack is specified unconditionally use it regardless of
CLONE_ parameters.
- Force enable shadow stacks in the selftest.
- Update changelogs for RISC-V feature rename.
- Link to v2: https://lore.kernel.org/r/20231114-clone3-shadow-stack-v2-0-b613f8681155@kernel.org
Changes in v2:
- Rebase onto v6.7-rc1.
- Remove ability to provide preallocated shadow stack, just specify the
desired size.
- Link to v1: https://lore.kernel.org/r/20231023-clone3-shadow-stack-v1-0-d867d0b5d4d0@kernel.org
---
Mark Brown (7):
Documentation: userspace-api: Add shadow stack API documentation
selftests: Provide helper header for shadow stack testing
mm: Introduce ARCH_HAS_USER_SHADOW_STACK
fork: Add shadow stack support to clone3()
selftests/clone3: Factor more of main loop into test_clone3()
selftests/clone3: Allow tests to flag if -E2BIG is a valid error code
selftests/clone3: Test shadow stack support
Documentation/userspace-api/index.rst | 1 +
Documentation/userspace-api/shadow_stack.rst | 41 +++++
arch/x86/Kconfig | 1 +
arch/x86/include/asm/shstk.h | 11 +-
arch/x86/kernel/process.c | 2 +-
arch/x86/kernel/shstk.c | 91 +++++++---
fs/proc/task_mmu.c | 2 +-
include/linux/mm.h | 2 +-
include/linux/sched/task.h | 2 +
include/uapi/linux/sched.h | 13 +-
kernel/fork.c | 61 +++++--
mm/Kconfig | 6 +
tools/testing/selftests/clone3/clone3.c | 211 ++++++++++++++++++----
tools/testing/selftests/clone3/clone3_selftests.h | 8 +
tools/testing/selftests/ksft_shstk.h | 63 +++++++
15 files changed, 430 insertions(+), 85 deletions(-)
---
base-commit: 41bccc98fb7931d63d03f326a746ac4d429c1dd3
change-id: 20231019-clone3-shadow-stack-15d40d2bf536
Best regards,
--
Mark Brown <broonie@kernel.org>
^ permalink raw reply
* Re: [net-next 0/3] Per epoll context busy poll support
From: Joe Damato @ 2024-02-02 20:55 UTC (permalink / raw)
To: Samudrala, Sridhar
Cc: Jakub Kicinski, Eric Dumazet, netdev, linux-kernel, chuck.lever,
jlayton, linux-api, brauner, davem, alexander.duyck, Wei Wang,
Amritha Nambiar
In-Reply-To: <0d030b68-0371-4460-8d76-cad129888496@intel.com>
On Fri, Feb 02, 2024 at 02:50:58PM -0600, Samudrala, Sridhar wrote:
>
>
> On 2/2/2024 2:23 PM, Joe Damato wrote:
> >On Fri, Feb 02, 2024 at 11:58:28AM -0800, Jakub Kicinski wrote:
> >>On Fri, 2 Feb 2024 11:33:33 -0800 Joe Damato wrote:
> >>>On Fri, Feb 02, 2024 at 10:22:39AM -0800, Jakub Kicinski wrote:
> >>>>On Fri, 2 Feb 2024 11:23:28 -0600 Samudrala, Sridhar wrote:
> >>>>>I think you should be able to get this functionality via the netdev-genl
> >>>>>API to get napi parameters. It returns ifindex as one of the parameters
> >>>>>and you should able to get the name from ifindex.
> >>>>>
> >>>>>$ ./cli.py --spec netdev.yaml --do napi-get --json='{"id": 593}'
> >>>>>{'id': 593, 'ifindex': 12, 'irq': 291, 'pid': 3727}
> >>>>
> >>>>FWIW we also have a C library to access those. Out of curiosity what's
> >>>>the programming language you'd use in user space, Joe?
> >>>
> >>>I am using C from user space.
> >>
> >>Ah, great! Here comes the advert.. :)
> >>
> >> make -C tools/net/ynl/
> >>
> >>will generate the C lib for you. tools/net/ynl/generated/netdev-user.h
> >>will have the full API. There are some samples in
> >>tools/net/ynl/samples/. And basic info also here:
> >>https://docs.kernel.org/next/userspace-api/netlink/intro-specs.html#ynl-lib
> >>
> >>You should be able to convert Sridhar's cli.py into an equivalent
> >>in C in ~10 LoC.
> >>
> >>>Curious what you think about
> >>>SIOCGIFNAME_BY_NAPI_ID, Jakub? I think it would be very useful, but not
> >>>sure if such an extension would be accepted. I can send an RFC, if you'd
> >>>like to take a look and consider it. I know you are busy and I don't want
> >>>to add too much noise to the list if I can help it :)
> >>
> >>Nothing wrong with it in particular, but we went with the netlink API
> >>because all the objects are related. There are interrupts, NAPI
> >>instances, queues, page pools etc. and we need to show all sort of
> >>attributes, capabilities, stats as well as the linking. So getsockopts
> >>may not scale, or we'd need to create a monster mux getsockopt?
> >>Plus with some luck the netlink API will send you notifications of
> >>things changing.
> >
> >Yes this all makes sense. The notification on changes would be excellent,
> >especially if NAPI IDs get changed for some reason (e.g. the queue count
> >is adjusted or the queues are rebuilt by the driver for some reason like a
> >timeout, etc).
> >
> >I think the issue I'm solving with SIOCGIFNAME_BY_NAPI_ID is related, but
> >different.
> >
> >In my case, SIOCGIFNAME_BY_NAPI_ID identifies which NIC a specific fd from
> >accept arrived from.
> >
> >AFAICT, the netlink API wouldn't be able to help me answer that question. I
> >could use SIOCGIFNAME_BY_NAPI_ID to tell me which NIC the fd is from and
> >then use netlink to figure out which CPU to bind to (for example), but I
> >think SIOCGIFNAME_BY_NAPI_ID is still needed.
>
> The napi-get netlink api takes napi_id and returns ifindex, irq and pid
> associated with the napi id. You can then pass ifindex to the SIOCGIFNAME
> ioctl to get the interface name. So it is definitely possible without the
> need for the new SIOCGIFNAME_BY_NAPI_ID
Ah, I see. OK. In that case, I won't bother with the RFC for
SIOCGIFNAME_BY_NAPI_ID.
I'll give your suggestion a try next week after I make the driver changes
needed to support it.
^ permalink raw reply
* Re: [net-next 0/3] Per epoll context busy poll support
From: Samudrala, Sridhar @ 2024-02-02 20:50 UTC (permalink / raw)
To: Joe Damato, Jakub Kicinski
Cc: Eric Dumazet, netdev, linux-kernel, chuck.lever, jlayton,
linux-api, brauner, davem, alexander.duyck, Wei Wang,
Amritha Nambiar
In-Reply-To: <20240202202344.GA9283@fastly.com>
On 2/2/2024 2:23 PM, Joe Damato wrote:
> On Fri, Feb 02, 2024 at 11:58:28AM -0800, Jakub Kicinski wrote:
>> On Fri, 2 Feb 2024 11:33:33 -0800 Joe Damato wrote:
>>> On Fri, Feb 02, 2024 at 10:22:39AM -0800, Jakub Kicinski wrote:
>>>> On Fri, 2 Feb 2024 11:23:28 -0600 Samudrala, Sridhar wrote:
>>>>> I think you should be able to get this functionality via the netdev-genl
>>>>> API to get napi parameters. It returns ifindex as one of the parameters
>>>>> and you should able to get the name from ifindex.
>>>>>
>>>>> $ ./cli.py --spec netdev.yaml --do napi-get --json='{"id": 593}'
>>>>> {'id': 593, 'ifindex': 12, 'irq': 291, 'pid': 3727}
>>>>
>>>> FWIW we also have a C library to access those. Out of curiosity what's
>>>> the programming language you'd use in user space, Joe?
>>>
>>> I am using C from user space.
>>
>> Ah, great! Here comes the advert.. :)
>>
>> make -C tools/net/ynl/
>>
>> will generate the C lib for you. tools/net/ynl/generated/netdev-user.h
>> will have the full API. There are some samples in
>> tools/net/ynl/samples/. And basic info also here:
>> https://docs.kernel.org/next/userspace-api/netlink/intro-specs.html#ynl-lib
>>
>> You should be able to convert Sridhar's cli.py into an equivalent
>> in C in ~10 LoC.
>>
>>> Curious what you think about
>>> SIOCGIFNAME_BY_NAPI_ID, Jakub? I think it would be very useful, but not
>>> sure if such an extension would be accepted. I can send an RFC, if you'd
>>> like to take a look and consider it. I know you are busy and I don't want
>>> to add too much noise to the list if I can help it :)
>>
>> Nothing wrong with it in particular, but we went with the netlink API
>> because all the objects are related. There are interrupts, NAPI
>> instances, queues, page pools etc. and we need to show all sort of
>> attributes, capabilities, stats as well as the linking. So getsockopts
>> may not scale, or we'd need to create a monster mux getsockopt?
>> Plus with some luck the netlink API will send you notifications of
>> things changing.
>
> Yes this all makes sense. The notification on changes would be excellent,
> especially if NAPI IDs get changed for some reason (e.g. the queue count
> is adjusted or the queues are rebuilt by the driver for some reason like a
> timeout, etc).
>
> I think the issue I'm solving with SIOCGIFNAME_BY_NAPI_ID is related, but
> different.
>
> In my case, SIOCGIFNAME_BY_NAPI_ID identifies which NIC a specific fd from
> accept arrived from.
>
> AFAICT, the netlink API wouldn't be able to help me answer that question. I
> could use SIOCGIFNAME_BY_NAPI_ID to tell me which NIC the fd is from and
> then use netlink to figure out which CPU to bind to (for example), but I
> think SIOCGIFNAME_BY_NAPI_ID is still needed.
The napi-get netlink api takes napi_id and returns ifindex, irq and pid
associated with the napi id. You can then pass ifindex to the
SIOCGIFNAME ioctl to get the interface name. So it is definitely
possible without the need for the new SIOCGIFNAME_BY_NAPI_ID
>
> I'll send an RFC about for that shortly, hope that's OK.
>
^ permalink raw reply
* Re: [net-next 0/3] Per epoll context busy poll support
From: Joe Damato @ 2024-02-02 20:23 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Samudrala, Sridhar, Eric Dumazet, netdev, linux-kernel,
chuck.lever, jlayton, linux-api, brauner, davem, alexander.duyck,
Wei Wang, Amritha Nambiar
In-Reply-To: <20240202115828.6fd125bf@kernel.org>
On Fri, Feb 02, 2024 at 11:58:28AM -0800, Jakub Kicinski wrote:
> On Fri, 2 Feb 2024 11:33:33 -0800 Joe Damato wrote:
> > On Fri, Feb 02, 2024 at 10:22:39AM -0800, Jakub Kicinski wrote:
> > > On Fri, 2 Feb 2024 11:23:28 -0600 Samudrala, Sridhar wrote:
> > > > I think you should be able to get this functionality via the netdev-genl
> > > > API to get napi parameters. It returns ifindex as one of the parameters
> > > > and you should able to get the name from ifindex.
> > > >
> > > > $ ./cli.py --spec netdev.yaml --do napi-get --json='{"id": 593}'
> > > > {'id': 593, 'ifindex': 12, 'irq': 291, 'pid': 3727}
> > >
> > > FWIW we also have a C library to access those. Out of curiosity what's
> > > the programming language you'd use in user space, Joe?
> >
> > I am using C from user space.
>
> Ah, great! Here comes the advert.. :)
>
> make -C tools/net/ynl/
>
> will generate the C lib for you. tools/net/ynl/generated/netdev-user.h
> will have the full API. There are some samples in
> tools/net/ynl/samples/. And basic info also here:
> https://docs.kernel.org/next/userspace-api/netlink/intro-specs.html#ynl-lib
>
> You should be able to convert Sridhar's cli.py into an equivalent
> in C in ~10 LoC.
>
> > Curious what you think about
> > SIOCGIFNAME_BY_NAPI_ID, Jakub? I think it would be very useful, but not
> > sure if such an extension would be accepted. I can send an RFC, if you'd
> > like to take a look and consider it. I know you are busy and I don't want
> > to add too much noise to the list if I can help it :)
>
> Nothing wrong with it in particular, but we went with the netlink API
> because all the objects are related. There are interrupts, NAPI
> instances, queues, page pools etc. and we need to show all sort of
> attributes, capabilities, stats as well as the linking. So getsockopts
> may not scale, or we'd need to create a monster mux getsockopt?
> Plus with some luck the netlink API will send you notifications of
> things changing.
Yes this all makes sense. The notification on changes would be excellent,
especially if NAPI IDs get changed for some reason (e.g. the queue count
is adjusted or the queues are rebuilt by the driver for some reason like a
timeout, etc).
I think the issue I'm solving with SIOCGIFNAME_BY_NAPI_ID is related, but
different.
In my case, SIOCGIFNAME_BY_NAPI_ID identifies which NIC a specific fd from
accept arrived from.
AFAICT, the netlink API wouldn't be able to help me answer that question. I
could use SIOCGIFNAME_BY_NAPI_ID to tell me which NIC the fd is from and
then use netlink to figure out which CPU to bind to (for example), but I
think SIOCGIFNAME_BY_NAPI_ID is still needed.
I'll send an RFC about for that shortly, hope that's OK.
> > Here's a brief description of what I'm doing, which others might find
> > helpful:
> >
> > 1. Machine has multiple NICs. Each NIC has 1 queue per busy poll app
> > thread, plus a few extra queues for other non busy poll usage.
> >
> > 2. A custom RSS context is created to distribute flows to the busy poll
> > queues. This context is created for each NIC. The default context directs
> > flows to the non-busy poll queues.
> >
> > 3. Each NIC has n-tuple filters inserted to direct incoming connections
> > with certain destination ports (e.g. 80, 443) to the custom RSS context.
> > All other incoming connections will land in the default context and go to
> > the other queues.
> >
> > 4. IRQs for the busy poll queues are pinned to specific CPUs which are NUMA
> > local to the NIC.
> >
> > 5. IRQ coalescing values are setup with busy poll in mind, so IRQs are
> > deferred as much as possible with the assumption userland will drive NAPI
> > via epoll_wait. This is done per queue (using ethtool --per-queue and a
> > queue mask). This is where napi_defer_hard_irqs and gro_flush_timeout
> > could help even more. IRQ deferral is only needed for the busy poll queues.
>
> Did you see SO_PREFER_BUSY_POLL by any chance? (In combination with
> gro_flush_timeout IIRC). We added it a while back with Bjorn, it seems
> like a great idea to me at the time but I'm unclear if anyone uses it
> in production..
I have seen it while reading the code, yes. I think maybe I missed
something about its interaction with gro_flush_timeout. In my use case,
the machine has no traffic until after the app is started.
In this case, I haven't needed to worry about regular NAPI monopolizing the
CPU and preventing busy poll from working.
Maybe I am missing something more nuanced, though? I'll have another look
at the code, just incase.
>
> > 6. userspace app config has NICs with their NUMA local CPUs listed, for
> > example like this:
> >
> > - eth0: 0,1,2,3
> > - eth1: 4,5,6,7
> >
> > The app reads that configuration in when it starts. Ideally, these are the
> > same CPUs the IRQs are pinned to in step 4, but hopefully the coalesce
> > settings let IRQs be deferred quite a bit so busy poll can take over.
>
> FWIW if the driver you're using annotates things right you'll also get
> the NAPI <> IRQ mapping via the netdev netlink. Hopefully that
> simplifies the pinning setup.
I looked last night after I learned about the netlink interface. It does
not look like the driver I am using does, but I can fix that fairly easily,
I think.
I'll try to send a patch for this next week.
> > 7. App threads are created and sockets are opened with REUSEPORT. Notably:
> > when the sockets are created, SO_BINDTODEVICE is used* (see below for
> > longer explanation about this).
> >
> > 8. cbpf reusport program inserted to distribute incoming connections to
> > threads based on skb->queue_mapping. skb->queue_mapping values are not
> > unique (e.g. each NIC will have queue_mapping==0), this is why BINDTODEVICE
> > is needed. Again, see below.
> >
> > 9. worker thread epoll contexts are set to busy poll by the ioctl I've
> > submit in my patches.
> >
> > The first time a worker thread receives a connection, it:
> >
> > 1. calls SO_INCOMING_NAPI_ID to get the NAPI ID associated with the
> > connection it received.
> >
> > 2. Takes that NAPI ID and calls SIOCGIFNAME_BY_NAPI_ID to figure out which
> > NIC the connection came in on.
> >
> > 3. Looks for an un-unsed CPU from the list it read in at configuration time
> > that is associated with that NIC and then pins itself to that CPU. That CPU
> > is removed from the list so other threads can't take it.
> >
> > All future incoming connections with the same NAPI ID will be distributed
> > to app threads which are pinned in the appropriate place and are doing busy
> > polling.
> >
> > So, as you can see, SIOCGIFNAME_BY_NAPI_ID makes this implementation very
> > simple.
> >
> > I plan to eventually add some information to the kernel networking
> > documentation to capture some more details of the above, which I think
> > might be helpful for others.
>
> Sounds very sensible & neat indeed. And makes sense to describe this
> in the docs, that should hopefully put more people on the right path :)
>
> > Another potential solution to avoid the above might be use an eBPF program
> > and to build a hash that maps NAPI IDs to thread IDs and write a more
> > complicated eBPF program to distribute connections that way. This seemed
> > cool, but involved a lot more work so I went with the SO_BINDTODEVICE +
> > SIOCGIFNAME_BY_NAPI_ID method instead which was pretty simple (C code wise)
> > and easy to implement.
>
> Interesting!
^ permalink raw reply
* Re: [net-next 0/3] Per epoll context busy poll support
From: Jakub Kicinski @ 2024-02-02 19:58 UTC (permalink / raw)
To: Joe Damato
Cc: Samudrala, Sridhar, Eric Dumazet, netdev, linux-kernel,
chuck.lever, jlayton, linux-api, brauner, davem, alexander.duyck,
Wei Wang, Amritha Nambiar
In-Reply-To: <20240202193332.GA8932@fastly.com>
On Fri, 2 Feb 2024 11:33:33 -0800 Joe Damato wrote:
> On Fri, Feb 02, 2024 at 10:22:39AM -0800, Jakub Kicinski wrote:
> > On Fri, 2 Feb 2024 11:23:28 -0600 Samudrala, Sridhar wrote:
> > > I think you should be able to get this functionality via the netdev-genl
> > > API to get napi parameters. It returns ifindex as one of the parameters
> > > and you should able to get the name from ifindex.
> > >
> > > $ ./cli.py --spec netdev.yaml --do napi-get --json='{"id": 593}'
> > > {'id': 593, 'ifindex': 12, 'irq': 291, 'pid': 3727}
> >
> > FWIW we also have a C library to access those. Out of curiosity what's
> > the programming language you'd use in user space, Joe?
>
> I am using C from user space.
Ah, great! Here comes the advert.. :)
make -C tools/net/ynl/
will generate the C lib for you. tools/net/ynl/generated/netdev-user.h
will have the full API. There are some samples in
tools/net/ynl/samples/. And basic info also here:
https://docs.kernel.org/next/userspace-api/netlink/intro-specs.html#ynl-lib
You should be able to convert Sridhar's cli.py into an equivalent
in C in ~10 LoC.
> Curious what you think about
> SIOCGIFNAME_BY_NAPI_ID, Jakub? I think it would be very useful, but not
> sure if such an extension would be accepted. I can send an RFC, if you'd
> like to take a look and consider it. I know you are busy and I don't want
> to add too much noise to the list if I can help it :)
Nothing wrong with it in particular, but we went with the netlink API
because all the objects are related. There are interrupts, NAPI
instances, queues, page pools etc. and we need to show all sort of
attributes, capabilities, stats as well as the linking. So getsockopts
may not scale, or we'd need to create a monster mux getsockopt?
Plus with some luck the netlink API will send you notifications of
things changing.
> Here's a brief description of what I'm doing, which others might find
> helpful:
>
> 1. Machine has multiple NICs. Each NIC has 1 queue per busy poll app
> thread, plus a few extra queues for other non busy poll usage.
>
> 2. A custom RSS context is created to distribute flows to the busy poll
> queues. This context is created for each NIC. The default context directs
> flows to the non-busy poll queues.
>
> 3. Each NIC has n-tuple filters inserted to direct incoming connections
> with certain destination ports (e.g. 80, 443) to the custom RSS context.
> All other incoming connections will land in the default context and go to
> the other queues.
>
> 4. IRQs for the busy poll queues are pinned to specific CPUs which are NUMA
> local to the NIC.
>
> 5. IRQ coalescing values are setup with busy poll in mind, so IRQs are
> deferred as much as possible with the assumption userland will drive NAPI
> via epoll_wait. This is done per queue (using ethtool --per-queue and a
> queue mask). This is where napi_defer_hard_irqs and gro_flush_timeout
> could help even more. IRQ deferral is only needed for the busy poll queues.
Did you see SO_PREFER_BUSY_POLL by any chance? (In combination with
gro_flush_timeout IIRC). We added it a while back with Bjorn, it seems
like a great idea to me at the time but I'm unclear if anyone uses it
in production..
> 6. userspace app config has NICs with their NUMA local CPUs listed, for
> example like this:
>
> - eth0: 0,1,2,3
> - eth1: 4,5,6,7
>
> The app reads that configuration in when it starts. Ideally, these are the
> same CPUs the IRQs are pinned to in step 4, but hopefully the coalesce
> settings let IRQs be deferred quite a bit so busy poll can take over.
FWIW if the driver you're using annotates things right you'll also get
the NAPI <> IRQ mapping via the netdev netlink. Hopefully that
simplifies the pinning setup.
> 7. App threads are created and sockets are opened with REUSEPORT. Notably:
> when the sockets are created, SO_BINDTODEVICE is used* (see below for
> longer explanation about this).
>
> 8. cbpf reusport program inserted to distribute incoming connections to
> threads based on skb->queue_mapping. skb->queue_mapping values are not
> unique (e.g. each NIC will have queue_mapping==0), this is why BINDTODEVICE
> is needed. Again, see below.
>
> 9. worker thread epoll contexts are set to busy poll by the ioctl I've
> submit in my patches.
>
> The first time a worker thread receives a connection, it:
>
> 1. calls SO_INCOMING_NAPI_ID to get the NAPI ID associated with the
> connection it received.
>
> 2. Takes that NAPI ID and calls SIOCGIFNAME_BY_NAPI_ID to figure out which
> NIC the connection came in on.
>
> 3. Looks for an un-unsed CPU from the list it read in at configuration time
> that is associated with that NIC and then pins itself to that CPU. That CPU
> is removed from the list so other threads can't take it.
>
> All future incoming connections with the same NAPI ID will be distributed
> to app threads which are pinned in the appropriate place and are doing busy
> polling.
>
> So, as you can see, SIOCGIFNAME_BY_NAPI_ID makes this implementation very
> simple.
>
> I plan to eventually add some information to the kernel networking
> documentation to capture some more details of the above, which I think
> might be helpful for others.
Sounds very sensible & neat indeed. And makes sense to describe this
in the docs, that should hopefully put more people on the right path :)
> Another potential solution to avoid the above might be use an eBPF program
> and to build a hash that maps NAPI IDs to thread IDs and write a more
> complicated eBPF program to distribute connections that way. This seemed
> cool, but involved a lot more work so I went with the SO_BINDTODEVICE +
> SIOCGIFNAME_BY_NAPI_ID method instead which was pretty simple (C code wise)
> and easy to implement.
Interesting!
^ permalink raw reply
* Re: [PATCH 1/3] pidfd_poll: report POLLHUP when pid_task() == NULL
From: Christian Brauner @ 2024-02-02 19:50 UTC (permalink / raw)
To: Oleg Nesterov
Cc: Andy Lutomirski, Eric W. Biederman, Tycho Andersen, linux-api,
linux-kernel
In-Reply-To: <20240202190529.GA28818@redhat.com>
On Fri, Feb 02, 2024 at 08:05:29PM +0100, Oleg Nesterov wrote:
> On 02/02, Christian Brauner wrote:
> >
> > > I think we need a simpler patch. I was going to send it as 4/4, but I'd
> > > like to think more, _perhaps_ we can also discriminate the PIDFD_THREAD
> > > and non-PIDFD_THREAD waiters. I'll try to make the patch(es) tomorrow or
> >
> > Right, I didn't go that far.
> >
> > > at least provided more info.
> > >
> > > 3 notes for now:
> > >
> > > 1. we can't use wake_up_poll(), it passes nr_exclusive => 1
> >
> > Bah. So we need the same stuff we did for io_uring and use
> > __wake_up() directly. Or we add wake_up_all_poll() and convert the other
> > three callsites:
>
> ...
>
> > +#define wake_up_all_poll(x, m) \
> > + __wake_up(x, TASK_NORMAL, 0, poll_to_key(m))
>
> Agreed, but I think this + s/wake_up/wake_up_all_poll/ conversions
> need a separate patch.
Yeah, I know. This is just a scribbled draft.
>
>
> > -void do_notify_pidfd(struct task_struct *task)
> > +void pidfd_wake_up_poll(struct task_struct *task, bool dead)
> > {
> > - struct pid *pid;
> > -
> > WARN_ON(task->exit_state == 0);
> > - pid = task_pid(task);
> > - wake_up_all(&pid->wait_pidfd);
> > + WARN_ON(mask == 0);
> > + wake_up_all_poll(&task_pid(task)->wait_pidfd,
> > + EPOLLIN | EPOLLRDNORM | dead ? EPOLLHUP : 0);
>
> No...
>
> This is still overcomplicated and is not right.
I'm all ears.
> Christian, I'll write another email tomorrow.
Sure, there's no rush. I had not intended that this patch be used. I
have another large series I need to take care of so I can't spend a lot
of time on writing this anyway. I just hadn't used the keyed apis before
and got curious. So don't get the impression that I intend to write
this. I fully expected you to do it.
^ permalink raw reply
* Re: [net-next 0/3] Per epoll context busy poll support
From: Joe Damato @ 2024-02-02 19:33 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Samudrala, Sridhar, Eric Dumazet, netdev, linux-kernel,
chuck.lever, jlayton, linux-api, brauner, davem, alexander.duyck,
Wei Wang, Amritha Nambiar
In-Reply-To: <20240202102239.274ca9bb@kernel.org>
On Fri, Feb 02, 2024 at 10:22:39AM -0800, Jakub Kicinski wrote:
> On Fri, 2 Feb 2024 11:23:28 -0600 Samudrala, Sridhar wrote:
> > > I know I am replying to a stale thread on the patches I've submit (there is
> > > a v5 now [1]), but I just looked at your message - sorry I didn't reply
> > > sooner.
> > >
> > > The per-queue and per-napi netlink APIs look extremely useful, thanks for
> > > pointing this out.
> > >
> > > In my development tree, I had added SIOCGIFNAME_BY_NAPI_ID which works
> > > similar to SIOCGIFNAME: it takes a NAPI ID and returns the IF name. This is
> > > useful on machines with multiple NICs where each NIC could be located in
> > > one of many different NUMA zones.
> > >
> > > The idea was that apps would use SO_INCOMING_NAPI_ID, distribute the NAPI
> > > ID to a worker thread which could then use SIOCGIFNAME_BY_NAPI_ID to
> > > compute which NIC the connection came in on. The app would then (via
> > > configuration) know where to pin that worker thread; ideally somewhere NUMA
> > > local to the NIC.
> > >
> > > I had assumed that such a change would be rejected, but I figured I'd send
> > > an RFC for it after the per epoll context stuff was done and see if anyone
> > > thought SIOCGIFNAME_BY_NAPI_ID would be useful for them, as well.
> >
> > I think you should be able to get this functionality via the netdev-genl
> > API to get napi parameters. It returns ifindex as one of the parameters
> > and you should able to get the name from ifindex.
> >
> > $ ./cli.py --spec netdev.yaml --do napi-get --json='{"id": 593}'
> > {'id': 593, 'ifindex': 12, 'irq': 291, 'pid': 3727}
>
> FWIW we also have a C library to access those. Out of curiosity what's
> the programming language you'd use in user space, Joe?
I am using C from user space. Curious what you think about
SIOCGIFNAME_BY_NAPI_ID, Jakub? I think it would be very useful, but not
sure if such an extension would be accepted. I can send an RFC, if you'd
like to take a look and consider it. I know you are busy and I don't want
to add too much noise to the list if I can help it :)
Here's a brief description of what I'm doing, which others might find
helpful:
1. Machine has multiple NICs. Each NIC has 1 queue per busy poll app
thread, plus a few extra queues for other non busy poll usage.
2. A custom RSS context is created to distribute flows to the busy poll
queues. This context is created for each NIC. The default context directs
flows to the non-busy poll queues.
3. Each NIC has n-tuple filters inserted to direct incoming connections
with certain destination ports (e.g. 80, 443) to the custom RSS context.
All other incoming connections will land in the default context and go to
the other queues.
4. IRQs for the busy poll queues are pinned to specific CPUs which are NUMA
local to the NIC.
5. IRQ coalescing values are setup with busy poll in mind, so IRQs are
deferred as much as possible with the assumption userland will drive NAPI
via epoll_wait. This is done per queue (using ethtool --per-queue and a
queue mask). This is where napi_defer_hard_irqs and gro_flush_timeout
could help even more. IRQ deferral is only needed for the busy poll queues.
6. userspace app config has NICs with their NUMA local CPUs listed, for
example like this:
- eth0: 0,1,2,3
- eth1: 4,5,6,7
The app reads that configuration in when it starts. Ideally, these are the
same CPUs the IRQs are pinned to in step 4, but hopefully the coalesce
settings let IRQs be deferred quite a bit so busy poll can take over.
7. App threads are created and sockets are opened with REUSEPORT. Notably:
when the sockets are created, SO_BINDTODEVICE is used* (see below for
longer explanation about this).
8. cbpf reusport program inserted to distribute incoming connections to
threads based on skb->queue_mapping. skb->queue_mapping values are not
unique (e.g. each NIC will have queue_mapping==0), this is why BINDTODEVICE
is needed. Again, see below.
9. worker thread epoll contexts are set to busy poll by the ioctl I've
submit in my patches.
The first time a worker thread receives a connection, it:
1. calls SO_INCOMING_NAPI_ID to get the NAPI ID associated with the
connection it received.
2. Takes that NAPI ID and calls SIOCGIFNAME_BY_NAPI_ID to figure out which
NIC the connection came in on.
3. Looks for an un-unsed CPU from the list it read in at configuration time
that is associated with that NIC and then pins itself to that CPU. That CPU
is removed from the list so other threads can't take it.
All future incoming connections with the same NAPI ID will be distributed
to app threads which are pinned in the appropriate place and are doing busy
polling.
So, as you can see, SIOCGIFNAME_BY_NAPI_ID makes this implementation very
simple.
I plan to eventually add some information to the kernel networking
documentation to capture some more details of the above, which I think
might be helpful for others.
Thanks,
Joe
* Longer explanation about SO_BINDTODEVICE (only relevant if you have
mulitple NICs):
It turns out that reuseport groups in the kernel are bounded by a few
attributes, port being one of them but also ifindex. Since multiple NICs
can have queue_mapping == 0, reusport groups need to be constructed in
userland with care if there are multiple NICs. This is required because
each epoll context can only do epoll busy poll on a single NAPI ID. So,
even if multiple NICs have queue_mapping == 0, the queues will have
different NAPI IDs and incoming connections must be distributed to threads
uniquely based on NAPI ID.
I am doing this by creating listen sockets for each NIC, one NIC at a time.
When the listen socket is created, SO_BINDTODEVICE is used on the socket
before calling listen.
In the kernel, this results in all listen sockets with the same ifindex to
form a reuseport group. So, if I have 2 NICs and 1 listen port (say port
80), this results in 2 reuseport groups -- one for nic0 port 80 and one for
nic1 port 80, because of SO_BINDTODEVICE.
The reuseport cbpf filter is inserted for each reuseport group, and then
the skb->queue_mapping based listen socket selection will work as expected
distributing NAPI IDs to app threads without breaking epoll busy poll.
Without the above, you can run into an issue where two connections with the
same queue_mapping (but from different NICs) can land in the same epoll
context, which breaks busy poll.
Another potential solution to avoid the above might be use an eBPF program
and to build a hash that maps NAPI IDs to thread IDs and write a more
complicated eBPF program to distribute connections that way. This seemed
cool, but involved a lot more work so I went with the SO_BINDTODEVICE +
SIOCGIFNAME_BY_NAPI_ID method instead which was pretty simple (C code wise)
and easy to implement.
^ permalink raw reply
* Re: [PATCH 1/3] pidfd_poll: report POLLHUP when pid_task() == NULL
From: Oleg Nesterov @ 2024-02-02 19:05 UTC (permalink / raw)
To: Christian Brauner
Cc: Andy Lutomirski, Eric W. Biederman, Tycho Andersen, linux-api,
linux-kernel
In-Reply-To: <20240202-lackmantel-vervielfachen-4c0f0374219b@brauner>
On 02/02, Christian Brauner wrote:
>
> > I think we need a simpler patch. I was going to send it as 4/4, but I'd
> > like to think more, _perhaps_ we can also discriminate the PIDFD_THREAD
> > and non-PIDFD_THREAD waiters. I'll try to make the patch(es) tomorrow or
>
> Right, I didn't go that far.
>
> > at least provided more info.
> >
> > 3 notes for now:
> >
> > 1. we can't use wake_up_poll(), it passes nr_exclusive => 1
>
> Bah. So we need the same stuff we did for io_uring and use
> __wake_up() directly. Or we add wake_up_all_poll() and convert the other
> three callsites:
...
> +#define wake_up_all_poll(x, m) \
> + __wake_up(x, TASK_NORMAL, 0, poll_to_key(m))
Agreed, but I think this + s/wake_up/wake_up_all_poll/ conversions
need a separate patch.
> -void do_notify_pidfd(struct task_struct *task)
> +void pidfd_wake_up_poll(struct task_struct *task, bool dead)
> {
> - struct pid *pid;
> -
> WARN_ON(task->exit_state == 0);
> - pid = task_pid(task);
> - wake_up_all(&pid->wait_pidfd);
> + WARN_ON(mask == 0);
> + wake_up_all_poll(&task_pid(task)->wait_pidfd,
> + EPOLLIN | EPOLLRDNORM | dead ? EPOLLHUP : 0);
No...
This is still overcomplicated and is not right.
Christian, I'll write another email tomorrow.
Oleg.
^ permalink raw reply
* Re: [net-next 0/3] Per epoll context busy poll support
From: Jakub Kicinski @ 2024-02-02 18:22 UTC (permalink / raw)
To: Joe Damato
Cc: Samudrala, Sridhar, Eric Dumazet, netdev, linux-kernel,
chuck.lever, jlayton, linux-api, brauner, davem, alexander.duyck,
Wei Wang, Amritha Nambiar
In-Reply-To: <f0b4d813-d7cb-428b-9c41-a2d86684f3f1@intel.com>
On Fri, 2 Feb 2024 11:23:28 -0600 Samudrala, Sridhar wrote:
> > I know I am replying to a stale thread on the patches I've submit (there is
> > a v5 now [1]), but I just looked at your message - sorry I didn't reply
> > sooner.
> >
> > The per-queue and per-napi netlink APIs look extremely useful, thanks for
> > pointing this out.
> >
> > In my development tree, I had added SIOCGIFNAME_BY_NAPI_ID which works
> > similar to SIOCGIFNAME: it takes a NAPI ID and returns the IF name. This is
> > useful on machines with multiple NICs where each NIC could be located in
> > one of many different NUMA zones.
> >
> > The idea was that apps would use SO_INCOMING_NAPI_ID, distribute the NAPI
> > ID to a worker thread which could then use SIOCGIFNAME_BY_NAPI_ID to
> > compute which NIC the connection came in on. The app would then (via
> > configuration) know where to pin that worker thread; ideally somewhere NUMA
> > local to the NIC.
> >
> > I had assumed that such a change would be rejected, but I figured I'd send
> > an RFC for it after the per epoll context stuff was done and see if anyone
> > thought SIOCGIFNAME_BY_NAPI_ID would be useful for them, as well.
>
> I think you should be able to get this functionality via the netdev-genl
> API to get napi parameters. It returns ifindex as one of the parameters
> and you should able to get the name from ifindex.
>
> $ ./cli.py --spec netdev.yaml --do napi-get --json='{"id": 593}'
> {'id': 593, 'ifindex': 12, 'irq': 291, 'pid': 3727}
FWIW we also have a C library to access those. Out of curiosity what's
the programming language you'd use in user space, Joe?
^ permalink raw reply
* Re: [PATCH 1/3] pidfd_poll: report POLLHUP when pid_task() == NULL
From: Christian Brauner @ 2024-02-02 17:24 UTC (permalink / raw)
To: Oleg Nesterov
Cc: Andy Lutomirski, Eric W. Biederman, Tycho Andersen, linux-api,
linux-kernel
In-Reply-To: <20240202160704.GA5850@redhat.com>
[-- Attachment #1: Type: text/plain, Size: 3897 bytes --]
> I think we need a simpler patch. I was going to send it as 4/4, but I'd
> like to think more, _perhaps_ we can also discriminate the PIDFD_THREAD
> and non-PIDFD_THREAD waiters. I'll try to make the patch(es) tomorrow or
Right, I didn't go that far.
> at least provided more info.
>
> 3 notes for now:
>
> 1. we can't use wake_up_poll(), it passes nr_exclusive => 1
Bah. So we need the same stuff we did for io_uring and use
__wake_up() directly. Or we add wake_up_all_poll() and convert the other
three callsites:
// uncompiled, untested
diff --git a/include/linux/wait.h b/include/linux/wait.h
index 8aa3372f21a0..210ee0d69b6f 100644
--- a/include/linux/wait.h
+++ b/include/linux/wait.h
@@ -234,6 +234,8 @@ void __wake_up_pollfree(struct wait_queue_head *wq_head);
#define key_to_poll(m) ((__force __poll_t)(uintptr_t)(void *)(m))
#define wake_up_poll(x, m) \
__wake_up(x, TASK_NORMAL, 1, poll_to_key(m))
+#define wake_up_all_poll(x, m) \
+ __wake_up(x, TASK_NORMAL, 0, poll_to_key(m))
#define wake_up_poll_on_current_cpu(x, m) \
__wake_up_on_current_cpu(x, TASK_NORMAL, poll_to_key(m))
#define wake_up_locked_poll(x, m) \
diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h
index 04e33f25919c..65dcd5dc9645 100644
--- a/io_uring/io_uring.h
+++ b/io_uring/io_uring.h
@@ -228,8 +228,7 @@ static inline void io_commit_cqring(struct io_ring_ctx *ctx)
static inline void io_poll_wq_wake(struct io_ring_ctx *ctx)
{
if (wq_has_sleeper(&ctx->poll_wq))
- __wake_up(&ctx->poll_wq, TASK_NORMAL, 0,
- poll_to_key(EPOLL_URING_WAKE | EPOLLIN));
+ wake_up_all_poll(&ctx->poll_wq, EPOLL_URING_WAKE | EPOLLIN);
}
static inline void io_cqring_wake(struct io_ring_ctx *ctx)
@@ -245,8 +244,7 @@ static inline void io_cqring_wake(struct io_ring_ctx *ctx)
* epoll and should terminate multishot poll at that point.
*/
if (wq_has_sleeper(&ctx->cq_wait))
- __wake_up(&ctx->cq_wait, TASK_NORMAL, 0,
- poll_to_key(EPOLL_URING_WAKE | EPOLLIN));
+ wake_up_all_poll(&ctx->cq_wait, EPOLL_URING_WAKE | EPOLLIN);
}
static inline bool io_sqring_full(struct io_ring_ctx *ctx)
diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c
index 51e38f5f4701..ee849fb35603 100644
--- a/kernel/sched/wait.c
+++ b/kernel/sched/wait.c
@@ -208,7 +208,7 @@ EXPORT_SYMBOL_GPL(__wake_up_sync); /* For internal
use only */
void __wake_up_pollfree(struct wait_queue_head *wq_head)
{
- __wake_up(wq_head, TASK_NORMAL, 0, poll_to_key(EPOLLHUP |
POLLFREE));
+ wake_up_all_poll(wq_head, EPOLLHUP | POLLFREE);
/* POLLFREE must have cleared the queue. */
WARN_ON_ONCE(waitqueue_active(wq_head));
}
>
> 2. exit_notify() should not pass EPOLLHUP to wake_up, we do
> not want to wake up the { .events = POLLHUP } waiters.
Indeed.
>
> 3. we do not need to change __change_pid().
>
> Well, _perhaps_ it can/should use __wake_up_pollfree(), but
> I need to check if fs/select.c use "autoremove" or not.
>
>
> > -static __poll_t pidfd_poll(struct file *file, struct poll_table_struct *pts)
> > +static __poll_t pidfd_poll(struct file *file, poll_table *wait)
> > {
> > struct pid *pid = file->private_data;
> > bool thread = file->f_flags & PIDFD_THREAD;
> > struct task_struct *task;
> > __poll_t poll_flags = 0;
> >
> > - poll_wait(file, &pid->wait_pidfd, pts);
> > + poll_wait(file, &pid->wait_pidfd, wait);
>
> This is correct but only cosemtic and has nothing to do with what
> we discuss?
No, I just folded all of the changes because it was just a draft. See
the updated draft I appended.
[-- Attachment #2: 0001-UNTESTED-UNCOMPILED.patch --]
[-- Type: text/x-diff, Size: 5210 bytes --]
From 1a026da491f1262dc525933c73b90b6297abf5da Mon Sep 17 00:00:00 2001
From: Christian Brauner <brauner@kernel.org>
Date: Fri, 2 Feb 2024 18:21:19 +0100
Subject: [PATCH] [UNTESTED][UNCOMPILED]
Signed-off-by: Christian Brauner <brauner@kernel.org>
---
include/linux/pid.h | 2 +-
include/linux/wait.h | 2 ++
io_uring/io_uring.h | 6 ++----
kernel/exit.c | 2 +-
kernel/fork.c | 4 ++--
kernel/sched/wait.c | 2 +-
kernel/signal.c | 11 +++++------
7 files changed, 14 insertions(+), 15 deletions(-)
diff --git a/include/linux/pid.h b/include/linux/pid.h
index 8124d57752b9..b261cd53517d 100644
--- a/include/linux/pid.h
+++ b/include/linux/pid.h
@@ -74,7 +74,7 @@ struct pid *pidfd_pid(const struct file *file);
struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags);
struct task_struct *pidfd_get_task(int pidfd, unsigned int *flags);
int pidfd_prepare(struct pid *pid, unsigned int flags, struct file **ret);
-void do_notify_pidfd(struct task_struct *task);
+void pidfd_wake_up_poll(struct task_struct *task, bool dead);
static inline struct pid *get_pid(struct pid *pid)
{
diff --git a/include/linux/wait.h b/include/linux/wait.h
index 8aa3372f21a0..210ee0d69b6f 100644
--- a/include/linux/wait.h
+++ b/include/linux/wait.h
@@ -234,6 +234,8 @@ void __wake_up_pollfree(struct wait_queue_head *wq_head);
#define key_to_poll(m) ((__force __poll_t)(uintptr_t)(void *)(m))
#define wake_up_poll(x, m) \
__wake_up(x, TASK_NORMAL, 1, poll_to_key(m))
+#define wake_up_all_poll(x, m) \
+ __wake_up(x, TASK_NORMAL, 0, poll_to_key(m))
#define wake_up_poll_on_current_cpu(x, m) \
__wake_up_on_current_cpu(x, TASK_NORMAL, poll_to_key(m))
#define wake_up_locked_poll(x, m) \
diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h
index 04e33f25919c..65dcd5dc9645 100644
--- a/io_uring/io_uring.h
+++ b/io_uring/io_uring.h
@@ -228,8 +228,7 @@ static inline void io_commit_cqring(struct io_ring_ctx *ctx)
static inline void io_poll_wq_wake(struct io_ring_ctx *ctx)
{
if (wq_has_sleeper(&ctx->poll_wq))
- __wake_up(&ctx->poll_wq, TASK_NORMAL, 0,
- poll_to_key(EPOLL_URING_WAKE | EPOLLIN));
+ wake_up_all_poll(&ctx->poll_wq, EPOLL_URING_WAKE | EPOLLIN);
}
static inline void io_cqring_wake(struct io_ring_ctx *ctx)
@@ -245,8 +244,7 @@ static inline void io_cqring_wake(struct io_ring_ctx *ctx)
* epoll and should terminate multishot poll at that point.
*/
if (wq_has_sleeper(&ctx->cq_wait))
- __wake_up(&ctx->cq_wait, TASK_NORMAL, 0,
- poll_to_key(EPOLL_URING_WAKE | EPOLLIN));
+ wake_up_all_poll(&ctx->cq_wait, EPOLL_URING_WAKE | EPOLLIN);
}
static inline bool io_sqring_full(struct io_ring_ctx *ctx)
diff --git a/kernel/exit.c b/kernel/exit.c
index c038d10dfb38..70c967e08efa 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -744,7 +744,7 @@ static void exit_notify(struct task_struct *tsk, int group_dead)
* PIDFD_THREAD waiters.
*/
if (!thread_group_empty(tsk))
- do_notify_pidfd(tsk);
+ pidfd_wake_up_poll(tsk, false);
if (unlikely(tsk->ptrace)) {
int sig = thread_group_leader(tsk) &&
diff --git a/kernel/fork.c b/kernel/fork.c
index aa08193d124f..7b882e66448b 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -2074,14 +2074,14 @@ static void pidfd_show_fdinfo(struct seq_file *m, struct file *f)
/*
* Poll support for process exit notification.
*/
-static __poll_t pidfd_poll(struct file *file, struct poll_table_struct *pts)
+static __poll_t pidfd_poll(struct file *file, poll_table *wait)
{
struct pid *pid = file->private_data;
bool thread = file->f_flags & PIDFD_THREAD;
struct task_struct *task;
__poll_t poll_flags = 0;
- poll_wait(file, &pid->wait_pidfd, pts);
+ poll_wait(file, &pid->wait_pidfd, wait);
/*
* Depending on PIDFD_THREAD, inform pollers when the thread
* or the whole thread-group exits.
diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c
index 51e38f5f4701..ee849fb35603 100644
--- a/kernel/sched/wait.c
+++ b/kernel/sched/wait.c
@@ -208,7 +208,7 @@ EXPORT_SYMBOL_GPL(__wake_up_sync); /* For internal use only */
void __wake_up_pollfree(struct wait_queue_head *wq_head)
{
- __wake_up(wq_head, TASK_NORMAL, 0, poll_to_key(EPOLLHUP | POLLFREE));
+ wake_up_all_poll(wq_head, EPOLLHUP | POLLFREE);
/* POLLFREE must have cleared the queue. */
WARN_ON_ONCE(waitqueue_active(wq_head));
}
diff --git a/kernel/signal.c b/kernel/signal.c
index 9b40109f0c56..86b3721ea08f 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -2019,13 +2019,12 @@ int send_sigqueue(struct sigqueue *q, struct pid *pid, enum pid_type type)
return ret;
}
-void do_notify_pidfd(struct task_struct *task)
+void pidfd_wake_up_poll(struct task_struct *task, bool dead)
{
- struct pid *pid;
-
WARN_ON(task->exit_state == 0);
- pid = task_pid(task);
- wake_up_all(&pid->wait_pidfd);
+ WARN_ON(mask == 0);
+ wake_up_all_poll(&task_pid(task)->wait_pidfd,
+ EPOLLIN | EPOLLRDNORM | dead ? EPOLLHUP : 0);
}
/*
@@ -2055,7 +2054,7 @@ bool do_notify_parent(struct task_struct *tsk, int sig)
* non-PIDFD_THREAD waiters.
*/
if (thread_group_empty(tsk))
- do_notify_pidfd(tsk);
+ pidfd_wake_up_poll(tsk, false);
if (sig != SIGCHLD) {
/*
--
2.43.0
^ permalink raw reply related
* Re: [net-next 0/3] Per epoll context busy poll support
From: Samudrala, Sridhar @ 2024-02-02 17:23 UTC (permalink / raw)
To: Joe Damato
Cc: Eric Dumazet, netdev, linux-kernel, chuck.lever, jlayton,
linux-api, brauner, davem, alexander.duyck, kuba, Wei Wang,
Amritha Nambiar
In-Reply-To: <20240202032806.GA8708@fastly.com>
On 2/1/2024 9:28 PM, Joe Damato wrote:
> On Tue, Jan 30, 2024 at 12:54:50PM -0600, Samudrala, Sridhar wrote:
>>
>>
>> On 1/24/2024 2:20 AM, Eric Dumazet wrote:
>>> On Wed, Jan 24, 2024 at 3:54 AM Joe Damato <jdamato@fastly.com> wrote:
>>>>
>>>> Greetings:
>>>>
>>>> TL;DR This builds on commit bf3b9f6372c4 ("epoll: Add busy poll support to
>>>> epoll with socket fds.") by allowing user applications to enable
>>>> epoll-based busy polling and set a busy poll packet budget on a per epoll
>>>> context basis.
>>>>
>>>> To allow for this, two ioctls have been added for epoll contexts for
>>>> getting and setting a new struct, struct epoll_params.
>>>>
>>>> This makes epoll-based busy polling much more usable for user
>>>> applications than the current system-wide sysctl and hardcoded budget.
>>
>> Agree. looking forward to see this patch series accepted soon.
>>
>>>>
>>>> Longer explanation:
>>>>
>>>> Presently epoll has support for a very useful form of busy poll based on
>>>> the incoming NAPI ID (see also: SO_INCOMING_NAPI_ID [1]).
>>>>
>>>> This form of busy poll allows epoll_wait to drive NAPI packet processing
>>>> which allows for a few interesting user application designs which can
>>>> reduce latency and also potentially improve L2/L3 cache hit rates by
>>>> deferring NAPI until userland has finished its work.
>>>>
>>>> The documentation available on this is, IMHO, a bit confusing so please
>>>> allow me to explain how one might use this:
>>>>
>>>> 1. Ensure each application thread has its own epoll instance mapping
>>>> 1-to-1 with NIC RX queues. An n-tuple filter would likely be used to
>>>> direct connections with specific dest ports to these queues.
>>>>
>>>> 2. Optionally: Setup IRQ coalescing for the NIC RX queues where busy
>>>> polling will occur. This can help avoid the userland app from being
>>>> pre-empted by a hard IRQ while userland is running. Note this means that
>>>> userland must take care to call epoll_wait and not take too long in
>>>> userland since it now drives NAPI via epoll_wait.
>>>>
>>>> 3. Ensure that all incoming connections added to an epoll instance
>>>> have the same NAPI ID. This can be done with a BPF filter when
>>>> SO_REUSEPORT is used or getsockopt + SO_INCOMING_NAPI_ID when a single
>>>> accept thread is used which dispatches incoming connections to threads.
>>>>
>>>> 4. Lastly, busy poll must be enabled via a sysctl
>>>> (/proc/sys/net/core/busy_poll).
>>>>
>>>> The unfortunate part about step 4 above is that this enables busy poll
>>>> system-wide which affects all user applications on the system,
>>>> including epoll-based network applications which were not intended to
>>>> be used this way or applications where increased CPU usage for lower
>>>> latency network processing is unnecessary or not desirable.
>>>>
>>>> If the user wants to run one low latency epoll-based server application
>>>> with epoll-based busy poll, but would like to run the rest of the
>>>> applications on the system (which may also use epoll) without busy poll,
>>>> this system-wide sysctl presents a significant problem.
>>>>
>>>> This change preserves the system-wide sysctl, but adds a mechanism (via
>>>> ioctl) to enable or disable busy poll for epoll contexts as needed by
>>>> individual applications, making epoll-based busy poll more usable.
>>>>
>>>
>>> I think this description missed the napi_defer_hard_irqs and
>>> gro_flush_timeout settings ?
>>>
>>> I would think that if an application really wants to make sure its
>>> thread is the only one
>>> eventually calling napi->poll(), we must make sure NIC interrupts stay masked.
>>>
>>> Current implementations of busy poll always release NAPI_STATE_SCHED bit when
>>> returning to user space.
>>>
>>> It seems you want to make sure the application and only the
>>> application calls the napi->poll()
>>> at chosen times.
>>>
>>> Some kind of contract is needed, and the presence of the hrtimer
>>> (currently only driven from dev->@gro_flush_timeout)
>>> would allow to do that correctly.
>>>
>>> Whenever we 'trust' user space to perform the napi->poll shortly, we
>>> also want to arm the hrtimer to eventually detect
>>> the application took too long, to restart the other mechanisms (NIC irq based)
>>>
>>> Note that we added the kthread based napi polling, and we are working
>>> to add a busy polling feature to these kthreads.
>>> allowing to completely mask NIC interrupts and further reduce latencies.
>>
>>
>> Good to know that you are looking into enabling busy polling for napi
>> kthreads.
>> We have something similar in our ice OOT driver that is implemented and we
>> call it 'independent pollers' as in this mode, busy polling will not be app
>> dependent or triggered by an application.
>> Here is a link to the slides we presented at netdev 0x16 driver workshop.
>> https://netdevconf.info/0x16/slides/48/netdev0x16_driver_workshop_ADQ.pdf
>>
>> We haven't yet submitted the patches upstream as there is no kernel
>> interface to configure napi specific timeouts.
>> With the recent per-queue and per-napi netlink APIs that are accepted
>> upstream
>> https://lore.kernel.org/netdev/170147307026.5260.9300080745237900261.stgit@anambiarhost.jf.intel.com/
>>
>> we are thinking of making timeout as a per-napi parameter and can be used as
>> an interface to enable napi kthread based busy polling.
>
> I know I am replying to a stale thread on the patches I've submit (there is
> a v5 now [1]), but I just looked at your message - sorry I didn't reply
> sooner.
>
> The per-queue and per-napi netlink APIs look extremely useful, thanks for
> pointing this out.
>
> In my development tree, I had added SIOCGIFNAME_BY_NAPI_ID which works
> similar to SIOCGIFNAME: it takes a NAPI ID and returns the IF name. This is
> useful on machines with multiple NICs where each NIC could be located in
> one of many different NUMA zones.
>
> The idea was that apps would use SO_INCOMING_NAPI_ID, distribute the NAPI
> ID to a worker thread which could then use SIOCGIFNAME_BY_NAPI_ID to
> compute which NIC the connection came in on. The app would then (via
> configuration) know where to pin that worker thread; ideally somewhere NUMA
> local to the NIC.
>
> I had assumed that such a change would be rejected, but I figured I'd send
> an RFC for it after the per epoll context stuff was done and see if anyone
> thought SIOCGIFNAME_BY_NAPI_ID would be useful for them, as well.
I think you should be able to get this functionality via the netdev-genl
API to get napi parameters. It returns ifindex as one of the parameters
and you should able to get the name from ifindex.
$ ./cli.py --spec netdev.yaml --do napi-get --json='{"id": 593}'
{'id': 593, 'ifindex': 12, 'irq': 291, 'pid': 3727}
>
>> I think even the per-device napi_defer_hard_irqs and gro_flush_timeout
>> should become per-napi parameters.
>
> I agree.
>
> I had been contemplating implementing this until I tried a different method
> similar to an academic paper I was reading [2][3]. I think per-device
> defer_hard_irqs and gro_flush_timeout would be extremely useful and a
> better approach than the one I'm currently using.
>
> Is this something you are currently working? I may try implementing this,
> but didn't want to duplicate effort if you are already working on this.
Not yet. Please go ahead and work on it it if you have time.
napi-get and napi-set can be extended to show and set these parameters.
>
> Thanks,
> Joe
>
> [1]: https://lore.kernel.org/all/20240131180811.23566-1-jdamato@fastly.com/
> [2]: https://dl.acm.org/doi/pdf/10.1145/3626780
> [3]: https://gitlab.uwaterloo.ca/p5cai/netstack-exp/-/blob/master/kernel-polling-5.15.79-base.patch?ref_type=heads
^ permalink raw reply
* [PATCH v5 4/4] mm/mempolicy: protect task interleave functions with tsk->mems_allowed_seq
From: Gregory Price @ 2024-02-02 17:02 UTC (permalink / raw)
To: linux-mm
Cc: linux-kernel, linux-doc, linux-fsdevel, linux-api, corbet, akpm,
gregory.price, honggyu.kim, rakie.kim, hyeongtak.ji, mhocko,
ying.huang, vtavarespetr, jgroves, ravis.opensrc, sthanneeru,
emirakhur, Hasan.Maruf, seungjun.ha, hannes, dan.j.williams
In-Reply-To: <20240202170238.90004-1-gregory.price@memverge.com>
In the event of rebind, pol->nodemask can change at the same time as an
allocation occurs. We can detect this with tsk->mems_allowed_seq and
prevent a miscount or an allocation failure from occurring.
The same thing happens in the allocators to detect failure, but this
can prevent spurious failures in a much smaller critical section.
Suggested-by: "Huang, Ying" <ying.huang@intel.com>
Signed-off-by: Gregory Price <gregory.price@memverge.com>
---
mm/mempolicy.c | 31 +++++++++++++++++++++++++------
1 file changed, 25 insertions(+), 6 deletions(-)
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index d8cc3a577986..ed0d5d2d456a 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -1878,11 +1878,17 @@ bool apply_policy_zone(struct mempolicy *policy, enum zone_type zone)
static unsigned int weighted_interleave_nodes(struct mempolicy *policy)
{
- unsigned int node = current->il_prev;
-
- if (!current->il_weight || !node_isset(node, policy->nodes)) {
+ unsigned int node;
+ unsigned int cpuset_mems_cookie;
+
+retry:
+ /* to prevent miscount use tsk->mems_allowed_seq to detect rebind */
+ cpuset_mems_cookie = read_mems_allowed_begin();
+ node = current->il_prev;
+ if (!node || !node_isset(node, policy->nodes)) {
node = next_node_in(node, policy->nodes);
- /* can only happen if nodemask is being rebound */
+ if (read_mems_allowed_retry(cpuset_mems_cookie))
+ goto retry;
if (node == MAX_NUMNODES)
return node;
current->il_prev = node;
@@ -1896,8 +1902,14 @@ static unsigned int weighted_interleave_nodes(struct mempolicy *policy)
static unsigned int interleave_nodes(struct mempolicy *policy)
{
unsigned int nid;
+ unsigned int cpuset_mems_cookie;
+
+ /* to prevent miscount, use tsk->mems_allowed_seq to detect rebind */
+ do {
+ cpuset_mems_cookie = read_mems_allowed_begin();
+ nid = next_node_in(current->il_prev, policy->nodes);
+ } while (read_mems_allowed_retry(cpuset_mems_cookie));
- nid = next_node_in(current->il_prev, policy->nodes);
if (nid < MAX_NUMNODES)
current->il_prev = nid;
return nid;
@@ -2374,6 +2386,7 @@ static unsigned long alloc_pages_bulk_array_weighted_interleave(gfp_t gfp,
struct page **page_array)
{
struct task_struct *me = current;
+ unsigned int cpuset_mems_cookie;
unsigned long total_allocated = 0;
unsigned long nr_allocated = 0;
unsigned long rounds;
@@ -2391,7 +2404,13 @@ static unsigned long alloc_pages_bulk_array_weighted_interleave(gfp_t gfp,
if (!nr_pages)
return 0;
- nnodes = read_once_policy_nodemask(pol, &nodes);
+ /* read the nodes onto the stack, retry if done during rebind */
+ do {
+ cpuset_mems_cookie = read_mems_allowed_begin();
+ nnodes = read_once_policy_nodemask(pol, &nodes);
+ } while (read_mems_allowed_retry(cpuset_mems_cookie));
+
+ /* if the nodemask has become invalid, we cannot do anything */
if (!nnodes)
return 0;
--
2.39.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox