diff options
Diffstat (limited to 'fs/sysfs/bin.c')
-rw-r--r-- | fs/sysfs/bin.c | 253 |
1 files changed, 240 insertions, 13 deletions
diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index f2c478c3424e..07703d3ff4a1 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c | |||
@@ -21,15 +21,28 @@ | |||
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
23 | #include <linux/mutex.h> | 23 | #include <linux/mutex.h> |
24 | #include <linux/mm.h> | ||
24 | 25 | ||
25 | #include <asm/uaccess.h> | 26 | #include <asm/uaccess.h> |
26 | 27 | ||
27 | #include "sysfs.h" | 28 | #include "sysfs.h" |
28 | 29 | ||
30 | /* | ||
31 | * There's one bin_buffer for each open file. | ||
32 | * | ||
33 | * filp->private_data points to bin_buffer and | ||
34 | * sysfs_dirent->s_bin_attr.buffers points to a the bin_buffer s | ||
35 | * sysfs_dirent->s_bin_attr.buffers is protected by sysfs_bin_lock | ||
36 | */ | ||
37 | static DEFINE_MUTEX(sysfs_bin_lock); | ||
38 | |||
29 | struct bin_buffer { | 39 | struct bin_buffer { |
30 | struct mutex mutex; | 40 | struct mutex mutex; |
31 | void *buffer; | 41 | void *buffer; |
32 | int mmapped; | 42 | int mmapped; |
43 | struct vm_operations_struct *vm_ops; | ||
44 | struct file *file; | ||
45 | struct hlist_node list; | ||
33 | }; | 46 | }; |
34 | 47 | ||
35 | static int | 48 | static int |
@@ -168,6 +181,175 @@ out_free: | |||
168 | return count; | 181 | return count; |
169 | } | 182 | } |
170 | 183 | ||
184 | static void bin_vma_open(struct vm_area_struct *vma) | ||
185 | { | ||
186 | struct file *file = vma->vm_file; | ||
187 | struct bin_buffer *bb = file->private_data; | ||
188 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
189 | |||
190 | if (!bb->vm_ops || !bb->vm_ops->open) | ||
191 | return; | ||
192 | |||
193 | if (!sysfs_get_active_two(attr_sd)) | ||
194 | return; | ||
195 | |||
196 | bb->vm_ops->open(vma); | ||
197 | |||
198 | sysfs_put_active_two(attr_sd); | ||
199 | } | ||
200 | |||
201 | static void bin_vma_close(struct vm_area_struct *vma) | ||
202 | { | ||
203 | struct file *file = vma->vm_file; | ||
204 | struct bin_buffer *bb = file->private_data; | ||
205 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
206 | |||
207 | if (!bb->vm_ops || !bb->vm_ops->close) | ||
208 | return; | ||
209 | |||
210 | if (!sysfs_get_active_two(attr_sd)) | ||
211 | return; | ||
212 | |||
213 | bb->vm_ops->close(vma); | ||
214 | |||
215 | sysfs_put_active_two(attr_sd); | ||
216 | } | ||
217 | |||
218 | static int bin_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | ||
219 | { | ||
220 | struct file *file = vma->vm_file; | ||
221 | struct bin_buffer *bb = file->private_data; | ||
222 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
223 | int ret; | ||
224 | |||
225 | if (!bb->vm_ops || !bb->vm_ops->fault) | ||
226 | return VM_FAULT_SIGBUS; | ||
227 | |||
228 | if (!sysfs_get_active_two(attr_sd)) | ||
229 | return VM_FAULT_SIGBUS; | ||
230 | |||
231 | ret = bb->vm_ops->fault(vma, vmf); | ||
232 | |||
233 | sysfs_put_active_two(attr_sd); | ||
234 | return ret; | ||
235 | } | ||
236 | |||
237 | static int bin_page_mkwrite(struct vm_area_struct *vma, struct page *page) | ||
238 | { | ||
239 | struct file *file = vma->vm_file; | ||
240 | struct bin_buffer *bb = file->private_data; | ||
241 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
242 | int ret; | ||
243 | |||
244 | if (!bb->vm_ops) | ||
245 | return -EINVAL; | ||
246 | |||
247 | if (!bb->vm_ops->page_mkwrite) | ||
248 | return 0; | ||
249 | |||
250 | if (!sysfs_get_active_two(attr_sd)) | ||
251 | return -EINVAL; | ||
252 | |||
253 | ret = bb->vm_ops->page_mkwrite(vma, page); | ||
254 | |||
255 | sysfs_put_active_two(attr_sd); | ||
256 | return ret; | ||
257 | } | ||
258 | |||
259 | static int bin_access(struct vm_area_struct *vma, unsigned long addr, | ||
260 | void *buf, int len, int write) | ||
261 | { | ||
262 | struct file *file = vma->vm_file; | ||
263 | struct bin_buffer *bb = file->private_data; | ||
264 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
265 | int ret; | ||
266 | |||
267 | if (!bb->vm_ops || !bb->vm_ops->access) | ||
268 | return -EINVAL; | ||
269 | |||
270 | if (!sysfs_get_active_two(attr_sd)) | ||
271 | return -EINVAL; | ||
272 | |||
273 | ret = bb->vm_ops->access(vma, addr, buf, len, write); | ||
274 | |||
275 | sysfs_put_active_two(attr_sd); | ||
276 | return ret; | ||
277 | } | ||
278 | |||
279 | #ifdef CONFIG_NUMA | ||
280 | static int bin_set_policy(struct vm_area_struct *vma, struct mempolicy *new) | ||
281 | { | ||
282 | struct file *file = vma->vm_file; | ||
283 | struct bin_buffer *bb = file->private_data; | ||
284 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
285 | int ret; | ||
286 | |||
287 | if (!bb->vm_ops || !bb->vm_ops->set_policy) | ||
288 | return 0; | ||
289 | |||
290 | if (!sysfs_get_active_two(attr_sd)) | ||
291 | return -EINVAL; | ||
292 | |||
293 | ret = bb->vm_ops->set_policy(vma, new); | ||
294 | |||
295 | sysfs_put_active_two(attr_sd); | ||
296 | return ret; | ||
297 | } | ||
298 | |||
299 | static struct mempolicy *bin_get_policy(struct vm_area_struct *vma, | ||
300 | unsigned long addr) | ||
301 | { | ||
302 | struct file *file = vma->vm_file; | ||
303 | struct bin_buffer *bb = file->private_data; | ||
304 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
305 | struct mempolicy *pol; | ||
306 | |||
307 | if (!bb->vm_ops || !bb->vm_ops->get_policy) | ||
308 | return vma->vm_policy; | ||
309 | |||
310 | if (!sysfs_get_active_two(attr_sd)) | ||
311 | return vma->vm_policy; | ||
312 | |||
313 | pol = bb->vm_ops->get_policy(vma, addr); | ||
314 | |||
315 | sysfs_put_active_two(attr_sd); | ||
316 | return pol; | ||
317 | } | ||
318 | |||
319 | static int bin_migrate(struct vm_area_struct *vma, const nodemask_t *from, | ||
320 | const nodemask_t *to, unsigned long flags) | ||
321 | { | ||
322 | struct file *file = vma->vm_file; | ||
323 | struct bin_buffer *bb = file->private_data; | ||
324 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
325 | int ret; | ||
326 | |||
327 | if (!bb->vm_ops || !bb->vm_ops->migrate) | ||
328 | return 0; | ||
329 | |||
330 | if (!sysfs_get_active_two(attr_sd)) | ||
331 | return 0; | ||
332 | |||
333 | ret = bb->vm_ops->migrate(vma, from, to, flags); | ||
334 | |||
335 | sysfs_put_active_two(attr_sd); | ||
336 | return ret; | ||
337 | } | ||
338 | #endif | ||
339 | |||
340 | static struct vm_operations_struct bin_vm_ops = { | ||
341 | .open = bin_vma_open, | ||
342 | .close = bin_vma_close, | ||
343 | .fault = bin_fault, | ||
344 | .page_mkwrite = bin_page_mkwrite, | ||
345 | .access = bin_access, | ||
346 | #ifdef CONFIG_NUMA | ||
347 | .set_policy = bin_set_policy, | ||
348 | .get_policy = bin_get_policy, | ||
349 | .migrate = bin_migrate, | ||
350 | #endif | ||
351 | }; | ||
352 | |||
171 | static int mmap(struct file *file, struct vm_area_struct *vma) | 353 | static int mmap(struct file *file, struct vm_area_struct *vma) |
172 | { | 354 | { |
173 | struct bin_buffer *bb = file->private_data; | 355 | struct bin_buffer *bb = file->private_data; |
@@ -179,18 +361,37 @@ static int mmap(struct file *file, struct vm_area_struct *vma) | |||
179 | mutex_lock(&bb->mutex); | 361 | mutex_lock(&bb->mutex); |
180 | 362 | ||
181 | /* need attr_sd for attr, its parent for kobj */ | 363 | /* need attr_sd for attr, its parent for kobj */ |
364 | rc = -ENODEV; | ||
182 | if (!sysfs_get_active_two(attr_sd)) | 365 | if (!sysfs_get_active_two(attr_sd)) |
183 | return -ENODEV; | 366 | goto out_unlock; |
184 | 367 | ||
185 | rc = -EINVAL; | 368 | rc = -EINVAL; |
186 | if (attr->mmap) | 369 | if (!attr->mmap) |
187 | rc = attr->mmap(kobj, attr, vma); | 370 | goto out_put; |
371 | |||
372 | rc = attr->mmap(kobj, attr, vma); | ||
373 | if (rc) | ||
374 | goto out_put; | ||
188 | 375 | ||
189 | if (rc == 0 && !bb->mmapped) | 376 | /* |
190 | bb->mmapped = 1; | 377 | * PowerPC's pci_mmap of legacy_mem uses shmem_zero_setup() |
191 | else | 378 | * to satisfy versions of X which crash if the mmap fails: that |
192 | sysfs_put_active_two(attr_sd); | 379 | * substitutes a new vm_file, and we don't then want bin_vm_ops. |
380 | */ | ||
381 | if (vma->vm_file != file) | ||
382 | goto out_put; | ||
193 | 383 | ||
384 | rc = -EINVAL; | ||
385 | if (bb->mmapped && bb->vm_ops != vma->vm_ops) | ||
386 | goto out_put; | ||
387 | |||
388 | rc = 0; | ||
389 | bb->mmapped = 1; | ||
390 | bb->vm_ops = vma->vm_ops; | ||
391 | vma->vm_ops = &bin_vm_ops; | ||
392 | out_put: | ||
393 | sysfs_put_active_two(attr_sd); | ||
394 | out_unlock: | ||
194 | mutex_unlock(&bb->mutex); | 395 | mutex_unlock(&bb->mutex); |
195 | 396 | ||
196 | return rc; | 397 | return rc; |
@@ -223,8 +424,13 @@ static int open(struct inode * inode, struct file * file) | |||
223 | goto err_out; | 424 | goto err_out; |
224 | 425 | ||
225 | mutex_init(&bb->mutex); | 426 | mutex_init(&bb->mutex); |
427 | bb->file = file; | ||
226 | file->private_data = bb; | 428 | file->private_data = bb; |
227 | 429 | ||
430 | mutex_lock(&sysfs_bin_lock); | ||
431 | hlist_add_head(&bb->list, &attr_sd->s_bin_attr.buffers); | ||
432 | mutex_unlock(&sysfs_bin_lock); | ||
433 | |||
228 | /* open succeeded, put active references */ | 434 | /* open succeeded, put active references */ |
229 | sysfs_put_active_two(attr_sd); | 435 | sysfs_put_active_two(attr_sd); |
230 | return 0; | 436 | return 0; |
@@ -237,11 +443,12 @@ static int open(struct inode * inode, struct file * file) | |||
237 | 443 | ||
238 | static int release(struct inode * inode, struct file * file) | 444 | static int release(struct inode * inode, struct file * file) |
239 | { | 445 | { |
240 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
241 | struct bin_buffer *bb = file->private_data; | 446 | struct bin_buffer *bb = file->private_data; |
242 | 447 | ||
243 | if (bb->mmapped) | 448 | mutex_lock(&sysfs_bin_lock); |
244 | sysfs_put_active_two(attr_sd); | 449 | hlist_del(&bb->list); |
450 | mutex_unlock(&sysfs_bin_lock); | ||
451 | |||
245 | kfree(bb->buffer); | 452 | kfree(bb->buffer); |
246 | kfree(bb); | 453 | kfree(bb); |
247 | return 0; | 454 | return 0; |
@@ -256,6 +463,26 @@ const struct file_operations bin_fops = { | |||
256 | .release = release, | 463 | .release = release, |
257 | }; | 464 | }; |
258 | 465 | ||
466 | |||
467 | void unmap_bin_file(struct sysfs_dirent *attr_sd) | ||
468 | { | ||
469 | struct bin_buffer *bb; | ||
470 | struct hlist_node *tmp; | ||
471 | |||
472 | if (sysfs_type(attr_sd) != SYSFS_KOBJ_BIN_ATTR) | ||
473 | return; | ||
474 | |||
475 | mutex_lock(&sysfs_bin_lock); | ||
476 | |||
477 | hlist_for_each_entry(bb, tmp, &attr_sd->s_bin_attr.buffers, list) { | ||
478 | struct inode *inode = bb->file->f_path.dentry->d_inode; | ||
479 | |||
480 | unmap_mapping_range(inode->i_mapping, 0, 0, 1); | ||
481 | } | ||
482 | |||
483 | mutex_unlock(&sysfs_bin_lock); | ||
484 | } | ||
485 | |||
259 | /** | 486 | /** |
260 | * sysfs_create_bin_file - create binary file for object. | 487 | * sysfs_create_bin_file - create binary file for object. |
261 | * @kobj: object. | 488 | * @kobj: object. |