/** * sysfs_write_file - write an attribute * @file: file pointer * @user_buf: data to write * @count: number of bytes * @ppos: starting offset * * Copy data in from userland and pass it to the matching * sysfs_ops->store() by invoking flush_write_buffer(). * * There is no easy way for us to know if userspace is only doing a partial * write, so we don't support them. We expect the entire buffer to come on * the first write. Hint: if you're writing a value, first read the file, * modify only the the value you're changing, then write entire buffer * back. */ static ssize_t sysfs_write_file(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct sysfs_open_file *of = sysfs_of(file); ssize_t len = min_t(size_t, count, PAGE_SIZE); loff_t size = file_inode(file)->i_size; char *buf; if (sysfs_is_bin(of->sd) && size) { if (size <= *ppos) return 0; len = min_t(ssize_t, len, size - *ppos); } if (!len) return 0; buf = kmalloc(len + 1, GFP_KERNEL); if (!buf) return -ENOMEM; if (copy_from_user(buf, user_buf, len)) { len = -EFAULT; goto out_free; } buf[len] = '\0'; /* guarantee string termination */ len = flush_write_buffer(of, buf, *ppos, len); if (len > 0) *ppos += len; out_free: kfree(buf); return len; }
/* * Read method for bin files. As reading a bin file can have side-effects, * the exact offset and bytes specified in read(2) call should be passed to * the read callback making it difficult to use seq_file. Implement * simplistic custom buffering for bin files. */ static ssize_t sysfs_bin_read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) { struct sysfs_open_file *of = sysfs_of(file); struct bin_attribute *battr = of->sd->s_attr.bin_attr; struct kobject *kobj = of->sd->s_parent->s_dir.kobj; loff_t size = file_inode(file)->i_size; int count = min_t(size_t, bytes, PAGE_SIZE); loff_t offs = *off; char *buf; if (!bytes) return 0; if (size) { if (offs > size) return 0; if (offs + count > size) count = size - offs; } buf = kmalloc(count, GFP_KERNEL); if (!buf) return -ENOMEM; /* need of->sd for battr, its parent for kobj */ mutex_lock(&of->mutex); if (!sysfs_get_active(of->sd)) { count = -ENODEV; mutex_unlock(&of->mutex); goto out_free; } if (battr->read) count = battr->read(file, kobj, battr, buf, offs, count); else count = -EIO; sysfs_put_active(of->sd); mutex_unlock(&of->mutex); if (count < 0) goto out_free; if (copy_to_user(userbuf, buf, count)) { count = -EFAULT; goto out_free; } pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count); *off = offs + count; out_free: kfree(buf); return count; }
static void sysfs_bin_vma_open(struct vm_area_struct *vma) { struct file *file = vma->vm_file; struct sysfs_open_file *of = sysfs_of(file); if (!of->vm_ops) return; if (!sysfs_get_active(of->sd)) return; if (of->vm_ops->open) of->vm_ops->open(vma); sysfs_put_active(of->sd); }
static int sysfs_bin_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct file *file = vma->vm_file; struct sysfs_open_file *of = sysfs_of(file); int ret; if (!of->vm_ops) return VM_FAULT_SIGBUS; if (!sysfs_get_active(of->sd)) return VM_FAULT_SIGBUS; ret = VM_FAULT_SIGBUS; if (of->vm_ops->fault) ret = of->vm_ops->fault(vma, vmf); sysfs_put_active(of->sd); return ret; }
static int sysfs_bin_access(struct vm_area_struct *vma, unsigned long addr, void *buf, int len, int write) { struct file *file = vma->vm_file; struct sysfs_open_file *of = sysfs_of(file); int ret; if (!of->vm_ops) return -EINVAL; if (!sysfs_get_active(of->sd)) return -EINVAL; ret = -EINVAL; if (of->vm_ops->access) ret = of->vm_ops->access(vma, addr, buf, len, write); sysfs_put_active(of->sd); return ret; }
static int sysfs_bin_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { struct file *file = vma->vm_file; struct sysfs_open_file *of = sysfs_of(file); int ret; if (!of->vm_ops) return VM_FAULT_SIGBUS; if (!sysfs_get_active(of->sd)) return VM_FAULT_SIGBUS; ret = 0; if (of->vm_ops->page_mkwrite) ret = of->vm_ops->page_mkwrite(vma, vmf); else file_update_time(file); sysfs_put_active(of->sd); return ret; }
return -EINVAL; ret = -EINVAL; if (of->vm_ops->access) ret = of->vm_ops->access(vma, addr, buf, len, write); sysfs_put_active(of->sd); return ret; } #ifdef CONFIG_NUMA static int sysfs_bin_set_policy(struct vm_area_struct *vma, struct mempolicy *new) { struct file *file = vma->vm_file; struct sysfs_open_file *of = sysfs_of(file); int ret; if (!of->vm_ops) return 0; if (!sysfs_get_active(of->sd)) return -EINVAL; ret = 0; if (of->vm_ops->set_policy) ret = of->vm_ops->set_policy(vma, new); sysfs_put_active(of->sd); return ret; }