Skip to content

Commit 0e4a862

Browse files
chuckleverbrauner
authored andcommitted
libfs: Convert simple directory offsets to use a Maple Tree
Test robot reports: > kernel test robot noticed a -19.0% regression of aim9.disk_src.ops_per_sec on: > > commit: a2e4595 ("shmem: stable directory offsets") > https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git master Feng Tang further clarifies that: > ... the new simple_offset_add() > called by shmem_mknod() brings extra cost related with slab, > specifically the 'radix_tree_node', which cause the regression. Willy's analysis is that, over time, the test workload causes xa_alloc_cyclic() to fragment the underlying SLAB cache. This patch replaces the offset_ctx's xarray with a Maple Tree in the hope that Maple Tree's dense node mode will handle this scenario more scalably. In addition, we can widen the simple directory offset maximum to signed long (as loff_t is also signed). Suggested-by: Matthew Wilcox <willy@infradead.org> Reported-by: kernel test robot <oliver.sang@intel.com> Closes: https://lore.kernel.org/oe-lkp/202309081306.3ecb3734-oliver.sang@intel.com Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Link: https://lore.kernel.org/r/170820145616.6328.12620992971699079156.stgit@91.116.238.104.host.secureserver.net Reviewed-by: Jan Kara <jack@suse.cz> Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent f92e1a8 commit 0e4a862

2 files changed

Lines changed: 26 additions & 26 deletions

File tree

fs/libfs.c

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -245,17 +245,17 @@ enum {
245245
DIR_OFFSET_MIN = 2,
246246
};
247247

248-
static void offset_set(struct dentry *dentry, u32 offset)
248+
static void offset_set(struct dentry *dentry, long offset)
249249
{
250-
dentry->d_fsdata = (void *)((uintptr_t)(offset));
250+
dentry->d_fsdata = (void *)offset;
251251
}
252252

253-
static u32 dentry2offset(struct dentry *dentry)
253+
static long dentry2offset(struct dentry *dentry)
254254
{
255-
return (u32)((uintptr_t)(dentry->d_fsdata));
255+
return (long)dentry->d_fsdata;
256256
}
257257

258-
static struct lock_class_key simple_offset_xa_lock;
258+
static struct lock_class_key simple_offset_lock_class;
259259

260260
/**
261261
* simple_offset_init - initialize an offset_ctx
@@ -264,8 +264,8 @@ static struct lock_class_key simple_offset_xa_lock;
264264
*/
265265
void simple_offset_init(struct offset_ctx *octx)
266266
{
267-
xa_init_flags(&octx->xa, XA_FLAGS_ALLOC1);
268-
lockdep_set_class(&octx->xa.xa_lock, &simple_offset_xa_lock);
267+
mt_init_flags(&octx->mt, MT_FLAGS_ALLOC_RANGE);
268+
lockdep_set_class(&octx->mt.ma_lock, &simple_offset_lock_class);
269269
octx->next_offset = DIR_OFFSET_MIN;
270270
}
271271

@@ -274,20 +274,19 @@ void simple_offset_init(struct offset_ctx *octx)
274274
* @octx: directory offset ctx to be updated
275275
* @dentry: new dentry being added
276276
*
277-
* Returns zero on success. @so_ctx and the dentry offset are updated.
277+
* Returns zero on success. @octx and the dentry's offset are updated.
278278
* Otherwise, a negative errno value is returned.
279279
*/
280280
int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry)
281281
{
282-
static const struct xa_limit limit = XA_LIMIT(DIR_OFFSET_MIN, U32_MAX);
283-
u32 offset;
282+
unsigned long offset;
284283
int ret;
285284

286285
if (dentry2offset(dentry) != 0)
287286
return -EBUSY;
288287

289-
ret = xa_alloc_cyclic(&octx->xa, &offset, dentry, limit,
290-
&octx->next_offset, GFP_KERNEL);
288+
ret = mtree_alloc_cyclic(&octx->mt, &offset, dentry, DIR_OFFSET_MIN,
289+
LONG_MAX, &octx->next_offset, GFP_KERNEL);
291290
if (ret < 0)
292291
return ret;
293292

@@ -303,13 +302,13 @@ int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry)
303302
*/
304303
void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry)
305304
{
306-
u32 offset;
305+
long offset;
307306

308307
offset = dentry2offset(dentry);
309308
if (offset == 0)
310309
return;
311310

312-
xa_erase(&octx->xa, offset);
311+
mtree_erase(&octx->mt, offset);
313312
offset_set(dentry, 0);
314313
}
315314

