diff options
Diffstat (limited to 'fs/sysfs/bin.c')
| -rw-r--r-- | fs/sysfs/bin.c | 253 |
1 files changed, 240 insertions, 13 deletions
diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index f2c478c3424e..07703d3ff4a1 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c | |||
| @@ -21,15 +21,28 @@ | |||
| 21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
| 22 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
| 23 | #include <linux/mutex.h> | 23 | #include <linux/mutex.h> |
| 24 | #include <linux/mm.h> | ||
| 24 | 25 | ||
| 25 | #include <asm/uaccess.h> | 26 | #include <asm/uaccess.h> |
| 26 | 27 | ||
| 27 | #include "sysfs.h" | 28 | #include "sysfs.h" |
| 28 | 29 | ||
| 30 | /* | ||
| 31 | * There's one bin_buffer for each open file. | ||
| 32 | * | ||
| 33 | * filp->private_data points to bin_buffer and | ||
| 34 | * sysfs_dirent->s_bin_attr.buffers points to a the bin_buffer s | ||
| 35 | * sysfs_dirent->s_bin_attr.buffers is protected by sysfs_bin_lock | ||
| 36 | */ | ||
| 37 | static DEFINE_MUTEX(sysfs_bin_lock); | ||
| 38 | |||
| 29 | struct bin_buffer { | 39 | struct bin_buffer { |
| 30 | struct mutex mutex; | 40 | struct mutex mutex; |
| 31 | void *buffer; | 41 | void *buffer; |
| 32 | int mmapped; | 42 | int mmapped; |
| 43 | struct vm_operations_struct *vm_ops; | ||
| 44 | struct file *file; | ||
| 45 | struct hlist_node list; | ||
| 33 | }; | 46 | }; |
| 34 | 47 | ||
| 35 | static int | 48 | static int |
| @@ -168,6 +181,175 @@ out_free: | |||
| 168 | return count; | 181 | return count; |
| 169 | } | 182 | } |
| 170 | 183 | ||
| 184 | static void bin_vma_open(struct vm_area_struct *vma) | ||
| 185 | { | ||
| 186 | struct file *file = vma->vm_file; | ||
| 187 | struct bin_buffer *bb = file->private_data; | ||
| 188 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
| 189 | |||
| 190 | if (!bb->vm_ops || !bb->vm_ops->open) | ||
| 191 | return; | ||
| 192 | |||
| 193 | if (!sysfs_get_active_two(attr_sd)) | ||
| 194 | return; | ||
| 195 | |||
| 196 | bb->vm_ops->open(vma); | ||
| 197 | |||
| 198 | sysfs_put_active_two(attr_sd); | ||
| 199 | } | ||
| 200 | |||
| 201 | static void bin_vma_close(struct vm_area_struct *vma) | ||
| 202 | { | ||
| 203 | struct file *file = vma->vm_file; | ||
| 204 | struct bin_buffer *bb = file->private_data; | ||
| 205 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
| 206 | |||
| 207 | if (!bb->vm_ops || !bb->vm_ops->close) | ||
| 208 | return; | ||
| 209 | |||
| 210 | if (!sysfs_get_active_two(attr_sd)) | ||
| 211 | return; | ||
| 212 | |||
| 213 | bb->vm_ops->close(vma); | ||
| 214 | |||
| 215 | sysfs_put_active_two(attr_sd); | ||
| 216 | } | ||
| 217 | |||
| 218 | static int bin_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | ||
| 219 | { | ||
| 220 | struct file *file = vma->vm_file; | ||
| 221 | struct bin_buffer *bb = file->private_data; | ||
| 222 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
| 223 | int ret; | ||
| 224 | |||
| 225 | if (!bb->vm_ops || !bb->vm_ops->fault) | ||
| 226 | return VM_FAULT_SIGBUS; | ||
| 227 | |||
| 228 | if (!sysfs_get_active_two(attr_sd)) | ||
| 229 | return VM_FAULT_SIGBUS; | ||
| 230 | |||
| 231 | ret = bb->vm_ops->fault(vma, vmf); | ||
| 232 | |||
| 233 | sysfs_put_active_two(attr_sd); | ||
| 234 | return ret; | ||
| 235 | } | ||
| 236 | |||
| 237 | static int bin_page_mkwrite(struct vm_area_struct *vma, struct page *page) | ||
| 238 | { | ||
| 239 | struct file *file = vma->vm_file; | ||
| 240 | struct bin_buffer *bb = file->private_data; | ||
| 241 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
| 242 | int ret; | ||
| 243 | |||
| 244 | if (!bb->vm_ops) | ||
| 245 | return -EINVAL; | ||
| 246 | |||
| 247 | if (!bb->vm_ops->page_mkwrite) | ||
| 248 | return 0; | ||
| 249 | |||
| 250 | if (!sysfs_get_active_two(attr_sd)) | ||
| 251 | return -EINVAL; | ||
| 252 | |||
| 253 | ret = bb->vm_ops->page_mkwrite(vma, page); | ||
| 254 | |||
| 255 | sysfs_put_active_two(attr_sd); | ||
| 256 | return ret; | ||
| 257 | } | ||
| 258 | |||
| 259 | static int bin_access(struct vm_area_struct *vma, unsigned long addr, | ||
| 260 | void *buf, int len, int write) | ||
| 261 | { | ||
| 262 | struct file *file = vma->vm_file; | ||
| 263 | struct bin_buffer *bb = file->private_data; | ||
| 264 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
| 265 | int ret; | ||
| 266 | |||
| 267 | if (!bb->vm_ops || !bb->vm_ops->access) | ||
| 268 | return -EINVAL; | ||
| 269 | |||
| 270 | if (!sysfs_get_active_two(attr_sd)) | ||
| 271 | return -EINVAL; | ||
| 272 | |||
| 273 | ret = bb->vm_ops->access(vma, addr, buf, len, write); | ||
| 274 | |||
| 275 | sysfs_put_active_two(attr_sd); | ||
| 276 | return ret; | ||
| 277 | } | ||
| 278 | |||
| 279 | #ifdef CONFIG_NUMA | ||
| 280 | static int bin_set_policy(struct vm_area_struct *vma, struct mempolicy *new) | ||
| 281 | { | ||
| 282 | struct file *file = vma->vm_file; | ||
| 283 | struct bin_buffer *bb = file->private_data; | ||
| 284 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
| 285 | int ret; | ||
| 286 | |||
| 287 | if (!bb->vm_ops || !bb->vm_ops->set_policy) | ||
| 288 | return 0; | ||
| 289 | |||
| 290 | if (!sysfs_get_active_two(attr_sd)) | ||
| 291 | return -EINVAL; | ||
| 292 | |||
| 293 | ret = bb->vm_ops->set_policy(vma, new); | ||
| 294 | |||
| 295 | sysfs_put_active_two(attr_sd); | ||
| 296 | return ret; | ||
| 297 | } | ||
| 298 | |||
| 299 | static struct mempolicy *bin_get_policy(struct vm_area_struct *vma, | ||
| 300 | unsigned long addr) | ||
| 301 | { | ||
| 302 | struct file *file = vma->vm_file; | ||
| 303 | struct bin_buffer *bb = file->private_data; | ||
| 304 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
| 305 | struct mempolicy *pol; | ||
| 306 | |||
| 307 | if (!bb->vm_ops || !bb->vm_ops->get_policy) | ||
| 308 | return vma->vm_policy; | ||
| 309 | |||
| 310 | if (!sysfs_get_active_two(attr_sd)) | ||
| 311 | return vma->vm_policy; | ||
| 312 | |||
| 313 | pol = bb->vm_ops->get_policy(vma, addr); | ||
| 314 | |||
| 315 | sysfs_put_active_two(attr_sd); | ||
| 316 | return pol; | ||
| 317 | } | ||
| 318 | |||
| 319 | static int bin_migrate(struct vm_area_struct *vma, const nodemask_t *from, | ||
| 320 | const nodemask_t *to, unsigned long flags) | ||
| 321 | { | ||
| 322 | struct file *file = vma->vm_file; | ||
| 323 | struct bin_buffer *bb = file->private_data; | ||
| 324 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
| 325 | int ret; | ||
| 326 | |||
| 327 | if (!bb->vm_ops || !bb->vm_ops->migrate) | ||
| 328 | return 0; | ||
| 329 | |||
| 330 | if (!sysfs_get_active_two(attr_sd)) | ||
| 331 | return 0; | ||
| 332 | |||
| 333 | ret = bb->vm_ops->migrate(vma, from, to, flags); | ||
| 334 | |||
| 335 | sysfs_put_active_two(attr_sd); | ||
| 336 | return ret; | ||
| 337 | } | ||
| 338 | #endif | ||
| 339 | |||
| 340 | static struct vm_operations_struct bin_vm_ops = { | ||
| 341 | .open = bin_vma_open, | ||
| 342 | .close = bin_vma_close, | ||
| 343 | .fault = bin_fault, | ||
| 344 | .page_mkwrite = bin_page_mkwrite, | ||
| 345 | .access = bin_access, | ||
| 346 | #ifdef CONFIG_NUMA | ||
| 347 | .set_policy = bin_set_policy, | ||
| 348 | .get_policy = bin_get_policy, | ||
| 349 | .migrate = bin_migrate, | ||
| 350 | #endif | ||
| 351 | }; | ||
| 352 | |||
| 171 | static int mmap(struct file *file, struct vm_area_struct *vma) | 353 | static int mmap(struct file *file, struct vm_area_struct *vma) |
| 172 | { | 354 | { |
| 173 | struct bin_buffer *bb = file->private_data; | 355 | struct bin_buffer *bb = file->private_data; |
| @@ -179,18 +361,37 @@ static int mmap(struct file *file, struct vm_area_struct *vma) | |||
| 179 | mutex_lock(&bb->mutex); | 361 | mutex_lock(&bb->mutex); |
| 180 | 362 | ||
| 181 | /* need attr_sd for attr, its parent for kobj */ | 363 | /* need attr_sd for attr, its parent for kobj */ |
| 364 | rc = -ENODEV; | ||
| 182 | if (!sysfs_get_active_two(attr_sd)) | 365 | if (!sysfs_get_active_two(attr_sd)) |
| 183 | return -ENODEV; | 366 | goto out_unlock; |
| 184 | 367 | ||
| 185 | rc = -EINVAL; | 368 | rc = -EINVAL; |
| 186 | if (attr->mmap) | 369 | if (!attr->mmap) |
| 187 | rc = attr->mmap(kobj, attr, vma); | 370 | goto out_put; |
| 371 | |||
| 372 | rc = attr->mmap(kobj, attr, vma); | ||
| 373 | if (rc) | ||
| 374 | goto out_put; | ||
| 188 | 375 | ||
| 189 | if (rc == 0 && !bb->mmapped) | 376 | /* |
| 190 | bb->mmapped = 1; | 377 | * PowerPC's pci_mmap of legacy_mem uses shmem_zero_setup() |
| 191 | else | 378 | * to satisfy versions of X which crash if the mmap fails: that |
| 192 | sysfs_put_active_two(attr_sd); | 379 | * substitutes a new vm_file, and we don't then want bin_vm_ops. |
| 380 | */ | ||
| 381 | if (vma->vm_file != file) | ||
| 382 | goto out_put; | ||
| 193 | 383 | ||
| 384 | rc = -EINVAL; | ||
| 385 | if (bb->mmapped && bb->vm_ops != vma->vm_ops) | ||
| 386 | goto out_put; | ||
| 387 | |||
| 388 | rc = 0; | ||
| 389 | bb->mmapped = 1; | ||
| 390 | bb->vm_ops = vma->vm_ops; | ||
| 391 | vma->vm_ops = &bin_vm_ops; | ||
| 392 | out_put: | ||
| 393 | sysfs_put_active_two(attr_sd); | ||
| 394 | out_unlock: | ||
| 194 | mutex_unlock(&bb->mutex); | 395 | mutex_unlock(&bb->mutex); |
| 195 | 396 | ||
| 196 | return rc; | 397 | return rc; |
| @@ -223,8 +424,13 @@ static int open(struct inode * inode, struct file * file) | |||
| 223 | goto err_out; | 424 | goto err_out; |
| 224 | 425 | ||
| 225 | mutex_init(&bb->mutex); | 426 | mutex_init(&bb->mutex); |
| 427 | bb->file = file; | ||
| 226 | file->private_data = bb; | 428 | file->private_data = bb; |
| 227 | 429 | ||
| 430 | mutex_lock(&sysfs_bin_lock); | ||
| 431 | hlist_add_head(&bb->list, &attr_sd->s_bin_attr.buffers); | ||
| 432 | mutex_unlock(&sysfs_bin_lock); | ||
| 433 | |||
| 228 | /* open succeeded, put active references */ | 434 | /* open succeeded, put active references */ |
| 229 | sysfs_put_active_two(attr_sd); | 435 | sysfs_put_active_two(attr_sd); |
| 230 | return 0; | 436 | return 0; |
| @@ -237,11 +443,12 @@ static int open(struct inode * inode, struct file * file) | |||
| 237 | 443 | ||
| 238 | static int release(struct inode * inode, struct file * file) | 444 | static int release(struct inode * inode, struct file * file) |
| 239 | { | 445 | { |
| 240 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
| 241 | struct bin_buffer *bb = file->private_data; | 446 | struct bin_buffer *bb = file->private_data; |
| 242 | 447 | ||
| 243 | if (bb->mmapped) | 448 | mutex_lock(&sysfs_bin_lock); |
| 244 | sysfs_put_active_two(attr_sd); | 449 | hlist_del(&bb->list); |
| 450 | mutex_unlock(&sysfs_bin_lock); | ||
| 451 | |||
| 245 | kfree(bb->buffer); | 452 | kfree(bb->buffer); |
| 246 | kfree(bb); | 453 | kfree(bb); |
| 247 | return 0; | 454 | return 0; |
| @@ -256,6 +463,26 @@ const struct file_operations bin_fops = { | |||
| 256 | .release = release, | 463 | .release = release, |
| 257 | }; | 464 | }; |
| 258 | 465 | ||
| 466 | |||
| 467 | void unmap_bin_file(struct sysfs_dirent *attr_sd) | ||
| 468 | { | ||
| 469 | struct bin_buffer *bb; | ||
| 470 | struct hlist_node *tmp; | ||
| 471 | |||
| 472 | if (sysfs_type(attr_sd) != SYSFS_KOBJ_BIN_ATTR) | ||
| 473 | return; | ||
| 474 | |||
| 475 | mutex_lock(&sysfs_bin_lock); | ||
| 476 | |||
| 477 | hlist_for_each_entry(bb, tmp, &attr_sd->s_bin_attr.buffers, list) { | ||
| 478 | struct inode *inode = bb->file->f_path.dentry->d_inode; | ||
| 479 | |||
| 480 | unmap_mapping_range(inode->i_mapping, 0, 0, 1); | ||
| 481 | } | ||
| 482 | |||
| 483 | mutex_unlock(&sysfs_bin_lock); | ||
| 484 | } | ||
| 485 | |||
| 259 | /** | 486 | /** |
| 260 | * sysfs_create_bin_file - create binary file for object. | 487 | * sysfs_create_bin_file - create binary file for object. |
| 261 | * @kobj: object. | 488 | * @kobj: object. |
