Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.23-rc8 255 lines 5.6 kB view raw
1/* 2 * bin.c - binary file operations for sysfs. 3 * 4 * Copyright (c) 2003 Patrick Mochel 5 * Copyright (c) 2003 Matthew Wilcox 6 * Copyright (c) 2004 Silicon Graphics, Inc. 7 */ 8 9#undef DEBUG 10 11#include <linux/errno.h> 12#include <linux/fs.h> 13#include <linux/kernel.h> 14#include <linux/kobject.h> 15#include <linux/module.h> 16#include <linux/slab.h> 17 18#include <asm/uaccess.h> 19#include <asm/semaphore.h> 20 21#include "sysfs.h" 22 23struct bin_buffer { 24 struct mutex mutex; 25 void *buffer; 26 int mmapped; 27}; 28 29static int 30fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) 31{ 32 struct sysfs_dirent *attr_sd = dentry->d_fsdata; 33 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; 34 struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; 35 int rc; 36 37 /* need attr_sd for attr, its parent for kobj */ 38 if (!sysfs_get_active_two(attr_sd)) 39 return -ENODEV; 40 41 rc = -EIO; 42 if (attr->read) 43 rc = attr->read(kobj, attr, buffer, off, count); 44 45 sysfs_put_active_two(attr_sd); 46 47 return rc; 48} 49 50static ssize_t 51read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) 52{ 53 struct bin_buffer *bb = file->private_data; 54 struct dentry *dentry = file->f_path.dentry; 55 int size = dentry->d_inode->i_size; 56 loff_t offs = *off; 57 int count = min_t(size_t, bytes, PAGE_SIZE); 58 59 if (size) { 60 if (offs > size) 61 return 0; 62 if (offs + count > size) 63 count = size - offs; 64 } 65 66 mutex_lock(&bb->mutex); 67 68 count = fill_read(dentry, bb->buffer, offs, count); 69 if (count < 0) 70 goto out_unlock; 71 72 if (copy_to_user(userbuf, bb->buffer, count)) { 73 count = -EFAULT; 74 goto out_unlock; 75 } 76 77 pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count); 78 79 *off = offs + count; 80 81 out_unlock: 82 mutex_unlock(&bb->mutex); 83 return count; 84} 85 86static int 87flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count) 88{ 89 struct sysfs_dirent *attr_sd = dentry->d_fsdata; 90 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; 91 struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; 92 int rc; 93 94 /* need attr_sd for attr, its parent for kobj */ 95 if (!sysfs_get_active_two(attr_sd)) 96 return -ENODEV; 97 98 rc = -EIO; 99 if (attr->write) 100 rc = attr->write(kobj, attr, buffer, offset, count); 101 102 sysfs_put_active_two(attr_sd); 103 104 return rc; 105} 106 107static ssize_t write(struct file *file, const char __user *userbuf, 108 size_t bytes, loff_t *off) 109{ 110 struct bin_buffer *bb = file->private_data; 111 struct dentry *dentry = file->f_path.dentry; 112 int size = dentry->d_inode->i_size; 113 loff_t offs = *off; 114 int count = min_t(size_t, bytes, PAGE_SIZE); 115 116 if (size) { 117 if (offs > size) 118 return 0; 119 if (offs + count > size) 120 count = size - offs; 121 } 122 123 mutex_lock(&bb->mutex); 124 125 if (copy_from_user(bb->buffer, userbuf, count)) { 126 count = -EFAULT; 127 goto out_unlock; 128 } 129 130 count = flush_write(dentry, bb->buffer, offs, count); 131 if (count > 0) 132 *off = offs + count; 133 134 out_unlock: 135 mutex_unlock(&bb->mutex); 136 return count; 137} 138 139static int mmap(struct file *file, struct vm_area_struct *vma) 140{ 141 struct bin_buffer *bb = file->private_data; 142 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 143 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; 144 struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; 145 int rc; 146 147 mutex_lock(&bb->mutex); 148 149 /* need attr_sd for attr, its parent for kobj */ 150 if (!sysfs_get_active_two(attr_sd)) 151 return -ENODEV; 152 153 rc = -EINVAL; 154 if (attr->mmap) 155 rc = attr->mmap(kobj, attr, vma); 156 157 if (rc == 0 && !bb->mmapped) 158 bb->mmapped = 1; 159 else 160 sysfs_put_active_two(attr_sd); 161 162 mutex_unlock(&bb->mutex); 163 164 return rc; 165} 166 167static int open(struct inode * inode, struct file * file) 168{ 169 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 170 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; 171 struct bin_buffer *bb = NULL; 172 int error; 173 174 /* need attr_sd for attr */ 175 if (!sysfs_get_active(attr_sd)) 176 return -ENODEV; 177 178 error = -EACCES; 179 if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap)) 180 goto err_out; 181 if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap)) 182 goto err_out; 183 184 error = -ENOMEM; 185 bb = kzalloc(sizeof(*bb), GFP_KERNEL); 186 if (!bb) 187 goto err_out; 188 189 bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); 190 if (!bb->buffer) 191 goto err_out; 192 193 mutex_init(&bb->mutex); 194 file->private_data = bb; 195 196 /* open succeeded, put active reference and pin attr_sd */ 197 sysfs_put_active(attr_sd); 198 sysfs_get(attr_sd); 199 return 0; 200 201 err_out: 202 sysfs_put_active(attr_sd); 203 kfree(bb); 204 return error; 205} 206 207static int release(struct inode * inode, struct file * file) 208{ 209 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 210 struct bin_buffer *bb = file->private_data; 211 212 if (bb->mmapped) 213 sysfs_put_active_two(attr_sd); 214 sysfs_put(attr_sd); 215 kfree(bb->buffer); 216 kfree(bb); 217 return 0; 218} 219 220const struct file_operations bin_fops = { 221 .read = read, 222 .write = write, 223 .mmap = mmap, 224 .llseek = generic_file_llseek, 225 .open = open, 226 .release = release, 227}; 228 229/** 230 * sysfs_create_bin_file - create binary file for object. 231 * @kobj: object. 232 * @attr: attribute descriptor. 233 */ 234 235int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr) 236{ 237 BUG_ON(!kobj || !kobj->sd || !attr); 238 239 return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR); 240} 241 242 243/** 244 * sysfs_remove_bin_file - remove binary file for object. 245 * @kobj: object. 246 * @attr: attribute descriptor. 247 */ 248 249void sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr) 250{ 251 sysfs_hash_and_remove(kobj->sd, attr->attr.name); 252} 253 254EXPORT_SYMBOL_GPL(sysfs_create_bin_file); 255EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);