Linux userland API discussions
 help / color / mirror / Atom feed
* Re: [PATCH net-next v3 2/6] tcp: copy write-data from listen socket to accept child socket
From: Eric Dumazet @ 2025-06-09 16:26 UTC (permalink / raw)
  To: Jeremy Harris; +Cc: netdev, linux-api, ncardwell
In-Reply-To: <838b683c8a47d7df158c6a5da440f38b38096068.1749466540.git.jgh@exim.org>

On Mon, Jun 9, 2025 at 9:05 AM Jeremy Harris <jgh@exim.org> wrote:
>
> Set the request_sock flag for fastopen earlier, making it available
> to the af_ops SYN-handler function.
>
> In that function copy data from the listen socket write queue into an
> sk_buff, allocating if needed and adding to the write queue of the
> newly-created child socket.
> Set sequence number values depending on the fastopen status.
>
> Signed-off-by: Jeremy Harris <jgh@exim.org>
> ---
>  net/ipv4/tcp_fastopen.c  |  3 ++-
>  net/ipv4/tcp_ipv4.c      |  4 +--
>  net/ipv4/tcp_minisocks.c | 58 ++++++++++++++++++++++++++++++++++++----
>  3 files changed, 57 insertions(+), 8 deletions(-)
>
> diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c
> index 9b83d639b5ac..03a86d0b87ba 100644
> --- a/net/ipv4/tcp_fastopen.c
> +++ b/net/ipv4/tcp_fastopen.c
> @@ -245,6 +245,8 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk,
>         struct sock *child;
>         bool own_req;
>
> +       tcp_rsk(req)->tfo_listener = true;
> +
>         child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL,
>                                                          NULL, &own_req);
>         if (!child)
> @@ -261,7 +263,6 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk,
>         tp = tcp_sk(child);
>
>         rcu_assign_pointer(tp->fastopen_rsk, req);
> -       tcp_rsk(req)->tfo_listener = true;
>
>         /* RFC1323: The window in SYN & SYN/ACK segments is never
>          * scaled. So correct it appropriately.
> diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
> index 6a14f9e6fef6..e488effdbdb2 100644
> --- a/net/ipv4/tcp_ipv4.c
> +++ b/net/ipv4/tcp_ipv4.c
> @@ -1747,8 +1747,8 @@ EXPORT_IPV6_MOD(tcp_v4_conn_request);
>
>
>  /*
> - * The three way handshake has completed - we got a valid synack -
> - * now create the new socket.
> + * The three way handshake has completed - we got a valid synack
> + * (or a FASTOPEN syn) - now create the new socket.
>   */
>  struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
>                                   struct request_sock *req,
> diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
> index 43d7852ce07e..d471531b4a78 100644
> --- a/net/ipv4/tcp_minisocks.c
> +++ b/net/ipv4/tcp_minisocks.c
> @@ -529,7 +529,7 @@ struct sock *tcp_create_openreq_child(const struct sock *sk,
>         struct inet_connection_sock *newicsk;
>         const struct tcp_sock *oldtp;
>         struct tcp_sock *newtp;
> -       u32 seq;
> +       u32 seq, a_seq, n_seq;
>
>         if (!newsk)
>                 return NULL;
> @@ -550,9 +550,55 @@ struct sock *tcp_create_openreq_child(const struct sock *sk,
>         newtp->segs_in = 1;
>
>         seq = treq->snt_isn + 1;
> -       newtp->snd_sml = newtp->snd_una = seq;
> -       WRITE_ONCE(newtp->snd_nxt, seq);
> -       newtp->snd_up = seq;
> +       n_seq = seq;
> +       a_seq = seq;
> +       newtp->write_seq = seq;
> +       newtp->snd_una = seq;
> +
> +       /* If there is write-data sitting on the listen socket, copy it to
> +        * the accept socket. If FASTOPEN we will send it on the synack,
> +        * otherwise it sits there until 3rd-ack arrives.
> +        */
> +
> +       if (unlikely(!skb_queue_empty(&sk->sk_write_queue))) {
> +               struct sk_buff *l_skb = tcp_send_head(sk),
> +                               *a_skb = tcp_write_queue_tail(newsk);
> +               ssize_t copy = 0;
> +
> +               if (a_skb)
> +                       copy = l_skb->len - a_skb->len;
> +
> +               if (copy <= 0 || !tcp_skb_can_collapse_to(a_skb)) {
> +                       bool first_skb = tcp_rtx_and_write_queues_empty(newsk);
> +
> +                       a_skb = tcp_stream_alloc_skb(newsk,
> +                                                    newsk->sk_allocation,
> +                                                    first_skb);
> +                       if (!a_skb) {
> +                               /* is this the correct free? */
> +                               bh_unlock_sock(newsk);
> +                               sk_free(newsk);
> +                               return NULL;
> +                       }
> +
> +                       tcp_skb_entail(newsk, a_skb);
> +               }
> +               copy = min_t(int, l_skb->len, skb_tailroom(a_skb));
> +               skb_put_data(a_skb, l_skb->data, copy);

TCP stack no longer puts payload in skb->head. This caused many
issues, trust me.
Please do not try to bring this back.

Also I see no locking there, this is racy with another thread trying
to sendmsg() data to the listener.

Honestly, I do not like this series at all adding cost in TCP fast
path for a very narrow use-case.

^ permalink raw reply

* Re: [DISCUSSION] proposed mctl() API
From: Tejun Heo @ 2025-06-09 17:03 UTC (permalink / raw)
  To: Johannes Weiner
  Cc: Vlastimil Babka, Matthew Wilcox, Lorenzo Stoakes, Andrew Morton,
	Shakeel Butt, Liam R . Howlett, David Hildenbrand, Jann Horn,
	Arnd Bergmann, Christian Brauner, SeongJae Park, Usama Arif,
	Mike Rapoport, Barry Song, linux-mm, linux-arch, linux-kernel,
	linux-api, Pedro Falcato
In-Reply-To: <20250605123156.GA2812@cmpxchg.org>

Hello,

On Thu, Jun 05, 2025 at 08:31:56AM -0400, Johannes Weiner wrote:
> On Wed, Jun 04, 2025 at 08:19:28AM -0400, Johannes Weiner wrote:
> > On Fri, May 30, 2025 at 12:31:35PM +0200, Vlastimil Babka wrote:
...
> > > I've just read the previous threads about Barry's proposal and if doing this
> > > always isn't feasible, I'm wondering if memcg would be a better interface to
> > > opt-in for this kind of behavior than both prctl or mctl. I think at least
> > > conceptually it fits what memcg is doing? The question is if the
> > > implementation would be feasible, and if android puts apps in separate memcgs...
> > 
> > CCing Tejun.
> > 
> > Cgroups has been trying to resist flag settings like these. The cgroup
> > tree is a nested hierarchical structure designed for dividing up
> > system resources. But flag properties don't have natural inheritance
> > rules. What does it mean if the parent group says one thing and the
> > child says another? Which one has precedence?
> > 
> > Hence the proposal to make it a per-process property that propagates
> > through fork() and exec(). This also enables the container usecase (by
> > setting the flag in the container launching process), without there
> > being any confusion what the *effective* setting for any given process
> > in the system is.

+1. If something can work as something which gets inherited through the
process hierarchy, that's usually the better choice than making it a cgroup
property. There isn't much to be gained by making them cgroup properties
especially given that cgroup hierarchy, in most systems at this point, is a
degenerated process hierarchy.

Thanks.

-- 
tejun

^ permalink raw reply

* Re: [DISCUSSION] proposed mctl() API
From: Usama Arif @ 2025-06-10 15:03 UTC (permalink / raw)
  To: Lorenzo Stoakes
  Cc: Andrew Morton, Shakeel Butt, Liam R . Howlett, David Hildenbrand,
	Vlastimil Babka, Jann Horn, Arnd Bergmann, Christian Brauner,
	SeongJae Park, Mike Rapoport, Johannes Weiner, Barry Song,
	linux-mm, linux-arch, linux-kernel, linux-api, Pedro Falcato
In-Reply-To: <d7ccb47b-7124-45e9-ace0-b0fa49f881ef@lucifer.local>



On 30/05/2025 14:10, Lorenzo Stoakes wrote:
> On Thu, May 29, 2025 at 06:21:55PM +0100, Usama Arif wrote:
>>
>>
>> My knowledge is security is limited, so please bare with me, but I actually
>> didn't understand the security issue and the need for CAP_SYS_ADMIN for
>> doing VM_(NO)HUGEPAGE.
>>
>> A process can already madvise its own VMAs, and this is just doing that
>> for the entire process. And VM_INIT_DEF_MASK is already set to VM_NOHUGEPAGE
>> so it will be inherited by the parent. Just adding VM_HUGEPAGE shouldnt be
>> a issue? Inheriting MMF_VM_HUGEPAGE will mean that khugepaged would enter
>> for that process as well, which again doesnt seem like a security issue
>> to me.
> 
> W.R.T. the current process, the Issue is one Jann raised, in relation to
> propagation of behaviour to privileged (e.g. setuid) processes.
> 

But what is the actual security issue of having hugepages (or not having them) when
the process is running with setuid?

I know the cgroup proposal has been shot down, but lets imagine if this was a cgroup
setting, similar to the other memory controls we have, for e.g. memory.swap.{max,high,peak}.

We can chown the cgroup so that the property is set by unprivileged process. 

Having the process swap with setuid when the unprivileged process has swap disabled
in the cgroup is not the right behaviour. What currently happens is that the process
after obtaining the higher privilege level doesn't swap as well.

Similarly for hugepages, if it was a cgroup level setting, having the process give
hugepages always with setuid when the unprivileged user had it disabled it or vice versa
would not be the right behaviour.

Another example is PR_SET_MEMORY_MERGE, setuid does not change how it works as far as
I can tell.

So madlibs I dont see what the security issue is and why we would need to elevate privileges
to do this.

> W.R.T. remote processes, obviously we want to make sure we are permitted to do
> so.
> 

I know that this needs to be future proof. But I don't actually know of a real world
usecase where we want to do any of these things for remote processes.
Whether its the existing per process changes like PR_SET_MEMORY_MERGE for KSM and
PR_SET_THP_DISABLE for THP or the newer proposals of PR_DEFAULT_MADV_(NO)HUGEPAGE
or Barrys proposal.
All of them are for the process itself (and its children by fork+exec) and not for
remote processes. As we try to make our changes usecase driven, I think we should
not add support for remote processes (which is another reason why I think this might
sit better in prctl).

^ permalink raw reply

* Re: [PATCH RFT v17 4/8] fork: Add shadow stack support to clone3()
From: Yury Khrustalev @ 2025-06-10 15:14 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, Valentin Schneider, Christian Brauner, Shuah Khan,
	linux-kernel, Catalin Marinas, Will Deacon, jannh, Wilco Dijkstra,
	linux-kselftest, linux-api, Kees Cook
In-Reply-To: <20250609-clone3-shadow-stack-v17-4-8840ed97ff6f@kernel.org>

On Mon, Jun 09, 2025 at 01:54:05PM +0100, Mark Brown wrote:
> Unlike with the normal stack there is no API for configuring 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 shadow stack pointer to use
> for the new thread, this is inconsistent with the way we specify the
> normal stack but during review concerns were expressed about having to
> identify where the shadow stack pointer should be placed especially in
> cases where the shadow stack has been previously active.  If no shadow
> stack is specified then the existing implicit allocation behaviour is
> maintained.
> 
> If a shadow stack pointer is specified then it is required to have an
> architecture defined token placed on the stack, this will be consumed by
> the new task.  If no valid token is present then this will be reported
> with -EINVAL.  This token prevents new threads being created pointing at
> the shadow stack of an existing running thread.
> 
> If the architecture does not support shadow stacks the shadow stack
> pointer must be not be specified, architectures that do support the
> feature are expected to enforce the same requirement on individual
> systems that lack shadow stack support.
> 
> Update the existing arm64 and x86 implementations to pay attention to
> the newly added arguments, in order to maintain compatibility we use the
> existing behaviour if no shadow stack is specified. Since we are now
> using more fields from the kernel_clone_args we pass that into the
> shadow stack code rather than individual fields.
> 
> Portions of the x86 architecture code were written by Rick Edgecombe.
> 
> Acked-by: Yury Khrustalev <yury.khrustalev@arm.com>

LGTM. I've tested this change on the FVP model along with my Glibc patch
series [1] and confirm that it works fine. The Glibc patch is at RFC stage
and will require more work, but it's enough to test this series.

Tested-by: Yury Khrustalev <yury.khrustalev@arm.com>

[1]: https://inbox.sourceware.org/libc-alpha/20250610151320.885131-1-yury.khrustalev@arm.com/

Thanks,
Yury


^ permalink raw reply

* Re: [DISCUSSION] proposed mctl() API
From: Lorenzo Stoakes @ 2025-06-10 15:17 UTC (permalink / raw)
  To: Usama Arif
  Cc: Andrew Morton, Shakeel Butt, Liam R . Howlett, David Hildenbrand,
	Vlastimil Babka, Jann Horn, Arnd Bergmann, Christian Brauner,
	SeongJae Park, Mike Rapoport, Johannes Weiner, Barry Song,
	linux-mm, linux-arch, linux-kernel, linux-api, Pedro Falcato
In-Reply-To: <f8db6b39-f11a-4378-8976-4169f4674e85@gmail.com>

On Tue, Jun 10, 2025 at 04:03:07PM +0100, Usama Arif wrote:
>
>
> On 30/05/2025 14:10, Lorenzo Stoakes wrote:
> > On Thu, May 29, 2025 at 06:21:55PM +0100, Usama Arif wrote:
> >>
> >>
> >> My knowledge is security is limited, so please bare with me, but I actually
> >> didn't understand the security issue and the need for CAP_SYS_ADMIN for
> >> doing VM_(NO)HUGEPAGE.
> >>
> >> A process can already madvise its own VMAs, and this is just doing that
> >> for the entire process. And VM_INIT_DEF_MASK is already set to VM_NOHUGEPAGE
> >> so it will be inherited by the parent. Just adding VM_HUGEPAGE shouldnt be
> >> a issue? Inheriting MMF_VM_HUGEPAGE will mean that khugepaged would enter
> >> for that process as well, which again doesnt seem like a security issue
> >> to me.
> >
> > W.R.T. the current process, the Issue is one Jann raised, in relation to
> > propagation of behaviour to privileged (e.g. setuid) processes.
> >
>
> But what is the actual security issue of having hugepages (or not having them) when
> the process is running with setuid?

Speak to Jann about this. Security isn't my area. He gave feedback on this,
which is why I raised it, if you search through previous threads you can find
it.

>
> I know the cgroup proposal has been shot down, but lets imagine if this was a cgroup
> setting, similar to the other memory controls we have, for e.g. memory.swap.{max,high,peak}.
>
> We can chown the cgroup so that the property is set by unprivileged process.
>
> Having the process swap with setuid when the unprivileged process has swap disabled
> in the cgroup is not the right behaviour. What currently happens is that the process
> after obtaining the higher privilege level doesn't swap as well.
>
> Similarly for hugepages, if it was a cgroup level setting, having the process give
> hugepages always with setuid when the unprivileged user had it disabled it or vice versa
> would not be the right behaviour.
>
> Another example is PR_SET_MEMORY_MERGE, setuid does not change how it works as far as
> I can tell.
>
> So madlibs I dont see what the security issue is and why we would need to elevate privileges
> to do this.
>
> > W.R.T. remote processes, obviously we want to make sure we are permitted to do
> > so.
> >
>
> I know that this needs to be future proof. But I don't actually know of a real world
> usecase where we want to do any of these things for remote processes.
> Whether its the existing per process changes like PR_SET_MEMORY_MERGE for KSM and
> PR_SET_THP_DISABLE for THP or the newer proposals of PR_DEFAULT_MADV_(NO)HUGEPAGE
> or Barrys proposal.
> All of them are for the process itself (and its children by fork+exec) and not for
> remote processes. As we try to make our changes usecase driven, I think we should
> not add support for remote processes (which is another reason why I think this might
> sit better in prctl).

I'm extremely confused as to why you think this propoal is predicated upon
remote process manipulation? It was simply suggested as a possibility for
increased flexibility.

We can just remove this parameter no?

It is entirely orthogonal to the prctl() stuff.

Overall at this point I share Matthew's point of view on this - we shouldn't be
doing any of this upstream.

^ permalink raw reply

* Re: [DISCUSSION] proposed mctl() API
From: Usama Arif @ 2025-06-10 15:30 UTC (permalink / raw)
  To: Lorenzo Stoakes, David Hildenbrand
  Cc: Andrew Morton, Shakeel Butt, Liam R . Howlett, Vlastimil Babka,
	Jann Horn, Arnd Bergmann, Christian Brauner, SeongJae Park,
	Mike Rapoport, Johannes Weiner, Barry Song, linux-mm, linux-arch,
	linux-kernel, linux-api, Pedro Falcato, Matthew Wilcox
In-Reply-To: <fcaa7ce6-3f03-4e3d-aa9f-1b1b53ed88f5@lucifer.local>



On 10/06/2025 16:17, Lorenzo Stoakes wrote:
> On Tue, Jun 10, 2025 at 04:03:07PM +0100, Usama Arif wrote:
>>
>>
>> On 30/05/2025 14:10, Lorenzo Stoakes wrote:
>>> On Thu, May 29, 2025 at 06:21:55PM +0100, Usama Arif wrote:
>>>>
>>>>
>>>> My knowledge is security is limited, so please bare with me, but I actually
>>>> didn't understand the security issue and the need for CAP_SYS_ADMIN for
>>>> doing VM_(NO)HUGEPAGE.
>>>>
>>>> A process can already madvise its own VMAs, and this is just doing that
>>>> for the entire process. And VM_INIT_DEF_MASK is already set to VM_NOHUGEPAGE
>>>> so it will be inherited by the parent. Just adding VM_HUGEPAGE shouldnt be
>>>> a issue? Inheriting MMF_VM_HUGEPAGE will mean that khugepaged would enter
>>>> for that process as well, which again doesnt seem like a security issue
>>>> to me.
>>>
>>> W.R.T. the current process, the Issue is one Jann raised, in relation to
>>> propagation of behaviour to privileged (e.g. setuid) processes.
>>>
>>
>> But what is the actual security issue of having hugepages (or not having them) when
>> the process is running with setuid?
> 
> Speak to Jann about this. Security isn't my area. He gave feedback on this,
> which is why I raised it, if you search through previous threads you can find
> it.
> 

Yes, he is in CC here as well. I have read it in the previous thread. Just raising it
here as it was mentioned here :)

>>
>> I know the cgroup proposal has been shot down, but lets imagine if this was a cgroup
>> setting, similar to the other memory controls we have, for e.g. memory.swap.{max,high,peak}.
>>
>> We can chown the cgroup so that the property is set by unprivileged process.
>>
>> Having the process swap with setuid when the unprivileged process has swap disabled
>> in the cgroup is not the right behaviour. What currently happens is that the process
>> after obtaining the higher privilege level doesn't swap as well.
>>
>> Similarly for hugepages, if it was a cgroup level setting, having the process give
>> hugepages always with setuid when the unprivileged user had it disabled it or vice versa
>> would not be the right behaviour.
>>
>> Another example is PR_SET_MEMORY_MERGE, setuid does not change how it works as far as
>> I can tell.
>>
>> So madlibs I dont see what the security issue is and why we would need to elevate privileges
>> to do this.
>>
>>> W.R.T. remote processes, obviously we want to make sure we are permitted to do
>>> so.
>>>
>>
>> I know that this needs to be future proof. But I don't actually know of a real world
>> usecase where we want to do any of these things for remote processes.
>> Whether its the existing per process changes like PR_SET_MEMORY_MERGE for KSM and
>> PR_SET_THP_DISABLE for THP or the newer proposals of PR_DEFAULT_MADV_(NO)HUGEPAGE
>> or Barrys proposal.
>> All of them are for the process itself (and its children by fork+exec) and not for
>> remote processes. As we try to make our changes usecase driven, I think we should
>> not add support for remote processes (which is another reason why I think this might
>> sit better in prctl).
> 
> I'm extremely confused as to why you think this propoal is predicated upon
> remote process manipulation? It was simply suggested as a possibility for
> increased flexibility.
> 
> We can just remove this parameter no?
> 

Sure.

> It is entirely orthogonal to the prctl() stuff.
> 
> Overall at this point I share Matthew's point of view on this - we shouldn't be
> doing any of this upstream.

As I replied to Matthew in [1], it would be amazing if it was not needed, but thats not
how it works in the medium term and I dont think it will work even in the long term.
I will paste my answer from [1] below as well:

If we have 2 workloads on the same server, For e.g. one is database where THPs 
just dont do well, but the other one is AI where THPs do really well. How
will the kernel monitor that the database workload is performing worse
and the AI one isnt?

I added THP shrinker to hopefully try and do this automatically, and it does
really help. But unfortunately it is not a complete solution.
There are severely memory bound workloads where even a tiny increase
in memory will lead to an OOM. And if you colocate the container thats running
that workload with one in which we will benefit with THPs, we unfortunately
can't just rely on the system doing the right thing.

It would be awesome if THPs are truly transparent and don't require
any input, but unfortunately I don't think that there is a solution
for this with just kernel monitoring.

This is just a big hint from the user. If the global system policy is madvise
and the workload owner has done their own benchmarks and see benefits
with always, they set DEFAULT_MADV_HUGEPAGE for the process to optin as "always".
If the global system policy is always and the workload owner has done their own 
benchmarks and see worse results with always, they set DEFAULT_MADV_NOHUGEPAGE for 
the process to optin as "madvise". 

[1] https://lore.kernel.org/all/162c14e6-0b16-4698-bd76-735037ea0d73@gmail.com/


I havent seen activity on this thread over the past week, but I was hoping
we can reach a consensus on which approach to use, prctl or mctl.
If its mctl and if you don't think this should be done, please let me know
if you would like me to work on this instead. This is a valid big realworld
usecase that is a real blocker for deploying THPs in workloads in servers.

Thanks!
Usama

^ permalink raw reply

* Re: [DISCUSSION] proposed mctl() API
From: Matthew Wilcox @ 2025-06-10 15:46 UTC (permalink / raw)
  To: Usama Arif
  Cc: Lorenzo Stoakes, David Hildenbrand, Andrew Morton, Shakeel Butt,
	Liam R . Howlett, Vlastimil Babka, Jann Horn, Arnd Bergmann,
	Christian Brauner, SeongJae Park, Mike Rapoport, Johannes Weiner,
	Barry Song, linux-mm, linux-arch, linux-kernel, linux-api,
	Pedro Falcato
In-Reply-To: <2fd7f80c-2b13-4478-900a-d65547586db3@gmail.com>

On Tue, Jun 10, 2025 at 04:30:43PM +0100, Usama Arif wrote:
> If we have 2 workloads on the same server, For e.g. one is database where THPs 
> just dont do well, but the other one is AI where THPs do really well. How
> will the kernel monitor that the database workload is performing worse
> and the AI one isnt?

It can monitor the allocation/access patterns and see who's getting
the benefit.  The two workloads are in competition for memory, and
we can tell which pages are hot and which cold.

And I don't believe it's a binary anyway.  I bet there are some
allocations where the database benefits from having THPs (I mean, I know
a database which invented the entire hugetlbfs subsystem so it could
use PMD entries and avoid one layer of TLB misses!)

> I added THP shrinker to hopefully try and do this automatically, and it does
> really help. But unfortunately it is not a complete solution.
> There are severely memory bound workloads where even a tiny increase
> in memory will lead to an OOM. And if you colocate the container thats running
> that workload with one in which we will benefit with THPs, we unfortunately
> can't just rely on the system doing the right thing.

Then maybe THP aren't for you.  If your workloads are this sensitive,
perhaps you should be using a mechanism which gives you complete control
like hugetlbfs.

^ permalink raw reply

* Re: [DISCUSSION] proposed mctl() API
From: Usama Arif @ 2025-06-10 16:00 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: Lorenzo Stoakes, David Hildenbrand, Andrew Morton, Shakeel Butt,
	Liam R . Howlett, Vlastimil Babka, Jann Horn, Arnd Bergmann,
	Christian Brauner, SeongJae Park, Mike Rapoport, Johannes Weiner,
	Barry Song, linux-mm, linux-arch, linux-kernel, linux-api,
	Pedro Falcato
In-Reply-To: <aEhTYkzsTsaBua40@casper.infradead.org>



On 10/06/2025 16:46, Matthew Wilcox wrote:
> On Tue, Jun 10, 2025 at 04:30:43PM +0100, Usama Arif wrote:
>> If we have 2 workloads on the same server, For e.g. one is database where THPs 
>> just dont do well, but the other one is AI where THPs do really well. How
>> will the kernel monitor that the database workload is performing worse
>> and the AI one isnt?
> 
> It can monitor the allocation/access patterns and see who's getting
> the benefit.  The two workloads are in competition for memory, and
> we can tell which pages are hot and which cold.
> 
> And I don't believe it's a binary anyway.  I bet there are some
> allocations where the database benefits from having THPs (I mean, I know
> a database which invented the entire hugetlbfs subsystem so it could
> use PMD entries and avoid one layer of TLB misses!)
> 

Sure, but this is just an example. Workload owners are not going to spend time
trying to see how each allocation works and if its hot, they put it in hugetlbfs.
Ofcourse hugetlbfs has its own drawbacks of reserving pages.
This is one of the reasons that we have THPs.

But they will try THPs. i.e. if they see performance benefits from just turning
a knob, they will take it otherwise leave it.

>> I added THP shrinker to hopefully try and do this automatically, and it does
>> really help. But unfortunately it is not a complete solution.
>> There are severely memory bound workloads where even a tiny increase
>> in memory will lead to an OOM. And if you colocate the container thats running
>> that workload with one in which we will benefit with THPs, we unfortunately
>> can't just rely on the system doing the right thing.
> 
> Then maybe THP aren't for you.  If your workloads are this sensitive,
> perhaps you should be using a mechanism which gives you complete control
> like hugetlbfs.

Yes, completely agree, THPs aren't for the workloads that are this sensitive.
But that's why we need this, to disable it for them if the global policy is always,
or enable it on other services that are not sensitive and benefit from THPs
if the global policy is madvise. We have to keep in mind that these workloads
will be colocated on the same server. 

and hugetlbfs isnt transparent enough.. :)


^ permalink raw reply

* Re: [DISCUSSION] proposed mctl() API
From: Lorenzo Stoakes @ 2025-06-10 16:02 UTC (permalink / raw)
  To: Usama Arif
  Cc: David Hildenbrand, Andrew Morton, Shakeel Butt, Liam R . Howlett,
	Vlastimil Babka, Jann Horn, Arnd Bergmann, Christian Brauner,
	SeongJae Park, Mike Rapoport, Johannes Weiner, Barry Song,
	linux-mm, linux-arch, linux-kernel, linux-api, Pedro Falcato,
	Matthew Wilcox
In-Reply-To: <2fd7f80c-2b13-4478-900a-d65547586db3@gmail.com>

On Tue, Jun 10, 2025 at 04:30:43PM +0100, Usama Arif wrote:
>
>
> On 10/06/2025 16:17, Lorenzo Stoakes wrote:
> > On Tue, Jun 10, 2025 at 04:03:07PM +0100, Usama Arif wrote:
> >>
> >>
> >> On 30/05/2025 14:10, Lorenzo Stoakes wrote:
> >>> On Thu, May 29, 2025 at 06:21:55PM +0100, Usama Arif wrote:
> >>>>
> >>>>
> >>>> My knowledge is security is limited, so please bare with me, but I actually
> >>>> didn't understand the security issue and the need for CAP_SYS_ADMIN for
> >>>> doing VM_(NO)HUGEPAGE.
> >>>>
> >>>> A process can already madvise its own VMAs, and this is just doing that
> >>>> for the entire process. And VM_INIT_DEF_MASK is already set to VM_NOHUGEPAGE
> >>>> so it will be inherited by the parent. Just adding VM_HUGEPAGE shouldnt be
> >>>> a issue? Inheriting MMF_VM_HUGEPAGE will mean that khugepaged would enter
> >>>> for that process as well, which again doesnt seem like a security issue
> >>>> to me.
> >>>
> >>> W.R.T. the current process, the Issue is one Jann raised, in relation to
> >>> propagation of behaviour to privileged (e.g. setuid) processes.
> >>>
> >>
> >> But what is the actual security issue of having hugepages (or not having them) when
> >> the process is running with setuid?
> >
> > Speak to Jann about this. Security isn't my area. He gave feedback on this,
> > which is why I raised it, if you search through previous threads you can find
> > it.
> >
>
> Yes, he is in CC here as well. I have read it in the previous thread. Just raising it
> here as it was mentioned here :)
>
> >>
> >> I know the cgroup proposal has been shot down, but lets imagine if this was a cgroup
> >> setting, similar to the other memory controls we have, for e.g. memory.swap.{max,high,peak}.
> >>
> >> We can chown the cgroup so that the property is set by unprivileged process.
> >>
> >> Having the process swap with setuid when the unprivileged process has swap disabled
> >> in the cgroup is not the right behaviour. What currently happens is that the process
> >> after obtaining the higher privilege level doesn't swap as well.
> >>
> >> Similarly for hugepages, if it was a cgroup level setting, having the process give
> >> hugepages always with setuid when the unprivileged user had it disabled it or vice versa
> >> would not be the right behaviour.
> >>
> >> Another example is PR_SET_MEMORY_MERGE, setuid does not change how it works as far as
> >> I can tell.
> >>
> >> So madlibs I dont see what the security issue is and why we would need to elevate privileges
> >> to do this.
> >>
> >>> W.R.T. remote processes, obviously we want to make sure we are permitted to do
> >>> so.
> >>>
> >>
> >> I know that this needs to be future proof. But I don't actually know of a real world
> >> usecase where we want to do any of these things for remote processes.
> >> Whether its the existing per process changes like PR_SET_MEMORY_MERGE for KSM and
> >> PR_SET_THP_DISABLE for THP or the newer proposals of PR_DEFAULT_MADV_(NO)HUGEPAGE
> >> or Barrys proposal.
> >> All of them are for the process itself (and its children by fork+exec) and not for
> >> remote processes. As we try to make our changes usecase driven, I think we should
> >> not add support for remote processes (which is another reason why I think this might
> >> sit better in prctl).
> >
> > I'm extremely confused as to why you think this propoal is predicated upon
> > remote process manipulation? It was simply suggested as a possibility for
> > increased flexibility.
> >
> > We can just remove this parameter no?
> >
>
> Sure.
>
> > It is entirely orthogonal to the prctl() stuff.
> >
> > Overall at this point I share Matthew's point of view on this - we shouldn't be
> > doing any of this upstream.
>
> As I replied to Matthew in [1], it would be amazing if it was not needed, but thats not
> how it works in the medium term and I dont think it will work even in the long term.
> I will paste my answer from [1] below as well:
>
> If we have 2 workloads on the same server, For e.g. one is database where THPs
> just dont do well, but the other one is AI where THPs do really well. How
> will the kernel monitor that the database workload is performing worse
> and the AI one isnt?
>
> I added THP shrinker to hopefully try and do this automatically, and it does
> really help. But unfortunately it is not a complete solution.
> There are severely memory bound workloads where even a tiny increase
> in memory will lead to an OOM. And if you colocate the container thats running
> that workload with one in which we will benefit with THPs, we unfortunately
> can't just rely on the system doing the right thing.
>
> It would be awesome if THPs are truly transparent and don't require
> any input, but unfortunately I don't think that there is a solution
> for this with just kernel monitoring.
>
> This is just a big hint from the user. If the global system policy is madvise
> and the workload owner has done their own benchmarks and see benefits
> with always, they set DEFAULT_MADV_HUGEPAGE for the process to optin as "always".
> If the global system policy is always and the workload owner has done their own
> benchmarks and see worse results with always, they set DEFAULT_MADV_NOHUGEPAGE for
> the process to optin as "madvise".
>
> [1] https://lore.kernel.org/all/162c14e6-0b16-4698-bd76-735037ea0d73@gmail.com/
>
>

Yup I appreciate these points, and we have discussed them I feel quite a
bit :) I echo them.

Nobody says that the interface isn't sucky and THPs are not as transparent
as they should be, nor that we lack decent non-cgroup 'policy'
manipulation.

BUT.

We're talking about adding a permanent hack into the kernel that
force-sets a VMA flag for all VMAs across fork/exec.

I have simply been trying to flesh out the _least worst_ means of
doing this - _if we have to do it_.

That last bit being operative - I have come to think, based on Matthew's
feedback, that the RoI of permanently adding this hack is not a good one.

I think the case remains to be made for that.

> I havent seen activity on this thread over the past week, but I was hoping
> we can reach a consensus on which approach to use, prctl or mctl.
> If its mctl and if you don't think this should be done, please let me know
> if you would like me to work on this instead. This is a valid big realworld
> usecase that is a real blocker for deploying THPs in workloads in servers.

Please exercise patience, upstream moves at its own pace.

>
> Thanks!
> Usama

^ permalink raw reply

* Re: [DISCUSSION] proposed mctl() API
From: Matthew Wilcox @ 2025-06-10 16:26 UTC (permalink / raw)
  To: Usama Arif
  Cc: Lorenzo Stoakes, David Hildenbrand, Andrew Morton, Shakeel Butt,
	Liam R . Howlett, Vlastimil Babka, Jann Horn, Arnd Bergmann,
	Christian Brauner, SeongJae Park, Mike Rapoport, Johannes Weiner,
	Barry Song, linux-mm, linux-arch, linux-kernel, linux-api,
	Pedro Falcato
In-Reply-To: <8c762435-f5d8-4366-84de-308c8280ff3d@gmail.com>

