Skip to content

Commit 5bb799c

Browse files
committed
fuse: Use -EDEADLK for DLM lock error instead of -EAGAIN
-EAGAIN is semantically overloaded for a DLM error and not self describing, switch to -EDEADLK. In order to allow a graceful daemon change, -EAGAIN is kept for now. Signed-off-by: Bernd Schubert <bernd@bsbernd.com>
1 parent 5632cca commit 5bb799c

2 files changed

Lines changed: 23 additions & 18 deletions

File tree

Documentation/filesystems/fuse/fuse-AOP_TRUNCATED_PAGE-reason.txt

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
=================================================================================
2-
WHY FUSE CONVERTS -EAGAIN TO AOP_TRUNCATED_PAGE IN fuse_read_folio()
2+
WHY FUSE CONVERTS -EDEADLK TO AOP_TRUNCATED_PAGE IN fuse_read_folio()
33
=================================================================================
44

55
TLDR: To prevent ABBA deadlock between page locks and DLM (cluster) locks.
@@ -292,21 +292,22 @@ Core-0: Application reading from FUSE filesystem
292292
│ Sends FUSE_READ to userspace daemon
293293
│ Daemon may need to acquire cluster lock
294294
295-
└─ Might return -EAGAIN if DLM detects possible
295+
└─ Might return -EDEADLK if DLM detects possible
296296
deadlock due to concurrant page invalidation
297+
- For now -EAGAIN handled the same
297298

298299

299300
─────────────────────────────────────────────────────────────────────────────
300301
WHY FUSE NEEDS THIS CONVERSION
301302
─────────────────────────────────────────────────────────────────────────────
302303

303-
When fuse_simple_request() returns -EAGAIN:
304+
When fuse_simple_request() returns -EDEADLK:
304305
- Userspace FUSE daemon encountered transient failure
305306
- Could be: cluster lock contention, timeout, daemon busy
306307
- The page might be stale/modified during the wait
307308
- Page lock is STILL HELD at this point
308309

309-
If -EAGAIN were returned directly:
310+
If -EDEADLK were returned directly:
310311
❌ VFS would see error and fail the read
311312
❌ Page would remain locked
312313
❌ No retry mechanism triggered
@@ -342,11 +343,12 @@ Location: fs/fuse/file.c:947-964
342343
out:
343344
// LINE 962: ✓ CRITICAL - Always unlocks page before returning
344345
folio_unlock(folio);
345-
return err; // Returns AOP_TRUNCATED_PAGE if -EAGAIN occurred
346+
return err; // Returns AOP_TRUNCATED_PAGE if -EDEADLK occurred
346347
}
347348

348349
This matches OCFS2's pattern:
349350
1. Detect lock contention (-EAGAIN from daemon)
351+
- Note: confusing that OCFS2 uses -EAGAIN instead of -EDEADLK
350352
2. Unlock the page (line 962)
351353
3. Return AOP_TRUNCATED_PAGE
352354
4. VFS retries the operation
@@ -372,7 +374,7 @@ Callers in mm/filemap.c handle it consistently:
372374
- do_filemap_fault(): "if (error == AOP_TRUNCATED_PAGE) goto retry_find;"
373375

374376
=================================================================================
375-
WHY NOT JUST RETURN -EAGAIN?
377+
WHY NOT JUST RETURN -EDEADLK (-EAGAIN)?
376378
=================================================================================
377379

378380
From OCFS2 comments (fs/ocfs2/dlmglue.c:2555-2560):
@@ -434,7 +436,7 @@ T2 Call: ocfs2_read_folio() DLM downconvert starts
434436
Lock: DLM inode lock
435437

436438
T3 Try: DLM lock (NONBLOCK) Want: page lock
437-
Returns: -EAGAIN ❌ BLOCKED (held by Core-0)
439+
Returns: -EDEADLK ❌ BLOCKED (held by Core-0)
438440

439441
T4 ✓ Unlock: page lock ✓ Acquires: page lock
440442
Return: AOP_TRUNCATED_PAGE Continues: reclaim work
@@ -517,10 +519,10 @@ Caller 3: do_filemap_fault() - lines 3542-3543
517519
Effect: Re-handles the page fault from scratch
518520

