bin.c 5.6 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * bin.c - binary file operations for sysfs.
 *
 * Copyright (c) 2003 Patrick Mochel
 * Copyright (c) 2003 Matthew Wilcox
 * Copyright (c) 2004 Silicon Graphics, Inc.
 */

#undef DEBUG

#include <linux/errno.h>
#include <linux/fs.h>
13
#include <linux/kernel.h>
L
Linus Torvalds 已提交
14 15 16 17 18
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/slab.h>

#include <asm/uaccess.h>
19
#include <asm/semaphore.h>
L
Linus Torvalds 已提交
20 21 22

#include "sysfs.h"

T
Tejun Heo 已提交
23 24 25
struct bin_buffer {
	struct mutex	mutex;
	void		*buffer;
26
	int		mmapped;
T
Tejun Heo 已提交
27 28
};

L
Linus Torvalds 已提交
29 30 31
static int
fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count)
{
32 33
	struct sysfs_dirent *attr_sd = dentry->d_fsdata;
	struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
34 35 36 37 38 39
	struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj;
	int rc;

	/* need attr_sd for attr, its parent for kobj */
	if (!sysfs_get_active_two(attr_sd))
		return -ENODEV;
L
Linus Torvalds 已提交
40

41 42
	rc = -EIO;
	if (attr->read)
43
		rc = attr->read(kobj, attr, buffer, off, count);
L
Linus Torvalds 已提交
44

45 46 47
	sysfs_put_active_two(attr_sd);

	return rc;
L
Linus Torvalds 已提交
48 49 50
}

static ssize_t
51
read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off)
L
Linus Torvalds 已提交
52
{
T
Tejun Heo 已提交
53
	struct bin_buffer *bb = file->private_data;
54
	struct dentry *dentry = file->f_path.dentry;
L
Linus Torvalds 已提交
55 56
	int size = dentry->d_inode->i_size;
	loff_t offs = *off;
57
	int count = min_t(size_t, bytes, PAGE_SIZE);
L
Linus Torvalds 已提交
58 59 60 61 62 63 64 65

	if (size) {
		if (offs > size)
			return 0;
		if (offs + count > size)
			count = size - offs;
	}

T
Tejun Heo 已提交
66 67 68
	mutex_lock(&bb->mutex);

	count = fill_read(dentry, bb->buffer, offs, count);
69
	if (count < 0)
T
Tejun Heo 已提交
70
		goto out_unlock;
L
Linus Torvalds 已提交
71

T
Tejun Heo 已提交
72 73 74 75
	if (copy_to_user(userbuf, bb->buffer, count)) {
		count = -EFAULT;
		goto out_unlock;
	}
L
Linus Torvalds 已提交
76

77
	pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count);
L
Linus Torvalds 已提交
78 79 80

	*off = offs + count;

T
Tejun Heo 已提交
81 82
 out_unlock:
	mutex_unlock(&bb->mutex);
L
Linus Torvalds 已提交
83 84 85 86 87 88
	return count;
}

static int
flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count)
{
89 90
	struct sysfs_dirent *attr_sd = dentry->d_fsdata;
	struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
91 92 93 94 95 96
	struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj;
	int rc;

	/* need attr_sd for attr, its parent for kobj */
	if (!sysfs_get_active_two(attr_sd))
		return -ENODEV;
L
Linus Torvalds 已提交
97

98 99
	rc = -EIO;
	if (attr->write)
100
		rc = attr->write(kobj, attr, buffer, offset, count);
L
Linus Torvalds 已提交
101

102 103 104
	sysfs_put_active_two(attr_sd);

	return rc;
L
Linus Torvalds 已提交
105 106
}

107 108
static ssize_t write(struct file *file, const char __user *userbuf,
		     size_t bytes, loff_t *off)
L
Linus Torvalds 已提交
109
{
T
Tejun Heo 已提交
110
	struct bin_buffer *bb = file->private_data;
111
	struct dentry *dentry = file->f_path.dentry;
L
Linus Torvalds 已提交
112 113
	int size = dentry->d_inode->i_size;
	loff_t offs = *off;
114
	int count = min_t(size_t, bytes, PAGE_SIZE);
L
Linus Torvalds 已提交
115 116 117 118 119 120 121 122

	if (size) {
		if (offs > size)
			return 0;
		if (offs + count > size)
			count = size - offs;
	}

T
Tejun Heo 已提交
123 124 125 126 127 128
	mutex_lock(&bb->mutex);

	if (copy_from_user(bb->buffer, userbuf, count)) {
		count = -EFAULT;
		goto out_unlock;
	}
L
Linus Torvalds 已提交
129

T
Tejun Heo 已提交
130
	count = flush_write(dentry, bb->buffer, offs, count);
L
Linus Torvalds 已提交
131 132
	if (count > 0)
		*off = offs + count;
T
Tejun Heo 已提交
133 134 135

 out_unlock:
	mutex_unlock(&bb->mutex);
L
Linus Torvalds 已提交
136 137 138 139 140
	return count;
}

