diff options
Diffstat (limited to 'fs/proc/inode.c')
| -rw-r--r-- | fs/proc/inode.c | 88 |
1 files changed, 76 insertions, 12 deletions
diff --git a/fs/proc/inode.c b/fs/proc/inode.c index b08d10017911..8bb03f056c28 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <linux/init.h> | 17 | #include <linux/init.h> |
| 18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
| 19 | #include <linux/smp_lock.h> | 19 | #include <linux/smp_lock.h> |
| 20 | #include <linux/sysctl.h> | ||
| 20 | 21 | ||
| 21 | #include <asm/system.h> | 22 | #include <asm/system.h> |
| 22 | #include <asm/uaccess.h> | 23 | #include <asm/uaccess.h> |
| @@ -65,6 +66,8 @@ static void proc_delete_inode(struct inode *inode) | |||
| 65 | module_put(de->owner); | 66 | module_put(de->owner); |
| 66 | de_put(de); | 67 | de_put(de); |
| 67 | } | 68 | } |
| 69 | if (PROC_I(inode)->sysctl) | ||
| 70 | sysctl_head_put(PROC_I(inode)->sysctl); | ||
| 68 | clear_inode(inode); | 71 | clear_inode(inode); |
| 69 | } | 72 | } |
| 70 | 73 | ||
| @@ -84,6 +87,8 @@ static struct inode *proc_alloc_inode(struct super_block *sb) | |||
| 84 | ei->fd = 0; | 87 | ei->fd = 0; |
| 85 | ei->op.proc_get_link = NULL; | 88 | ei->op.proc_get_link = NULL; |
| 86 | ei->pde = NULL; | 89 | ei->pde = NULL; |
| 90 | ei->sysctl = NULL; | ||
| 91 | ei->sysctl_entry = NULL; | ||
| 87 | inode = &ei->vfs_inode; | 92 | inode = &ei->vfs_inode; |
| 88 | inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; | 93 | inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; |
| 89 | return inode; | 94 | return inode; |
| @@ -94,7 +99,7 @@ static void proc_destroy_inode(struct inode *inode) | |||
| 94 | kmem_cache_free(proc_inode_cachep, PROC_I(inode)); | 99 | kmem_cache_free(proc_inode_cachep, PROC_I(inode)); |
| 95 | } | 100 | } |
| 96 | 101 | ||
| 97 | static void init_once(struct kmem_cache * cachep, void *foo) | 102 | static void init_once(void *foo) |
| 98 | { | 103 | { |
| 99 | struct proc_inode *ei = (struct proc_inode *) foo; | 104 | struct proc_inode *ei = (struct proc_inode *) foo; |
| 100 | 105 | ||
| @@ -111,27 +116,25 @@ int __init proc_init_inodecache(void) | |||
| 111 | return 0; | 116 | return 0; |
| 112 | } | 117 | } |
| 113 | 118 | ||
| 114 | static int proc_remount(struct super_block *sb, int *flags, char *data) | ||
| 115 | { | ||
| 116 | *flags |= MS_NODIRATIME; | ||
| 117 | return 0; | ||
| 118 | } | ||
| 119 | |||
| 120 | static const struct super_operations proc_sops = { | 119 | static const struct super_operations proc_sops = { |
| 121 | .alloc_inode = proc_alloc_inode, | 120 | .alloc_inode = proc_alloc_inode, |
| 122 | .destroy_inode = proc_destroy_inode, | 121 | .destroy_inode = proc_destroy_inode, |
| 123 | .drop_inode = generic_delete_inode, | 122 | .drop_inode = generic_delete_inode, |
| 124 | .delete_inode = proc_delete_inode, | 123 | .delete_inode = proc_delete_inode, |
| 125 | .statfs = simple_statfs, | 124 | .statfs = simple_statfs, |
| 126 | .remount_fs = proc_remount, | ||
| 127 | }; | 125 | }; |
| 128 | 126 | ||
| 129 | static void pde_users_dec(struct proc_dir_entry *pde) | 127 | static void __pde_users_dec(struct proc_dir_entry *pde) |
| 130 | { | 128 | { |
| 131 | spin_lock(&pde->pde_unload_lock); | ||
| 132 | pde->pde_users--; | 129 | pde->pde_users--; |
| 133 | if (pde->pde_unload_completion && pde->pde_users == 0) | 130 | if (pde->pde_unload_completion && pde->pde_users == 0) |
| 134 | complete(pde->pde_unload_completion); | 131 | complete(pde->pde_unload_completion); |
| 132 | } | ||
| 133 | |||
| 134 | static void pde_users_dec(struct proc_dir_entry *pde) | ||
| 135 | { | ||
| 136 | spin_lock(&pde->pde_unload_lock); | ||
| 137 | __pde_users_dec(pde); | ||
| 135 | spin_unlock(&pde->pde_unload_lock); | 138 | spin_unlock(&pde->pde_unload_lock); |
| 136 | } | 139 | } |
| 137 | 140 | ||
| @@ -318,36 +321,97 @@ static int proc_reg_open(struct inode *inode, struct file *file) | |||
| 318 | struct proc_dir_entry *pde = PDE(inode); | 321 | struct proc_dir_entry *pde = PDE(inode); |
| 319 | int rv = 0; | 322 | int rv = 0; |
| 320 | int (*open)(struct inode *, struct file *); | 323 | int (*open)(struct inode *, struct file *); |
| 324 | int (*release)(struct inode *, struct file *); | ||
| 325 | struct pde_opener *pdeo; | ||
| 326 | |||
| 327 | /* | ||
| 328 | * What for, you ask? Well, we can have open, rmmod, remove_proc_entry | ||
| 329 | * sequence. ->release won't be called because ->proc_fops will be | ||
| 330 | * cleared. Depending on complexity of ->release, consequences vary. | ||
| 331 | * | ||
| 332 | * We can't wait for mercy when close will be done for real, it's | ||
| 333 | * deadlockable: rmmod foo </proc/foo . So, we're going to do ->release | ||
| 334 | * by hand in remove_proc_entry(). For this, save opener's credentials | ||
| 335 | * for later. | ||
| 336 | */ | ||
| 337 | pdeo = kmalloc(sizeof(struct pde_opener), GFP_KERNEL); | ||
| 338 | if (!pdeo) | ||
| 339 | return -ENOMEM; | ||
| 321 | 340 | ||
| 322 | spin_lock(&pde->pde_unload_lock); | 341 | spin_lock(&pde->pde_unload_lock); |
| 323 | if (!pde->proc_fops) { | 342 | if (!pde->proc_fops) { |
| 324 | spin_unlock(&pde->pde_unload_lock); | 343 | spin_unlock(&pde->pde_unload_lock); |
| 344 | kfree(pdeo); | ||
| 325 | return rv; | 345 | return rv; |
| 326 | } | 346 | } |
| 327 | pde->pde_users++; | 347 | pde->pde_users++; |
| 328 | open = pde->proc_fops->open; | 348 | open = pde->proc_fops->open; |
| 349 | release = pde->proc_fops->release; | ||
| 329 | spin_unlock(&pde->pde_unload_lock); | 350 | spin_unlock(&pde->pde_unload_lock); |
| 330 | 351 | ||
| 331 | if (open) | 352 | if (open) |
| 332 | rv = open(inode, file); | 353 | rv = open(inode, file); |
| 333 | 354 | ||
| 334 | pde_users_dec(pde); | 355 | spin_lock(&pde->pde_unload_lock); |
| 356 | if (rv == 0 && release) { | ||
| 357 | /* To know what to release. */ | ||
| 358 | pdeo->inode = inode; | ||
| 359 | pdeo->file = file; | ||
| 360 | /* Strictly for "too late" ->release in proc_reg_release(). */ | ||
| 361 | pdeo->release = release; | ||
| 362 | list_add(&pdeo->lh, &pde->pde_openers); | ||
| 363 | } else | ||
| 364 | kfree(pdeo); | ||
| 365 | __pde_users_dec(pde); | ||
| 366 | spin_unlock(&pde->pde_unload_lock); | ||
| 335 | return rv; | 367 | return rv; |
| 336 | } | 368 | } |
| 337 | 369 | ||
| 370 | static struct pde_opener *find_pde_opener(struct proc_dir_entry *pde, | ||
| 371 | struct inode *inode, struct file *file) | ||
| 372 | { | ||
| 373 | struct pde_opener *pdeo; | ||
| 374 | |||
| 375 | list_for_each_entry(pdeo, &pde->pde_openers, lh) { | ||
| 376 | if (pdeo->inode == inode && pdeo->file == file) | ||
| 377 | return pdeo; | ||
| 378 | } | ||
| 379 | return NULL; | ||
| 380 | } | ||
| 381 | |||
| 338 | static int proc_reg_release(struct inode *inode, struct file *file) | 382 | static int proc_reg_release(struct inode *inode, struct file *file) |
| 339 | { | 383 | { |
| 340 | struct proc_dir_entry *pde = PDE(inode); | 384 | struct proc_dir_entry *pde = PDE(inode); |
| 341 | int rv = 0; | 385 | int rv = 0; |
| 342 | int (*release)(struct inode *, struct file *); | 386 | int (*release)(struct inode *, struct file *); |
| 387 | struct pde_opener *pdeo; | ||
| 343 | 388 | ||
| 344 | spin_lock(&pde->pde_unload_lock); | 389 | spin_lock(&pde->pde_unload_lock); |
| 390 | pdeo = find_pde_opener(pde, inode, file); | ||
| 345 | if (!pde->proc_fops) { | 391 | if (!pde->proc_fops) { |
| 346 | spin_unlock(&pde->pde_unload_lock); | 392 | /* |
| 393 | * Can't simply exit, __fput() will think that everything is OK, | ||
| 394 | * and move on to freeing struct file. remove_proc_entry() will | ||
| 395 | * find slacker in opener's list and will try to do non-trivial | ||
| 396 | * things with struct file. Therefore, remove opener from list. | ||
| 397 | * | ||
| 398 | * But if opener is removed from list, who will ->release it? | ||
| 399 | */ | ||
| 400 | if (pdeo) { | ||
| 401 | list_del(&pdeo->lh); | ||
| 402 | spin_unlock(&pde->pde_unload_lock); | ||
| 403 | rv = pdeo->release(inode, file); | ||
| 404 | kfree(pdeo); | ||
| 405 | } else | ||
| 406 | spin_unlock(&pde->pde_unload_lock); | ||
| 347 | return rv; | 407 | return rv; |
| 348 | } | 408 | } |
| 349 | pde->pde_users++; | 409 | pde->pde_users++; |
| 350 | release = pde->proc_fops->release; | 410 | release = pde->proc_fops->release; |
| 411 | if (pdeo) { | ||
| 412 | list_del(&pdeo->lh); | ||
| 413 | kfree(pdeo); | ||
| 414 | } | ||
| 351 | spin_unlock(&pde->pde_unload_lock); | 415 | spin_unlock(&pde->pde_unload_lock); |
| 352 | 416 | ||
| 353 | if (release) | 417 | if (release) |
