Blame view
fs/sysfs/bin.c
10.7 KB
1da177e4c
|
1 |
/* |
6d66f5cd2
|
2 |
* fs/sysfs/bin.c - sysfs binary file implementation |
1da177e4c
|
3 4 5 6 |
* * Copyright (c) 2003 Patrick Mochel * Copyright (c) 2003 Matthew Wilcox * Copyright (c) 2004 Silicon Graphics, Inc. |
6d66f5cd2
|
7 8 9 10 11 12 |
* Copyright (c) 2007 SUSE Linux Products GmbH * Copyright (c) 2007 Tejun Heo <teheo@suse.de> * * This file is released under the GPLv2. * * Please see Documentation/filesystems/sysfs.txt for more information. |
1da177e4c
|
13 14 15 16 17 18 |
*/ #undef DEBUG #include <linux/errno.h> #include <linux/fs.h> |
995982ca7
|
19 |
#include <linux/kernel.h> |
1da177e4c
|
20 21 22 |
#include <linux/kobject.h> #include <linux/module.h> #include <linux/slab.h> |
869512ab5
|
23 |
#include <linux/mutex.h> |
e0edd3c65
|
24 |
#include <linux/mm.h> |
1da177e4c
|
25 26 27 28 |
#include <asm/uaccess.h> #include "sysfs.h" |
e0edd3c65
|
29 30 31 32 33 34 35 36 |
/* * There's one bin_buffer for each open file. * * filp->private_data points to bin_buffer and * sysfs_dirent->s_bin_attr.buffers points to a the bin_buffer s * sysfs_dirent->s_bin_attr.buffers is protected by sysfs_bin_lock */ static DEFINE_MUTEX(sysfs_bin_lock); |
eb3616535
|
37 |
struct bin_buffer { |
e0edd3c65
|
38 39 40 |
struct mutex mutex; void *buffer; int mmapped; |
f0f37e2f7
|
41 |
const struct vm_operations_struct *vm_ops; |
e0edd3c65
|
42 43 |
struct file *file; struct hlist_node list; |
eb3616535
|
44 |
}; |
1da177e4c
|
45 |
static int |
2c3c8bea6
|
46 |
fill_read(struct file *file, char *buffer, loff_t off, size_t count) |
1da177e4c
|
47 |
{ |
2c3c8bea6
|
48 |
struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
b1fc3d614
|
49 50 |
struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr; struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; |
0ab66088c
|
51 52 53 |
int rc; /* need attr_sd for attr, its parent for kobj */ |
e72ceb8cc
|
54 |
if (!sysfs_get_active(attr_sd)) |
0ab66088c
|
55 |
return -ENODEV; |
1da177e4c
|
56 |
|
0ab66088c
|
57 58 |
rc = -EIO; if (attr->read) |
2c3c8bea6
|
59 |
rc = attr->read(file, kobj, attr, buffer, off, count); |
1da177e4c
|
60 |
|
e72ceb8cc
|
61 |
sysfs_put_active(attr_sd); |
0ab66088c
|
62 63 |
return rc; |
1da177e4c
|
64 65 66 |
} static ssize_t |
93e3cd827
|
67 |
read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) |
1da177e4c
|
68 |
{ |
eb3616535
|
69 |
struct bin_buffer *bb = file->private_data; |
2c3c8bea6
|
70 |
int size = file->f_path.dentry->d_inode->i_size; |
1da177e4c
|
71 |
loff_t offs = *off; |
93e3cd827
|
72 |
int count = min_t(size_t, bytes, PAGE_SIZE); |
b31ca3f5d
|
73 |
char *temp; |
1da177e4c
|
74 |
|
4503efd08
|
75 76 |
if (!bytes) return 0; |
1da177e4c
|
77 78 79 80 81 82 |
if (size) { if (offs > size) return 0; if (offs + count > size) count = size - offs; } |
b31ca3f5d
|
83 84 85 |
temp = kmalloc(count, GFP_KERNEL); if (!temp) return -ENOMEM; |
eb3616535
|
86 |
mutex_lock(&bb->mutex); |
2c3c8bea6
|
87 |
count = fill_read(file, bb->buffer, offs, count); |
b31ca3f5d
|
88 89 90 91 |
if (count < 0) { mutex_unlock(&bb->mutex); goto out_free; } |
1da177e4c
|
92 |
|
b31ca3f5d
|
93 94 95 96 97 |
memcpy(temp, bb->buffer, count); mutex_unlock(&bb->mutex); if (copy_to_user(userbuf, temp, count)) { |
eb3616535
|
98 |
count = -EFAULT; |
b31ca3f5d
|
99 |
goto out_free; |
eb3616535
|
100 |
} |
1da177e4c
|
101 |
|
93e3cd827
|
102 103 |
pr_debug("offs = %lld, *off = %lld, count = %d ", offs, *off, count); |
1da177e4c
|
104 105 |
*off = offs + count; |
b31ca3f5d
|
106 107 |
out_free: kfree(temp); |
1da177e4c
|
108 109 110 111 |
return count; } static int |
2c3c8bea6
|
112 |
flush_write(struct file *file, char *buffer, loff_t offset, size_t count) |
1da177e4c
|
113 |
{ |
2c3c8bea6
|
114 |
struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
b1fc3d614
|
115 116 |
struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr; struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; |
0ab66088c
|
117 118 119 |
int rc; /* need attr_sd for attr, its parent for kobj */ |
e72ceb8cc
|
120 |
if (!sysfs_get_active(attr_sd)) |
0ab66088c
|
121 |
return -ENODEV; |
1da177e4c
|
122 |
|
0ab66088c
|
123 124 |
rc = -EIO; if (attr->write) |
2c3c8bea6
|
125 |
rc = attr->write(file, kobj, attr, buffer, offset, count); |
1da177e4c
|
126 |
|
e72ceb8cc
|
127 |
sysfs_put_active(attr_sd); |
0ab66088c
|
128 129 |
return rc; |
1da177e4c
|
130 |
} |
93e3cd827
|
131 132 |
static ssize_t write(struct file *file, const char __user *userbuf, size_t bytes, loff_t *off) |
1da177e4c
|
133 |
{ |
eb3616535
|
134 |
struct bin_buffer *bb = file->private_data; |
2c3c8bea6
|
135 |
int size = file->f_path.dentry->d_inode->i_size; |
1da177e4c
|
136 |
loff_t offs = *off; |
93e3cd827
|
137 |
int count = min_t(size_t, bytes, PAGE_SIZE); |
b31ca3f5d
|
138 |
char *temp; |
1da177e4c
|
139 |
|
4503efd08
|
140 141 |
if (!bytes) return 0; |
1da177e4c
|
142 143 144 145 146 147 |
if (size) { if (offs > size) return 0; if (offs + count > size) count = size - offs; } |
1c8542c7b
|
148 149 150 |
temp = memdup_user(userbuf, count); if (IS_ERR(temp)) return PTR_ERR(temp); |
1da177e4c
|
151 |
|
b31ca3f5d
|
152 153 154 |
mutex_lock(&bb->mutex); memcpy(bb->buffer, temp, count); |
2c3c8bea6
|
155 |
count = flush_write(file, bb->buffer, offs, count); |
b31ca3f5d
|
156 |
mutex_unlock(&bb->mutex); |
1da177e4c
|
157 158 |
if (count > 0) *off = offs + count; |
eb3616535
|
159 |
|
d5ce5b40b
|
160 |
kfree(temp); |
1da177e4c
|
161 162 |
return count; } |
e0edd3c65
|
163 164 165 166 167 |
static void bin_vma_open(struct vm_area_struct *vma) { struct file *file = vma->vm_file; struct bin_buffer *bb = file->private_data; struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
38f49a513
|
168 |
if (!bb->vm_ops) |
e0edd3c65
|
169 |
return; |
e72ceb8cc
|
170 |
if (!sysfs_get_active(attr_sd)) |
e0edd3c65
|
171 |
return; |
38f49a513
|
172 173 |
if (bb->vm_ops->open) bb->vm_ops->open(vma); |
e0edd3c65
|
174 |
|
e72ceb8cc
|
175 |
sysfs_put_active(attr_sd); |
e0edd3c65
|
176 |
} |
e0edd3c65
|
177 178 179 180 181 182 |
static int bin_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct file *file = vma->vm_file; struct bin_buffer *bb = file->private_data; struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; int ret; |
38f49a513
|
183 |
if (!bb->vm_ops) |
e0edd3c65
|
184 |
return VM_FAULT_SIGBUS; |
e72ceb8cc
|
185 |
if (!sysfs_get_active(attr_sd)) |
e0edd3c65
|
186 |
return VM_FAULT_SIGBUS; |
38f49a513
|
187 188 189 |
ret = VM_FAULT_SIGBUS; if (bb->vm_ops->fault) ret = bb->vm_ops->fault(vma, vmf); |
e0edd3c65
|
190 |
|
e72ceb8cc
|
191 |
sysfs_put_active(attr_sd); |
e0edd3c65
|
192 193 |
return ret; } |
851a039cc
|
194 |
static int bin_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) |
e0edd3c65
|
195 196 197 198 199 |
{ struct file *file = vma->vm_file; struct bin_buffer *bb = file->private_data; struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; int ret; |
095160aee
|
200 |
if (!bb->vm_ops) |
851a039cc
|
201 |
return VM_FAULT_SIGBUS; |
e0edd3c65
|
202 |
|
e72ceb8cc
|
203 |
if (!sysfs_get_active(attr_sd)) |
851a039cc
|
204 |
return VM_FAULT_SIGBUS; |
e0edd3c65
|
205 |
|
38f49a513
|
206 207 208 |
ret = 0; if (bb->vm_ops->page_mkwrite) ret = bb->vm_ops->page_mkwrite(vma, vmf); |
e0edd3c65
|
209 |
|
e72ceb8cc
|
210 |
sysfs_put_active(attr_sd); |
e0edd3c65
|
211 212 213 214 215 216 217 218 219 220 |
return ret; } static int bin_access(struct vm_area_struct *vma, unsigned long addr, void *buf, int len, int write) { struct file *file = vma->vm_file; struct bin_buffer *bb = file->private_data; struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; int ret; |
38f49a513
|
221 |
if (!bb->vm_ops) |
e0edd3c65
|
222 |
return -EINVAL; |
e72ceb8cc
|
223 |
if (!sysfs_get_active(attr_sd)) |
e0edd3c65
|
224 |
return -EINVAL; |
38f49a513
|
225 226 227 |
ret = -EINVAL; if (bb->vm_ops->access) ret = bb->vm_ops->access(vma, addr, buf, len, write); |
e0edd3c65
|
228 |
|
e72ceb8cc
|
229 |
sysfs_put_active(attr_sd); |
e0edd3c65
|
230 231 |
return ret; } |
095160aee
|
232 233 234 235 236 237 238 |
#ifdef CONFIG_NUMA static int bin_set_policy(struct vm_area_struct *vma, struct mempolicy *new) { struct file *file = vma->vm_file; struct bin_buffer *bb = file->private_data; struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; int ret; |
38f49a513
|
239 |
if (!bb->vm_ops) |
095160aee
|
240 |
return 0; |
e72ceb8cc
|
241 |
if (!sysfs_get_active(attr_sd)) |
095160aee
|
242 |
return -EINVAL; |
38f49a513
|
243 244 245 |
ret = 0; if (bb->vm_ops->set_policy) ret = bb->vm_ops->set_policy(vma, new); |
095160aee
|
246 |
|
e72ceb8cc
|
247 |
sysfs_put_active(attr_sd); |
095160aee
|
248 249 250 251 252 253 254 255 256 257 |
return ret; } static struct mempolicy *bin_get_policy(struct vm_area_struct *vma, unsigned long addr) { struct file *file = vma->vm_file; struct bin_buffer *bb = file->private_data; struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct mempolicy *pol; |
38f49a513
|
258 |
if (!bb->vm_ops) |
095160aee
|
259 |
return vma->vm_policy; |
e72ceb8cc
|
260 |
if (!sysfs_get_active(attr_sd)) |
095160aee
|
261 |
return vma->vm_policy; |
38f49a513
|
262 263 264 |
pol = vma->vm_policy; if (bb->vm_ops->get_policy) pol = bb->vm_ops->get_policy(vma, addr); |
095160aee
|
265 |
|
e72ceb8cc
|
266 |
sysfs_put_active(attr_sd); |
095160aee
|
267 268 269 270 271 272 273 274 275 276 |
return pol; } static int bin_migrate(struct vm_area_struct *vma, const nodemask_t *from, const nodemask_t *to, unsigned long flags) { struct file *file = vma->vm_file; struct bin_buffer *bb = file->private_data; struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; int ret; |
38f49a513
|
277 |
if (!bb->vm_ops) |
095160aee
|
278 |
return 0; |
e72ceb8cc
|
279 |
if (!sysfs_get_active(attr_sd)) |
095160aee
|
280 |
return 0; |
38f49a513
|
281 282 283 |
ret = 0; if (bb->vm_ops->migrate) ret = bb->vm_ops->migrate(vma, from, to, flags); |
095160aee
|
284 |
|
e72ceb8cc
|
285 |
sysfs_put_active(attr_sd); |
095160aee
|
286 287 288 |
return ret; } #endif |
f0f37e2f7
|
289 |
static const struct vm_operations_struct bin_vm_ops = { |
e0edd3c65
|
290 |
.open = bin_vma_open, |
e0edd3c65
|
291 292 293 |
.fault = bin_fault, .page_mkwrite = bin_page_mkwrite, .access = bin_access, |
095160aee
|
294 295 296 297 298 |
#ifdef CONFIG_NUMA .set_policy = bin_set_policy, .get_policy = bin_get_policy, .migrate = bin_migrate, #endif |
e0edd3c65
|
299 |
}; |
1da177e4c
|
300 301 |
static int mmap(struct file *file, struct vm_area_struct *vma) { |
eb3616535
|
302 |
struct bin_buffer *bb = file->private_data; |
3e5190380
|
303 |
struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
b1fc3d614
|
304 305 |
struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr; struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; |
eb3616535
|
306 |
int rc; |
1da177e4c
|
307 |
|
eb3616535
|
308 |
mutex_lock(&bb->mutex); |
0ab66088c
|
309 310 |
/* need attr_sd for attr, its parent for kobj */ |
e0edd3c65
|
311 |
rc = -ENODEV; |
e72ceb8cc
|
312 |
if (!sysfs_get_active(attr_sd)) |
e0edd3c65
|
313 |
goto out_unlock; |
0ab66088c
|
314 315 |
rc = -EINVAL; |
e0edd3c65
|
316 317 |
if (!attr->mmap) goto out_put; |
0ab66088c
|
318 |
|
2c3c8bea6
|
319 |
rc = attr->mmap(file, kobj, attr, vma); |
e0edd3c65
|
320 321 |
if (rc) goto out_put; |
0ab66088c
|
322 |
|
095160aee
|
323 324 325 326 327 328 |
/* * PowerPC's pci_mmap of legacy_mem uses shmem_zero_setup() * to satisfy versions of X which crash if the mmap fails: that * substitutes a new vm_file, and we don't then want bin_vm_ops. */ if (vma->vm_file != file) |
e0edd3c65
|
329 |
goto out_put; |
e0edd3c65
|
330 |
rc = -EINVAL; |
095160aee
|
331 |
if (bb->mmapped && bb->vm_ops != vma->vm_ops) |
e0edd3c65
|
332 |
goto out_put; |
e0edd3c65
|
333 |
|
a6849fa1f
|
334 335 336 337 338 339 340 |
/* * It is not possible to successfully wrap close. * So error if someone is trying to use close. */ rc = -EINVAL; if (vma->vm_ops && vma->vm_ops->close) goto out_put; |
e0edd3c65
|
341 342 |
rc = 0; bb->mmapped = 1; |
095160aee
|
343 344 |
bb->vm_ops = vma->vm_ops; vma->vm_ops = &bin_vm_ops; |
e0edd3c65
|
345 |
out_put: |
e72ceb8cc
|
346 |
sysfs_put_active(attr_sd); |
e0edd3c65
|
347 |
out_unlock: |
eb3616535
|
348 349 350 |
mutex_unlock(&bb->mutex); return rc; |
1da177e4c
|
351 352 353 354 |
} static int open(struct inode * inode, struct file * file) { |
3e5190380
|
355 |
struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
b1fc3d614
|
356 |
struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr; |
eb3616535
|
357 |
struct bin_buffer *bb = NULL; |
0ab66088c
|
358 |
int error; |
1da177e4c
|
359 |
|
078ce6409
|
360 |
/* binary file operations requires both @sd and its parent */ |
e72ceb8cc
|
361 |
if (!sysfs_get_active(attr_sd)) |
0ab66088c
|
362 |
return -ENODEV; |
1da177e4c
|
363 |
|
1da177e4c
|
364 365 |
error = -EACCES; if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap)) |
7b595756e
|
366 |
goto err_out; |
1da177e4c
|
367 |
if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap)) |
7b595756e
|
368 |
goto err_out; |
1da177e4c
|
369 370 |
error = -ENOMEM; |
eb3616535
|
371 372 |
bb = kzalloc(sizeof(*bb), GFP_KERNEL); if (!bb) |
7b595756e
|
373 |
goto err_out; |
1da177e4c
|
374 |
|
eb3616535
|
375 376 |
bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!bb->buffer) |
7b595756e
|
377 |
goto err_out; |
eb3616535
|
378 379 |
mutex_init(&bb->mutex); |
e0edd3c65
|
380 |
bb->file = file; |
eb3616535
|
381 |
file->private_data = bb; |
e0edd3c65
|
382 383 384 |
mutex_lock(&sysfs_bin_lock); hlist_add_head(&bb->list, &attr_sd->s_bin_attr.buffers); mutex_unlock(&sysfs_bin_lock); |
078ce6409
|
385 |
/* open succeeded, put active references */ |
e72ceb8cc
|
386 |
sysfs_put_active(attr_sd); |
0ab66088c
|
387 |
return 0; |
1da177e4c
|
388 |
|
7b595756e
|
389 |
err_out: |
e72ceb8cc
|
390 |
sysfs_put_active(attr_sd); |
0ab66088c
|
391 |
kfree(bb); |
1da177e4c
|
392 393 394 395 396 |
return error; } static int release(struct inode * inode, struct file * file) { |
eb3616535
|
397 |
struct bin_buffer *bb = file->private_data; |
1da177e4c
|
398 |
|
e0edd3c65
|
399 400 401 |
mutex_lock(&sysfs_bin_lock); hlist_del(&bb->list); mutex_unlock(&sysfs_bin_lock); |
eb3616535
|
402 403 |
kfree(bb->buffer); kfree(bb); |
1da177e4c
|
404 405 |
return 0; } |
4b6f5d20b
|
406 |
const struct file_operations bin_fops = { |
1da177e4c
|
407 408 409 410 411 412 413 |
.read = read, .write = write, .mmap = mmap, .llseek = generic_file_llseek, .open = open, .release = release, }; |
e0edd3c65
|
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 |
void unmap_bin_file(struct sysfs_dirent *attr_sd) { struct bin_buffer *bb; struct hlist_node *tmp; if (sysfs_type(attr_sd) != SYSFS_KOBJ_BIN_ATTR) return; mutex_lock(&sysfs_bin_lock); hlist_for_each_entry(bb, tmp, &attr_sd->s_bin_attr.buffers, list) { struct inode *inode = bb->file->f_path.dentry->d_inode; unmap_mapping_range(inode->i_mapping, 0, 0, 1); } mutex_unlock(&sysfs_bin_lock); } |
1da177e4c
|
433 434 435 436 |
/** * sysfs_create_bin_file - create binary file for object. * @kobj: object. * @attr: attribute descriptor. |
1da177e4c
|
437 |
*/ |
66ecb92be
|
438 439 |
int sysfs_create_bin_file(struct kobject *kobj, const struct bin_attribute *attr) |
1da177e4c
|
440 |
{ |
608e266a2
|
441 |
BUG_ON(!kobj || !kobj->sd || !attr); |
1da177e4c
|
442 |
|
608e266a2
|
443 |
return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR); |
1da177e4c
|
444 445 446 447 448 449 450 |
} /** * sysfs_remove_bin_file - remove binary file for object. * @kobj: object. * @attr: attribute descriptor. |
1da177e4c
|
451 |
*/ |
66ecb92be
|
452 453 |
void sysfs_remove_bin_file(struct kobject *kobj, const struct bin_attribute *attr) |
1da177e4c
|
454 |
{ |
3ff195b01
|
455 |
sysfs_hash_and_remove(kobj->sd, NULL, attr->attr.name); |
1da177e4c
|
456 457 458 459 |
} EXPORT_SYMBOL_GPL(sysfs_create_bin_file); EXPORT_SYMBOL_GPL(sysfs_remove_bin_file); |