diff options
| -rw-r--r-- | Documentation/filesystems/porting | 8 | ||||
| -rw-r--r-- | Documentation/filesystems/vfs.txt | 4 | ||||
| -rw-r--r-- | fs/namei.c | 28 | ||||
| -rw-r--r-- | fs/nfs/dir.c | 6 | ||||
| -rw-r--r-- | include/linux/list_bl.h | 5 | ||||
| -rw-r--r-- | include/linux/rculist_bl.h | 3 |
6 files changed, 40 insertions, 14 deletions
diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index 266d2059b9b8..dfbcd1b00b0a 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting | |||
| @@ -365,8 +365,8 @@ must be done in the RCU callback. | |||
| 365 | [recommended] | 365 | [recommended] |
| 366 | vfs now tries to do path walking in "rcu-walk mode", which avoids | 366 | vfs now tries to do path walking in "rcu-walk mode", which avoids |
| 367 | atomic operations and scalability hazards on dentries and inodes (see | 367 | atomic operations and scalability hazards on dentries and inodes (see |
| 368 | Documentation/filesystems/path-walk.txt). d_hash and d_compare changes (above) | 368 | Documentation/filesystems/path-lookup.txt). d_hash and d_compare changes |
| 369 | are examples of the changes required to support this. For more complex | 369 | (above) are examples of the changes required to support this. For more complex |
| 370 | filesystem callbacks, the vfs drops out of rcu-walk mode before the fs call, so | 370 | filesystem callbacks, the vfs drops out of rcu-walk mode before the fs call, so |
| 371 | no changes are required to the filesystem. However, this is costly and loses | 371 | no changes are required to the filesystem. However, this is costly and loses |
| 372 | the benefits of rcu-walk mode. We will begin to add filesystem callbacks that | 372 | the benefits of rcu-walk mode. We will begin to add filesystem callbacks that |
| @@ -383,8 +383,8 @@ Documentation/filesystems/vfs.txt for more details. | |||
| 383 | 383 | ||
| 384 | permission and check_acl are inode permission checks that are called | 384 | permission and check_acl are inode permission checks that are called |
| 385 | on many or all directory inodes on the way down a path walk (to check for | 385 | on many or all directory inodes on the way down a path walk (to check for |
| 386 | exec permission). These must now be rcu-walk aware (flags & IPERM_RCU). See | 386 | exec permission). These must now be rcu-walk aware (flags & IPERM_FLAG_RCU). |
| 387 | Documentation/filesystems/vfs.txt for more details. | 387 | See Documentation/filesystems/vfs.txt for more details. |
| 388 | 388 | ||
| 389 | -- | 389 | -- |
| 390 | [mandatory] | 390 | [mandatory] |
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index fbb324e2bd43..cae6d27c9f5b 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt | |||
| @@ -415,8 +415,8 @@ otherwise noted. | |||
| 415 | permission: called by the VFS to check for access rights on a POSIX-like | 415 | permission: called by the VFS to check for access rights on a POSIX-like |
| 416 | filesystem. | 416 | filesystem. |
| 417 | 417 | ||
| 418 | May be called in rcu-walk mode (flags & IPERM_RCU). If in rcu-walk | 418 | May be called in rcu-walk mode (flags & IPERM_FLAG_RCU). If in rcu-walk |
| 419 | mode, the filesystem must check the permission without blocking or | 419 | mode, the filesystem must check the permission without blocking or |
| 420 | storing to the inode. | 420 | storing to the inode. |
| 421 | 421 | ||
| 422 | If a situation is encountered that rcu-walk cannot handle, return | 422 | If a situation is encountered that rcu-walk cannot handle, return |
diff --git a/fs/namei.c b/fs/namei.c index 0b14f6910fc6..86643302079e 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
| @@ -479,6 +479,14 @@ static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry | |||
| 479 | struct fs_struct *fs = current->fs; | 479 | struct fs_struct *fs = current->fs; |
| 480 | struct dentry *parent = nd->path.dentry; | 480 | struct dentry *parent = nd->path.dentry; |
| 481 | 481 | ||
| 482 | /* | ||
| 483 | * It can be possible to revalidate the dentry that we started | ||
| 484 | * the path walk with. force_reval_path may also revalidate the | ||
| 485 | * dentry already committed to the nameidata. | ||
| 486 | */ | ||
| 487 | if (unlikely(parent == dentry)) | ||
| 488 | return nameidata_drop_rcu(nd); | ||
| 489 | |||
| 482 | BUG_ON(!(nd->flags & LOOKUP_RCU)); | 490 | BUG_ON(!(nd->flags & LOOKUP_RCU)); |
| 483 | if (nd->root.mnt) { | 491 | if (nd->root.mnt) { |
| 484 | spin_lock(&fs->lock); | 492 | spin_lock(&fs->lock); |
| @@ -583,6 +591,13 @@ void release_open_intent(struct nameidata *nd) | |||
| 583 | fput(nd->intent.open.file); | 591 | fput(nd->intent.open.file); |
| 584 | } | 592 | } |
| 585 | 593 | ||
| 594 | /* | ||
| 595 | * Call d_revalidate and handle filesystems that request rcu-walk | ||
| 596 | * to be dropped. This may be called and return in rcu-walk mode, | ||
| 597 | * regardless of success or error. If -ECHILD is returned, the caller | ||
| 598 | * must return -ECHILD back up the path walk stack so path walk may | ||
| 599 | * be restarted in ref-walk mode. | ||
| 600 | */ | ||
| 586 | static int d_revalidate(struct dentry *dentry, struct nameidata *nd) | 601 | static int d_revalidate(struct dentry *dentry, struct nameidata *nd) |
| 587 | { | 602 | { |
| 588 | int status; | 603 | int status; |
| @@ -673,6 +688,9 @@ force_reval_path(struct path *path, struct nameidata *nd) | |||
| 673 | return 0; | 688 | return 0; |
| 674 | 689 | ||
| 675 | if (!status) { | 690 | if (!status) { |
| 691 | /* Don't d_invalidate in rcu-walk mode */ | ||
| 692 | if (nameidata_drop_rcu(nd)) | ||
| 693 | return -ECHILD; | ||
| 676 | d_invalidate(dentry); | 694 | d_invalidate(dentry); |
| 677 | status = -ESTALE; | 695 | status = -ESTALE; |
| 678 | } | 696 | } |
| @@ -2105,11 +2123,13 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
| 2105 | dir = nd->path.dentry; | 2123 | dir = nd->path.dentry; |
| 2106 | case LAST_DOT: | 2124 | case LAST_DOT: |
| 2107 | if (need_reval_dot(dir)) { | 2125 | if (need_reval_dot(dir)) { |
| 2108 | error = d_revalidate(nd->path.dentry, nd); | 2126 | int status = d_revalidate(nd->path.dentry, nd); |
| 2109 | if (!error) | 2127 | if (!status) |
| 2110 | error = -ESTALE; | 2128 | status = -ESTALE; |
| 2111 | if (error < 0) | 2129 | if (status < 0) { |
| 2130 | error = status; | ||
| 2112 | goto exit; | 2131 | goto exit; |
| 2132 | } | ||
| 2113 | } | 2133 | } |
| 2114 | /* fallthrough */ | 2134 | /* fallthrough */ |
| 2115 | case LAST_ROOT: | 2135 | case LAST_ROOT: |
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 64ee240f3c80..df8c03a02161 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
| @@ -1406,11 +1406,15 @@ no_open: | |||
| 1406 | static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) | 1406 | static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) |
| 1407 | { | 1407 | { |
| 1408 | struct dentry *parent = NULL; | 1408 | struct dentry *parent = NULL; |
| 1409 | struct inode *inode = dentry->d_inode; | 1409 | struct inode *inode; |
| 1410 | struct inode *dir; | 1410 | struct inode *dir; |
| 1411 | struct nfs_open_context *ctx; | 1411 | struct nfs_open_context *ctx; |
| 1412 | int openflags, ret = 0; | 1412 | int openflags, ret = 0; |
| 1413 | 1413 | ||
| 1414 | if (nd->flags & LOOKUP_RCU) | ||
| 1415 | return -ECHILD; | ||
| 1416 | |||
| 1417 | inode = dentry->d_inode; | ||
| 1414 | if (!is_atomic_open(nd) || d_mountpoint(dentry)) | 1418 | if (!is_atomic_open(nd) || d_mountpoint(dentry)) |
| 1415 | goto no_open; | 1419 | goto no_open; |
| 1416 | 1420 | ||
diff --git a/include/linux/list_bl.h b/include/linux/list_bl.h index 9ee97e7f2be4..b2adbb4b2f73 100644 --- a/include/linux/list_bl.h +++ b/include/linux/list_bl.h | |||
| @@ -16,7 +16,7 @@ | |||
| 16 | * some fast and compact auxiliary data. | 16 | * some fast and compact auxiliary data. |
| 17 | */ | 17 | */ |
| 18 | 18 | ||
| 19 | #if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) | 19 | #if defined(CONFIG_SMP) |
| 20 | #define LIST_BL_LOCKMASK 1UL | 20 | #define LIST_BL_LOCKMASK 1UL |
| 21 | #else | 21 | #else |
| 22 | #define LIST_BL_LOCKMASK 0UL | 22 | #define LIST_BL_LOCKMASK 0UL |
| @@ -62,7 +62,8 @@ static inline void hlist_bl_set_first(struct hlist_bl_head *h, | |||
| 62 | struct hlist_bl_node *n) | 62 | struct hlist_bl_node *n) |
| 63 | { | 63 | { |
| 64 | LIST_BL_BUG_ON((unsigned long)n & LIST_BL_LOCKMASK); | 64 | LIST_BL_BUG_ON((unsigned long)n & LIST_BL_LOCKMASK); |
| 65 | LIST_BL_BUG_ON(!((unsigned long)h->first & LIST_BL_LOCKMASK)); | 65 | LIST_BL_BUG_ON(((unsigned long)h->first & LIST_BL_LOCKMASK) != |
| 66 | LIST_BL_LOCKMASK); | ||
| 66 | h->first = (struct hlist_bl_node *)((unsigned long)n | LIST_BL_LOCKMASK); | 67 | h->first = (struct hlist_bl_node *)((unsigned long)n | LIST_BL_LOCKMASK); |
| 67 | } | 68 | } |
| 68 | 69 | ||
diff --git a/include/linux/rculist_bl.h b/include/linux/rculist_bl.h index b872b493724d..cf1244fbf3b6 100644 --- a/include/linux/rculist_bl.h +++ b/include/linux/rculist_bl.h | |||
| @@ -11,7 +11,8 @@ static inline void hlist_bl_set_first_rcu(struct hlist_bl_head *h, | |||
| 11 | struct hlist_bl_node *n) | 11 | struct hlist_bl_node *n) |
| 12 | { | 12 | { |
| 13 | LIST_BL_BUG_ON((unsigned long)n & LIST_BL_LOCKMASK); | 13 | LIST_BL_BUG_ON((unsigned long)n & LIST_BL_LOCKMASK); |
| 14 | LIST_BL_BUG_ON(!((unsigned long)h->first & LIST_BL_LOCKMASK)); | 14 | LIST_BL_BUG_ON(((unsigned long)h->first & LIST_BL_LOCKMASK) != |
| 15 | LIST_BL_LOCKMASK); | ||
| 15 | rcu_assign_pointer(h->first, | 16 | rcu_assign_pointer(h->first, |
| 16 | (struct hlist_bl_node *)((unsigned long)n | LIST_BL_LOCKMASK)); | 17 | (struct hlist_bl_node *)((unsigned long)n | LIST_BL_LOCKMASK)); |
| 17 | } | 18 | } |
