Re: [RFC PATCH 11/13] x86/uintr: Introduce uintr_wait() syscall

From: Sohil Mehta
Date: Tue Sep 28 2021 - 21:09:23 EST


On 9/26/2021 7:41 AM, Thomas Gleixner wrote:
On Mon, Sep 13 2021 at 13:01, Sohil Mehta wrote:
Add a new system call to allow applications to block in the kernel and
wait for user interrupts.

<The current implementation doesn't support waking up from other
blocking system calls like sleep(), read(), epoll(), etc.

uintr_wait() is a placeholder syscall while we decide on that
behaviour.>
Which behaviour? You cannot integrate this into [clock_]nanosleep() by
any means or wakeup something which is blocked in read(somefd) via
SENDUIPI.

That is the (wishful) desire.

The idea is to have a behavior similar to signals for all or a subset of system calls. i.e. return an EINTR by interrupting the blocked syscall and possibly have a SA_RESTART type of mechanism.

Can we use the existing signal infrastructure to generate a temporary in-kernel signal upon detection of an pending user interrupt? The temporary signal doesn't need to be delivered to application but it would just be a mechanism to interrupt the blocked syscall.

I don't know anything about the signaling subsystem nor have I tried prototyping this. So, all this might be completely baseless.


What you can do is implement read() and poll() support for the
uintrfd. Anything else is just not going to fly.

Adding support for read/poll is pretty much a straight forward variant
of a correctly implemented wait()/wakeup() mechanism.

I tried doing this but I ran into a couple of issues.

1) uintrfd is mapped to a single vector (out of 64). But there is no easy hardware mechanism to wait for specific vectors. Waiting for one vector might mean waiting for all.

2) The scope of uintrfd is process wide. Also, it would be shared with senders. But the wait/wake mechanism is specific to the task that created the fd and has a UPID allocated.
As you mentioned below, relaying the pending interrupt information of another task would be very tricky.


While poll()/read() support might be useful and poll() also provides a
timeout, having an explicit (timed) wait mechanism might be interesting.

I prototyped uintr_wait() with the same intention to have an explicit timed yield mechanism. There is very little ambiguity about who is waiting for what and how we would deliver the interrupts.


But that brings me to an interesting question. There are two cases:

1) The task installed a user space interrupt handler. Now it
want's to play nice and yield the CPU while waiting.

So it needs to reinstall the UINV vector on return to user and
update UIRR, but that'd be covered by the existing mechanism. Fine.

2) Task has no user space interrupt handler installed and just want's
to use that wait mechanism.

What is consuming the pending bit(s)?

If that's not a valid use case, then the wait has to check for that
and reject the syscall with EINVAL.

Yeah. I feel this is not a valid use case. But I am no application developer. I will try to seek more opinions here.


If it is valid, then how are the pending bits consumed and relayed to
user space?

This is very tricky. Because a task that owns the UPID might be consuming interrupts while the kernel tries to relay the pending interrupt information to another task.


The same questions arise when you think about implementing poll/read
support simply because the regular poll/read semantics are:

poll waits for the event and read consumes the event
which would be similar to #2 above, but with an installed user space
interrupt handler the return from the poll system call would consume the
event immediately (assumed that UIF is set).


Yup. There is no read data associated with uintrfd. This might be confusing for the application.

Overall, I feel signal handler semantics fit better with User interrupts handlers. But as you mentioned there might be no easy way to achieve that.

Thanks again for providing your input on this.

Sohil