diff options
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 151 |
1 files changed, 46 insertions, 105 deletions
diff --git a/fs/namei.c b/fs/namei.c index 54fc993e3027..1039cbae0c12 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -391,79 +391,28 @@ void path_put(struct path *path) | |||
391 | } | 391 | } |
392 | EXPORT_SYMBOL(path_put); | 392 | EXPORT_SYMBOL(path_put); |
393 | 393 | ||
394 | /** | 394 | /* |
395 | * nameidata_drop_rcu - drop this nameidata out of rcu-walk | ||
396 | * @nd: nameidata pathwalk data to drop | ||
397 | * Returns: 0 on success, -ECHILD on failure | ||
398 | * | ||
399 | * Path walking has 2 modes, rcu-walk and ref-walk (see | 395 | * Path walking has 2 modes, rcu-walk and ref-walk (see |
400 | * Documentation/filesystems/path-lookup.txt). __drop_rcu* functions attempt | 396 | * Documentation/filesystems/path-lookup.txt). In situations when we can't |
401 | * to drop out of rcu-walk mode and take normal reference counts on dentries | 397 | * continue in RCU mode, we attempt to drop out of rcu-walk mode and grab |
402 | * and vfsmounts to transition to rcu-walk mode. __drop_rcu* functions take | 398 | * normal reference counts on dentries and vfsmounts to transition to rcu-walk |
403 | * refcounts at the last known good point before rcu-walk got stuck, so | 399 | * mode. Refcounts are grabbed at the last known good point before rcu-walk |
404 | * ref-walk may continue from there. If this is not successful (eg. a seqcount | 400 | * got stuck, so ref-walk may continue from there. If this is not successful |
405 | * has changed), then failure is returned and path walk restarts from the | 401 | * (eg. a seqcount has changed), then failure is returned and it's up to caller |
406 | * beginning in ref-walk mode. | 402 | * to restart the path walk from the beginning in ref-walk mode. |
407 | * | ||
408 | * nameidata_drop_rcu attempts to drop the current nd->path and nd->root into | ||
409 | * ref-walk. Must be called from rcu-walk context. | ||
410 | */ | 403 | */ |
411 | static int nameidata_drop_rcu(struct nameidata *nd) | ||
412 | { | ||
413 | struct fs_struct *fs = current->fs; | ||
414 | struct dentry *dentry = nd->path.dentry; | ||
415 | int want_root = 0; | ||
416 | |||
417 | BUG_ON(!(nd->flags & LOOKUP_RCU)); | ||
418 | if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) { | ||
419 | want_root = 1; | ||
420 | spin_lock(&fs->lock); | ||
421 | if (nd->root.mnt != fs->root.mnt || | ||
422 | nd->root.dentry != fs->root.dentry) | ||
423 | goto err_root; | ||
424 | } | ||
425 | spin_lock(&dentry->d_lock); | ||
426 | if (!__d_rcu_to_refcount(dentry, nd->seq)) | ||
427 | goto err; | ||
428 | BUG_ON(nd->inode != dentry->d_inode); | ||
429 | spin_unlock(&dentry->d_lock); | ||
430 | if (want_root) { | ||
431 | path_get(&nd->root); | ||
432 | spin_unlock(&fs->lock); | ||
433 | } | ||
434 | mntget(nd->path.mnt); | ||
435 | |||
436 | rcu_read_unlock(); | ||
437 | br_read_unlock(vfsmount_lock); | ||
438 | nd->flags &= ~LOOKUP_RCU; | ||
439 | return 0; | ||
440 | err: | ||
441 | spin_unlock(&dentry->d_lock); | ||
442 | err_root: | ||
443 | if (want_root) | ||
444 | spin_unlock(&fs->lock); | ||
445 | return -ECHILD; | ||
446 | } | ||
447 | |||
448 | /* Try to drop out of rcu-walk mode if we were in it, otherwise do nothing. */ | ||
449 | static inline int nameidata_drop_rcu_maybe(struct nameidata *nd) | ||
450 | { | ||
451 | if (nd->flags & LOOKUP_RCU) | ||
452 | return nameidata_drop_rcu(nd); | ||
453 | return 0; | ||
454 | } | ||
455 | 404 | ||
456 | /** | 405 | /** |
457 | * nameidata_dentry_drop_rcu - drop nameidata and dentry out of rcu-walk | 406 | * unlazy_walk - try to switch to ref-walk mode. |
458 | * @nd: nameidata pathwalk data to drop | 407 | * @nd: nameidata pathwalk data |
459 | * @dentry: dentry to drop | 408 | * @dentry: child of nd->path.dentry or NULL |
460 | * Returns: 0 on success, -ECHILD on failure | 409 | * Returns: 0 on success, -ECHILD on failure |
461 | * | 410 | * |
462 | * nameidata_dentry_drop_rcu attempts to drop the current nd->path and nd->root, | 411 | * unlazy_walk attempts to legitimize the current nd->path, nd->root and dentry |
463 | * and dentry into ref-walk. @dentry must be a path found by a do_lookup call on | 412 | * for ref-walk mode. @dentry must be a path found by a do_lookup call on |
464 | * @nd. Must be called from rcu-walk context. | 413 | * @nd or NULL. Must be called from rcu-walk context. |
465 | */ | 414 | */ |
466 | static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry) | 415 | static int unlazy_walk(struct nameidata *nd, struct dentry *dentry) |
467 | { | 416 | { |
468 | struct fs_struct *fs = current->fs; | 417 | struct fs_struct *fs = current->fs; |
469 | struct dentry *parent = nd->path.dentry; | 418 | struct dentry *parent = nd->path.dentry; |
@@ -478,18 +427,25 @@ static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry | |||
478 | goto err_root; | 427 | goto err_root; |
479 | } | 428 | } |
480 | spin_lock(&parent->d_lock); | 429 | spin_lock(&parent->d_lock); |
481 | spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); | 430 | if (!dentry) { |
482 | if (!__d_rcu_to_refcount(dentry, nd->seq)) | 431 | if (!__d_rcu_to_refcount(parent, nd->seq)) |
483 | goto err; | 432 | goto err_parent; |
484 | /* | 433 | BUG_ON(nd->inode != parent->d_inode); |
485 | * If the sequence check on the child dentry passed, then the child has | 434 | } else { |
486 | * not been removed from its parent. This means the parent dentry must | 435 | spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); |
487 | * be valid and able to take a reference at this point. | 436 | if (!__d_rcu_to_refcount(dentry, nd->seq)) |
488 | */ | 437 | goto err_child; |
489 | BUG_ON(!IS_ROOT(dentry) && dentry->d_parent != parent); | 438 | /* |
490 | BUG_ON(!parent->d_count); | 439 | * If the sequence check on the child dentry passed, then |
491 | parent->d_count++; | 440 | * the child has not been removed from its parent. This |
492 | spin_unlock(&dentry->d_lock); | 441 | * means the parent dentry must be valid and able to take |
442 | * a reference at this point. | ||
443 | */ | ||
444 | BUG_ON(!IS_ROOT(dentry) && dentry->d_parent != parent); | ||
445 | BUG_ON(!parent->d_count); | ||
446 | parent->d_count++; | ||
447 | spin_unlock(&dentry->d_lock); | ||
448 | } | ||
493 | spin_unlock(&parent->d_lock); | 449 | spin_unlock(&parent->d_lock); |
494 | if (want_root) { | 450 | if (want_root) { |
495 | path_get(&nd->root); | 451 | path_get(&nd->root); |
@@ -501,8 +457,10 @@ static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry | |||
501 | br_read_unlock(vfsmount_lock); | 457 | br_read_unlock(vfsmount_lock); |
502 | nd->flags &= ~LOOKUP_RCU; | 458 | nd->flags &= ~LOOKUP_RCU; |
503 | return 0; | 459 | return 0; |
504 | err: | 460 | |
461 | err_child: | ||
505 | spin_unlock(&dentry->d_lock); | 462 | spin_unlock(&dentry->d_lock); |
463 | err_parent: | ||
506 | spin_unlock(&parent->d_lock); | 464 | spin_unlock(&parent->d_lock); |
507 | err_root: | 465 | err_root: |
508 | if (want_root) | 466 | if (want_root) |
@@ -510,22 +468,6 @@ err_root: | |||
510 | return -ECHILD; | 468 | return -ECHILD; |
511 | } | 469 | } |
512 | 470 | ||
513 | /* Try to drop out of rcu-walk mode if we were in it, otherwise do nothing. */ | ||
514 | static inline int nameidata_dentry_drop_rcu_maybe(struct nameidata *nd, struct dentry *dentry) | ||
515 | { | ||
516 | if (nd->flags & LOOKUP_RCU) { | ||
517 | if (unlikely(nameidata_dentry_drop_rcu(nd, dentry))) { | ||
518 | nd->flags &= ~LOOKUP_RCU; | ||
519 | if (!(nd->flags & LOOKUP_ROOT)) | ||
520 | nd->root.mnt = NULL; | ||
521 | rcu_read_unlock(); | ||
522 | br_read_unlock(vfsmount_lock); | ||
523 | return -ECHILD; | ||
524 | } | ||
525 | } | ||
526 | return 0; | ||
527 | } | ||
528 | |||
529 | /** | 471 | /** |
530 | * nameidata_drop_rcu_last - drop nameidata ending path walk out of rcu-walk | 472 | * nameidata_drop_rcu_last - drop nameidata ending path walk out of rcu-walk |
531 | * @nd: nameidata pathwalk data to drop | 473 | * @nd: nameidata pathwalk data to drop |
@@ -1241,13 +1183,8 @@ static int do_lookup(struct nameidata *nd, struct qstr *name, | |||
1241 | if (likely(__follow_mount_rcu(nd, path, inode, false))) | 1183 | if (likely(__follow_mount_rcu(nd, path, inode, false))) |
1242 | return 0; | 1184 | return 0; |
1243 | unlazy: | 1185 | unlazy: |
1244 | if (dentry) { | 1186 | if (unlazy_walk(nd, dentry)) |
1245 | if (nameidata_dentry_drop_rcu(nd, dentry)) | 1187 | return -ECHILD; |
1246 | return -ECHILD; | ||
1247 | } else { | ||
1248 | if (nameidata_drop_rcu(nd)) | ||
1249 | return -ECHILD; | ||
1250 | } | ||
1251 | } else { | 1188 | } else { |
1252 | dentry = __d_lookup(parent, name); | 1189 | dentry = __d_lookup(parent, name); |
1253 | } | 1190 | } |
@@ -1303,7 +1240,7 @@ static inline int may_lookup(struct nameidata *nd) | |||
1303 | int err = exec_permission(nd->inode, IPERM_FLAG_RCU); | 1240 | int err = exec_permission(nd->inode, IPERM_FLAG_RCU); |
1304 | if (err != -ECHILD) | 1241 | if (err != -ECHILD) |
1305 | return err; | 1242 | return err; |
1306 | if (nameidata_drop_rcu(nd)) | 1243 | if (unlazy_walk(nd, NULL)) |
1307 | return -ECHILD; | 1244 | return -ECHILD; |
1308 | } | 1245 | } |
1309 | return exec_permission(nd->inode, 0); | 1246 | return exec_permission(nd->inode, 0); |
@@ -1357,8 +1294,12 @@ static inline int walk_component(struct nameidata *nd, struct path *path, | |||
1357 | return -ENOENT; | 1294 | return -ENOENT; |
1358 | } | 1295 | } |
1359 | if (unlikely(inode->i_op->follow_link) && follow) { | 1296 | if (unlikely(inode->i_op->follow_link) && follow) { |
1360 | if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry)) | 1297 | if (nd->flags & LOOKUP_RCU) { |
1361 | return -ECHILD; | 1298 | if (unlikely(unlazy_walk(nd, path->dentry))) { |
1299 | terminate_walk(nd); | ||
1300 | return -ECHILD; | ||
1301 | } | ||
1302 | } | ||
1362 | BUG_ON(inode != path->dentry->d_inode); | 1303 | BUG_ON(inode != path->dentry->d_inode); |
1363 | return 1; | 1304 | return 1; |
1364 | } | 1305 | } |