diff options
Diffstat (limited to 'fs/sysfs')
| -rw-r--r-- | fs/sysfs/bin.c | 253 | ||||
| -rw-r--r-- | fs/sysfs/dir.c | 35 | ||||
| -rw-r--r-- | fs/sysfs/file.c | 26 | ||||
| -rw-r--r-- | fs/sysfs/inode.c | 17 | ||||
| -rw-r--r-- | fs/sysfs/mount.c | 6 | ||||
| -rw-r--r-- | fs/sysfs/sysfs.h | 3 |
6 files changed, 319 insertions, 21 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. |
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 82d3b79d0e08..d88d0fac9fa5 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
| @@ -302,7 +302,7 @@ static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) | |||
| 302 | iput(inode); | 302 | iput(inode); |
| 303 | } | 303 | } |
| 304 | 304 | ||
| 305 | static struct dentry_operations sysfs_dentry_ops = { | 305 | static const struct dentry_operations sysfs_dentry_ops = { |
| 306 | .d_iput = sysfs_d_iput, | 306 | .d_iput = sysfs_d_iput, |
| 307 | }; | 307 | }; |
| 308 | 308 | ||
| @@ -434,6 +434,26 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) | |||
| 434 | } | 434 | } |
| 435 | 435 | ||
| 436 | /** | 436 | /** |
| 437 | * sysfs_pathname - return full path to sysfs dirent | ||
| 438 | * @sd: sysfs_dirent whose path we want | ||
| 439 | * @path: caller allocated buffer | ||
| 440 | * | ||
| 441 | * Gives the name "/" to the sysfs_root entry; any path returned | ||
| 442 | * is relative to wherever sysfs is mounted. | ||
| 443 | * | ||
| 444 | * XXX: does no error checking on @path size | ||
| 445 | */ | ||
| 446 | static char *sysfs_pathname(struct sysfs_dirent *sd, char *path) | ||
| 447 | { | ||
| 448 | if (sd->s_parent) { | ||
| 449 | sysfs_pathname(sd->s_parent, path); | ||
| 450 | strcat(path, "/"); | ||
| 451 | } | ||
| 452 | strcat(path, sd->s_name); | ||
| 453 | return path; | ||
| 454 | } | ||
| 455 | |||
| 456 | /** | ||
| 437 | * sysfs_add_one - add sysfs_dirent to parent | 457 | * sysfs_add_one - add sysfs_dirent to parent |
| 438 | * @acxt: addrm context to use | 458 | * @acxt: addrm context to use |
| 439 | * @sd: sysfs_dirent to be added | 459 | * @sd: sysfs_dirent to be added |
| @@ -458,8 +478,16 @@ int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) | |||
| 458 | int ret; | 478 | int ret; |
| 459 | 479 | ||
| 460 | ret = __sysfs_add_one(acxt, sd); | 480 | ret = __sysfs_add_one(acxt, sd); |
| 461 | WARN(ret == -EEXIST, KERN_WARNING "sysfs: duplicate filename '%s' " | 481 | if (ret == -EEXIST) { |
| 462 | "can not be created\n", sd->s_name); | 482 | char *path = kzalloc(PATH_MAX, GFP_KERNEL); |
| 483 | WARN(1, KERN_WARNING | ||
| 484 | "sysfs: cannot create duplicate filename '%s'\n", | ||
| 485 | (path == NULL) ? sd->s_name : | ||
| 486 | strcat(strcat(sysfs_pathname(acxt->parent_sd, path), "/"), | ||
| 487 | sd->s_name)); | ||
| 488 | kfree(path); | ||
| 489 | } | ||
| 490 | |||
| 463 | return ret; | 491 | return ret; |
| 464 | } | 492 | } |
| 465 | 493 | ||
| @@ -581,6 +609,7 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) | |||
| 581 | 609 | ||
| 582 | sysfs_drop_dentry(sd); | 610 | sysfs_drop_dentry(sd); |
| 583 | sysfs_deactivate(sd); | 611 | sysfs_deactivate(sd); |
| 612 | unmap_bin_file(sd); | ||
| 584 | sysfs_put(sd); | 613 | sysfs_put(sd); |
| 585 | } | 614 | } |
| 586 | } | 615 | } |
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 1f4a3f877262..289c43a47263 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c | |||
| @@ -659,13 +659,16 @@ void sysfs_remove_file_from_group(struct kobject *kobj, | |||
| 659 | EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); | 659 | EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); |
| 660 | 660 | ||
| 661 | struct sysfs_schedule_callback_struct { | 661 | struct sysfs_schedule_callback_struct { |
| 662 | struct kobject *kobj; | 662 | struct list_head workq_list; |
| 663 | struct kobject *kobj; | ||
| 663 | void (*func)(void *); | 664 | void (*func)(void *); |
| 664 | void *data; | 665 | void *data; |
| 665 | struct module *owner; | 666 | struct module *owner; |
| 666 | struct work_struct work; | 667 | struct work_struct work; |
| 667 | }; | 668 | }; |
| 668 | 669 | ||
| 670 | static DEFINE_MUTEX(sysfs_workq_mutex); | ||
| 671 | static LIST_HEAD(sysfs_workq); | ||
| 669 | static void sysfs_schedule_callback_work(struct work_struct *work) | 672 | static void sysfs_schedule_callback_work(struct work_struct *work) |
| 670 | { | 673 | { |
| 671 | struct sysfs_schedule_callback_struct *ss = container_of(work, | 674 | struct sysfs_schedule_callback_struct *ss = container_of(work, |
| @@ -674,6 +677,9 @@ static void sysfs_schedule_callback_work(struct work_struct *work) | |||
| 674 | (ss->func)(ss->data); | 677 | (ss->func)(ss->data); |
| 675 | kobject_put(ss->kobj); | 678 | kobject_put(ss->kobj); |
| 676 | module_put(ss->owner); | 679 | module_put(ss->owner); |
| 680 | mutex_lock(&sysfs_workq_mutex); | ||
| 681 | list_del(&ss->workq_list); | ||
| 682 | mutex_unlock(&sysfs_workq_mutex); | ||
| 677 | kfree(ss); | 683 | kfree(ss); |
| 678 | } | 684 | } |
| 679 | 685 | ||
| @@ -695,15 +701,25 @@ static void sysfs_schedule_callback_work(struct work_struct *work) | |||
| 695 | * until @func returns. | 701 | * until @func returns. |
| 696 | * | 702 | * |
| 697 | * Returns 0 if the request was submitted, -ENOMEM if storage could not | 703 | * Returns 0 if the request was submitted, -ENOMEM if storage could not |
| 698 | * be allocated, -ENODEV if a reference to @owner isn't available. | 704 | * be allocated, -ENODEV if a reference to @owner isn't available, |
| 705 | * -EAGAIN if a callback has already been scheduled for @kobj. | ||
| 699 | */ | 706 | */ |
| 700 | int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), | 707 | int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), |
| 701 | void *data, struct module *owner) | 708 | void *data, struct module *owner) |
| 702 | { | 709 | { |
| 703 | struct sysfs_schedule_callback_struct *ss; | 710 | struct sysfs_schedule_callback_struct *ss, *tmp; |
| 704 | 711 | ||
| 705 | if (!try_module_get(owner)) | 712 | if (!try_module_get(owner)) |
| 706 | return -ENODEV; | 713 | return -ENODEV; |
| 714 | |||
| 715 | mutex_lock(&sysfs_workq_mutex); | ||
| 716 | list_for_each_entry_safe(ss, tmp, &sysfs_workq, workq_list) | ||
| 717 | if (ss->kobj == kobj) { | ||
| 718 | mutex_unlock(&sysfs_workq_mutex); | ||
| 719 | return -EAGAIN; | ||
| 720 | } | ||
| 721 | mutex_unlock(&sysfs_workq_mutex); | ||
| 722 | |||
| 707 | ss = kmalloc(sizeof(*ss), GFP_KERNEL); | 723 | ss = kmalloc(sizeof(*ss), GFP_KERNEL); |
| 708 | if (!ss) { | 724 | if (!ss) { |
| 709 | module_put(owner); | 725 | module_put(owner); |
| @@ -715,6 +731,10 @@ int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), | |||
| 715 | ss->data = data; | 731 | ss->data = data; |
| 716 | ss->owner = owner; | 732 | ss->owner = owner; |
| 717 | INIT_WORK(&ss->work, sysfs_schedule_callback_work); | 733 | INIT_WORK(&ss->work, sysfs_schedule_callback_work); |
| 734 | INIT_LIST_HEAD(&ss->workq_list); | ||
| 735 | mutex_lock(&sysfs_workq_mutex); | ||
| 736 | list_add_tail(&ss->workq_list, &sysfs_workq); | ||
| 737 | mutex_unlock(&sysfs_workq_mutex); | ||
| 718 | schedule_work(&ss->work); | 738 | schedule_work(&ss->work); |
| 719 | return 0; | 739 | return 0; |
| 720 | } | 740 | } |
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index dfa3d94cfc74..555f0ff988df 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c | |||
| @@ -147,6 +147,7 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) | |||
| 147 | { | 147 | { |
| 148 | struct bin_attribute *bin_attr; | 148 | struct bin_attribute *bin_attr; |
| 149 | 149 | ||
| 150 | inode->i_private = sysfs_get(sd); | ||
| 150 | inode->i_mapping->a_ops = &sysfs_aops; | 151 | inode->i_mapping->a_ops = &sysfs_aops; |
| 151 | inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; | 152 | inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; |
| 152 | inode->i_op = &sysfs_inode_operations; | 153 | inode->i_op = &sysfs_inode_operations; |
| @@ -214,6 +215,22 @@ struct inode * sysfs_get_inode(struct sysfs_dirent *sd) | |||
| 214 | return inode; | 215 | return inode; |
| 215 | } | 216 | } |
| 216 | 217 | ||
| 218 | /* | ||
| 219 | * The sysfs_dirent serves as both an inode and a directory entry for sysfs. | ||
| 220 | * To prevent the sysfs inode numbers from being freed prematurely we take a | ||
| 221 | * reference to sysfs_dirent from the sysfs inode. A | ||
| 222 | * super_operations.delete_inode() implementation is needed to drop that | ||
| 223 | * reference upon inode destruction. | ||
| 224 | */ | ||
| 225 | void sysfs_delete_inode(struct inode *inode) | ||
| 226 | { | ||
| 227 | struct sysfs_dirent *sd = inode->i_private; | ||
| 228 | |||
| 229 | truncate_inode_pages(&inode->i_data, 0); | ||
| 230 | clear_inode(inode); | ||
| 231 | sysfs_put(sd); | ||
| 232 | } | ||
| 233 | |||
| 217 | int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) | 234 | int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) |
| 218 | { | 235 | { |
| 219 | struct sysfs_addrm_cxt acxt; | 236 | struct sysfs_addrm_cxt acxt; |
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index ab343e371d64..49749955ccaf 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c | |||
| @@ -17,11 +17,10 @@ | |||
| 17 | #include <linux/pagemap.h> | 17 | #include <linux/pagemap.h> |
| 18 | #include <linux/init.h> | 18 | #include <linux/init.h> |
| 19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
| 20 | #include <linux/magic.h> | ||
| 20 | 21 | ||
| 21 | #include "sysfs.h" | 22 | #include "sysfs.h" |
| 22 | 23 | ||
| 23 | /* Random magic number */ | ||
| 24 | #define SYSFS_MAGIC 0x62656572 | ||
| 25 | 24 | ||
| 26 | static struct vfsmount *sysfs_mount; | 25 | static struct vfsmount *sysfs_mount; |
| 27 | struct super_block * sysfs_sb = NULL; | 26 | struct super_block * sysfs_sb = NULL; |
| @@ -30,6 +29,7 @@ struct kmem_cache *sysfs_dir_cachep; | |||
| 30 | static const struct super_operations sysfs_ops = { | 29 | static const struct super_operations sysfs_ops = { |
| 31 | .statfs = simple_statfs, | 30 | .statfs = simple_statfs, |
| 32 | .drop_inode = generic_delete_inode, | 31 | .drop_inode = generic_delete_inode, |
| 32 | .delete_inode = sysfs_delete_inode, | ||
| 33 | }; | 33 | }; |
| 34 | 34 | ||
| 35 | struct sysfs_dirent sysfs_root = { | 35 | struct sysfs_dirent sysfs_root = { |
| @@ -53,7 +53,9 @@ static int sysfs_fill_super(struct super_block *sb, void *data, int silent) | |||
| 53 | sysfs_sb = sb; | 53 | sysfs_sb = sb; |
| 54 | 54 | ||
| 55 | /* get root inode, initialize and unlock it */ | 55 | /* get root inode, initialize and unlock it */ |
| 56 | mutex_lock(&sysfs_mutex); | ||
| 56 | inode = sysfs_get_inode(&sysfs_root); | 57 | inode = sysfs_get_inode(&sysfs_root); |
| 58 | mutex_unlock(&sysfs_mutex); | ||
| 57 | if (!inode) { | 59 | if (!inode) { |
| 58 | pr_debug("sysfs: could not get root inode\n"); | 60 | pr_debug("sysfs: could not get root inode\n"); |
| 59 | return -ENOMEM; | 61 | return -ENOMEM; |
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 93c6d6b27c4d..3fa0d98481e2 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h | |||
| @@ -28,6 +28,7 @@ struct sysfs_elem_attr { | |||
| 28 | 28 | ||
| 29 | struct sysfs_elem_bin_attr { | 29 | struct sysfs_elem_bin_attr { |
| 30 | struct bin_attribute *bin_attr; | 30 | struct bin_attribute *bin_attr; |
| 31 | struct hlist_head buffers; | ||
| 31 | }; | 32 | }; |
| 32 | 33 | ||
| 33 | /* | 34 | /* |
| @@ -145,6 +146,7 @@ static inline void __sysfs_put(struct sysfs_dirent *sd) | |||
| 145 | * inode.c | 146 | * inode.c |
| 146 | */ | 147 | */ |
| 147 | struct inode *sysfs_get_inode(struct sysfs_dirent *sd); | 148 | struct inode *sysfs_get_inode(struct sysfs_dirent *sd); |
| 149 | void sysfs_delete_inode(struct inode *inode); | ||
| 148 | int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); | 150 | int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); |
| 149 | int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name); | 151 | int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name); |
| 150 | int sysfs_inode_init(void); | 152 | int sysfs_inode_init(void); |
| @@ -163,6 +165,7 @@ int sysfs_add_file_mode(struct sysfs_dirent *dir_sd, | |||
| 163 | * bin.c | 165 | * bin.c |
| 164 | */ | 166 | */ |
| 165 | extern const struct file_operations bin_fops; | 167 | extern const struct file_operations bin_fops; |
| 168 | void unmap_bin_file(struct sysfs_dirent *attr_sd); | ||
| 166 | 169 | ||
| 167 | /* | 170 | /* |
| 168 | * symlink.c | 171 | * symlink.c |
