diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2014-09-29 14:54:27 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2014-10-09 02:38:41 -0400 |
commit | 8d85b4845a668d9a72649005c5aa932657311bd4 (patch) | |
tree | 47d8ce0b4836404030837c8ad3abb28e047ad594 | |
parent | 6d13f69444bd3d4888e43f7756449748f5a98bad (diff) |
Allow sharing external names after __d_move()
* external dentry names get a small structure prepended to them
(struct external_name).
* it contains an atomic refcount, matching the number of struct dentry
instances that have ->d_name.name pointing to that external name. The
first thing free_dentry() does is decrementing refcount of external name,
so the instances that are between the call of free_dentry() and
RCU-delayed actual freeing do not contribute.
* __d_move(x, y, false) makes the name of x equal to the name of y,
external or not. If y has an external name, extra reference is grabbed
and put into x->d_name.name. If x used to have an external name, the
reference to the old name is dropped and, should it reach zero, freeing
is scheduled via kfree_rcu().
* free_dentry() in dentry with external name decrements the refcount of
that name and, should it reach zero, does RCU-delayed call that will
free both the dentry and external name. Otherwise it does what it
used to do, except that __d_free() doesn't even look at ->d_name.name;
it simply frees the dentry.
All non-RCU accesses to dentry external name are safe wrt freeing since they
all should happen before free_dentry() is called. RCU accesses might run
into a dentry seen by free_dentry() or into an old name that got already
dropped by __d_move(); however, in both cases dentry must have been
alive and refer to that name at some point after we'd done rcu_read_lock(),
which means that any freeing must be still pending.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/dcache.c | 75 |
1 files changed, 59 insertions, 16 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index e7484f9c73b4..4858d2e5cf2e 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -235,18 +235,44 @@ static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *c | |||
235 | return dentry_string_cmp(cs, ct, tcount); | 235 | return dentry_string_cmp(cs, ct, tcount); |
236 | } | 236 | } |
237 | 237 | ||
238 | struct external_name { | ||
239 | union { | ||
240 | atomic_t count; | ||
241 | struct rcu_head head; | ||
242 | } u; | ||
243 | unsigned char name[]; | ||
244 | }; | ||
245 | |||
246 | static inline struct external_name *external_name(struct dentry *dentry) | ||
247 | { | ||
248 | return container_of(dentry->d_name.name, struct external_name, name[0]); | ||
249 | } | ||
250 | |||
238 | static void __d_free(struct rcu_head *head) | 251 | static void __d_free(struct rcu_head *head) |
239 | { | 252 | { |
240 | struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu); | 253 | struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu); |
241 | 254 | ||
242 | WARN_ON(!hlist_unhashed(&dentry->d_alias)); | 255 | WARN_ON(!hlist_unhashed(&dentry->d_alias)); |
243 | if (dname_external(dentry)) | 256 | kmem_cache_free(dentry_cache, dentry); |
244 | kfree(dentry->d_name.name); | 257 | } |
258 | |||
259 | static void __d_free_external(struct rcu_head *head) | ||
260 | { | ||
261 | struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu); | ||
262 | WARN_ON(!hlist_unhashed(&dentry->d_alias)); | ||
263 | kfree(external_name(dentry)); | ||
245 | kmem_cache_free(dentry_cache, dentry); | 264 | kmem_cache_free(dentry_cache, dentry); |
246 | } | 265 | } |
247 | 266 | ||
248 | static void dentry_free(struct dentry *dentry) | 267 | static void dentry_free(struct dentry *dentry) |
249 | { | 268 | { |
269 | if (unlikely(dname_external(dentry))) { | ||
270 | struct external_name *p = external_name(dentry); | ||
271 | if (likely(atomic_dec_and_test(&p->u.count))) { | ||
272 | call_rcu(&dentry->d_u.d_rcu, __d_free_external); | ||
273 | return; | ||
274 | } | ||
275 | } | ||
250 | /* if dentry was never visible to RCU, immediate free is OK */ | 276 | /* if dentry was never visible to RCU, immediate free is OK */ |
251 | if (!(dentry->d_flags & DCACHE_RCUACCESS)) | 277 | if (!(dentry->d_flags & DCACHE_RCUACCESS)) |
252 | __d_free(&dentry->d_u.d_rcu); | 278 | __d_free(&dentry->d_u.d_rcu); |
@@ -1438,11 +1464,14 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name) | |||
1438 | */ | 1464 | */ |
1439 | dentry->d_iname[DNAME_INLINE_LEN-1] = 0; | 1465 | dentry->d_iname[DNAME_INLINE_LEN-1] = 0; |
1440 | if (name->len > DNAME_INLINE_LEN-1) { | 1466 | if (name->len > DNAME_INLINE_LEN-1) { |
1441 | dname = kmalloc(name->len + 1, GFP_KERNEL); | 1467 | size_t size = offsetof(struct external_name, name[1]); |
1442 | if (!dname) { | 1468 | struct external_name *p = kmalloc(size + name->len, GFP_KERNEL); |
1469 | if (!p) { | ||
1443 | kmem_cache_free(dentry_cache, dentry); | 1470 | kmem_cache_free(dentry_cache, dentry); |
1444 | return NULL; | 1471 | return NULL; |
1445 | } | 1472 | } |
1473 | atomic_set(&p->u.count, 1); | ||
1474 | dname = p->name; | ||
1446 | } else { | 1475 | } else { |
1447 | dname = dentry->d_iname; | 1476 | dname = dentry->d_iname; |
1448 | } | 1477 | } |
@@ -2372,11 +2401,10 @@ void dentry_update_name_case(struct dentry *dentry, struct qstr *name) | |||
2372 | } | 2401 | } |
2373 | EXPORT_SYMBOL(dentry_update_name_case); | 2402 | EXPORT_SYMBOL(dentry_update_name_case); |
2374 | 2403 | ||
2375 | static void switch_names(struct dentry *dentry, struct dentry *target, | 2404 | static void swap_names(struct dentry *dentry, struct dentry *target) |
2376 | bool exchange) | ||
2377 | { | 2405 | { |
2378 | if (dname_external(target)) { | 2406 | if (unlikely(dname_external(target))) { |
2379 | if (dname_external(dentry)) { | 2407 | if (unlikely(dname_external(dentry))) { |
2380 | /* | 2408 | /* |
2381 | * Both external: swap the pointers | 2409 | * Both external: swap the pointers |
2382 | */ | 2410 | */ |
@@ -2392,7 +2420,7 @@ static void switch_names(struct dentry *dentry, struct dentry *target, | |||
2392 | target->d_name.name = target->d_iname; | 2420 | target->d_name.name = target->d_iname; |
2393 | } | 2421 | } |
2394 | } else { | 2422 | } else { |
2395 | if (dname_external(dentry)) { | 2423 | if (unlikely(dname_external(dentry))) { |
2396 | /* | 2424 | /* |
2397 | * dentry:external, target:internal. Give dentry's | 2425 | * dentry:external, target:internal. Give dentry's |
2398 | * storage to target and make dentry internal | 2426 | * storage to target and make dentry internal |
@@ -2407,12 +2435,6 @@ static void switch_names(struct dentry *dentry, struct dentry *target, | |||
2407 | */ | 2435 | */ |
2408 | unsigned int i; | 2436 | unsigned int i; |
2409 | BUILD_BUG_ON(!IS_ALIGNED(DNAME_INLINE_LEN, sizeof(long))); | 2437 | BUILD_BUG_ON(!IS_ALIGNED(DNAME_INLINE_LEN, sizeof(long))); |
2410 | if (!exchange) { | ||
2411 | memcpy(dentry->d_iname, target->d_name.name, | ||
2412 | target->d_name.len + 1); | ||
2413 | dentry->d_name.hash_len = target->d_name.hash_len; | ||
2414 | return; | ||
2415 | } | ||
2416 | for (i = 0; i < DNAME_INLINE_LEN / sizeof(long); i++) { | 2438 | for (i = 0; i < DNAME_INLINE_LEN / sizeof(long); i++) { |
2417 | swap(((long *) &dentry->d_iname)[i], | 2439 | swap(((long *) &dentry->d_iname)[i], |
2418 | ((long *) &target->d_iname)[i]); | 2440 | ((long *) &target->d_iname)[i]); |
@@ -2422,6 +2444,24 @@ static void switch_names(struct dentry *dentry, struct dentry *target, | |||
2422 | swap(dentry->d_name.hash_len, target->d_name.hash_len); | 2444 | swap(dentry->d_name.hash_len, target->d_name.hash_len); |
2423 | } | 2445 | } |
2424 | 2446 | ||
2447 | static void copy_name(struct dentry *dentry, struct dentry *target) | ||
2448 | { | ||
2449 | struct external_name *old_name = NULL; | ||
2450 | if (unlikely(dname_external(dentry))) | ||
2451 | old_name = external_name(dentry); | ||
2452 | if (unlikely(dname_external(target))) { | ||
2453 | atomic_inc(&external_name(target)->u.count); | ||
2454 | dentry->d_name = target->d_name; | ||
2455 | } else { | ||
2456 | memcpy(dentry->d_iname, target->d_name.name, | ||
2457 | target->d_name.len + 1); | ||
2458 | dentry->d_name.name = dentry->d_iname; | ||
2459 | dentry->d_name.hash_len = target->d_name.hash_len; | ||
2460 | } | ||
2461 | if (old_name && likely(atomic_dec_and_test(&old_name->u.count))) | ||
2462 | kfree_rcu(old_name, u.head); | ||
2463 | } | ||
2464 | |||
2425 | static void dentry_lock_for_move(struct dentry *dentry, struct dentry *target) | 2465 | static void dentry_lock_for_move(struct dentry *dentry, struct dentry *target) |
2426 | { | 2466 | { |
2427 | /* | 2467 | /* |
@@ -2518,7 +2558,10 @@ static void __d_move(struct dentry *dentry, struct dentry *target, | |||
2518 | } | 2558 | } |
2519 | 2559 | ||
2520 | /* Switch the names.. */ | 2560 | /* Switch the names.. */ |
2521 | switch_names(dentry, target, exchange); | 2561 | if (exchange) |
2562 | swap_names(dentry, target); | ||
2563 | else | ||
2564 | copy_name(dentry, target); | ||
2522 | 2565 | ||
2523 | /* ... and switch them in the tree */ | 2566 | /* ... and switch them in the tree */ |
2524 | if (IS_ROOT(dentry)) { | 2567 | if (IS_ROOT(dentry)) { |