diff options
author | Tejun Heo <tj@kernel.org> | 2014-04-02 16:40:52 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-04-16 14:54:40 -0400 |
commit | 4afddd60a770560d370d6f85c5aef57c16bf7502 (patch) | |
tree | 43ef62593beb12321088ddae46375ffae49d402f /fs/kernfs | |
parent | a2a4dc494a7b7135f460e38e788c4a58f65e4ac3 (diff) |
kernfs: protect lazy kernfs_iattrs allocation with mutex
kernfs_iattrs is allocated lazily when operations which require it
take place; unfortunately, the lazy allocation and returning weren't
properly synchronized and when there are multiple concurrent
operations, it might end up returning kernfs_iattrs which hasn't
finished initialization yet or different copies to different callers.
Fix it by synchronizing with a mutex. This can be smarter with memory
barriers but let's go there if it actually turns out to be necessary.
Signed-off-by: Tejun Heo <tj@kernel.org>
Link: http://lkml.kernel.org/g/533ABA32.9080602@oracle.com
Reported-by: Sasha Levin <sasha.levin@oracle.com>
Cc: stable@vger.kernel.org # 3.14
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/kernfs')
-rw-r--r-- | fs/kernfs/inode.c | 14 |
1 files changed, 10 insertions, 4 deletions
diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index abb0f1f53d93..985217626e66 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c | |||
@@ -48,14 +48,18 @@ void __init kernfs_inode_init(void) | |||
48 | 48 | ||
49 | static struct kernfs_iattrs *kernfs_iattrs(struct kernfs_node *kn) | 49 | static struct kernfs_iattrs *kernfs_iattrs(struct kernfs_node *kn) |
50 | { | 50 | { |
51 | static DEFINE_MUTEX(iattr_mutex); | ||
52 | struct kernfs_iattrs *ret; | ||
51 | struct iattr *iattrs; | 53 | struct iattr *iattrs; |
52 | 54 | ||
55 | mutex_lock(&iattr_mutex); | ||
56 | |||
53 | if (kn->iattr) | 57 | if (kn->iattr) |
54 | return kn->iattr; | 58 | goto out_unlock; |
55 | 59 | ||
56 | kn->iattr = kzalloc(sizeof(struct kernfs_iattrs), GFP_KERNEL); | 60 | kn->iattr = kzalloc(sizeof(struct kernfs_iattrs), GFP_KERNEL); |
57 | if (!kn->iattr) | 61 | if (!kn->iattr) |
58 | return NULL; | 62 | goto out_unlock; |
59 | iattrs = &kn->iattr->ia_iattr; | 63 | iattrs = &kn->iattr->ia_iattr; |
60 | 64 | ||
61 | /* assign default attributes */ | 65 | /* assign default attributes */ |
@@ -65,8 +69,10 @@ static struct kernfs_iattrs *kernfs_iattrs(struct kernfs_node *kn) | |||
65 | iattrs->ia_atime = iattrs->ia_mtime = iattrs->ia_ctime = CURRENT_TIME; | 69 | iattrs->ia_atime = iattrs->ia_mtime = iattrs->ia_ctime = CURRENT_TIME; |
66 | 70 | ||
67 | simple_xattrs_init(&kn->iattr->xattrs); | 71 | simple_xattrs_init(&kn->iattr->xattrs); |
68 | 72 | out_unlock: | |
69 | return kn->iattr; | 73 | ret = kn->iattr; |
74 | mutex_unlock(&iattr_mutex); | ||
75 | return ret; | ||
70 | } | 76 | } |
71 | 77 | ||
72 | static int __kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr) | 78 | static int __kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr) |