Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/build-proton.yml
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ jobs:
Single SDK 28 build with 16KB page size support (works on Android 9+ and Android 15+).

### Features
- Runtime ntsync backend selection: kernel `/dev/ntsync` when present and usable (probed by wineserver at startup), otherwise experimental userspace ntsync via [ntsync-android](https://github.com/GameNative/ntsync-android) (commit [`7ce6435`](https://github.com/GameNative/ntsync-android/commit/7ce6435e5979b1cb5341aa4b299f31e8937fe121)) — plus fsync and wineserver-side `ntsync_event_set` support; `PROTON_NO_NTSYNC=1` disables ntsync entirely
- Runtime ntsync backend selection: kernel `/dev/ntsync` when present and usable (probed by wineserver at startup), otherwise experimental userspace ntsync via [ntsync-android](https://github.com/GameNative/ntsync-android) (commit [`7ce6435`](https://github.com/GameNative/ntsync-android/commit/7ce6435e5979b1cb5341aa4b299f31e8937fe121)) — plus fsync and wineserver-side `ntsync_event_set` support; `PROTON_NO_NTSYNC=1` disables ntsync entirely, `PROTON_NO_KERNEL_NTSYNC=1` forces the userspace backend
- `WINE_FAST_YIELD` fast-yield hook in `NtYieldExecution`
- FEX integration (stats shm, `SkipThreadAttach` detach guard) via bylaws' arm64ec patches
- FEX unixlib loader: `MemoryWineLoadUnixLibByName` / wow64 / unload support with `$PREFIX/lib/wine` fallback
Expand All @@ -340,8 +340,8 @@ jobs:
- Stripped, `-g0 -O2` build: smaller tree, faster install (zstd-packed `.wcp`)

### ntsync changes in this build
- **Runtime kernel/userspace backend detection (proton-wine side)** — ntsync is no longer hard-selected at compile time. wineserver probes `/dev/ntsync` at startup with a real `NTSYNC_IOC_CREATE_EVENT` ioctl (catches SELinux/seccomp-blocked nodes): if the kernel driver is present and usable it is used directly; otherwise the build falls back to the userspace ntsync-android shm backend. Backend is chosen once per session and applies to all processes.
- **No shm region on kernel-ntsync devices** — the dead-process sweep is now gated on the userspace backend being active, so the ~960 KB shm file is never initialized when the kernel driver handles ntsync.
- **`PROTON_NO_KERNEL_NTSYNC=1` forces the userspace backend** — new runtime escape hatch for A/B testing: wineserver skips `/dev/ntsync` even when the kernel driver is present and working, and initializes the userspace ntsync-android shm backend instead. The forced backend is identifiable in logs: `ntsync: PROTON_NO_KERNEL_NTSYNC set, using userspace ntsync.`
- Runtime kernel/userspace backend detection (probed `NTSYNC_IOC_CREATE_EVENT`, userspace fallback, no shm region on kernel-ntsync devices) shipped in the previous build and is unchanged.
- ntsync-android library is unchanged since the last build ([`7ce6435`](https://github.com/GameNative/ntsync-android/commit/7ce6435e5979b1cb5341aa4b299f31e8937fe121)); `PROTON_NO_NTSYNC=1` still disables both backends.

### Android / bionic fixes
Expand Down
10 changes: 7 additions & 3 deletions android/ntsync_android/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,19 @@ wineserver probes for in-process sync support once at startup

1. `PROTON_NO_NTSYNC=1` disables ntsync entirely (both kernel and
userspace); wineserver falls back to server-side synchronization.
2. Otherwise it opens `/dev/ntsync` and **probes** it with a real
2. `PROTON_NO_KERNEL_NTSYNC=1` skips the kernel driver even when it is
present and working, forcing the userspace backend
(`ntsync: PROTON_NO_KERNEL_NTSYNC set, using userspace ntsync.`).
Intended for A/B testing of the two backends.
3. Otherwise it opens `/dev/ntsync` and **probes** it with a real
`NTSYNC_IOC_CREATE_EVENT` ioctl (the node can exist but be unusable,
e.g. SELinux policy or seccomp). If the probe succeeds, the kernel
driver is used, exactly like upstream Proton.
3. If the device is missing or the probe fails, it tries
4. If the device is missing or the probe fails, it tries
`ntsync_init()`; on success all processes attach to the shared-memory
region and use the userspace implementation
(`ntsync: no usable /dev/ntsync, using userspace ntsync.`).
4. If that also fails, wineserver falls back to server-side
5. If that also fails, wineserver falls back to server-side
synchronization.

wineserver tells each client which backend is in use through the
Expand Down
37 changes: 25 additions & 12 deletions android/patches/common/server_inproc_sync_c.patch
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
diff --git a/server/inproc_sync.c b/server/inproc_sync.c
index a7020ef8fcd..c8691d1cd49 100644
index a7020ef8fcd..6c6b4d4cc0f 100644
--- a/server/inproc_sync.c
+++ b/server/inproc_sync.c
@@ -36,7 +36,14 @@
Expand All @@ -17,7 +17,7 @@ index a7020ef8fcd..c8691d1cd49 100644

#ifdef NTSYNC_IOC_EVENT_READ

@@ -45,6 +52,36 @@
@@ -45,18 +52,89 @@
#include <sys/stat.h>
#include <unistd.h>

Expand Down Expand Up @@ -54,13 +54,24 @@ index a7020ef8fcd..c8691d1cd49 100644
int get_inproc_device_fd(void)
{
static int fd = -2;
@@ -53,10 +90,39 @@ int get_inproc_device_fd(void)
if (fd == -2)
{
+#ifdef __ANDROID__
+ /* escape hatch for A/B testing: PROTON_NO_KERNEL_NTSYNC=1 ignores a
+ * working kernel driver and forces the userspace backend */
+ int force_userspace = getenv( "PROTON_NO_KERNEL_NTSYNC" ) && atoi(getenv( "PROTON_NO_KERNEL_NTSYNC" ));
+#endif
if (getenv( "PROTON_NO_NTSYNC" ) && atoi(getenv( "PROTON_NO_NTSYNC" )))
fd = -1;
else
+ {
fd = open( "/dev/ntsync", O_CLOEXEC | O_RDONLY );
+#ifdef __ANDROID__
+ if (force_userspace && fd >= 0)
+ {
+ close( fd );
+ fd = -1;
+ }
+ if (fd >= 0)
+ {
+ /* the device node can exist but be unusable (SELinux policy,
Expand Down Expand Up @@ -88,13 +99,15 @@ index a7020ef8fcd..c8691d1cd49 100644
do_fsync_cached = 0;
+#ifdef __ANDROID__
+ if (ntsync_userspace)
+ fprintf( stderr, "ntsync: no usable /dev/ntsync, using userspace ntsync.\n" );
+ fprintf( stderr, force_userspace
+ ? "ntsync: PROTON_NO_KERNEL_NTSYNC set, using userspace ntsync.\n"
+ : "ntsync: no usable /dev/ntsync, using userspace ntsync.\n" );
+ else
+#endif
fprintf( stderr, "ntsync: up and running.\n" );
}
else if (do_fsync()) fd = FSYNC_USED_BY_SERVER;
@@ -124,6 +190,10 @@ struct inproc_sync *create_inproc_internal_sync( int manual, int signaled )
@@ -124,6 +202,10 @@ struct inproc_sync *create_inproc_internal_sync( int manual, int signaled )
else
{
event->type = INPROC_SYNC_INTERNAL;
Expand All @@ -105,7 +118,7 @@ index a7020ef8fcd..c8691d1cd49 100644
event->fd = ioctl( get_inproc_device_fd(), NTSYNC_IOC_CREATE_EVENT, &args );
}
list_init( &event->entry );
@@ -151,6 +221,10 @@ struct inproc_sync *create_inproc_event_sync( int manual, int signaled )
@@ -151,6 +233,10 @@ struct inproc_sync *create_inproc_event_sync( int manual, int signaled )
else
{
event->type = INPROC_SYNC_EVENT;
Expand All @@ -116,7 +129,7 @@ index a7020ef8fcd..c8691d1cd49 100644
event->fd = ioctl( get_inproc_device_fd(), NTSYNC_IOC_CREATE_EVENT, &args );
}
list_init( &event->entry );
@@ -178,6 +252,10 @@ struct inproc_sync *create_inproc_mutex_sync( thread_id_t owner, unsigned int co
@@ -178,6 +264,10 @@ struct inproc_sync *create_inproc_mutex_sync( thread_id_t owner, unsigned int co
else
{
mutex->type = INPROC_SYNC_MUTEX;
Expand All @@ -127,7 +140,7 @@ index a7020ef8fcd..c8691d1cd49 100644
mutex->fd = ioctl( get_inproc_device_fd(), NTSYNC_IOC_CREATE_MUTEX, &args );
}
list_add_tail( &inproc_mutexes, &mutex->entry );
@@ -206,6 +284,10 @@ struct inproc_sync *create_inproc_semaphore_sync( unsigned int initial, unsigned
@@ -206,6 +296,10 @@ struct inproc_sync *create_inproc_semaphore_sync( unsigned int initial, unsigned
else
{
sem->type = INPROC_SYNC_SEMAPHORE;
Expand All @@ -138,7 +151,7 @@ index a7020ef8fcd..c8691d1cd49 100644
sem->fd = ioctl( get_inproc_device_fd(), NTSYNC_IOC_CREATE_SEM, &args );
}
list_init( &sem->entry );
@@ -228,17 +310,23 @@ static void inproc_sync_dump( struct object *obj, int verbose )
@@ -228,17 +322,23 @@ static void inproc_sync_dump( struct object *obj, int verbose )

void signal_inproc_sync( struct inproc_sync *sync )
{
Expand All @@ -164,7 +177,7 @@ index a7020ef8fcd..c8691d1cd49 100644
else ioctl( sync->fd, NTSYNC_IOC_EVENT_RESET, &count );
}

@@ -262,6 +350,9 @@ static void inproc_sync_destroy( struct object *obj )
@@ -262,6 +362,9 @@ static void inproc_sync_destroy( struct object *obj )
assert( obj->ops == &inproc_sync_ops );
list_remove( &sync->entry );
if (do_fsync()) fsync_free_shm_idx( sync->fd );
Expand All @@ -174,7 +187,7 @@ index a7020ef8fcd..c8691d1cd49 100644
else close( sync->fd );
}

@@ -277,7 +368,13 @@ void abandon_inproc_mutexes( thread_id_t tid )
@@ -277,7 +380,13 @@ void abandon_inproc_mutexes( thread_id_t tid )
}

LIST_FOR_EACH_ENTRY( mutex, &inproc_mutexes, struct inproc_sync, entry )
Expand All @@ -188,7 +201,7 @@ index a7020ef8fcd..c8691d1cd49 100644
}

static int get_obj_inproc_sync( struct object *obj, int *type )
@@ -359,6 +456,10 @@ DECL_HANDLER(get_inproc_sync_fd)
@@ -359,6 +468,10 @@ DECL_HANDLER(get_inproc_sync_fd)

if ((fd = get_obj_inproc_sync( obj, &reply->type )) < 0) set_error( STATUS_NOT_IMPLEMENTED );
else if (do_fsync()) reply->fsync_shm_idx = fsync_grab_shm_idx( fd );
Expand Down
Loading