at v3.4 506 lines 11 kB view raw
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 26#include <asm/uaccess.h> 27 28#include "sysfs.h" 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 */ 37static DEFINE_MUTEX(sysfs_bin_lock); 38 39struct bin_buffer { 40 struct mutex mutex; 41 void *buffer; 42 int mmapped; 43 const struct vm_operations_struct *vm_ops; 44 struct file *file; 45 struct hlist_node list; 46}; 47 48static int 49fill_read(struct file *file, char *buffer, loff_t off, size_t count) 50{ 51 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 52 struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr; 53 struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; 54 int rc; 55 56 /* need attr_sd for attr, its parent for kobj */ 57 if (!sysfs_get_active(attr_sd)) 58 return -ENODEV; 59 60 rc = -EIO; 61 if (attr->read) 62 rc = attr->read(file, kobj, attr, buffer, off, count); 63 64 sysfs_put_active(attr_sd); 65 66 return rc; 67} 68 69static ssize_t 70read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) 71{ 72 struct bin_buffer *bb = file->private_data; 73 int size = file->f_path.dentry->d_inode->i_size; 74 loff_t offs = *off; 75 int count = min_t(size_t, bytes, PAGE_SIZE); 76 char *temp; 77 78 if (!bytes) 79 return 0; 80 81 if (size) { 82 if (offs > size) 83 return 0; 84 if (offs + count > size) 85 count = size - offs; 86 } 87 88 temp = kmalloc(count, GFP_KERNEL); 89 if (!temp) 90 return -ENOMEM; 91 92 mutex_lock(&bb->mutex); 93 94 count = fill_read(file, bb->buffer, offs, count); 95 if (count < 0) { 96 mutex_unlock(&bb->mutex); 97 goto out_free; 98 } 99 100 memcpy(temp, bb->buffer, count); 101 102 mutex_unlock(&bb->mutex); 103 104 if (copy_to_user(userbuf, temp, count)) { 105 count = -EFAULT; 106 goto out_free; 107 } 108 109 pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count); 110 111 *off = offs + count; 112 113 out_free: 114 kfree(temp); 115 return count; 116} 117 118static int 119flush_write(struct file *file, char *buffer, loff_t offset, size_t count) 120{ 121 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 122 struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr; 123 struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; 124 int rc; 125 126 /* need attr_sd for attr, its parent for kobj */ 127 if (!sysfs_get_active(attr_sd)) 128 return -ENODEV; 129 130 rc = -EIO; 131 if (attr->write) 132 rc = attr->write(file, kobj, attr, buffer, offset, count); 133 134 sysfs_put_active(attr_sd); 135 136 return rc; 137} 138 139static ssize_t write(struct file *file, const char __user *userbuf, 140 size_t bytes, loff_t *off) 141{ 142 struct bin_buffer *bb = file->private_data; 143 int size = file->f_path.dentry->d_inode->i_size; 144 loff_t offs = *off; 145 int count = min_t(size_t, bytes, PAGE_SIZE); 146 char *temp; 147 148 if (!bytes) 149 return 0; 150 151 if (size) { 152 if (offs > size) 153 return 0; 154 if (offs + count > size) 155 count = size - offs; 156 } 157 158 temp = memdup_user(userbuf, count); 159 if (IS_ERR(temp)) 160 return PTR_ERR(temp); 161 162 mutex_lock(&bb->mutex); 163 164 memcpy(bb->buffer, temp, count); 165 166 count = flush_write(file, bb->buffer, offs, count); 167 mutex_unlock(&bb->mutex); 168 169 if (count > 0) 170 *off = offs + count; 171 172 kfree(temp); 173 return count; 174} 175 176static void bin_vma_open(struct vm_area_struct *vma) 177{ 178 struct file *file = vma->vm_file; 179 struct bin_buffer *bb = file->private_data; 180 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 181 182 if (!bb->vm_ops) 183 return; 184 185 if (!sysfs_get_active(attr_sd)) 186 return; 187 188 if (bb->vm_ops->open) 189 bb->vm_ops->open(vma); 190 191 sysfs_put_active(attr_sd); 192} 193 194static int bin_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 195{ 196 struct file *file = vma->vm_file; 197 struct bin_buffer *bb = file->private_data; 198 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 199 int ret; 200 201 if (!bb->vm_ops) 202 return VM_FAULT_SIGBUS; 203 204 if (!sysfs_get_active(attr_sd)) 205 return VM_FAULT_SIGBUS; 206 207 ret = VM_FAULT_SIGBUS; 208 if (bb->vm_ops->fault) 209 ret = bb->vm_ops->fault(vma, vmf); 210 211 sysfs_put_active(attr_sd); 212 return ret; 213} 214 215static int bin_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) 216{ 217 struct file *file = vma->vm_file; 218 struct bin_buffer *bb = file->private_data; 219 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 220 int ret; 221 222 if (!bb->vm_ops) 223 return VM_FAULT_SIGBUS; 224 225 if (!sysfs_get_active(attr_sd)) 226 return VM_FAULT_SIGBUS; 227 228 ret = 0; 229 if (bb->vm_ops->page_mkwrite) 230 ret = bb->vm_ops->page_mkwrite(vma, vmf); 231 232 sysfs_put_active(attr_sd); 233 return ret; 234} 235 236static int bin_access(struct vm_area_struct *vma, unsigned long addr, 237 void *buf, int len, int write) 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 (!sysfs_get_active(attr_sd)) 248 return -EINVAL; 249 250 ret = -EINVAL; 251 if (bb->vm_ops->access) 252 ret = bb->vm_ops->access(vma, addr, buf, len, write); 253 254 sysfs_put_active(attr_sd); 255 return ret; 256} 257 258#ifdef CONFIG_NUMA 259static int bin_set_policy(struct vm_area_struct *vma, struct mempolicy *new) 260{ 261 struct file *file = vma->vm_file; 262 struct bin_buffer *bb = file->private_data; 263 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 264 int ret; 265 266 if (!bb->vm_ops) 267 return 0; 268 269 if (!sysfs_get_active(attr_sd)) 270 return -EINVAL; 271 272 ret = 0; 273 if (bb->vm_ops->set_policy) 274 ret = bb->vm_ops->set_policy(vma, new); 275 276 sysfs_put_active(attr_sd); 277 return ret; 278} 279 280static struct mempolicy *bin_get_policy(struct vm_area_struct *vma, 281 unsigned long addr) 282{ 283 struct file *file = vma->vm_file; 284 struct bin_buffer *bb = file->private_data; 285 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 286 struct mempolicy *pol; 287 288 if (!bb->vm_ops) 289 return vma->vm_policy; 290 291 if (!sysfs_get_active(attr_sd)) 292 return vma->vm_policy; 293 294 pol = vma->vm_policy; 295 if (bb->vm_ops->get_policy) 296 pol = bb->vm_ops->get_policy(vma, addr); 297 298 sysfs_put_active(attr_sd); 299 return pol; 300} 301 302static int bin_migrate(struct vm_area_struct *vma, const nodemask_t *from, 303 const nodemask_t *to, unsigned long flags) 304{ 305 struct file *file = vma->vm_file; 306 struct bin_buffer *bb = file->private_data; 307 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 308 int ret; 309 310 if (!bb->vm_ops) 311 return 0; 312 313 if (!sysfs_get_active(attr_sd)) 314 return 0; 315 316 ret = 0; 317 if (bb->vm_ops->migrate) 318 ret = bb->vm_ops->migrate(vma, from, to, flags); 319 320 sysfs_put_active(attr_sd); 321 return ret; 322} 323#endif 324 325static const struct vm_operations_struct bin_vm_ops = { 326 .open = bin_vma_open, 327 .fault = bin_fault, 328 .page_mkwrite = bin_page_mkwrite, 329 .access = bin_access, 330#ifdef CONFIG_NUMA 331 .set_policy = bin_set_policy, 332 .get_policy = bin_get_policy, 333 .migrate = bin_migrate, 334#endif 335}; 336 337static int mmap(struct file *file, struct vm_area_struct *vma) 338{ 339 struct bin_buffer *bb = file->private_data; 340 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 341 struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr; 342 struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; 343 int rc; 344 345 mutex_lock(&bb->mutex); 346 347 /* need attr_sd for attr, its parent for kobj */ 348 rc = -ENODEV; 349 if (!sysfs_get_active(attr_sd)) 350 goto out_unlock; 351 352 rc = -EINVAL; 353 if (!attr->mmap) 354 goto out_put; 355 356 rc = attr->mmap(file, kobj, attr, vma); 357 if (rc) 358 goto out_put; 359 360 /* 361 * PowerPC's pci_mmap of legacy_mem uses shmem_zero_setup() 362 * to satisfy versions of X which crash if the mmap fails: that 363 * substitutes a new vm_file, and we don't then want bin_vm_ops. 364 */ 365 if (vma->vm_file != file) 366 goto out_put; 367 368 rc = -EINVAL; 369 if (bb->mmapped && bb->vm_ops != vma->vm_ops) 370 goto out_put; 371 372 /* 373 * It is not possible to successfully wrap close. 374 * So error if someone is trying to use close. 375 */ 376 rc = -EINVAL; 377 if (vma->vm_ops && vma->vm_ops->close) 378 goto out_put; 379 380 rc = 0; 381 bb->mmapped = 1; 382 bb->vm_ops = vma->vm_ops; 383 vma->vm_ops = &bin_vm_ops; 384out_put: 385 sysfs_put_active(attr_sd); 386out_unlock: 387 mutex_unlock(&bb->mutex); 388 389 return rc; 390} 391 392static int open(struct inode * inode, struct file * file) 393{ 394 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 395 struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr; 396 struct bin_buffer *bb = NULL; 397 int error; 398 399 /* binary file operations requires both @sd and its parent */ 400 if (!sysfs_get_active(attr_sd)) 401 return -ENODEV; 402 403 error = -EACCES; 404 if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap)) 405 goto err_out; 406 if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap)) 407 goto err_out; 408 409 error = -ENOMEM; 410 bb = kzalloc(sizeof(*bb), GFP_KERNEL); 411 if (!bb) 412 goto err_out; 413 414 bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); 415 if (!bb->buffer) 416 goto err_out; 417 418 mutex_init(&bb->mutex); 419 bb->file = file; 420 file->private_data = bb; 421 422 mutex_lock(&sysfs_bin_lock); 423 hlist_add_head(&bb->list, &attr_sd->s_bin_attr.buffers); 424 mutex_unlock(&sysfs_bin_lock); 425 426 /* open succeeded, put active references */ 427 sysfs_put_active(attr_sd); 428 return 0; 429 430 err_out: 431 sysfs_put_active(attr_sd); 432 kfree(bb); 433 return error; 434} 435 436static int release(struct inode * inode, struct file * file) 437{ 438 struct bin_buffer *bb = file->private_data; 439 440 mutex_lock(&sysfs_bin_lock); 441 hlist_del(&bb->list); 442 mutex_unlock(&sysfs_bin_lock); 443 444 kfree(bb->buffer); 445 kfree(bb); 446 return 0; 447} 448 449const struct file_operations bin_fops = { 450 .read = read, 451 .write = write, 452 .mmap = mmap, 453 .llseek = generic_file_llseek, 454 .open = open, 455 .release = release, 456}; 457 458 459void unmap_bin_file(struct sysfs_dirent *attr_sd) 460{ 461 struct bin_buffer *bb; 462 struct hlist_node *tmp; 463 464 if (sysfs_type(attr_sd) != SYSFS_KOBJ_BIN_ATTR) 465 return; 466 467 mutex_lock(&sysfs_bin_lock); 468 469 hlist_for_each_entry(bb, tmp, &attr_sd->s_bin_attr.buffers, list) { 470 struct inode *inode = bb->file->f_path.dentry->d_inode; 471 472 unmap_mapping_range(inode->i_mapping, 0, 0, 1); 473 } 474 475 mutex_unlock(&sysfs_bin_lock); 476} 477 478/** 479 * sysfs_create_bin_file - create binary file for object. 480 * @kobj: object. 481 * @attr: attribute descriptor. 482 */ 483 484int sysfs_create_bin_file(struct kobject *kobj, 485 const struct bin_attribute *attr) 486{ 487 BUG_ON(!kobj || !kobj->sd || !attr); 488 489 return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR); 490} 491 492 493/** 494 * sysfs_remove_bin_file - remove binary file for object. 495 * @kobj: object. 496 * @attr: attribute descriptor. 497 */ 498 499void sysfs_remove_bin_file(struct kobject *kobj, 500 const struct bin_attribute *attr) 501{ 502 sysfs_hash_and_remove(kobj->sd, NULL, attr->attr.name); 503} 504 505EXPORT_SYMBOL_GPL(sysfs_create_bin_file); 506EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);