Skip to content

Commit 1c921ba

Browse files
committed
Merge patch series "Allow knfsd to use atomic_open()"
Benjamin Coddington <bcodding@hammerspace.com> says: We have workloads that will benefit from allowing knfsd to use atomic_open() in the open/create path. There are two benefits; the first is the original matter of correctness: when knfsd must perform both vfs_create() and vfs_open() in series there can be races or error results that cause the caller to receive unexpected results. The second benefit is that for some network filesystems, we can reduce the number of remote round-trip operations by using a single atomic_open() path which provides a performance benefit. I've implemented this with the simplest possible change - by modifying dentry_create() which has a single user: knfsd. The changes cause us to insert ourselves part-way into the previously closed/static atomic_open() path, so I expect VFS folks to have some good ideas about potentially superior approaches. Previous work on commit fb70bf1 ("NFSD: Instantiate a struct file when creating a regular NFSv4 file") addressed most of the atomicity issues, but there are still a few gaps on network filesystems. The problem was noticed on a test that did open O_CREAT with mode 0 which will succeed in creating the file but will return -EACCES from vfs_open() - this specific test is mentioned in 3/3 description. Also, Trond notes that independently of the permissions issues, atomic_open also solves races in open(O_CREAT|O_TRUNC). The NFS client now uses it for both NFSv4 and NFSv3 for that reason. See commit 7c6c524 "NFS: add atomic_open for NFSv3 to handle O_TRUNC correctly." * patches from https://patch.msgid.link/cover.1764259052.git.bcodding@hammerspace.com: VFS/knfsd: Teach dentry_create() to use atomic_open() VFS: Prepare atomic_open() for dentry_create() VFS: move dentry_create() from fs/open.c to fs/namei.c Link: https://patch.msgid.link/cover.1764259052.git.bcodding@hammerspace.com Signed-off-by: Christian Brauner <brauner@kernel.org>
2 parents 8f0b4cc + 64a989d commit 1c921ba

4 files changed

Lines changed: 82 additions & 50 deletions

File tree

