diff options
Diffstat (limited to 'fs/proc/generic.c')
-rw-r--r-- | fs/proc/generic.c | 52 |
1 files changed, 38 insertions, 14 deletions
diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 8a40e15f5ecb..b5e7155d30d8 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/namei.h> | 20 | #include <linux/namei.h> |
21 | #include <linux/bitops.h> | 21 | #include <linux/bitops.h> |
22 | #include <linux/spinlock.h> | 22 | #include <linux/spinlock.h> |
23 | #include <linux/completion.h> | ||
23 | #include <asm/uaccess.h> | 24 | #include <asm/uaccess.h> |
24 | 25 | ||
25 | #include "internal.h" | 26 | #include "internal.h" |
@@ -529,12 +530,6 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp | |||
529 | return -EAGAIN; | 530 | return -EAGAIN; |
530 | dp->low_ino = i; | 531 | dp->low_ino = i; |
531 | 532 | ||
532 | spin_lock(&proc_subdir_lock); | ||
533 | dp->next = dir->subdir; | ||
534 | dp->parent = dir; | ||
535 | dir->subdir = dp; | ||
536 | spin_unlock(&proc_subdir_lock); | ||
537 | |||
538 | if (S_ISDIR(dp->mode)) { | 533 | if (S_ISDIR(dp->mode)) { |
539 | if (dp->proc_iops == NULL) { | 534 | if (dp->proc_iops == NULL) { |
540 | dp->proc_fops = &proc_dir_operations; | 535 | dp->proc_fops = &proc_dir_operations; |
@@ -550,6 +545,13 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp | |||
550 | if (dp->proc_iops == NULL) | 545 | if (dp->proc_iops == NULL) |
551 | dp->proc_iops = &proc_file_inode_operations; | 546 | dp->proc_iops = &proc_file_inode_operations; |
552 | } | 547 | } |
548 | |||
549 | spin_lock(&proc_subdir_lock); | ||
550 | dp->next = dir->subdir; | ||
551 | dp->parent = dir; | ||
552 | dir->subdir = dp; | ||
553 | spin_unlock(&proc_subdir_lock); | ||
554 | |||
553 | return 0; | 555 | return 0; |
554 | } | 556 | } |
555 | 557 | ||
@@ -613,6 +615,9 @@ static struct proc_dir_entry *proc_create(struct proc_dir_entry **parent, | |||
613 | ent->namelen = len; | 615 | ent->namelen = len; |
614 | ent->mode = mode; | 616 | ent->mode = mode; |
615 | ent->nlink = nlink; | 617 | ent->nlink = nlink; |
618 | ent->pde_users = 0; | ||
619 | spin_lock_init(&ent->pde_unload_lock); | ||
620 | ent->pde_unload_completion = NULL; | ||
616 | out: | 621 | out: |
617 | return ent; | 622 | return ent; |
618 | } | 623 | } |
@@ -649,9 +654,6 @@ struct proc_dir_entry *proc_mkdir_mode(const char *name, mode_t mode, | |||
649 | 654 | ||
650 | ent = proc_create(&parent, name, S_IFDIR | mode, 2); | 655 | ent = proc_create(&parent, name, S_IFDIR | mode, 2); |
651 | if (ent) { | 656 | if (ent) { |
652 | ent->proc_fops = &proc_dir_operations; | ||
653 | ent->proc_iops = &proc_dir_inode_operations; | ||
654 | |||
655 | if (proc_register(parent, ent) < 0) { | 657 | if (proc_register(parent, ent) < 0) { |
656 | kfree(ent); | 658 | kfree(ent); |
657 | ent = NULL; | 659 | ent = NULL; |
@@ -686,10 +688,6 @@ struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, | |||
686 | 688 | ||
687 | ent = proc_create(&parent,name,mode,nlink); | 689 | ent = proc_create(&parent,name,mode,nlink); |
688 | if (ent) { | 690 | if (ent) { |
689 | if (S_ISDIR(mode)) { | ||
690 | ent->proc_fops = &proc_dir_operations; | ||
691 | ent->proc_iops = &proc_dir_inode_operations; | ||
692 | } | ||
693 | if (proc_register(parent, ent) < 0) { | 691 | if (proc_register(parent, ent) < 0) { |
694 | kfree(ent); | 692 | kfree(ent); |
695 | ent = NULL; | 693 | ent = NULL; |
@@ -734,9 +732,35 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) | |||
734 | de = *p; | 732 | de = *p; |
735 | *p = de->next; | 733 | *p = de->next; |
736 | de->next = NULL; | 734 | de->next = NULL; |
735 | |||
736 | spin_lock(&de->pde_unload_lock); | ||
737 | /* | ||
738 | * Stop accepting new callers into module. If you're | ||
739 | * dynamically allocating ->proc_fops, save a pointer somewhere. | ||
740 | */ | ||
741 | de->proc_fops = NULL; | ||
742 | /* Wait until all existing callers into module are done. */ | ||
743 | if (de->pde_users > 0) { | ||
744 | DECLARE_COMPLETION_ONSTACK(c); | ||
745 | |||
746 | if (!de->pde_unload_completion) | ||
747 | de->pde_unload_completion = &c; | ||
748 | |||
749 | spin_unlock(&de->pde_unload_lock); | ||
750 | spin_unlock(&proc_subdir_lock); | ||
751 | |||
752 | wait_for_completion(de->pde_unload_completion); | ||
753 | |||
754 | spin_lock(&proc_subdir_lock); | ||
755 | goto continue_removing; | ||
756 | } | ||
757 | spin_unlock(&de->pde_unload_lock); | ||
758 | |||
759 | continue_removing: | ||
737 | if (S_ISDIR(de->mode)) | 760 | if (S_ISDIR(de->mode)) |
738 | parent->nlink--; | 761 | parent->nlink--; |
739 | proc_kill_inodes(de); | 762 | if (!S_ISREG(de->mode)) |
763 | proc_kill_inodes(de); | ||
740 | de->nlink = 0; | 764 | de->nlink = 0; |
741 | WARN_ON(de->subdir); | 765 | WARN_ON(de->subdir); |
742 | if (!atomic_read(&de->count)) | 766 | if (!atomic_read(&de->count)) |