mbox series

[v6,00/28] NT synchronization primitive driver

Message ID 20241209185904.507350-1-zfigura@codeweavers.com (mailing list archive)
Headers show
Series NT synchronization primitive driver | expand

Message

Elizabeth Figura Dec. 9, 2024, 6:58 p.m. UTC
This patch series implements a new char misc driver, /dev/ntsync, which is used
to implement Windows NT synchronization primitives.

NT synchronization primitives are unique in that the wait functions both are
vectored, operate on multiple types of object with different behaviour (mutex,
semaphore, event), and affect the state of the objects they wait on. This model
is not compatible with existing kernel synchronization objects or interfaces,
and therefore the ntsync driver implements its own wait queues and locking.

This patch series is rebased against the "char-misc-next" branch of
gregkh/char-misc.git.

== Background ==

The Wine project emulates the Windows API in user space. One particular part of
that API, namely the NT synchronization primitives, have historically been
implemented via RPC to a dedicated "kernel" process. However, more recent
applications use these APIs more strenuously, and the overhead of RPC has become
a bottleneck.

The NT synchronization APIs are too complex to implement on top of existing
primitives without sacrificing correctness. Certain operations, such as
NtPulseEvent() or the "wait-for-all" mode of NtWaitForMultipleObjects(), require
direct control over the underlying wait queue, and implementing a wait queue
sufficiently robust for Wine in user space is not possible. This proposed
driver, therefore, implements the problematic interfaces directly in the Linux
kernel.

This driver was presented at Linux Plumbers Conference 2023. For those further
interested in the history of synchronization in Wine and past attempts to solve
this problem in user space, a recording of the presentation can be viewed here:

    https://www.youtube.com/watch?v=NjU4nyWyhU8


== Performance ==

The performance measurements described below are copied from earlier versions of
the patch set. While some of the code has changed, I do not currently anticipate
that it has changed drastically enough to affect those measurements.

The gain in performance varies wildly depending on the application in question
and the user's hardware. For some games NT synchronization is not a bottleneck
and no change can be observed, but for others frame rate improvements of 50 to
150 percent are not atypical. The following table lists frame rate measurements
from a variety of games on a variety of hardware, taken by users Dmitry
Skvortsov, FuzzyQuils, OnMars, and myself:

Game                            Upstream        ntsync          improvement
===========================================================================
Anger Foot                       69              99              43%
Call of Juarez                   99.8           224.1           125%
Dirt 3                          110.6           860.7           678%
Forza Horizon 5                 108             160              48%
Lara Croft: Temple of Osiris    141             326             131%
Metro 2033                      164.4           199.2            21%
Resident Evil 2                  26              77             196%
The Crew                         26              51              96%
Tiny Tina's Wonderlands         130             360             177%
Total War Saga: Troy            109             146              34%
===========================================================================


== Patches ==

The intended semantics of the patches are broadly intended to match those of the
corresponding Windows functions. For those not already familiar with the Windows
functions (or their undocumented behaviour), patch 27/28 provides a detailed
specification, and individual patches also include a brief description of the
API they are implementing.

The patches making use of this driver in Wine can be retrieved or browsed here:

    https://repo.or.cz/wine/zf.git/shortlog/refs/heads/ntsync5

== Previous versions ==

No changes were made from v5 other than rebasing on top of the 6.13-rc1
char-misc-next tree.

I would like to repeat a question from the last round of review, though. Two
changes were suggested related to API design, which I did not make because the
APIs in question were already released in upstream Linux. However, the driver is
also completely nonfunctional and hidden behind BROKEN, so would this be
acceptable anyway? The changes in question are:

* rename NTSYNC_IOC_SEM_POST to NTSYNC_IOC_SEM_RELEASE (matching the NT
  terminology instead of POSIX),

* change object creation ioctls to return the fds directly in the return value
  instead of through the args struct. I would also still appreciate a
  clarification on the advice in [1], which is why I didn't do this in the first
  place.

  [1] https://docs.kernel.org/driver-api/ioctl.html#return-code

* Link to v5: https://lore.kernel.org/lkml/20240519202454.1192826-1-zfigura@codeweavers.com/
* Link to v4: https://lore.kernel.org/lkml/20240416010837.333694-1-zfigura@codeweavers.com/
* Link to v3: https://lore.kernel.org/lkml/20240329000621.148791-1-zfigura@codeweavers.com/
* Link to v2: https://lore.kernel.org/lkml/20240219223833.95710-1-zfigura@codeweavers.com/
* Link to v1: https://lore.kernel.org/lkml/20240214233645.9273-1-zfigura@codeweavers.com/
* Link to RFC v2: https://lore.kernel.org/lkml/20240131021356.10322-1-zfigura@codeweavers.com/
* Link to RFC v1: https://lore.kernel.org/lkml/20240124004028.16826-1-zfigura@codeweavers.com/