On Tue, Jun 10, 2025 at 05:00:47PM +0100, Usama Arif wrote:
> On 10/06/2025 16:46, Matthew Wilcox wrote:
> > On Tue, Jun 10, 2025 at 04:30:43PM +0100, Usama Arif wrote:
> >> If we have 2 workloads on the same server, For e.g. one is database where THPs 
> >> just dont do well, but the other one is AI where THPs do really well. How
> >> will the kernel monitor that the database workload is performing worse
> >> and the AI one isnt?
> > 
> > It can monitor the allocation/access patterns and see who's getting
> > the benefit.  The two workloads are in competition for memory, and
> > we can tell which pages are hot and which cold.
> > 
> > And I don't believe it's a binary anyway.  I bet there are some
> > allocations where the database benefits from having THPs (I mean, I know
> > a database which invented the entire hugetlbfs subsystem so it could
> > use PMD entries and avoid one layer of TLB misses!)
> > 
> 
> Sure, but this is just an example. Workload owners are not going to spend time
> trying to see how each allocation works and if its hot, they put it in hugetlbfs.

No, they're not.  It should be automatic.  There are many deficiencies
in the kernel; this is one of them.

> Ofcourse hugetlbfs has its own drawbacks of reserving pages.

Drawback or advantage?  It's a feature.  You're being very strange about
this.  First you want to reserve THPs for some workloads only, then when
given a way to do that you complain that ... you have to reserve hugetlb
pages.  You can't possibly mean both of these things sincerely.


^ permalink raw reply

* Re: [DISCUSSION] proposed mctl() API
From: Usama Arif @ 2025-06-10 17:02 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: Lorenzo Stoakes, David Hildenbrand, Andrew Morton, Shakeel Butt,
	Liam R . Howlett, Vlastimil Babka, Jann Horn, Arnd Bergmann,
	Christian Brauner, SeongJae Park, Mike Rapoport, Johannes Weiner,
	Barry Song, linux-mm, linux-arch, linux-kernel, linux-api,
	Pedro Falcato
In-Reply-To: <aEhct_dQxGAazoiY@casper.infradead.org>



On 10/06/2025 17:26, Matthew Wilcox wrote:
> On Tue, Jun 10, 2025 at 05:00:47PM +0100, Usama Arif wrote:
>> On 10/06/2025 16:46, Matthew Wilcox wrote:
>>> On Tue, Jun 10, 2025 at 04:30:43PM +0100, Usama Arif wrote:
>>>> If we have 2 workloads on the same server, For e.g. one is database where THPs 
>>>> just dont do well, but the other one is AI where THPs do really well. How
>>>> will the kernel monitor that the database workload is performing worse
>>>> and the AI one isnt?
>>>
>>> It can monitor the allocation/access patterns and see who's getting
>>> the benefit.  The two workloads are in competition for memory, and
>>> we can tell which pages are hot and which cold.
>>>
>>> And I don't believe it's a binary anyway.  I bet there are some
>>> allocations where the database benefits from having THPs (I mean, I know
>>> a database which invented the entire hugetlbfs subsystem so it could
>>> use PMD entries and avoid one layer of TLB misses!)
>>>
>>
>> Sure, but this is just an example. Workload owners are not going to spend time
>> trying to see how each allocation works and if its hot, they put it in hugetlbfs.
> 
> No, they're not.  It should be automatic.  There are many deficiencies
> in the kernel; this is one of them.
> 
>> Ofcourse hugetlbfs has its own drawbacks of reserving pages.
> 
> Drawback or advantage?  It's a feature.  You're being very strange about
> this.  First you want to reserve THPs for some workloads only, then when
> given a way to do that you complain that ... you have to reserve hugetlb
> pages.  You can't possibly mean both of these things sincerely.
> 

Let me try and explain my view better:

hugetlb requires 2 things, reserving hugepages and passing MAP_HUGETLB at mmap time i.e.
not "transparent". (I know the meaning of transparent even in THP is a bit messed up :))

There are some workload owners that will happily test (and have the resources to do
so) to see what is the best point to use hugetlb. They can go in their code and change
mmap and make the necessary changes to disrupt workload orchestration so that hugetlb
is reserved. This is a small minority.

An extremely large majority of workload owners will not be willing to do this (and don't
have the resources to do so as well).
For them, we have THPs to do it "transparently". If you just give a knob to switch
THP=always on/off for *just their workload* without affecting others on the same server,
they will be happy to try it and other workloads that are running on the same server
in controlled cgroups wont care and won't be affected. i.e.:

- if the machine policy (/sys/kernel/mm/transparent_hugepage/enabled) is madvise, workloads can
  opt-in getting THPs by just having this call (the PR_DEFAULT_MADV_HUGEPAGE version) in systemd.

- if the machine policy is always, and they dont benefit, they can opt-out of getting THPs
  by having this call (the PR_DEFAULT_MADV_NOHUGEPAGE) version in systemd *without* disrupting
  the other workloads that are running on the same server that do.

Doing above is very simple. This is how KSM is done as well. It doesnt require doing any changes
to mmap, i.e. is "transparent" (after the prctl/mctl call :)) and doesn't require reserving anything
for hugetlb before the application starts.



^ permalink raw reply

* Re: [PATCH 07/10] fs: introduce FALLOC_FL_WRITE_ZEROES to fallocate
From: Darrick J. Wong @ 2025-06-11 15:05 UTC (permalink / raw)
  To: Zhang Yi
  Cc: linux-fsdevel, linux-ext4, linux-block, dm-devel, linux-nvme,
	linux-scsi, linux-xfs, linux-kernel, hch, tytso, john.g.garry,
	bmarzins, chaitanyak, shinichiro.kawasaki, brauner,
	martin.petersen, yi.zhang, chengzhihao1, yukuai3, yangerkun,
	linux-api
In-Reply-To: <20250604020850.1304633-8-yi.zhang@huaweicloud.com>

[cc linux-api about a fallocate uapi change]

On Wed, Jun 04, 2025 at 10:08:47AM +0800, Zhang Yi wrote:
> From: Zhang Yi <yi.zhang@huawei.com>
> 
> With the development of flash-based storage devices, we can quickly
> write zeros to SSDs using the WRITE_ZERO command if the devices do not
> actually write physical zeroes to the media. Therefore, we can use this
> command to quickly preallocate a real all-zero file with written
> extents. This approach should be beneficial for subsequent pure
> overwriting within this file, as it can save on block allocation and,
> consequently, significant metadata changes, which should greatly improve
> overwrite performance on certain filesystems.
> 
> Therefore, introduce a new operation FALLOC_FL_WRITE_ZEROES to
> fallocate. This flag is used to convert a specified range of a file to
> zeros by issuing a zeroing operation. Blocks should be allocated for the
> regions that span holes in the file, and the entire range is converted
> to written extents. If the underlying device supports the actual offload
> write zeroes command, the process of zeroing out operation can be
> accelerated. If it does not, we currently don't prevent the file system
> from writing actual zeros to the device. This provides users with a new
> method to quickly generate a zeroed file, users no longer need to write
> zero data to create a file with written extents.
> 
> Users can determine whether a disk supports the unmap write zeroes
> operation through querying this sysfs interface:
> 
>     /sys/block/<disk>/queue/write_zeroes_unmap
> 
> Finally, this flag cannot be specified in conjunction with the
> FALLOC_FL_KEEP_SIZE since allocating written extents beyond file EOF is
> not permitted. In addition, filesystems that always require out-of-place
> writes should not support this flag since they still need to allocated
> new blocks during subsequent overwrites.
> 
> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/open.c                   |  1 +
>  include/linux/falloc.h      |  3 ++-
>  include/uapi/linux/falloc.h | 18 ++++++++++++++++++
>  3 files changed, 21 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/open.c b/fs/open.c
> index 7828234a7caa..b777e11e5522 100644
> --- a/fs/open.c
> +++ b/fs/open.c
> @@ -281,6 +281,7 @@ int vfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
>  		break;
>  	case FALLOC_FL_COLLAPSE_RANGE:
>  	case FALLOC_FL_INSERT_RANGE:
> +	case FALLOC_FL_WRITE_ZEROES:
>  		if (mode & FALLOC_FL_KEEP_SIZE)
>  			return -EOPNOTSUPP;
>  		break;
> diff --git a/include/linux/falloc.h b/include/linux/falloc.h
> index 3f49f3df6af5..7c38c6b76b60 100644
> --- a/include/linux/falloc.h
> +++ b/include/linux/falloc.h
> @@ -36,7 +36,8 @@ struct space_resv {
>  				 FALLOC_FL_COLLAPSE_RANGE |	\
>  				 FALLOC_FL_ZERO_RANGE |		\
>  				 FALLOC_FL_INSERT_RANGE |	\
> -				 FALLOC_FL_UNSHARE_RANGE)
> +				 FALLOC_FL_UNSHARE_RANGE |	\
> +				 FALLOC_FL_WRITE_ZEROES)
>  
>  /* on ia32 l_start is on a 32-bit boundary */
>  #if defined(CONFIG_X86_64)
> diff --git a/include/uapi/linux/falloc.h b/include/uapi/linux/falloc.h
> index 5810371ed72b..265aae7ff8c1 100644
> --- a/include/uapi/linux/falloc.h
> +++ b/include/uapi/linux/falloc.h
> @@ -78,4 +78,22 @@
>   */
>  #define FALLOC_FL_UNSHARE_RANGE		0x40
>  
> +/*
> + * FALLOC_FL_WRITE_ZEROES is used to convert a specified range of a file to
> + * zeros by issuing a zeroing operation. Blocks should be allocated for the
> + * regions that span holes in the file, and the entire range is converted to
> + * written extents.

I think you could simplify this a bit by talking only about the end
state after a successful call:

"FALLOC_FL_WRITE_ZEROES zeroes a specified file range in such a way that
subsequent writes to that range do not require further changes to file
mapping metadata."

Note that we don't say how the filesystem gets to this goal.  Presumably
the first implementations will send a zeroing operation to the block
device during allocation and the fs will create written mappings, but
there are other ways to get there -- a filesystem could maintain a pool
of pre-zeroed space and hand those out; or it could zero space on
freeing and mounting such that all new mappings can be created as
written even without the block device zeroing operation.

Or you could be running on some carefully engineered system where you
know the storage will always be zeroed at allocation time due to some
other aspect of the system design, e.g. a single-use throwaway cloud vm
where you allocate to the end of the disk and reboot the node.

> + *                  This flag is beneficial for subsequent pure overwriting
> + * within this range, as it can save on block allocation and, consequently,
> + * significant metadata changes. Therefore, filesystems that always require
> + * out-of-place writes should not support this flag.
> + *
> + * Different filesystems may implement different limitations on the
> + * granularity of the zeroing operation. Most will preferably be accelerated
> + * by submitting write zeroes command if the backing storage supports, which
> + * may not physically write zeros to the media.
> + *
> + * This flag cannot be specified in conjunction with the FALLOC_FL_KEEP_SIZE.
> + */
> +#define FALLOC_FL_WRITE_ZEROES		0x80

The rest of the writeup seems fine to me.

--D

> +
>  #endif /* _UAPI_FALLOC_H_ */
> -- 
> 2.46.1
> 
> 

^ permalink raw reply

* Re: [PATCH] man/man2/memfd_secret.2: Correct the flags
From: Alejandro Colomar @ 2025-06-12  9:45 UTC (permalink / raw)
  To: Zhengyi Fu
  Cc: linux-man, David Herrmann, Mike Rapoport, David Rheinsberg,
	Hugh Dickins, Hagen Paul Pfeifer, James Bottomley, Andrew Morton,
	Linus Torvalds, Andy Lutomirski, linux-kernel, linux-api,
	linux-fsdevel, linux-mm
In-Reply-To: <20250612061705.1177931-1-i@fuzy.me>

[-- Attachment #1: Type: text/plain, Size: 9270 bytes --]

[CC += people related to memfd_{create,secret}(2) in the kernel]

Hi Zhengyi,

On Thu, Jun 12, 2025 at 02:17:05PM +0800, Zhengyi Fu wrote:
> memfd_secret returns EINVAL when called with FD_CLOEXEC.  The
> correct flag should be O_CLOEXEC.

Thanks for the report!  It seems like a bug in the kernel.  The
documentation was written (relatively) consistent with memfd_create(2),
but the implementation was made different.  I say the documentation was
relatively consistent, because memfd_create(2) uses MFD_CLOEXEC, and
memfd_secret(2) documents FD_CLOEXEC, which could be confused, and since
they have the same value, it could be considered just a typo.  However,
O_CLOEXEC is an entirely different flag, which doesn't seem to make
sense here.

	$ grepc -tfld memfd_create . | grep -A4 -e '^[{}.]' -e CLOEXEC;
	./mm/memfd.c:SYSCALL_DEFINE2(memfd_create,
			const char __user *, uname,
			unsigned int, flags)
	{
		struct file *file;
		int fd, error;
		char *name;

	--
		fd = get_unused_fd_flags((flags & MFD_CLOEXEC) ? O_CLOEXEC : 0);
		if (fd < 0) {
			error = fd;
			goto err_name;
		}
	--
	}

	$ grepc -tfld memfd_secret . | grep -A3 -e '^[{}.]' -e CLOEXEC;
	./mm/secretmem.c:SYSCALL_DEFINE1(memfd_secret, unsigned int, flags)
	{
		struct file *file;
		int fd, err;

	--
		BUILD_BUG_ON(SECRETMEM_FLAGS_MASK & O_CLOEXEC);

		if (!secretmem_enable || !can_set_direct_map())
			return -ENOSYS;
	--
		if (flags & ~(SECRETMEM_FLAGS_MASK | O_CLOEXEC))
			return -EINVAL;
		if (atomic_read(&secretmem_users) < 0)
			return -ENFILE;
	--
		fd = get_unused_fd_flags(flags & O_CLOEXEC);
		if (fd < 0)
			return fd;

	--
	}

Let's see who added memfd_create(2):

	alx@devuan:~/src/linux/linux/master$ git blame -- ./mm/memfd.c | grep _CLOEXEC
	105ff5339f498 (Jeff Xu                 2022-12-15 00:12:03 +0000 306) #define MFD_ALL_FLAGS (MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_HUGETLB | MFD_NOEXEC_SEAL | MFD_EXEC)
	f5dbcd90dacd3 (Isaac J. Manjarres      2025-01-10 08:58:59 -0800 475) 	fd = get_unused_fd_flags((flags & MFD_CLOEXEC) ? O_CLOEXEC : 0);
	alx@devuan:~/src/linux/linux/master$ git show f5dbcd90dacd3 | grep -e _CLOEXEC -e ^diff | grep -B1 -v ^d
	diff --git a/mm/memfd.c b/mm/memfd.c
	-	fd = get_unused_fd_flags((flags & MFD_CLOEXEC) ? O_CLOEXEC : 0);
	+	fd = get_unused_fd_flags((flags & MFD_CLOEXEC) ? O_CLOEXEC : 0);
	alx@devuan:~/src/linux/linux/master$ git blame f5dbcd90dacd3^ -- mm/memfd.c | grep _CLOEXEC
	105ff5339f498 (Jeff Xu                 2022-12-15 00:12:03 +0000 305) #define MFD_ALL_FLAGS (MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_HUGETLB | MFD_NOEXEC_SEAL | MFD_EXEC)
	5d752600a8c37 (Mike Kravetz            2018-06-07 17:06:01 -0700 423) 	fd = get_unused_fd_flags((flags & MFD_CLOEXEC) ? O_CLOEXEC : 0);
	alx@devuan:~/src/linux/linux/master$ git show 5d752600a8c37 | grep -e _CLOEXEC -e ^diff | grep -B1 -v ^d
	diff --git a/mm/memfd.c b/mm/memfd.c
	+#define MFD_ALL_FLAGS (MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_HUGETLB)
	+	fd = get_unused_fd_flags((flags & MFD_CLOEXEC) ? O_CLOEXEC : 0);
	diff --git a/mm/shmem.c b/mm/shmem.c
	-#define MFD_ALL_FLAGS (MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_HUGETLB)
	-	fd = get_unused_fd_flags((flags & MFD_CLOEXEC) ? O_CLOEXEC : 0);
	alx@devuan:~/src/linux/linux/master$ git blame 5d752600a8c37^ -- mm/shmem.c | grep _CLOEXEC
	749df87bd7bee (Mike Kravetz            2017-09-06 16:24:16 -0700 3684) #define MFD_ALL_FLAGS (MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_HUGETLB)
	9183df25fe7b1 (David Rheinsberg        2014-08-08 14:25:29 -0700 3729) 	fd = get_unused_fd_flags((flags & MFD_CLOEXEC) ? O_CLOEXEC : 0);
	alx@devuan:~/src/linux/linux/master$ git show 9183df25fe7b1 | grep -e _CLOEXEC -e ^diff | grep -B1 -v ^d
	diff --git a/include/uapi/linux/memfd.h b/include/uapi/linux/memfd.h
	+#define MFD_CLOEXEC		0x0001U
	--
	diff --git a/mm/shmem.c b/mm/shmem.c
	+#define MFD_ALL_FLAGS (MFD_CLOEXEC | MFD_ALLOW_SEALING)
	+	fd = get_unused_fd_flags((flags & MFD_CLOEXEC) ? O_CLOEXEC : 0);

	alx@devuan:~/src/linux/linux/master$ git show 9183df25fe7b1 | head -n5
	commit 9183df25fe7b194563db3fec6dc3202a5855839c
	Author: David Rheinsberg <david@readahead.eu>
	Date:   Fri Aug 8 14:25:29 2014 -0700

	    shm: add memfd_create() syscall

	alx@devuan:~/src/linux/linux/master$ git log -1 9183df25fe7b1 | grep @
	Author: David Rheinsberg <david@readahead.eu>
	    Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
	    Acked-by: Hugh Dickins <hughd@google.com>
	    Cc: Michael Kerrisk <mtk.manpages@gmail.com>
	    Cc: Ryan Lortie <desrt@desrt.ca>
	    Cc: Lennart Poettering <lennart@poettering.net>
	    Cc: Daniel Mack <zonque@gmail.com>
	    Cc: Andy Lutomirski <luto@amacapital.net>
	    Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
	    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

And memfd_secret(2):

	alx@devuan:~/src/linux/linux/master$ git blame -- ./mm/secretmem.c | grep _CLOEXEC
	1507f51255c9f (Mike Rapoport           2021-07-07 18:08:03 -0700 238) 	BUILD_BUG_ON(SECRETMEM_FLAGS_MASK & O_CLOEXEC);
	1507f51255c9f (Mike Rapoport           2021-07-07 18:08:03 -0700 243) 	if (flags & ~(SECRETMEM_FLAGS_MASK | O_CLOEXEC))
	1507f51255c9f (Mike Rapoport           2021-07-07 18:08:03 -0700 248) 	fd = get_unused_fd_flags(flags & O_CLOEXEC);
	alx@devuan:~/src/linux/linux/master$ git show 1507f51255c9f | grep -e _CLOEXEC -e ^diff | grep -B1 -v ^d
	diff --git a/mm/secretmem.c b/mm/secretmem.c
	+	BUILD_BUG_ON(SECRETMEM_FLAGS_MASK & O_CLOEXEC);
	+	if (flags & ~(SECRETMEM_FLAGS_MASK | O_CLOEXEC))
	+	fd = get_unused_fd_flags(flags & O_CLOEXEC);
	alx@devuan:~/src/linux/linux/master$ git show 1507f51255c9f | head -n5
	commit 1507f51255c9ff07d75909a84e7c0d7f3c4b2f49
	Author: Mike Rapoport <rppt@kernel.org>
	Date:   Wed Jul 7 18:08:03 2021 -0700

	    mm: introduce memfd_secret system call to create "secret" memory areas
	alx@devuan:~/src/linux/linux/master$ git log -1 1507f51255c9f | grep @
	Author: Mike Rapoport <rppt@kernel.org>
	    [1] https://lore.kernel.org/linux-mm/213b4567-46ce-f116-9cdf-bbd0c884eb3c@linux.intel.com/
	    [akpm@linux-foundation.org: suppress Kconfig whine]
	    Link: https://lkml.kernel.org/r/20210518072034.31572-5-rppt@kernel.org
	    Signed-off-by: Mike Rapoport <rppt@linux.ibm.com>
	    Acked-by: Hagen Paul Pfeifer <hagen@jauu.net>
	    Acked-by: James Bottomley <James.Bottomley@HansenPartnership.com>
	    Cc: Alexander Viro <viro@zeniv.linux.org.uk>
	    Cc: Andy Lutomirski <luto@kernel.org>
	    Cc: Arnd Bergmann <arnd@arndb.de>
	    Cc: Borislav Petkov <bp@alien8.de>
	    Cc: Catalin Marinas <catalin.marinas@arm.com>
	    Cc: Christopher Lameter <cl@linux.com>
	    Cc: Dan Williams <dan.j.williams@intel.com>
	    Cc: Dave Hansen <dave.hansen@linux.intel.com>
	    Cc: Elena Reshetova <elena.reshetova@intel.com>
	    Cc: "H. Peter Anvin" <hpa@zytor.com>
	    Cc: Ingo Molnar <mingo@redhat.com>
	    Cc: James Bottomley <jejb@linux.ibm.com>
	    Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
	    Cc: Matthew Wilcox <willy@infradead.org>
	    Cc: Mark Rutland <mark.rutland@arm.com>
	    Cc: Michael Kerrisk <mtk.manpages@gmail.com>
	    Cc: Palmer Dabbelt <palmer@dabbelt.com>
	    Cc: Palmer Dabbelt <palmerdabbelt@google.com>
	    Cc: Paul Walmsley <paul.walmsley@sifive.com>
	    Cc: Peter Zijlstra <peterz@infradead.org>
	    Cc: Rick Edgecombe <rick.p.edgecombe@intel.com>
	    Cc: Roman Gushchin <guro@fb.com>
	    Cc: Shakeel Butt <shakeelb@google.com>
	    Cc: Shuah Khan <shuah@kernel.org>
	    Cc: Thomas Gleixner <tglx@linutronix.de>
	    Cc: Tycho Andersen <tycho@tycho.ws>
	    Cc: Will Deacon <will@kernel.org>
	    Cc: David Hildenbrand <david@redhat.com>
	    Cc: kernel test robot <lkp@intel.com>
	    Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
	    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

I've added to CC everyone who had something different than Cc, and
everyone who had Cc in both.

Now about the situation: it seems there is only one user of CLOEXEC
with memfd_secret(2) in Debian: systemtap.
<https://codesearch.debian.net/search?q=memfd_secret.*CLOEXEC&literal=0>

Do we want to fix the bug, or do we want to document it?  This is for
kernel people to respond.

Also, was O_CLOEXEC used on purpose, or was it by accident?  I expect
that either MFD_CLOEXEC should have been used, by imitating
memfd_create(2), or a new MFDS_CLOEXEC could have been invented, but
O_CLOEXEC doesn't make much sense, IMO.


Have a lovely day!
Alex

> 
> Signed-off-by: Zhengyi Fu <i@fuzy.me>
> ---
>  man/man2/memfd_secret.2 | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/man/man2/memfd_secret.2 b/man/man2/memfd_secret.2
> index 5ba7813c1..c6abd2f5f 100644
> --- a/man/man2/memfd_secret.2
> +++ b/man/man2/memfd_secret.2
> @@ -51,7 +51,7 @@ The following values may be bitwise ORed in
>  to control the behavior of
>  .BR memfd_secret ():
>  .TP
> -.B FD_CLOEXEC
> +.B O_CLOEXEC
>  Set the close-on-exec flag on the new file descriptor,
>  which causes the region to be removed from the process on
>  .BR execve (2).
> 

-- 
<https://www.alejandro-colomar.es/>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply

* Re: [PATCH 07/10] fs: introduce FALLOC_FL_WRITE_ZEROES to fallocate
From: Zhang Yi @ 2025-06-12 11:37 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: linux-fsdevel, linux-ext4, linux-block, dm-devel, linux-nvme,
	linux-scsi, linux-xfs, linux-kernel, hch, tytso, john.g.garry,
	bmarzins, chaitanyak, shinichiro.kawasaki, brauner,
	martin.petersen, yi.zhang, chengzhihao1, yukuai3, yangerkun,
	linux-api
In-Reply-To: <20250611150555.GB6134@frogsfrogsfrogs>

On 2025/6/11 23:05, Darrick J. Wong wrote:
> [cc linux-api about a fallocate uapi change]
> 
> On Wed, Jun 04, 2025 at 10:08:47AM +0800, Zhang Yi wrote:
>> From: Zhang Yi <yi.zhang@huawei.com>
>>
>> With the development of flash-based storage devices, we can quickly
>> write zeros to SSDs using the WRITE_ZERO command if the devices do not
>> actually write physical zeroes to the media. Therefore, we can use this
>> command to quickly preallocate a real all-zero file with written
>> extents. This approach should be beneficial for subsequent pure
>> overwriting within this file, as it can save on block allocation and,
>> consequently, significant metadata changes, which should greatly improve
>> overwrite performance on certain filesystems.
>>
>> Therefore, introduce a new operation FALLOC_FL_WRITE_ZEROES to
>> fallocate. This flag is used to convert a specified range of a file to
>> zeros by issuing a zeroing operation. Blocks should be allocated for the
>> regions that span holes in the file, and the entire range is converted
>> to written extents. If the underlying device supports the actual offload
>> write zeroes command, the process of zeroing out operation can be
>> accelerated. If it does not, we currently don't prevent the file system
>> from writing actual zeros to the device. This provides users with a new
>> method to quickly generate a zeroed file, users no longer need to write
>> zero data to create a file with written extents.
>>
>> Users can determine whether a disk supports the unmap write zeroes
>> operation through querying this sysfs interface:
>>
>>     /sys/block/<disk>/queue/write_zeroes_unmap
>>
>> Finally, this flag cannot be specified in conjunction with the
>> FALLOC_FL_KEEP_SIZE since allocating written extents beyond file EOF is
>> not permitted. In addition, filesystems that always require out-of-place
>> writes should not support this flag since they still need to allocated
>> new blocks during subsequent overwrites.
>>
>> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
>> Reviewed-by: Christoph Hellwig <hch@lst.de>
>> ---
>>  fs/open.c                   |  1 +
>>  include/linux/falloc.h      |  3 ++-
>>  include/uapi/linux/falloc.h | 18 ++++++++++++++++++
>>  3 files changed, 21 insertions(+), 1 deletion(-)
>>
[...]
>> diff --git a/include/uapi/linux/falloc.h b/include/uapi/linux/falloc.h
>> index 5810371ed72b..265aae7ff8c1 100644
>> --- a/include/uapi/linux/falloc.h
>> +++ b/include/uapi/linux/falloc.h
>> @@ -78,4 +78,22 @@
>>   */
>>  #define FALLOC_FL_UNSHARE_RANGE		0x40
>>  
>> +/*
>> + * FALLOC_FL_WRITE_ZEROES is used to convert a specified range of a file to
>> + * zeros by issuing a zeroing operation. Blocks should be allocated for the
>> + * regions that span holes in the file, and the entire range is converted to
>> + * written extents.
> 
> I think you could simplify this a bit by talking only about the end
> state after a successful call:
> 
> "FALLOC_FL_WRITE_ZEROES zeroes a specified file range in such a way that
> subsequent writes to that range do not require further changes to file
> mapping metadata."
> 
> Note that we don't say how the filesystem gets to this goal.  Presumably
> the first implementations will send a zeroing operation to the block
> device during allocation and the fs will create written mappings, but
> there are other ways to get there -- a filesystem could maintain a pool
> of pre-zeroed space and hand those out; or it could zero space on
> freeing and mounting such that all new mappings can be created as
> written even without the block device zeroing operation.
> 
> Or you could be running on some carefully engineered system where you
> know the storage will always be zeroed at allocation time due to some
> other aspect of the system design, e.g. a single-use throwaway cloud vm
> where you allocate to the end of the disk and reboot the node.

Indeed, it makes sense to me. It appears to be more generic and obscures
the methods by which different file systems may achieve this goal. Thank
you for the suggestion.

Best regards,
Yi.


^ permalink raw reply

* [RFC 00/19] Kernel API Specification Framework
From: Sasha Levin @ 2025-06-14 13:48 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-api, workflows, tools, Sasha Levin

This patch series introduces a framework for formally specifying kernel
APIs, addressing the long-standing challenge of maintaining stable
interfaces between the kernel and user-space programs. As outlined in
previous discussions about kernel ABI stability, the lack of
machine-readable API specifications has led to inadvertent breakages and
inconsistent validation across system calls and IOCTLs.

The framework provides three key components: declarative macros for
specifying system call and IOCTL interfaces directly in the kernel
source, automated extraction tools for generating machine-readable
specifications, and a runtime validation infrastructure accessible
through debugfs. By embedding specifications alongside implementation
code, we ensure they remain synchronized and enable automated detection
of API/ABI changes that could break user-space applications.

This implementation demonstrates the approach with specifications for
core system calls (epoll, exec, mlock families) and complex IOCTL
interfaces (binder, fwctl).  The specifications capture parameter types,
validation rules, return values, and error conditions in a structured
format that enables both documentation generation and runtime
verification. Future work will expand coverage to additional subsystems
and integrate with existing testing infrastructure to provide
API compatibility guarantees.

To complement the framework, we introduce the 'kapi' tool - a
utility for extracting and analyzing kernel API specifications from
multiple sources. The tool can extract specifications from kernel source
code (parsing KAPI macros), compiled vmlinux binaries (reading the
.kapi_specs ELF section), or from a running kernel via debugfs. It
supports multiple output formats (plain text, JSON, RST) to facilitate
integration with documentation systems and automated testing workflows.
This tool enables developers to easily inspect API specifications,
verify changes across kernel versions, and generate documentation
without requiring kernel rebuilds.

Sasha Levin (19):
  kernel/api: introduce kernel API specification framework
  eventpoll: add API specification for epoll_create1
  eventpoll: add API specification for epoll_create
  eventpoll: add API specification for epoll_ctl
  eventpoll: add API specification for epoll_wait
  eventpoll: add API specification for epoll_pwait
  eventpoll: add API specification for epoll_pwait2
  exec: add API specification for execve
  exec: add API specification for execveat
  mm/mlock: add API specification for mlock
  mm/mlock: add API specification for mlock2
  mm/mlock: add API specification for mlockall
  mm/mlock: add API specification for munlock
  mm/mlock: add API specification for munlockall
  kernel/api: add debugfs interface for kernel API specifications
  kernel/api: add IOCTL specification infrastructure
  fwctl: add detailed IOCTL API specifications
  binder: add detailed IOCTL API specifications
  tools/kapi: Add kernel API specification extraction tool

 Documentation/admin-guide/kernel-api-spec.rst |  699 +++++++++
 MAINTAINERS                                   |    9 +
 arch/um/kernel/dyn.lds.S                      |    3 +
 arch/um/kernel/uml.lds.S                      |    3 +
 arch/x86/kernel/vmlinux.lds.S                 |    3 +
 drivers/android/binder.c                      |  758 ++++++++++
 drivers/fwctl/main.c                          |  295 +++-
 fs/eventpoll.c                                | 1056 ++++++++++++++
 fs/exec.c                                     |  463 ++++++
 include/asm-generic/vmlinux.lds.h             |   20 +
 include/linux/ioctl_api_spec.h                |  540 +++++++
 include/linux/kernel_api_spec.h               |  942 ++++++++++++
 include/linux/syscall_api_spec.h              |  341 +++++
 include/linux/syscalls.h                      |    1 +
 init/Kconfig                                  |    2 +
 kernel/Makefile                               |    1 +
 kernel/api/Kconfig                            |   55 +
 kernel/api/Makefile                           |   13 +
 kernel/api/ioctl_validation.c                 |  360 +++++
 kernel/api/kapi_debugfs.c                     |  340 +++++
 kernel/api/kernel_api_spec.c                  | 1257 +++++++++++++++++
 mm/mlock.c                                    |  646 +++++++++
 tools/kapi/.gitignore                         |    4 +
 tools/kapi/Cargo.toml                         |   19 +
 tools/kapi/src/extractor/debugfs.rs           |  204 +++
 tools/kapi/src/extractor/mod.rs               |   95 ++
 tools/kapi/src/extractor/source_parser.rs     |  488 +++++++
 .../src/extractor/vmlinux/binary_utils.rs     |  130 ++
 tools/kapi/src/extractor/vmlinux/mod.rs       |  372 +++++
 tools/kapi/src/formatter/json.rs              |  170 +++
 tools/kapi/src/formatter/mod.rs               |   68 +
 tools/kapi/src/formatter/plain.rs             |   99 ++
 tools/kapi/src/formatter/rst.rs               |  144 ++
 tools/kapi/src/main.rs                        |  121 ++
 34 files changed, 9719 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/admin-guide/kernel-api-spec.rst
 create mode 100644 include/linux/ioctl_api_spec.h
 create mode 100644 include/linux/kernel_api_spec.h
 create mode 100644 include/linux/syscall_api_spec.h
 create mode 100644 kernel/api/Kconfig
 create mode 100644 kernel/api/Makefile
 create mode 100644 kernel/api/ioctl_validation.c
 create mode 100644 kernel/api/kapi_debugfs.c
 create mode 100644 kernel/api/kernel_api_spec.c
 create mode 100644 tools/kapi/.gitignore
 create mode 100644 tools/kapi/Cargo.toml
 create mode 100644 tools/kapi/src/extractor/debugfs.rs
 create mode 100644 tools/kapi/src/extractor/mod.rs
 create mode 100644 tools/kapi/src/extractor/source_parser.rs
 create mode 100644 tools/kapi/src/extractor/vmlinux/binary_utils.rs
 create mode 100644 tools/kapi/src/extractor/vmlinux/mod.rs
 create mode 100644 tools/kapi/src/formatter/json.rs
 create mode 100644 tools/kapi/src/formatter/mod.rs
 create mode 100644 tools/kapi/src/formatter/plain.rs
 create mode 100644 tools/kapi/src/formatter/rst.rs
 create mode 100644 tools/kapi/src/main.rs

-- 
2.39.5


^ permalink raw reply

* [RFC 01/19] kernel/api: introduce kernel API specification framework
From: Sasha Levin @ 2025-06-14 13:48 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-api, workflows, tools, Sasha Levin
In-Reply-To: <20250614134858.790460-1-sashal@kernel.org>

Add a comprehensive framework for formally documenting kernel APIs with
inline specifications. This framework provides:

- Structured API documentation with parameter specifications, return
  values, error conditions, and execution context requirements
- Runtime validation capabilities for debugging (CONFIG_KAPI_RUNTIME_CHECKS)
- Export of specifications via debugfs for tooling integration
- Support for both internal kernel APIs and system calls

The framework stores specifications in a dedicated ELF section and
provides infrastructure for:
- Compile-time validation of specifications
- Runtime querying of API documentation
- Machine-readable export formats
- Integration with existing SYSCALL_DEFINE macros

This commit introduces the core infrastructure without modifying any
existing APIs. Subsequent patches will add specifications to individual
subsystems.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 Documentation/admin-guide/kernel-api-spec.rst |  507 +++++++
 MAINTAINERS                                   |    9 +
 arch/um/kernel/dyn.lds.S                      |    3 +
 arch/um/kernel/uml.lds.S                      |    3 +
 arch/x86/kernel/vmlinux.lds.S                 |    3 +
 include/asm-generic/vmlinux.lds.h             |   20 +
 include/linux/kernel_api_spec.h               |  942 +++++++++++++
 include/linux/syscall_api_spec.h              |  341 +++++
 include/linux/syscalls.h                      |    1 +
 init/Kconfig                                  |    2 +
 kernel/Makefile                               |    1 +
 kernel/api/Kconfig                            |   35 +
 kernel/api/Makefile                           |    7 +
 kernel/api/kernel_api_spec.c                  | 1169 +++++++++++++++++
 14 files changed, 3043 insertions(+)
 create mode 100644 Documentation/admin-guide/kernel-api-spec.rst
 create mode 100644 include/linux/kernel_api_spec.h
 create mode 100644 include/linux/syscall_api_spec.h
 create mode 100644 kernel/api/Kconfig
 create mode 100644 kernel/api/Makefile
 create mode 100644 kernel/api/kernel_api_spec.c

diff --git a/Documentation/admin-guide/kernel-api-spec.rst b/Documentation/admin-guide/kernel-api-spec.rst
new file mode 100644
index 0000000000000..3a63f6711e27b
--- /dev/null
+++ b/Documentation/admin-guide/kernel-api-spec.rst
@@ -0,0 +1,507 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================================
+Kernel API Specification Framework
+======================================
+
+:Author: Sasha Levin <sashal@kernel.org>
+:Date: June 2025
+
+.. contents:: Table of Contents
+   :depth: 3
+   :local:
+
+Introduction
+============
+
+The Kernel API Specification Framework (KAPI) provides a comprehensive system for
+formally documenting, validating, and introspecting kernel APIs. This framework
+addresses the long-standing challenge of maintaining accurate, machine-readable
+documentation for the thousands of internal kernel APIs and system calls.
+
+Purpose and Goals
+-----------------
+
+The framework aims to:
+
+1. **Improve API Documentation**: Provide structured, inline documentation that
+   lives alongside the code and is maintained as part of the development process.
+
+2. **Enable Runtime Validation**: Optionally validate API usage at runtime to catch
+   common programming errors during development and testing.
+
+3. **Support Tooling**: Export API specifications in machine-readable formats for
+   use by static analyzers, documentation generators, and development tools.
+
+4. **Enhance Debugging**: Provide detailed API information at runtime through debugfs
+   for debugging and introspection.
+
+5. **Formalize Contracts**: Explicitly document API contracts including parameter
+   constraints, execution contexts, locking requirements, and side effects.
+
+Architecture Overview
+=====================
+
+Components
+----------
+
+The framework consists of several key components:
+
+1. **Core Framework** (``kernel/api/kernel_api_spec.c``)
+
+   - API specification registration and storage
+   - Runtime validation engine
+   - Specification lookup and querying
+
+2. **DebugFS Interface** (``kernel/api/kapi_debugfs.c``)
+
+   - Runtime introspection via ``/sys/kernel/debug/kapi/``
+   - JSON and XML export formats
+   - Per-API detailed information
+
+3. **IOCTL Support** (``kernel/api/ioctl_validation.c``)
+
+   - Extended framework for IOCTL specifications
+   - Automatic validation wrappers
+   - Structure field validation
+
+4. **Specification Macros** (``include/linux/kernel_api_spec.h``)
+
+   - Declarative macros for API documentation
+   - Type-safe parameter specifications
+   - Context and constraint definitions
+
+Data Model
+----------
+
+The framework uses a hierarchical data model::
+
+    kernel_api_spec
+    ├── Basic Information
+    │   ├── name (API function name)
+    │   ├── version (specification version)
+    │   ├── description (human-readable description)
+    │   └── kernel_version (when API was introduced)
+    │
+    ├── Parameters (up to 16)
+    │   └── kapi_param_spec
+    │       ├── name
+    │       ├── type (int, pointer, string, etc.)
+    │       ├── direction (in, out, inout)
+    │       ├── constraints (range, mask, enum values)
+    │       └── validation rules
+    │
+    ├── Return Value
+    │   └── kapi_return_spec
+    │       ├── type
+    │       ├── success conditions
+    │       └── validation rules
+    │
+    ├── Error Conditions (up to 32)
+    │   └── kapi_error_spec
+    │       ├── error code
+    │       ├── condition description
+    │       └── recovery advice
+    │
+    ├── Execution Context
+    │   ├── allowed contexts (process, interrupt, etc.)
+    │   ├── locking requirements
+    │   └── preemption/interrupt state
+    │
+    └── Side Effects
+        ├── memory allocation
+        ├── state changes
+        └── signal handling
+
+Usage Guide
+===========
+
+Basic API Specification
+-----------------------
+
+To document a kernel API, use the specification macros in the implementation file:
+
+.. code-block:: c
+
+    #include <linux/kernel_api_spec.h>
+
+    KAPI_DEFINE_SPEC(kmalloc_spec, kmalloc, "3.0")
+    KAPI_DESCRIPTION("Allocate kernel memory")
+    KAPI_PARAM(0, size, KAPI_TYPE_SIZE_T, KAPI_DIR_IN,
+               "Number of bytes to allocate")
+    KAPI_PARAM_RANGE(0, 0, KMALLOC_MAX_SIZE)
+    KAPI_PARAM(1, flags, KAPI_TYPE_FLAGS, KAPI_DIR_IN,
+               "Allocation flags (GFP_*)")
+    KAPI_PARAM_MASK(1, __GFP_BITS_MASK)
+    KAPI_RETURN(KAPI_TYPE_POINTER, "Pointer to allocated memory or NULL")
+    KAPI_ERROR(ENOMEM, "Out of memory")
+    KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SOFTIRQ | KAPI_CTX_HARDIRQ)
+    KAPI_SIDE_EFFECT("Allocates memory from kernel heap")
+    KAPI_LOCK_NOT_REQUIRED("Any lock")
+    KAPI_END_SPEC
+
+    void *kmalloc(size_t size, gfp_t flags)
+    {
+        /* Implementation */
+    }
+
+System Call Specification
+-------------------------
+
+System calls use specialized macros:
+
+.. code-block:: c
+
+    KAPI_DEFINE_SYSCALL_SPEC(open_spec, open, "1.0")
+    KAPI_DESCRIPTION("Open a file")
+    KAPI_PARAM(0, pathname, KAPI_TYPE_USER_STRING, KAPI_DIR_IN,
+               "Path to file")
+    KAPI_PARAM_PATH(0, PATH_MAX)
+    KAPI_PARAM(1, flags, KAPI_TYPE_FLAGS, KAPI_DIR_IN,
+               "Open flags (O_*)")
+    KAPI_PARAM(2, mode, KAPI_TYPE_MODE_T, KAPI_DIR_IN,
+               "File permissions (if creating)")
+    KAPI_RETURN(KAPI_TYPE_INT, "File descriptor or -1")
+    KAPI_ERROR(EACCES, "Permission denied")
+    KAPI_ERROR(ENOENT, "File does not exist")
+    KAPI_ERROR(EMFILE, "Too many open files")
+    KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
+    KAPI_SIGNAL(EINTR, "Open can be interrupted by signal")
+    KAPI_END_SYSCALL_SPEC
+
+IOCTL Specification
+-------------------
+
+IOCTLs have extended support for structure validation:
+
+.. code-block:: c
+
+    KAPI_DEFINE_IOCTL_SPEC(vidioc_querycap_spec, VIDIOC_QUERYCAP,
+                           "VIDIOC_QUERYCAP",
+                           sizeof(struct v4l2_capability),
+                           sizeof(struct v4l2_capability),
+                           "video_fops")
+    KAPI_DESCRIPTION("Query device capabilities")
+    KAPI_IOCTL_FIELD(driver, KAPI_TYPE_CHAR_ARRAY, KAPI_DIR_OUT,
+                     "Driver name", 16)
+    KAPI_IOCTL_FIELD(card, KAPI_TYPE_CHAR_ARRAY, KAPI_DIR_OUT,
+                     "Device name", 32)
+    KAPI_IOCTL_FIELD(version, KAPI_TYPE_U32, KAPI_DIR_OUT,
+                     "Driver version")
+    KAPI_IOCTL_FIELD(capabilities, KAPI_TYPE_FLAGS, KAPI_DIR_OUT,
+                     "Device capabilities")
+    KAPI_END_IOCTL_SPEC
+
+Runtime Validation
+==================
+
+Enabling Validation
+-------------------
+
+Runtime validation is controlled by kernel configuration:
+
+1. Enable ``CONFIG_KAPI_SPEC`` to build the framework
+2. Enable ``CONFIG_KAPI_RUNTIME_CHECKS`` for runtime validation
+3. Optionally enable ``CONFIG_KAPI_SPEC_DEBUGFS`` for debugfs interface
+
+Validation Modes
+----------------
+
+The framework supports several validation modes:
+
+.. code-block:: c
+
+    /* Enable validation for specific API */
+    kapi_enable_validation("kmalloc");
+
+    /* Enable validation for all APIs */
+    kapi_enable_all_validation();
+
+    /* Set validation level */
+    kapi_set_validation_level(KAPI_VALIDATE_FULL);
+
+Validation Levels:
+
+- ``KAPI_VALIDATE_NONE``: No validation
+- ``KAPI_VALIDATE_BASIC``: Type and NULL checks only
+- ``KAPI_VALIDATE_NORMAL``: Basic + range and constraint checks
+- ``KAPI_VALIDATE_FULL``: All checks including custom validators
+
+Custom Validators
+-----------------
+
+APIs can register custom validation functions:
+
+.. code-block:: c
+
+    static bool validate_buffer_size(const struct kapi_param_spec *spec,
+                                     const void *value, void *context)
+    {
+        size_t size = *(size_t *)value;
+        struct my_context *ctx = context;
+
+        return size > 0 && size <= ctx->max_buffer_size;
+    }
+
+    KAPI_PARAM_CUSTOM_VALIDATOR(0, validate_buffer_size)
+
+DebugFS Interface
+=================
+
+The debugfs interface provides runtime access to API specifications:
+
+Directory Structure
+-------------------
+
+::
+
+    /sys/kernel/debug/kapi/
+    ├── apis/                    # All registered APIs
+    │   ├── kmalloc/
+    │   │   ├── specification   # Human-readable spec
+    │   │   ├── json           # JSON format
+    │   │   └── xml            # XML format
+    │   └── open/
+    │       └── ...
+    ├── summary                  # Overview of all APIs
+    ├── validation/              # Validation controls
+    │   ├── enabled             # Global enable/disable
+    │   ├── level               # Validation level
+    │   └── stats               # Validation statistics
+    └── export/                  # Bulk export options
+        ├── all.json            # All specs in JSON
+        └── all.xml             # All specs in XML
+
+Usage Examples
+--------------
+
+Query specific API::
+
+    $ cat /sys/kernel/debug/kapi/apis/kmalloc/specification
+    API: kmalloc
+    Version: 3.0
+    Description: Allocate kernel memory
+
+    Parameters:
+      [0] size (size_t, in): Number of bytes to allocate
+          Range: 0 - 4194304
+      [1] flags (flags, in): Allocation flags (GFP_*)
+          Mask: 0x1ffffff
+
+    Returns: pointer - Pointer to allocated memory or NULL
+
+    Errors:
+      ENOMEM: Out of memory
+
+    Context: process, softirq, hardirq
+
+    Side Effects:
+      - Allocates memory from kernel heap
+
+Export all specifications::
+
+    $ cat /sys/kernel/debug/kapi/export/all.json > kernel-apis.json
+
+Enable validation for specific API::
+
+    $ echo 1 > /sys/kernel/debug/kapi/apis/kmalloc/validate
+
+Performance Considerations
+==========================
+
+Memory Overhead
+---------------
+
+Each API specification consumes approximately 2-4KB of memory. With thousands
+of kernel APIs, this can add up to several megabytes. Consider:
+
+1. Building with ``CONFIG_KAPI_SPEC=n`` for production kernels
+2. Using ``__init`` annotations for APIs only used during boot
+3. Implementing lazy loading for rarely used specifications
+
+Runtime Overhead
+----------------
+
+When ``CONFIG_KAPI_RUNTIME_CHECKS`` is enabled:
+
+- Each validated API call adds 50-200ns overhead
+- Complex validations (custom validators) may add more
+- Use validation only in development/testing kernels
+
+Optimization Strategies
+-----------------------
+
+1. **Compile-time optimization**: When validation is disabled, all
+   validation code is optimized away by the compiler.
+
+2. **Selective validation**: Enable validation only for specific APIs
+   or subsystems under test.
+
+3. **Caching**: The framework caches validation results for repeated
+   calls with identical parameters.
+
+Documentation Generation
+------------------------
+
+The framework exports specifications via debugfs that can be used
+to generate documentation. Tools for automatic documentation generation
+from specifications are planned for future development.
+
+IDE Integration
+---------------
+
+Modern IDEs can use the JSON export for:
+
+- Parameter hints
+- Type checking
+- Context validation
+- Error code documentation
+
+Testing Framework
+-----------------
+
+The framework includes test helpers::
+
+    #ifdef CONFIG_KAPI_TESTING
+    /* Verify API behaves according to specification */
+    kapi_test_api("kmalloc", test_cases);
+    #endif
+
+Best Practices
+==============
+
+Writing Specifications
+----------------------
+
+1. **Be Comprehensive**: Document all parameters, errors, and side effects
+2. **Keep Updated**: Update specs when API behavior changes
+3. **Use Examples**: Include usage examples in descriptions
+4. **Validate Constraints**: Define realistic constraints for parameters
+5. **Document Context**: Clearly specify allowed execution contexts
+
+Maintenance
+-----------
+
+1. **Version Specifications**: Increment version when API changes
+2. **Deprecation**: Mark deprecated APIs and suggest replacements
+3. **Cross-reference**: Link related APIs in descriptions
+4. **Test Specifications**: Verify specs match implementation
+
+Common Patterns
+---------------
+
+**Optional Parameters**::
+
+    KAPI_PARAM(2, optional_arg, KAPI_TYPE_POINTER, KAPI_DIR_IN,
+               "Optional argument (may be NULL)")
+    KAPI_PARAM_OPTIONAL(2)
+
+**Variable Arguments**::
+
+    KAPI_PARAM(1, fmt, KAPI_TYPE_FORMAT_STRING, KAPI_DIR_IN,
+               "Printf-style format string")
+    KAPI_PARAM_VARIADIC(2, "Format arguments")
+
+**Callback Functions**::
+
+    KAPI_PARAM(1, callback, KAPI_TYPE_FUNCTION_PTR, KAPI_DIR_IN,
+               "Callback function")
+    KAPI_PARAM_CALLBACK(1, "int (*)(void *data)", "data")
+
+Troubleshooting
+===============
+
+Common Issues
+-------------
+
+**Specification Not Found**::
+
+    kernel: KAPI: Specification for 'my_api' not found
+
+    Solution: Ensure KAPI_DEFINE_SPEC is in the same translation unit
+    as the function implementation.
+
+**Validation Failures**::
+
+    kernel: KAPI: Validation failed for kmalloc parameter 'size':
+            value 5242880 exceeds maximum 4194304
+
+    Solution: Check parameter constraints or adjust specification if
+    the constraint is incorrect.
+
+**Build Errors**::
+
+    error: 'KAPI_TYPE_UNKNOWN' undeclared
+
+    Solution: Include <linux/kernel_api_spec.h> and ensure
+    CONFIG_KAPI_SPEC is enabled.
+
+Debug Options
+-------------
+
+Enable verbose debugging::
+
+    echo 8 > /proc/sys/kernel/printk
+    echo 1 > /sys/kernel/debug/kapi/debug/verbose
+
+Future Directions
+=================
+
+Planned Features
+----------------
+
+1. **Automatic Extraction**: Tool to extract specifications from existing
+   kernel-doc comments
+
+2. **Contract Verification**: Static analysis to verify implementation
+   matches specification
+
+3. **Performance Profiling**: Measure actual API performance against
+   documented expectations
+
+4. **Fuzzing Integration**: Use specifications to guide intelligent
+   fuzzing of kernel APIs
+
+5. **Version Compatibility**: Track API changes across kernel versions
+
+Research Areas
+--------------
+
+1. **Formal Verification**: Use specifications for mathematical proofs
+   of correctness
+
+2. **Runtime Monitoring**: Detect specification violations in production
+   with minimal overhead
+
+3. **API Evolution**: Analyze how kernel APIs change over time
+
+4. **Security Applications**: Use specifications for security policy
+   enforcement
+
+Contributing
+============
+
+Submitting Specifications
+-------------------------
+
+1. Add specifications to the same file as the API implementation
+2. Follow existing patterns and naming conventions
+3. Test with CONFIG_KAPI_RUNTIME_CHECKS enabled
+4. Verify debugfs output is correct
+5. Run scripts/checkpatch.pl on your changes
+
+Review Criteria
+---------------
+
+Specifications will be reviewed for:
+
+1. **Completeness**: All parameters and errors documented
+2. **Accuracy**: Specification matches implementation
+3. **Clarity**: Descriptions are clear and helpful
+4. **Consistency**: Follows framework conventions
+5. **Performance**: No unnecessary runtime overhead
+
+Contact
+-------
+
+- Maintainer: Sasha Levin <sashal@kernel.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index a92290fffa163..7a2cb663131bd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13047,6 +13047,15 @@ W:	https://linuxtv.org
 T:	git git://linuxtv.org/media.git
 F:	drivers/media/radio/radio-keene*
 