static int mmap(struct file *file, struct vm_area_struct *vma)
{
T
Tejun Heo 已提交
141
	struct bin_buffer *bb = file->private_data;
142 143
	struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
	struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
144
	struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj;
T
Tejun Heo 已提交
145
	int rc;
L
Linus Torvalds 已提交
146

T
Tejun Heo 已提交
147
	mutex_lock(&bb->mutex);
148 149 150 151 152 153 154 155 156 157 158 159 160 161

	/* need attr_sd for attr, its parent for kobj */
	if (!sysfs_get_active_two(attr_sd))
		return -ENODEV;

	rc = -EINVAL;
	if (attr->mmap)
		rc = attr->mmap(kobj, attr, vma);

	if (rc == 0 && !bb->mmapped)
		bb->mmapped = 1;
	else
		sysfs_put_active_two(attr_sd);

T
Tejun Heo 已提交
162 163 164
	mutex_unlock(&bb->mutex);

	return rc;
L
Linus Torvalds 已提交
165 166 167 168
}

static int open(struct inode * inode, struct file * file)
{
169 170
	struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
	struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
T
Tejun Heo 已提交
171
	struct bin_buffer *bb = NULL;
172
	int error;
L
Linus Torvalds 已提交
173

174 175 176
	/* need attr_sd for attr */
	if (!sysfs_get_active(attr_sd))
		return -ENODEV;
L
Linus Torvalds 已提交
177 178 179

	error = -EACCES;
	if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap))
180
		goto err_out;
L
Linus Torvalds 已提交
181
	if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap))
182
		goto err_out;
L
Linus Torvalds 已提交
183 184

	error = -ENOMEM;
T
Tejun Heo 已提交
185 186
	bb = kzalloc(sizeof(*bb), GFP_KERNEL);
	if (!bb)
187
		goto err_out;
L
Linus Torvalds 已提交
188

T
Tejun Heo 已提交
189 190
	bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
	if (!bb->buffer)
191
		goto err_out;
T
Tejun Heo 已提交
192 193 194 195

	mutex_init(&bb->mutex);
	file->private_data = bb;

196 197 198 199
	/* open succeeded, put active reference and pin attr_sd */
	sysfs_put_active(attr_sd);
	sysfs_get(attr_sd);
	return 0;
L
Linus Torvalds 已提交
200

201
 err_out:
202 203
	sysfs_put_active(attr_sd);
	kfree(bb);
L
Linus Torvalds 已提交
204 205 206 207 208
	return error;
}

static int release(struct inode * inode, struct file * file)
{
209
	struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
T
Tejun Heo 已提交
210
	struct bin_buffer *bb = file->private_data;
L
Linus Torvalds 已提交
211

212 213 214
	if (bb->mmapped)
		sysfs_put_active_two(attr_sd);
	sysfs_put(attr_sd);
T
Tejun Heo 已提交
215 216
	kfree(bb->buffer);
	kfree(bb);
L
Linus Torvalds 已提交
217 218 219
	return 0;
}

220
const struct file_operations bin_fops = {
L
Linus Torvalds 已提交
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
	.read		= read,
	.write		= write,
	.mmap		= mmap,
	.llseek		= generic_file_llseek,
	.open		= open,
	.release	= release,
};

/**
 *	sysfs_create_bin_file - create binary file for object.
 *	@kobj:	object.
 *	@attr:	attribute descriptor.
 */

int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
{
237
	BUG_ON(!kobj || !kobj->sd || !attr);
L
Linus Torvalds 已提交
238

239
	return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
L
Linus Torvalds 已提交
240 241 242 243 244 245 246 247 248
}


/**
 *	sysfs_remove_bin_file - remove binary file for object.
 *	@kobj:	object.
 *	@attr:	attribute descriptor.
 */

249
void sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr)
L
Linus Torvalds 已提交
250
{
251
	if (sysfs_hash_and_remove(kobj->sd, attr->attr.name) < 0) {
252 253 254 255 256
		printk(KERN_ERR "%s: "
			"bad dentry or inode or no such file: \"%s\"\n",
			__FUNCTION__, attr->attr.name);
		dump_stack();
	}
L
Linus Torvalds 已提交
257 258 259 260
}

EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);