diff options
author | npiggin@suse.de <npiggin@suse.de> | 2009-04-26 06:25:55 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2009-06-11 21:36:02 -0400 |
commit | 96029c4e09ccbd73a6d0ed2b29e80bf2586ad7ef (patch) | |
tree | 032d2ac7024250c18487a2c7122af68fe56567ff | |
parent | d3ef3d7351ccfbef3e5d926efc5ee332136f40d4 (diff) |
fs: introduce mnt_clone_write
This patch speeds up lmbench lat_mmap test by about another 2% after the
first patch.
Before:
avg = 462.286
std = 5.46106
After:
avg = 453.12
std = 9.58257
(50 runs of each, stddev gives a reasonable confidence)
It does this by introducing mnt_clone_write, which avoids some heavyweight
operations of mnt_want_write if called on a vfsmount which we know already
has a write count; and mnt_want_write_file, which can call mnt_clone_write
if the file is open for write.
After these two patches, mnt_want_write and mnt_drop_write go from 7% on
the profile down to 1.3% (including mnt_clone_write).
[AV: mnt_want_write_file() should take file alone and derive mnt from it;
not only all callers have that form, but that's the only mnt about which
we know that it's already held for write if file is opened for write]
Cc: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/file_table.c | 2 | ||||
-rw-r--r-- | fs/inode.c | 2 | ||||
-rw-r--r-- | fs/namespace.c | 40 | ||||
-rw-r--r-- | fs/open.c | 4 | ||||
-rw-r--r-- | fs/xattr.c | 4 | ||||
-rw-r--r-- | include/linux/mount.h | 4 |
6 files changed, 50 insertions, 6 deletions
diff --git a/fs/file_table.c b/fs/file_table.c index 54018fe48840..3d66dbcebef6 100644 --- a/fs/file_table.c +++ b/fs/file_table.c | |||
@@ -214,7 +214,7 @@ int init_file(struct file *file, struct vfsmount *mnt, struct dentry *dentry, | |||
214 | */ | 214 | */ |
215 | if ((mode & FMODE_WRITE) && !special_file(dentry->d_inode->i_mode)) { | 215 | if ((mode & FMODE_WRITE) && !special_file(dentry->d_inode->i_mode)) { |
216 | file_take_write(file); | 216 | file_take_write(file); |
217 | error = mnt_want_write(mnt); | 217 | error = mnt_clone_write(mnt); |
218 | WARN_ON(error); | 218 | WARN_ON(error); |
219 | } | 219 | } |
220 | return error; | 220 | return error; |
diff --git a/fs/inode.c b/fs/inode.c index ca337014ae29..a88baebf77cf 100644 --- a/fs/inode.c +++ b/fs/inode.c | |||
@@ -1422,7 +1422,7 @@ void file_update_time(struct file *file) | |||
1422 | if (IS_NOCMTIME(inode)) | 1422 | if (IS_NOCMTIME(inode)) |
1423 | return; | 1423 | return; |
1424 | 1424 | ||
1425 | err = mnt_want_write(file->f_path.mnt); | 1425 | err = mnt_want_write_file(file); |
1426 | if (err) | 1426 | if (err) |
1427 | return; | 1427 | return; |
1428 | 1428 | ||
diff --git a/fs/namespace.c b/fs/namespace.c index 22ae06ad751d..120b8a6b99ed 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -265,6 +265,46 @@ out: | |||
265 | EXPORT_SYMBOL_GPL(mnt_want_write); | 265 | EXPORT_SYMBOL_GPL(mnt_want_write); |
266 | 266 | ||
267 | /** | 267 | /** |
268 | * mnt_clone_write - get write access to a mount | ||
269 | * @mnt: the mount on which to take a write | ||
270 | * | ||
271 | * This is effectively like mnt_want_write, except | ||
272 | * it must only be used to take an extra write reference | ||
273 | * on a mountpoint that we already know has a write reference | ||
274 | * on it. This allows some optimisation. | ||
275 | * | ||
276 | * After finished, mnt_drop_write must be called as usual to | ||
277 | * drop the reference. | ||
278 | */ | ||
279 | int mnt_clone_write(struct vfsmount *mnt) | ||
280 | { | ||
281 | /* superblock may be r/o */ | ||
282 | if (__mnt_is_readonly(mnt)) | ||
283 | return -EROFS; | ||
284 | preempt_disable(); | ||
285 | inc_mnt_writers(mnt); | ||
286 | preempt_enable(); | ||
287 | return 0; | ||
288 | } | ||
289 | EXPORT_SYMBOL_GPL(mnt_clone_write); | ||
290 | |||
291 | /** | ||
292 | * mnt_want_write_file - get write access to a file's mount | ||
293 | * @file: the file who's mount on which to take a write | ||
294 | * | ||
295 | * This is like mnt_want_write, but it takes a file and can | ||
296 | * do some optimisations if the file is open for write already | ||
297 | */ | ||
298 | int mnt_want_write_file(struct file *file) | ||
299 | { | ||
300 | if (!(file->f_mode & FMODE_WRITE)) | ||
301 | return mnt_want_write(file->f_path.mnt); | ||
302 | else | ||
303 | return mnt_clone_write(file->f_path.mnt); | ||
304 | } | ||
305 | EXPORT_SYMBOL_GPL(mnt_want_write_file); | ||
306 | |||
307 | /** | ||
268 | * mnt_drop_write - give up write access to a mount | 308 | * mnt_drop_write - give up write access to a mount |
269 | * @mnt: the mount on which to give up write access | 309 | * @mnt: the mount on which to give up write access |
270 | * | 310 | * |
@@ -612,7 +612,7 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd, mode_t, mode) | |||
612 | 612 | ||
613 | audit_inode(NULL, dentry); | 613 | audit_inode(NULL, dentry); |
614 | 614 | ||
615 | err = mnt_want_write(file->f_path.mnt); | 615 | err = mnt_want_write_file(file); |
616 | if (err) | 616 | if (err) |
617 | goto out_putf; | 617 | goto out_putf; |
618 | mutex_lock(&inode->i_mutex); | 618 | mutex_lock(&inode->i_mutex); |
@@ -761,7 +761,7 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group) | |||
761 | if (!file) | 761 | if (!file) |
762 | goto out; | 762 | goto out; |
763 | 763 | ||
764 | error = mnt_want_write(file->f_path.mnt); | 764 | error = mnt_want_write_file(file); |
765 | if (error) | 765 | if (error) |
766 | goto out_fput; | 766 | goto out_fput; |
767 | dentry = file->f_path.dentry; | 767 | dentry = file->f_path.dentry; |
diff --git a/fs/xattr.c b/fs/xattr.c index d51b8f9db921..1c3d0af59ddf 100644 --- a/fs/xattr.c +++ b/fs/xattr.c | |||
@@ -297,7 +297,7 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name, | |||
297 | return error; | 297 | return error; |
298 | dentry = f->f_path.dentry; | 298 | dentry = f->f_path.dentry; |
299 | audit_inode(NULL, dentry); | 299 | audit_inode(NULL, dentry); |
300 | error = mnt_want_write(f->f_path.mnt); | 300 | error = mnt_want_write_file(f); |
301 | if (!error) { | 301 | if (!error) { |
302 | error = setxattr(dentry, name, value, size, flags); | 302 | error = setxattr(dentry, name, value, size, flags); |
303 | mnt_drop_write(f->f_path.mnt); | 303 | mnt_drop_write(f->f_path.mnt); |
@@ -524,7 +524,7 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name) | |||
524 | return error; | 524 | return error; |
525 | dentry = f->f_path.dentry; | 525 | dentry = f->f_path.dentry; |
526 | audit_inode(NULL, dentry); | 526 | audit_inode(NULL, dentry); |
527 | error = mnt_want_write(f->f_path.mnt); | 527 | error = mnt_want_write_file(f); |
528 | if (!error) { | 528 | if (!error) { |
529 | error = removexattr(dentry, name); | 529 | error = removexattr(dentry, name); |
530 | mnt_drop_write(f->f_path.mnt); | 530 | mnt_drop_write(f->f_path.mnt); |
diff --git a/include/linux/mount.h b/include/linux/mount.h index ac49c1f8e5c0..5d5275364867 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h | |||
@@ -88,7 +88,11 @@ static inline struct vfsmount *mntget(struct vfsmount *mnt) | |||
88 | return mnt; | 88 | return mnt; |
89 | } | 89 | } |
90 | 90 | ||
91 | struct file; /* forward dec */ | ||
92 | |||
91 | extern int mnt_want_write(struct vfsmount *mnt); | 93 | extern int mnt_want_write(struct vfsmount *mnt); |
94 | extern int mnt_want_write_file(struct file *file); | ||
95 | extern int mnt_clone_write(struct vfsmount *mnt); | ||
92 | extern void mnt_drop_write(struct vfsmount *mnt); | 96 | extern void mnt_drop_write(struct vfsmount *mnt); |
93 | extern void mntput_no_expire(struct vfsmount *mnt); | 97 | extern void mntput_no_expire(struct vfsmount *mnt); |
94 | extern void mnt_pin(struct vfsmount *mnt); | 98 | extern void mnt_pin(struct vfsmount *mnt); |