Skip to content

Commit 963c63f

Browse files
committed
fuse: handle U64_MAX correctly in DLM locked range index
When merging and checking overlap U64_MAX was not handled this will create problems on multiple DLM results that cover the whole range. Add a fast path for adding and checking locked ranges when the whole range is covered. Signed-off-by: Horst Birthelmer <hbirthelmer@ddn.com>
1 parent 6c9ec1d commit 963c63f

1 file changed

Lines changed: 70 additions & 27 deletions

File tree

fs/fuse/fuse_dlm_cache.c

Lines changed: 70 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <linux/list.h>
99
#include <linux/types.h>
1010
#include <linux/slab.h>
11+
#include <linux/limits.h>
1112
#include <linux/interval_tree_generic.h>
1213

1314

@@ -130,16 +131,16 @@ static void fuse_dlm_try_merge(struct fuse_dlm_cache *cache, uint64_t start,
130131
node = rb_first_cached(&cache->ranges);
131132
while (node) {
132133
range = rb_entry(node, struct fuse_dlm_range, rb);
133-
if (range->end >= start - 1)
134+
if (start == 0 || range->end >= start - 1)
134135
break;
135136
node = rb_next(node);
136137
}
137138

138-
if (!range || range->start > end + 1)
139+
if (!range || (end != U64_MAX && range->start > end + 1))
139140
return;
140141

141142
/* Try to merge ranges in and around the specified region */
142-
while (range && range->start <= end + 1) {
143+
while (range && (end == U64_MAX || range->start <= end + 1)) {
143144
/* Get next range before we potentially modify the tree */
144145
next = NULL;
145146
if (rb_next(&range->rb)) {
@@ -149,9 +150,11 @@ static void fuse_dlm_try_merge(struct fuse_dlm_cache *cache, uint64_t start,
149150

150151
/* Try to merge with next range if adjacent and same mode */
151152
if (next && range->mode == next->mode &&
152-
range->end + 1 == next->start) {
153-
/* Merge ranges */
154-
range->end = next->end;
153+
(range->end == U64_MAX || range->end + 1 == next->start)) {
154+
/* Merge ranges - but can't merge if range->end is already U64_MAX */
155+
if (range->end != U64_MAX) {
156+
range->end = next->end;
157+
}
155158

156159
/* Remove next from tree */
157160
fuse_page_it_remove(next, &cache->ranges);
@@ -198,10 +201,25 @@ int fuse_dlm_lock_range(struct fuse_inode *inode, uint64_t start,
198201

199202
/* Convert to lock mode */
200203
lock_mode = (mode == FUSE_PAGE_LOCK_READ) ? FUSE_PCACHE_LK_READ :
201-
FUSE_PCACHE_LK_WRITE;
204+
FUSE_PCACHE_LK_WRITE;
202205

203206
down_write(&cache->lock);
204207

208+
/*
209+
* Fast path: check if the requested range is already fully covered.
210+
* This is an optimization for the common case where we grant
211+
* [0, U64_MAX] locks and avoids overflow arithmetic.
212+
*/
213+
range = fuse_page_it_iter_first(&cache->ranges, start, end);
214+
if (range && range->start <= start && range->end >= end) {
215+
bool same_mode = (range->mode == lock_mode);
216+
bool stronger_mode = (lock_mode == FUSE_PCACHE_LK_READ &&
217+
range->mode == FUSE_PCACHE_LK_WRITE);
218+
219+
if (same_mode || stronger_mode)
220+
goto out;
221+
}
222+
205223
/* Find all ranges that overlap with [start, end] */
206224
range = fuse_page_it_iter_first(&cache->ranges, start, end);
207225
while (range) {
@@ -270,6 +288,7 @@ int fuse_dlm_lock_range(struct fuse_inode *inode, uint64_t start,
270288
/* Try to merge adjacent ranges with the same mode */
271289
fuse_dlm_try_merge(cache, start, end);
272290

291+
out:
273292
up_write(&cache->lock);
274293
return 0;
275294

@@ -322,13 +341,25 @@ static int fuse_dlm_punch_hole(struct fuse_dlm_cache *cache, uint64_t start,
322341

323342
/* If the hole is at the beginning of the range */
324343
if (start == range->start) {
325-
range->start = end + 1;
344+
if (end == U64_MAX) {
345+
/* Hole goes to end of address space, remove entire range */
346+
fuse_page_it_remove(range, &cache->ranges);
347+
kfree(range);
348+
} else {
349+
range->start = end + 1;
350+
}
326351
goto out;
327352
}
328353

329354
/* If the hole is at the end of the range */
330355
if (end == range->end) {
331-
range->end = start - 1;
356+
if (start == 0) {
357+
/* Hole starts at 0, remove entire range */
358+
fuse_page_it_remove(range, &cache->ranges);
359+
kfree(range);
360+
} else {
361+
range->end = start - 1;
362+
}
332363
goto out;
333364
}
334365

@@ -399,10 +430,10 @@ int fuse_dlm_unlock_range(struct fuse_inode *inode,
399430
/* After punching a hole, we're done */
400431
break;
401432
} else if (start > range->start) {
402-
/* Adjust the end of the range */
433+
/* Adjust the end of the range (start is > 0, so start - 1 is safe) */
403434
range->end = start - 1;
404435
} else if (end < range->end) {
405-
/* Adjust the start of the range */
436+
/* Adjust the start of the range (end is < U64_MAX, so end + 1 is safe) */
406437
range->start = end + 1;
407438
} else {
408439
/* Complete overlap, remove the range */
@@ -437,6 +468,7 @@ bool fuse_dlm_range_is_locked(struct fuse_inode *inode, uint64_t start,
437468
struct fuse_dlm_range *range;
438469
int lock_mode = 0;
439470
uint64_t current_start = start;
471+
bool ret = false;
440472

441473
if (!cache || start > end)
442474
return false;
@@ -452,20 +484,34 @@ bool fuse_dlm_range_is_locked(struct fuse_inode *inode, uint64_t start,
452484
/* Find the first range that overlaps with [start, end] */
453485
range = fuse_dlm_find_overlapping(cache, start, end);
454486

455-
/* Check if the entire range is covered */
487+
/*
488+
* Fast path: check if a single range covers the entire request.
489+
* This is common when we grant [0, U64_MAX] locks.
490+
*/
491+
if (range && range->start <= start && range->end >= end) {
492+
/* Range fully covers [start, end] */
493+
if (!lock_mode || range->mode == lock_mode) {
494+
ret = true;
495+
goto out;
496+
}
497+
/* Wrong lock mode */
498+
goto out;
499+
}
500+
501+
/* Slow path: check if multiple ranges cover the request */
456502
while (range && current_start <= end) {
457503
/* If we're checking for a specific mode, verify it matches */
458-
if (lock_mode && range->mode != lock_mode) {
459-
/* Wrong lock mode */
460-
up_read(&cache->lock);
461-
return false;
462-
}
504+
if (lock_mode && range->mode != lock_mode)
505+
goto out;
463506

464507
/* Check if there's a gap before this range */
465-
if (current_start < range->start) {
466-
/* Found a gap */
467-
up_read(&cache->lock);
468-
return false;
508+
if (current_start < range->start)
509+
goto out;
510+
511+
if (range->end == U64_MAX) {
512+
/* Range covers to end of address space */
513+
ret = true;
514+
goto out;
469515
}
470516

471517
/* Move current_start past this range */
@@ -476,14 +522,11 @@ bool fuse_dlm_range_is_locked(struct fuse_inode *inode, uint64_t start,
476522
}
477523

478524
/* Check if we covered the entire range */
479-
if (current_start <= end) {
480-
/* There's a gap at the end */
481-
up_read(&cache->lock);
482-
return false;
483-
}
525+
ret = (current_start > end);
484526

527+
out:
485528
up_read(&cache->lock);
486-
return true;
529+
return ret;
487530
}
488531

489532
/**

0 commit comments

Comments
 (0)