+KERNEL API SPECIFICATION FRAMEWORK (KAPI)
+M:	Sasha Levin <sashal@kernel.org>
+L:	linux-api@vger.kernel.org
+S:	Maintained
+F:	Documentation/admin-guide/kernel-api-spec.rst
+F:	include/linux/kernel_api_spec.h
+F:	kernel/api/
+F:	scripts/extract-kapi-spec.sh
+
 KERNEL AUTOMOUNTER
 M:	Ian Kent <raven@themaw.net>
 L:	autofs@vger.kernel.org
diff --git a/arch/um/kernel/dyn.lds.S b/arch/um/kernel/dyn.lds.S
index a36b7918a011a..283ab11788d8c 100644
--- a/arch/um/kernel/dyn.lds.S
+++ b/arch/um/kernel/dyn.lds.S
@@ -102,6 +102,9 @@ SECTIONS
   init.data : { INIT_DATA }
   __init_end = .;
 
+  /* Kernel API specifications in dedicated section */
+  KAPI_SPECS_SECTION()
+
   /* Ensure the __preinit_array_start label is properly aligned.  We
      could instead move the label definition inside the section, but
      the linker would then create the section even if it turns out to
diff --git a/arch/um/kernel/uml.lds.S b/arch/um/kernel/uml.lds.S
index a409d4b66114f..e3850d8293436 100644
--- a/arch/um/kernel/uml.lds.S
+++ b/arch/um/kernel/uml.lds.S
@@ -74,6 +74,9 @@ SECTIONS
   init.data : { INIT_DATA }
   __init_end = .;
 
+  /* Kernel API specifications in dedicated section */
+  KAPI_SPECS_SECTION()
+
   .data    :
   {
     INIT_TASK_DATA(KERNEL_STACK_SIZE)
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index 4fa0be732af10..8cc508adc9d51 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -173,6 +173,9 @@ SECTIONS
 	RO_DATA(PAGE_SIZE)
 	X86_ALIGN_RODATA_END
 
+	/* Kernel API specifications in dedicated section */
+	KAPI_SPECS_SECTION()
+
 	/* Data */
 	.data : AT(ADDR(.data) - LOAD_OFFSET) {
 		/* Start of data section */
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index fa5f19b8d53a0..7b47736057e01 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -279,6 +279,26 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG)
 #define TRACE_SYSCALLS()
 #endif
 
+#ifdef CONFIG_KAPI_SPEC
+#define KAPI_SPECS()				\
+	. = ALIGN(8);				\
+	__start_kapi_specs = .;			\
+	KEEP(*(.kapi_specs))			\
+	__stop_kapi_specs = .;
+
+/* For placing KAPI specs in a dedicated section */
+#define KAPI_SPECS_SECTION()			\
+	.kapi_specs : AT(ADDR(.kapi_specs) - LOAD_OFFSET) {	\
+		. = ALIGN(8);			\
+		__start_kapi_specs = .;		\
+		KEEP(*(.kapi_specs))		\
+		__stop_kapi_specs = .;		\
+	}
+#else
+#define KAPI_SPECS()
+#define KAPI_SPECS_SECTION()
+#endif
+
 #ifdef CONFIG_BPF_EVENTS
 #define BPF_RAW_TP() STRUCT_ALIGN();				\
 	BOUNDED_SECTION_BY(__bpf_raw_tp_map, __bpf_raw_tp)
diff --git a/include/linux/kernel_api_spec.h b/include/linux/kernel_api_spec.h
new file mode 100644
index 0000000000000..04df5892bc6d6
--- /dev/null
+++ b/include/linux/kernel_api_spec.h
@@ -0,0 +1,942 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * kernel_api_spec.h - Kernel API Formal Specification Framework
+ *
+ * This framework provides structures and macros to formally specify kernel APIs
+ * in both human and machine-readable formats. It supports comprehensive documentation
+ * of function signatures, parameters, return values, error conditions, and constraints.
+ */
+
+#ifndef _LINUX_KERNEL_API_SPEC_H
+#define _LINUX_KERNEL_API_SPEC_H
+
+#include <linux/types.h>
+#include <linux/stringify.h>
+#include <linux/compiler.h>
+
+#define KAPI_MAX_PARAMS		16
+#define KAPI_MAX_ERRORS		32
+#define KAPI_MAX_CONSTRAINTS	16
+#define KAPI_MAX_SIGNALS	32
+#define KAPI_MAX_NAME_LEN	128
+#define KAPI_MAX_DESC_LEN	512
+
+/**
+ * enum kapi_param_type - Parameter type classification
+ * @KAPI_TYPE_VOID: void type
+ * @KAPI_TYPE_INT: Integer types (int, long, etc.)
+ * @KAPI_TYPE_UINT: Unsigned integer types
+ * @KAPI_TYPE_PTR: Pointer types
+ * @KAPI_TYPE_STRUCT: Structure types
+ * @KAPI_TYPE_UNION: Union types
+ * @KAPI_TYPE_ENUM: Enumeration types
+ * @KAPI_TYPE_FUNC_PTR: Function pointer types
+ * @KAPI_TYPE_ARRAY: Array types
+ * @KAPI_TYPE_FD: File descriptor - validated in process context
+ * @KAPI_TYPE_USER_PTR: User space pointer - validated for access and size
+ * @KAPI_TYPE_PATH: Pathname - validated for access and path limits
+ * @KAPI_TYPE_CUSTOM: Custom/complex types
+ */
+enum kapi_param_type {
+	KAPI_TYPE_VOID = 0,
+	KAPI_TYPE_INT,
+	KAPI_TYPE_UINT,
+	KAPI_TYPE_PTR,
+	KAPI_TYPE_STRUCT,
+	KAPI_TYPE_UNION,
+	KAPI_TYPE_ENUM,
+	KAPI_TYPE_FUNC_PTR,
+	KAPI_TYPE_ARRAY,
+	KAPI_TYPE_FD,		/* File descriptor - validated in process context */
+	KAPI_TYPE_USER_PTR,	/* User space pointer - validated for access and size */
+	KAPI_TYPE_PATH,		/* Pathname - validated for access and path limits */
+	KAPI_TYPE_CUSTOM,
+};
+
+/**
+ * enum kapi_param_flags - Parameter attribute flags
+ * @KAPI_PARAM_IN: Input parameter
+ * @KAPI_PARAM_OUT: Output parameter
+ * @KAPI_PARAM_INOUT: Input/output parameter
+ * @KAPI_PARAM_OPTIONAL: Optional parameter (can be NULL)
+ * @KAPI_PARAM_CONST: Const qualified parameter
+ * @KAPI_PARAM_VOLATILE: Volatile qualified parameter
+ * @KAPI_PARAM_USER: User space pointer
+ * @KAPI_PARAM_DMA: DMA-capable memory required
+ * @KAPI_PARAM_ALIGNED: Alignment requirements
+ */
+enum kapi_param_flags {
+	KAPI_PARAM_IN		= (1 << 0),
+	KAPI_PARAM_OUT		= (1 << 1),
+	KAPI_PARAM_INOUT	= (1 << 2),
+	KAPI_PARAM_OPTIONAL	= (1 << 3),
+	KAPI_PARAM_CONST	= (1 << 4),
+	KAPI_PARAM_VOLATILE	= (1 << 5),
+	KAPI_PARAM_USER		= (1 << 6),
+	KAPI_PARAM_DMA		= (1 << 7),
+	KAPI_PARAM_ALIGNED	= (1 << 8),
+};
+
+/**
+ * enum kapi_context_flags - Function execution context flags
+ * @KAPI_CTX_PROCESS: Can be called from process context
+ * @KAPI_CTX_SOFTIRQ: Can be called from softirq context
+ * @KAPI_CTX_HARDIRQ: Can be called from hardirq context
+ * @KAPI_CTX_NMI: Can be called from NMI context
+ * @KAPI_CTX_ATOMIC: Must be called in atomic context
+ * @KAPI_CTX_SLEEPABLE: May sleep
+ * @KAPI_CTX_PREEMPT_DISABLED: Requires preemption disabled
+ * @KAPI_CTX_IRQ_DISABLED: Requires interrupts disabled
+ */
+enum kapi_context_flags {
+	KAPI_CTX_PROCESS	= (1 << 0),
+	KAPI_CTX_SOFTIRQ	= (1 << 1),
+	KAPI_CTX_HARDIRQ	= (1 << 2),
+	KAPI_CTX_NMI		= (1 << 3),
+	KAPI_CTX_ATOMIC		= (1 << 4),
+	KAPI_CTX_SLEEPABLE	= (1 << 5),
+	KAPI_CTX_PREEMPT_DISABLED = (1 << 6),
+	KAPI_CTX_IRQ_DISABLED	= (1 << 7),
+};
+
+/**
+ * enum kapi_lock_type - Lock types used/required by the function
+ * @KAPI_LOCK_NONE: No locking requirements
+ * @KAPI_LOCK_MUTEX: Mutex lock
+ * @KAPI_LOCK_SPINLOCK: Spinlock
+ * @KAPI_LOCK_RWLOCK: Read-write lock
+ * @KAPI_LOCK_SEQLOCK: Sequence lock
+ * @KAPI_LOCK_RCU: RCU lock
+ * @KAPI_LOCK_SEMAPHORE: Semaphore
+ * @KAPI_LOCK_CUSTOM: Custom locking mechanism
+ */
+enum kapi_lock_type {
+	KAPI_LOCK_NONE = 0,
+	KAPI_LOCK_MUTEX,
+	KAPI_LOCK_SPINLOCK,
+	KAPI_LOCK_RWLOCK,
+	KAPI_LOCK_SEQLOCK,
+	KAPI_LOCK_RCU,
+	KAPI_LOCK_SEMAPHORE,
+	KAPI_LOCK_CUSTOM,
+};
+
+/**
+ * enum kapi_constraint_type - Types of parameter constraints
+ * @KAPI_CONSTRAINT_NONE: No constraint
+ * @KAPI_CONSTRAINT_RANGE: Numeric range constraint
+ * @KAPI_CONSTRAINT_MASK: Bitmask constraint
+ * @KAPI_CONSTRAINT_ENUM: Enumerated values constraint
+ * @KAPI_CONSTRAINT_CUSTOM: Custom validation function
+ */
+enum kapi_constraint_type {
+	KAPI_CONSTRAINT_NONE = 0,
+	KAPI_CONSTRAINT_RANGE,
+	KAPI_CONSTRAINT_MASK,
+	KAPI_CONSTRAINT_ENUM,
+	KAPI_CONSTRAINT_CUSTOM,
+};
+
+/**
+ * struct kapi_param_spec - Parameter specification
+ * @name: Parameter name
+ * @type_name: Type name as string
+ * @type: Parameter type classification
+ * @flags: Parameter attribute flags
+ * @size: Size in bytes (for arrays/buffers)
+ * @alignment: Required alignment
+ * @min_value: Minimum valid value (for numeric types)
+ * @max_value: Maximum valid value (for numeric types)
+ * @valid_mask: Valid bits mask (for flag parameters)
+ * @enum_values: Array of valid enumerated values
+ * @enum_count: Number of valid enumerated values
+ * @constraint_type: Type of constraint applied
+ * @validate: Custom validation function
+ * @description: Human-readable description
+ * @constraints: Additional constraints description
+ * @size_param_idx: Index of parameter that determines size (-1 if fixed size)
+ * @size_multiplier: Multiplier for size calculation (e.g., sizeof(struct))
+ */
+struct kapi_param_spec {
+	char name[KAPI_MAX_NAME_LEN];
+	char type_name[KAPI_MAX_NAME_LEN];
+	enum kapi_param_type type;
+	u32 flags;
+	size_t size;
+	size_t alignment;
+	s64 min_value;
+	s64 max_value;
+	u64 valid_mask;
+	const s64 *enum_values;
+	u32 enum_count;
+	enum kapi_constraint_type constraint_type;
+	bool (*validate)(s64 value);
+	char description[KAPI_MAX_DESC_LEN];
+	char constraints[KAPI_MAX_DESC_LEN];
+	int size_param_idx;	/* Index of param that determines size, -1 if N/A */
+	size_t size_multiplier;	/* Size per unit (e.g., sizeof(struct epoll_event)) */
+} __attribute__((packed));
+
+/**
+ * struct kapi_error_spec - Error condition specification
+ * @error_code: Error code value
+ * @name: Error code name (e.g., "EINVAL")
+ * @condition: Condition that triggers this error
+ * @description: Detailed error description
+ */
+struct kapi_error_spec {
+	int error_code;
+	char name[KAPI_MAX_NAME_LEN];
+	char condition[KAPI_MAX_DESC_LEN];
+	char description[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+/**
+ * enum kapi_return_check_type - Return value check types
+ * @KAPI_RETURN_EXACT: Success is an exact value
+ * @KAPI_RETURN_RANGE: Success is within a range
+ * @KAPI_RETURN_ERROR_CHECK: Success is when NOT in error list
+ * @KAPI_RETURN_FD: Return value is a file descriptor (>= 0 is success)
+ * @KAPI_RETURN_CUSTOM: Custom validation function
+ */
+enum kapi_return_check_type {
+	KAPI_RETURN_EXACT,
+	KAPI_RETURN_RANGE,
+	KAPI_RETURN_ERROR_CHECK,
+	KAPI_RETURN_FD,
+	KAPI_RETURN_CUSTOM,
+};
+
+/**
+ * struct kapi_return_spec - Return value specification
+ * @type_name: Return type name
+ * @type: Return type classification
+ * @check_type: Type of success check to perform
+ * @success_value: Exact value indicating success (for EXACT)
+ * @success_min: Minimum success value (for RANGE)
+ * @success_max: Maximum success value (for RANGE)
+ * @error_values: Array of error values (for ERROR_CHECK)
+ * @error_count: Number of error values
+ * @is_success: Custom function to check success
+ * @description: Return value description
+ */
+struct kapi_return_spec {
+	char type_name[KAPI_MAX_NAME_LEN];
+	enum kapi_param_type type;
+	enum kapi_return_check_type check_type;
+	s64 success_value;
+	s64 success_min;
+	s64 success_max;
+	const s64 *error_values;
+	u32 error_count;
+	bool (*is_success)(s64 retval);
+	char description[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+/**
+ * struct kapi_lock_spec - Lock requirement specification
+ * @lock_name: Name of the lock
+ * @lock_type: Type of lock
+ * @acquired: Whether function acquires this lock
+ * @released: Whether function releases this lock
+ * @held_on_entry: Whether lock must be held on entry
+ * @held_on_exit: Whether lock is held on exit
+ * @description: Additional lock requirements
+ */
+struct kapi_lock_spec {
+	char lock_name[KAPI_MAX_NAME_LEN];
+	enum kapi_lock_type lock_type;
+	bool acquired;
+	bool released;
+	bool held_on_entry;
+	bool held_on_exit;
+	char description[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+/**
+ * struct kapi_constraint_spec - Additional constraint specification
+ * @name: Constraint name
+ * @description: Constraint description
+ * @expression: Formal expression (if applicable)
+ */
+struct kapi_constraint_spec {
+	char name[KAPI_MAX_NAME_LEN];
+	char description[KAPI_MAX_DESC_LEN];
+	char expression[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+/**
+ * enum kapi_signal_direction - Signal flow direction
+ * @KAPI_SIGNAL_RECEIVE: Function may receive this signal
+ * @KAPI_SIGNAL_SEND: Function may send this signal
+ * @KAPI_SIGNAL_HANDLE: Function handles this signal specially
+ * @KAPI_SIGNAL_BLOCK: Function blocks this signal
+ * @KAPI_SIGNAL_IGNORE: Function ignores this signal
+ */
+enum kapi_signal_direction {
+	KAPI_SIGNAL_RECEIVE	= (1 << 0),
+	KAPI_SIGNAL_SEND	= (1 << 1),
+	KAPI_SIGNAL_HANDLE	= (1 << 2),
+	KAPI_SIGNAL_BLOCK	= (1 << 3),
+	KAPI_SIGNAL_IGNORE	= (1 << 4),
+};
+
+/**
+ * enum kapi_signal_action - What the function does with the signal
+ * @KAPI_SIGNAL_ACTION_DEFAULT: Default signal action applies
+ * @KAPI_SIGNAL_ACTION_TERMINATE: Causes termination
+ * @KAPI_SIGNAL_ACTION_COREDUMP: Causes termination with core dump
+ * @KAPI_SIGNAL_ACTION_STOP: Stops the process
+ * @KAPI_SIGNAL_ACTION_CONTINUE: Continues a stopped process
+ * @KAPI_SIGNAL_ACTION_CUSTOM: Custom handling described in notes
+ * @KAPI_SIGNAL_ACTION_RETURN: Returns from syscall with EINTR
+ * @KAPI_SIGNAL_ACTION_RESTART: Restarts the syscall
+ */
+enum kapi_signal_action {
+	KAPI_SIGNAL_ACTION_DEFAULT = 0,
+	KAPI_SIGNAL_ACTION_TERMINATE,
+	KAPI_SIGNAL_ACTION_COREDUMP,
+	KAPI_SIGNAL_ACTION_STOP,
+	KAPI_SIGNAL_ACTION_CONTINUE,
+	KAPI_SIGNAL_ACTION_CUSTOM,
+	KAPI_SIGNAL_ACTION_RETURN,
+	KAPI_SIGNAL_ACTION_RESTART,
+};
+
+/**
+ * struct kapi_signal_spec - Signal specification
+ * @signal_num: Signal number (e.g., SIGKILL, SIGTERM)
+ * @signal_name: Signal name as string
+ * @direction: Direction flags (OR of kapi_signal_direction)
+ * @action: What happens when signal is received
+ * @target: Description of target process/thread for sent signals
+ * @condition: Condition under which signal is sent/received/handled
+ * @description: Detailed description of signal handling
+ * @restartable: Whether syscall is restartable after this signal
+ */
+struct kapi_signal_spec {
+	int signal_num;
+	char signal_name[32];
+	u32 direction;
+	enum kapi_signal_action action;
+	char target[KAPI_MAX_DESC_LEN];
+	char condition[KAPI_MAX_DESC_LEN];
+	char description[KAPI_MAX_DESC_LEN];
+	bool restartable;
+} __attribute__((packed));
+
+/**
+ * struct kapi_signal_mask_spec - Signal mask specification
+ * @mask_name: Name of the signal mask
+ * @signals: Array of signal numbers in the mask
+ * @signal_count: Number of signals in the mask
+ * @description: Description of what this mask represents
+ */
+struct kapi_signal_mask_spec {
+	char mask_name[KAPI_MAX_NAME_LEN];
+	int signals[KAPI_MAX_SIGNALS];
+	u32 signal_count;
+	char description[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+/**
+ * struct kapi_struct_field - Structure field specification
+ * @name: Field name
+ * @type: Field type classification
+ * @type_name: Type name as string
+ * @offset: Offset within structure
+ * @size: Size of field in bytes
+ * @flags: Field attribute flags
+ * @constraint_type: Type of constraint applied
+ * @min_value: Minimum valid value (for numeric types)
+ * @max_value: Maximum valid value (for numeric types)
+ * @valid_mask: Valid bits mask (for flag fields)
+ * @description: Field description
+ */
+struct kapi_struct_field {
+	char name[KAPI_MAX_NAME_LEN];
+	enum kapi_param_type type;
+	char type_name[KAPI_MAX_NAME_LEN];
+	size_t offset;
+	size_t size;
+	u32 flags;
+	enum kapi_constraint_type constraint_type;
+	s64 min_value;
+	s64 max_value;
+	u64 valid_mask;
+	char description[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+/**
+ * struct kapi_struct_spec - Structure type specification
+ * @name: Structure name
+ * @size: Total size of structure
+ * @alignment: Required alignment
+ * @field_count: Number of fields
+ * @fields: Field specifications
+ * @description: Structure description
+ */
+struct kapi_struct_spec {
+	char name[KAPI_MAX_NAME_LEN];
+	size_t size;
+	size_t alignment;
+	u32 field_count;
+	struct kapi_struct_field fields[KAPI_MAX_PARAMS];
+	char description[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+/**
+ * enum kapi_side_effect_type - Types of side effects
+ * @KAPI_EFFECT_NONE: No side effects
+ * @KAPI_EFFECT_ALLOC_MEMORY: Allocates memory
+ * @KAPI_EFFECT_FREE_MEMORY: Frees memory
+ * @KAPI_EFFECT_MODIFY_STATE: Modifies global/shared state
+ * @KAPI_EFFECT_SIGNAL_SEND: Sends signals
+ * @KAPI_EFFECT_FILE_POSITION: Modifies file position
+ * @KAPI_EFFECT_LOCK_ACQUIRE: Acquires locks
+ * @KAPI_EFFECT_LOCK_RELEASE: Releases locks
+ * @KAPI_EFFECT_RESOURCE_CREATE: Creates system resources (FDs, PIDs, etc)
+ * @KAPI_EFFECT_RESOURCE_DESTROY: Destroys system resources
+ * @KAPI_EFFECT_SCHEDULE: May cause scheduling/context switch
+ * @KAPI_EFFECT_HARDWARE: Interacts with hardware
+ * @KAPI_EFFECT_NETWORK: Network I/O operation
+ * @KAPI_EFFECT_FILESYSTEM: Filesystem modification
+ * @KAPI_EFFECT_PROCESS_STATE: Modifies process state
+ */
+enum kapi_side_effect_type {
+	KAPI_EFFECT_NONE = 0,
+	KAPI_EFFECT_ALLOC_MEMORY = (1 << 0),
+	KAPI_EFFECT_FREE_MEMORY = (1 << 1),
+	KAPI_EFFECT_MODIFY_STATE = (1 << 2),
+	KAPI_EFFECT_SIGNAL_SEND = (1 << 3),
+	KAPI_EFFECT_FILE_POSITION = (1 << 4),
+	KAPI_EFFECT_LOCK_ACQUIRE = (1 << 5),
+	KAPI_EFFECT_LOCK_RELEASE = (1 << 6),
+	KAPI_EFFECT_RESOURCE_CREATE = (1 << 7),
+	KAPI_EFFECT_RESOURCE_DESTROY = (1 << 8),
+	KAPI_EFFECT_SCHEDULE = (1 << 9),
+	KAPI_EFFECT_HARDWARE = (1 << 10),
+	KAPI_EFFECT_NETWORK = (1 << 11),
+	KAPI_EFFECT_FILESYSTEM = (1 << 12),
+	KAPI_EFFECT_PROCESS_STATE = (1 << 13),
+};
+
+/**
+ * struct kapi_side_effect - Side effect specification
+ * @type: Bitmask of effect types
+ * @target: What is affected (e.g., "process memory", "file descriptor table")
+ * @condition: Condition under which effect occurs
+ * @description: Detailed description of the effect
+ * @reversible: Whether the effect can be undone
+ */
+struct kapi_side_effect {
+	u32 type;
+	char target[KAPI_MAX_NAME_LEN];
+	char condition[KAPI_MAX_DESC_LEN];
+	char description[KAPI_MAX_DESC_LEN];
+	bool reversible;
+} __attribute__((packed));
+
+/**
+ * struct kapi_state_transition - State transition specification
+ * @from_state: Starting state description
+ * @to_state: Ending state description
+ * @condition: Condition for transition
+ * @object: Object whose state changes
+ * @description: Detailed description
+ */
+struct kapi_state_transition {
+	char from_state[KAPI_MAX_NAME_LEN];
+	char to_state[KAPI_MAX_NAME_LEN];
+	char condition[KAPI_MAX_DESC_LEN];
+	char object[KAPI_MAX_NAME_LEN];
+	char description[KAPI_MAX_DESC_LEN];
+} __attribute__((packed));
+
+#define KAPI_MAX_STRUCT_SPECS	8
+#define KAPI_MAX_SIDE_EFFECTS	16
+#define KAPI_MAX_STATE_TRANS	8
+
+/**
+ * struct kernel_api_spec - Complete kernel API specification
+ * @name: Function name
+ * @version: API version
+ * @description: Brief description
+ * @long_description: Detailed description
+ * @context_flags: Execution context flags
+ * @param_count: Number of parameters
+ * @params: Parameter specifications
+ * @return_spec: Return value specification
+ * @error_count: Number of possible errors
+ * @errors: Error specifications
+ * @lock_count: Number of lock specifications
+ * @locks: Lock requirement specifications
+ * @constraint_count: Number of additional constraints
+ * @constraints: Additional constraint specifications
+ * @examples: Usage examples
+ * @notes: Additional notes
+ * @since_version: Kernel version when introduced
+ * @deprecated: Whether API is deprecated
+ * @replacement: Replacement API if deprecated
+ * @signal_count: Number of signal specifications
+ * @signals: Signal handling specifications
+ * @signal_mask_count: Number of signal mask specifications
+ * @signal_masks: Signal mask specifications
+ * @struct_spec_count: Number of structure specifications
+ * @struct_specs: Structure type specifications
+ * @side_effect_count: Number of side effect specifications
+ * @side_effects: Side effect specifications
+ * @state_trans_count: Number of state transition specifications
+ * @state_transitions: State transition specifications
+ */
+struct kernel_api_spec {
+	char name[KAPI_MAX_NAME_LEN];
+	u32 version;
+	char description[KAPI_MAX_DESC_LEN];
+	char long_description[KAPI_MAX_DESC_LEN * 4];
+	u32 context_flags;
+
+	/* Parameters */
+	u32 param_count;
+	struct kapi_param_spec params[KAPI_MAX_PARAMS];
+
+	/* Return value */
+	struct kapi_return_spec return_spec;
+
+	/* Errors */
+	u32 error_count;
+	struct kapi_error_spec errors[KAPI_MAX_ERRORS];
+
+	/* Locking */
+	u32 lock_count;
+	struct kapi_lock_spec locks[KAPI_MAX_CONSTRAINTS];
+
+	/* Constraints */
+	u32 constraint_count;
+	struct kapi_constraint_spec constraints[KAPI_MAX_CONSTRAINTS];
+
+	/* Additional information */
+	char examples[KAPI_MAX_DESC_LEN * 2];
+	char notes[KAPI_MAX_DESC_LEN];
+	char since_version[32];
+	bool deprecated;
+	char replacement[KAPI_MAX_NAME_LEN];
+
+	/* Signal specifications */
+	u32 signal_count;
+	struct kapi_signal_spec signals[KAPI_MAX_SIGNALS];
+
+	/* Signal mask specifications */
+	u32 signal_mask_count;
+	struct kapi_signal_mask_spec signal_masks[KAPI_MAX_SIGNALS];
+
+	/* Structure specifications */
+	u32 struct_spec_count;
+	struct kapi_struct_spec struct_specs[KAPI_MAX_STRUCT_SPECS];
+
+	/* Side effects */
+	u32 side_effect_count;
+	struct kapi_side_effect side_effects[KAPI_MAX_SIDE_EFFECTS];
+
+	/* State transitions */
+	u32 state_trans_count;
+	struct kapi_state_transition state_transitions[KAPI_MAX_STATE_TRANS];
+} __attribute__((packed));
+
+/* Macros for defining API specifications */
+
+/**
+ * DEFINE_KERNEL_API_SPEC - Define a kernel API specification
+ * @func_name: Function name to specify
+ */
+#define DEFINE_KERNEL_API_SPEC(func_name) \
+	static struct kernel_api_spec __kapi_spec_##func_name \
+	__used __section(".kapi_specs") = {	\
+		.name = __stringify(func_name),	\
+		.version = 1,
+
+#define KAPI_END_SPEC };
+
+/**
+ * KAPI_DESCRIPTION - Set API description
+ * @desc: Description string
+ */
+#define KAPI_DESCRIPTION(desc) \
+	.description = desc,
+
+/**
+ * KAPI_LONG_DESC - Set detailed API description
+ * @desc: Detailed description string
+ */
+#define KAPI_LONG_DESC(desc) \
+	.long_description = desc,
+
+/**
+ * KAPI_CONTEXT - Set execution context flags
+ * @flags: Context flags (OR'ed KAPI_CTX_* values)
+ */
+#define KAPI_CONTEXT(flags) \
+	.context_flags = flags,
+
+/**
+ * KAPI_PARAM - Define a parameter specification
+ * @idx: Parameter index (0-based)
+ * @pname: Parameter name
+ * @ptype: Type name string
+ * @pdesc: Parameter description
+ */
+#define KAPI_PARAM(idx, pname, ptype, pdesc) \
+	.params[idx] = {			\
+		.name = pname,			\
+		.type_name = ptype,		\
+		.description = pdesc,		\
+		.size_param_idx = -1,		/* Default: no dynamic sizing */
+
+#define KAPI_PARAM_FLAGS(pflags) \
+		.flags = pflags,
+
+#define KAPI_PARAM_SIZE(psize) \
+		.size = psize,
+
+#define KAPI_PARAM_RANGE(pmin, pmax) \
+		.min_value = pmin,	\
+		.max_value = pmax,
+
+#define KAPI_PARAM_END },
+
+/**
+ * KAPI_RETURN - Define return value specification
+ * @rtype: Return type name
+ * @rdesc: Return value description
+ */
+#define KAPI_RETURN(rtype, rdesc) \
+	.return_spec = {		\
+		.type_name = rtype,	\
+		.description = rdesc,
+
+#define KAPI_RETURN_SUCCESS(val) \
+		.success_value = val,
+
+#define KAPI_RETURN_END },
+
+/**
+ * KAPI_ERROR - Define an error condition
+ * @idx: Error index
+ * @ecode: Error code value
+ * @ename: Error name
+ * @econd: Error condition
+ * @edesc: Error description
+ */
+#define KAPI_ERROR(idx, ecode, ename, econd, edesc) \
+	.errors[idx] = {			\
+		.error_code = ecode,		\
+		.name = ename,			\
+		.condition = econd,		\
+		.description = edesc,		\
+	},
+
+/**
+ * KAPI_LOCK - Define a lock requirement
+ * @idx: Lock index
+ * @lname: Lock name
+ * @ltype: Lock type
+ */
+#define KAPI_LOCK(idx, lname, ltype) \
+	.locks[idx] = {			\
+		.lock_name = lname,	\
+		.lock_type = ltype,
+
+#define KAPI_LOCK_ACQUIRED \
+		.acquired = true,
+
+#define KAPI_LOCK_RELEASED \
+		.released = true,
+
+#define KAPI_LOCK_HELD_ENTRY \
+		.held_on_entry = true,
+
+#define KAPI_LOCK_HELD_EXIT \
+		.held_on_exit = true,
+
+#define KAPI_LOCK_DESC(ldesc) \
+		.description = ldesc,
+
+#define KAPI_LOCK_END },
+
+/**
+ * KAPI_CONSTRAINT - Define an additional constraint
+ * @idx: Constraint index
+ * @cname: Constraint name
+ * @cdesc: Constraint description
+ */
+#define KAPI_CONSTRAINT(idx, cname, cdesc) \
+	.constraints[idx] = {		\
+		.name = cname,		\
+		.description = cdesc,
+
+#define KAPI_CONSTRAINT_EXPR(expr) \
+		.expression = expr,
+
+#define KAPI_CONSTRAINT_END },
+
+/**
+ * KAPI_SIGNAL - Define a signal specification
+ * @idx: Signal index
+ * @signum: Signal number (e.g., SIGKILL)
+ * @signame: Signal name string
+ * @dir: Direction flags
+ * @act: Action taken
+ */
+#define KAPI_SIGNAL(idx, signum, signame, dir, act) \
+	.signals[idx] = {			\
+		.signal_num = signum,		\
+		.signal_name = signame,		\
+		.direction = dir,		\
+		.action = act,
+
+#define KAPI_SIGNAL_TARGET(tgt) \
+		.target = tgt,
+
+#define KAPI_SIGNAL_CONDITION(cond) \
+		.condition = cond,
+
+#define KAPI_SIGNAL_DESC(desc) \
+		.description = desc,
+
+#define KAPI_SIGNAL_RESTARTABLE \
+		.restartable = true,
+
+#define KAPI_SIGNAL_END },
+
+/**
+ * KAPI_SIGNAL_MASK - Define a signal mask specification
+ * @idx: Mask index
+ * @name: Mask name
+ * @desc: Mask description
+ */
+#define KAPI_SIGNAL_MASK(idx, name, desc) \
+	.signal_masks[idx] = {		\
+		.mask_name = name,	\
+		.description = desc,
+
+#define KAPI_SIGNAL_MASK_ADD(signum) \
+		.signals[.signal_count++] = signum,
+
+#define KAPI_SIGNAL_MASK_END },
+
+/**
+ * KAPI_STRUCT_SPEC - Define a structure specification
+ * @idx: Structure spec index
+ * @sname: Structure name
+ * @sdesc: Structure description
+ */
+#define KAPI_STRUCT_SPEC(idx, sname, sdesc) \
+	.struct_specs[idx] = {		\
+		.name = #sname,		\
+		.description = sdesc,
+
+#define KAPI_STRUCT_SIZE(ssize, salign) \
+		.size = ssize,		\
+		.alignment = salign,
+
+#define KAPI_STRUCT_FIELD_COUNT(n) \
+		.field_count = n,
+
+/**
+ * KAPI_STRUCT_FIELD - Define a structure field
+ * @fidx: Field index
+ * @fname: Field name
+ * @ftype: Field type (KAPI_TYPE_*)
+ * @ftype_name: Type name as string
+ * @fdesc: Field description
+ */
+#define KAPI_STRUCT_FIELD(fidx, fname, ftype, ftype_name, fdesc) \
+		.fields[fidx] = {	\
+			.name = fname,	\
+			.type = ftype,	\
+			.type_name = ftype_name, \
+			.description = fdesc,
+
+#define KAPI_FIELD_OFFSET(foffset) \
+			.offset = foffset,
+
+#define KAPI_FIELD_SIZE(fsize) \
+			.size = fsize,
+
+#define KAPI_FIELD_FLAGS(fflags) \
+			.flags = fflags,
+
+#define KAPI_FIELD_CONSTRAINT_RANGE(min, max) \
+			.constraint_type = KAPI_CONSTRAINT_RANGE, \
+			.min_value = min, \
+			.max_value = max,
+
+#define KAPI_FIELD_CONSTRAINT_MASK(mask) \
+			.constraint_type = KAPI_CONSTRAINT_MASK, \
+			.valid_mask = mask,
+
+#define KAPI_FIELD_CONSTRAINT_ENUM(values, count) \
+			.constraint_type = KAPI_CONSTRAINT_ENUM, \
+			.enum_values = values, \
+			.enum_count = count,
+
+#define KAPI_STRUCT_FIELD_END },
+
+#define KAPI_STRUCT_SPEC_END },
+
+/* Counter for structure specifications */
+#define KAPI_STRUCT_SPEC_COUNT(n) \
+	.struct_spec_count = n,
+
+/**
+ * KAPI_SIDE_EFFECT - Define a side effect
+ * @idx: Side effect index
+ * @etype: Effect type bitmask (OR'ed KAPI_EFFECT_* values)
+ * @etarget: What is affected
+ * @edesc: Effect description
+ */
+#define KAPI_SIDE_EFFECT(idx, etype, etarget, edesc) \
+	.side_effects[idx] = {		\
+		.type = etype,		\
+		.target = etarget,	\
+		.description = edesc,	\
+		.reversible = false,	/* Default to non-reversible */
+
+#define KAPI_EFFECT_CONDITION(cond) \
+		.condition = cond,
+
+#define KAPI_EFFECT_REVERSIBLE \
+		.reversible = true,
+
+#define KAPI_SIDE_EFFECT_END },
+
+/**
+ * KAPI_STATE_TRANS - Define a state transition
+ * @idx: State transition index
+ * @obj: Object whose state changes
+ * @from: From state
+ * @to: To state
+ * @desc: Transition description
+ */
+#define KAPI_STATE_TRANS(idx, obj, from, to, desc) \
+	.state_transitions[idx] = {	\
+		.object = obj,		\
+		.from_state = from,	\
+		.to_state = to,		\
+		.description = desc,
+
+#define KAPI_STATE_TRANS_COND(cond) \
+		.condition = cond,
+
+#define KAPI_STATE_TRANS_END },
+
+/* Counters for side effects and state transitions */
+#define KAPI_SIDE_EFFECT_COUNT(n) \
+	.side_effect_count = n,
+
+#define KAPI_STATE_TRANS_COUNT(n) \
+	.state_trans_count = n,
+
+/* Helper macros for common side effect patterns */
+#define KAPI_EFFECTS_MEMORY	(KAPI_EFFECT_ALLOC_MEMORY | KAPI_EFFECT_FREE_MEMORY)
+#define KAPI_EFFECTS_LOCKING	(KAPI_EFFECT_LOCK_ACQUIRE | KAPI_EFFECT_LOCK_RELEASE)
+#define KAPI_EFFECTS_RESOURCES	(KAPI_EFFECT_RESOURCE_CREATE | KAPI_EFFECT_RESOURCE_DESTROY)
+#define KAPI_EFFECTS_IO		(KAPI_EFFECT_NETWORK | KAPI_EFFECT_FILESYSTEM)
+
+/* Helper macros for common patterns */
+
+#define KAPI_PARAM_IN		(KAPI_PARAM_IN)
+#define KAPI_PARAM_OUT		(KAPI_PARAM_OUT)
+#define KAPI_PARAM_INOUT	(KAPI_PARAM_IN | KAPI_PARAM_OUT)
+#define KAPI_PARAM_OPTIONAL	(KAPI_PARAM_OPTIONAL)
+#define KAPI_PARAM_USER_PTR	(KAPI_PARAM_USER | KAPI_PARAM_PTR)
+
+/* Validation and runtime checking */
+
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+bool kapi_validate_params(const struct kernel_api_spec *spec, ...);
+bool kapi_validate_param(const struct kapi_param_spec *param_spec, s64 value);
+bool kapi_validate_param_with_context(const struct kapi_param_spec *param_spec,
+				       s64 value, const s64 *all_params, int param_count);
+int kapi_validate_syscall_param(const struct kernel_api_spec *spec,
+				int param_idx, s64 value);
+int kapi_validate_syscall_params(const struct kernel_api_spec *spec,
+				 const s64 *params, int param_count);
+bool kapi_check_return_success(const struct kapi_return_spec *return_spec, s64 retval);
+bool kapi_validate_return_value(const struct kernel_api_spec *spec, s64 retval);
+int kapi_validate_syscall_return(const struct kernel_api_spec *spec, s64 retval);
+void kapi_check_context(const struct kernel_api_spec *spec);
+void kapi_check_locks(const struct kernel_api_spec *spec);
+#else
+static inline bool kapi_validate_params(const struct kernel_api_spec *spec, ...)
+{
+	return true;
+}
+static inline bool kapi_validate_param(const struct kapi_param_spec *param_spec, s64 value)
+{
+	return true;
+}
+static inline bool kapi_validate_param_with_context(const struct kapi_param_spec *param_spec,
+						     s64 value, const s64 *all_params, int param_count)
+{
+	return true;
+}
+static inline int kapi_validate_syscall_param(const struct kernel_api_spec *spec,
+					       int param_idx, s64 value)
+{
+	return 0;
+}
+static inline int kapi_validate_syscall_params(const struct kernel_api_spec *spec,
+					       const s64 *params, int param_count)
+{
+	return 0;
+}
+static inline bool kapi_check_return_success(const struct kapi_return_spec *return_spec, s64 retval)
+{
+	return true;
+}
+static inline bool kapi_validate_return_value(const struct kernel_api_spec *spec, s64 retval)
+{
+	return true;
+}
+static inline int kapi_validate_syscall_return(const struct kernel_api_spec *spec, s64 retval)
+{
+	return 0;
+}
+static inline void kapi_check_context(const struct kernel_api_spec *spec) {}
+static inline void kapi_check_locks(const struct kernel_api_spec *spec) {}
+#endif
+
+/* Export/query functions */
+const struct kernel_api_spec *kapi_get_spec(const char *name);
+int kapi_export_json(const struct kernel_api_spec *spec, char *buf, size_t size);
+int kapi_export_xml(const struct kernel_api_spec *spec, char *buf, size_t size);
+void kapi_print_spec(const struct kernel_api_spec *spec);
+
+/* Registration for dynamic APIs */
+int kapi_register_spec(struct kernel_api_spec *spec);
+void kapi_unregister_spec(const char *name);
+
+/* Helper to get parameter constraint info */
+static inline bool kapi_get_param_constraint(const char *api_name, int param_idx,
+					      enum kapi_constraint_type *type,
+					      u64 *valid_mask, s64 *min_val, s64 *max_val)
+{
+	const struct kernel_api_spec *spec = kapi_get_spec(api_name);
+
+	if (!spec || param_idx >= spec->param_count)
+		return false;
+
+	if (type)
+		*type = spec->params[param_idx].constraint_type;
+	if (valid_mask)
+		*valid_mask = spec->params[param_idx].valid_mask;
+	if (min_val)
+		*min_val = spec->params[param_idx].min_value;
+	if (max_val)
+		*max_val = spec->params[param_idx].max_value;
+
+	return true;
+}
+
+#endif /* _LINUX_KERNEL_API_SPEC_H */
\ No newline at end of file
diff --git a/include/linux/syscall_api_spec.h b/include/linux/syscall_api_spec.h
new file mode 100644
index 0000000000000..48ad95647dd39
--- /dev/null
+++ b/include/linux/syscall_api_spec.h
@@ -0,0 +1,341 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * syscall_api_spec.h - System Call API Specification Integration
+ *
+ * This header extends the SYSCALL_DEFINEX macros to support inline API specifications,
+ * allowing syscall documentation to be written alongside the implementation in a
+ * human-readable and machine-parseable format.
+ */
+
+#ifndef _LINUX_SYSCALL_API_SPEC_H
+#define _LINUX_SYSCALL_API_SPEC_H
+
+#include <linux/kernel_api_spec.h>
+
+/*
+ * Extended SYSCALL_DEFINE macros with API specification support
+ *
+ * Usage example:
+ *
+ * SYSCALL_DEFINE_SPEC2(example,
+ *     KAPI_DESCRIPTION("Example system call"),
+ *     KAPI_LONG_DESC("This is a detailed description of the example syscall"),
+ *     KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE),
+ *
+ *     KAPI_PARAM(0, "fd", "int", "File descriptor to operate on")
+ *         KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+ *         KAPI_PARAM_RANGE(0, INT_MAX)
+ *     KAPI_PARAM_END,
+ *
+ *     KAPI_PARAM(1, "flags", "unsigned int", "Operation flags")
+ *         KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+ *     KAPI_PARAM_END,
+ *
+ *     KAPI_RETURN("long", "0 on success, negative error code on failure")
+ *         KAPI_RETURN_SUCCESS(0, "== 0")
+ *     KAPI_RETURN_END,
+ *
+ *     KAPI_ERROR(0, -EBADF, "EBADF", "fd is not a valid file descriptor",
+ *                "The file descriptor is invalid or closed"),
+ *     KAPI_ERROR(1, -EINVAL, "EINVAL", "flags contains invalid values",
+ *                "Invalid flag combination specified"),
+ *
+ *     .error_count = 2,
+ *     .param_count = 2,
+ *
+ *     int, fd, unsigned int, flags)
+ * {
+ *     // Implementation here
+ * }
+ */
+
+/* Helper to count parameters */
+#define __SYSCALL_PARAM_COUNT(...) __SYSCALL_PARAM_COUNT_I(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0)
+#define __SYSCALL_PARAM_COUNT_I(_1, _2, _3, _4, _5, _6, N, ...) N
+
+/* Extract syscall name from parameters */
+#define __SYSCALL_NAME(name, ...) name
+
+/* Generate API spec structure name */
+#define __SYSCALL_API_SPEC_NAME(name) __kapi_spec_sys_##name
+
+/* Helper to count syscall parameters (pairs of type, name) */
+#define __SYSCALL_ARG_COUNT(...) __SYSCALL_ARG_COUNT_I(__VA_ARGS__, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0)
+#define __SYSCALL_ARG_COUNT_I(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, N, ...) N
+
+/* Automatic syscall validation infrastructure */
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+
+/* Helper to inject validation at the beginning of syscall */
+#define __KAPI_SYSCALL_VALIDATE_0(name)
+#define __KAPI_SYSCALL_VALIDATE_1(name, t1, a1) \
+	const struct kernel_api_spec *__spec = kapi_get_spec("sys_" #name); \
+	if (__spec) { \
+		s64 __params[1] = { (s64)(a1) }; \
+		int __ret = kapi_validate_syscall_params(__spec, __params, 1); \
+		if (__ret) return __ret; \
+	}
+#define __KAPI_SYSCALL_VALIDATE_2(name, t1, a1, t2, a2) \
+	const struct kernel_api_spec *__spec = kapi_get_spec("sys_" #name); \
+	if (__spec) { \
+		s64 __params[2] = { (s64)(a1), (s64)(a2) }; \
+		int __ret = kapi_validate_syscall_params(__spec, __params, 2); \
+		if (__ret) return __ret; \
+	}
+#define __KAPI_SYSCALL_VALIDATE_3(name, t1, a1, t2, a2, t3, a3) \
+	const struct kernel_api_spec *__spec = kapi_get_spec("sys_" #name); \
+	if (__spec) { \
+		s64 __params[3] = { (s64)(a1), (s64)(a2), (s64)(a3) }; \
+		int __ret = kapi_validate_syscall_params(__spec, __params, 3); \
+		if (__ret) return __ret; \
+	}
+#define __KAPI_SYSCALL_VALIDATE_4(name, t1, a1, t2, a2, t3, a3, t4, a4) \
+	const struct kernel_api_spec *__spec = kapi_get_spec("sys_" #name); \
+	if (__spec) { \
+		s64 __params[4] = { (s64)(a1), (s64)(a2), (s64)(a3), (s64)(a4) }; \
+		int __ret = kapi_validate_syscall_params(__spec, __params, 4); \
+		if (__ret) return __ret; \
+	}
+#define __KAPI_SYSCALL_VALIDATE_5(name, t1, a1, t2, a2, t3, a3, t4, a4, t5, a5) \
+	const struct kernel_api_spec *__spec = kapi_get_spec("sys_" #name); \
+	if (__spec) { \
+		s64 __params[5] = { (s64)(a1), (s64)(a2), (s64)(a3), (s64)(a4), (s64)(a5) }; \
+		int __ret = kapi_validate_syscall_params(__spec, __params, 5); \
+		if (__ret) return __ret; \
+	}
+#define __KAPI_SYSCALL_VALIDATE_6(name, t1, a1, t2, a2, t3, a3, t4, a4, t5, a5, t6, a6) \
+	const struct kernel_api_spec *__spec = kapi_get_spec("sys_" #name); \
+	if (__spec) { \
+		s64 __params[6] = { (s64)(a1), (s64)(a2), (s64)(a3), (s64)(a4), (s64)(a5), (s64)(a6) }; \
+		int __ret = kapi_validate_syscall_params(__spec, __params, 6); \
+		if (__ret) return __ret; \
+	}
+
+#else /* !CONFIG_KAPI_RUNTIME_CHECKS */
+
+#define __KAPI_SYSCALL_VALIDATE_0(name)
+#define __KAPI_SYSCALL_VALIDATE_1(name, t1, a1)
+#define __KAPI_SYSCALL_VALIDATE_2(name, t1, a1, t2, a2)
+#define __KAPI_SYSCALL_VALIDATE_3(name, t1, a1, t2, a2, t3, a3)
+#define __KAPI_SYSCALL_VALIDATE_4(name, t1, a1, t2, a2, t3, a3, t4, a4)
+#define __KAPI_SYSCALL_VALIDATE_5(name, t1, a1, t2, a2, t3, a3, t4, a4, t5, a5)
+#define __KAPI_SYSCALL_VALIDATE_6(name, t1, a1, t2, a2, t3, a3, t4, a4, t5, a5, t6, a6)
+
+#endif /* CONFIG_KAPI_RUNTIME_CHECKS */
+
+/* Helper to inject validation for return values */
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+
+#define __KAPI_SYSCALL_VALIDATE_RETURN(name, retval) \
+	do { \
+		const struct kernel_api_spec *__spec = kapi_get_spec("sys_" #name); \
+		if (__spec) { \
+			kapi_validate_syscall_return(__spec, (s64)(retval)); \
+		} \
+	} while (0)
+
+/* Wrapper to validate both params and return value */
+#define __SYSCALL_DEFINE_SPEC(name, spec_args, ...) \
+	DEFINE_KERNEL_API_SPEC(sys_##name) \
+		.name = "sys_" #name, \
+		spec_args \
+	KAPI_END_SPEC; \
+	static long __kapi_sys_##name(__MAP((__SYSCALL_ARG_COUNT(__VA_ARGS__)), __SC_DECL, __VA_ARGS__)); \
+	SYSCALL_DEFINE##__SYSCALL_ARG_COUNT(__VA_ARGS__)(name, __VA_ARGS__) \
+	{ \
+		long __ret; \
+		__KAPI_SYSCALL_VALIDATE_##__SYSCALL_ARG_COUNT(__VA_ARGS__)(name, __VA_ARGS__); \
+		__ret = __kapi_sys_##name(__MAP((__SYSCALL_ARG_COUNT(__VA_ARGS__)), __SC_CAST, __VA_ARGS__)); \
+		__KAPI_SYSCALL_VALIDATE_RETURN(name, __ret); \
+		return __ret; \
+	} \
+	static long __kapi_sys_##name(__MAP((__SYSCALL_ARG_COUNT(__VA_ARGS__)), __SC_DECL, __VA_ARGS__))
+
+#else /* !CONFIG_KAPI_RUNTIME_CHECKS */
+
+#define __SYSCALL_DEFINE_SPEC(name, spec_args, ...) \
+	DEFINE_KERNEL_API_SPEC(sys_##name) \
+		.name = "sys_" #name, \
+		spec_args \
+	KAPI_END_SPEC; \
+	SYSCALL_DEFINE##__SYSCALL_ARG_COUNT(__VA_ARGS__)(name, __VA_ARGS__)
+
+#endif /* CONFIG_KAPI_RUNTIME_CHECKS */
+
+
+/* Convenience macros for different parameter counts */
+#define SYSCALL_DEFINE_SPEC0(name, spec_args)				\
+	DEFINE_KERNEL_API_SPEC(sys_##name)				\
+		.name = "sys_" #name,					\
+		.param_count = 0,					\
+		spec_args						\
+	KAPI_END_SPEC;							\
+	SYSCALL_DEFINE0(name)
+
+#define SYSCALL_DEFINE_SPEC1(name, spec_args, t1, a1)			\
+	__SYSCALL_DEFINE_SPEC(name, spec_args, t1, a1)
+
+#define SYSCALL_DEFINE_SPEC2(name, spec_args, t1, a1, t2, a2)		\
+	__SYSCALL_DEFINE_SPEC(name, spec_args, t1, a1, t2, a2)
+
+#define SYSCALL_DEFINE_SPEC3(name, spec_args, t1, a1, t2, a2, t3, a3)	\
+	__SYSCALL_DEFINE_SPEC(name, spec_args, t1, a1, t2, a2, t3, a3)
+
+#define SYSCALL_DEFINE_SPEC4(name, spec_args, t1, a1, t2, a2, t3, a3,	\
+			     t4, a4)					\
+	__SYSCALL_DEFINE_SPEC(name, spec_args, t1, a1, t2, a2, t3, a3, t4, a4)
+
+#define SYSCALL_DEFINE_SPEC5(name, spec_args, t1, a1, t2, a2, t3, a3,	\
+			     t4, a4, t5, a5)				\
+	__SYSCALL_DEFINE_SPEC(name, spec_args, t1, a1, t2, a2, t3, a3,	\
+			      t4, a4, t5, a5)
+
+#define SYSCALL_DEFINE_SPEC6(name, spec_args, t1, a1, t2, a2, t3, a3,	\
+			     t4, a4, t5, a5, t6, a6)			\
+	__SYSCALL_DEFINE_SPEC(name, spec_args, t1, a1, t2, a2, t3, a3,	\
+			      t4, a4, t5, a5, t6, a6)
+
+/*
+ * Helper macros for common syscall patterns
+ */
+
+/* For syscalls that can sleep */
+#define KAPI_SYSCALL_SLEEPABLE \
+	KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
+
+/* For syscalls that must be atomic */
+#define KAPI_SYSCALL_ATOMIC \
+	KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_ATOMIC)
+
+/* Common parameter specifications */
+#define KAPI_PARAM_FD(idx, desc) \
+	KAPI_PARAM(idx, "fd", "int", desc) \
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN) \
+		.type = KAPI_TYPE_FD, \
+		.constraint_type = KAPI_CONSTRAINT_NONE, \
+	KAPI_PARAM_END
+
+#define KAPI_PARAM_USER_BUF(idx, name, desc) \
+	KAPI_PARAM(idx, name, "void __user *", desc) \
+		KAPI_PARAM_FLAGS(KAPI_PARAM_USER_PTR | KAPI_PARAM_IN) \
+	KAPI_PARAM_END
+
+#define KAPI_PARAM_USER_STRUCT(idx, name, struct_type, desc) \
+	KAPI_PARAM(idx, name, #struct_type " __user *", desc) \
+		KAPI_PARAM_FLAGS(KAPI_PARAM_USER | KAPI_PARAM_IN) \
+		.type = KAPI_TYPE_USER_PTR, \
+		.size = sizeof(struct_type), \
+		.constraint_type = KAPI_CONSTRAINT_NONE, \
+	KAPI_PARAM_END
+
+#define KAPI_PARAM_SIZE_T(idx, name, desc) \
+	KAPI_PARAM(idx, name, "size_t", desc) \
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN) \
+		KAPI_PARAM_RANGE(0, SIZE_MAX) \
+	KAPI_PARAM_END
+
+/* Common error specifications */
+#define KAPI_ERROR_EBADF(idx) \
+	KAPI_ERROR(idx, -EBADF, "EBADF", "Invalid file descriptor", \
+		   "The file descriptor is not valid or has been closed")
+
+#define KAPI_ERROR_EINVAL(idx, condition) \
+	KAPI_ERROR(idx, -EINVAL, "EINVAL", condition, \
+		   "Invalid argument provided")
+
+#define KAPI_ERROR_ENOMEM(idx) \
+	KAPI_ERROR(idx, -ENOMEM, "ENOMEM", "Insufficient memory", \
+		   "Cannot allocate memory for the operation")
+
+#define KAPI_ERROR_EPERM(idx) \
+	KAPI_ERROR(idx, -EPERM, "EPERM", "Operation not permitted", \
+		   "The calling process does not have the required permissions")
+
+#define KAPI_ERROR_EFAULT(idx) \
+	KAPI_ERROR(idx, -EFAULT, "EFAULT", "Bad address", \
+		   "Invalid user space address provided")
+
+/* Standard return value specifications */
+#define KAPI_RETURN_SUCCESS_ZERO \
+	KAPI_RETURN("long", "0 on success, negative error code on failure") \
+		KAPI_RETURN_SUCCESS(0, "== 0") \
+	KAPI_RETURN_END
+
+#define KAPI_RETURN_FD_SPEC \
+	KAPI_RETURN("long", "File descriptor on success, negative error code on failure") \
+		.check_type = KAPI_RETURN_FD, \
+	KAPI_RETURN_END
+
+#define KAPI_RETURN_COUNT \
+	KAPI_RETURN("long", "Number of bytes processed on success, negative error code on failure") \
+		KAPI_RETURN_SUCCESS(0, ">= 0") \
+	KAPI_RETURN_END
+
+
+/*
+ * Compat syscall support
+ */
+#ifdef CONFIG_COMPAT
+
+#define COMPAT_SYSCALL_DEFINE_SPEC0(name, spec_args) \
+	DEFINE_KERNEL_API_SPEC(compat_sys_##name) \
+		.name = "compat_sys_" #name, \
+		.param_count = 0, \
+		spec_args \
+	KAPI_END_SPEC; \
+	COMPAT_SYSCALL_DEFINE0(name)
+
+#define COMPAT_SYSCALL_DEFINE_SPEC1(name, spec_args, t1, a1) \
+	DEFINE_KERNEL_API_SPEC(compat_sys_##name) \
+		.name = "compat_sys_" #name, \
+		.param_count = 1, \
+		spec_args \
+	KAPI_END_SPEC; \
+	COMPAT_SYSCALL_DEFINE1(name, t1, a1)
+
+#define COMPAT_SYSCALL_DEFINE_SPEC2(name, spec_args, t1, a1, t2, a2) \
+	DEFINE_KERNEL_API_SPEC(compat_sys_##name) \
+		.name = "compat_sys_" #name, \
+		.param_count = 2, \
+		spec_args \
+	KAPI_END_SPEC; \
+	COMPAT_SYSCALL_DEFINE2(name, t1, a1, t2, a2)
+
+#define COMPAT_SYSCALL_DEFINE_SPEC3(name, spec_args, t1, a1, t2, a2, t3, a3) \
+	DEFINE_KERNEL_API_SPEC(compat_sys_##name) \
+		.name = "compat_sys_" #name, \
+		.param_count = 3, \
+		spec_args \
+	KAPI_END_SPEC; \
+	COMPAT_SYSCALL_DEFINE3(name, t1, a1, t2, a2, t3, a3)
+
+#define COMPAT_SYSCALL_DEFINE_SPEC4(name, spec_args, t1, a1, t2, a2, t3, a3, \
+				     t4, a4) \
+	DEFINE_KERNEL_API_SPEC(compat_sys_##name) \
+		.name = "compat_sys_" #name, \
+		.param_count = 4, \
+		spec_args \
+	KAPI_END_SPEC; \
+	COMPAT_SYSCALL_DEFINE4(name, t1, a1, t2, a2, t3, a3, t4, a4)
+
+#define COMPAT_SYSCALL_DEFINE_SPEC5(name, spec_args, t1, a1, t2, a2, t3, a3, \
+				     t4, a4, t5, a5) \
+	DEFINE_KERNEL_API_SPEC(compat_sys_##name) \
+		.name = "compat_sys_" #name, \
+		.param_count = 5, \
+		spec_args \
+	KAPI_END_SPEC; \
+	COMPAT_SYSCALL_DEFINE5(name, t1, a1, t2, a2, t3, a3, t4, a4, t5, a5)
+
+#define COMPAT_SYSCALL_DEFINE_SPEC6(name, spec_args, t1, a1, t2, a2, t3, a3, \
+				     t4, a4, t5, a5, t6, a6) \
+	DEFINE_KERNEL_API_SPEC(compat_sys_##name) \
+		.name = "compat_sys_" #name, \
+		.param_count = 6, \
+		spec_args \
+	KAPI_END_SPEC; \
+	COMPAT_SYSCALL_DEFINE6(name, t1, a1, t2, a2, t3, a3, t4, a4, t5, a5, t6, a6)
+
+#endif /* CONFIG_COMPAT */
+
+#endif /* _LINUX_SYSCALL_API_SPEC_H */
\ No newline at end of file
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index e5603cc91963d..f2951ece2068b 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -87,6 +87,7 @@ struct xattr_args;
 #include <linux/bug.h>
 #include <linux/sem.h>
 #include <asm/siginfo.h>
+#include <linux/syscall_api_spec.h>
 #include <linux/unistd.h>
 #include <linux/quota.h>
 #include <linux/key.h>
diff --git a/init/Kconfig b/init/Kconfig
index af4c2f0854554..7a15248933895 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -2079,6 +2079,8 @@ config TRACEPOINTS
 
 source "kernel/Kconfig.kexec"
 
+source "kernel/api/Kconfig"
+
 endmenu		# General setup
 
 source "arch/Kconfig"
diff --git a/kernel/Makefile b/kernel/Makefile
index 32e80dd626af0..ba94ee4bb2292 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -56,6 +56,7 @@ obj-y += livepatch/
 obj-y += dma/
 obj-y += entry/
 obj-$(CONFIG_MODULES) += module/
+obj-$(CONFIG_KAPI_SPEC) += api/
 
 obj-$(CONFIG_KCMP) += kcmp.o
 obj-$(CONFIG_FREEZER) += freezer.o
diff --git a/kernel/api/Kconfig b/kernel/api/Kconfig
new file mode 100644
index 0000000000000..fde25ec70e134
--- /dev/null
+++ b/kernel/api/Kconfig
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Kernel API Specification Framework Configuration
+#
+
+config KAPI_SPEC
+	bool "Kernel API Specification Framework"
+	help
+	  This option enables the kernel API specification framework,
+	  which provides formal documentation of kernel APIs in both
+	  human and machine-readable formats.
+
+	  The framework allows developers to document APIs inline with
+	  their implementation, including parameter specifications,
+	  return values, error conditions, locking requirements, and
+	  execution context constraints.
+
+	  When enabled, API specifications can be queried at runtime
+	  and exported in various formats (JSON, XML) through debugfs.
+
+	  If unsure, say N.
+
+config KAPI_RUNTIME_CHECKS
+	bool "Runtime API specification checks"
+	depends on KAPI_SPEC
+	depends on DEBUG_KERNEL
+	help
+	  Enable runtime validation of API usage against specifications.
+	  This includes checking execution context requirements, parameter
+	  validation, and lock state verification.
+
+	  This adds overhead and should only be used for debugging and
+	  development. The checks use WARN_ONCE to report violations.
+
+	  If unsure, say N.
diff --git a/kernel/api/Makefile b/kernel/api/Makefile
new file mode 100644
index 0000000000000..4120ded7e5cf1
--- /dev/null
+++ b/kernel/api/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Kernel API Specification Framework
+#
+
+# Core API specification framework
+obj-$(CONFIG_KAPI_SPEC)		+= kernel_api_spec.o
\ No newline at end of file
diff --git a/kernel/api/kernel_api_spec.c b/kernel/api/kernel_api_spec.c
new file mode 100644
index 0000000000000..29c0c84d87f7c
--- /dev/null
+++ b/kernel/api/kernel_api_spec.c
@@ -0,0 +1,1169 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * kernel_api_spec.c - Kernel API Specification Framework Implementation
+ *
+ * Provides runtime support for kernel API specifications including validation,
+ * export to various formats, and querying capabilities.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kernel_api_spec.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/export.h>
+#include <linux/preempt.h>
+#include <linux/hardirq.h>
+#include <linux/file.h>
+#include <linux/fdtable.h>
+#include <linux/uaccess.h>
+#include <linux/limits.h>
+#include <linux/fcntl.h>
+
+/* Section where API specifications are stored */
+extern struct kernel_api_spec __start_kapi_specs[];
+extern struct kernel_api_spec __stop_kapi_specs[];
+
+/* Dynamic API registration */
+static LIST_HEAD(dynamic_api_specs);
+static DEFINE_MUTEX(api_spec_mutex);
+
+struct dynamic_api_spec {
+	struct list_head list;
+	struct kernel_api_spec *spec;
+};
+
+/**
+ * kapi_get_spec - Get API specification by name
+ * @name: Function name to look up
+ *
+ * Return: Pointer to API specification or NULL if not found
+ */
+const struct kernel_api_spec *kapi_get_spec(const char *name)
+{
+	struct kernel_api_spec *spec;
+	struct dynamic_api_spec *dyn_spec;
+
+	/* Search static specifications */
+	for (spec = __start_kapi_specs; spec < __stop_kapi_specs; spec++) {
+		if (strcmp(spec->name, name) == 0)
+			return spec;
+	}
+
+	/* Search dynamic specifications */
+	mutex_lock(&api_spec_mutex);
+	list_for_each_entry(dyn_spec, &dynamic_api_specs, list) {
+		if (strcmp(dyn_spec->spec->name, name) == 0) {
+			mutex_unlock(&api_spec_mutex);
+			return dyn_spec->spec;
+		}
+	}
+	mutex_unlock(&api_spec_mutex);
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(kapi_get_spec);
+
+/**
+ * kapi_register_spec - Register a dynamic API specification
+ * @spec: API specification to register
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int kapi_register_spec(struct kernel_api_spec *spec)
+{
+	struct dynamic_api_spec *dyn_spec;
+
+	if (!spec || !spec->name[0])
+		return -EINVAL;
+
+	/* Check if already exists */
+	if (kapi_get_spec(spec->name))
+		return -EEXIST;
+
+	dyn_spec = kzalloc(sizeof(*dyn_spec), GFP_KERNEL);
+	if (!dyn_spec)
+		return -ENOMEM;
+
+	dyn_spec->spec = spec;
+
+	mutex_lock(&api_spec_mutex);
+	list_add_tail(&dyn_spec->list, &dynamic_api_specs);
+	mutex_unlock(&api_spec_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(kapi_register_spec);
+
+/**
+ * kapi_unregister_spec - Unregister a dynamic API specification
+ * @name: Name of API to unregister
+ */
+void kapi_unregister_spec(const char *name)
+{
+	struct dynamic_api_spec *dyn_spec, *tmp;
+
+	mutex_lock(&api_spec_mutex);
+	list_for_each_entry_safe(dyn_spec, tmp, &dynamic_api_specs, list) {
+		if (strcmp(dyn_spec->spec->name, name) == 0) {
+			list_del(&dyn_spec->list);
+			kfree(dyn_spec);
+			break;
+		}
+	}
+	mutex_unlock(&api_spec_mutex);
+}
+EXPORT_SYMBOL_GPL(kapi_unregister_spec);
+
+/**
+ * param_type_to_string - Convert parameter type to string
+ * @type: Parameter type
+ *
+ * Return: String representation of type
+ */
+static const char *param_type_to_string(enum kapi_param_type type)
+{
+	static const char * const type_names[] = {
+		[KAPI_TYPE_VOID] = "void",
+		[KAPI_TYPE_INT] = "int",
+		[KAPI_TYPE_UINT] = "uint",
+		[KAPI_TYPE_PTR] = "pointer",
+		[KAPI_TYPE_STRUCT] = "struct",
+		[KAPI_TYPE_UNION] = "union",
+		[KAPI_TYPE_ENUM] = "enum",
+		[KAPI_TYPE_FUNC_PTR] = "function_pointer",
+		[KAPI_TYPE_ARRAY] = "array",
+		[KAPI_TYPE_FD] = "file_descriptor",
+		[KAPI_TYPE_USER_PTR] = "user_pointer",
+		[KAPI_TYPE_PATH] = "pathname",
+		[KAPI_TYPE_CUSTOM] = "custom",
+	};
+
+	if (type >= ARRAY_SIZE(type_names))
+		return "unknown";
+
+	return type_names[type];
+}
+
+/**
+ * lock_type_to_string - Convert lock type to string
+ * @type: Lock type
+ *
+ * Return: String representation of lock type
+ */
+static const char *lock_type_to_string(enum kapi_lock_type type)
+{
+	static const char * const lock_names[] = {
+		[KAPI_LOCK_NONE] = "none",
+		[KAPI_LOCK_MUTEX] = "mutex",
+		[KAPI_LOCK_SPINLOCK] = "spinlock",
+		[KAPI_LOCK_RWLOCK] = "rwlock",
+		[KAPI_LOCK_SEQLOCK] = "seqlock",
+		[KAPI_LOCK_RCU] = "rcu",
+		[KAPI_LOCK_SEMAPHORE] = "semaphore",
+		[KAPI_LOCK_CUSTOM] = "custom",
+	};
+
+	if (type >= ARRAY_SIZE(lock_names))
+		return "unknown";
+
+	return lock_names[type];
+}
+
+/**
+ * return_check_type_to_string - Convert return check type to string
+ * @type: Return check type
+ *
+ * Return: String representation of return check type
+ */
+static const char *return_check_type_to_string(enum kapi_return_check_type type)
+{
+	static const char * const check_names[] = {
+		[KAPI_RETURN_EXACT] = "exact",
+		[KAPI_RETURN_RANGE] = "range",
+		[KAPI_RETURN_ERROR_CHECK] = "error_check",
+		[KAPI_RETURN_FD] = "file_descriptor",
+		[KAPI_RETURN_CUSTOM] = "custom",
+	};
+
+	if (type >= ARRAY_SIZE(check_names))
+		return "unknown";
+
+	return check_names[type];
+}
+
+/**
+ * kapi_export_json - Export API specification to JSON format
+ * @spec: API specification to export
+ * @buf: Buffer to write JSON to
+ * @size: Size of buffer
+ *
+ * Return: Number of bytes written or negative error
+ */
+int kapi_export_json(const struct kernel_api_spec *spec, char *buf, size_t size)
+{
+	int ret = 0;
+	int i;
+
+	if (!spec || !buf || size == 0)
+		return -EINVAL;
+
+	ret = scnprintf(buf, size,
+		"{\n"
+		"  \"name\": \"%s\",\n"
+		"  \"version\": %u,\n"
+		"  \"description\": \"%s\",\n"
+		"  \"long_description\": \"%s\",\n"
+		"  \"context_flags\": \"0x%x\",\n",
+		spec->name,
+		spec->version,
+		spec->description,
+		spec->long_description,
+		spec->context_flags);
+
+	/* Parameters */
+	ret += scnprintf(buf + ret, size - ret,
+		"  \"parameters\": [\n");
+
+	for (i = 0; i < spec->param_count && i < KAPI_MAX_PARAMS; i++) {
+		const struct kapi_param_spec *param = &spec->params[i];
+
+		ret += scnprintf(buf + ret, size - ret,
+			"    {\n"
+			"      \"name\": \"%s\",\n"
+			"      \"type\": \"%s\",\n"
+			"      \"type_class\": \"%s\",\n"
+			"      \"flags\": \"0x%x\",\n"
+			"      \"description\": \"%s\"\n"
+			"    }%s\n",
+			param->name,
+			param->type_name,
+			param_type_to_string(param->type),
+			param->flags,
+			param->description,
+			(i < spec->param_count - 1) ? "," : "");
+	}
+
+	ret += scnprintf(buf + ret, size - ret, "  ],\n");
+
+	/* Return value */
+	ret += scnprintf(buf + ret, size - ret,
+		"  \"return\": {\n"
+		"    \"type\": \"%s\",\n"
+		"    \"type_class\": \"%s\",\n"
+		"    \"check_type\": \"%s\",\n",
+		spec->return_spec.type_name,
+		param_type_to_string(spec->return_spec.type),
+		return_check_type_to_string(spec->return_spec.check_type));
+
+	switch (spec->return_spec.check_type) {
+	case KAPI_RETURN_EXACT:
+		ret += scnprintf(buf + ret, size - ret,
+			"    \"success_value\": %lld,\n",
+			spec->return_spec.success_value);
+		break;
+	case KAPI_RETURN_RANGE:
+		ret += scnprintf(buf + ret, size - ret,
+			"    \"success_min\": %lld,\n"
+			"    \"success_max\": %lld,\n",
+			spec->return_spec.success_min,
+			spec->return_spec.success_max);
+		break;
+	case KAPI_RETURN_ERROR_CHECK:
+		ret += scnprintf(buf + ret, size - ret,
+			"    \"error_count\": %u,\n",
+			spec->return_spec.error_count);
+		break;
+	default:
+		break;
+	}
+
+	ret += scnprintf(buf + ret, size - ret,
+		"    \"description\": \"%s\"\n"
+		"  },\n",
+		spec->return_spec.description);
+
+	/* Errors */
+	ret += scnprintf(buf + ret, size - ret,
+		"  \"errors\": [\n");
+
+	for (i = 0; i < spec->error_count && i < KAPI_MAX_ERRORS; i++) {
+		const struct kapi_error_spec *error = &spec->errors[i];
+
+		ret += scnprintf(buf + ret, size - ret,
+			"    {\n"
+			"      \"code\": %d,\n"
+			"      \"name\": \"%s\",\n"
+			"      \"condition\": \"%s\",\n"
+			"      \"description\": \"%s\"\n"
+			"    }%s\n",
+			error->error_code,
+			error->name,
+			error->condition,
+			error->description,
+			(i < spec->error_count - 1) ? "," : "");
+	}
+
+	ret += scnprintf(buf + ret, size - ret, "  ],\n");
+
+	/* Locks */
+	ret += scnprintf(buf + ret, size - ret,
+		"  \"locks\": [\n");
+
+	for (i = 0; i < spec->lock_count && i < KAPI_MAX_CONSTRAINTS; i++) {
+		const struct kapi_lock_spec *lock = &spec->locks[i];
+
+		ret += scnprintf(buf + ret, size - ret,
+			"    {\n"
+			"      \"name\": \"%s\",\n"
+			"      \"type\": \"%s\",\n"
+			"      \"acquired\": %s,\n"
+			"      \"released\": %s,\n"
+			"      \"held_on_entry\": %s,\n"
+			"      \"held_on_exit\": %s,\n"
+			"      \"description\": \"%s\"\n"
+			"    }%s\n",
+			lock->lock_name,
+			lock_type_to_string(lock->lock_type),
+			lock->acquired ? "true" : "false",
+			lock->released ? "true" : "false",
+			lock->held_on_entry ? "true" : "false",
+			lock->held_on_exit ? "true" : "false",
+			lock->description,
+			(i < spec->lock_count - 1) ? "," : "");
+	}
+
+	ret += scnprintf(buf + ret, size - ret, "  ],\n");
+
+	/* Additional info */
+	ret += scnprintf(buf + ret, size - ret,
+		"  \"since_version\": \"%s\",\n"
+		"  \"deprecated\": %s,\n"
+		"  \"replacement\": \"%s\",\n"
+		"  \"examples\": \"%s\",\n"
+		"  \"notes\": \"%s\"\n"
+		"}\n",
+		spec->since_version,
+		spec->deprecated ? "true" : "false",
+		spec->replacement,
+		spec->examples,
+		spec->notes);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(kapi_export_json);
+
+/**
+ * kapi_export_xml - Export API specification to XML format
+ * @spec: API specification to export
+ * @buf: Buffer to write XML to
+ * @size: Size of buffer
+ *
+ * Return: Number of bytes written or negative error
+ */
+int kapi_export_xml(const struct kernel_api_spec *spec, char *buf, size_t size)
+{
+	int ret = 0;
+	int i;
+
+	if (!spec || !buf || size == 0)
+		return -EINVAL;
+
+	ret = scnprintf(buf, size,
+		"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+		"<kernel_api>\n"
+		"  <name>%s</name>\n"
+		"  <version>%u</version>\n"
+		"  <description>%s</description>\n"
+		"  <long_description><![CDATA[%s]]></long_description>\n"
+		"  <context_flags>0x%x</context_flags>\n",
+		spec->name,
+		spec->version,
+		spec->description,
+		spec->long_description,
+		spec->context_flags);
+
+	/* Parameters */
+	ret += scnprintf(buf + ret, size - ret, "  <parameters>\n");
+
+	for (i = 0; i < spec->param_count && i < KAPI_MAX_PARAMS; i++) {
+		const struct kapi_param_spec *param = &spec->params[i];
+
+		ret += scnprintf(buf + ret, size - ret,
+			"    <parameter>\n"
+			"      <name>%s</name>\n"
+			"      <type>%s</type>\n"
+			"      <type_class>%s</type_class>\n"
+			"      <flags>0x%x</flags>\n"
+			"      <description><![CDATA[%s]]></description>\n"
+			"    </parameter>\n",
+			param->name,
+			param->type_name,
+			param_type_to_string(param->type),
+			param->flags,
+			param->description);
+	}
+
+	ret += scnprintf(buf + ret, size - ret, "  </parameters>\n");
+
+	/* Return value */
+	ret += scnprintf(buf + ret, size - ret,
+		"  <return>\n"
+		"    <type>%s</type>\n"
+		"    <type_class>%s</type_class>\n"
+		"    <check_type>%s</check_type>\n",
+		spec->return_spec.type_name,
+		param_type_to_string(spec->return_spec.type),
+		return_check_type_to_string(spec->return_spec.check_type));
+
+	switch (spec->return_spec.check_type) {
+	case KAPI_RETURN_EXACT:
+		ret += scnprintf(buf + ret, size - ret,
+			"    <success_value>%lld</success_value>\n",
+			spec->return_spec.success_value);
+		break;
+	case KAPI_RETURN_RANGE:
+		ret += scnprintf(buf + ret, size - ret,
+			"    <success_min>%lld</success_min>\n"
+			"    <success_max>%lld</success_max>\n",
+			spec->return_spec.success_min,
+			spec->return_spec.success_max);
+		break;
+	case KAPI_RETURN_ERROR_CHECK:
+		ret += scnprintf(buf + ret, size - ret,
+			"    <error_count>%u</error_count>\n",
+			spec->return_spec.error_count);
+		break;
+	default:
+		break;
+	}
+
+	ret += scnprintf(buf + ret, size - ret,
+		"    <description><![CDATA[%s]]></description>\n"
+		"  </return>\n",
+		spec->return_spec.description);
+
+	/* Errors */
+	ret += scnprintf(buf + ret, size - ret, "  <errors>\n");
+
+	for (i = 0; i < spec->error_count && i < KAPI_MAX_ERRORS; i++) {
+		const struct kapi_error_spec *error = &spec->errors[i];
+
+		ret += scnprintf(buf + ret, size - ret,
+			"    <error>\n"
+			"      <code>%d</code>\n"
+			"      <name>%s</name>\n"
+			"      <condition><![CDATA[%s]]></condition>\n"
+			"      <description><![CDATA[%s]]></description>\n"
+			"    </error>\n",
+			error->error_code,
+			error->name,
+			error->condition,
+			error->description);
+	}
+
+	ret += scnprintf(buf + ret, size - ret, "  </errors>\n");
+
+	/* Additional info */
+	ret += scnprintf(buf + ret, size - ret,
+		"  <since_version>%s</since_version>\n"
+		"  <deprecated>%s</deprecated>\n"
+		"  <replacement>%s</replacement>\n"
+		"  <examples><![CDATA[%s]]></examples>\n"
+		"  <notes><![CDATA[%s]]></notes>\n"
+		"</kernel_api>\n",
+		spec->since_version,
+		spec->deprecated ? "true" : "false",
+		spec->replacement,
+		spec->examples,
+		spec->notes);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(kapi_export_xml);
+
+/**
+ * kapi_print_spec - Print API specification to kernel log
+ * @spec: API specification to print
+ */
+void kapi_print_spec(const struct kernel_api_spec *spec)
+{
+	int i;
+
+	if (!spec)
+		return;
+
+	pr_info("=== Kernel API Specification ===\n");
+	pr_info("Name: %s\n", spec->name);
+	pr_info("Version: %u\n", spec->version);
+	pr_info("Description: %s\n", spec->description);
+
+	if (spec->long_description[0])
+		pr_info("Long Description: %s\n", spec->long_description);
+
+	pr_info("Context Flags: 0x%x\n", spec->context_flags);
+
+	/* Parameters */
+	if (spec->param_count > 0) {
+		pr_info("Parameters:\n");
+		for (i = 0; i < spec->param_count && i < KAPI_MAX_PARAMS; i++) {
+			const struct kapi_param_spec *param = &spec->params[i];
+			pr_info("  [%d] %s: %s (flags: 0x%x)\n",
+				i, param->name, param->type_name, param->flags);
+			if (param->description[0])
+				pr_info("      Description: %s\n", param->description);
+		}
+	}
+
+	/* Return value */
+	pr_info("Return: %s\n", spec->return_spec.type_name);
+	if (spec->return_spec.description[0])
+		pr_info("  Description: %s\n", spec->return_spec.description);
+
+	/* Errors */
+	if (spec->error_count > 0) {
+		pr_info("Possible Errors:\n");
+		for (i = 0; i < spec->error_count && i < KAPI_MAX_ERRORS; i++) {
+			const struct kapi_error_spec *error = &spec->errors[i];
+			pr_info("  %s (%d): %s\n",
+				error->name, error->error_code, error->condition);
+		}
+	}
+
+	pr_info("================================\n");
+}
+EXPORT_SYMBOL_GPL(kapi_print_spec);
+
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+
+/**
+ * kapi_validate_fd - Validate that a file descriptor is valid in current context
+ * @fd: File descriptor to validate
+ *
+ * Return: true if fd is valid in current process context, false otherwise
+ */
+static bool kapi_validate_fd(int fd)
+{
+	struct fd f;
+
+	/* Special case: AT_FDCWD is always valid */
+	if (fd == AT_FDCWD)
+		return true;
+
+	/* Check basic range */
+	if (fd < 0)
+		return false;
+
+	/* Check if fd is valid in current process context */
+	f = fdget(fd);
+	if (fd_empty(f)) {
+		return false;
+	}
+
+	/* fd is valid, release reference */
+	fdput(f);
+	return true;
+}
+
+/**
+ * kapi_validate_user_ptr - Validate that a user pointer is accessible
+ * @ptr: User pointer to validate
+ * @size: Size in bytes to validate
+ * @write: Whether write access is required
+ *
+ * Return: true if user memory is accessible, false otherwise
+ */
+static bool kapi_validate_user_ptr(const void __user *ptr, size_t size, bool write)
+{
+	/* NULL is valid if parameter is marked optional */
+	if (!ptr)
+		return false;
+
+	/* Check if the user memory region is accessible */
+	if (write) {
+		return access_ok(ptr, size);
+	} else {
+		return access_ok(ptr, size);
+	}
+}
+
+/**
+ * kapi_validate_user_ptr_with_params - Validate user pointer with dynamic size
+ * @param_spec: Parameter specification
+ * @ptr: User pointer to validate
+ * @all_params: Array of all parameter values
+ * @param_count: Number of parameters
+ *
+ * Return: true if user memory is accessible, false otherwise
+ */
+static bool kapi_validate_user_ptr_with_params(const struct kapi_param_spec *param_spec,
+						const void __user *ptr,
+						const s64 *all_params,
+						int param_count)
+{
+	size_t actual_size;
+	bool write;
+
+	/* NULL is allowed for optional parameters */
+	if (!ptr && (param_spec->flags & KAPI_PARAM_OPTIONAL))
+		return true;
+
+	/* Calculate actual size based on related parameter */
+	if (param_spec->size_param_idx >= 0 &&
+	    param_spec->size_param_idx < param_count) {
+		s64 count = all_params[param_spec->size_param_idx];
+
+		/* Validate count is positive */
+		if (count <= 0) {
+			pr_warn("Parameter %s: size determinant is non-positive (%lld)\n",
+				param_spec->name, count);
+			return false;
+		}
+
+		/* Check for multiplication overflow */
+		if (param_spec->size_multiplier > 0 &&
+		    count > SIZE_MAX / param_spec->size_multiplier) {
+			pr_warn("Parameter %s: size calculation overflow\n",
+				param_spec->name);
+			return false;
+		}
+
+		actual_size = count * param_spec->size_multiplier;
+	} else {
+		/* Use fixed size */
+		actual_size = param_spec->size;
+	}
+
+	write = (param_spec->flags & KAPI_PARAM_OUT) ||
+		(param_spec->flags & KAPI_PARAM_INOUT);
+
+	return kapi_validate_user_ptr(ptr, actual_size, write);
+}
+
+/**
+ * kapi_validate_path - Validate that a pathname is accessible and within limits
+ * @path: User pointer to pathname
+ * @param_spec: Parameter specification
+ *
+ * Return: true if path is valid, false otherwise
+ */
+static bool kapi_validate_path(const char __user *path,
+				const struct kapi_param_spec *param_spec)
+{
+	size_t len;
+
+	/* NULL is allowed for optional parameters */
+	if (!path && (param_spec->flags & KAPI_PARAM_OPTIONAL))
+		return true;
+
+	if (!path) {
+		pr_warn("Parameter %s: NULL path not allowed\n", param_spec->name);
+		return false;
+	}
+
+	/* Check if the path is accessible */
+	if (!access_ok(path, 1)) {
+		pr_warn("Parameter %s: path pointer %p not accessible\n",
+			param_spec->name, path);
+		return false;
+	}
+
+	/* Use strnlen_user to get the length and validate accessibility */
+	len = strnlen_user(path, PATH_MAX + 1);
+	if (len == 0) {
+		pr_warn("Parameter %s: invalid path pointer %p\n",
+			param_spec->name, path);
+		return false;
+	}
+
+	/* Check path length limit */
+	if (len > PATH_MAX) {
+		pr_warn("Parameter %s: path too long (exceeds PATH_MAX)\n",
+			param_spec->name);
+		return false;
+	}
+
+	return true;
+}
+
+/**
+ * kapi_validate_param - Validate a parameter against its specification
+ * @param_spec: Parameter specification
+ * @value: Parameter value to validate
+ *
+ * Return: true if valid, false otherwise
+ */
+bool kapi_validate_param(const struct kapi_param_spec *param_spec, s64 value)
+{
+	int i;
+
+	/* Special handling for file descriptor type */
+	if (param_spec->type == KAPI_TYPE_FD) {
+		if (!kapi_validate_fd((int)value)) {
+			pr_warn("Parameter %s: invalid file descriptor %lld\n",
+				param_spec->name, value);
+			return false;
+		}
+		/* Continue with additional constraint checks if needed */
+	}
+
+	/* Special handling for user pointer type */
+	if (param_spec->type == KAPI_TYPE_USER_PTR) {
+		const void __user *ptr = (const void __user *)value;
+		bool write = (param_spec->flags & KAPI_PARAM_OUT) ||
+			     (param_spec->flags & KAPI_PARAM_INOUT);
+
+		/* NULL is allowed for optional parameters */
+		if (!ptr && (param_spec->flags & KAPI_PARAM_OPTIONAL))
+			return true;
+
+		if (!kapi_validate_user_ptr(ptr, param_spec->size, write)) {
+			pr_warn("Parameter %s: invalid user pointer %p (size: %zu, %s)\n",
+				param_spec->name, ptr, param_spec->size,
+				write ? "write" : "read");
+			return false;
+		}
+		/* Continue with additional constraint checks if needed */
+	}
+
+	/* Special handling for path type */
+	if (param_spec->type == KAPI_TYPE_PATH) {
+		const char __user *path = (const char __user *)value;
+
+		if (!kapi_validate_path(path, param_spec)) {
+			return false;
+		}
+		/* Continue with additional constraint checks if needed */
+	}
+
+	switch (param_spec->constraint_type) {
+	case KAPI_CONSTRAINT_NONE:
+		return true;
+
+	case KAPI_CONSTRAINT_RANGE:
+		if (value < param_spec->min_value || value > param_spec->max_value) {
+			pr_warn("Parameter %s value %lld out of range [%lld, %lld]\n",
+				param_spec->name, value,
+				param_spec->min_value, param_spec->max_value);
+			return false;
+		}
+		return true;
+
+	case KAPI_CONSTRAINT_MASK:
+		if (value & ~param_spec->valid_mask) {
+			pr_warn("Parameter %s value 0x%llx contains invalid bits (valid mask: 0x%llx)\n",
+				param_spec->name, value, param_spec->valid_mask);
+			return false;
+		}
+		return true;
+
+	case KAPI_CONSTRAINT_ENUM:
+		if (!param_spec->enum_values || param_spec->enum_count == 0)
+			return true;
+
+		for (i = 0; i < param_spec->enum_count; i++) {
+			if (value == param_spec->enum_values[i])
+				return true;
+		}
+		pr_warn("Parameter %s value %lld not in valid enumeration\n",
+			param_spec->name, value);
+		return false;
+
+	case KAPI_CONSTRAINT_CUSTOM:
+		if (param_spec->validate)
+			return param_spec->validate(value);
+		return true;
+
+	default:
+		return true;
+	}
+}
+EXPORT_SYMBOL_GPL(kapi_validate_param);
+
+/**
+ * kapi_validate_param_with_context - Validate parameter with access to all params
+ * @param_spec: Parameter specification
+ * @value: Parameter value to validate
+ * @all_params: Array of all parameter values
+ * @param_count: Number of parameters
+ *
+ * Return: true if valid, false otherwise
+ */
+bool kapi_validate_param_with_context(const struct kapi_param_spec *param_spec,
+				       s64 value, const s64 *all_params, int param_count)
+{
+	/* Special handling for user pointer type with dynamic sizing */
+	if (param_spec->type == KAPI_TYPE_USER_PTR) {
+		const void __user *ptr = (const void __user *)value;
+
+		/* NULL is allowed for optional parameters */
+		if (!ptr && (param_spec->flags & KAPI_PARAM_OPTIONAL))
+			return true;
+
+		if (!kapi_validate_user_ptr_with_params(param_spec, ptr, all_params, param_count)) {
+			pr_warn("Parameter %s: invalid user pointer %p\n",
+				param_spec->name, ptr);
+			return false;
+		}
+		/* Continue with additional constraint checks if needed */
+	}
+
+	/* For other types, fall back to regular validation */
+	return kapi_validate_param(param_spec, value);
+}
+EXPORT_SYMBOL_GPL(kapi_validate_param_with_context);
+
+/**
+ * kapi_validate_syscall_param - Validate syscall parameter with enforcement
+ * @spec: API specification
+ * @param_idx: Parameter index
+ * @value: Parameter value
+ *
+ * Return: -EINVAL if invalid, 0 if valid
+ */
+int kapi_validate_syscall_param(const struct kernel_api_spec *spec,
+				 int param_idx, s64 value)
+{
+	const struct kapi_param_spec *param_spec;
+
+	if (!spec || param_idx >= spec->param_count)
+		return 0;
+
+	param_spec = &spec->params[param_idx];
+
+	if (!kapi_validate_param(param_spec, value)) {
+		if (strncmp(spec->name, "sys_", 4) == 0) {
+			/* For syscalls, we can return EINVAL to userspace */
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(kapi_validate_syscall_param);
+
+/**
+ * kapi_validate_syscall_params - Validate all syscall parameters together
+ * @spec: API specification
+ * @params: Array of parameter values
+ * @param_count: Number of parameters
+ *
+ * Return: -EINVAL if any parameter is invalid, 0 if all valid
+ */
+int kapi_validate_syscall_params(const struct kernel_api_spec *spec,
+				 const s64 *params, int param_count)
+{
+	int i;
+
+	if (!spec || !params)
+		return 0;
+
+	/* Validate that we have the expected number of parameters */
+	if (param_count != spec->param_count) {
+		pr_warn("API %s: parameter count mismatch (expected %u, got %d)\n",
+			spec->name, spec->param_count, param_count);
+		return -EINVAL;
+	}
+
+	/* Validate each parameter with context */
+	for (i = 0; i < spec->param_count && i < KAPI_MAX_PARAMS; i++) {
+		const struct kapi_param_spec *param_spec = &spec->params[i];
+
+		if (!kapi_validate_param_with_context(param_spec, params[i], params, param_count)) {
+			if (strncmp(spec->name, "sys_", 4) == 0) {
+				/* For syscalls, we can return EINVAL to userspace */
+				return -EINVAL;
+			}
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(kapi_validate_syscall_params);
+
+/**
+ * kapi_check_return_success - Check if return value indicates success
+ * @return_spec: Return specification
+ * @retval: Return value to check
+ *
+ * Returns true if the return value indicates success according to the spec.
+ */
+bool kapi_check_return_success(const struct kapi_return_spec *return_spec, s64 retval)
+{
+	u32 i;
+
+	if (!return_spec)
+		return true; /* No spec means we can't validate */
+
+	switch (return_spec->check_type) {
+	case KAPI_RETURN_EXACT:
+		return retval == return_spec->success_value;
+
+	case KAPI_RETURN_RANGE:
+		return retval >= return_spec->success_min &&
+		       retval <= return_spec->success_max;
+
+	case KAPI_RETURN_ERROR_CHECK:
+		/* Success if NOT in error list */
+		if (return_spec->error_values) {
+			for (i = 0; i < return_spec->error_count; i++) {
+				if (retval == return_spec->error_values[i])
+					return false; /* Found in error list */
+			}
+		}
+		return true; /* Not in error list = success */
+
+	case KAPI_RETURN_FD:
+		/* File descriptors: >= 0 is success, < 0 is error */
+		return retval >= 0;
+
+	case KAPI_RETURN_CUSTOM:
+		if (return_spec->is_success)
+			return return_spec->is_success(retval);
+		fallthrough;
+
+	default:
+		return true; /* Unknown check type, assume success */
+	}
+}
+EXPORT_SYMBOL_GPL(kapi_check_return_success);
+
+/**
+ * kapi_validate_return_value - Validate that return value matches spec
+ * @spec: API specification
+ * @retval: Return value to validate
+ *
+ * Return: true if return value is valid according to spec, false otherwise.
+ *
+ * This function checks:
+ * 1. If the value indicates success, it must match the success criteria
+ * 2. If the value indicates error, it must be one of the specified error codes
+ */
+bool kapi_validate_return_value(const struct kernel_api_spec *spec, s64 retval)
+{
+	int i;
+	bool is_success;
+
+	if (!spec)
+		return true; /* No spec means we can't validate */
+
+	/* First check if this is a success return */
+	is_success = kapi_check_return_success(&spec->return_spec, retval);
+
+	if (is_success) {
+		/* Success case - already validated by kapi_check_return_success */
+		return true;
+	}
+
+	/* Special validation for file descriptor returns */
+	if (spec->return_spec.check_type == KAPI_RETURN_FD && is_success) {
+		/* For successful FD returns, validate it's a valid FD */
+		if (!kapi_validate_fd((int)retval)) {
+			pr_warn("API %s returned invalid file descriptor %lld\n",
+				spec->name, retval);
+			return false;
+		}
+		return true;
+	}
+
+	/* Error case - check if it's one of the specified errors */
+	if (spec->error_count == 0) {
+		/* No errors specified, so any error is potentially valid */
+		pr_debug("API %s returned unspecified error %lld\n",
+			 spec->name, retval);
+		return true;
+	}
+
+	/* Check if the error is in our list of specified errors */
+	for (i = 0; i < spec->error_count && i < KAPI_MAX_ERRORS; i++) {
+		if (retval == spec->errors[i].error_code)
+			return true;
+	}
+
+	/* Error not in spec */
+	pr_warn("API %s returned unspecified error code %lld. Valid errors are:\n",
+		spec->name, retval);
+	for (i = 0; i < spec->error_count && i < KAPI_MAX_ERRORS; i++) {
+		pr_warn("  %s (%d): %s\n",
+			spec->errors[i].name,
+			spec->errors[i].error_code,
+			spec->errors[i].condition);
+	}
+
+	return false;
+}
+EXPORT_SYMBOL_GPL(kapi_validate_return_value);
+
+/**
+ * kapi_validate_syscall_return - Validate syscall return value with enforcement
+ * @spec: API specification
+ * @retval: Return value
+ *
+ * Return: 0 if valid, -EINVAL if the return value doesn't match spec
+ *
+ * For syscalls, this can help detect kernel bugs where unspecified error
+ * codes are returned to userspace.
+ */
+int kapi_validate_syscall_return(const struct kernel_api_spec *spec, s64 retval)
+{
+	if (!spec)
+		return 0;
+
+	if (!kapi_validate_return_value(spec, retval)) {
+		/* Log the violation but don't change the return value */
+		WARN_ONCE(1, "Syscall %s returned unspecified value %lld\n",
+			  spec->name, retval);
+		/* Could return -EINVAL here to enforce, but that might break userspace */
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(kapi_validate_syscall_return);
+
+/**
+ * kapi_check_context - Check if current context matches API requirements
+ * @spec: API specification to check against
+ */
+void kapi_check_context(const struct kernel_api_spec *spec)
+{
+	u32 ctx = spec->context_flags;
+	bool valid = false;
+
+	if (!ctx)
+		return;
+
+	/* Check if we're in an allowed context */
+	if ((ctx & KAPI_CTX_PROCESS) && !in_interrupt())
+		valid = true;
+
+	if ((ctx & KAPI_CTX_SOFTIRQ) && in_softirq())
+		valid = true;
+
+	if ((ctx & KAPI_CTX_HARDIRQ) && in_hardirq())
+		valid = true;
+
+	if ((ctx & KAPI_CTX_NMI) && in_nmi())
+		valid = true;
+
+	if (!valid) {
+		WARN_ONCE(1, "API %s called from invalid context\n", spec->name);
+	}
+
+	/* Check specific requirements */
+	if ((ctx & KAPI_CTX_ATOMIC) && preemptible()) {
+		WARN_ONCE(1, "API %s requires atomic context\n", spec->name);
+	}
+
+	if ((ctx & KAPI_CTX_SLEEPABLE) && !preemptible()) {
+		WARN_ONCE(1, "API %s requires sleepable context\n", spec->name);
+	}
+}
+EXPORT_SYMBOL_GPL(kapi_check_context);
+
+#endif /* CONFIG_KAPI_RUNTIME_CHECKS */
+
+/* DebugFS interface */
+#ifdef CONFIG_DEBUG_FS
+
+static struct dentry *kapi_debugfs_root;
+
+static int kapi_spec_show(struct seq_file *s, void *v)
+{
+	struct kernel_api_spec *spec = s->private;
+	char *buf;
+	int ret;
+
+	buf = kmalloc(PAGE_SIZE * 4, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = kapi_export_json(spec, buf, PAGE_SIZE * 4);
+	if (ret > 0)
+		seq_printf(s, "%s", buf);
+
+	kfree(buf);
+	return 0;
+}
+
+static int kapi_spec_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, kapi_spec_show, inode->i_private);
+}
+
+static const struct file_operations kapi_spec_fops = {
+	.open = kapi_spec_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int kapi_list_show(struct seq_file *s, void *v)
+{
+	struct kernel_api_spec *spec;
+	struct dynamic_api_spec *dyn_spec;
+
+	seq_printf(s, "Kernel API Specifications:\n\n");
+
+	/* List static specifications */
+	seq_printf(s, "Static APIs:\n");
+	for (spec = __start_kapi_specs; spec < __stop_kapi_specs; spec++) {
+		seq_printf(s, "  %s (v%u): %s\n",
+			spec->name, spec->version, spec->description);
+	}
+
+	/* List dynamic specifications */
+	seq_printf(s, "\nDynamic APIs:\n");
+	mutex_lock(&api_spec_mutex);
+	list_for_each_entry(dyn_spec, &dynamic_api_specs, list) {
+		spec = dyn_spec->spec;
+		seq_printf(s, "  %s (v%u): %s\n",
+			spec->name, spec->version, spec->description);
+	}
+	mutex_unlock(&api_spec_mutex);
+
+	return 0;
+}
+
+static int kapi_list_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, kapi_list_show, NULL);
+}
+
+static const struct file_operations kapi_list_fops = {
+	.open = kapi_list_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int __init kapi_debugfs_init(void)
+{
+	struct kernel_api_spec *spec;
+	struct dentry *spec_dir;
+
+	kapi_debugfs_root = debugfs_create_dir("kapi", NULL);
+	if (!kapi_debugfs_root)
+		return -ENOMEM;
+
+	/* Create list file */
+	debugfs_create_file("list", 0444, kapi_debugfs_root, NULL,
+			    &kapi_list_fops);
+
+	/* Create directory for specifications */
+	spec_dir = debugfs_create_dir("specs", kapi_debugfs_root);
+
+	/* Create files for each static specification */
+	for (spec = __start_kapi_specs; spec < __stop_kapi_specs; spec++) {
+		debugfs_create_file(spec->name, 0444, spec_dir, spec,
+				    &kapi_spec_fops);
+	}
+
+	return 0;
+}
+
+late_initcall(kapi_debugfs_init);
+
+#endif /* CONFIG_DEBUG_FS */
\ No newline at end of file
-- 
2.39.5


^ permalink raw reply related

* [RFC 02/19] eventpoll: add API specification for epoll_create1
From: Sasha Levin @ 2025-06-14 13:48 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-api, workflows, tools, Sasha Levin
In-Reply-To: <20250614134858.790460-1-sashal@kernel.org>

Add kernel API specification for the epoll_create1() system call.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 fs/eventpoll.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 86 insertions(+)

diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index d4dbffdedd08e..8f8a64ebbaef6 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -21,6 +21,7 @@
 #include <linux/hash.h>
 #include <linux/spinlock.h>
 #include <linux/syscalls.h>
+#include <linux/syscall_api_spec.h>
 #include <linux/rbtree.h>
 #include <linux/wait.h>
 #include <linux/eventpoll.h>
@@ -2265,6 +2266,91 @@ static int do_epoll_create(int flags)
 	return error;
 }
 
+
+/* Valid values for epoll_create1 flags parameter */
+static const s64 epoll_create1_valid_values[] = { 0, EPOLL_CLOEXEC };
+
+DEFINE_KERNEL_API_SPEC(sys_epoll_create1)
+	KAPI_DESCRIPTION("Create an epoll instance")
+	KAPI_LONG_DESC("Creates a new epoll instance and returns a file descriptor "
+		       "referring to that instance. The file descriptor is used for all "
+		       "subsequent calls to the epoll interface.")
+	KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
+
+	KAPI_PARAM(0, "flags", "int", "Creation flags for the epoll instance")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.constraint_type = KAPI_CONSTRAINT_ENUM,
+		.enum_values = epoll_create1_valid_values,
+		.enum_count = ARRAY_SIZE(epoll_create1_valid_values),
+		.constraints = "Must be 0 or EPOLL_CLOEXEC",
+	KAPI_PARAM_END
+
+	KAPI_RETURN("long", "File descriptor on success, negative error code on failure")
+		.type = KAPI_TYPE_INT,
+		.check_type = KAPI_RETURN_FD,
+	KAPI_RETURN_END
+
+	KAPI_ERROR(0, -EINVAL, "EINVAL", "Invalid flags specified",
+		   "The flags parameter contains invalid values. Only EPOLL_CLOEXEC is allowed.")
+	KAPI_ERROR(1, -EMFILE, "EMFILE", "Per-process file descriptor limit reached",
+		   "The per-process limit on the number of open file descriptors has been reached.")
+	KAPI_ERROR(2, -ENFILE, "ENFILE", "System file table overflow",
+		   "The system-wide limit on the total number of open files has been reached.")
+	KAPI_ERROR(3, -ENOMEM, "ENOMEM", "Insufficient kernel memory",
+		   "There was insufficient kernel memory to create the epoll instance.")
+	KAPI_ERROR(4, -EINTR, "EINTR", "Interrupted by signal",
+		   "The system call was interrupted by a signal before the epoll instance could be created.")
+
+	.error_count = 5,
+	.param_count = 1,
+	.since_version = "2.6.27",
+	.examples = "int epfd = epoll_create1(EPOLL_CLOEXEC);",
+	.notes = "EPOLL_CLOEXEC sets the close-on-exec (FD_CLOEXEC) flag on the new file descriptor. "
+		 "When all file descriptors referring to an epoll instance are closed, the kernel "
+		 "destroys the instance and releases associated resources.",
+
+	/* Side effects */
+	KAPI_SIDE_EFFECT(0, KAPI_EFFECT_RESOURCE_CREATE | KAPI_EFFECT_ALLOC_MEMORY,
+			 "epoll instance",
+			 "Creates a new epoll instance and allocates kernel memory for it")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(1, KAPI_EFFECT_RESOURCE_CREATE,
+			 "file descriptor",
+			 "Allocates a new file descriptor in the process's file descriptor table")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT_COUNT(2)
+
+	/* State transitions */
+	KAPI_STATE_TRANS(0, "epoll instance", "non-existent", "created and empty",
+			 "A new epoll instance is created with no monitored file descriptors")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS_COUNT(1)
+
+	/* Signal specifications */
+	KAPI_SIGNAL(0, SIGINT, "SIGINT", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN)
+		KAPI_SIGNAL_CONDITION("During creation if process receives SIGINT")
+		KAPI_SIGNAL_DESC("If interrupted during kernel memory allocation, returns -EINTR")
+		KAPI_SIGNAL_RESTARTABLE
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(1, SIGTERM, "SIGTERM", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN)
+		KAPI_SIGNAL_CONDITION("During creation if process receives SIGTERM")
+		KAPI_SIGNAL_DESC("If interrupted during kernel memory allocation, returns -EINTR")
+		KAPI_SIGNAL_RESTARTABLE
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(2, SIGKILL, "SIGKILL", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_TERMINATE)
+		KAPI_SIGNAL_CONDITION("At any point during the syscall")
+		KAPI_SIGNAL_DESC("Process is terminated immediately, epoll instance creation may be incomplete")
+	KAPI_SIGNAL_END
+
+	.signal_count = 3,
+KAPI_END_SPEC;
 SYSCALL_DEFINE1(epoll_create1, int, flags)
 {
 	return do_epoll_create(flags);
-- 
2.39.5


^ permalink raw reply related

* [RFC 03/19] eventpoll: add API specification for epoll_create
From: Sasha Levin @ 2025-06-14 13:48 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-api, workflows, tools, Sasha Levin
In-Reply-To: <20250614134858.790460-1-sashal@kernel.org>

Add kernel API specification for the epoll_create() system call.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 fs/eventpoll.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 111 insertions(+)

diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 8f8a64ebbaef6..50adea7ba43d1 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -2356,6 +2356,117 @@ SYSCALL_DEFINE1(epoll_create1, int, flags)
 	return do_epoll_create(flags);
 }
 
+
+DEFINE_KERNEL_API_SPEC(sys_epoll_create)
+	KAPI_DESCRIPTION("Create an epoll instance (obsolete)")
+	KAPI_LONG_DESC("Creates a new epoll instance and returns a file descriptor "
+		       "referring to that instance. This is the obsolete interface; "
+		       "new applications should use epoll_create1() instead.")
+	KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
+
+	KAPI_PARAM(0, "size", "int", "Ignored hint about expected number of file descriptors")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		KAPI_PARAM_RANGE(1, INT_MAX)
+		.constraint_type = KAPI_CONSTRAINT_RANGE,
+		.constraints = "Must be greater than zero (ignored since Linux 2.6.8)",
+	KAPI_PARAM_END
+
+	KAPI_RETURN("long", "File descriptor on success, negative error code on failure")
+		.type = KAPI_TYPE_INT,
+		.check_type = KAPI_RETURN_FD,
+	KAPI_RETURN_END
+
+	KAPI_ERROR(0, -EINVAL, "EINVAL", "size <= 0",
+		   "The size parameter must be greater than zero.")
+	KAPI_ERROR(1, -EMFILE, "EMFILE", "Per-process file descriptor limit reached",
+		   "The per-process limit on the number of open file descriptors has been reached.")
+	KAPI_ERROR(2, -ENFILE, "ENFILE", "System file table overflow",
+		   "The system-wide limit on the total number of open files has been reached.")
+	KAPI_ERROR(3, -ENOMEM, "ENOMEM", "Insufficient kernel memory",
+		   "There was insufficient kernel memory to create the epoll instance.")
+	KAPI_ERROR(4, -EINTR, "EINTR", "Interrupted by signal",
+		   "The system call was interrupted by a signal before the epoll instance could be created.")
+
+	.error_count = 5,
+	.param_count = 1,
+	.since_version = "2.6",
+	.deprecated = true,
+	.replacement = "epoll_create1",
+	.examples = "int epfd = epoll_create(1024); // size is ignored since Linux 2.6.8",
+	.notes = "Since Linux 2.6.8, the size argument is ignored but must be greater than zero. "
+		 "The kernel dynamically sizes the data structures as needed. "
+		 "For new applications, epoll_create1() should be preferred as it allows "
+		 "setting close-on-exec flag atomically.",
+
+	/* Side effects */
+	KAPI_SIDE_EFFECT(0, KAPI_EFFECT_RESOURCE_CREATE | KAPI_EFFECT_ALLOC_MEMORY,
+			 "epoll instance",
+			 "Creates a new epoll instance and allocates kernel memory for it")
+		KAPI_EFFECT_CONDITION("Always when successful")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(1, KAPI_EFFECT_RESOURCE_CREATE,
+			 "file descriptor",
+			 "Allocates a new file descriptor in the process's file descriptor table")
+		KAPI_EFFECT_CONDITION("Always when successful")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(2, KAPI_EFFECT_MODIFY_STATE,
+			 "kernel file table",
+			 "Adds new file structure to system-wide file table")
+		KAPI_EFFECT_CONDITION("Always when successful")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT_COUNT(3)
+
+	/* State transitions */
+	KAPI_STATE_TRANS(0, "epoll instance", "non-existent", "created and empty",
+			 "A new epoll instance is created with no monitored file descriptors")
+		KAPI_STATE_TRANS_COND("On successful creation")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(1, "file descriptor", "unallocated", "allocated and open",
+			 "A new file descriptor is allocated in the process's fd table")
+		KAPI_STATE_TRANS_COND("On successful creation")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS_COUNT(2)
+
+	/* Signal specifications */
+	KAPI_SIGNAL(0, SIGINT, "SIGINT", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN)
+		KAPI_SIGNAL_CONDITION("During kernel memory allocation")
+		KAPI_SIGNAL_DESC("If interrupted during memory allocation or fd allocation, returns -EINTR")
+		KAPI_SIGNAL_RESTARTABLE
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(1, SIGTERM, "SIGTERM", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN)
+		KAPI_SIGNAL_CONDITION("During kernel memory allocation")
+		KAPI_SIGNAL_DESC("If interrupted during memory allocation or fd allocation, returns -EINTR")
+		KAPI_SIGNAL_RESTARTABLE
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(2, SIGKILL, "SIGKILL", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_TERMINATE)
+		KAPI_SIGNAL_CONDITION("At any point during the syscall")
+		KAPI_SIGNAL_DESC("Process is terminated immediately, epoll instance creation may be incomplete")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(3, SIGHUP, "SIGHUP", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN)
+		KAPI_SIGNAL_CONDITION("During kernel operations")
+		KAPI_SIGNAL_DESC("If process is being terminated due to terminal hangup, may return -EINTR")
+		KAPI_SIGNAL_RESTARTABLE
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(4, SIGPIPE, "SIGPIPE", KAPI_SIGNAL_IGNORE, KAPI_SIGNAL_ACTION_DEFAULT)
+		KAPI_SIGNAL_CONDITION("Never generated by epoll_create")
+		KAPI_SIGNAL_DESC("This signal is not relevant to epoll_create as it doesn't involve pipes")
+	KAPI_SIGNAL_END
+
+	.signal_count = 5,
+KAPI_END_SPEC;
+
 SYSCALL_DEFINE1(epoll_create, int, size)
 {
 	if (size <= 0)
-- 
2.39.5


^ permalink raw reply related

* [RFC 04/19] eventpoll: add API specification for epoll_ctl
From: Sasha Levin @ 2025-06-14 13:48 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-api, workflows, tools, Sasha Levin
In-Reply-To: <20250614134858.790460-1-sashal@kernel.org>

Add kernel API specification for the epoll_ctl() system call.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 fs/eventpoll.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 203 insertions(+)

diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 50adea7ba43d1..409a0c440f112 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -2647,6 +2647,209 @@ int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds,
  * the eventpoll file that enables the insertion/removal/change of
  * file descriptors inside the interest set.
  */
+
+/* Valid values for epoll_ctl op parameter */
+static const s64 epoll_ctl_valid_ops[] = {
+	EPOLL_CTL_ADD,
+	EPOLL_CTL_DEL,
+	EPOLL_CTL_MOD,
+};
+
+DEFINE_KERNEL_API_SPEC(sys_epoll_ctl)
+	KAPI_DESCRIPTION("Control interface for an epoll file descriptor")
+	KAPI_LONG_DESC("Performs control operations on the epoll instance referred to by epfd. "
+		       "It requests that the operation op be performed for the target file "
+		       "descriptor fd. Valid operations are adding, modifying, or deleting "
+		       "file descriptors from the interest set.")
+	KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
+
+	KAPI_PARAM(0, "epfd", "int", "File descriptor referring to the epoll instance")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.type = KAPI_TYPE_FD,
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+	KAPI_PARAM_END
+
+	KAPI_PARAM(1, "op", "int", "Operation to be performed")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.type = KAPI_TYPE_INT,
+		.constraint_type = KAPI_CONSTRAINT_ENUM,
+		.enum_values = epoll_ctl_valid_ops,
+		.enum_count = ARRAY_SIZE(epoll_ctl_valid_ops),
+		.constraints = "Must be EPOLL_CTL_ADD, EPOLL_CTL_DEL, or EPOLL_CTL_MOD",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(2, "fd", "int", "File descriptor to be monitored")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.type = KAPI_TYPE_FD,
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+		.constraints = "Must refer to a file that supports poll operations",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(3, "event", "struct epoll_event __user *", "Settings for the file descriptor")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN | KAPI_PARAM_USER | KAPI_PARAM_OPTIONAL)
+		.type = KAPI_TYPE_USER_PTR,
+		KAPI_PARAM_SIZE(sizeof(struct epoll_event))
+		.constraints = "Required for ADD and MOD operations, ignored for DEL",
+	KAPI_PARAM_END
+
+	KAPI_RETURN("long", "0 on success, negative error code on failure")
+		.type = KAPI_TYPE_INT,
+		.check_type = KAPI_RETURN_EXACT,
+		KAPI_RETURN_SUCCESS(0)
+	KAPI_RETURN_END
+
+	KAPI_ERROR(0, -EBADF, "EBADF", "epfd or fd is not a valid file descriptor",
+		   "One of the file descriptors is invalid or has been closed.")
+	KAPI_ERROR(1, -EEXIST, "EEXIST", "op is EPOLL_CTL_ADD and fd is already registered",
+		   "The file descriptor is already present in the epoll instance.")
+	KAPI_ERROR(2, -EINVAL, "EINVAL", "Invalid operation or parameters",
+		   "epfd is not an epoll file descriptor, epfd == fd, op is not valid, "
+		   "or EPOLLEXCLUSIVE was specified with invalid events.")
+	KAPI_ERROR(3, -ENOENT, "ENOENT", "op is EPOLL_CTL_MOD or EPOLL_CTL_DEL and fd is not registered",
+		   "The file descriptor is not registered with this epoll instance.")
+	KAPI_ERROR(4, -ENOMEM, "ENOMEM", "Insufficient kernel memory",
+		   "There was insufficient memory to handle the requested operation.")
+	KAPI_ERROR(5, -EPERM, "EPERM", "Target file does not support epoll",
+		   "The target file fd does not support poll operations.")
+	KAPI_ERROR(6, -ELOOP, "ELOOP", "Circular monitoring detected",
+		   "fd refers to an epoll instance and this operation would result "
+		   "in a circular loop of epoll instances monitoring one another.")
+	KAPI_ERROR(7, -EFAULT, "EFAULT", "event points outside accessible address space",
+		   "The memory area pointed to by event is not accessible with write permissions.")
+	KAPI_ERROR(8, -EAGAIN, "EAGAIN", "Nonblocking mode and lock not available",
+		   "The operation was called in nonblocking mode and could not acquire necessary locks.")
+	KAPI_ERROR(9, -ENOSPC, "ENOSPC", "User epoll watch limit exceeded",
+		   "The limit on the total number of epoll watches was exceeded. "
+		   "See /proc/sys/fs/epoll/max_user_watches.")
+	KAPI_ERROR(10, -EINTR, "EINTR", "Interrupted by signal",
+		   "The system call was interrupted by a signal before completion.")
+
+	.error_count = 11,
+	.param_count = 4,
+	.since_version = "2.6",
+
+	/* Locking specifications */
+	KAPI_LOCK(0, "ep->mtx", KAPI_LOCK_MUTEX)
+		KAPI_LOCK_ACQUIRED
+		KAPI_LOCK_RELEASED
+		KAPI_LOCK_DESC("Protects the epoll instance during control operations")
+	KAPI_LOCK_END
+
+	KAPI_LOCK(1, "epnested_mutex", KAPI_LOCK_MUTEX)
+		KAPI_LOCK_DESC("Global mutex to prevent circular epoll structures (acquired for nested epoll)")
+	KAPI_LOCK_END
+
+	.lock_count = 2,
+
+	/* Side effects */
+	KAPI_SIDE_EFFECT(0, KAPI_EFFECT_MODIFY_STATE | KAPI_EFFECT_ALLOC_MEMORY,
+			 "epoll interest list",
+			 "Adds new epitem structure to the epoll interest list")
+		KAPI_EFFECT_CONDITION("When op is EPOLL_CTL_ADD")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(1, KAPI_EFFECT_MODIFY_STATE | KAPI_EFFECT_FREE_MEMORY,
+			 "epoll interest list",
+			 "Removes epitem structure from the epoll interest list")
+		KAPI_EFFECT_CONDITION("When op is EPOLL_CTL_DEL")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(2, KAPI_EFFECT_MODIFY_STATE,
+			 "epoll event mask",
+			 "Modifies the event mask for an existing epitem")
+		KAPI_EFFECT_CONDITION("When op is EPOLL_CTL_MOD")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(3, KAPI_EFFECT_MODIFY_STATE,
+			 "file reference count",
+			 "Increases reference count on the monitored file")
+		KAPI_EFFECT_CONDITION("When op is EPOLL_CTL_ADD")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(4, KAPI_EFFECT_MODIFY_STATE,
+			 "file reference count",
+			 "Decreases reference count on the monitored file")
+		KAPI_EFFECT_CONDITION("When op is EPOLL_CTL_DEL")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(5, KAPI_EFFECT_SCHEDULE,
+			 "process state",
+			 "May wake up processes waiting on the epoll instance if events become available")
+		KAPI_EFFECT_CONDITION("When adding or modifying entries that match current events")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT_COUNT(6)
+
+	/* State transitions */
+	KAPI_STATE_TRANS(0, "epoll entry", "non-existent", "monitored",
+			 "File descriptor is added to epoll interest list")
+		KAPI_STATE_TRANS_COND("When op is EPOLL_CTL_ADD")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(1, "epoll entry", "monitored", "non-existent",
+			 "File descriptor is removed from epoll interest list")
+		KAPI_STATE_TRANS_COND("When op is EPOLL_CTL_DEL")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(2, "epoll entry", "monitored with events A", "monitored with events B",
+			 "Event mask for file descriptor is modified")
+		KAPI_STATE_TRANS_COND("When op is EPOLL_CTL_MOD")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(3, "epoll ready list", "empty or partial", "contains new events",
+			 "Ready list may be updated if new/modified entry has pending events")
+		KAPI_STATE_TRANS_COND("When monitored fd has events matching the mask")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS_COUNT(4)
+
+	/* Signal specifications */
+	KAPI_SIGNAL(0, SIGINT, "SIGINT", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN)
+		KAPI_SIGNAL_CONDITION("During mutex acquisition or memory allocation")
+		KAPI_SIGNAL_DESC("Returns -EINTR if interrupted before completing the operation")
+		KAPI_SIGNAL_RESTARTABLE
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(1, SIGTERM, "SIGTERM", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN)
+		KAPI_SIGNAL_CONDITION("During mutex acquisition or memory allocation")
+		KAPI_SIGNAL_DESC("Returns -EINTR if interrupted before completing the operation")
+		KAPI_SIGNAL_RESTARTABLE
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(2, SIGKILL, "SIGKILL", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_TERMINATE)
+		KAPI_SIGNAL_CONDITION("At any point during the syscall")
+		KAPI_SIGNAL_DESC("Process is terminated immediately, operation may be partially completed")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(3, SIGHUP, "SIGHUP", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN)
+		KAPI_SIGNAL_CONDITION("During blocking operations")
+		KAPI_SIGNAL_DESC("Returns -EINTR if terminal hangup occurs")
+		KAPI_SIGNAL_RESTARTABLE
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(4, SIGURG, "SIGURG", KAPI_SIGNAL_IGNORE, KAPI_SIGNAL_ACTION_DEFAULT)
+		KAPI_SIGNAL_CONDITION("May be generated by monitored sockets")
+		KAPI_SIGNAL_DESC("Urgent data signals from monitored sockets do not affect epoll_ctl")
+	KAPI_SIGNAL_END
+
+	.signal_count = 5,
+
+	.examples = "struct epoll_event ev;\n"
+		    "ev.events = EPOLLIN | EPOLLOUT;\n"
+		    "ev.data.fd = fd;\n"
+		    "if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1)\n"
+		    "    handle_error();\n",
+	.notes = "EPOLL_CTL_DEL ignores the event parameter (can be NULL). "
+		 "EPOLLEXCLUSIVE flag has restrictions and cannot be used with EPOLL_CTL_MOD. "
+		 "The epoll instance maintains a reference to registered files until they are "
+		 "explicitly removed with EPOLL_CTL_DEL or the epoll instance is closed.",
+KAPI_END_SPEC;
+
 SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,
 		struct epoll_event __user *, event)
 {
-- 
2.39.5


^ permalink raw reply related

* [RFC 05/19] eventpoll: add API specification for epoll_wait
From: Sasha Levin @ 2025-06-14 13:48 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-api, workflows, tools, Sasha Levin
In-Reply-To: <20250614134858.790460-1-sashal@kernel.org>

Add kernel API specification for the epoll_wait() system call.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 fs/eventpoll.c | 182 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 182 insertions(+)

diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 409a0c440f112..254b50d687d37 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -2932,6 +2932,188 @@ static int do_epoll_wait(int epfd, struct epoll_event __user *events,
 	return ep_poll(ep, events, maxevents, to);
 }
 
+
+DEFINE_KERNEL_API_SPEC(sys_epoll_wait)
+	KAPI_DESCRIPTION("Wait for events on an epoll instance")
+	KAPI_LONG_DESC("Waits for events on the epoll instance referred to by epfd. "
+		       "The function blocks the calling thread until either at least one of the "
+		       "file descriptors referred to by epfd becomes ready for some I/O operation, "
+		       "the call is interrupted by a signal handler, or the timeout expires.")
+	KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
+
+	KAPI_PARAM(0, "epfd", "int", "File descriptor referring to the epoll instance")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.type = KAPI_TYPE_FD,
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+	KAPI_PARAM_END
+
+	KAPI_PARAM(1, "events", "struct epoll_event __user *", "Buffer where ready events will be stored")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_OUT | KAPI_PARAM_USER)
+		.type = KAPI_TYPE_USER_PTR,
+		KAPI_PARAM_SIZE(sizeof(struct epoll_event))  /* Base size of single element */
+		.size_param_idx = 2,  /* Size determined by maxevents parameter */
+		.size_multiplier = sizeof(struct epoll_event),
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+		.constraints = "Must point to an array of at least maxevents epoll_event structures",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(2, "maxevents", "int", "Maximum number of events to return")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.type = KAPI_TYPE_INT,
+		KAPI_PARAM_RANGE(1, INT_MAX / sizeof(struct epoll_event))  /* EP_MAX_EVENTS */
+		.constraint_type = KAPI_CONSTRAINT_RANGE,
+		.constraints = "Must be greater than zero and not exceed system limits",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(3, "timeout", "int", "Timeout in milliseconds")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.type = KAPI_TYPE_INT,
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+		.constraints = "-1 blocks indefinitely, 0 returns immediately, >0 specifies milliseconds to wait",
+	KAPI_PARAM_END
+
+	KAPI_RETURN("long", "Number of ready file descriptors on success, negative error code on failure")
+		.type = KAPI_TYPE_INT,
+		.check_type = KAPI_RETURN_RANGE,
+		.success_min = 0,
+		.success_max = INT_MAX,
+	KAPI_RETURN_END
+
+	KAPI_ERROR(0, -EBADF, "EBADF", "epfd is not a valid file descriptor",
+		   "The epoll file descriptor is invalid or has been closed.")
+	KAPI_ERROR(1, -EFAULT, "EFAULT", "events points outside accessible address space",
+		   "The memory area pointed to by events is not accessible with write permissions.")
+	KAPI_ERROR(2, -EINTR, "EINTR", "Call interrupted by signal handler",
+		   "The call was interrupted by a signal handler before any events "
+		   "became ready or the timeout expired.")
+	KAPI_ERROR(3, -EINVAL, "EINVAL", "Invalid parameters",
+		   "epfd is not an epoll file descriptor, or maxevents is less than or equal to zero.")
+
+	.error_count = 4,
+	.param_count = 4,
+	.since_version = "2.6",
+
+	/* Side effects */
+	KAPI_SIDE_EFFECT(0, KAPI_EFFECT_MODIFY_STATE,
+			 "ready list",
+			 "Removes events from the epoll ready list as they are reported")
+		KAPI_EFFECT_CONDITION("When events are available and level-triggered")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(1, KAPI_EFFECT_SCHEDULE,
+			 "process state",
+			 "Blocks the calling thread until events are available or timeout")
+		KAPI_EFFECT_CONDITION("When timeout != 0 and no events are immediately available")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(2, KAPI_EFFECT_MODIFY_STATE,
+			 "user memory",
+			 "Writes event data to user-provided buffer")
+		KAPI_EFFECT_CONDITION("When events are available")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(3, KAPI_EFFECT_PROCESS_STATE,
+			 "signal state",
+			 "Clears TIF_SIGPENDING if a signal was pending")
+		KAPI_EFFECT_CONDITION("When returning due to signal interruption")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT_COUNT(4)
+
+	/* State transitions */
+	KAPI_STATE_TRANS(0, "process", "running", "blocked",
+			 "Process blocks waiting for events")
+		KAPI_STATE_TRANS_COND("When no events available and timeout != 0")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(1, "process", "blocked", "running",
+			 "Process wakes up due to events, timeout, or signal")
+		KAPI_STATE_TRANS_COND("When wait condition is satisfied")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(2, "epoll ready list", "has events", "events consumed",
+			 "Ready events are consumed from the epoll instance")
+		KAPI_STATE_TRANS_COND("When returning events to userspace")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(3, "events buffer", "uninitialized", "contains event data",
+			 "User buffer is populated with ready events")
+		KAPI_STATE_TRANS_COND("When events are available")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS_COUNT(4)
+
+	/* Signal specifications */
+	KAPI_SIGNAL(0, 0, "ANY", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN)
+		KAPI_SIGNAL_CONDITION("Any pending signal")
+		KAPI_SIGNAL_DESC("Any signal delivered to the thread will interrupt epoll_wait() "
+				 "and cause it to return -EINTR. This is checked via signal_pending() "
+				 "after checking for available events.")
+		KAPI_SIGNAL_RESTARTABLE
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(1, SIGKILL, "SIGKILL", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_TERMINATE)
+		KAPI_SIGNAL_CONDITION("Always delivered, cannot be blocked")
+		KAPI_SIGNAL_DESC("SIGKILL will terminate the process. The epoll_wait call will "
+				 "not return as the process is terminated immediately.")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(2, SIGSTOP, "SIGSTOP", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_STOP)
+		KAPI_SIGNAL_CONDITION("Always delivered, cannot be blocked")
+		KAPI_SIGNAL_DESC("SIGSTOP will stop the process. When continued with SIGCONT, "
+				 "epoll_wait may return -EINTR if the timeout has not expired.")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(3, SIGCONT, "SIGCONT", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_CONTINUE)
+		KAPI_SIGNAL_CONDITION("When process is stopped")
+		KAPI_SIGNAL_DESC("SIGCONT resumes a stopped process. If epoll_wait was interrupted "
+				 "by SIGSTOP, it may return -EINTR when continued.")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(4, SIGALRM, "SIGALRM", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN)
+		KAPI_SIGNAL_CONDITION("Timer expiration")
+		KAPI_SIGNAL_DESC("SIGALRM from timer expiration will interrupt epoll_wait with -EINTR")
+		KAPI_SIGNAL_RESTARTABLE
+	KAPI_SIGNAL_END
+
+	.signal_count = 5,
+	.signal_mask_count = 0, /* No signal mask manipulation in epoll_wait */
+
+	/* Locking specifications */
+	KAPI_LOCK(0, "ep->lock", KAPI_LOCK_SPINLOCK)
+		KAPI_LOCK_ACQUIRED
+		KAPI_LOCK_RELEASED
+		KAPI_LOCK_DESC("Protects the ready list while checking for and consuming events")
+	KAPI_LOCK_END
+
+	KAPI_LOCK(1, "ep->mtx", KAPI_LOCK_MUTEX)
+		KAPI_LOCK_ACQUIRED
+		KAPI_LOCK_RELEASED
+		KAPI_LOCK_DESC("Protects against concurrent epoll_ctl operations during wait")
+	KAPI_LOCK_END
+
+	.lock_count = 2,
+
+	.examples = "struct epoll_event events[10];\n"
+		    "int nfds = epoll_wait(epfd, events, 10, 1000);\n"
+		    "if (nfds == -1) {\n"
+		    "    perror(\"epoll_wait\");\n"
+		    "    exit(EXIT_FAILURE);\n"
+		    "}\n"
+		    "for (int n = 0; n < nfds; ++n) {\n"
+		    "    if (events[n].data.fd == listen_sock) {\n"
+		    "        accept_new_connection();\n"
+		    "    } else {\n"
+		    "        handle_io(events[n].data.fd);\n"
+		    "    }\n"
+		    "}",
+	.notes = "The timeout uses CLOCK_MONOTONIC and may be rounded up to system clock granularity. "
+		 "A timeout of -1 causes epoll_wait to block indefinitely, while a timeout of 0 "
+		 "causes it to return immediately even if no events are available. "
+		 "The struct epoll_event is defined as containing events (uint32_t) and data (epoll_data_t union).",
+KAPI_END_SPEC;
+
 SYSCALL_DEFINE4(epoll_wait, int, epfd, struct epoll_event __user *, events,
 		int, maxevents, int, timeout)
 {
-- 
2.39.5


^ permalink raw reply related

* [RFC 06/19] eventpoll: add API specification for epoll_pwait
From: Sasha Levin @ 2025-06-14 13:48 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-api, workflows, tools, Sasha Levin
In-Reply-To: <20250614134858.790460-1-sashal@kernel.org>

Add kernel API specification for the epoll_pwait() system call.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 fs/eventpoll.c | 230 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 230 insertions(+)

diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 254b50d687d37..8bd25f9230fc8 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -3148,6 +3148,236 @@ static int do_epoll_pwait(int epfd, struct epoll_event __user *events,
 	return error;
 }
 
+
+DEFINE_KERNEL_API_SPEC(sys_epoll_pwait)
+	KAPI_DESCRIPTION("Wait for events on an epoll instance with signal handling")
+	KAPI_LONG_DESC("Similar to epoll_wait(), but allows the caller to safely wait for "
+		       "either events on the epoll instance or the delivery of a signal. "
+		       "The sigmask argument specifies a signal mask which is atomically "
+		       "set during the wait, allowing signals to be blocked while not waiting "
+		       "and ensuring no signal is lost between checking for events and blocking.")
+	KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
+
+	KAPI_PARAM(0, "epfd", "int", "File descriptor referring to the epoll instance")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.type = KAPI_TYPE_FD,
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+	KAPI_PARAM_END
+
+	KAPI_PARAM(1, "events", "struct epoll_event __user *", "Buffer where ready events will be stored")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_OUT | KAPI_PARAM_USER)
+		.type = KAPI_TYPE_USER_PTR,
+		KAPI_PARAM_SIZE(sizeof(struct epoll_event))
+		.size_param_idx = 2,  /* Size determined by maxevents parameter */
+		.size_multiplier = sizeof(struct epoll_event),
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+		.constraints = "Must point to an array of at least maxevents epoll_event structures",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(2, "maxevents", "int", "Maximum number of events to return")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.type = KAPI_TYPE_INT,
+		KAPI_PARAM_RANGE(1, INT_MAX / sizeof(struct epoll_event))  /* EP_MAX_EVENTS */
+		.constraint_type = KAPI_CONSTRAINT_RANGE,
+		.constraints = "Must be greater than zero and not exceed system limits",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(3, "timeout", "int", "Timeout in milliseconds")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.type = KAPI_TYPE_INT,
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+		.constraints = "-1 blocks indefinitely, 0 returns immediately, >0 specifies milliseconds to wait",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(4, "sigmask", "const sigset_t __user *", "Signal mask to atomically set during wait")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN | KAPI_PARAM_USER | KAPI_PARAM_OPTIONAL)
+		.type = KAPI_TYPE_USER_PTR,
+		KAPI_PARAM_SIZE(sizeof(sigset_t))
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+		.constraints = "Can be NULL if no signal mask change is desired",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(5, "sigsetsize", "size_t", "Size of the signal set in bytes")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.type = KAPI_TYPE_UINT,
+		KAPI_PARAM_RANGE(sizeof(sigset_t), sizeof(sigset_t))
+		.constraint_type = KAPI_CONSTRAINT_RANGE,
+		.constraints = "Must be sizeof(sigset_t)",
+	KAPI_PARAM_END
+
+	KAPI_RETURN("long", "Number of ready file descriptors on success, negative error code on failure")
+		.type = KAPI_TYPE_INT,
+		.check_type = KAPI_RETURN_RANGE,
+		.success_min = 0,
+		.success_max = INT_MAX,
+	KAPI_RETURN_END
+
+	KAPI_ERROR(0, -EBADF, "EBADF", "epfd is not a valid file descriptor",
+		   "The epoll file descriptor is invalid or has been closed.")
+	KAPI_ERROR(1, -EFAULT, "EFAULT", "Memory area not accessible",
+		   "The memory area pointed to by events or sigmask is not accessible.")
+	KAPI_ERROR(2, -EINTR, "EINTR", "Call interrupted by signal handler",
+		   "The call was interrupted by a signal handler before any events "
+		   "became ready or the timeout expired; see signal(7).")
+	KAPI_ERROR(3, -EINVAL, "EINVAL", "Invalid parameters",
+		   "epfd is not an epoll file descriptor, maxevents is less than or equal to zero, "
+		   "or sigsetsize is not equal to sizeof(sigset_t).")
+
+	.error_count = 4,
+	.param_count = 6,
+	.since_version = "2.6.19",
+
+	/* Side effects */
+	KAPI_SIDE_EFFECT(0, KAPI_EFFECT_MODIFY_STATE,
+			 "signal mask",
+			 "Atomically sets the signal mask for the calling thread")
+		KAPI_EFFECT_CONDITION("When sigmask is not NULL")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(1, KAPI_EFFECT_MODIFY_STATE,
+			 "ready list",
+			 "Removes events from the epoll ready list as they are reported")
+		KAPI_EFFECT_CONDITION("When events are available and level-triggered")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(2, KAPI_EFFECT_SCHEDULE,
+			 "process state",
+			 "Blocks the calling thread until events are available, timeout, or signal")
+		KAPI_EFFECT_CONDITION("When timeout != 0 and no events are immediately available")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(3, KAPI_EFFECT_MODIFY_STATE,
+			 "user memory",
+			 "Writes event data to user-provided buffer")
+		KAPI_EFFECT_CONDITION("When events are available")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(4, KAPI_EFFECT_PROCESS_STATE,
+			 "saved signal mask",
+			 "Saves and restores the original signal mask")
+		KAPI_EFFECT_CONDITION("When sigmask is not NULL")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT_COUNT(5)
+
+	/* State transitions */
+	KAPI_STATE_TRANS(0, "signal mask", "original mask", "user-specified mask",
+			 "Thread's signal mask is atomically changed to the provided mask")
+		KAPI_STATE_TRANS_COND("When sigmask is not NULL")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(1, "process", "running", "blocked",
+			 "Process blocks waiting for events with specified signal mask")
+		KAPI_STATE_TRANS_COND("When no events available and timeout != 0")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(2, "process", "blocked", "running",
+			 "Process wakes up due to events, timeout, or unblocked signal")
+		KAPI_STATE_TRANS_COND("When wait condition is satisfied")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(3, "signal mask", "user-specified mask", "original mask",
+			 "Thread's signal mask is restored to its original value")
+		KAPI_STATE_TRANS_COND("When returning from epoll_pwait")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(4, "pending signals", "blocked", "deliverable",
+			 "Signals that were blocked by the temporary mask become deliverable")
+		KAPI_STATE_TRANS_COND("When signal mask is restored and signals were pending")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS_COUNT(5)
+
+	/* Signal specifications */
+	KAPI_SIGNAL(0, 0, "ANY_UNBLOCKED", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN)
+		KAPI_SIGNAL_CONDITION("Signal not blocked by provided sigmask")
+		KAPI_SIGNAL_DESC("Any signal not blocked by the sigmask parameter will interrupt "
+				 "epoll_pwait() and cause it to return -EINTR. The signal mask is "
+				 "atomically set via set_user_sigmask() and restored via "
+				 "restore_saved_sigmask_unless() before returning.")
+		KAPI_SIGNAL_RESTARTABLE
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(1, SIGKILL, "SIGKILL", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_TERMINATE)
+		KAPI_SIGNAL_CONDITION("Cannot be blocked by sigmask")
+		KAPI_SIGNAL_DESC("SIGKILL cannot be blocked and will terminate the process immediately. "
+				 "The epoll_pwait call will not return.")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(2, SIGSTOP, "SIGSTOP", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_STOP)
+		KAPI_SIGNAL_CONDITION("Cannot be blocked by sigmask")
+		KAPI_SIGNAL_DESC("SIGSTOP cannot be blocked and will stop the process. When continued "
+				 "with SIGCONT, epoll_pwait may return -EINTR.")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(3, 0, "BLOCKED_SIGNALS", KAPI_SIGNAL_BLOCK, KAPI_SIGNAL_ACTION_DEFAULT)
+		KAPI_SIGNAL_CONDITION("Signals in provided sigmask")
+		KAPI_SIGNAL_DESC("Signals specified in the sigmask parameter are blocked for the "
+				 "duration of the epoll_pwait call. They remain pending and will be "
+				 "delivered after the signal mask is restored.")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(4, SIGCONT, "SIGCONT", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_CONTINUE)
+		KAPI_SIGNAL_CONDITION("When process is stopped")
+		KAPI_SIGNAL_DESC("SIGCONT resumes a stopped process. If epoll_pwait was interrupted "
+				 "by SIGSTOP, it may return -EINTR when continued.")
+	KAPI_SIGNAL_END
+
+	.signal_count = 5,
+
+	/* Signal mask specifications */
+	KAPI_SIGNAL_MASK(0, "user_sigmask", "User-provided signal mask atomically applied")
+		.description = "The signal mask provided in the sigmask parameter is atomically "
+			       "set for the duration of the wait operation. This prevents race "
+			       "conditions between checking for events and blocking. The original "
+			       "signal mask is restored before epoll_pwait returns, unless the "
+			       "return value is -EINTR (in which case the mask is restored by "
+			       "the signal delivery mechanism)."
+	KAPI_SIGNAL_MASK_END
+
+	.signal_mask_count = 1,
+
+	/* Locking specifications */
+	KAPI_LOCK(0, "ep->lock", KAPI_LOCK_SPINLOCK)
+		KAPI_LOCK_ACQUIRED
+		KAPI_LOCK_RELEASED
+		KAPI_LOCK_DESC("Protects the ready list while checking for and consuming events")
+	KAPI_LOCK_END
+
+	KAPI_LOCK(1, "ep->mtx", KAPI_LOCK_MUTEX)
+		KAPI_LOCK_ACQUIRED
+		KAPI_LOCK_RELEASED
+		KAPI_LOCK_DESC("Protects against concurrent epoll_ctl operations during wait")
+	KAPI_LOCK_END
+
+	.lock_count = 2,
+
+	.examples = "sigset_t sigmask;\n"
+		    "struct epoll_event events[10];\n\n"
+		    "/* Block SIGINT during epoll_pwait */\n"
+		    "sigemptyset(&sigmask);\n"
+		    "sigaddset(&sigmask, SIGINT);\n\n"
+		    "int nfds = epoll_pwait(epfd, events, 10, 1000, &sigmask, sizeof(sigmask));\n"
+		    "if (nfds == -1) {\n"
+		    "    if (errno == EINTR) {\n"
+		    "        /* Handle signal */\n"
+		    "    }\n"
+		    "    perror(\"epoll_pwait\");\n"
+		    "    exit(EXIT_FAILURE);\n"
+		    "}",
+	.notes = "epoll_pwait() is equivalent to atomically executing:\n"
+		 "    sigset_t origmask;\n"
+		 "    pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);\n"
+		 "    ready = epoll_wait(epfd, events, maxevents, timeout);\n"
+		 "    pthread_sigmask(SIG_SETMASK, &origmask, NULL);\n"
+		 "This atomicity prevents race conditions where a signal could be delivered "
+		 "after checking for events but before blocking in epoll_wait(). "
+		 "The signal mask is always restored before epoll_pwait() returns.",
+KAPI_END_SPEC;
+
 SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
 		int, maxevents, int, timeout, const sigset_t __user *, sigmask,
 		size_t, sigsetsize)
-- 
2.39.5


^ permalink raw reply related

* [RFC 07/19] eventpoll: add API specification for epoll_pwait2
From: Sasha Levin @ 2025-06-14 13:48 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-api, workflows, tools, Sasha Levin
In-Reply-To: <20250614134858.790460-1-sashal@kernel.org>

Add kernel API specification for the epoll_pwait2() system call.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 fs/eventpoll.c | 244 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 244 insertions(+)

diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 8bd25f9230fc8..0e90d66467010 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -3389,6 +3389,250 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
 			      sigmask, sigsetsize);
 }
 
+
+DEFINE_KERNEL_API_SPEC(sys_epoll_pwait2)
+	KAPI_DESCRIPTION("Wait for events on an epoll instance with nanosecond precision timeout")
+	KAPI_LONG_DESC("Similar to epoll_pwait(), but takes a timespec structure that allows "
+		       "nanosecond precision for the timeout value. This provides more accurate "
+		       "timeout control compared to the millisecond precision of epoll_pwait(). "
+		       "Like epoll_pwait(), it atomically sets a signal mask during the wait.")
+	KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
+
+	KAPI_PARAM(0, "epfd", "int", "File descriptor referring to the epoll instance")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.type = KAPI_TYPE_FD,
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+	KAPI_PARAM_END
+
+	KAPI_PARAM(1, "events", "struct epoll_event __user *", "Buffer where ready events will be stored")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_OUT | KAPI_PARAM_USER)
+		.type = KAPI_TYPE_USER_PTR,
+		KAPI_PARAM_SIZE(sizeof(struct epoll_event))
+		.size_param_idx = 2,  /* Size determined by maxevents parameter */
+		.size_multiplier = sizeof(struct epoll_event),
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+		.constraints = "Must point to an array of at least maxevents epoll_event structures",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(2, "maxevents", "int", "Maximum number of events to return")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.type = KAPI_TYPE_INT,
+		KAPI_PARAM_RANGE(1, INT_MAX / sizeof(struct epoll_event))  /* EP_MAX_EVENTS */
+		.constraint_type = KAPI_CONSTRAINT_RANGE,
+		.constraints = "Must be greater than zero and not exceed system limits",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(3, "timeout", "const struct __kernel_timespec __user *", "Timeout with nanosecond precision")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN | KAPI_PARAM_USER | KAPI_PARAM_OPTIONAL)
+		.type = KAPI_TYPE_USER_PTR,
+		KAPI_PARAM_SIZE(sizeof(struct __kernel_timespec))
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+		.constraints = "NULL means block indefinitely, {0, 0} returns immediately, "
+			       "negative values are invalid",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(4, "sigmask", "const sigset_t __user *", "Signal mask to atomically set during wait")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN | KAPI_PARAM_USER | KAPI_PARAM_OPTIONAL)
+		.type = KAPI_TYPE_USER_PTR,
+		KAPI_PARAM_SIZE(sizeof(sigset_t))
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+		.constraints = "Can be NULL if no signal mask change is desired",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(5, "sigsetsize", "size_t", "Size of the signal set in bytes")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.type = KAPI_TYPE_UINT,
+		KAPI_PARAM_RANGE(sizeof(sigset_t), sizeof(sigset_t))
+		.constraint_type = KAPI_CONSTRAINT_RANGE,
+		.constraints = "Must be sizeof(sigset_t)",
+	KAPI_PARAM_END
+
+	KAPI_RETURN("long", "Number of ready file descriptors on success, negative error code on failure")
+		.type = KAPI_TYPE_INT,
+		.check_type = KAPI_RETURN_RANGE,
+		.success_min = 0,
+		.success_max = INT_MAX,
+	KAPI_RETURN_END
+
+	KAPI_ERROR(0, -EBADF, "EBADF", "epfd is not a valid file descriptor",
+		   "The epoll file descriptor is invalid or has been closed.")
+	KAPI_ERROR(1, -EFAULT, "EFAULT", "Memory area not accessible",
+		   "The memory area pointed to by events, timeout, or sigmask is not accessible.")
+	KAPI_ERROR(2, -EINTR, "EINTR", "Call interrupted by signal handler",
+		   "The call was interrupted by a signal handler before any events "
+		   "became ready or the timeout expired.")
+	KAPI_ERROR(3, -EINVAL, "EINVAL", "Invalid parameters",
+		   "epfd is not an epoll file descriptor, maxevents is less than or equal to zero, "
+		   "sigsetsize is not equal to sizeof(sigset_t), or timeout values are invalid.")
+
+	.error_count = 4,
+	.param_count = 6,
+	.since_version = "5.11",
+
+	/* Side effects */
+	KAPI_SIDE_EFFECT(0, KAPI_EFFECT_MODIFY_STATE,
+			 "signal mask",
+			 "Atomically sets the signal mask for the calling thread")
+		KAPI_EFFECT_CONDITION("When sigmask is not NULL")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(1, KAPI_EFFECT_MODIFY_STATE,
+			 "ready list",
+			 "Removes events from the epoll ready list as they are reported")
+		KAPI_EFFECT_CONDITION("When events are available and level-triggered")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(2, KAPI_EFFECT_SCHEDULE,
+			 "process state",
+			 "Blocks the calling thread until events, timeout, or signal")
+		KAPI_EFFECT_CONDITION("When timeout != NULL or timeout->tv_sec/tv_nsec != 0")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(3, KAPI_EFFECT_MODIFY_STATE,
+			 "user memory",
+			 "Writes event data to user-provided buffer")
+		KAPI_EFFECT_CONDITION("When events are available")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(4, KAPI_EFFECT_PROCESS_STATE,
+			 "saved signal mask",
+			 "Saves and restores the original signal mask")
+		KAPI_EFFECT_CONDITION("When sigmask is not NULL")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(5, KAPI_EFFECT_MODIFY_STATE,
+			 "timer precision",
+			 "Timeout may be rounded up to system timer granularity")
+		KAPI_EFFECT_CONDITION("When timeout is specified")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT_COUNT(6)
+
+	/* State transitions */
+	KAPI_STATE_TRANS(0, "signal mask", "original mask", "user-specified mask",
+			 "Thread's signal mask is atomically changed to the provided mask")
+		KAPI_STATE_TRANS_COND("When sigmask is not NULL")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(1, "process", "running", "blocked",
+			 "Process blocks waiting for events with specified signal mask")
+		KAPI_STATE_TRANS_COND("When no events available and not immediate return")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(2, "process", "blocked", "running",
+			 "Process wakes up due to events, timeout expiry, or unblocked signal")
+		KAPI_STATE_TRANS_COND("When wait condition is satisfied")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(3, "signal mask", "user-specified mask", "original mask",
+			 "Thread's signal mask is restored to its original value")
+		KAPI_STATE_TRANS_COND("When returning from epoll_pwait2")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(4, "pending signals", "blocked", "deliverable",
+			 "Signals that were blocked by the temporary mask become deliverable")
+		KAPI_STATE_TRANS_COND("When signal mask is restored and signals were pending")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(5, "timeout timer", "not started", "armed with nanosecond precision",
+			 "High resolution timer is armed with the specified timeout")
+		KAPI_STATE_TRANS_COND("When timeout is specified and > 0")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS_COUNT(6)
+
+	/* Signal specifications */
+	KAPI_SIGNAL(0, 0, "ANY_UNBLOCKED", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN)
+		KAPI_SIGNAL_CONDITION("Signal not blocked by provided sigmask")
+		KAPI_SIGNAL_DESC("Any signal not blocked by the sigmask parameter will interrupt "
+				 "epoll_pwait2() and cause it to return -EINTR. Signal handling is "
+				 "identical to epoll_pwait().")
+		KAPI_SIGNAL_RESTARTABLE
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(1, SIGKILL, "SIGKILL", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_TERMINATE)
+		KAPI_SIGNAL_CONDITION("Cannot be blocked by sigmask")
+		KAPI_SIGNAL_DESC("SIGKILL cannot be blocked and will terminate the process immediately.")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(2, SIGSTOP, "SIGSTOP", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_STOP)
+		KAPI_SIGNAL_CONDITION("Cannot be blocked by sigmask")
+		KAPI_SIGNAL_DESC("SIGSTOP cannot be blocked and will stop the process.")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(3, 0, "BLOCKED_SIGNALS", KAPI_SIGNAL_BLOCK, KAPI_SIGNAL_ACTION_DEFAULT)
+		KAPI_SIGNAL_CONDITION("Signals in provided sigmask")
+		KAPI_SIGNAL_DESC("Signals specified in the sigmask parameter are blocked during "
+				 "the epoll_pwait2 call.")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(4, SIGCONT, "SIGCONT", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_CONTINUE)
+		KAPI_SIGNAL_CONDITION("When process is stopped")
+		KAPI_SIGNAL_DESC("SIGCONT resumes a stopped process. If epoll_pwait2 was interrupted "
+				 "by SIGSTOP, it may return -EINTR when continued.")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(5, SIGALRM, "SIGALRM", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN)
+		KAPI_SIGNAL_CONDITION("Timer expiration")
+		KAPI_SIGNAL_DESC("SIGALRM or other timer signals will interrupt epoll_pwait2 with -EINTR "
+				 "if not blocked by sigmask")
+		KAPI_SIGNAL_RESTARTABLE
+	KAPI_SIGNAL_END
+
+	.signal_count = 6,
+
+	/* Signal mask specifications */
+	KAPI_SIGNAL_MASK(0, "user_sigmask", "User-provided signal mask atomically applied")
+		.description = "The signal mask is atomically set and restored exactly as in "
+			       "epoll_pwait(), providing the same race-condition prevention."
+	KAPI_SIGNAL_MASK_END
+
+	.signal_mask_count = 1,
+
+	/* Locking specifications */
+	KAPI_LOCK(0, "ep->lock", KAPI_LOCK_SPINLOCK)
+		KAPI_LOCK_ACQUIRED
+		KAPI_LOCK_RELEASED
+		KAPI_LOCK_DESC("Protects the ready list while checking for and consuming events")
+	KAPI_LOCK_END
+
+	KAPI_LOCK(1, "ep->mtx", KAPI_LOCK_MUTEX)
+		KAPI_LOCK_ACQUIRED
+		KAPI_LOCK_RELEASED
+		KAPI_LOCK_DESC("Protects against concurrent epoll_ctl operations during wait")
+	KAPI_LOCK_END
+
+	.lock_count = 2,
+
+	.examples = "sigset_t sigmask;\n"
+		    "struct epoll_event events[10];\n"
+		    "struct __kernel_timespec ts;\n\n"
+		    "/* Block SIGINT during epoll_pwait2 */\n"
+		    "sigemptyset(&sigmask);\n"
+		    "sigaddset(&sigmask, SIGINT);\n\n"
+		    "/* Wait for 1.5 seconds */\n"
+		    "ts.tv_sec = 1;\n"
+		    "ts.tv_nsec = 500000000; /* 500 milliseconds */\n\n"
+		    "int nfds = epoll_pwait2(epfd, events, 10, &ts, &sigmask, sizeof(sigmask));\n"
+		    "if (nfds == -1) {\n"
+		    "    if (errno == EINTR) {\n"
+		    "        /* Handle signal */\n"
+		    "    }\n"
+		    "    perror(\"epoll_pwait2\");\n"
+		    "    exit(EXIT_FAILURE);\n"
+		    "}\n\n"
+		    "/* Example with infinite timeout */\n"
+		    "nfds = epoll_pwait2(epfd, events, 10, NULL, &sigmask, sizeof(sigmask));",
+	.notes = "epoll_pwait2() provides nanosecond precision timeouts, addressing the limitation "
+		 "of epoll_pwait() which only supports millisecond precision. The timeout parameter "
+		 "uses struct __kernel_timespec which is compatible with 64-bit time values, making "
+		 "it Y2038-safe. Like epoll_pwait(), the signal mask operation is atomic. "
+		 "The timeout is still subject to system timer granularity and may be rounded up.",
+KAPI_END_SPEC;
+
 SYSCALL_DEFINE6(epoll_pwait2, int, epfd, struct epoll_event __user *, events,
 		int, maxevents, const struct __kernel_timespec __user *, timeout,
 		const sigset_t __user *, sigmask, size_t, sigsetsize)
-- 
2.39.5


^ permalink raw reply related

* [RFC 08/19] exec: add API specification for execve
From: Sasha Levin @ 2025-06-14 13:48 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-api, workflows, tools, Sasha Levin
In-Reply-To: <20250614134858.790460-1-sashal@kernel.org>

Add comprehensive kernel API specification for the execve() system call.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 fs/exec.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 218 insertions(+)

diff --git a/fs/exec.c b/fs/exec.c
index 1f5fdd2e096e3..3d006105ab23d 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -52,6 +52,7 @@
 #include <linux/mount.h>
 #include <linux/security.h>
 #include <linux/syscalls.h>
+#include <linux/syscall_api_spec.h>
 #include <linux/tsacct_kern.h>
 #include <linux/cn_proc.h>
 #include <linux/audit.h>
@@ -1997,7 +1998,224 @@ void set_dumpable(struct mm_struct *mm, int value)
 	set_mask_bits(&mm->flags, MMF_DUMPABLE_MASK, value);
 }
 
+
+DEFINE_KERNEL_API_SPEC(sys_execve)
+	KAPI_DESCRIPTION("Execute a new program")
+	KAPI_LONG_DESC("Executes the program referred to by filename. This causes the program "
+		       "that is currently being run by the calling process to be replaced with "
+		       "a new program, with newly initialized stack, heap, and (initialized and "
+		       "uninitialized) data segments. The process ID remains the same.")
+	KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
+
+	KAPI_PARAM(0, "filename", "const char __user *", "Pathname of the program to execute")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN | KAPI_PARAM_USER)
+		.type = KAPI_TYPE_PATH,
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+		.constraints = "Must be a valid pathname to an executable file or script",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(1, "argv", "const char __user *const __user *", "Array of argument strings passed to the new program")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN | KAPI_PARAM_USER)
+		.type = KAPI_TYPE_USER_PTR,
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+		.constraints = "NULL-terminated array of pointers to null-terminated strings",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(2, "envp", "const char __user *const __user *", "Array of environment strings for the new program")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN | KAPI_PARAM_USER)
+		.type = KAPI_TYPE_USER_PTR,
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+		.constraints = "NULL-terminated array of pointers to null-terminated strings in form key=value",
+	KAPI_PARAM_END
+
+	KAPI_RETURN("long", "Does not return on success; returns -1 on error")
+		.type = KAPI_TYPE_INT,
+		.check_type = KAPI_RETURN_ERROR_CHECK,
+	KAPI_RETURN_END
+
+	KAPI_ERROR(0, -E2BIG, "E2BIG", "Argument list too long",
+		   "The total size of argv and envp exceeds the system limit.")
+	KAPI_ERROR(1, -EACCES, "EACCES", "Permission denied",
+		   "Search permission denied on a component of the path, file is not regular, "
+		   "or execute permission denied for file or interpreter.")
+	KAPI_ERROR(2, -EFAULT, "EFAULT", "Bad address",
+		   "filename, argv, or envp points outside accessible address space.")
+	KAPI_ERROR(3, -EINVAL, "EINVAL", "Invalid executable format",
+		   "An ELF executable has more than one PT_INTERP segment.")
+	KAPI_ERROR(4, -EIO, "EIO", "I/O error",
+		   "An I/O error occurred while reading from the file system.")
+	KAPI_ERROR(5, -EISDIR, "EISDIR", "Is a directory",
+		   "An ELF interpreter was a directory.")
+	KAPI_ERROR(6, -ELIBBAD, "ELIBBAD", "Invalid ELF interpreter",
+		   "An ELF interpreter was not in a recognized format.")
+	KAPI_ERROR(7, -ELOOP, "ELOOP", "Too many symbolic links",
+		   "Too many symbolic links encountered while resolving filename or interpreter.")
+	KAPI_ERROR(8, -EMFILE, "EMFILE", "Too many open files",
+		   "The per-process limit on open file descriptors has been reached.")
+	KAPI_ERROR(9, -ENAMETOOLONG, "ENAMETOOLONG", "Filename too long",
+		   "filename or one of the strings in argv or envp is too long.")
+	KAPI_ERROR(10, -ENFILE, "ENFILE", "System file table overflow",
+		   "The system-wide limit on open files has been reached.")
+	KAPI_ERROR(11, -ENOENT, "ENOENT", "File not found",
+		   "The file filename or an interpreter does not exist.")
+	KAPI_ERROR(12, -ENOEXEC, "ENOEXEC", "Exec format error",
+		   "An executable is not in a recognized format, is for wrong architecture, "
+		   "or has other format errors preventing execution.")
+	KAPI_ERROR(13, -ENOMEM, "ENOMEM", "Out of memory",
+		   "Insufficient kernel memory available.")
+	KAPI_ERROR(14, -ENOTDIR, "ENOTDIR", "Not a directory",
+		   "A component of the path prefix is not a directory.")
+	KAPI_ERROR(15, -EPERM, "EPERM", "Operation not permitted",
+		   "The filesystem is mounted nosuid, the user is not root, and the file has "
+		   "set-user-ID or set-group-ID bit set.")
+	KAPI_ERROR(16, -ETXTBSY, "ETXTBSY", "Text file busy",
+		   "The executable was open for writing by one or more processes.")
+	KAPI_ERROR(17, -EAGAIN, "EAGAIN", "Resource temporarily unavailable",
+		   "RLIMIT_NPROC limit exceeded - too many processes for this user.")
+
+	.error_count = 18,
+	.param_count = 3,
+	.since_version = "1.0",
+	.examples = "char *argv[] = { \"echo\", \"hello\", \"world\", NULL };\n"
+		    "char *envp[] = { \"PATH=/bin\", NULL };\n"
+		    "execve(\"/bin/echo\", argv, envp);\n"
+		    "/* This point is only reached on error */\n"
+		    "perror(\"execve failed\");\n"
+		    "exit(EXIT_FAILURE);",
+	.notes = "On success, execve() does not return; the new program is executed. "
+		 "File descriptors remain open unless marked close-on-exec. "
+		 "Signal dispositions are reset to default except for ignored signals. "
+		 "Any alternate signal stack is not preserved. "
+		 "The process's set of pending signals is cleared. "
+		 "All threads except the calling thread are destroyed.",
+
+	/* Fatal signals can interrupt exec */
+	KAPI_SIGNAL(0, 0, "FATAL_SIGNALS", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_TERMINATE)
+		KAPI_SIGNAL_CONDITION("Fatal signal pending during exec setup")
+		KAPI_SIGNAL_DESC("Fatal signals (checked via fatal_signal_pending()) can interrupt "
+				 "exec during setup phases like de_thread(). This causes exec to fail "
+				 "and the process to exit.")
+	KAPI_SIGNAL_END
+
+	/* SIGKILL sent to other threads */
+	KAPI_SIGNAL(1, SIGKILL, "SIGKILL", KAPI_SIGNAL_SEND, KAPI_SIGNAL_ACTION_TERMINATE)
+		KAPI_SIGNAL_TARGET("All other threads in the thread group")
+		KAPI_SIGNAL_CONDITION("Multi-threaded process doing exec")
+		KAPI_SIGNAL_DESC("During de_thread(), zap_other_threads() sends SIGKILL to all "
+				 "other threads in the thread group to ensure only the execing "
+				 "thread survives.")
+	KAPI_SIGNAL_END
+
+	/* Signal handlers reset */
+	KAPI_SIGNAL(2, 0, "ALL_HANDLERS", KAPI_SIGNAL_HANDLE, KAPI_SIGNAL_ACTION_CUSTOM)
+		KAPI_SIGNAL_CONDITION("Signal has a handler installed")
+		KAPI_SIGNAL_DESC("flush_signal_handlers() resets all signal handlers to SIG_DFL "
+				 "except for signals that are ignored (SIG_IGN). This happens "
+				 "after de_thread() completes.")
+	KAPI_SIGNAL_END
+
+	/* Ignored signals preserved */
+	KAPI_SIGNAL(3, 0, "IGNORED_SIGNALS", KAPI_SIGNAL_IGNORE, KAPI_SIGNAL_ACTION_CUSTOM)
+		KAPI_SIGNAL_CONDITION("Signal disposition is SIG_IGN")
+		KAPI_SIGNAL_DESC("Signals set to SIG_IGN are preserved across exec. This is "
+				 "POSIX-compliant behavior allowing parent processes to ignore "
+				 "signals in children.")
+	KAPI_SIGNAL_END
+
+	/* Pending signals cleared */
+	KAPI_SIGNAL(4, 0, "PENDING_SIGNALS", KAPI_SIGNAL_HANDLE, KAPI_SIGNAL_ACTION_CUSTOM)
+		KAPI_SIGNAL_CONDITION("Any pending signals")
+		KAPI_SIGNAL_DESC("All pending signals are cleared during exec. This includes "
+				 "both thread-specific and process-wide pending signals.")
+	KAPI_SIGNAL_END
+
+	/* Timer signals cleared */
+	KAPI_SIGNAL(5, 0, "TIMER_SIGNALS", KAPI_SIGNAL_HANDLE, KAPI_SIGNAL_ACTION_CUSTOM)
+		KAPI_SIGNAL_CONDITION("Timer-generated signals pending")
+		KAPI_SIGNAL_DESC("flush_itimer_signals() clears any pending timer signals "
+				 "(SIGALRM, SIGVTALRM, SIGPROF) to prevent confusion in the "
+				 "new program.")
+	KAPI_SIGNAL_END
+
+	/* Exit signal set to SIGCHLD */
+	KAPI_SIGNAL(6, SIGCHLD, "SIGCHLD", KAPI_SIGNAL_SEND, KAPI_SIGNAL_ACTION_DEFAULT)
+		KAPI_SIGNAL_TARGET("Parent process when this process exits")
+		KAPI_SIGNAL_CONDITION("Process exit after exec")
+		KAPI_SIGNAL_DESC("The exit_signal is set to SIGCHLD during exec, ensuring the "
+				 "parent will receive SIGCHLD when this process terminates.")
+	KAPI_SIGNAL_END
+
+	/* Alternate signal stack cleared */
+	KAPI_SIGNAL(7, 0, "SIGALTSTACK", KAPI_SIGNAL_HANDLE, KAPI_SIGNAL_ACTION_CUSTOM)
+		KAPI_SIGNAL_CONDITION("Process had alternate signal stack")
+		KAPI_SIGNAL_DESC("Any alternate signal stack (sigaltstack) is not preserved "
+				 "across exec. The new program starts with no alternate stack.")
+	KAPI_SIGNAL_END
+
+	.signal_count = 8,
+
+	/* Side effects */
+	KAPI_SIDE_EFFECT(0, KAPI_EFFECT_PROCESS_STATE | KAPI_EFFECT_FREE_MEMORY | KAPI_EFFECT_ALLOC_MEMORY,
+			 "process image",
+			 "Replaces entire process image including code, data, heap, and stack")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(1, KAPI_EFFECT_MODIFY_STATE | KAPI_EFFECT_RESOURCE_DESTROY,
+			 "file descriptors",
+			 "Closes all file descriptors with close-on-exec flag set")
+		KAPI_EFFECT_CONDITION("FD_CLOEXEC flag set")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(2, KAPI_EFFECT_MODIFY_STATE,
+			 "signal handlers",
+			 "Resets all signal handlers to default, preserves ignored signals")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(3, KAPI_EFFECT_PROCESS_STATE | KAPI_EFFECT_SIGNAL_SEND,
+			 "thread group",
+			 "Kills all other threads in the thread group with SIGKILL")
+		KAPI_EFFECT_CONDITION("Multi-threaded process")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(4, KAPI_EFFECT_MODIFY_STATE,
+			 "process attributes",
+			 "Clears pending signals, timers, alternate signal stack, and various process attributes")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(5, KAPI_EFFECT_FILESYSTEM,
+			 "executable file",
+			 "Opens and reads the executable file, may trigger filesystem operations")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT_COUNT(6)
+
+	/* State transitions */
+	KAPI_STATE_TRANS(0, "process memory",
+			 "old program image", "new program image",
+			 "Complete replacement of process address space with new program")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(1, "process credentials",
+			 "current credentials", "potentially modified credentials",
+			 "May change effective UID/GID based on file permissions")
+		KAPI_STATE_TRANS_COND("setuid/setgid binary")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(2, "thread state",
+			 "multi-threaded", "single-threaded",
+			 "Process becomes single-threaded after killing other threads")
+		KAPI_STATE_TRANS_COND("Multi-threaded process")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(3, "signal state",
+			 "custom handlers and pending signals", "default handlers, no pending signals",
+			 "Signal handling reset to clean state for new program")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS_COUNT(4)
+KAPI_END_SPEC;
 SYSCALL_DEFINE3(execve,
+
 		const char __user *, filename,
 		const char __user *const __user *, argv,
 		const char __user *const __user *, envp)
-- 
2.39.5


^ permalink raw reply related

* [RFC 09/19] exec: add API specification for execveat
From: Sasha Levin @ 2025-06-14 13:48 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-api, workflows, tools, Sasha Levin
In-Reply-To: <20250614134858.790460-1-sashal@kernel.org>

Add comprehensive kernel API specification for the execveat() system
call.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 fs/exec.c | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 245 insertions(+)

diff --git a/fs/exec.c b/fs/exec.c
index 3d006105ab23d..49d8647c053ef 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -2223,6 +2223,251 @@ SYSCALL_DEFINE3(execve,
 	return do_execve(getname(filename), argv, envp);
 }
 
+
+/* Valid flag combinations for execveat */
+static const s64 execveat_valid_flags[] = {
+	0,
+	AT_EMPTY_PATH,
+	AT_SYMLINK_NOFOLLOW,
+	AT_EXECVE_CHECK,
+	AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW,
+	AT_EMPTY_PATH | AT_EXECVE_CHECK,
+	AT_SYMLINK_NOFOLLOW | AT_EXECVE_CHECK,
+	AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW | AT_EXECVE_CHECK,
+};
+
+DEFINE_KERNEL_API_SPEC(sys_execveat)
+	KAPI_DESCRIPTION("Execute a new program relative to a directory file descriptor")
+	KAPI_LONG_DESC("Executes the program referred to by the combination of fd and filename. "
+		       "This system call is useful when implementing a secure execution environment "
+		       "or when the calling process has an open file descriptor but no access to "
+		       "the corresponding pathname. Like execve(), it replaces the current process "
+		       "image with a new process image.")
+	KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
+
+	KAPI_PARAM(0, "fd", "int", "Directory file descriptor")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.type = KAPI_TYPE_FD,
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+		.constraints = "AT_FDCWD for current directory, or valid directory file descriptor",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(1, "filename", "const char __user *", "Pathname of the program to execute")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN | KAPI_PARAM_USER | KAPI_PARAM_OPTIONAL)
+		.type = KAPI_TYPE_PATH,
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+		.constraints = "Relative or absolute path; empty string with AT_EMPTY_PATH to use fd directly",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(2, "argv", "const char __user *const __user *", "Array of argument strings passed to the new program")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN | KAPI_PARAM_USER)
+		.type = KAPI_TYPE_USER_PTR,
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+		.constraints = "NULL-terminated array of pointers to null-terminated strings",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(3, "envp", "const char __user *const __user *", "Array of environment strings for the new program")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN | KAPI_PARAM_USER)
+		.type = KAPI_TYPE_USER_PTR,
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+		.constraints = "NULL-terminated array of pointers to null-terminated strings in form key=value",
+	KAPI_PARAM_END
+
+	KAPI_PARAM(4, "flags", "int", "Execution flags")
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.type = KAPI_TYPE_INT,
+		.constraint_type = KAPI_CONSTRAINT_MASK,
+		.valid_mask = AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW | AT_EXECVE_CHECK,
+		.constraints = "0 or combination of AT_EMPTY_PATH, AT_SYMLINK_NOFOLLOW, and AT_EXECVE_CHECK",
+	KAPI_PARAM_END
+
+	/* Return specification */
+	KAPI_RETURN("long", "Does not return on success (except with AT_EXECVE_CHECK which returns 0); returns -1 on error")
+		.type = KAPI_TYPE_INT,
+		.check_type = KAPI_RETURN_ERROR_CHECK,
+	KAPI_RETURN_END
+
+	/* Error codes */
+	KAPI_ERROR(0, -E2BIG, "E2BIG", "Argument list too long", "The total size of argv and envp exceeds the system limit.")
+	KAPI_ERROR(1, -EACCES, "EACCES", "Permission denied", "Search permission denied on a component of the path, file is not regular, or execute permission denied for file or interpreter.")
+	KAPI_ERROR(2, -EBADF, "EBADF", "Bad file descriptor", "fd is not a valid file descriptor.")
+	KAPI_ERROR(3, -EFAULT, "EFAULT", "Bad address", "filename, argv, or envp points outside accessible address space.")
+	KAPI_ERROR(4, -EINVAL, "EINVAL", "Invalid flags or executable format", "Invalid flags specified, or ELF executable has more than one PT_INTERP segment.")
+	KAPI_ERROR(5, -EIO, "EIO", "I/O error", "An I/O error occurred while reading from the file system.")
+	KAPI_ERROR(6, -EISDIR, "EISDIR", "Is a directory", "An ELF interpreter was a directory.")
+	KAPI_ERROR(7, -ELIBBAD, "ELIBBAD", "Invalid ELF interpreter", "An ELF interpreter was not in a recognized format.")
+	KAPI_ERROR(8, -ELOOP, "ELOOP", "Too many symbolic links", "Too many symbolic links encountered, or AT_SYMLINK_NOFOLLOW was specified but filename refers to a symbolic link.")
+	KAPI_ERROR(9, -EMFILE, "EMFILE", "Too many open files", "The per-process limit on open file descriptors has been reached.")
+	KAPI_ERROR(10, -ENAMETOOLONG, "ENAMETOOLONG", "Filename too long", "filename or one of the strings in argv or envp is too long.")
+	KAPI_ERROR(11, -ENFILE, "ENFILE", "System file table overflow", "The system-wide limit on open files has been reached.")
+	KAPI_ERROR(12, -ENOENT, "ENOENT", "File not found", "The file filename or an interpreter does not exist, or filename is empty and AT_EMPTY_PATH was not specified in flags.")
+	KAPI_ERROR(13, -ENOEXEC, "ENOEXEC", "Exec format error", "An executable is not in a recognized format, is for wrong architecture, or has other format errors preventing execution.")
+	KAPI_ERROR(14, -ENOMEM, "ENOMEM", "Out of memory", "Insufficient kernel memory available.")
+	KAPI_ERROR(15, -ENOTDIR, "ENOTDIR", "Not a directory", "A component of the path prefix is not a directory, or fd is not a directory when a relative path is given.")
+	KAPI_ERROR(16, -EPERM, "EPERM", "Operation not permitted", "The filesystem is mounted nosuid, the user is not root, and the file has set-user-ID or set-group-ID bit set.")
+	KAPI_ERROR(17, -ETXTBSY, "ETXTBSY", "Text file busy", "The executable was open for writing by one or more processes.")
+	KAPI_ERROR(18, -EAGAIN, "EAGAIN", "Resource temporarily unavailable", "RLIMIT_NPROC limit exceeded - too many processes for this user.")
+	KAPI_ERROR(19, -EINTR, "EINTR", "Interrupted by signal", "The exec was interrupted by a signal during setup phase.")
+
+	/* Signal specifications */
+	KAPI_SIGNAL(0, 0, "FATAL_SIGNALS", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_TERMINATE)
+		KAPI_SIGNAL_CONDITION("Fatal signal pending during exec setup")
+		KAPI_SIGNAL_DESC("Fatal signals (checked via fatal_signal_pending()) can interrupt exec during setup phases like de_thread(). This causes exec to fail and the process to exit.")
+		KAPI_SIGNAL_RESTARTABLE
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(1, SIGKILL, "SIGKILL", KAPI_SIGNAL_SEND, KAPI_SIGNAL_ACTION_TERMINATE)
+		KAPI_SIGNAL_TARGET("All other threads in the thread group")
+		KAPI_SIGNAL_CONDITION("Multi-threaded process doing exec")
+		KAPI_SIGNAL_DESC("During de_thread(), zap_other_threads() sends SIGKILL to all other threads in the thread group to ensure only the execing thread survives.")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(2, 0, "ALL_HANDLERS", KAPI_SIGNAL_HANDLE, KAPI_SIGNAL_ACTION_CUSTOM)
+		KAPI_SIGNAL_CONDITION("Signal has a handler installed")
+		KAPI_SIGNAL_DESC("flush_signal_handlers() resets all signal handlers to SIG_DFL except for signals that are ignored (SIG_IGN). This happens after de_thread() completes.")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(3, 0, "IGNORED_SIGNALS", KAPI_SIGNAL_IGNORE, KAPI_SIGNAL_ACTION_CUSTOM)
+		KAPI_SIGNAL_CONDITION("Signal disposition is SIG_IGN")
+		KAPI_SIGNAL_DESC("Signals set to SIG_IGN are preserved across exec. This is POSIX-compliant behavior allowing parent processes to ignore signals in children.")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(4, 0, "PENDING_SIGNALS", KAPI_SIGNAL_HANDLE, KAPI_SIGNAL_ACTION_CUSTOM)
+		KAPI_SIGNAL_CONDITION("Any pending signals")
+		KAPI_SIGNAL_DESC("All pending signals are cleared during exec. This includes both thread-specific and process-wide pending signals.")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(5, 0, "TIMER_SIGNALS", KAPI_SIGNAL_HANDLE, KAPI_SIGNAL_ACTION_CUSTOM)
+		KAPI_SIGNAL_CONDITION("Timer-generated signals pending")
+		KAPI_SIGNAL_DESC("flush_itimer_signals() clears any pending timer signals (SIGALRM, SIGVTALRM, SIGPROF) to prevent confusion in the new program.")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(6, SIGCHLD, "SIGCHLD", KAPI_SIGNAL_SEND, KAPI_SIGNAL_ACTION_DEFAULT)
+		KAPI_SIGNAL_TARGET("Parent process when this process exits")
+		KAPI_SIGNAL_CONDITION("Process exit after exec")
+		KAPI_SIGNAL_DESC("The exit_signal is set to SIGCHLD during exec, ensuring the parent will receive SIGCHLD when this process terminates.")
+	KAPI_SIGNAL_END
+
+	KAPI_SIGNAL(7, 0, "SIGALTSTACK", KAPI_SIGNAL_HANDLE, KAPI_SIGNAL_ACTION_CUSTOM)
+		KAPI_SIGNAL_CONDITION("Process had alternate signal stack")
+		KAPI_SIGNAL_DESC("Any alternate signal stack (sigaltstack) is not preserved across exec. The new program starts with no alternate stack.")
+	KAPI_SIGNAL_END
+
+	/* Side effects */
+	KAPI_SIDE_EFFECT(0, KAPI_EFFECT_PROCESS_STATE | KAPI_EFFECT_FREE_MEMORY | KAPI_EFFECT_ALLOC_MEMORY,
+			 "process image",
+			 "Replaces entire process image including code, data, heap, and stack")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(1, KAPI_EFFECT_MODIFY_STATE | KAPI_EFFECT_RESOURCE_DESTROY,
+			 "file descriptors",
+			 "Closes all file descriptors with close-on-exec flag set")
+		KAPI_EFFECT_CONDITION("FD_CLOEXEC flag set")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(2, KAPI_EFFECT_MODIFY_STATE,
+			 "signal handlers",
+			 "Resets all signal handlers to default, preserves ignored signals")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(3, KAPI_EFFECT_PROCESS_STATE | KAPI_EFFECT_SIGNAL_SEND,
+			 "thread group",
+			 "Kills all other threads in the thread group with SIGKILL")
+		KAPI_EFFECT_CONDITION("Multi-threaded process")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(4, KAPI_EFFECT_MODIFY_STATE,
+			 "process attributes",
+			 "Clears pending signals, timers, alternate signal stack, and various process attributes")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(5, KAPI_EFFECT_FILESYSTEM,
+			 "executable file",
+			 "Opens and reads the executable file, may trigger filesystem operations")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(6, KAPI_EFFECT_MODIFY_STATE,
+			 "security context",
+			 "May change SELinux/AppArmor context based on file labels and transitions")
+		KAPI_EFFECT_CONDITION("LSM enabled")
+	KAPI_SIDE_EFFECT_END
+
+	/* State transitions */
+	KAPI_STATE_TRANS(0, "process memory",
+			 "old program image", "new program image",
+			 "Complete replacement of process address space with new program")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(1, "process credentials",
+			 "current credentials", "potentially modified credentials",
+			 "May change effective UID/GID based on file permissions")
+		KAPI_STATE_TRANS_COND("setuid/setgid binary")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(2, "thread state",
+			 "multi-threaded", "single-threaded",
+			 "Process becomes single-threaded after killing other threads")
+		KAPI_STATE_TRANS_COND("Multi-threaded process")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(3, "signal state",
+			 "custom handlers and pending signals", "default handlers, no pending signals",
+			 "Signal handling reset to clean state for new program")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(4, "file descriptor table",
+			 "contains close-on-exec FDs", "close-on-exec FDs closed",
+			 "All file descriptors marked FD_CLOEXEC are closed during exec")
+		KAPI_STATE_TRANS_COND("FDs with FD_CLOEXEC")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(5, "working directory",
+			 "fd-relative operations", "resolved to absolute paths",
+			 "Directory fd operations resolved before exec completes")
+		KAPI_STATE_TRANS_COND("Using dirfd != AT_FDCWD")
+	KAPI_STATE_TRANS_END
+
+	/* Locking information */
+	KAPI_LOCK(0, "cred_guard_mutex", KAPI_LOCK_MUTEX)
+		KAPI_LOCK_DESC("Protects against concurrent credential changes during exec")
+		KAPI_LOCK_ACQUIRED
+		KAPI_LOCK_DESC("Ensures atomic credential transition during exec process")
+	KAPI_LOCK_END
+
+	KAPI_LOCK(1, "sighand->siglock", KAPI_LOCK_SPINLOCK)
+		KAPI_LOCK_DESC("Protects signal handler modifications")
+		KAPI_LOCK_ACQUIRED
+		KAPI_LOCK_RELEASED
+		KAPI_LOCK_DESC("Taken during signal handler reset and pending signal clearing")
+	KAPI_LOCK_END
+
+	KAPI_SIDE_EFFECT_COUNT(7)
+	KAPI_STATE_TRANS_COUNT(6)
+
+	.error_count = 20,
+	.param_count = 5,
+	.since_version = "3.19",
+	.examples = "/* Execute /bin/echo using AT_FDCWD */\n"
+		    "char *argv[] = { \"echo\", \"hello\", NULL };\n"
+		    "char *envp[] = { \"PATH=/bin\", NULL };\n"
+		    "execveat(AT_FDCWD, \"/bin/echo\", argv, envp, 0);\n\n"
+		    "/* Execute via file descriptor */\n"
+		    "int fd = open(\"/bin/echo\", O_PATH);\n"
+		    "execveat(fd, \"\", argv, envp, AT_EMPTY_PATH);\n\n"
+		    "/* Execute relative to directory fd */\n"
+		    "int dirfd = open(\"/bin\", O_RDONLY | O_DIRECTORY);\n"
+		    "execveat(dirfd, \"echo\", argv, envp, 0);",
+	.notes = "execveat() was added to allow fexecve() to be implemented on systems that "
+		 "do not have /proc mounted. When filename is an empty string and AT_EMPTY_PATH "
+		 "is specified, the file descriptor fd specifies the file to be executed. "
+		 "AT_SYMLINK_NOFOLLOW prevents following symbolic links. "
+		 "AT_EXECVE_CHECK (since Linux 6.12) only checks if execution would be allowed "
+		 "without actually executing. Like execve(), on success execveat() does not return "
+		 "(except with AT_EXECVE_CHECK which returns 0).",
+	.signal_count = 8,
+	.lock_count = 2,
+KAPI_END_SPEC;
+
 SYSCALL_DEFINE5(execveat,
 		int, fd, const char __user *, filename,
 		const char __user *const __user *, argv,
-- 
2.39.5


^ permalink raw reply related

* [RFC 10/19] mm/mlock: add API specification for mlock
From: Sasha Levin @ 2025-06-14 13:48 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-api, workflows, tools, Sasha Levin
In-Reply-To: <20250614134858.790460-1-sashal@kernel.org>

Add kernel API specification for the mlock() system call.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 mm/mlock.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 105 insertions(+)

diff --git a/mm/mlock.c b/mm/mlock.c
index 3cb72b579ffd3..a37102df54b01 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -25,6 +25,7 @@
 #include <linux/memcontrol.h>
 #include <linux/mm_inline.h>
 #include <linux/secretmem.h>
+#include <linux/syscall_api_spec.h>
 
 #include "internal.h"
 
@@ -658,6 +659,110 @@ static __must_check int do_mlock(unsigned long start, size_t len, vm_flags_t fla
 	return 0;
 }
 
+
+DEFINE_KERNEL_API_SPEC(sys_mlock)
+	KAPI_DESCRIPTION("Lock pages in memory")
+	KAPI_LONG_DESC("Locks pages in the specified address range into RAM, "
+		       "preventing them from being paged to swap. Requires "
+		       "CAP_IPC_LOCK capability or RLIMIT_MEMLOCK resource limit.")
+	KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
+
+	KAPI_PARAM(0, "start", "unsigned long", "Starting address of memory range to lock")
+		.type = KAPI_TYPE_UINT,
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.constraint_type = KAPI_CONSTRAINT_NONE,
+		.constraints = "Rounded down to page boundary",
+	KAPI_PARAM_END
+	KAPI_PARAM(1, "len", "size_t", "Length of memory range to lock in bytes")
+		.type = KAPI_TYPE_UINT,
+		KAPI_PARAM_FLAGS(KAPI_PARAM_IN)
+		.constraint_type = KAPI_CONSTRAINT_RANGE,
+		KAPI_PARAM_RANGE(0, LONG_MAX)
+		.constraints = "Rounded up to page boundary",
+	KAPI_PARAM_END
+
+	.return_spec = {
+		.type_name = "long",
+		.type = KAPI_TYPE_INT,
+		.check_type = KAPI_RETURN_ERROR_CHECK,
+		.success_value = 0,
+		.description = "0 on success, negative error code on failure",
+	},
+
+	KAPI_ERROR(0, -ENOMEM, "ENOMEM", "Address range issue",
+		   "Some of the specified range is not mapped, has unmapped gaps, "
+		   "or the lock would cause the number of mapped regions to exceed the limit.")
+	KAPI_ERROR(1, -EPERM, "EPERM", "Insufficient privileges",
+		   "The caller is not privileged (no CAP_IPC_LOCK) and RLIMIT_MEMLOCK is 0.")
+	KAPI_ERROR(2, -EINVAL, "EINVAL", "Address overflow",
+		   "The result of the addition start+len was less than start (arithmetic overflow).")
+	KAPI_ERROR(3, -EAGAIN, "EAGAIN", "Some or all memory could not be locked",
+		   "Some or all of the specified address range could not be locked.")
+	KAPI_ERROR(4, -EINTR, "EINTR", "Interrupted by signal",
+		   "The operation was interrupted by a fatal signal before completion.")
+
+	.error_count = 5,
+	.param_count = 2,
+	.since_version = "2.0",
+
+	.locks[0] = {
+		.lock_name = "mmap_lock",
+		.lock_type = KAPI_LOCK_RWLOCK,
+		.acquired = true,
+		.released = true,
+		.description = "Process memory map write lock",
+	},
+	.lock_count = 1,
+
+	/* Signal specifications */
+	.signal_count = 1,
+
+	/* Fatal signals can interrupt mmap_write_lock_killable */
+	KAPI_SIGNAL(0, 0, "FATAL", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN)
+		KAPI_SIGNAL_CONDITION("Fatal signal pending")
+		KAPI_SIGNAL_DESC("Fatal signals (SIGKILL, etc.) can interrupt the operation "
+				 "when acquiring mmap_write_lock_killable(), causing -EINTR return")
+	KAPI_SIGNAL_END
+
+	.examples = "mlock(addr, 4096);  // Lock one page\n"
+		    "mlock(addr, len);   // Lock range of pages",
+	.notes = "Memory locks do not stack - multiple calls on the same range can be "
+		 "undone by a single munlock. Locks are not inherited by child processes. "
+		 "Pages are locked on whole page boundaries.",
+
+	/* Side effects */
+	KAPI_SIDE_EFFECT(0, KAPI_EFFECT_MODIFY_STATE | KAPI_EFFECT_ALLOC_MEMORY,
+			 "process memory",
+			 "Locks pages into physical memory, preventing swapping")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(1, KAPI_EFFECT_MODIFY_STATE,
+			 "mm->locked_vm",
+			 "Increases process locked memory counter")
+		KAPI_EFFECT_REVERSIBLE
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT(2, KAPI_EFFECT_ALLOC_MEMORY,
+			 "physical pages",
+			 "May allocate and populate page table entries")
+		KAPI_EFFECT_CONDITION("Pages not already present")
+	KAPI_SIDE_EFFECT_END
+
+	KAPI_SIDE_EFFECT_COUNT(3)
+
+	/* State transitions */
+	KAPI_STATE_TRANS(0, "memory pages", "swappable", "locked in RAM",
+			 "Pages become non-swappable and pinned in physical memory")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS(1, "VMA flags", "unlocked", "VM_LOCKED set",
+			 "Virtual memory area marked as locked")
+	KAPI_STATE_TRANS_END
+
+	KAPI_STATE_TRANS_COUNT(2)
+KAPI_END_SPEC;
+
 SYSCALL_DEFINE2(mlock, unsigned long, start, size_t, len)
 {
 	return do_mlock(start, len, VM_LOCKED);
-- 
2.39.5


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox