diff options
Diffstat (limited to 'fs/sysfs/sysfs.h')
-rw-r--r-- | fs/sysfs/sysfs.h | 123 |
1 files changed, 102 insertions, 21 deletions
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 60717660ac55..d998e8e27841 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h | |||
@@ -14,8 +14,14 @@ struct sysfs_elem_bin_attr { | |||
14 | struct bin_attribute * bin_attr; | 14 | struct bin_attribute * bin_attr; |
15 | }; | 15 | }; |
16 | 16 | ||
17 | /* | ||
18 | * As long as s_count reference is held, the sysfs_dirent itself is | ||
19 | * accessible. Dereferencing s_elem or any other outer entity | ||
20 | * requires s_active reference. | ||
21 | */ | ||
17 | struct sysfs_dirent { | 22 | struct sysfs_dirent { |
18 | atomic_t s_count; | 23 | atomic_t s_count; |
24 | struct rw_semaphore s_active; | ||
19 | struct sysfs_dirent * s_parent; | 25 | struct sysfs_dirent * s_parent; |
20 | struct list_head s_sibling; | 26 | struct list_head s_sibling; |
21 | struct list_head s_children; | 27 | struct list_head s_children; |
@@ -36,6 +42,17 @@ struct sysfs_dirent { | |||
36 | atomic_t s_event; | 42 | atomic_t s_event; |
37 | }; | 43 | }; |
38 | 44 | ||
45 | /* | ||
46 | * A sysfs file which deletes another file when written to need to | ||
47 | * write lock the s_active of the victim while its s_active is read | ||
48 | * locked for the write operation. Tell lockdep that this is okay. | ||
49 | */ | ||
50 | enum sysfs_s_active_class | ||
51 | { | ||
52 | SYSFS_S_ACTIVE_NORMAL, /* file r/w access, etc - default */ | ||
53 | SYSFS_S_ACTIVE_DEACTIVATE, /* file deactivation */ | ||
54 | }; | ||
55 | |||
39 | extern struct vfsmount * sysfs_mount; | 56 | extern struct vfsmount * sysfs_mount; |
40 | extern struct kmem_cache *sysfs_dir_cachep; | 57 | extern struct kmem_cache *sysfs_dir_cachep; |
41 | 58 | ||
@@ -87,43 +104,107 @@ struct sysfs_buffer_collection { | |||
87 | struct list_head associates; | 104 | struct list_head associates; |
88 | }; | 105 | }; |
89 | 106 | ||
90 | static inline struct kobject * to_kobj(struct dentry * dentry) | 107 | static inline struct sysfs_dirent * sysfs_get(struct sysfs_dirent * sd) |
91 | { | 108 | { |
92 | struct sysfs_dirent * sd = dentry->d_fsdata; | 109 | if (sd) { |
93 | return sd->s_elem.dir.kobj; | 110 | WARN_ON(!atomic_read(&sd->s_count)); |
111 | atomic_inc(&sd->s_count); | ||
112 | } | ||
113 | return sd; | ||
94 | } | 114 | } |
95 | 115 | ||
96 | static inline struct kobject *sysfs_get_kobject(struct dentry *dentry) | 116 | static inline void sysfs_put(struct sysfs_dirent * sd) |
97 | { | 117 | { |
98 | struct kobject * kobj = NULL; | 118 | if (sd && atomic_dec_and_test(&sd->s_count)) |
99 | 119 | release_sysfs_dirent(sd); | |
100 | spin_lock(&dcache_lock); | 120 | } |
101 | if (!d_unhashed(dentry)) { | ||
102 | struct sysfs_dirent * sd = dentry->d_fsdata; | ||
103 | |||
104 | if (sd->s_type & SYSFS_KOBJ_LINK) | ||
105 | sd = sd->s_elem.symlink.target_sd; | ||
106 | 121 | ||
107 | kobj = kobject_get(sd->s_elem.dir.kobj); | 122 | /** |
123 | * sysfs_get_active - get an active reference to sysfs_dirent | ||
124 | * @sd: sysfs_dirent to get an active reference to | ||
125 | * | ||
126 | * Get an active reference of @sd. This function is noop if @sd | ||
127 | * is NULL. | ||
128 | * | ||
129 | * RETURNS: | ||
130 | * Pointer to @sd on success, NULL on failure. | ||
131 | */ | ||
132 | static inline struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) | ||
133 | { | ||
134 | if (sd) { | ||
135 | if (unlikely(!down_read_trylock(&sd->s_active))) | ||
136 | sd = NULL; | ||
108 | } | 137 | } |
109 | spin_unlock(&dcache_lock); | 138 | return sd; |
139 | } | ||
110 | 140 | ||
111 | return kobj; | 141 | /** |
142 | * sysfs_put_active - put an active reference to sysfs_dirent | ||
143 | * @sd: sysfs_dirent to put an active reference to | ||
144 | * | ||
145 | * Put an active reference to @sd. This function is noop if @sd | ||
146 | * is NULL. | ||
147 | */ | ||
148 | static inline void sysfs_put_active(struct sysfs_dirent *sd) | ||
149 | { | ||
150 | if (sd) | ||
151 | up_read(&sd->s_active); | ||
112 | } | 152 | } |
113 | 153 | ||
114 | static inline struct sysfs_dirent * sysfs_get(struct sysfs_dirent * sd) | 154 | /** |
155 | * sysfs_get_active_two - get active references to sysfs_dirent and parent | ||
156 | * @sd: sysfs_dirent of interest | ||
157 | * | ||
158 | * Get active reference to @sd and its parent. Parent's active | ||
159 | * reference is grabbed first. This function is noop if @sd is | ||
160 | * NULL. | ||
161 | * | ||
162 | * RETURNS: | ||
163 | * Pointer to @sd on success, NULL on failure. | ||
164 | */ | ||
165 | static inline struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd) | ||
115 | { | 166 | { |
116 | if (sd) { | 167 | if (sd) { |
117 | WARN_ON(!atomic_read(&sd->s_count)); | 168 | if (sd->s_parent && unlikely(!sysfs_get_active(sd->s_parent))) |
118 | atomic_inc(&sd->s_count); | 169 | return NULL; |
170 | if (unlikely(!sysfs_get_active(sd))) { | ||
171 | sysfs_put_active(sd->s_parent); | ||
172 | return NULL; | ||
173 | } | ||
119 | } | 174 | } |
120 | return sd; | 175 | return sd; |
121 | } | 176 | } |
122 | 177 | ||
123 | static inline void sysfs_put(struct sysfs_dirent * sd) | 178 | /** |
179 | * sysfs_put_active_two - put active references to sysfs_dirent and parent | ||
180 | * @sd: sysfs_dirent of interest | ||
181 | * | ||
182 | * Put active references to @sd and its parent. This function is | ||
183 | * noop if @sd is NULL. | ||
184 | */ | ||
185 | static inline void sysfs_put_active_two(struct sysfs_dirent *sd) | ||
124 | { | 186 | { |
125 | if (sd && atomic_dec_and_test(&sd->s_count)) | 187 | if (sd) { |
126 | release_sysfs_dirent(sd); | 188 | sysfs_put_active(sd); |
189 | sysfs_put_active(sd->s_parent); | ||
190 | } | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * sysfs_deactivate - deactivate sysfs_dirent | ||
195 | * @sd: sysfs_dirent to deactivate | ||
196 | * | ||
197 | * Deny new active references and drain existing ones. s_active | ||
198 | * will be unlocked when the sysfs_dirent is released. | ||
199 | */ | ||
200 | static inline void sysfs_deactivate(struct sysfs_dirent *sd) | ||
201 | { | ||
202 | down_write_nested(&sd->s_active, SYSFS_S_ACTIVE_DEACTIVATE); | ||
203 | |||
204 | /* s_active will be unlocked by the thread doing the final put | ||
205 | * on @sd. Lie to lockdep. | ||
206 | */ | ||
207 | rwsem_release(&sd->s_active.dep_map, 1, _RET_IP_); | ||
127 | } | 208 | } |
128 | 209 | ||
129 | static inline int sysfs_is_shadowed_inode(struct inode *inode) | 210 | static inline int sysfs_is_shadowed_inode(struct inode *inode) |