519521
=================================================================================
520-
SUMMARY: Why -EAGAIN → AOP_TRUNCATED_PAGE is Essential
522+
SUMMARY: Why -EDEADLK → AOP_TRUNCATED_PAGE is Essential
521523
=================================================================================
522524

523-
The conversion -EAGAIN → AOP_TRUNCATED_PAGE in fuse_read_folio() is necessary:
525+
The conversion -EDEADLK → AOP_TRUNCATED_PAGE in fuse_read_folio() is necessary:
524526

525527
1. **Prevent Deadlock**: Avoids page lock vs cluster lock ABBA deadlock
526528
- Core-0 holds page lock, wants DLM lock
@@ -535,7 +537,7 @@ The conversion -EAGAIN → AOP_TRUNCATED_PAGE in fuse_read_folio() is necessary:
535537
3. **Enable Retry**: Uses VFS's built-in retry mechanism properly
536538
- AOP_TRUNCATED_PAGE is understood by mm/filemap.c
537539
- Triggers automatic retry loops at multiple call sites
538-
- -EAGAIN alone would just fail the operation
540+
- -EDEADLK alone would just fail the operation
539541

540542
4. **Ensure Fairness**: Allows fair lock acquisition through retry cycle
541543
- Other threads get chance to acquire locks
@@ -553,7 +555,7 @@ Core-0 (Read Path):
553555
- Entry: filemap_read() [mm/filemap.c:2675]
554556
- Page lock: folio_trylock() [mm/filemap.c:2466]
555557
- FUSE read: fuse_do_readfolio() [fs/fuse/file.c:905]
556-
- -EAGAIN conversion [fs/fuse/file.c:934-935]
558+
- -EDEADLK conversion [fs/fuse/file.c:934-935]
557559
- Page unlock: folio_unlock() [fs/fuse/file.c:962]
558560

559561
Core-1 (Reclaim Path):

fs/fuse/file.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -959,11 +959,14 @@ static int fuse_do_readfolio(struct file *file, struct folio *folio,
959959
res = fuse_simple_request(fm, &ia.ap.args);
960960
if (res < 0) {
961961
/*
962-
* please refer to Documentation/filesystems/fuse/fuse-AOP_TRUNCATED_PAGE-reason.txt
963-
* why READ can return -EAGAIN from DLM subsystem.
964-
* XXX find a better DLM specific error code
962+
* Please refer to Documentation/filesystems/fuse/fuse-AOP_TRUNCATED_PAGE-reason.txt
963+
* why READ can return -EDEADLK from DLM subsystem.
964+
*
965+
* -EDEADLK: Preferred error code indicating DLM lock ordering violation
966+
* (would cause deadlock with page lock)
967+
* -EAGAIN: Legacy error code, maintained for backward compatibility
965968
*/
966-
if (res == -EAGAIN && fm->fc->dlm)
969+
if ((res == -EDEADLK || res == -EAGAIN) && fm->fc->dlm)
967970
res = AOP_TRUNCATED_PAGE;
968971
return res;
969972
}
@@ -1010,9 +1013,9 @@ static int fuse_iomap_read_folio_range(const struct iomap_iter *iter,
10101013
/*
10111014
* TEMPORARY WORKAROUND for iomap write deadlock:
10121015
*
1013-
* When FUSE server returns -EAGAIN due to DLM,
1014-
* fuse_do_readfolio() converts it to AOP_TRUNCATED_PAGE and
1015-
* unlocks the folio (per AOP_TRUNCATED_PAGE contract).
1016+
* When FUSE server returns -EDEADLK (or legacy -EAGAIN) due to DLM
1017+
* lock contention, fuse_do_readfolio() converts it to AOP_TRUNCATED_PAGE
1018+
* and unlocks the folio (per AOP_TRUNCATED_PAGE contract).
10161019
*
10171020
* However, iomap doesn't understand AOP_TRUNCATED_PAGE.
10181021
* We need to:

0 commit comments

Comments
 (0)