Skip to content

Commit a2e4595

Browse files
chuckleverbrauner
authored andcommitted
shmem: stable directory offsets
The current cursor-based directory offset mechanism doesn't work when a tmpfs filesystem is exported via NFS. This is because NFS clients do not open directories. Each server-side READDIR operation has to open the directory, read it, then close it. The cursor state for that directory, being associated strictly with the opened struct file, is thus discarded after each NFS READDIR operation. Directory offsets are cached not only by NFS clients, but also by user space libraries on those clients. Essentially there is no way to invalidate those caches when directory offsets have changed on an NFS server after the offset-to-dentry mapping changes. Thus the whole application stack depends on unchanging directory offsets. The solution we've come up with is to make the directory offset for each file in a tmpfs filesystem stable for the life of the directory entry it represents. shmem_readdir() and shmem_dir_llseek() now use an xarray to map each directory offset (an loff_t integer) to the memory address of a struct dentry. Signed-off-by: Chuck Lever <[email protected]> Message-Id: <168814734331.530310.3911190551060453102.stgit@manet.1015granger.net> Signed-off-by: Christian Brauner <[email protected]>
1 parent 23a31d8 commit a2e4595

File tree

2 files changed

+41
-7
lines changed

2 files changed

+41
-7
lines changed

include/linux/shmem_fs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ struct shmem_inode_info {
3434
#ifdef CONFIG_TMPFS_QUOTA
3535
struct dquot *i_dquot[MAXQUOTAS];
3636
#endif
37+
struct offset_ctx dir_offsets; /* stable entry offsets */
3738
struct inode vfs_inode;
3839
};
3940

mm/shmem.c

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2427,6 +2427,11 @@ static void shmem_set_inode_flags(struct inode *inode, unsigned int fsflags)
24272427
#define shmem_initxattrs NULL
24282428
#endif
24292429