fs/namei.c

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4279,19 +4279,16 @@ static int may_o_create(struct mnt_idmap *idmap,
42794279
*
42804280
* Returns an error code otherwise.
42814281
*/
4282-
static struct dentry *atomic_open(struct nameidata *nd, struct dentry *dentry,
4282+
static struct dentry *atomic_open(const struct path *path, struct dentry *dentry,
42834283
struct file *file,
42844284
int open_flag, umode_t mode)
42854285
{
42864286
struct dentry *const DENTRY_NOT_SET = (void *) -1UL;
4287-
struct inode *dir = nd->path.dentry->d_inode;
4287+
struct inode *dir = path->dentry->d_inode;
42884288
int error;
42894289

4290-
if (nd->flags & LOOKUP_DIRECTORY)
4291-
open_flag |= O_DIRECTORY;
4292-
42934290
file->__f_path.dentry = DENTRY_NOT_SET;
4294-
file->__f_path.mnt = nd->path.mnt;
4291+
file->__f_path.mnt = path->mnt;
42954292
error = dir->i_op->atomic_open(dir, dentry, file,
42964293
open_to_namei_flags(open_flag), mode);
42974294
d_lookup_done(dentry);
@@ -4403,7 +4400,9 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
44034400
if (create_error)
44044401
open_flag &= ~O_CREAT;
44054402
if (dir_inode->i_op->atomic_open) {
4406-
dentry = atomic_open(nd, dentry, file, open_flag, mode);
4403+
if (nd->flags & LOOKUP_DIRECTORY)
4404+
open_flag |= O_DIRECTORY;
4405+
dentry = atomic_open(&nd->path, dentry, file, open_flag, mode);
44074406
if (unlikely(create_error) && dentry == ERR_PTR(-ENOENT))
44084407
dentry = ERR_PTR(create_error);
44094408
return dentry;
@@ -4937,6 +4936,73 @@ inline struct dentry *start_creating_user_path(
49374936
}
49384937
EXPORT_SYMBOL(start_creating_user_path);
49394938

4939+
/**
4940+
* dentry_create - Create and open a file
4941+
* @path: path to create
4942+
* @flags: O_ flags
4943+
* @mode: mode bits for new file
4944+
* @cred: credentials to use
4945+
*
4946+
* Caller must hold the parent directory's lock, and have prepared
4947+
* a negative dentry, placed in @path->dentry, for the new file.
4948+
*
4949+
* Caller sets @path->mnt to the vfsmount of the filesystem where
4950+
* the new file is to be created. The parent directory and the
4951+
* negative dentry must reside on the same filesystem instance.
4952+
*
4953+
* On success, returns a "struct file *". Otherwise a ERR_PTR
4954+
* is returned.
4955+
*/
4956+
struct file *dentry_create(struct path *path, int flags, umode_t mode,
4957+
const struct cred *cred)
4958+
{
4959+
struct file *file __free(fput) = NULL;
4960+
struct dentry *dentry = path->dentry;
4961+
struct dentry *dir = dentry->d_parent;
4962+
struct inode *dir_inode = d_inode(dir);
4963+
struct mnt_idmap *idmap;
4964+
int error, create_error;
4965+
4966+
file = alloc_empty_file(flags, cred);
4967+
if (IS_ERR(file))
4968+
return file;
4969+
4970+
idmap = mnt_idmap(path->mnt);
4971+
4972+
if (dir_inode->i_op->atomic_open) {
4973+
path->dentry = dir;
4974+
mode = vfs_prepare_mode(idmap, dir_inode, mode, S_IALLUGO, S_IFREG);
4975+
4976+
create_error = may_o_create(idmap, path, dentry, mode);
4977+
if (create_error)
4978+
flags &= ~O_CREAT;
4979+
4980+
dentry = atomic_open(path, dentry, file, flags, mode);
4981+
error = PTR_ERR_OR_ZERO(dentry);
4982+
4983+
if (unlikely(create_error) && error == -ENOENT)
4984+
error = create_error;
4985+
4986+
if (!error) {
4987+
if (file->f_mode & FMODE_CREATED)
4988+
fsnotify_create(dir->d_inode, dentry);
4989+
if (file->f_mode & FMODE_OPENED)
4990+
fsnotify_open(file);
4991+
}
4992+
4993+
path->dentry = dentry;
4994+
4995+
} else {
4996+
error = vfs_create(mnt_idmap(path->mnt), path->dentry, mode, NULL);
4997+
if (!error)
4998+
error = vfs_open(path, file);
4999+
}
5000+
if (unlikely(error))
5001+
return ERR_PTR(error);
5002+
5003+
return no_free_ptr(file);
5004+
}
5005+
EXPORT_SYMBOL(dentry_create);
49405006

49415007
/**
49425008
* vfs_mknod - create device node or file

fs/nfsd/nfs4proc.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,14 +194,17 @@ static inline bool nfsd4_create_is_exclusive(int createmode)
194194
}
195195

196196
static __be32
197-
nfsd4_vfs_create(struct svc_fh *fhp, struct dentry *child,
197+
nfsd4_vfs_create(struct svc_fh *fhp, struct dentry **child,
198198
struct nfsd4_open *open)
199199
{
200200
struct file *filp;
201201
struct path path;
202202
int oflags;
203203

204204
oflags = O_CREAT | O_LARGEFILE;
205+
if (nfsd4_create_is_exclusive(open->op_createmode))
206+
oflags |= O_EXCL;
207+
205208
switch (open->op_share_access & NFS4_SHARE_ACCESS_BOTH) {
206209
case NFS4_SHARE_ACCESS_WRITE:
207210
oflags |= O_WRONLY;
@@ -214,9 +217,11 @@ nfsd4_vfs_create(struct svc_fh *fhp, struct dentry *child,
214217
}
215218

216219
path.mnt = fhp->fh_export->ex_path.mnt;
217-
path.dentry = child;
220+
path.dentry = *child;
218221
filp = dentry_create(&path, oflags, open->op_iattr.ia_mode,
219222
current_cred());
223+
*child = path.dentry;
224+
220225
if (IS_ERR(filp))
221226
return nfserrno(PTR_ERR(filp));
222227

@@ -350,7 +355,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
350355
status = fh_fill_pre_attrs(fhp);
351356
if (status != nfs_ok)
352357
goto out;
353-
status = nfsd4_vfs_create(fhp, child, open);
358+
status = nfsd4_vfs_create(fhp, &child, open);
354359
if (status != nfs_ok)
355360
goto out;
356361
open->op_created = true;

fs/open.c

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,45 +1141,6 @@ struct file *dentry_open_nonotify(const struct path *path, int flags,
11411141
return f;
11421142
}
11431143

1144-
/**
1145-
* dentry_create - Create and open a file
1146-
* @path: path to create
1147-
* @flags: O_ flags
1148-
* @mode: mode bits for new file
1149-
* @cred: credentials to use
1150-
*
1151-
* Caller must hold the parent directory's lock, and have prepared
1152-
* a negative dentry, placed in @path->dentry, for the new file.
1153-
*
1154-
* Caller sets @path->mnt to the vfsmount of the filesystem where
1155-
* the new file is to be created. The parent directory and the
1156-
* negative dentry must reside on the same filesystem instance.
1157-
*
1158-
* On success, returns a "struct file *". Otherwise a ERR_PTR
1159-
* is returned.
1160-
*/
1161-
struct file *dentry_create(const struct path *path, int flags, umode_t mode,
1162-
const struct cred *cred)
1163-
{
1164-
struct file *f;
1165-
int error;
1166-
1167-
f = alloc_empty_file(flags, cred);
1168-
if (IS_ERR(f))
1169-
return f;
1170-
1171-
error = vfs_create(mnt_idmap(path->mnt), path->dentry, mode, NULL);
1172-
if (!error)
1173-
error = vfs_open(path, f);
1174-
1175-
if (unlikely(error)) {
1176-
fput(f);
1177-
return ERR_PTR(error);
1178-
}
1179-
return f;
1180-
}
1181-
EXPORT_SYMBOL(dentry_create);
1182-
11831144
/**
11841145
* kernel_file_open - open a file for kernel internal use
11851146
* @path: path of the file to open

include/linux/fs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2457,7 +2457,7 @@ struct file *dentry_open(const struct path *path, int flags,
24572457
const struct cred *creds);
24582458
struct file *dentry_open_nonotify(const struct path *path, int flags,
24592459
const struct cred *cred);
2460-
struct file *dentry_create(const struct path *path, int flags, umode_t mode,
2460+
struct file *dentry_create(struct path *path, int flags, umode_t mode,
24612461
const struct cred *cred);
24622462
const struct path *backing_file_user_path(const struct file *f);
24632463

0 commit comments

Comments
 (0)