aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs/sysfs.h
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2007-06-13 14:45:16 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-07-11 19:09:05 -0400
commit0ab66088c855eca68513bdd7442a426c4b374ced (patch)
tree7f931f7f984ac14701a33d123fa9e03d9048bf8f /fs/sysfs/sysfs.h
parenteb36165353d0e5ac32b063f555acedcbaf6d3b75 (diff)
sysfs: implement sysfs_dirent active reference and immediate disconnect
sysfs: implement sysfs_dirent active reference and immediate disconnect Opening a sysfs node references its associated kobject, so userland can arbitrarily prolong lifetime of a kobject which complicates lifetime rules in drivers. This patch implements active reference and makes the association between kobject and sysfs immediately breakable. Now each sysfs_dirent has two reference counts - s_count and s_active. s_count is a regular reference count which guarantees that the containing sysfs_dirent is accessible. As long as s_count reference is held, all sysfs internal fields in sysfs_dirent are accessible including s_parent and s_name. The newly added s_active is active reference count. This is acquired by invoking sysfs_get_active() and it's the caller's responsibility to ensure sysfs_dirent itself is accessible (should be holding s_count one way or the other). Dereferencing sysfs_dirent to access objects out of sysfs proper requires active reference. This includes access to the associated kobjects, attributes and ops. The active references can be drained and denied by calling sysfs_deactivate(). All active sysfs_dirents must be deactivated after deletion but before the default reference is dropped. This enables immediate disconnect of sysfs nodes. Once a sysfs_dirent is deleted, it won't access any entity external to sysfs proper. Because attr/bin_attr ops access both the node itself and its parent for kobject, they need to hold active references to both. sysfs_get/put_active_two() helpers are provided to help grabbing both references. Parent's is acquired first and released last. Unlike other operations, mmapped area lingers on after mmap() is finished and the module implement implementing it and kobj need to stay referenced till all the mapped pages are gone. This is accomplished by holding one set of active references to the bin_attr and its parent if there have been any mmap during lifetime of an openfile. The references are dropped when the openfile is released. This change makes sysfs lifetime rules independent from both kobject's and module's. It not only fixes several race conditions caused by sysfs not holding onto the proper module when referencing kobject, but also helps fixing and simplifying lifetime management in driver model and drivers by taking sysfs out of the equation. Please read the following message for more info. http://article.gmane.org/gmane.linux.kernel/510293 Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'fs/sysfs/sysfs.h')
-rw-r--r--fs/sysfs/sysfs.h123
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 */
17struct sysfs_dirent { 22struct 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 */
50enum 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
39extern struct vfsmount * sysfs_mount; 56extern struct vfsmount * sysfs_mount;
40extern struct kmem_cache *sysfs_dir_cachep; 57extern 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
90static inline struct kobject * to_kobj(struct dentry * dentry) 107static 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
96static inline struct kobject *sysfs_get_kobject(struct dentry *dentry) 116static 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 */
132static 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 */
148static inline void sysfs_put_active(struct sysfs_dirent *sd)
149{
150 if (sd)
151 up_read(&sd->s_active);
112} 152}
113 153
114static 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 */
165static 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
123static 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 */
185static 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 */
200static 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
129static inline int sysfs_is_shadowed_inode(struct inode *inode) 210static inline int sysfs_is_shadowed_inode(struct inode *inode)