2430+
static struct offset_ctx *shmem_get_offset_ctx(struct inode *inode)
2431+
{
2432+
return &SHMEM_I(inode)->dir_offsets;
2433+
}
2434+
24302435
static struct inode *__shmem_get_inode(struct mnt_idmap *idmap,
24312436
struct super_block *sb,
24322437
struct inode *dir, umode_t mode,
@@ -2492,7 +2497,8 @@ static struct inode *__shmem_get_inode(struct mnt_idmap *idmap,
24922497
/* Some things misbehave if size == 0 on a directory */
24932498
inode->i_size = 2 * BOGO_DIRENT_SIZE;
24942499
inode->i_op = &shmem_dir_inode_operations;
2495-
inode->i_fop = &simple_dir_operations;
2500+
inode->i_fop = &simple_offset_dir_operations;
2501+
simple_offset_init(shmem_get_offset_ctx(inode));
24962502
break;
24972503
case S_IFLNK:
24982504
/*
@@ -3204,7 +3210,10 @@ shmem_mknod(struct mnt_idmap *idmap, struct inode *dir,
32043210
if (error && error != -EOPNOTSUPP)
32053211
goto out_iput;
32063212

3207-
error = 0;
3213+
error = simple_offset_add(shmem_get_offset_ctx(dir), dentry);
3214+
if (error)
3215+
goto out_iput;
3216+
32083217
dir->i_size += BOGO_DIRENT_SIZE;
32093218
dir->i_ctime = dir->i_mtime = current_time(dir);
32103219
inode_inc_iversion(dir);
@@ -3287,6 +3296,13 @@ static int shmem_link(struct dentry *old_dentry, struct inode *dir, struct dentr
32873296
goto out;
32883297
}
32893298

3299+
ret = simple_offset_add(shmem_get_offset_ctx(dir), dentry);
3300+
if (ret) {
3301+
if (inode->i_nlink)
3302+
shmem_free_inode(inode->i_sb);
3303+
goto out;
3304+
}
3305+
32903306
dir->i_size += BOGO_DIRENT_SIZE;
32913307
inode->i_ctime = dir->i_ctime = dir->i_mtime = current_time(inode);
32923308
inode_inc_iversion(dir);
@@ -3305,6 +3321,8 @@ static int shmem_unlink(struct inode *dir, struct dentry *dentry)
33053321
if (inode->i_nlink > 1 && !S_ISDIR(inode->i_mode))
33063322
shmem_free_inode(inode->i_sb);
33073323

3324+
simple_offset_remove(shmem_get_offset_ctx(dir), dentry);
3325+
33083326
dir->i_size -= BOGO_DIRENT_SIZE;
33093327
inode->i_ctime = dir->i_ctime = dir->i_mtime = current_time(inode);
33103328
inode_inc_iversion(dir);
@@ -3363,24 +3381,29 @@ static int shmem_rename2(struct mnt_idmap *idmap,
33633381
{
33643382
struct inode *inode = d_inode(old_dentry);
33653383
int they_are_dirs = S_ISDIR(inode->i_mode);
3384+
int error;
33663385

33673386
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
33683387
return -EINVAL;
33693388

33703389
if (flags & RENAME_EXCHANGE)
3371-
return simple_rename_exchange(old_dir, old_dentry, new_dir, new_dentry);
3390+
return simple_offset_rename_exchange(old_dir, old_dentry,
3391+
new_dir, new_dentry);
33723392

33733393
if (!simple_empty(new_dentry))
33743394
return -ENOTEMPTY;
33753395

33763396
if (flags & RENAME_WHITEOUT) {
3377-
int error;
3378-
33793397
error = shmem_whiteout(idmap, old_dir, old_dentry);
33803398
if (error)
33813399
return error;
33823400
}
33833401

3402+
simple_offset_remove(shmem_get_offset_ctx(old_dir), old_dentry);
3403+
error = simple_offset_add(shmem_get_offset_ctx(new_dir), old_dentry);
3404+
if (error)
3405+
return error;
3406+
33843407
if (d_really_is_positive(new_dentry)) {
33853408
(void) shmem_unlink(new_dir, new_dentry);
33863409
if (they_are_dirs) {
@@ -3425,19 +3448,23 @@ static int shmem_symlink(struct mnt_idmap *idmap, struct inode *dir,
34253448
if (error && error != -EOPNOTSUPP)
34263449
goto out_iput;
34273450

3451+
error = simple_offset_add(shmem_get_offset_ctx(dir), dentry);
3452+
if (error)
3453+
goto out_iput;
3454+
34283455
inode->i_size = len-1;
34293456
if (len <= SHORT_SYMLINK_LEN) {
34303457
inode->i_link = kmemdup(symname, len, GFP_KERNEL);
34313458
if (!inode->i_link) {
34323459
error = -ENOMEM;
3433-
goto out_iput;
3460+
goto out_remove_offset;
34343461
}
34353462
inode->i_op = &shmem_short_symlink_operations;
34363463
} else {
34373464
inode_nohighmem(inode);
34383465
error = shmem_get_folio(inode, 0, &folio, SGP_WRITE);
34393466
if (error)
3440-
goto out_iput;
3467+
goto out_remove_offset;
34413468
inode->i_mapping->a_ops = &shmem_aops;
34423469
inode->i_op = &shmem_symlink_inode_operations;
34433470
memcpy(folio_address(folio), symname, len);
@@ -3452,6 +3479,9 @@ static int shmem_symlink(struct mnt_idmap *idmap, struct inode *dir,
34523479
d_instantiate(dentry, inode);
34533480
dget(dentry);
34543481
return 0;
3482+
3483+
out_remove_offset:
3484+
simple_offset_remove(shmem_get_offset_ctx(dir), dentry);
34553485
out_iput:
34563486
iput(inode);
34573487
return error;
@@ -4295,6 +4325,8 @@ static void shmem_destroy_inode(struct inode *inode)
42954325
{
42964326
if (S_ISREG(inode->i_mode))
42974327
mpol_free_shared_policy(&SHMEM_I(inode)->policy);
4328+
if (S_ISDIR(inode->i_mode))
4329+
simple_offset_destroy(shmem_get_offset_ctx(inode));
42984330
}
42994331

43004332
static void shmem_init_inode(void *foo)
@@ -4375,6 +4407,7 @@ static const struct inode_operations shmem_dir_inode_operations = {
43754407
.mknod = shmem_mknod,
43764408
.rename = shmem_rename2,
43774409
.tmpfile = shmem_tmpfile,
4410+
.get_offset_ctx = shmem_get_offset_ctx,
43784411
#endif
43794412
#ifdef CONFIG_TMPFS_XATTR
43804413
.listxattr = shmem_listxattr,

0 commit comments

Comments
 (0)