diff options
author | Jeff Layton <jlayton@redhat.com> | 2012-10-10 16:43:13 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2012-10-12 20:15:10 -0400 |
commit | 7950e3852ab86826b7349a535d2e8b0000340d7f (patch) | |
tree | b07bcf28bf0fc3bdc610595287250c3c3a3d974a /fs/namei.c | |
parent | adb5c2473d3f91526c79db972aafb20a56d3fbb3 (diff) |
vfs: embed struct filename inside of names_cache allocation if possible
In the common case where a name is much smaller than PATH_MAX, an extra
allocation for struct filename is unnecessary. Before allocating a
separate one, try to embed the struct filename inside the buffer first. If
it turns out that that's not long enough, then fall back to allocating a
separate struct filename and redoing the copy.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 69 |
1 files changed, 49 insertions, 20 deletions
diff --git a/fs/namei.c b/fs/namei.c index 80b162b142f9..d1895f308156 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -119,40 +119,69 @@ | |||
119 | */ | 119 | */ |
120 | void final_putname(struct filename *name) | 120 | void final_putname(struct filename *name) |
121 | { | 121 | { |
122 | __putname(name->name); | 122 | if (name->separate) { |
123 | kfree(name); | 123 | __putname(name->name); |
124 | kfree(name); | ||
125 | } else { | ||
126 | __putname(name); | ||
127 | } | ||
124 | } | 128 | } |
125 | 129 | ||
130 | #define EMBEDDED_NAME_MAX (PATH_MAX - sizeof(struct filename)) | ||
131 | |||
126 | static struct filename * | 132 | static struct filename * |
127 | getname_flags(const char __user *filename, int flags, int *empty) | 133 | getname_flags(const char __user *filename, int flags, int *empty) |
128 | { | 134 | { |
129 | struct filename *result, *err; | 135 | struct filename *result, *err; |
130 | char *kname; | ||
131 | int len; | 136 | int len; |
137 | long max; | ||
138 | char *kname; | ||
132 | 139 | ||
133 | result = audit_reusename(filename); | 140 | result = audit_reusename(filename); |
134 | if (result) | 141 | if (result) |
135 | return result; | 142 | return result; |
136 | 143 | ||
137 | /* FIXME: create dedicated slabcache? */ | 144 | result = __getname(); |
138 | result = kzalloc(sizeof(*result), GFP_KERNEL); | ||
139 | if (unlikely(!result)) | 145 | if (unlikely(!result)) |
140 | return ERR_PTR(-ENOMEM); | 146 | return ERR_PTR(-ENOMEM); |
141 | 147 | ||
142 | kname = __getname(); | 148 | /* |
143 | if (unlikely(!kname)) { | 149 | * First, try to embed the struct filename inside the names_cache |
144 | err = ERR_PTR(-ENOMEM); | 150 | * allocation |
145 | goto error_free_name; | 151 | */ |
146 | } | 152 | kname = (char *)result + sizeof(*result); |
147 | |||
148 | result->name = kname; | 153 | result->name = kname; |
149 | result->uptr = filename; | 154 | result->separate = false; |
150 | len = strncpy_from_user(kname, filename, PATH_MAX); | 155 | max = EMBEDDED_NAME_MAX; |
156 | |||
157 | recopy: | ||
158 | len = strncpy_from_user(kname, filename, max); | ||
151 | if (unlikely(len < 0)) { | 159 | if (unlikely(len < 0)) { |
152 | err = ERR_PTR(len); | 160 | err = ERR_PTR(len); |
153 | goto error; | 161 | goto error; |
154 | } | 162 | } |
155 | 163 | ||
164 | /* | ||
165 | * Uh-oh. We have a name that's approaching PATH_MAX. Allocate a | ||
166 | * separate struct filename so we can dedicate the entire | ||
167 | * names_cache allocation for the pathname, and re-do the copy from | ||
168 | * userland. | ||
169 | */ | ||
170 | if (len == EMBEDDED_NAME_MAX && max == EMBEDDED_NAME_MAX) { | ||
171 | kname = (char *)result; | ||
172 | |||
173 | result = kzalloc(sizeof(*result), GFP_KERNEL); | ||
174 | if (!result) { | ||
175 | err = ERR_PTR(-ENOMEM); | ||
176 | result = (struct filename *)kname; | ||
177 | goto error; | ||
178 | } | ||
179 | result->name = kname; | ||
180 | result->separate = true; | ||
181 | max = PATH_MAX; | ||
182 | goto recopy; | ||
183 | } | ||
184 | |||
156 | /* The empty path is special. */ | 185 | /* The empty path is special. */ |
157 | if (unlikely(!len)) { | 186 | if (unlikely(!len)) { |
158 | if (empty) | 187 | if (empty) |
@@ -163,15 +192,15 @@ getname_flags(const char __user *filename, int flags, int *empty) | |||
163 | } | 192 | } |
164 | 193 | ||
165 | err = ERR_PTR(-ENAMETOOLONG); | 194 | err = ERR_PTR(-ENAMETOOLONG); |
166 | if (likely(len < PATH_MAX)) { | 195 | if (unlikely(len >= PATH_MAX)) |
167 | audit_getname(result); | 196 | goto error; |
168 | return result; | 197 | |
169 | } | 198 | result->uptr = filename; |
199 | audit_getname(result); | ||
200 | return result; | ||
170 | 201 | ||
171 | error: | 202 | error: |
172 | __putname(kname); | 203 | final_putname(result); |
173 | error_free_name: | ||
174 | kfree(result); | ||
175 | return err; | 204 | return err; |
176 | } | 205 | } |
177 | 206 | ||