linux/fs/sysfs/bin.c
<<
>>
Prefs
   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_inode(file)->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_inode(file)->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
 465        if (sysfs_type(attr_sd) != SYSFS_KOBJ_BIN_ATTR)
 466                return;
 467
 468        mutex_lock(&sysfs_bin_lock);
 469
 470        hlist_for_each_entry(bb, &attr_sd->s_bin_attr.buffers, list) {
 471                struct inode *inode = file_inode(bb->file);
 472
 473                unmap_mapping_range(inode->i_mapping, 0, 0, 1);
 474        }
 475
 476        mutex_unlock(&sysfs_bin_lock);
 477}
 478
 479/**
 480 *      sysfs_create_bin_file - create binary file for object.
 481 *      @kobj:  object.
 482 *      @attr:  attribute descriptor.
 483 */
 484
 485int sysfs_create_bin_file(struct kobject *kobj,
 486                          const struct bin_attribute *attr)
 487{
 488        BUG_ON(!kobj || !kobj->sd || !attr);
 489
 490        return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
 491}
 492
 493
 494/**
 495 *      sysfs_remove_bin_file - remove binary file for object.
 496 *      @kobj:  object.
 497 *      @attr:  attribute descriptor.
 498 */
 499
 500void sysfs_remove_bin_file(struct kobject *kobj,
 501                           const struct bin_attribute *attr)
 502{
 503        sysfs_hash_and_remove(kobj->sd, NULL, attr->attr.name);
 504}
 505
 506EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
 507EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
 508
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.