aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2013-10-01 17:42:09 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-10-05 20:27:40 -0400
commit3124eb1679b28726eacbc8973a891235dca3ed99 (patch)
tree75d826df41f4b3c3c74d565ca5a39974468b611a /fs/sysfs
parent49fe604781cbb03eb6ff12a7bc4ad8eef8b830c4 (diff)
sysfs: merge regular and bin file handling
With the previous changes, sysfs regular file code is ready to handle bin files too. This patch makes bin files share the regular file path. * sysfs_create/remove_bin_file() are moved to fs/sysfs/file.c. * sysfs_init_inode() is updated to use the new sysfs_bin_operations instead of bin_fops for bin files. * fs/sysfs/bin.c and the related pieces are removed. This patch shouldn't introduce any behavior difference to bin file accesses. Overall, this unification reduces the amount of duplicate logic, makes behaviors more consistent and paves the road for building simpler and more versatile interface which will allow other subsystems to make use of sysfs for their pseudo filesystems. v2: Stale fs/sysfs/bin.c reference dropped from Documentation/DocBook/filesystems.tmpl. Reported by kbuild test robot. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Kay Sievers <kay@vrfy.org> Cc: kbuild test robot <fengguang.wu@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/sysfs')
-rw-r--r--fs/sysfs/Makefile3
-rw-r--r--fs/sysfs/bin.c491
-rw-r--r--fs/sysfs/dir.c1
-rw-r--r--fs/sysfs/file.c26
-rw-r--r--fs/sysfs/inode.c2
-rw-r--r--fs/sysfs/sysfs.h6
6 files changed, 28 insertions, 501 deletions
diff --git a/fs/sysfs/Makefile b/fs/sysfs/Makefile
index 7a1ceb946b80..8876ac183373 100644
--- a/fs/sysfs/Makefile
+++ b/fs/sysfs/Makefile
@@ -2,5 +2,4 @@
2# Makefile for the sysfs virtual filesystem 2# Makefile for the sysfs virtual filesystem
3# 3#
4 4
5obj-y := inode.o file.o dir.o symlink.o mount.o bin.o \ 5obj-y := inode.o file.o dir.o symlink.o mount.o group.o
6 group.o
diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c
deleted file mode 100644
index 60a4e787dc4f..000000000000
--- a/fs/sysfs/bin.c
+++ /dev/null
@@ -1,491 +0,0 @@
1/*
2 * fs/sysfs/bin.c - sysfs binary file implementation
3 *
4 * Copyright (c) 2003 Patrick Mochel
5 * Copyright (c) 2003 Matthew Wilcox
6 * Copyright (c) 2004 Silicon Graphics, Inc.
7 * Copyright (c) 2007 SUSE Linux Products GmbH
8 * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
9 *
10 * This file is released under the GPLv2.
11 *
12 * Please see Documentation/filesystems/sysfs.txt for more information.
13 */
14
15#undef DEBUG
16
17#include <linux/errno.h>
18#include <linux/fs.h>
19#include <linux/kernel.h>
20#include <linux/kobject.h>
21#include <linux/module.h>
22#include <linux/slab.h>
23#include <linux/mutex.h>
24#include <linux/mm.h>
25#include <linux/uaccess.h>
26
27#include "sysfs.h"
28
29/*
30 * There's one bin_buffer for each open file.
31 *
32 * filp->private_data points to bin_buffer and
33 * sysfs_dirent->s_bin_attr.buffers points to a the bin_buffer s
34 * sysfs_dirent->s_bin_attr.buffers is protected by sysfs_bin_lock
35 */
36static DEFINE_MUTEX(sysfs_bin_lock);
37
38struct bin_buffer {
39 struct mutex mutex;
40 void *buffer;
41 int mmapped;
42 const struct vm_operations_struct *vm_ops;
43 struct file *file;
44 struct hlist_node list;
45};
46
47static ssize_t
48read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off)
49{
50 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
51 struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr;
52 struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
53 struct bin_buffer *bb = file->private_data;
54 int size = file_inode(file)->i_size;
55 loff_t offs = *off;
56 int count = min_t(size_t, bytes, PAGE_SIZE);
57 char *buf;
58
59 if (!bytes)
60 return 0;
61
62 if (size) {
63 if (offs > size)
64 return 0;
65 if (offs + count > size)
66 count = size - offs;
67 }
68
69 buf = kmalloc(count, GFP_KERNEL);
70 if (!buf)
71 return -ENOMEM;
72
73 /* need attr_sd for attr, its parent for kobj */
74 mutex_lock(&bb->mutex);
75 if (!sysfs_get_active(attr_sd)) {
76 count = -ENODEV;
77 mutex_unlock(&bb->mutex);
78 goto out_free;
79 }
80
81 if (attr->read)
82 count = attr->read(file, kobj, attr, buf, offs, count);
83 else
84 count = -EIO;
85
86 sysfs_put_active(attr_sd);
87 mutex_unlock(&bb->mutex);
88
89 if (count < 0)
90 goto out_free;
91
92 if (copy_to_user(userbuf, buf, count)) {
93 count = -EFAULT;
94 goto out_free;
95 }
96
97 pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count);
98
99 *off = offs + count;
100
101 out_free:
102 kfree(buf);
103 return count;
104}
105
106static int
107flush_write(struct file *file, char *buffer, loff_t offset, size_t count)
108{
109 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
110 struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr;
111 struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
112 int rc;
113
114 /* need attr_sd for attr, its parent for kobj */
115 if (!sysfs_get_active(attr_sd))
116 return -ENODEV;
117
118 rc = -EIO;
119 if (attr->write)
120 rc = attr->write(file, kobj, attr, buffer, offset, count);
121
122 sysfs_put_active(attr_sd);
123
124 return rc;
125}
126
127static ssize_t write(struct file *file, const char __user *userbuf,
128 size_t bytes, loff_t *off)
129{
130 struct bin_buffer *bb = file->private_data;
131 int size = file_inode(file)->i_size;
132 loff_t offs = *off;
133 int count = min_t(size_t, bytes, PAGE_SIZE);
134 char *temp;
135
136 if (!bytes)
137 return 0;
138
139 if (size) {
140 if (offs > size)
141 return 0;
142 if (offs + count > size)
143 count = size - offs;
144 }
145
146 temp = memdup_user(userbuf, count);
147 if (IS_ERR(temp))
148 return PTR_ERR(temp);
149
150 mutex_lock(&bb->mutex);
151
152 memcpy(bb->buffer, temp, count);
153
154 count = flush_write(file, bb->buffer, offs, count);
155 mutex_unlock(&bb->mutex);
156
157 if (count > 0)
158 *off = offs + count;
159
160 kfree(temp);
161 return count;
162}
163
164static void bin_vma_open(struct vm_area_struct *vma)
165{
166 struct file *file = vma->vm_file;
167 struct bin_buffer *bb = file->private_data;
168 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
169
170 if (!bb->vm_ops)
171 return;
172
173 if (!sysfs_get_active(attr_sd))
174 return;
175
176 if (bb->vm_ops->open)
177 bb->vm_ops->open(vma);
178
179 sysfs_put_active(attr_sd);
180}
181
182static int bin_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
183{
184 struct file *file = vma->vm_file;
185 struct bin_buffer *bb = file->private_data;
186 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
187 int ret;
188
189 if (!bb->vm_ops)
190 return VM_FAULT_SIGBUS;
191
192 if (!sysfs_get_active(attr_sd))
193 return VM_FAULT_SIGBUS;
194
195 ret = VM_FAULT_SIGBUS;
196 if (bb->vm_ops->fault)
197 ret = bb->vm_ops->fault(vma, vmf);
198
199 sysfs_put_active(attr_sd);
200 return ret;
201}
202
203static int bin_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
204{
205 struct file *file = vma->vm_file;
206 struct bin_buffer *bb = file->private_data;
207 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
208 int ret;
209
210 if (!bb->vm_ops)
211 return VM_FAULT_SIGBUS;
212
213 if (!sysfs_get_active(attr_sd))
214 return VM_FAULT_SIGBUS;
215
216 ret = 0;
217 if (bb->vm_ops->page_mkwrite)
218 ret = bb->vm_ops->page_mkwrite(vma, vmf);
219 else
220 file_update_time(file);
221
222 sysfs_put_active(attr_sd);
223 return ret;
224}
225
226static int bin_access(struct vm_area_struct *vma, unsigned long addr,
227 void *buf, int len, int write)
228{
229 struct file *file = vma->vm_file;
230 struct bin_buffer *bb = file->private_data;
231 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
232 int ret;
233
234 if (!bb->vm_ops)
235 return -EINVAL;
236
237 if (!sysfs_get_active(attr_sd))
238 return -EINVAL;
239
240 ret = -EINVAL;
241 if (bb->vm_ops->access)
242 ret = bb->vm_ops->access(vma, addr, buf, len, write);
243
244 sysfs_put_active(attr_sd);
245 return ret;
246}
247
248#ifdef CONFIG_NUMA
249static int bin_set_policy(struct vm_area_struct *vma, struct mempolicy *new)
250{
251 struct file *file = vma->vm_file;
252 struct bin_buffer *bb = file->private_data;
253 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
254 int ret;
255
256 if (!bb->vm_ops)
257 return 0;
258
259 if (!sysfs_get_active(attr_sd))
260 return -EINVAL;
261
262 ret = 0;
263 if (bb->vm_ops->set_policy)
264 ret = bb->vm_ops->set_policy(vma, new);
265
266 sysfs_put_active(attr_sd);
267 return ret;
268}
269
270static struct mempolicy *bin_get_policy(struct vm_area_struct *vma,
271 unsigned long addr)
272{
273 struct file *file = vma->vm_file;
274 struct bin_buffer *bb = file->private_data;
275 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
276 struct mempolicy *pol;
277
278 if (!bb->vm_ops)
279 return vma->vm_policy;
280
281 if (!sysfs_get_active(attr_sd))
282 return vma->vm_policy;
283
284 pol = vma->vm_policy;
285 if (bb->vm_ops->get_policy)
286 pol = bb->vm_ops->get_policy(vma, addr);
287
288 sysfs_put_active(attr_sd);
289 return pol;
290}
291
292static int bin_migrate(struct vm_area_struct *vma, const nodemask_t *from,
293 const nodemask_t *to, unsigned long flags)
294{
295 struct file *file = vma->vm_file;
296 struct bin_buffer *bb = file->private_data;
297 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
298 int ret;
299
300 if (!bb->vm_ops)
301 return 0;
302
303 if (!sysfs_get_active(attr_sd))
304 return 0;
305
306 ret = 0;
307 if (bb->vm_ops->migrate)
308 ret = bb->vm_ops->migrate(vma, from, to, flags);
309
310 sysfs_put_active(attr_sd);
311 return ret;
312}
313#endif
314
315static const struct vm_operations_struct bin_vm_ops = {
316 .open = bin_vma_open,
317 .fault = bin_fault,
318 .page_mkwrite = bin_page_mkwrite,
319 .access = bin_access,
320#ifdef CONFIG_NUMA
321 .set_policy = bin_set_policy,
322 .get_policy = bin_get_policy,
323 .migrate = bin_migrate,
324#endif
325};
326
327static int mmap(struct file *file, struct vm_area_struct *vma)
328{
329 struct bin_buffer *bb = file->private_data;
330 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
331 struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr;
332 struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
333 int rc;
334
335 mutex_lock(&bb->mutex);
336
337 /* need attr_sd for attr, its parent for kobj */
338 rc = -ENODEV;
339 if (!sysfs_get_active(attr_sd))
340 goto out_unlock;
341
342 rc = -EINVAL;
343 if (!attr->mmap)
344 goto out_put;
345
346 rc = attr->mmap(file, kobj, attr, vma);
347 if (rc)
348 goto out_put;
349
350 /*
351 * PowerPC's pci_mmap of legacy_mem uses shmem_zero_setup()
352 * to satisfy versions of X which crash if the mmap fails: that
353 * substitutes a new vm_file, and we don't then want bin_vm_ops.
354 */
355 if (vma->vm_file != file)
356 goto out_put;
357
358 rc = -EINVAL;
359 if (bb->mmapped && bb->vm_ops != vma->vm_ops)
360 goto out_put;
361
362 /*
363 * It is not possible to successfully wrap close.
364 * So error if someone is trying to use close.
365 */
366 rc = -EINVAL;
367 if (vma->vm_ops && vma->vm_ops->close)
368 goto out_put;
369
370 rc = 0;
371 bb->mmapped = 1;
372 bb->vm_ops = vma->vm_ops;
373 vma->vm_ops = &bin_vm_ops;
374out_put:
375 sysfs_put_active(attr_sd);
376out_unlock:
377 mutex_unlock(&bb->mutex);
378
379 return rc;
380}
381
382static int open(struct inode *inode, struct file *file)
383{
384 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
385 struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr;
386 struct bin_buffer *bb = NULL;
387 int error;
388
389 /* binary file operations requires both @sd and its parent */
390 if (!sysfs_get_active(attr_sd))
391 return -ENODEV;
392
393 error = -EACCES;
394 if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap))
395 goto err_out;
396 if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap))
397 goto err_out;
398
399 error = -ENOMEM;
400 bb = kzalloc(sizeof(*bb), GFP_KERNEL);
401 if (!bb)
402 goto err_out;
403
404 bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
405 if (!bb->buffer)
406 goto err_out;
407
408 mutex_init(&bb->mutex);
409 bb->file = file;
410 file->private_data = bb;
411
412 mutex_lock(&sysfs_bin_lock);
413 hlist_add_head(&bb->list, &attr_sd->s_bin_attr.buffers);
414 mutex_unlock(&sysfs_bin_lock);
415
416 /* open succeeded, put active references */
417 sysfs_put_active(attr_sd);
418 return 0;
419
420 err_out:
421 sysfs_put_active(attr_sd);
422 kfree(bb);
423 return error;
424}
425
426static int release(struct inode *inode, struct file *file)
427{
428 struct bin_buffer *bb = file->private_data;
429
430 mutex_lock(&sysfs_bin_lock);
431 hlist_del(&bb->list);
432 mutex_unlock(&sysfs_bin_lock);
433
434 kfree(bb->buffer);
435 kfree(bb);
436 return 0;
437}
438
439const struct file_operations bin_fops = {
440 .read = read,
441 .write = write,
442 .mmap = mmap,
443 .llseek = generic_file_llseek,
444 .open = open,
445 .release = release,
446};
447
448
449void unmap_bin_file(struct sysfs_dirent *attr_sd)
450{
451 struct bin_buffer *bb;
452
453 if (sysfs_type(attr_sd) != SYSFS_KOBJ_BIN_ATTR)
454 return;
455
456 mutex_lock(&sysfs_bin_lock);
457
458 hlist_for_each_entry(bb, &attr_sd->s_bin_attr.buffers, list) {
459 struct inode *inode = file_inode(bb->file);
460
461 unmap_mapping_range(inode->i_mapping, 0, 0, 1);
462 }
463
464 mutex_unlock(&sysfs_bin_lock);
465}
466
467/**
468 * sysfs_create_bin_file - create binary file for object.
469 * @kobj: object.
470 * @attr: attribute descriptor.
471 */
472int sysfs_create_bin_file(struct kobject *kobj,
473 const struct bin_attribute *attr)
474{
475 BUG_ON(!kobj || !kobj->sd || !attr);
476
477 return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
478}
479EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
480
481/**
482 * sysfs_remove_bin_file - remove binary file for object.
483 * @kobj: object.
484 * @attr: attribute descriptor.
485 */
486void sysfs_remove_bin_file(struct kobject *kobj,
487 const struct bin_attribute *attr)
488{
489 sysfs_hash_and_remove(kobj->sd, attr->attr.name, NULL);
490}
491EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index c4040ddb8308..f6025c81bfd5 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -596,7 +596,6 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
596 596
597 sysfs_deactivate(sd); 597 sysfs_deactivate(sd);
598 sysfs_unmap_bin_file(sd); 598 sysfs_unmap_bin_file(sd);
599 unmap_bin_file(sd);
600 sysfs_put(sd); 599 sysfs_put(sd);
601 } 600 }
602} 601}
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index 417d005955d9..5f7a955550de 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -987,6 +987,32 @@ void sysfs_remove_file_from_group(struct kobject *kobj,
987} 987}
988EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); 988EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);
989 989
990/**
991 * sysfs_create_bin_file - create binary file for object.
992 * @kobj: object.
993 * @attr: attribute descriptor.
994 */
995int sysfs_create_bin_file(struct kobject *kobj,
996 const struct bin_attribute *attr)
997{
998 BUG_ON(!kobj || !kobj->sd || !attr);
999
1000 return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
1001}
1002EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
1003
1004/**
1005 * sysfs_remove_bin_file - remove binary file for object.
1006 * @kobj: object.
1007 * @attr: attribute descriptor.
1008 */
1009void sysfs_remove_bin_file(struct kobject *kobj,
1010 const struct bin_attribute *attr)
1011{
1012 sysfs_hash_and_remove(kobj->sd, attr->attr.name, NULL);
1013}
1014EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
1015
990struct sysfs_schedule_callback_struct { 1016struct sysfs_schedule_callback_struct {
991 struct list_head workq_list; 1017 struct list_head workq_list;
992 struct kobject *kobj; 1018 struct kobject *kobj;
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index 63f755ef71dd..2cb1b6b8ccbc 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -260,7 +260,7 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
260 case SYSFS_KOBJ_BIN_ATTR: 260 case SYSFS_KOBJ_BIN_ATTR:
261 bin_attr = sd->s_bin_attr.bin_attr; 261 bin_attr = sd->s_bin_attr.bin_attr;
262 inode->i_size = bin_attr->size; 262 inode->i_size = bin_attr->size;
263 inode->i_fop = &bin_fops; 263 inode->i_fop = &sysfs_bin_operations;
264 break; 264 break;
265 case SYSFS_KOBJ_LINK: 265 case SYSFS_KOBJ_LINK:
266 inode->i_op = &sysfs_symlink_inode_operations; 266 inode->i_op = &sysfs_symlink_inode_operations;
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index c960d6272cf6..4e01d3b3909c 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -223,12 +223,6 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd,
223void sysfs_unmap_bin_file(struct sysfs_dirent *sd); 223void sysfs_unmap_bin_file(struct sysfs_dirent *sd);
224 224
225/* 225/*
226 * bin.c
227 */
228extern const struct file_operations bin_fops;
229void unmap_bin_file(struct sysfs_dirent *attr_sd);
230
231/*
232 * symlink.c 226 * symlink.c
233 */ 227 */
234extern const struct inode_operations sysfs_symlink_inode_operations; 228extern const struct inode_operations sysfs_symlink_inode_operations;