diff options
author | Tejun Heo <tj@kernel.org> | 2013-11-28 14:54:29 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-11-29 20:48:15 -0500 |
commit | 517e64f57883bd63c5a4ab8b3d0d3ed68c55d0cf (patch) | |
tree | 573f1e42c269ab32985511788a612ed3d7a99347 | |
parent | 2b25a62901a1af654c2604f19592b13742ad1601 (diff) |
sysfs, kernfs: revamp sysfs_dirent active_ref lockdep annotation
Currently, sysfs_dirent active_ref lockdep annotation uses
attribute->[s]key as the lockdep key, which forces
kernfs_create_file_ns() to assume that sysfs_dirent->priv is pointing
to a struct attribute which may not be true for non-sysfs users. This
patch restructures the lockdep annotation such that
* kernfs_ops contains lockdep_key which is used by default for files
created kernfs_create_file_ns().
* kernfs_create_file_ns_key() is introduced which takes an extra @key
argument. The created file will use the specified key for
active_ref lockdep annotation. If NULL is specified, lockdep for
the file is disabled.
* sysfs_add_file_mode_ns() is updated to use
kernfs_create_file_ns_key() with the appropriate key from the
attribute or NULL if ignore_lockdep is set.
This makes the lockdep annotation properly contained in kernfs while
allowing sysfs to cleanly keep its current behavior. This patch
doesn't introduce any behavior differences.
Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | fs/sysfs/dir.c | 4 | ||||
-rw-r--r-- | fs/sysfs/file.c | 35 | ||||
-rw-r--r-- | fs/sysfs/sysfs.h | 32 | ||||
-rw-r--r-- | include/linux/kernfs.h | 37 |
4 files changed, 56 insertions, 52 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index e88e9a94a083..8f2d577b5f64 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
@@ -150,7 +150,7 @@ struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) | |||
150 | if (!atomic_inc_unless_negative(&sd->s_active)) | 150 | if (!atomic_inc_unless_negative(&sd->s_active)) |
151 | return NULL; | 151 | return NULL; |
152 | 152 | ||
153 | if (likely(!sysfs_ignore_lockdep(sd))) | 153 | if (sd->s_flags & SYSFS_FLAG_LOCKDEP) |
154 | rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_); | 154 | rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_); |
155 | return sd; | 155 | return sd; |
156 | } | 156 | } |
@@ -169,7 +169,7 @@ void sysfs_put_active(struct sysfs_dirent *sd) | |||
169 | if (unlikely(!sd)) | 169 | if (unlikely(!sd)) |
170 | return; | 170 | return; |
171 | 171 | ||
172 | if (likely(!sysfs_ignore_lockdep(sd))) | 172 | if (sd->s_flags & SYSFS_FLAG_LOCKDEP) |
173 | rwsem_release(&sd->dep_map, 1, _RET_IP_); | 173 | rwsem_release(&sd->dep_map, 1, _RET_IP_); |
174 | v = atomic_dec_return(&sd->s_active); | 174 | v = atomic_dec_return(&sd->s_active); |
175 | if (likely(v != SD_DEACTIVATED_BIAS)) | 175 | if (likely(v != SD_DEACTIVATED_BIAS)) |
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index a68cbef3a674..e4eca285b390 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c | |||
@@ -58,7 +58,7 @@ static struct sysfs_open_file *sysfs_of(struct file *file) | |||
58 | */ | 58 | */ |
59 | static const struct kernfs_ops *kernfs_ops(struct sysfs_dirent *sd) | 59 | static const struct kernfs_ops *kernfs_ops(struct sysfs_dirent *sd) |
60 | { | 60 | { |
61 | if (!sysfs_ignore_lockdep(sd)) | 61 | if (sd->s_flags & SYSFS_FLAG_LOCKDEP) |
62 | lockdep_assert_held(sd); | 62 | lockdep_assert_held(sd); |
63 | return sd->s_attr.ops; | 63 | return sd->s_attr.ops; |
64 | } | 64 | } |
@@ -71,7 +71,7 @@ static const struct sysfs_ops *sysfs_file_ops(struct sysfs_dirent *sd) | |||
71 | { | 71 | { |
72 | struct kobject *kobj = sd->s_parent->priv; | 72 | struct kobject *kobj = sd->s_parent->priv; |
73 | 73 | ||
74 | if (!sysfs_ignore_lockdep(sd)) | 74 | if (sd->s_flags & SYSFS_FLAG_LOCKDEP) |
75 | lockdep_assert_held(sd); | 75 | lockdep_assert_held(sd); |
76 | return kobj->ktype ? kobj->ktype->sysfs_ops : NULL; | 76 | return kobj->ktype ? kobj->ktype->sysfs_ops : NULL; |
77 | } | 77 | } |
@@ -942,6 +942,7 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, | |||
942 | const struct attribute *attr, bool is_bin, | 942 | const struct attribute *attr, bool is_bin, |
943 | umode_t mode, const void *ns) | 943 | umode_t mode, const void *ns) |
944 | { | 944 | { |
945 | struct lock_class_key *key = NULL; | ||
945 | const struct kernfs_ops *ops; | 946 | const struct kernfs_ops *ops; |
946 | struct sysfs_dirent *sd; | 947 | struct sysfs_dirent *sd; |
947 | loff_t size; | 948 | loff_t size; |
@@ -981,8 +982,12 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, | |||
981 | size = battr->size; | 982 | size = battr->size; |
982 | } | 983 | } |
983 | 984 | ||
984 | sd = kernfs_create_file_ns(dir_sd, attr->name, mode, size, | 985 | #ifdef CONFIG_DEBUG_LOCK_ALLOC |
985 | ops, (void *)attr, ns); | 986 | if (!attr->ignore_lockdep) |
987 | key = attr->key ?: (struct lock_class_key *)&attr->skey; | ||
988 | #endif | ||
989 | sd = kernfs_create_file_ns_key(dir_sd, attr->name, mode, size, | ||
990 | ops, (void *)attr, ns, key); | ||
986 | if (IS_ERR(sd)) { | 991 | if (IS_ERR(sd)) { |
987 | if (PTR_ERR(sd) == -EEXIST) | 992 | if (PTR_ERR(sd) == -EEXIST) |
988 | sysfs_warn_dup(dir_sd, attr->name); | 993 | sysfs_warn_dup(dir_sd, attr->name); |
@@ -992,7 +997,7 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, | |||
992 | } | 997 | } |
993 | 998 | ||
994 | /** | 999 | /** |
995 | * kernfs_create_file_ns - create a file | 1000 | * kernfs_create_file_ns_key - create a file |
996 | * @parent: directory to create the file in | 1001 | * @parent: directory to create the file in |
997 | * @name: name of the file | 1002 | * @name: name of the file |
998 | * @mode: mode of the file | 1003 | * @mode: mode of the file |
@@ -1000,14 +1005,16 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, | |||
1000 | * @ops: kernfs operations for the file | 1005 | * @ops: kernfs operations for the file |
1001 | * @priv: private data for the file | 1006 | * @priv: private data for the file |
1002 | * @ns: optional namespace tag of the file | 1007 | * @ns: optional namespace tag of the file |
1008 | * @key: lockdep key for the file's active_ref, %NULL to disable lockdep | ||
1003 | * | 1009 | * |
1004 | * Returns the created node on success, ERR_PTR() value on error. | 1010 | * Returns the created node on success, ERR_PTR() value on error. |
1005 | */ | 1011 | */ |
1006 | struct sysfs_dirent *kernfs_create_file_ns(struct sysfs_dirent *parent, | 1012 | struct sysfs_dirent *kernfs_create_file_ns_key(struct sysfs_dirent *parent, |
1007 | const char *name, | 1013 | const char *name, |
1008 | umode_t mode, loff_t size, | 1014 | umode_t mode, loff_t size, |
1009 | const struct kernfs_ops *ops, | 1015 | const struct kernfs_ops *ops, |
1010 | void *priv, const void *ns) | 1016 | void *priv, const void *ns, |
1017 | struct lock_class_key *key) | ||
1011 | { | 1018 | { |
1012 | struct sysfs_addrm_cxt acxt; | 1019 | struct sysfs_addrm_cxt acxt; |
1013 | struct sysfs_dirent *sd; | 1020 | struct sysfs_dirent *sd; |
@@ -1022,7 +1029,13 @@ struct sysfs_dirent *kernfs_create_file_ns(struct sysfs_dirent *parent, | |||
1022 | sd->s_attr.size = size; | 1029 | sd->s_attr.size = size; |
1023 | sd->s_ns = ns; | 1030 | sd->s_ns = ns; |
1024 | sd->priv = priv; | 1031 | sd->priv = priv; |
1025 | sysfs_dirent_init_lockdep(sd); | 1032 | |
1033 | #ifdef CONFIG_DEBUG_LOCK_ALLOC | ||
1034 | if (key) { | ||
1035 | lockdep_init_map(&sd->dep_map, "s_active", key, 0); | ||
1036 | sd->s_flags |= SYSFS_FLAG_LOCKDEP; | ||
1037 | } | ||
1038 | #endif | ||
1026 | 1039 | ||
1027 | /* | 1040 | /* |
1028 | * sd->s_attr.ops is accesible only while holding active ref. We | 1041 | * sd->s_attr.ops is accesible only while holding active ref. We |
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index c86456c9b19e..e93f8b845611 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h | |||
@@ -92,43 +92,13 @@ struct sysfs_dirent { | |||
92 | #define SYSFS_FLAG_NS 0x0020 | 92 | #define SYSFS_FLAG_NS 0x0020 |
93 | #define SYSFS_FLAG_HAS_SEQ_SHOW 0x0040 | 93 | #define SYSFS_FLAG_HAS_SEQ_SHOW 0x0040 |
94 | #define SYSFS_FLAG_HAS_MMAP 0x0080 | 94 | #define SYSFS_FLAG_HAS_MMAP 0x0080 |
95 | #define SYSFS_FLAG_LOCKDEP 0x0100 | ||
95 | 96 | ||
96 | static inline unsigned int sysfs_type(struct sysfs_dirent *sd) | 97 | static inline unsigned int sysfs_type(struct sysfs_dirent *sd) |
97 | { | 98 | { |
98 | return sd->s_flags & SYSFS_TYPE_MASK; | 99 | return sd->s_flags & SYSFS_TYPE_MASK; |
99 | } | 100 | } |
100 | 101 | ||
101 | #ifdef CONFIG_DEBUG_LOCK_ALLOC | ||
102 | |||
103 | #define sysfs_dirent_init_lockdep(sd) \ | ||
104 | do { \ | ||
105 | struct attribute *attr = sd->priv; \ | ||
106 | struct lock_class_key *key = attr->key; \ | ||
107 | if (!key) \ | ||
108 | key = &attr->skey; \ | ||
109 | \ | ||
110 | lockdep_init_map(&sd->dep_map, "s_active", key, 0); \ | ||
111 | } while (0) | ||
112 | |||
113 | /* Test for attributes that want to ignore lockdep for read-locking */ | ||
114 | static inline bool sysfs_ignore_lockdep(struct sysfs_dirent *sd) | ||
115 | { | ||
116 | struct attribute *attr = sd->priv; | ||
117 | |||
118 | return sysfs_type(sd) == SYSFS_KOBJ_ATTR && attr->ignore_lockdep; | ||
119 | } | ||
120 | |||
121 | #else | ||
122 | |||
123 | #define sysfs_dirent_init_lockdep(sd) do {} while (0) | ||
124 | |||
125 | static inline bool sysfs_ignore_lockdep(struct sysfs_dirent *sd) | ||
126 | { | ||
127 | return true; | ||
128 | } | ||
129 | |||
130 | #endif | ||
131 | |||
132 | /* | 102 | /* |
133 | * Context structure to be used while adding/removing nodes. | 103 | * Context structure to be used while adding/removing nodes. |
134 | */ | 104 | */ |
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index f20796ecc76e..105d09dcb064 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/err.h> | 11 | #include <linux/err.h> |
12 | #include <linux/list.h> | 12 | #include <linux/list.h> |
13 | #include <linux/mutex.h> | 13 | #include <linux/mutex.h> |
14 | #include <linux/lockdep.h> | ||
14 | 15 | ||
15 | struct file; | 16 | struct file; |
16 | struct iattr; | 17 | struct iattr; |
@@ -62,6 +63,10 @@ struct kernfs_ops { | |||
62 | loff_t off); | 63 | loff_t off); |
63 | 64 | ||
64 | int (*mmap)(struct sysfs_open_file *of, struct vm_area_struct *vma); | 65 | int (*mmap)(struct sysfs_open_file *of, struct vm_area_struct *vma); |
66 | |||
67 | #ifdef CONFIG_DEBUG_LOCK_ALLOC | ||
68 | struct lock_class_key lockdep_key; | ||
69 | #endif | ||
65 | }; | 70 | }; |
66 | 71 | ||
67 | #ifdef CONFIG_SYSFS | 72 | #ifdef CONFIG_SYSFS |
@@ -69,11 +74,12 @@ struct kernfs_ops { | |||
69 | struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, | 74 | struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, |
70 | const char *name, void *priv, | 75 | const char *name, void *priv, |
71 | const void *ns); | 76 | const void *ns); |
72 | struct sysfs_dirent *kernfs_create_file_ns(struct sysfs_dirent *parent, | 77 | struct sysfs_dirent *kernfs_create_file_ns_key(struct sysfs_dirent *parent, |
73 | const char *name, | 78 | const char *name, |
74 | umode_t mode, loff_t size, | 79 | umode_t mode, loff_t size, |
75 | const struct kernfs_ops *ops, | 80 | const struct kernfs_ops *ops, |
76 | void *priv, const void *ns); | 81 | void *priv, const void *ns, |
82 | struct lock_class_key *key); | ||
77 | struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, | 83 | struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, |
78 | const char *name, | 84 | const char *name, |
79 | struct sysfs_dirent *target); | 85 | struct sysfs_dirent *target); |
@@ -94,9 +100,10 @@ kernfs_create_dir_ns(struct sysfs_dirent *parent, const char *name, void *priv, | |||
94 | { return ERR_PTR(-ENOSYS); } | 100 | { return ERR_PTR(-ENOSYS); } |
95 | 101 | ||
96 | static inline struct sysfs_dirent * | 102 | static inline struct sysfs_dirent * |
97 | kernfs_create_file_ns(struct sysfs_dirent *parent, const char *name, | 103 | kernfs_create_file_ns_key(struct sysfs_dirent *parent, const char *name, |
98 | umode_t mode, loff_t size, const struct kernfs_ops *ops, | 104 | umode_t mode, loff_t size, |
99 | void *priv, const void *ns) | 105 | const struct kernfs_ops *ops, void *priv, |
106 | const void *ns, struct lock_class_key *key) | ||
100 | { return ERR_PTR(-ENOSYS); } | 107 | { return ERR_PTR(-ENOSYS); } |
101 | 108 | ||
102 | static inline struct sysfs_dirent * | 109 | static inline struct sysfs_dirent * |
@@ -132,6 +139,20 @@ kernfs_create_dir(struct sysfs_dirent *parent, const char *name, void *priv) | |||
132 | } | 139 | } |
133 | 140 | ||
134 | static inline struct sysfs_dirent * | 141 | static inline struct sysfs_dirent * |
142 | kernfs_create_file_ns(struct sysfs_dirent *parent, const char *name, | ||
143 | umode_t mode, loff_t size, const struct kernfs_ops *ops, | ||
144 | void *priv, const void *ns) | ||
145 | { | ||
146 | struct lock_class_key *key = NULL; | ||
147 | |||
148 | #ifdef CONFIG_DEBUG_LOCK_ALLOC | ||
149 | key = (struct lock_class_key *)&ops->lockdep_key; | ||
150 | #endif | ||
151 | return kernfs_create_file_ns_key(parent, name, mode, size, ops, priv, | ||
152 | ns, key); | ||
153 | } | ||
154 | |||
155 | static inline struct sysfs_dirent * | ||
135 | kernfs_create_file(struct sysfs_dirent *parent, const char *name, umode_t mode, | 156 | kernfs_create_file(struct sysfs_dirent *parent, const char *name, umode_t mode, |
136 | loff_t size, const struct kernfs_ops *ops, void *priv) | 157 | loff_t size, const struct kernfs_ops *ops, void *priv) |
137 | { | 158 | { |