]>
Commit | Line | Data |
---|---|---|
00e5a55c BS |
1 | From: Nick Piggin <npiggin@suse.de> |
2 | Subject: fs: introduce mnt_clone_write | |
3 | References: bnc#436953 | |
4 | Patch-upstream: no (could be submitted) | |
5 | ||
6 | This patch speeds up lmbench lat_mmap test by about another 2% after the | |
7 | first patch. | |
8 | ||
9 | Before: | |
10 | avg = 462.286 | |
11 | std = 5.46106 | |
12 | ||
13 | After: | |
14 | avg = 453.12 | |
15 | std = 9.58257 | |
16 | ||
17 | (50 runs of each, stddev gives a reasonable confidence) | |
18 | ||
19 | It does this by introducing mnt_clone_write, which avoids some heavyweight | |
20 | operations of mnt_want_write if called on a vfsmount which we know already | |
21 | has a write count; and mnt_want_write_file, which can call mnt_clone_write | |
22 | if the file is open for write. | |
23 | ||
24 | After these two patches, mnt_want_write and mnt_drop_write go from 7% on | |
25 | the profile down to 1.3% (including mnt_clone_write). | |
26 | ||
27 | --- | |
28 | fs/file_table.c | 3 +-- | |
29 | fs/inode.c | 2 +- | |
30 | fs/namespace.c | 38 ++++++++++++++++++++++++++++++++++++++ | |
31 | fs/open.c | 4 ++-- | |
32 | fs/xattr.c | 4 ++-- | |
33 | include/linux/mount.h | 2 ++ | |
34 | 6 files changed, 46 insertions(+), 7 deletions(-) | |
35 | ||
36 | --- a/fs/file_table.c | |
37 | +++ b/fs/file_table.c | |
38 | @@ -210,8 +210,7 @@ int init_file(struct file *file, struct | |
39 | */ | |
40 | if ((mode & FMODE_WRITE) && !special_file(dentry->d_inode->i_mode)) { | |
41 | file_take_write(file); | |
42 | - error = mnt_want_write(mnt); | |
43 | - WARN_ON(error); | |
44 | + mnt_clone_write(mnt); | |
45 | } | |
46 | return error; | |
47 | } | |
48 | --- a/fs/inode.c | |
49 | +++ b/fs/inode.c | |
50 | @@ -1256,7 +1256,7 @@ void file_update_time(struct file *file) | |
51 | if (IS_NOCMTIME(inode)) | |
52 | return; | |
53 | ||
54 | - err = mnt_want_write(file->f_path.mnt); | |
55 | + err = mnt_want_write_file(file->f_path.mnt, file); | |
56 | if (err) | |
57 | return; | |
58 | ||
59 | --- a/fs/namespace.c | |
60 | +++ b/fs/namespace.c | |
61 | @@ -264,6 +264,44 @@ out: | |
62 | EXPORT_SYMBOL_GPL(mnt_want_write); | |
63 | ||
64 | /** | |
65 | + * mnt_clone_write - get write access to a mount | |
66 | + * @mnt: the mount on which to take a write | |
67 | + * | |
68 | + * This is effectively like mnt_want_write, except | |
69 | + * it must only be used to take an extra write reference | |
70 | + * on a mountpoint that we already know has a write reference | |
71 | + * on it. This allows some optimisation. | |
72 | + * | |
73 | + * After finished, mnt_drop_write must be called as usual to | |
74 | + * drop the reference. | |
75 | + */ | |
76 | +void mnt_clone_write(struct vfsmount *mnt) | |
77 | +{ | |
78 | + preempt_disable(); | |
79 | + inc_mnt_writers(mnt); | |
80 | + preempt_enable(); | |
81 | +} | |
82 | +EXPORT_SYMBOL_GPL(mnt_clone_write); | |
83 | + | |
84 | +/** | |
85 | + * mnt_want_write_file - get write access to a file's mount | |
86 | + * @file: the file who's mount on which to take a write | |
87 | + * | |
88 | + * This is like mnt_want_write, but it takes a file and can | |
89 | + * do some optimisations if the file is open for write already | |
90 | + */ | |
91 | +int mnt_want_write_file(struct vfsmount *mnt, struct file *file) | |
92 | +{ | |
93 | + if (!(file->f_mode & FMODE_WRITE)) | |
94 | + return mnt_want_write(mnt); | |
95 | + else { | |
96 | + mnt_clone_write(mnt); | |
97 | + return 0; | |
98 | + } | |
99 | +} | |
100 | +EXPORT_SYMBOL_GPL(mnt_want_write_file); | |
101 | + | |
102 | +/** | |
103 | * mnt_drop_write - give up write access to a mount | |
104 | * @mnt: the mount on which to give up write access | |
105 | * | |
106 | --- a/fs/open.c | |
107 | +++ b/fs/open.c | |
108 | @@ -616,7 +616,7 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd | |
109 | ||
110 | audit_inode(NULL, dentry); | |
111 | ||
112 | - err = mnt_want_write(file->f_path.mnt); | |
113 | + err = mnt_want_write_file(file->f_path.mnt, file); | |
114 | if (err) | |
115 | goto out_putf; | |
116 | mutex_lock(&inode->i_mutex); | |
117 | @@ -765,7 +765,7 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd | |
118 | if (!file) | |
119 | goto out; | |
120 | ||
121 | - error = mnt_want_write(file->f_path.mnt); | |
122 | + error = mnt_want_write_file(file->f_path.mnt, file); | |
123 | if (error) | |
124 | goto out_fput; | |
125 | dentry = file->f_path.dentry; | |
126 | --- a/fs/xattr.c | |
127 | +++ b/fs/xattr.c | |
128 | @@ -301,7 +301,7 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, cons | |
129 | return error; | |
130 | dentry = f->f_path.dentry; | |
131 | audit_inode(NULL, dentry); | |
132 | - error = mnt_want_write(f->f_path.mnt); | |
133 | + error = mnt_want_write_file(f->f_path.mnt, f); | |
134 | if (!error) { | |
135 | error = setxattr(dentry, name, value, size, flags); | |
136 | mnt_drop_write(f->f_path.mnt); | |
137 | @@ -528,7 +528,7 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, c | |
138 | return error; | |
139 | dentry = f->f_path.dentry; | |
140 | audit_inode(NULL, dentry); | |
141 | - error = mnt_want_write(f->f_path.mnt); | |
142 | + error = mnt_want_write_file(f->f_path.mnt, f); | |
143 | if (!error) { | |
144 | error = removexattr(dentry, name); | |
145 | mnt_drop_write(f->f_path.mnt); | |
146 | --- a/include/linux/mount.h | |
147 | +++ b/include/linux/mount.h | |
148 | @@ -99,6 +99,8 @@ static inline struct vfsmount *mntget(st | |
149 | } | |
150 | ||
151 | extern int mnt_want_write(struct vfsmount *mnt); | |
152 | +extern int mnt_want_write_file(struct vfsmount *mnt, struct file *file); | |
153 | +extern void mnt_clone_write(struct vfsmount *mnt); | |
154 | extern void mnt_drop_write(struct vfsmount *mnt); | |
155 | extern void mntput_no_expire(struct vfsmount *mnt); | |
156 | extern void mnt_pin(struct vfsmount *mnt); |