2024-02-14 23:57:48

by Elizabeth Figura

[permalink] [raw]
Subject: [PATCH 00/31] NT synchronization primitive driver

This patch series introduces a new char misc driver, /dev/ntsync, which is used
to implement Windows NT synchronization primitives.

This was previously submitted as an RFC [1]. Since there were no major changes
requested to the last RFC revision, I've stripped the RFC prefix.

[1] https://lore.kernel.org/lkml/[email protected]/

== 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 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 31/31 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


== Implementation ==

Some aspects of the implementation may deserve particular comment:

* In the interest of performance, each object is governed only by a single
spinlock. However, NTSYNC_IOC_WAIT_ALL requires that the state of multiple
objects be changed as a single atomic operation. In order to achieve this, we
first take a device-wide lock ("wait_all_lock") any time we are going to lock
more than one object at a time.

The maximum number of objects that can be used in a vectored wait, and
therefore the maximum that can be locked simultaneously, is 64. This number is
NT's own limit.

The acquisition of multiple spinlocks will degrade performance. This is a
conscious choice, however. Wait-for-all is known to be a very rare operation
in practice, especially with counts that approach the maximum, and it is the
intent of the ntsync driver to optimize wait-for-any at the expense of
wait-for-all as much as possible.

* NT mutexes are tied to their threads on an OS level, and the kernel includes
builtin support for "robust" mutexes. In order to keep the ntsync driver
self-contained and avoid touching more code than necessary, it does not hook
into task exit nor use pids.

Instead, the user space emulator is expected to manage thread IDs and pass
them as an argument to any relevant functions; this is the "owner" field of
ntsync_wait_args and ntsync_mutex_args.

When the emulator detects that a thread dies, it should therefore call
NTSYNC_IOC_MUTEX_KILL on any open mutexes.

* ntsync is module-capable mostly because there was nothing preventing it, and
because it aided development. It is not a hard requirement, though.


== Previous versions ==

Changes from the last (v2) RFC:

* Add a new wait flag NTSYNC_WAIT_REALTIME. I had originally missed a corner
case in NtWaitForMultipleObjects() related to its interaction with system time
adjustments. Essentially the function is sometimes supposed to respect system
time adjustments and sometimes supposed to ignore them, so in order to achieve
this I've added a function that controls which flag is being synchronized to.
Thanks Piotr Caban for catching this.

* Add tests for overflowing semaphore and mutex counters, and a test for
exceeding NTSYNC_MAX_WAIT_COUNT, per Andi Kleen.

* Add a more intense and realistic test involving multiple threads using the
same mutex to access data, per Andi Kleen.

* Use check_add_overflow() instead of writing out overflow checking manually
[and thereby avoid relying on -fwrapv].

* Add some missing headers that were being implicitly included: atomic.h,
hrtimer.h, ktime.h, sched.h, sched/signal.h, spinlock.h.

* Link to RFC v2: https://lore.kernel.org/lkml/[email protected]/
* Link to RFC v1: https://lore.kernel.org/lkml/[email protected]/

Elizabeth Figura (31):
ntsync: Introduce the ntsync driver and character device.
ntsync: Introduce NTSYNC_IOC_CREATE_SEM.
ntsync: Introduce NTSYNC_IOC_SEM_POST.
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.
ntsync: Allow waits to use the REALTIME clock.
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.

Documentation/userspace-api/index.rst | 1 +
.../userspace-api/ioctl/ioctl-number.rst | 2 +
Documentation/userspace-api/ntsync.rst | 399 +++++
MAINTAINERS | 9 +
drivers/misc/Kconfig | 9 +
drivers/misc/Makefile | 1 +
drivers/misc/ntsync.c | 1146 ++++++++++++++
include/uapi/linux/ntsync.h | 62 +
tools/testing/selftests/Makefile | 1 +
.../testing/selftests/drivers/ntsync/Makefile | 8 +
tools/testing/selftests/drivers/ntsync/config | 1 +
.../testing/selftests/drivers/ntsync/ntsync.c | 1407 +++++++++++++++++
12 files changed, 3046 insertions(+)
create mode 100644 Documentation/userspace-api/ntsync.rst
create mode 100644 drivers/misc/ntsync.c
create mode 100644 include/uapi/linux/ntsync.h
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: e21817acb23ece75d41a4fa7b40c85550f147389
--
2.43.0



