pastebin - collaborative debugging tool
rovema.kpaste.net RSS


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

  1. From fcc0fa2154f9808ead7b32d5f80ee6c60df38347 Mon Sep 17 00:00:00 2001
  2. From: Roland Mainz <roland.mainz@nrubsig.org>
  3. Date: Wed, 1 Oct 2025 12:42:17 +0200
  4. Subject: [PATCH 1/2] sys: Fix minimum and maximum retry delay values for
  5.  locking
  6.  
  7. Fix minimum and maximum retry delay values for locking. Minimum
  8. retry delay is now 100ms, maximum is 15s.
  9.  
  10. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  11. ---
  12. sys/nfs41sys_lock.c | 12 ++++++------
  13.  1 file changed, 6 insertions(+), 6 deletions(-)
  14.  
  15. diff --git a/sys/nfs41sys_lock.c b/sys/nfs41sys_lock.c
  16. index 4581a96..a729ada 100644
  17. --- a/sys/nfs41sys_lock.c
  18. +++ b/sys/nfs41sys_lock.c
  19. @@ -220,19 +220,19 @@ static void print_lock_args(
  20.  
  21.  
  22.  /* use exponential backoff between polls for blocking locks */
  23. -#define MSEC_TO_RELATIVE_WAIT   (-10000)
  24. -#define MIN_LOCK_POLL_WAIT      (500 * MSEC_TO_RELATIVE_WAIT) /* 500ms */
  25. -#define MAX_LOCK_POLL_WAIT      (30000 * MSEC_TO_RELATIVE_WAIT) /* 30s */
  26. +#define MSEC_TO_RELATIVE_WAIT   (-10000LL)
  27. +#define MIN_LOCK_POLL_WAIT      (100 * MSEC_TO_RELATIVE_WAIT) /* 100ms */
  28. +#define MAX_LOCK_POLL_WAIT      (15000 * MSEC_TO_RELATIVE_WAIT) /* 15s */
  29.  
  30.  static void denied_lock_backoff(
  31.      IN OUT PLARGE_INTEGER delay)
  32.  {
  33. -    if (delay->QuadPart == 0)
  34. +    if (delay->QuadPart == 0LL)
  35.          delay->QuadPart = MIN_LOCK_POLL_WAIT;
  36.      else
  37. -        delay->QuadPart <<= 1;
  38. +        delay->QuadPart *= 2LL;
  39.  
  40. -    if (delay->QuadPart < MAX_LOCK_POLL_WAIT)
  41. +    if (delay->QuadPart > MAX_LOCK_POLL_WAIT)
  42.          delay->QuadPart = MAX_LOCK_POLL_WAIT;
  43.  }
  44.  
  45. --
  46. 2.51.0
  47.  
  48. From bd12adf1df802887e416d389dc4641be7e88e3c4 Mon Sep 17 00:00:00 2001
  49. From: Roland Mainz <roland.mainz@nrubsig.org>
  50. Date: Wed, 1 Oct 2025 15:21:18 +0200
  51. Subject: [PATCH 2/2] daemon,include,nfs41_build_features.h,sys: Kernel should
  52.  wait longer if server returns |NFS4ERR_GRACE| and |NFS4ERR_DELAY|
  53.  
  54. Kernel should wait longer if server returns |NFS4ERR_GRACE| and
  55. |NFS4ERR_DELAY|.
  56.  
  57. This is still a bit work-in-progress and still requires some cleanup,
  58. as we do not have a simple way that |compound_encode_send_decode()|
  59. can access the current kernel XID.
  60. As hackish quickfix we use a thread-local variable to store the
  61. current XID, but IMO this is suboptimal.
  62.  
  63. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  64. ---
  65. daemon/nfs41_compound.c   | 14 ++++++-
  66.  daemon/nfs41_daemon.c     | 20 ++++++++++
  67.  daemon/util.c             | 44 ++++++++++++++++++++
  68.  daemon/util.h             |  2 +
  69.  include/nfs41_driver.h    |  3 +-
  70.  nfs41_build_features.h    | 10 +++++
  71.  sys/nfs41sys_driver.c     |  3 ++
  72.  sys/nfs41sys_driver.h     |  3 ++
  73.  sys/nfs41sys_updowncall.c | 84 ++++++++++++++++++++++++++++++++++++---
  74.  9 files changed, 175 insertions(+), 8 deletions(-)
  75.  
  76. diff --git a/daemon/nfs41_compound.c b/daemon/nfs41_compound.c
  77. index 4d3b49e..faa62aa 100644
  78. --- a/daemon/nfs41_compound.c
  79. +++ b/daemon/nfs41_compound.c
  80. @@ -1,5 +1,6 @@
  81.  /* NFSv4.1 client for Windows
  82. - * Copyright (C) 2012 The Regents of the University of Michigan
  83. + * Copyright (C) 2012 The Regents of the University of Michigan
  84. + * Copyright (C) 2023-2025 Roland Mainz <roland.mainz@nrubsig.org>
  85.   *
  86.   * Olga Kornievskaia <aglo@umich.edu>
  87.   * Casey Bodley <cbodley@umich.edu>
  88. @@ -337,6 +338,17 @@ retry:
  89.                      "NFS4ERR_GRACE":"NFS4ERR_DELAY"),
  90.                  (int)retry_count,
  91.                  delayby));
  92. +#ifdef NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP
  93. +            {
  94. +                extern __declspec(thread) LONGLONG curr_upcall_xid;
  95. +                if (curr_upcall_xid != -1) {
  96. +                    DPRINTF(0,
  97. +                        ("compound_encode_send_decode: delayxid(xid=%llu)\n",
  98. +                        curr_upcall_xid));
  99. +                    (void)delayxid(curr_upcall_xid, 60);
  100. +                }
  101. +            }
  102. +#endif /* NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP */
  103.              Sleep(delayby);
  104.              DPRINTF(1, ("Attempting to resend compound.\n"));
  105.              goto do_retry;
  106. diff --git a/daemon/nfs41_daemon.c b/daemon/nfs41_daemon.c
  107. index 074c291..9f47d43 100644
  108. --- a/daemon/nfs41_daemon.c
  109. +++ b/daemon/nfs41_daemon.c
  110. @@ -130,6 +130,18 @@ out_map_default_ids:
  111.      goto out;
  112.  }
  113.  
  114. +#ifdef NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP
  115. +/* Store the current kernel XID in |curr_upcall_xid|
  116. + * so |compound_encode_send_decode()| can use this to extend the
  117. + * kernel timout
  118. + * This code still requires some cleanup, as we do not
  119. + * have a simple way that |compound_encode_send_decode()| can
  120. + * access the current kernel XID. As hackish quickfix we
  121. + * use this thread-local variable to store the current XID.
  122. + */
  123. +__declspec(thread) LONGLONG curr_upcall_xid = -1;
  124. +#endif /* NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP */
  125. +
  126.  static unsigned int nfsd_worker_thread_main(void *args)
  127.  {
  128.      nfs41_daemon_globals *nfs41dg = (nfs41_daemon_globals *)args;
  129. @@ -183,6 +195,10 @@ static unsigned int nfsd_worker_thread_main(void *args)
  130.              goto write_downcall;
  131.          }
  132.  
  133. +#ifdef NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP
  134. +        curr_upcall_xid = upcall.xid;
  135. +#endif /* NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP */
  136. +
  137.          if (!OpenThreadToken(GetCurrentThread(),
  138.              TOKEN_QUERY/*|TOKEN_IMPERSONATE*/, FALSE,
  139.              &upcall.currentthread_token)) {
  140. @@ -226,6 +242,10 @@ write_downcall:
  141.          (void)CloseHandle(upcall.currentthread_token);
  142.          upcall.currentthread_token = INVALID_HANDLE_VALUE;
  143.  
  144. +#ifdef NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP
  145. +        curr_upcall_xid = -1LL;
  146. +#endif /* NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP */
  147. +
  148.          DPRINTF(2,
  149.              ("making a downcall: "
  150.              "xid=%lld inbuf_len=%ld opcode='%s' status=%d\n",
  151. diff --git a/daemon/util.c b/daemon/util.c
  152. index 902c3f8..a9c96a3 100644
  153. --- a/daemon/util.c
  154. +++ b/daemon/util.c
  155. @@ -31,6 +31,8 @@
  156.  #include "util.h"
  157.  #include "sid.h"
  158.  #include "nfs41_ops.h"
  159. +#include <devioctl.h>
  160. +#include "nfs41_driver.h" /* for |delayxid()| */
  161.  
  162.  
  163.  char *stpcpy(char *restrict s1, const char *restrict s2)
  164. @@ -794,3 +796,45 @@ int parse_fs_location_server_address(IN const char *restrict inaddr,
  165.          inaddr);
  166.      return ERROR_BAD_NET_NAME;
  167.  }
  168. +
  169. +int delayxid(LONGLONG xid, LONGLONG moredelaysecs)
  170. +{
  171. +    int status;
  172. +    HANDLE pipe;
  173. +    unsigned char inbuf[sizeof(LONGLONG)*2], *buffer = inbuf;
  174. +    DWORD inbuf_len = sizeof(LONGLONG)*2, outbuf_len, dstatus;
  175. +    uint32_t length;
  176. +
  177. +    pipe = CreateFileA(NFS41_USER_DEVICE_NAME_A,
  178. +        GENERIC_READ|GENERIC_WRITE,
  179. +        FILE_SHARE_READ|FILE_SHARE_WRITE,
  180. +        NULL,
  181. +        OPEN_EXISTING,
  182. +        0,
  183. +        NULL);
  184. +    if (pipe == INVALID_HANDLE_VALUE) {
  185. +        status = GetLastError();
  186. +        eprintf("delayxid: Unable to open downcall pipe. lasterr=%d\n",
  187. +            status);
  188. +        return status;
  189. +    }
  190. +
  191. +    length = inbuf_len;
  192. +    safe_write(&buffer, &length, &xid, sizeof(xid));
  193. +    safe_write(&buffer, &length, &moredelaysecs, sizeof(moredelaysecs));
  194. +    EASSERT(length == 0);
  195. +
  196. +    dstatus = DeviceIoControl(pipe, IOCTL_NFS41_DELAYXID,
  197. +        inbuf, inbuf_len, NULL, 0, &outbuf_len, NULL);
  198. +    if (dstatus) {
  199. +        status = ERROR_SUCCESS;
  200. +    }
  201. +    else {
  202. +        status = GetLastError();
  203. +        eprintf("delayxid: IOCTL_NFS41_INVALCACHE failed, lasterr=%d\n",
  204. +            status);
  205. +    }
  206. +    (void)CloseHandle(pipe);
  207. +
  208. +    return status;
  209. +}
  210. diff --git a/daemon/util.h b/daemon/util.h
  211. index 83b925d..a424442 100644
  212. --- a/daemon/util.h
  213. +++ b/daemon/util.h
  214. @@ -436,4 +436,6 @@ int parse_fs_location_server_address(IN const char *restrict inaddr,
  215.      OUT char *restrict addr,
  216.      OUT unsigned short *restrict port);
  217.  
  218. +int delayxid(LONGLONG xid, LONGLONG moredelaysecs);
  219. +
  220.  #endif /* !__NFS41_DAEMON_UTIL_H__ */
  221. diff --git a/include/nfs41_driver.h b/include/nfs41_driver.h
  222. index 310eeee..1b6bae2 100644
  223. --- a/include/nfs41_driver.h
  224. +++ b/include/nfs41_driver.h
  225. @@ -50,7 +50,8 @@
  226.  #define IOCTL_NFS41_DELCONN     _RDR_CTL_CODE(5, METHOD_BUFFERED)
  227.  #define IOCTL_NFS41_READ        _RDR_CTL_CODE(6, METHOD_BUFFERED)
  228.  #define IOCTL_NFS41_WRITE       _RDR_CTL_CODE(7, METHOD_BUFFERED)
  229. -#define IOCTL_NFS41_INVALCACHE  _RDR_CTL_CODE(8, METHOD_BUFFERED)
  230. +#define IOCTL_NFS41_DELAYXID    _RDR_CTL_CODE(8, METHOD_BUFFERED)
  231. +#define IOCTL_NFS41_INVALCACHE  _RDR_CTL_CODE(9, METHOD_BUFFERED)
  232.  
  233.  /*
  234.   * NFS41_SYS_MAX_PATH_LEN - Maximum path length
  235. diff --git a/nfs41_build_features.h b/nfs41_build_features.h
  236. index c2963f4..27cfb07 100644
  237. --- a/nfs41_build_features.h
  238. +++ b/nfs41_build_features.h
  239. @@ -262,4 +262,14 @@
  240.   */
  241.  #define NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS 1
  242.  
  243. +/*
  244. + * |NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP| - handle
  245. + * |NFS4ERR_GRACE| and |NFS4ERR_DELAY|.
  246. + * This code still requires some cleanup, as we do not
  247. + * have a simple way that |compound_encode_send_decode()| can
  248. + * access the current kernel XID. As hackish quickfix we
  249. + * use a thread-local variable to store the current XID.
  250. + */
  251. +#define NFS41_DRIVER_HACK_HANDLE_NFS_DELAY_GRACE_WIP 1
  252. +
  253.  #endif /* !_NFS41_DRIVER_BUILDFEATURES_ */
  254. diff --git a/sys/nfs41sys_driver.c b/sys/nfs41sys_driver.c
  255. index 33b4a40..3bf52cc 100644
  256. --- a/sys/nfs41sys_driver.c
  257. +++ b/sys/nfs41sys_driver.c
  258. @@ -479,6 +479,9 @@ NTSTATUS nfs41_DevFcbXXXControlFile(
  259.          case IOCTL_NFS41_WRITE:
  260.              status = nfs41_downcall(RxContext);
  261.              break;
  262. +        case IOCTL_NFS41_DELAYXID:
  263. +            status = nfs41_delayxid(RxContext);
  264. +            break;
  265.          case IOCTL_NFS41_ADDCONN:
  266.              status = nfs41_CreateConnection(RxContext, &RxContext->PostRequest);
  267.              break;
  268. diff --git a/sys/nfs41sys_driver.h b/sys/nfs41sys_driver.h
  269. index 95f4c8c..913288f 100644
  270. --- a/sys/nfs41sys_driver.h
  271. +++ b/sys/nfs41sys_driver.h
  272. @@ -169,6 +169,7 @@ typedef struct _updowncall_entry {
  273.      LONGLONG xid;
  274.      nfs41_opcodes opcode;
  275.      NTSTATUS status;
  276. +    volatile LONGLONG timeout_secs;
  277.      nfs41_updowncall_state state;
  278.      FAST_MUTEX lock;
  279.      LIST_ENTRY next;
  280. @@ -885,6 +886,8 @@ NTSTATUS nfs41_upcall(
  281.      IN PRX_CONTEXT RxContext);
  282.  NTSTATUS nfs41_downcall(
  283.      IN PRX_CONTEXT RxContext);
  284. +NTSTATUS nfs41_delayxid(
  285. +    IN PRX_CONTEXT RxContext);
  286.  
  287.  /* nfs41sys_fileinfo.c */
  288.  NTSTATUS marshal_nfs41_filequery(
  289. diff --git a/sys/nfs41sys_updowncall.c b/sys/nfs41sys_updowncall.c
  290. index 5066c99..08d9165 100644
  291. --- a/sys/nfs41sys_updowncall.c
  292. +++ b/sys/nfs41sys_updowncall.c
  293. @@ -476,19 +476,22 @@ NTSTATUS nfs41_UpcallWaitForReply(
  294.  
  295.      const ULONG tickIncrement = KeQueryTimeIncrement();
  296.  
  297. +    /*
  298. +     * |entry->timeout_secs| can be increased by |nfs41_delayxid()| from
  299. +     * another userland thread!
  300. +     */
  301. +    entry->timeout_secs = secs;
  302. +
  303.      LARGE_INTEGER startTicks, currTicks;
  304.      KeQueryTickCount(&startTicks);
  305.  
  306.      LARGE_INTEGER timeout;
  307. -    timeout.QuadPart = RELATIVE(SECONDS(secs));
  308. +    timeout.QuadPart = RELATIVE(SECONDS(entry->timeout_secs));
  309.  
  310.  retry_wait:
  311.      status = KeWaitForSingleObject(&entry->cond, Executive,
  312.                  UserMode, FALSE, &timeout);
  313.  
  314. -    if (status == STATUS_TIMEOUT)
  315. -            status = STATUS_NETWORK_UNREACHABLE;
  316. -
  317.      print_wait_status(0, "[downcall]", status,
  318.          ENTRY_OPCODE2STRING(entry), entry,
  319.          (entry?entry->xid:-1LL));
  320. @@ -496,17 +499,20 @@ retry_wait:
  321.      switch(status) {
  322.      case STATUS_SUCCESS:
  323.          break;
  324. +    case STATUS_TIMEOUT:
  325.      case STATUS_USER_APC:
  326.      case STATUS_ALERTED:
  327.          /*
  328. -         * Check for timeout here, because |KeWaitForSingleObject()| does not
  329. +         * Check for timeout here, because...
  330. +         * 1. ... |KeWaitForSingleObject()| does not
  331.           * decrement the timout value.
  332.           * This prevents endless retry loops in case of APC storms or
  333.           * that the calling thread is in the process of being terminated.
  334. +         * 2. ... |nfs41_delayxid()| might have increased the timeout
  335.           */
  336.          KeQueryTickCount(&currTicks);
  337.          if (((currTicks.QuadPart - startTicks.QuadPart) * tickIncrement) <=
  338. -            SECONDS(secs)) {
  339. +            SECONDS(entry->timeout_secs)) {
  340.              DbgP("nfs41_UpcallWaitForReply: KeWaitForSingleObject() "
  341.                  "returned status(=0x%lx), "
  342.                  "retry waiting for '%s' entry=0x%p xid=%lld\n",
  343. @@ -536,6 +542,9 @@ retry_wait:
  344.  out:
  345.      FsRtlExitFileSystem();
  346.  
  347. +    if (status == STATUS_TIMEOUT)
  348. +        status = STATUS_NETWORK_UNREACHABLE;
  349. +
  350.      return status;
  351.  }
  352.  
  353. @@ -796,3 +805,66 @@ out:
  354.  
  355.      return status;
  356.  }
  357. +
  358. +NTSTATUS nfs41_delayxid(
  359. +    IN PRX_CONTEXT RxContext)
  360. +{
  361. +    NTSTATUS status = STATUS_SUCCESS;
  362. +    PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
  363. +#ifdef DEBUG_PRINT_DOWNCALL_HEXBUF
  364. +    ULONG in_len = LowIoContext->ParamsFor.IoCtl.InputBufferLength;
  365. +#endif /* DEBUG_PRINT_DOWNCALL_HEXBUF */
  366. +    unsigned char *buf = LowIoContext->ParamsFor.IoCtl.pInputBuffer;
  367. +    PLIST_ENTRY pEntry;
  368. +    nfs41_updowncall_entry *cur = NULL;
  369. +    BOOLEAN found = FALSE;
  370. +
  371. +    FsRtlEnterFileSystem();
  372. +
  373. +#ifdef DEBUG_PRINT_DOWNCALL_HEXBUF
  374. +    print_hexbuf("delayxid buffer", buf, in_len);
  375. +#endif /* DEBUG_PRINT_DOWNCALL_HEXBUF */
  376. +
  377. +    LONGLONG delayxid;
  378. +    LONGLONG moredelay;
  379. +
  380. +    /* Unmarshal XID+delay value */
  381. +    RtlCopyMemory(&delayxid, buf, sizeof(delayxid));
  382. +    buf += sizeof(delayxid);
  383. +    RtlCopyMemory(&moredelay, buf, sizeof(moredelay));
  384. +    /* buf += sizeof(delay); */
  385. +
  386. +    ExAcquireFastMutexUnsafe(&downcalllist.lock);
  387. +    pEntry = &downcalllist.head;
  388. +    pEntry = pEntry->Flink;
  389. +    while (pEntry != NULL) {
  390. +        cur = (nfs41_updowncall_entry *)CONTAINING_RECORD(pEntry,
  391. +                nfs41_updowncall_entry, next);
  392. +        if (cur->xid == delayxid) {
  393. +            found = TRUE;
  394. +            break;
  395. +        }
  396. +        if (pEntry->Flink == &downcalllist.head)
  397. +            break;
  398. +        pEntry = pEntry->Flink;
  399. +    }
  400. +    ExReleaseFastMutexUnsafe(&downcalllist.lock);
  401. +
  402. +    if (!found) {
  403. +        print_error("nfs41_delayxid: Did not find xid=%lld entry\n", delayxid);
  404. +        status = STATUS_NOT_FOUND;
  405. +        goto out;
  406. +    }
  407. +
  408. +    DbgP("nfs41_delayxid: Adding moredelay=%llu xid=%lld entry\n",
  409. +        moredelay, delayxid);
  410. +
  411. +    ExAcquireFastMutexUnsafe(&cur->lock);
  412. +    cur->timeout_secs += moredelay;
  413. +    ExReleaseFastMutexUnsafe(&cur->lock);
  414. +
  415. +out:
  416. +    FsRtlExitFileSystem();
  417. +
  418. +    return status;
  419. +}
  420. --
  421. 2.51.0

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.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at