1121 lines · 27.0 KB · C
RawDownload
/* * fs/sysfs/file.c - sysfs regular (text) file implementation * * Copyright (c) 2001-3 Patrick Mochel * Copyright (c) 2007 SUSE Linux Products GmbH * Copyright (c) 2007 Tejun Heo <teheo@suse.de> * * This file is released under the GPLv2. * * Please see Documentation/filesystems/sysfs.txt for more information. */ #include <linux/module.h>#include <linux/kobject.h>#include <linux/kallsyms.h>#include <linux/slab.h>#include <linux/fsnotify.h>#include <linux/namei.h>#include <linux/poll.h>#include <linux/list.h>#include <linux/mutex.h>#include <linux/limits.h>#include <linux/uaccess.h>#include <linux/seq_file.h>#include <linux/mm.h> #include "sysfs.h" /* * There's one sysfs_open_file for each open file and one sysfs_open_dirent * for each sysfs_dirent with one or more open files. * * sysfs_dirent->s_attr.open points to sysfs_open_dirent.  s_attr.open is * protected by sysfs_open_dirent_lock. * * filp->private_data points to seq_file whose ->private points to * sysfs_open_file.  sysfs_open_files are chained at * sysfs_open_dirent->files, which is protected by sysfs_open_file_mutex. */static DEFINE_SPINLOCK(sysfs_open_dirent_lock);static DEFINE_MUTEX(sysfs_open_file_mutex); struct sysfs_open_dirent {	atomic_t		refcnt;	atomic_t		event;	wait_queue_head_t	poll;	struct list_head	files; /* goes through sysfs_open_file.list */}; struct sysfs_open_file {	struct sysfs_dirent	*sd;	struct file		*file;	struct mutex		mutex;	int			event;	struct list_head	list; 	bool			mmapped;	const struct vm_operations_struct *vm_ops;}; static bool sysfs_is_bin(struct sysfs_dirent *sd){	return sysfs_type(sd) == SYSFS_KOBJ_BIN_ATTR;} static struct sysfs_open_file *sysfs_of(struct file *file){	return ((struct seq_file *)file->private_data)->private;} /* * Determine ktype->sysfs_ops for the given sysfs_dirent.  This function * must be called while holding an active reference. */static const struct sysfs_ops *sysfs_file_ops(struct sysfs_dirent *sd){	struct kobject *kobj = sd->s_parent->s_dir.kobj; 	if (!sysfs_ignore_lockdep(sd))		lockdep_assert_held(sd);	return kobj->ktype ? kobj->ktype->sysfs_ops : NULL;} /* * 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;} /** * 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;} /** * 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;} 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_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;} 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;} #ifdef CONFIG_NUMAstatic 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;} static struct mempolicy *sysfs_bin_get_policy(struct vm_area_struct *vma,					      unsigned long addr){	struct file *file = vma->vm_file;	struct sysfs_open_file *of = sysfs_of(file);	struct mempolicy *pol; 	if (!of->vm_ops)		return vma->vm_policy; 	if (!sysfs_get_active(of->sd))		return vma->vm_policy; 	pol = vma->vm_policy;	if (of->vm_ops->get_policy)		pol = of->vm_ops->get_policy(vma, addr); 	sysfs_put_active(of->sd);	return pol;} static int sysfs_bin_migrate(struct vm_area_struct *vma, const nodemask_t *from,			     const nodemask_t *to, unsigned long flags){	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 0; 	ret = 0;	if (of->vm_ops->migrate)		ret = of->vm_ops->migrate(vma, from, to, flags); 	sysfs_put_active(of->sd);	return ret;}#endif static const struct vm_operations_struct sysfs_bin_vm_ops = {	.open		= sysfs_bin_vma_open,	.fault		= sysfs_bin_fault,	.page_mkwrite	= sysfs_bin_page_mkwrite,	.access		= sysfs_bin_access,#ifdef CONFIG_NUMA	.set_policy	= sysfs_bin_set_policy,	.get_policy	= sysfs_bin_get_policy,	.migrate	= sysfs_bin_migrate,#endif}; static int sysfs_bin_mmap(struct file *file, struct vm_area_struct *vma){	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;	int rc; 	mutex_lock(&of->mutex); 	/* need of->sd for battr, its parent for kobj */	rc = -ENODEV;	if (!sysfs_get_active(of->sd))		goto out_unlock; 	if (!battr->mmap)		goto out_put; 	rc = battr->mmap(file, kobj, battr, vma);	if (rc)		goto out_put; 	/*	 * PowerPC's pci_mmap of legacy_mem uses shmem_zero_setup()	 * to satisfy versions of X which crash if the mmap fails: that	 * substitutes a new vm_file, and we don't then want bin_vm_ops.	 */	if (vma->vm_file != file)		goto out_put; 	rc = -EINVAL;	if (of->mmapped && of->vm_ops != vma->vm_ops)		goto out_put; 	/*	 * It is not possible to successfully wrap close.

Lines 1–500 of 1121.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t