Elizabeth Figura (28):
  ntsync: Introduce NTSYNC_IOC_WAIT_ANY.
  ntsync: Introduce NTSYNC_IOC_WAIT_ALL.
  ntsync: Introduce NTSYNC_IOC_CREATE_MUTEX.
  ntsync: Introduce NTSYNC_IOC_MUTEX_UNLOCK.
  ntsync: Introduce NTSYNC_IOC_MUTEX_KILL.
  ntsync: Introduce NTSYNC_IOC_CREATE_EVENT.
  ntsync: Introduce NTSYNC_IOC_EVENT_SET.
  ntsync: Introduce NTSYNC_IOC_EVENT_RESET.
  ntsync: Introduce NTSYNC_IOC_EVENT_PULSE.
  ntsync: Introduce NTSYNC_IOC_SEM_READ.
  ntsync: Introduce NTSYNC_IOC_MUTEX_READ.
  ntsync: Introduce NTSYNC_IOC_EVENT_READ.
  ntsync: Introduce alertable waits.
  selftests: ntsync: Add some tests for semaphore state.
  selftests: ntsync: Add some tests for mutex state.
  selftests: ntsync: Add some tests for NTSYNC_IOC_WAIT_ANY.
  selftests: ntsync: Add some tests for NTSYNC_IOC_WAIT_ALL.
  selftests: ntsync: Add some tests for wakeup signaling with
    WINESYNC_IOC_WAIT_ANY.
  selftests: ntsync: Add some tests for wakeup signaling with
    WINESYNC_IOC_WAIT_ALL.
  selftests: ntsync: Add some tests for manual-reset event state.
  selftests: ntsync: Add some tests for auto-reset event state.
  selftests: ntsync: Add some tests for wakeup signaling with events.
  selftests: ntsync: Add tests for alertable waits.
  selftests: ntsync: Add some tests for wakeup signaling via alerts.
  selftests: ntsync: Add a stress test for contended waits.
  maintainers: Add an entry for ntsync.
  docs: ntsync: Add documentation for the ntsync uAPI.
  ntsync: No longer depend on BROKEN.

 Documentation/userspace-api/index.rst         |    1 +
 Documentation/userspace-api/ntsync.rst        |  398 +++++
 MAINTAINERS                                   |    9 +
 drivers/misc/Kconfig                          |    1 -
 drivers/misc/ntsync.c                         |  989 +++++++++++-
 include/uapi/linux/ntsync.h                   |   39 +
 tools/testing/selftests/Makefile              |    1 +
 .../selftests/drivers/ntsync/.gitignore       |    1 +
 .../testing/selftests/drivers/ntsync/Makefile |    7 +
 tools/testing/selftests/drivers/ntsync/config |    1 +
 .../testing/selftests/drivers/ntsync/ntsync.c | 1407 +++++++++++++++++
 11 files changed, 2850 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/userspace-api/ntsync.rst
 create mode 100644 tools/testing/selftests/drivers/ntsync/.gitignore
 create mode 100644 tools/testing/selftests/drivers/ntsync/Makefile
 create mode 100644 tools/testing/selftests/drivers/ntsync/config
 create mode 100644 tools/testing/selftests/drivers/ntsync/ntsync.c


base-commit: cdd30ebb1b9f36159d66f088b61aee264e649d7a

Comments

Arnd Bergmann Dec. 9, 2024, 8:24 p.m. UTC | #1
On Mon, Dec 9, 2024, at 19:58, Elizabeth Figura wrote:
> == Previous versions ==
>
> No changes were made from v5 other than rebasing on top of the 6.13-rc1
> char-misc-next tree.
>
> I would like to repeat a question from the last round of review, though. Two
> changes were suggested related to API design, which I did not make because the
> APIs in question were already released in upstream Linux. However, the driver is
> also completely nonfunctional and hidden behind BROKEN, so would this be
> acceptable anyway? The changes in question are:

If it was impossible to use the driver, there is no regression.
I feel the entire point of marking it as broken was to be able
to add that type of change.

> * rename NTSYNC_IOC_SEM_POST to NTSYNC_IOC_SEM_RELEASE (matching the NT
>   terminology instead of POSIX),

No objections my me on either name.

> * change object creation ioctls to return the fds directly in the return value
>   instead of through the args struct. I would also still appreciate a
>   clarification on the advice in [1], which is why I didn't do this in the first
>   place.
>
>   [1] https://docs.kernel.org/driver-api/ioctl.html#return-code

The git log tells me that I have written that, but I don't remember
why I put that in, maybe someone else suggested it.

