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 || !bb->vm_ops->open) 183 return; 184 185 if (!sysfs_get_active(attr_sd)) 186 return; 187 188 bb->vm_ops->open(vma); 189 190 sysfs_put_active(attr_sd); 191} 192 193static void bin_vma_close(struct vm_area_struct *vma) 194{ 195 struct file *file = vma->vm_file; 196 struct bin_buffer *bb = file->private_data; 197 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 198 199 if (!bb->vm_ops || !bb->vm_ops->close) 200 return; 201 202 if (!sysfs_get_active(attr_sd)) 203 return; 204 205 bb->vm_ops->close(vma); 206 207 sysfs_put_active(attr_sd); 208} 209 210static int bin_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 211{ 212 struct file *file = vma->vm_file; 213 struct bin_buffer *bb = file->private_data; 214 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 215 int ret; 216 217 if (!bb->vm_ops || !bb->vm_ops->fault) 218 return VM_FAULT_SIGBUS; 219 220 if (!sysfs_get_active(attr_sd)) 221 return VM_FAULT_SIGBUS; 222 223 ret = bb->vm_ops->fault(vma, vmf); 224 225 sysfs_put_active(attr_sd); 226 return ret; 227} 228 229static int bin_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) 230{ 231 struct file *file = vma->vm_file; 232 struct bin_buffer *bb = file->private_data; 233 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 234 int ret; 235 236 if (!bb->vm_ops) 237 return VM_FAULT_SIGBUS; 238 239 if (!bb->vm_ops->page_mkwrite) 240 return 0; 241 242 if (!sysfs_get_active(attr_sd)) 243 return VM_FAULT_SIGBUS; 244 245 ret = bb->vm_ops->page_mkwrite(vma, vmf); 246 247 sysfs_put_active(attr_sd); 248 return ret; 249} 250 251static int bin_access(struct vm_area_struct *vma, unsigned long addr, 252 void *buf, int len, int write) 253{ 254 struct file *file = vma->vm_file; 255 struct bin_buffer *bb = file->private_data; 256 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 257 int ret; 258 259 if (!bb->vm_ops || !bb->vm_ops->access) 260 return -EINVAL; 261 262 if (!sysfs_get_active(attr_sd)) 263 return -EINVAL; 264 265 ret = bb->vm_ops->access(vma, addr, buf, len, write); 266 267 sysfs_put_active(attr_sd); 268 return ret; 269} 270 271#ifdef CONFIG_NUMA 272static int bin_set_policy(struct vm_area_struct *vma, struct mempolicy *new) 273{ 274 struct file *file = vma->vm_file; 275 struct bin_buffer *bb = file->private_data; 276 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 277 int ret; 278 279 if (!bb->vm_ops || !bb->vm_ops->set_policy) 280 return 0; 281 282 if (!sysfs_get_active(attr_sd)) 283 return -EINVAL; 284 285 ret = bb->vm_ops->set_policy(vma, new); 286 287 sysfs_put_active(attr_sd); 288 return ret; 289} 290 291static struct mempolicy *bin_get_policy(struct vm_area_struct *vma, 292 unsigned long addr) 293{ 294 struct file *file = vma->vm_file; 295 struct bin_buffer *bb = file->private_data; 296 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 297 struct mempolicy *pol; 298 299 if (!bb->vm_ops || !bb->vm_ops->get_policy) 300 return vma->vm_policy; 301 302 if (!sysfs_get_active(attr_sd)) 303 return vma->vm_policy; 304 305 pol = bb->vm_ops->get_policy(vma, addr); 306 307 sysfs_put_active(attr_sd); 308 return pol; 309} 310 311static int bin_migrate(struct vm_area_struct *vma, const nodemask_t *from, 312 const nodemask_t *to, unsigned long flags) 313{ 314 struct file *file = vma->vm_file; 315 struct bin_buffer *bb = file->private_data; 316 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 317 int ret; 318 319 if (!bb->vm_ops || !bb->vm_ops->migrate) 320 return 0; 321 322 if (!sysfs_get_active(attr_sd)) 323 return 0; 324 325 ret = bb->vm_ops->migrate(vma, from, to, flags); 326 327 sysfs_put_active(attr_sd); 328 return ret; 329} 330#endif 331 332static const struct vm_operations_struct bin_vm_ops = { 333 .open = bin_vma_open, 334 .close = bin_vma_close, 335 .fault = bin_fault, 336 .page_mkwrite = bin_page_mkwrite, 337 .access = bin_access, 338#ifdef CONFIG_NUMA 339 .set_policy = bin_set_policy, 340 .get_policy = bin_get_policy, 341 .migrate = bin_migrate, 342#endif 343}; 344 345static int mmap(struct file *file, struct vm_area_struct *vma) 346{ 347 struct bin_buffer *bb = file->private_data; 348 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 349 struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr; 350 struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; 351 int rc; 352 353 mutex_lock(&bb->mutex); 354 355 /* need attr_sd for attr, its parent for kobj */ 356 rc = -ENODEV; 357 if (!sysfs_get_active(attr_sd)) 358 goto out_unlock; 359 360 rc = -EINVAL; 361 if (!attr->mmap) 362 goto out_put; 363 364 rc = attr->mmap(file, kobj, attr, vma); 365 if (rc) 366 goto out_put; 367 368 /* 369 * PowerPC's pci_mmap of legacy_mem uses shmem_zero_setup() 370 * to satisfy versions of X which crash if the mmap fails: that 371 * substitutes a new vm_file, and we don't then want bin_vm_ops. 372 */ 373 if (vma->vm_file != file) 374 goto out_put; 375 376 rc = -EINVAL; 377 if (bb->mmapped && bb->vm_ops != vma->vm_ops) 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); 507