2024-02-15 00:03:01

by Elizabeth Figura

[permalink] [raw]
Subject: Re: [PATCH 00/31] NT synchronization primitive driver

On Wednesday, 14 February 2024 17:36:36 CST Elizabeth Figura wrote:
> This patch series introduces a new char misc driver, /dev/ntsync, which is used
> to implement Windows NT synchronization primitives.

Ugh, sorry, I made a bit of a mess while sending this revision. I accidentally
sent 000* instead of 00*, and then tried to fix it by sending the rest with
--in-reply-to but forgot to also add --no-thread. Please let me know if I should
resend the whole series.

--Zeb



2024-02-15 07:39:09

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH 00/31] NT synchronization primitive driver

On Wed, Feb 14, 2024 at 06:01:22PM -0600, Elizabeth Figura wrote:
> On Wednesday, 14 February 2024 17:36:36 CST Elizabeth Figura wrote:
> > This patch series introduces a new char misc driver, /dev/ntsync, which is used
> > to implement Windows NT synchronization primitives.
>
> Ugh, sorry, I made a bit of a mess while sending this revision. I accidentally
> sent 000* instead of 00*, and then tried to fix it by sending the rest with
> --in-reply-to but forgot to also add --no-thread. Please let me know if I should
> resend the whole series.

Please resend the whole thing, as a v2 series, as our tools can't pick
this up easily as-is.

thanks,

greg k-h

2024-02-16 16:31:44

by Alexey Dobriyan

[permalink] [raw]
Subject: Re: [PATCH 00/31] NT synchronization primitive driver

> drivers/misc/ntsync.c | 1146 ++++++++++++++

Assuming this doesn't go into futex(2) or some other existing code...

Can you start putting all of this into top-level "windows" directory?
I suspect there will be more Windows stuff in the future.

So those who don't care about Windows can turn off just one config option
(CONFIG_WINDOWS) and be done with it.

Name it "Linux Subsystem for Windows" for 146% better memes.

2024-02-17 08:02:07

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH 00/31] NT synchronization primitive driver

On Fri, Feb 16, 2024 at 07:31:12PM +0300, Alexey Dobriyan wrote:
> > drivers/misc/ntsync.c | 1146 ++++++++++++++
>
> Assuming this doesn't go into futex(2) or some other existing code...
>
> Can you start putting all of this into top-level "windows" directory?
> I suspect there will be more Windows stuff in the future.

There will? Like what?

> So those who don't care about Windows can turn off just one config option
> (CONFIG_WINDOWS) and be done with it.

This should all be configured under one option anyway, so I don't see
the need.

> Name it "Linux Subsystem for Windows" for 146% better memes.

Fun with marketing! :)

greg k-h

2024-02-21 16:49:33

by Alexey Dobriyan

[permalink] [raw]
Subject: Re: [PATCH 00/31] NT synchronization primitive driver

On Sat, Feb 17, 2024 at 09:01:53AM +0100, Greg Kroah-Hartman wrote:
> On Fri, Feb 16, 2024 at 07:31:12PM +0300, Alexey Dobriyan wrote:
> > > drivers/misc/ntsync.c | 1146 ++++++++++++++
> >
> > Assuming this doesn't go into futex(2) or some other existing code...
> >
> > Can you start putting all of this into top-level "windows" directory?
> > I suspect there will be more Windows stuff in the future.
>
> There will? Like what?

I don't know, native PE loader is probably a must for running Excel natively.

> > So those who don't care about Windows can turn off just one config option
> > (CONFIG_WINDOWS) and be done with it.
>
> This should all be configured under one option anyway, so I don't see
> the need.

It is handy to have 1 obvious place to nuke some features completely.
Especially for polarising ones like Windows support.

> > Name it "Linux Subsystem for Windows" for 146% better memes.
>
> Fun with marketing! :)

If they can "Subsystem for Linux", we can too.