My feeling right now is that returning a file descriptor number
as a small positive integer from the ioctl() return code makes
sense. On the other hand, returning pointers, negative signed
integers or large (> 32bit) 'unsigned long' values can cause
a number of issues, so I would avoid all those the same way we
discourage passing those integers as a literal 'arg' into ioctl()
instead of going through a pointer.

So either way, this looks good to me. I also looked through the
series again to double-check that you avoid the usual common
problems we list in Documentation/driver-api/ioctl.rst, and
I found this is all fine.

So with or without the two changes you listed:

Acked-by: Arnd Bergmann <arnd@arndb.de>

      Arnd
Elizabeth Figura Dec. 9, 2024, 10:08 p.m. UTC | #2
On Monday, 9 December 2024 14:24:36 CST Arnd Bergmann wrote:
> On Mon, Dec 9, 2024, at 19:58, Elizabeth Figura wrote:
> > == Previous versions ==
> >
> > No changes were made from v5 other than rebasing on top of the 6.13-rc1
> > char-misc-next tree.
> >
> > I would like to repeat a question from the last round of review, though. Two
> > changes were suggested related to API design, which I did not make because the
> > APIs in question were already released in upstream Linux. However, the driver is
> > also completely nonfunctional and hidden behind BROKEN, so would this be
> > acceptable anyway? The changes in question are:
> 
> If it was impossible to use the driver, there is no regression.
> I feel the entire point of marking it as broken was to be able
> to add that type of change.

Makes sense. [I figured that the BROKEN was just there to prevent anyone from trying to use a half-finished driver, and the point of committing a half-finished driver was just to reduce the number of patches that needed to be resent.]

I'll make these changes and resend.

> > * rename NTSYNC_IOC_SEM_POST to NTSYNC_IOC_SEM_RELEASE (matching the NT
> >   terminology instead of POSIX),
> 
> No objections my me on either name.
> 
> > * change object creation ioctls to return the fds directly in the return value
> >   instead of through the args struct. I would also still appreciate a
> >   clarification on the advice in [1], which is why I didn't do this in the first
> >   place.
> >
> >   [1] https://docs.kernel.org/driver-api/ioctl.html#return-code
> 
> The git log tells me that I have written that, but I don't remember
> why I put that in, maybe someone else suggested it.
> 
> My feeling right now is that returning a file descriptor number
> as a small positive integer from the ioctl() return code makes
> sense. On the other hand, returning pointers, negative signed
> integers or large (> 32bit) 'unsigned long' values can cause
> a number of issues, so I would avoid all those the same way we
> discourage passing those integers as a literal 'arg' into ioctl()
> instead of going through a pointer.

Ah, that makes sense to me, thanks.
Peter Zijlstra Dec. 12, 2024, 12:01 p.m. UTC | #3
On Mon, Dec 09, 2024 at 12:58:36PM -0600, Elizabeth Figura wrote:

> I would like to repeat a question from the last round of review, though. Two
> changes were suggested related to API design, which I did not make because the
> APIs in question were already released in upstream Linux. However, the driver is
> also completely nonfunctional and hidden behind BROKEN, so would this be
> acceptable anyway? The changes in question are:
> 
> * rename NTSYNC_IOC_SEM_POST to NTSYNC_IOC_SEM_RELEASE (matching the NT
>   terminology instead of POSIX),
> 
> * change object creation ioctls to return the fds directly in the return value
>   instead of through the args struct. I would also still appreciate a
>   clarification on the advice in [1], which is why I didn't do this in the first
>   place.

I see no problem making those changes; esp. since Arnd doesn't seem to
object to the latter.

> Elizabeth Figura (28):
>   ntsync: Introduce NTSYNC_IOC_WAIT_ANY.
>   ntsync: Introduce NTSYNC_IOC_WAIT_ALL.
>   ntsync: Introduce NTSYNC_IOC_CREATE_MUTEX.
>   ntsync: Introduce NTSYNC_IOC_MUTEX_UNLOCK.
>   ntsync: Introduce NTSYNC_IOC_MUTEX_KILL.
>   ntsync: Introduce NTSYNC_IOC_CREATE_EVENT.
>   ntsync: Introduce NTSYNC_IOC_EVENT_SET.
>   ntsync: Introduce NTSYNC_IOC_EVENT_RESET.
>   ntsync: Introduce NTSYNC_IOC_EVENT_PULSE.
>   ntsync: Introduce NTSYNC_IOC_SEM_READ.
>   ntsync: Introduce NTSYNC_IOC_MUTEX_READ.
>   ntsync: Introduce NTSYNC_IOC_EVENT_READ.
>   ntsync: Introduce alertable waits.

Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>