@@ -332,7 +331,7 @@ int simple_offset_empty(struct dentry *dentry)
332331

333332
index = DIR_OFFSET_MIN;
334333
octx = inode->i_op->get_offset_ctx(inode);
335-
xa_for_each(&octx->xa, index, child) {
334+
mt_for_each(&octx->mt, child, index, LONG_MAX) {
336335
spin_lock(&child->d_lock);
337336
if (simple_positive(child)) {
338337
spin_unlock(&child->d_lock);
@@ -362,8 +361,8 @@ int simple_offset_rename_exchange(struct inode *old_dir,
362361
{
363362
struct offset_ctx *old_ctx = old_dir->i_op->get_offset_ctx(old_dir);
364363
struct offset_ctx *new_ctx = new_dir->i_op->get_offset_ctx(new_dir);
365-
u32 old_index = dentry2offset(old_dentry);
366-
u32 new_index = dentry2offset(new_dentry);
364+
long old_index = dentry2offset(old_dentry);
365+
long new_index = dentry2offset(new_dentry);
367366
int ret;
368367

369368
simple_offset_remove(old_ctx, old_dentry);
@@ -389,9 +388,9 @@ int simple_offset_rename_exchange(struct inode *old_dir,
389388

390389
out_restore:
391390
offset_set(old_dentry, old_index);
392-
xa_store(&old_ctx->xa, old_index, old_dentry, GFP_KERNEL);
391+
mtree_store(&old_ctx->mt, old_index, old_dentry, GFP_KERNEL);
393392
offset_set(new_dentry, new_index);
394-
xa_store(&new_ctx->xa, new_index, new_dentry, GFP_KERNEL);
393+
mtree_store(&new_ctx->mt, new_index, new_dentry, GFP_KERNEL);
395394
return ret;
396395
}
397396

@@ -404,7 +403,7 @@ int simple_offset_rename_exchange(struct inode *old_dir,
404403
*/
405404
void simple_offset_destroy(struct offset_ctx *octx)
406405
{
407-
xa_destroy(&octx->xa);
406+
mtree_destroy(&octx->mt);
408407
}
409408

410409
/**
@@ -434,16 +433,16 @@ static loff_t offset_dir_llseek(struct file *file, loff_t offset, int whence)
434433

435434
/* In this case, ->private_data is protected by f_pos_lock */
436435
file->private_data = NULL;
437-
return vfs_setpos(file, offset, U32_MAX);
436+
return vfs_setpos(file, offset, LONG_MAX);
438437
}
439438

440439
static struct dentry *offset_find_next(struct offset_ctx *octx, loff_t offset)
441440
{
441+
MA_STATE(mas, &octx->mt, offset, offset);
442442
struct dentry *child, *found = NULL;
443-
XA_STATE(xas, &octx->xa, offset);
444443

445444
rcu_read_lock();
446-
child = xas_next_entry(&xas, U32_MAX);
445+
child = mas_find(&mas, LONG_MAX);
447446
if (!child)
448447
goto out;
449448
spin_lock(&child->d_lock);
@@ -457,8 +456,8 @@ static struct dentry *offset_find_next(struct offset_ctx *octx, loff_t offset)
457456

458457
static bool offset_dir_emit(struct dir_context *ctx, struct dentry *dentry)
459458
{
460-
u32 offset = dentry2offset(dentry);
461459
struct inode *inode = d_inode(dentry);
460+
long offset = dentry2offset(dentry);
462461

463462
return ctx->actor(ctx, dentry->d_name.name, dentry->d_name.len, offset,
464463
inode->i_ino, fs_umode_to_dtype(inode->i_mode));

include/linux/fs.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include <linux/cred.h>
4444
#include <linux/mnt_idmapping.h>
4545
#include <linux/slab.h>
46+
#include <linux/maple_tree.h>
4647

4748
#include <asm/byteorder.h>
4849
#include <uapi/linux/fs.h>
@@ -3260,8 +3261,8 @@ extern ssize_t simple_write_to_buffer(void *to, size_t available, loff_t *ppos,
32603261
const void __user *from, size_t count);
32613262

32623263
struct offset_ctx {
3263-
struct xarray xa;
3264-
u32 next_offset;
3264+
struct maple_tree mt;
3265+
unsigned long next_offset;
32653266
};
32663267

32673268
void simple_offset_init(struct offset_ctx *octx);

0 commit comments

Comments
 (0)