/** * sysfs_get_active_two - get active references to sysfs_dirent and parent * @sd: sysfs_dirent of interest * * Get active reference to @sd and its parent. Parent's active * reference is grabbed first. This function is noop if @sd is * NULL. * * RETURNS: * Pointer to @sd on success, NULL on failure. */ struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd) { if (sd) { if (sd->s_parent && unlikely(!sysfs_get_active(sd->s_parent))) return NULL; if (unlikely(!sysfs_get_active(sd))) { sysfs_put_active(sd->s_parent); return NULL; } } return sd; }
/** * flush_write_buffer - push buffer to kobject * @of: open file * @buf: data buffer for file * @off: file offset to write to * @count: number of bytes * * Get the correct pointers for the kobject and the attribute we're dealing * with, then call the store() method for it with @buf. */ static int flush_write_buffer(struct sysfs_open_file *of, char *buf, loff_t off, size_t count) { struct kobject *kobj = of->sd->s_parent->s_dir.kobj; int rc = 0; /* * Need @of->sd for attr and ops, its parent for kobj. @of->mutex * nests outside active ref and is just to ensure that the ops * aren't called concurrently for the same open file. */ mutex_lock(&of->mutex); if (!sysfs_get_active(of->sd)) { mutex_unlock(&of->mutex); return -ENODEV; } if (sysfs_is_bin(of->sd)) { struct bin_attribute *battr = of->sd->s_attr.bin_attr; rc = -EIO; if (battr->write) rc = battr->write(of->file, kobj, battr, buf, off, count); } else { const struct sysfs_ops *ops = sysfs_file_ops(of->sd); rc = ops->store(kobj, of->sd->s_attr.attr, buf, count); } sysfs_put_active(of->sd); mutex_unlock(&of->mutex); return rc; }
/* * Reads on sysfs are handled through seq_file, which takes care of hairy * details like buffering and seeking. The following function pipes * sysfs_ops->show() result through seq_file. */ static int sysfs_seq_show(struct seq_file *sf, void *v) { struct sysfs_open_file *of = sf->private; struct kobject *kobj = of->sd->s_parent->s_dir.kobj; const struct sysfs_ops *ops; char *buf; ssize_t count; /* acquire buffer and ensure that it's >= PAGE_SIZE */ count = seq_get_buf(sf, &buf); if (count < PAGE_SIZE) { seq_commit(sf, -1); return 0; } /* * Need @of->sd for attr and ops, its parent for kobj. @of->mutex * nests outside active ref and is just to ensure that the ops * aren't called concurrently for the same open file. */ mutex_lock(&of->mutex); if (!sysfs_get_active(of->sd)) { mutex_unlock(&of->mutex); return -ENODEV; } of->event = atomic_read(&of->sd->s_attr.open->event); /* * Lookup @ops and invoke show(). Control may reach here via seq * file lseek even if @ops->show() isn't implemented. */ ops = sysfs_file_ops(of->sd); if (ops->show) count = ops->show(kobj, of->sd->s_attr.attr, buf); else count = 0; sysfs_put_active(of->sd); mutex_unlock(&of->mutex); if (count < 0) return count; /* * The code works fine with PAGE_SIZE return but it's likely to * indicate truncated result or overflow in normal use cases. */ if (count >= (ssize_t)PAGE_SIZE) { print_symbol("fill_read_buffer: %s returned bad count\n", (unsigned long)ops->show); /* Try to struggle along */ count = PAGE_SIZE - 1; } seq_commit(sf, count); return 0; }
/* * 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 void bin_vma_close(struct vm_area_struct *vma) { struct file *file = vma->vm_file; struct bin_buffer *bb = file->private_data; struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; if (!bb->vm_ops || !bb->vm_ops->close) return; if (!sysfs_get_active(attr_sd)) return; bb->vm_ops->close(vma); sysfs_put_active(attr_sd); }
static int bin_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct file *file = vma->vm_file; struct bin_buffer *bb = file->private_data; struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; int ret; if (!bb->vm_ops || !bb->vm_ops->fault) return VM_FAULT_SIGBUS; if (!sysfs_get_active(attr_sd)) return VM_FAULT_SIGBUS; ret = bb->vm_ops->fault(vma, vmf); sysfs_put_active(attr_sd); return ret; }
static int flush_write_buffer(struct dentry * dentry, struct sysfs_buffer * buffer, size_t count) { struct sysfs_dirent *attr_sd = dentry->d_fsdata; struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; const struct sysfs_ops * ops = buffer->ops; int rc; /* need attr_sd for attr and ops, its parent for kobj */ if (!sysfs_get_active(attr_sd)) return -ENODEV; rc = ops->store(kobj, attr_sd->s_attr.attr, buffer->page, count); sysfs_put_active(attr_sd); return rc; }
static int bin_access(struct vm_area_struct *vma, unsigned long addr, void *buf, int len, int write) { struct file *file = vma->vm_file; struct bin_buffer *bb = file->private_data; struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; int ret; if (!bb->vm_ops || !bb->vm_ops->access) return -EINVAL; if (!sysfs_get_active(attr_sd)) return -EINVAL; ret = bb->vm_ops->access(vma, addr, buf, len, write); sysfs_put_active(attr_sd); return ret; }
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 fill_read(struct file *file, char *buffer, loff_t off, size_t count) { struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr; struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; int rc; /* need attr_sd for attr, its parent for kobj */ if (!sysfs_get_active(attr_sd)) return -ENODEV; rc = -EIO; if (attr->read) rc = attr->read(file, kobj, attr, buffer, off, count); sysfs_put_active(attr_sd); return rc; }
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; }
/** * fill_read_buffer - allocate and fill buffer from object. * @dentry: dentry pointer. * @buffer: data buffer for file. * * Allocate @buffer->page, if it hasn't been already, then call the * kobject's show() method to fill the buffer with this attribute's * data. * This is called only once, on the file's first read unless an error * is returned. */ static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) { struct sysfs_dirent *attr_sd = dentry->d_fsdata; struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; const struct sysfs_ops * ops = buffer->ops; int ret = 0; ssize_t count; if (!buffer->page) buffer->page = (char *) get_zeroed_page(GFP_KERNEL); if (!buffer->page) return -ENOMEM; /* need attr_sd for attr and ops, its parent for kobj */ if (!sysfs_get_active(attr_sd)) return -ENODEV; buffer->event = atomic_read_unchecked(&attr_sd->s_attr.open->event); count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page); sysfs_put_active(attr_sd); /* * The code works fine with PAGE_SIZE return but it's likely to * indicate truncated result or overflow in normal use cases. */ if (count >= (ssize_t)PAGE_SIZE) { print_symbol("fill_read_buffer: %s returned bad count\n", (unsigned long)ops->show); /* Try to struggle along */ count = PAGE_SIZE - 1; } if (count >= 0) { buffer->needs_read_fill = 0; buffer->count = count; } else { ret = count; } return ret; }
static int bin_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { struct file *file = vma->vm_file; struct bin_buffer *bb = file->private_data; struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; int ret; if (!bb->vm_ops) return VM_FAULT_SIGBUS; if (!sysfs_get_active(attr_sd)) return VM_FAULT_SIGBUS; ret = 0; if (bb->vm_ops->page_mkwrite) ret = bb->vm_ops->page_mkwrite(vma, vmf); else file_update_time(file); sysfs_put_active(attr_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; }
/* Sysfs attribute files are pollable. The idea is that you read * the content and then you use 'poll' or 'select' to wait for * the content to change. When the content changes (assuming the * manager for the kobject supports notification), poll will * return POLLERR|POLLPRI, and select will return the fd whether * it is waiting for read, write, or exceptions. * Once poll/select indicates that the value has changed, you * need to close and re-open the file, or seek to 0 and read again. * Reminder: this only works for attributes which actively support * it, and it is not possible to test an attribute from userspace * to see if it supports poll (Neither 'poll' nor 'select' return * an appropriate error code). When in doubt, set a suitable timeout value. */ static unsigned int sysfs_poll(struct file *filp, poll_table *wait) { struct sysfs_buffer * buffer = filp->private_data; struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; struct sysfs_open_dirent *od = attr_sd->s_attr.open; /* need parent for the kobj, grab both */ if (!sysfs_get_active(attr_sd)) goto trigger; poll_wait(filp, &od->poll, wait); sysfs_put_active(attr_sd); if (buffer->event != atomic_read_unchecked(&od->event)) goto trigger; return DEFAULT_POLLMASK; trigger: buffer->needs_read_fill = 1; return DEFAULT_POLLMASK|POLLERR|POLLPRI; }
static int sysfs_open_file(struct inode *inode, struct file *file) { struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; struct sysfs_buffer *buffer; const struct sysfs_ops *ops; int error = -EACCES; /* need attr_sd for attr and ops, its parent for kobj */ if (!sysfs_get_active(attr_sd)) return -ENODEV; /* every kobject with an attribute needs a ktype assigned */ if (kobj->ktype && kobj->ktype->sysfs_ops) ops = kobj->ktype->sysfs_ops; else { WARN(1, KERN_ERR "missing sysfs attribute operations for " "kobject: %s\n", kobject_name(kobj)); goto err_out; } /* File needs write support. * The inode's perms must say it's ok, * and we must have a store method. */ if (file->f_mode & FMODE_WRITE) { if (!(inode->i_mode & S_IWUGO) || !ops->store) goto err_out; } /* File needs read support. * The inode's perms must say it's ok, and we there * must be a show method for it. */ if (file->f_mode & FMODE_READ) { if (!(inode->i_mode & S_IRUGO) || !ops->show) goto err_out; } /* No error? Great, allocate a buffer for the file, and store it * it in file->private_data for easy access. */ error = -ENOMEM; buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); if (!buffer) goto err_out; mutex_init(&buffer->mutex); buffer->needs_read_fill = 1; buffer->ops = ops; file->private_data = buffer; /* make sure we have open dirent struct */ error = sysfs_get_open_dirent(attr_sd, buffer); if (error) goto err_free; /* open succeeded, put active references */ sysfs_put_active(attr_sd); return 0; err_free: kfree(buffer); err_out: sysfs_put_active(attr_sd); return error; }