at v3.7 508 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 else 232 file_update_time(file); 233 234 sysfs_put_active(attr_sd); 235 return ret; 236} 237 238static int bin_access(struct vm_area_struct *vma, unsigned long addr, 239 void *buf, int len, int write) 240{ 241 struct file *file = vma->vm_file; 242 struct bin_buffer *bb = file->private_data; 243 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 244 int ret; 245 246 if (!bb->vm_ops) 247 return -EINVAL; 248 249 if (!sysfs_get_active(attr_sd)) 250 return -EINVAL; 251 252 ret = -EINVAL; 253 if (bb->vm_ops->access) 254 ret = bb->vm_ops->access(vma, addr, buf, len, write); 255 256 sysfs_put_active(attr_sd); 257 return ret; 258} 259 260#ifdef CONFIG_NUMA 261static int bin_set_policy(struct vm_area_struct *vma, struct mempolicy *new) 262{ 263 struct file *file = vma->vm_file; 264 struct bin_buffer *bb = file->private_data; 265 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 266 int ret; 267 268 if (!bb->vm_ops) 269 return 0; 270 271 if (!sysfs_get_active(attr_sd)) 272 return -EINVAL; 273 274 ret = 0; 275 if (bb->vm_ops->set_policy) 276 ret = bb->vm_ops->set_policy(vma, new); 277 278 sysfs_put_active(attr_sd); 279 return ret; 280} 281 282static struct mempolicy *bin_get_policy(struct vm_area_struct *vma, 283 unsigned long addr) 284{ 285 struct file *file = vma->vm_file; 286 struct bin_buffer *bb = file->private_data; 287 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 288 struct mempolicy *pol; 289 290 if (!bb->vm_ops) 291 return vma->vm_policy; 292 293 if (!sysfs_get_active(attr_sd)) 294 return vma->vm_policy; 295 296 pol = vma->vm_policy; 297 if (bb->vm_ops->get_policy) 298 pol = bb->vm_ops->get_policy(vma, addr); 299 300 sysfs_put_active(attr_sd); 301 return pol; 302} 303 304static int bin_migrate(struct vm_area_struct *vma, const nodemask_t *from, 305 const nodemask_t *to, unsigned long flags) 306{ 307 struct file *file = vma->vm_file; 308 struct bin_buffer *bb = file->private_data; 309 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 310 int ret; 311 312 if (!bb->vm_ops) 313 return 0; 314 315 if (!sysfs_get_active(attr_sd)) 316 return 0; 317 318 ret = 0; 319 if (bb->vm_ops->migrate) 320 ret = bb->vm_ops->migrate(vma, from, to, flags); 321 322 sysfs_put_active(attr_sd); 323 return ret; 324} 325#endif 326 327static const struct vm_operations_struct bin_vm_ops = { 328 .open = bin_vma_open, 329 .fault = bin_fault, 330 .page_mkwrite = bin_page_mkwrite, 331 .access = bin_access, 332#ifdef CONFIG_NUMA 333 .set_policy = bin_set_policy, 334 .get_policy = bin_get_policy, 335 .migrate = bin_migrate, 336#endif 337}; 338 339static int mmap(struct file *file, struct vm_area_struct *vma) 340{ 341 struct bin_buffer *bb = file->private_data; 342 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 343 struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr; 344 struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; 345 int rc; 346 347 mutex_lock(&bb->mutex); 348 349 /* need attr_sd for attr, its parent for kobj */ 350 rc = -ENODEV; 351 if (!sysfs_get_active(attr_sd)) 352 goto out_unlock; 353 354 rc = -EINVAL; 355 if (!attr->mmap) 356 goto out_put; 357 358 rc = attr->mmap(file, kobj, attr, vma); 359 if (rc) 360 goto out_put; 361 362 /* 363 * PowerPC's pci_mmap of legacy_mem uses shmem_zero_setup() 364 * to satisfy versions of X which crash if the mmap fails: that 365 * substitutes a new vm_file, and we don't then want bin_vm_ops. 366 */ 367 if (vma->vm_file != file) 368 goto out_put; 369 370 rc = -EINVAL; 371 if (bb->mmapped && bb->vm_ops != vma->vm_ops) 372 goto out_put; 373 374 /* 375 * It is not possible to successfully wrap close. 376 * So error if someone is trying to use close. 377 */ 378 rc = -EINVAL; 379 if (vma->vm_ops && vma->vm_ops->close) 380 goto out_put; 381 382 rc = 0; 383 bb->mmapped = 1; 384 bb->vm_ops = vma->vm_ops; 385 vma->vm_ops = &bin_vm_ops; 386out_put: 387 sysfs_put_active(attr_sd); 388out_unlock: 389 mutex_unlock(&bb->mutex); 390 391 return rc; 392} 393 394static int open(struct inode * inode, struct file * file) 395{ 396 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 397 struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr; 398 struct bin_buffer *bb = NULL; 399 int error; 400 401 /* binary file operations requires both @sd and its parent */ 402 if (!sysfs_get_active(attr_sd)) 403 return -ENODEV; 404 405 error = -EACCES; 406 if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap)) 407 goto err_out; 408 if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap)) 409 goto err_out; 410 411 error = -ENOMEM; 412 bb = kzalloc(sizeof(*bb), GFP_KERNEL); 413 if (!bb) 414 goto err_out; 415 416 bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); 417 if (!bb->buffer) 418 goto err_out; 419 420 mutex_init(&bb->mutex); 421 bb->file = file; 422 file->private_data = bb; 423 424 mutex_lock(&sysfs_bin_lock); 425 hlist_add_head(&bb->list, &attr_sd->s_bin_attr.buffers); 426 mutex_unlock(&sysfs_bin_lock); 427 428 /* open succeeded, put active references */ 429 sysfs_put_active(attr_sd); 430 return 0; 431 432 err_out: 433 sysfs_put_active(attr_sd); 434 kfree(bb); 435 return error; 436} 437 438static int release(struct inode * inode, struct file * file) 439{ 440 struct bin_buffer *bb = file->private_data; 441 442 mutex_lock(&sysfs_bin_lock); 443 hlist_del(&bb->list); 444 mutex_unlock(&sysfs_bin_lock); 445 446 kfree(bb->buffer); 447 kfree(bb); 448 return 0; 449} 450 451const struct file_operations bin_fops = { 452 .read = read, 453 .write = write, 454 .mmap = mmap, 455 .llseek = generic_file_llseek, 456 .open = open, 457 .release = release, 458}; 459 460 461void unmap_bin_file(struct sysfs_dirent *attr_sd) 462{ 463 struct bin_buffer *bb; 464 struct hlist_node *tmp; 465 466 if (sysfs_type(attr_sd) != SYSFS_KOBJ_BIN_ATTR) 467 return; 468 469 mutex_lock(&sysfs_bin_lock); 470 471 hlist_for_each_entry(bb, tmp, &attr_sd->s_bin_attr.buffers, list) { 472 struct inode *inode = bb->file->f_path.dentry->d_inode; 473 474 unmap_mapping_range(inode->i_mapping, 0, 0, 1); 475 } 476 477 mutex_unlock(&sysfs_bin_lock); 478} 479 480/** 481 * sysfs_create_bin_file - create binary file for object. 482 * @kobj: object. 483 * @attr: attribute descriptor. 484 */ 485 486int sysfs_create_bin_file(struct kobject *kobj, 487 const struct bin_attribute *attr) 488{ 489 BUG_ON(!kobj || !kobj->sd || !attr); 490 491 return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR); 492} 493 494 495/** 496 * sysfs_remove_bin_file - remove binary file for object. 497 * @kobj: object. 498 * @attr: attribute descriptor. 499 */ 500 501void sysfs_remove_bin_file(struct kobject *kobj, 502 const struct bin_attribute *attr) 503{ 504 sysfs_hash_and_remove(kobj->sd, NULL, attr->attr.name); 505} 506 507EXPORT_SYMBOL_GPL(sysfs_create_bin_file); 508EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);