- From fcc0fa2154f9808ead7b32d5f80ee6c60df38347 Mon Sep 17 00:00:00 2001
- From: Roland Mainz <roland.mainz@nrubsig.org>
- Date: Wed, 1 Oct 2025 12:42:17 +0200
- Subject: [PATCH 1/2] sys: Fix minimum and maximum retry delay values for
- locking
- Fix minimum and maximum retry delay values for locking. Minimum
- retry delay is now 100ms, maximum is 15s.
- Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
- ---
- sys/nfs41sys_lock.c | 12 ++++++------
- 1 file changed, 6 insertions(+), 6 deletions(-)
- diff --git a/sys/nfs41sys_lock.c b/sys/nfs41sys_lock.c
- index 4581a96..a729ada 100644
- --- a/sys/nfs41sys_lock.c
- +++ b/sys/nfs41sys_lock.c
- @@ -220,19 +220,19 @@ static void print_lock_args(
- /* use exponential backoff between polls for blocking locks */
- -#define MSEC_TO_RELATIVE_WAIT (-10000)
- -#define MIN_LOCK_POLL_WAIT (500 * MSEC_TO_RELATIVE_WAIT) /* 500ms */
- -#define MAX_LOCK_POLL_WAIT (30000 * MSEC_TO_RELATIVE_WAIT) /* 30s */
- +#define MSEC_TO_RELATIVE_WAIT (-10000LL)
- +#define MIN_LOCK_POLL_WAIT (100 * MSEC_TO_RELATIVE_WAIT) /* 100ms */
- +#define MAX_LOCK_POLL_WAIT (15000 * MSEC_TO_RELATIVE_WAIT) /* 15s */
- static void denied_lock_backoff(
- IN OUT PLARGE_INTEGER delay)
- {
- - if (delay->QuadPart == 0)
- + if (delay->QuadPart == 0LL)
- delay->QuadPart = MIN_LOCK_POLL_WAIT;
- else
- - delay->QuadPart <<= 1;
- + delay->QuadPart *= 2LL;
- - if (delay->QuadPart < MAX_LOCK_POLL_WAIT)
- + if (delay->QuadPart > MAX_LOCK_POLL_WAIT)
- delay->QuadPart = MAX_LOCK_POLL_WAIT;
- }
- --
- 2.51.0
- From bd12adf1df802887e416d389dc4641be7e88e3c4 Mon Sep 17 00:00:00 2001
- From: Roland Mainz <roland.mainz@nrubsig.org>
- Date: Wed, 1 Oct 2025 15:21:18 +0200
- Subject: [PATCH 2/2] daemon,include,nfs41_build_features.h,sys: Kernel should
- wait longer if server returns |NFS4ERR_GRACE| and |NFS4ERR_DELAY|
- Kernel should wait longer if server returns |NFS4ERR_GRACE| and
- |NFS4ERR_DELAY|.
- This is still a bit work-in-progress and still requires some cleanup,
- as we do not have a simple way that |compound_encode_send_decode()|
- can access the current kernel XID.
- As hackish quickfix we use a thread-local variable to store the
- current XID, but IMO this is suboptimal.
- Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
- ---
- daemon/nfs41_compound.c | 14 ++++++-
- daemon/nfs41_daemon.c | 20 ++++++++++
- daemon/util.c | 44 ++++++++++++++++++++
- daemon/util.h | 2 +
- include/nfs41_driver.h | 3 +-
- nfs41_build_features.h | 10 +++++
- sys/nfs41sys_driver.c | 3 ++
- sys/nfs41sys_driver.h | 3 ++
- sys/nfs41sys_updowncall.c | 84 ++++++++++++++++++++++++++++++++++++---
- 9 files changed, 175 insertions(+), 8 deletions(-)
- diff --git a/daemon/nfs41_compound.c b/daemon/nfs41_compound.c
- index 4d3b49e..faa62aa 100644
- --- a/daemon/nfs41_compound.c
- +++ b/daemon/nfs41_compound.c
- @@ -1,5 +1,6 @@
- /* NFSv4.1 client for Windows
- - * Copyright (C) 2012 The Regents of the University of Michigan
- + * Copyright (C) 2012 The Regents of the University of Michigan
- + * Copyright (C) 2023-2025 Roland Mainz <roland.mainz@nrubsig.org>
- *
- * Olga Kornievskaia <aglo@umich.edu>
- * Casey Bodley <cbodley@umich.edu>
- @@ -337,6 +338,17 @@ retry:
- "NFS4ERR_GRACE":"NFS4ERR_DELAY"),
- (int)retry_count,
- delayby));
- +#ifdef NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP
- + {
- + extern __declspec(thread) LONGLONG curr_upcall_xid;
- + if (curr_upcall_xid != -1) {
- + DPRINTF(0,
- + ("compound_encode_send_decode: delayxid(xid=%llu)\n",
- + curr_upcall_xid));
- + (void)delayxid(curr_upcall_xid, 60);
- + }
- + }
- +#endif /* NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP */
- Sleep(delayby);
- DPRINTF(1, ("Attempting to resend compound.\n"));
- goto do_retry;
- diff --git a/daemon/nfs41_daemon.c b/daemon/nfs41_daemon.c
- index 074c291..9f47d43 100644
- --- a/daemon/nfs41_daemon.c
- +++ b/daemon/nfs41_daemon.c
- @@ -130,6 +130,18 @@ out_map_default_ids:
- goto out;
- }
- +#ifdef NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP
- +/* Store the current kernel XID in |curr_upcall_xid|
- + * so |compound_encode_send_decode()| can use this to extend the
- + * kernel timout
- + * This code still requires some cleanup, as we do not
- + * have a simple way that |compound_encode_send_decode()| can
- + * access the current kernel XID. As hackish quickfix we
- + * use this thread-local variable to store the current XID.
- + */
- +__declspec(thread) LONGLONG curr_upcall_xid = -1;
- +#endif /* NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP */
- +
- static unsigned int nfsd_worker_thread_main(void *args)
- {
- nfs41_daemon_globals *nfs41dg = (nfs41_daemon_globals *)args;
- @@ -183,6 +195,10 @@ static unsigned int nfsd_worker_thread_main(void *args)
- goto write_downcall;
- }
- +#ifdef NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP
- + curr_upcall_xid = upcall.xid;
- +#endif /* NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP */
- +
- if (!OpenThreadToken(GetCurrentThread(),
- TOKEN_QUERY/*|TOKEN_IMPERSONATE*/, FALSE,
- &upcall.currentthread_token)) {
- @@ -226,6 +242,10 @@ write_downcall:
- (void)CloseHandle(upcall.currentthread_token);
- upcall.currentthread_token = INVALID_HANDLE_VALUE;
- +#ifdef NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP
- + curr_upcall_xid = -1LL;
- +#endif /* NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP */
- +
- DPRINTF(2,
- ("making a downcall: "
- "xid=%lld inbuf_len=%ld opcode='%s' status=%d\n",
- diff --git a/daemon/util.c b/daemon/util.c
- index 902c3f8..a9c96a3 100644
- --- a/daemon/util.c
- +++ b/daemon/util.c
- @@ -31,6 +31,8 @@
- #include "util.h"
- #include "sid.h"
- #include "nfs41_ops.h"
- +#include <devioctl.h>
- +#include "nfs41_driver.h" /* for |delayxid()| */
- char *stpcpy(char *restrict s1, const char *restrict s2)
- @@ -794,3 +796,45 @@ int parse_fs_location_server_address(IN const char *restrict inaddr,
- inaddr);
- return ERROR_BAD_NET_NAME;
- }
- +
- +int delayxid(LONGLONG xid, LONGLONG moredelaysecs)
- +{
- + int status;
- + HANDLE pipe;
- + unsigned char inbuf[sizeof(LONGLONG)*2], *buffer = inbuf;
- + DWORD inbuf_len = sizeof(LONGLONG)*2, outbuf_len, dstatus;
- + uint32_t length;
- +
- + pipe = CreateFileA(NFS41_USER_DEVICE_NAME_A,
- + GENERIC_READ|GENERIC_WRITE,
- + FILE_SHARE_READ|FILE_SHARE_WRITE,
- + NULL,
- + OPEN_EXISTING,
- + 0,
- + NULL);
- + if (pipe == INVALID_HANDLE_VALUE) {
- + status = GetLastError();
- + eprintf("delayxid: Unable to open downcall pipe. lasterr=%d\n",
- + status);
- + return status;
- + }
- +
- + length = inbuf_len;
- + safe_write(&buffer, &length, &xid, sizeof(xid));
- + safe_write(&buffer, &length, &moredelaysecs, sizeof(moredelaysecs));
- + EASSERT(length == 0);
- +
- + dstatus = DeviceIoControl(pipe, IOCTL_NFS41_DELAYXID,
- + inbuf, inbuf_len, NULL, 0, &outbuf_len, NULL);
- + if (dstatus) {
- + status = ERROR_SUCCESS;
- + }
- + else {
- + status = GetLastError();
- + eprintf("delayxid: IOCTL_NFS41_INVALCACHE failed, lasterr=%d\n",
- + status);
- + }
- + (void)CloseHandle(pipe);
- +
- + return status;
- +}
- diff --git a/daemon/util.h b/daemon/util.h
- index 83b925d..a424442 100644
- --- a/daemon/util.h
- +++ b/daemon/util.h
- @@ -436,4 +436,6 @@ int parse_fs_location_server_address(IN const char *restrict inaddr,
- OUT char *restrict addr,
- OUT unsigned short *restrict port);
- +int delayxid(LONGLONG xid, LONGLONG moredelaysecs);
- +
- #endif /* !__NFS41_DAEMON_UTIL_H__ */
- diff --git a/include/nfs41_driver.h b/include/nfs41_driver.h
- index 310eeee..1b6bae2 100644
- --- a/include/nfs41_driver.h
- +++ b/include/nfs41_driver.h
- @@ -50,7 +50,8 @@
- #define IOCTL_NFS41_DELCONN _RDR_CTL_CODE(5, METHOD_BUFFERED)
- #define IOCTL_NFS41_READ _RDR_CTL_CODE(6, METHOD_BUFFERED)
- #define IOCTL_NFS41_WRITE _RDR_CTL_CODE(7, METHOD_BUFFERED)
- -#define IOCTL_NFS41_INVALCACHE _RDR_CTL_CODE(8, METHOD_BUFFERED)
- +#define IOCTL_NFS41_DELAYXID _RDR_CTL_CODE(8, METHOD_BUFFERED)
- +#define IOCTL_NFS41_INVALCACHE _RDR_CTL_CODE(9, METHOD_BUFFERED)
- /*
- * NFS41_SYS_MAX_PATH_LEN - Maximum path length
- diff --git a/nfs41_build_features.h b/nfs41_build_features.h
- index c2963f4..27cfb07 100644
- --- a/nfs41_build_features.h
- +++ b/nfs41_build_features.h
- @@ -262,4 +262,14 @@
- */
- #define NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS 1
- +/*
- + * |NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP| - handle
- + * |NFS4ERR_GRACE| and |NFS4ERR_DELAY|.
- + * This code still requires some cleanup, as we do not
- + * have a simple way that |compound_encode_send_decode()| can
- + * access the current kernel XID. As hackish quickfix we
- + * use a thread-local variable to store the current XID.
- + */
- +#define NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP 1
- +
- #endif /* !_NFS41_DRIVER_BUILDFEATURES_ */
- diff --git a/sys/nfs41sys_driver.c b/sys/nfs41sys_driver.c
- index 33b4a40..3bf52cc 100644
- --- a/sys/nfs41sys_driver.c
- +++ b/sys/nfs41sys_driver.c
- @@ -479,6 +479,9 @@ NTSTATUS nfs41_DevFcbXXXControlFile(
- case IOCTL_NFS41_WRITE:
- status = nfs41_downcall(RxContext);
- break;
- + case IOCTL_NFS41_DELAYXID:
- + status = nfs41_delayxid(RxContext);
- + break;
- case IOCTL_NFS41_ADDCONN:
- status = nfs41_CreateConnection(RxContext, &RxContext->PostRequest);
- break;
- diff --git a/sys/nfs41sys_driver.h b/sys/nfs41sys_driver.h
- index 95f4c8c..913288f 100644
- --- a/sys/nfs41sys_driver.h
- +++ b/sys/nfs41sys_driver.h
- @@ -169,6 +169,7 @@ typedef struct _updowncall_entry {
- LONGLONG xid;
- nfs41_opcodes opcode;
- NTSTATUS status;
- + volatile LONGLONG timeout_secs;
- nfs41_updowncall_state state;
- FAST_MUTEX lock;
- LIST_ENTRY next;
- @@ -885,6 +886,8 @@ NTSTATUS nfs41_upcall(
- IN PRX_CONTEXT RxContext);
- NTSTATUS nfs41_downcall(
- IN PRX_CONTEXT RxContext);
- +NTSTATUS nfs41_delayxid(
- + IN PRX_CONTEXT RxContext);
- /* nfs41sys_fileinfo.c */
- NTSTATUS marshal_nfs41_filequery(
- diff --git a/sys/nfs41sys_updowncall.c b/sys/nfs41sys_updowncall.c
- index 5066c99..08d9165 100644
- --- a/sys/nfs41sys_updowncall.c
- +++ b/sys/nfs41sys_updowncall.c
- @@ -476,19 +476,22 @@ NTSTATUS nfs41_UpcallWaitForReply(
- const ULONG tickIncrement = KeQueryTimeIncrement();
- + /*
- + * |entry->timeout_secs| can be increased by |nfs41_delayxid()| from
- + * another userland thread!
- + */
- + entry->timeout_secs = secs;
- +
- LARGE_INTEGER startTicks, currTicks;
- KeQueryTickCount(&startTicks);
- LARGE_INTEGER timeout;
- - timeout.QuadPart = RELATIVE(SECONDS(secs));
- + timeout.QuadPart = RELATIVE(SECONDS(entry->timeout_secs));
- retry_wait:
- status = KeWaitForSingleObject(&entry->cond, Executive,
- UserMode, FALSE, &timeout);
- - if (status == STATUS_TIMEOUT)
- - status = STATUS_NETWORK_UNREACHABLE;
- -
- print_wait_status(0, "[downcall]", status,
- ENTRY_OPCODE2STRING(entry), entry,
- (entry?entry->xid:-1LL));
- @@ -496,17 +499,20 @@ retry_wait:
- switch(status) {
- case STATUS_SUCCESS:
- break;
- + case STATUS_TIMEOUT:
- case STATUS_USER_APC:
- case STATUS_ALERTED:
- /*
- - * Check for timeout here, because |KeWaitForSingleObject()| does not
- + * Check for timeout here, because...
- + * 1. ... |KeWaitForSingleObject()| does not
- * decrement the timout value.
- * This prevents endless retry loops in case of APC storms or
- * that the calling thread is in the process of being terminated.
- + * 2. ... |nfs41_delayxid()| might have increased the timeout
- */
- KeQueryTickCount(&currTicks);
- if (((currTicks.QuadPart - startTicks.QuadPart) * tickIncrement) <=
- - SECONDS(secs)) {
- + SECONDS(entry->timeout_secs)) {
- DbgP("nfs41_UpcallWaitForReply: KeWaitForSingleObject() "
- "returned status(=0x%lx), "
- "retry waiting for '%s' entry=0x%p xid=%lld\n",
- @@ -536,6 +542,9 @@ retry_wait:
- out:
- FsRtlExitFileSystem();
- + if (status == STATUS_TIMEOUT)
- + status = STATUS_NETWORK_UNREACHABLE;
- +
- return status;
- }
- @@ -796,3 +805,66 @@ out:
- return status;
- }
- +
- +NTSTATUS nfs41_delayxid(
- + IN PRX_CONTEXT RxContext)
- +{
- + NTSTATUS status = STATUS_SUCCESS;
- + PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
- +#ifdef DEBUG_PRINT_DOWNCALL_HEXBUF
- + ULONG in_len = LowIoContext->ParamsFor.IoCtl.InputBufferLength;
- +#endif /* DEBUG_PRINT_DOWNCALL_HEXBUF */
- + unsigned char *buf = LowIoContext->ParamsFor.IoCtl.pInputBuffer;
- + PLIST_ENTRY pEntry;
- + nfs41_updowncall_entry *cur = NULL;
- + BOOLEAN found = FALSE;
- +
- + FsRtlEnterFileSystem();
- +
- +#ifdef DEBUG_PRINT_DOWNCALL_HEXBUF
- + print_hexbuf("delayxid buffer", buf, in_len);
- +#endif /* DEBUG_PRINT_DOWNCALL_HEXBUF */
- +
- + LONGLONG delayxid;
- + LONGLONG moredelay;
- +
- + /* Unmarshal XID+delay value */
- + RtlCopyMemory(&delayxid, buf, sizeof(delayxid));
- + buf += sizeof(delayxid);
- + RtlCopyMemory(&moredelay, buf, sizeof(moredelay));
- + /* buf += sizeof(delay); */
- +
- + ExAcquireFastMutexUnsafe(&downcalllist.lock);
- + pEntry = &downcalllist.head;
- + pEntry = pEntry->Flink;
- + while (pEntry != NULL) {
- + cur = (nfs41_updowncall_entry *)CONTAINING_RECORD(pEntry,
- + nfs41_updowncall_entry, next);
- + if (cur->xid == delayxid) {
- + found = TRUE;
- + break;
- + }
- + if (pEntry->Flink == &downcalllist.head)
- + break;
- + pEntry = pEntry->Flink;
- + }
- + ExReleaseFastMutexUnsafe(&downcalllist.lock);
- +
- + if (!found) {
- + print_error("nfs41_delayxid: Did not find xid=%lld entry\n", delayxid);
- + status = STATUS_NOT_FOUND;
- + goto out;
- + }
- +
- + DbgP("nfs41_delayxid: Adding moredelay=%llu xid=%lld entry\n",
- + moredelay, delayxid);
- +
- + ExAcquireFastMutexUnsafe(&cur->lock);
- + cur->timeout_secs += moredelay;
- + ExReleaseFastMutexUnsafe(&cur->lock);
- +
- +out:
- + FsRtlExitFileSystem();
- +
- + return status;
- +}
- --
- 2.51.0
msnfs41client: Patches for Win32 locking timeouts and kernel should wait longer in case of |NFS4ERR_DELAY|+|NFS4ERR_GRACE|, 2025-10-01
Posted by Anonymous on Wed 1st Oct 2025 14:38
raw | new post
Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.