aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs/bin.c
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/bin.c
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/bin.c')
-rw-r--r--fs/sysfs/bin.c95
1 files changed, 62 insertions, 33 deletions
diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c
index 5dc47fe5de5e..618b8aea6a7b 100644
--- a/fs/sysfs/bin.c
+++ b/fs/sysfs/bin.c
@@ -23,6 +23,7 @@
23struct bin_buffer { 23struct bin_buffer {
24 struct mutex mutex; 24 struct mutex mutex;
25 void *buffer; 25 void *buffer;
26 int mmapped;
26}; 27};
27 28
28static int 29static int
@@ -30,12 +31,20 @@ fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count)
30{ 31{
31 struct sysfs_dirent *attr_sd = dentry->d_fsdata; 32 struct sysfs_dirent *attr_sd = dentry->d_fsdata;
32 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; 33 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
33 struct kobject * kobj = to_kobj(dentry->d_parent); 34 struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj;
35 int rc;
36
37 /* need attr_sd for attr, its parent for kobj */
38 if (!sysfs_get_active_two(attr_sd))
39 return -ENODEV;
34 40
35 if (!attr->read) 41 rc = -EIO;
36 return -EIO; 42 if (attr->read)
43 rc = attr->read(kobj, buffer, off, count);
37 44
38 return attr->read(kobj, buffer, off, count); 45 sysfs_put_active_two(attr_sd);
46
47 return rc;
39} 48}
40 49
41static ssize_t 50static ssize_t
@@ -79,12 +88,20 @@ flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count)
79{ 88{
80 struct sysfs_dirent *attr_sd = dentry->d_fsdata; 89 struct sysfs_dirent *attr_sd = dentry->d_fsdata;
81 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; 90 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
82 struct kobject *kobj = to_kobj(dentry->d_parent); 91 struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj;
92 int rc;
93
94 /* need attr_sd for attr, its parent for kobj */
95 if (!sysfs_get_active_two(attr_sd))
96 return -ENODEV;
83 97
84 if (!attr->write) 98 rc = -EIO;
85 return -EIO; 99 if (attr->write)
100 rc = attr->write(kobj, buffer, offset, count);
86 101
87 return attr->write(kobj, buffer, offset, count); 102 sysfs_put_active_two(attr_sd);
103
104 return rc;
88} 105}
89 106
90static ssize_t write(struct file *file, const char __user *userbuf, 107static ssize_t write(struct file *file, const char __user *userbuf,
@@ -124,14 +141,24 @@ static int mmap(struct file *file, struct vm_area_struct *vma)
124 struct bin_buffer *bb = file->private_data; 141 struct bin_buffer *bb = file->private_data;
125 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 142 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
126 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; 143 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
127 struct kobject *kobj = to_kobj(file->f_path.dentry->d_parent); 144 struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj;
128 int rc; 145 int rc;
129 146
130 if (!attr->mmap)
131 return -EINVAL;
132
133 mutex_lock(&bb->mutex); 147 mutex_lock(&bb->mutex);
134 rc = attr->mmap(kobj, attr, vma); 148
149 /* need attr_sd for attr, its parent for kobj */
150 if (!sysfs_get_active_two(attr_sd))
151 return -ENODEV;
152
153 rc = -EINVAL;
154 if (attr->mmap)
155 rc = attr->mmap(kobj, attr, vma);
156
157 if (rc == 0 && !bb->mmapped)
158 bb->mmapped = 1;
159 else
160 sysfs_put_active_two(attr_sd);
161
135 mutex_unlock(&bb->mutex); 162 mutex_unlock(&bb->mutex);
136 163
137 return rc; 164 return rc;
@@ -139,58 +166,60 @@ static int mmap(struct file *file, struct vm_area_struct *vma)
139 166
140static int open(struct inode * inode, struct file * file) 167static int open(struct inode * inode, struct file * file)
141{ 168{
142 struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent);
143 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 169 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
144 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; 170 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
145 struct bin_buffer *bb = NULL; 171 struct bin_buffer *bb = NULL;
146 int error = -EINVAL; 172 int error;
147 173
148 if (!kobj || !attr) 174 /* need attr_sd for attr */
149 goto Done; 175 if (!sysfs_get_active(attr_sd))
176 return -ENODEV;
150 177
151 /* Grab the module reference for this attribute if we have one */ 178 /* Grab the module reference for this attribute */
152 error = -ENODEV; 179 error = -ENODEV;
153 if (!try_module_get(attr->attr.owner)) 180 if (!try_module_get(attr->attr.owner))
154 goto Done; 181 goto err_sput;
155 182
156 error = -EACCES; 183 error = -EACCES;
157 if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap)) 184 if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap))
158 goto Error; 185 goto err_mput;
159 if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap)) 186 if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap))
160 goto Error; 187 goto err_mput;
161 188
162 error = -ENOMEM; 189 error = -ENOMEM;
163 bb = kzalloc(sizeof(*bb), GFP_KERNEL); 190 bb = kzalloc(sizeof(*bb), GFP_KERNEL);
164 if (!bb) 191 if (!bb)
165 goto Error; 192 goto err_mput;
166 193
167 bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); 194 bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
168 if (!bb->buffer) 195 if (!bb->buffer)
169 goto Error; 196 goto err_mput;
170 197
171 mutex_init(&bb->mutex); 198 mutex_init(&bb->mutex);
172 file->private_data = bb; 199 file->private_data = bb;
173 200
174 error = 0; 201 /* open succeeded, put active reference and pin attr_sd */
175 goto Done; 202 sysfs_put_active(attr_sd);
203 sysfs_get(attr_sd);
204 return 0;
176 205
177 Error: 206 err_mput:
178 kfree(bb);
179 module_put(attr->attr.owner); 207 module_put(attr->attr.owner);
180 Done: 208 err_sput:
181 if (error) 209 sysfs_put_active(attr_sd);
182 kobject_put(kobj); 210 kfree(bb);
183 return error; 211 return error;
184} 212}
185 213
186static int release(struct inode * inode, struct file * file) 214static int release(struct inode * inode, struct file * file)
187{ 215{
188 struct kobject * kobj = to_kobj(file->f_path.dentry->d_parent);
189 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 216 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
190 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; 217 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
191 struct bin_buffer *bb = file->private_data; 218 struct bin_buffer *bb = file->private_data;
192 219
193 kobject_put(kobj); 220 if (bb->mmapped)
221 sysfs_put_active_two(attr_sd);
222 sysfs_put(attr_sd);
194 module_put(attr->attr.owner); 223 module_put(attr->attr.owner);
195 kfree(bb->buffer); 224 kfree(bb->buffer);
196 kfree(bb); 225 kfree(bb);