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 | } |