diff options
author | Al Viro <viro@ftp.linux.org.uk> | 2008-05-01 17:36:36 -0400 |
---|---|---|
committer | Tony Luck <tony.luck@intel.com> | 2008-05-01 17:36:36 -0400 |
commit | f8e811b98935f702b48abc92563462a15c226eb8 (patch) | |
tree | 5446de054b7d168c118783df77577f14d4055d81 /arch | |
parent | 848376c774a941c29e4fa083d96d84a5f2190857 (diff) |
[IA64] fix file and descriptor handling in perfmon
Races galore... General rule: as soon as it's in descriptor table,
it's over; another thread might have started IO on it/dup2() it
elsewhere/dup2() something *over* it/etc. fd_install() is the very
last step one should take - it's a point of no return.
Besides, the damn thing leaked on failure exits...
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/ia64/kernel/perfmon.c | 199 |
1 files changed, 87 insertions, 112 deletions
diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index 7fbb51e10bbe..c1ad27de2dd2 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c | |||
@@ -867,7 +867,7 @@ pfm_rvfree(void *mem, unsigned long size) | |||
867 | } | 867 | } |
868 | 868 | ||
869 | static pfm_context_t * | 869 | static pfm_context_t * |
870 | pfm_context_alloc(void) | 870 | pfm_context_alloc(int ctx_flags) |
871 | { | 871 | { |
872 | pfm_context_t *ctx; | 872 | pfm_context_t *ctx; |
873 | 873 | ||
@@ -878,6 +878,46 @@ pfm_context_alloc(void) | |||
878 | ctx = kzalloc(sizeof(pfm_context_t), GFP_KERNEL); | 878 | ctx = kzalloc(sizeof(pfm_context_t), GFP_KERNEL); |
879 | if (ctx) { | 879 | if (ctx) { |
880 | DPRINT(("alloc ctx @%p\n", ctx)); | 880 | DPRINT(("alloc ctx @%p\n", ctx)); |
881 | |||
882 | /* | ||
883 | * init context protection lock | ||
884 | */ | ||
885 | spin_lock_init(&ctx->ctx_lock); | ||
886 | |||
887 | /* | ||
888 | * context is unloaded | ||
889 | */ | ||
890 | ctx->ctx_state = PFM_CTX_UNLOADED; | ||
891 | |||
892 | /* | ||
893 | * initialization of context's flags | ||
894 | */ | ||
895 | ctx->ctx_fl_block = (ctx_flags & PFM_FL_NOTIFY_BLOCK) ? 1 : 0; | ||
896 | ctx->ctx_fl_system = (ctx_flags & PFM_FL_SYSTEM_WIDE) ? 1: 0; | ||
897 | ctx->ctx_fl_no_msg = (ctx_flags & PFM_FL_OVFL_NO_MSG) ? 1: 0; | ||
898 | /* | ||
899 | * will move to set properties | ||
900 | * ctx->ctx_fl_excl_idle = (ctx_flags & PFM_FL_EXCL_IDLE) ? 1: 0; | ||
901 | */ | ||
902 | |||
903 | /* | ||
904 | * init restart semaphore to locked | ||
905 | */ | ||
906 | init_completion(&ctx->ctx_restart_done); | ||
907 | |||
908 | /* | ||
909 | * activation is used in SMP only | ||
910 | */ | ||
911 | ctx->ctx_last_activation = PFM_INVALID_ACTIVATION; | ||
912 | SET_LAST_CPU(ctx, -1); | ||
913 | |||
914 | /* | ||
915 | * initialize notification message queue | ||
916 | */ | ||
917 | ctx->ctx_msgq_head = ctx->ctx_msgq_tail = 0; | ||
918 | init_waitqueue_head(&ctx->ctx_msgq_wait); | ||
919 | init_waitqueue_head(&ctx->ctx_zombieq); | ||
920 | |||
881 | } | 921 | } |
882 | return ctx; | 922 | return ctx; |
883 | } | 923 | } |
@@ -2165,28 +2205,21 @@ static struct dentry_operations pfmfs_dentry_operations = { | |||
2165 | }; | 2205 | }; |
2166 | 2206 | ||
2167 | 2207 | ||
2168 | static int | 2208 | static struct file * |
2169 | pfm_alloc_fd(struct file **cfile) | 2209 | pfm_alloc_file(pfm_context_t *ctx) |
2170 | { | 2210 | { |
2171 | int fd, ret = 0; | 2211 | struct file *file; |
2172 | struct file *file = NULL; | 2212 | struct inode *inode; |
2173 | struct inode * inode; | 2213 | struct dentry *dentry; |
2174 | char name[32]; | 2214 | char name[32]; |
2175 | struct qstr this; | 2215 | struct qstr this; |
2176 | 2216 | ||
2177 | fd = get_unused_fd(); | ||
2178 | if (fd < 0) return -ENFILE; | ||
2179 | |||
2180 | ret = -ENFILE; | ||
2181 | |||
2182 | file = get_empty_filp(); | ||
2183 | if (!file) goto out; | ||
2184 | |||
2185 | /* | 2217 | /* |
2186 | * allocate a new inode | 2218 | * allocate a new inode |
2187 | */ | 2219 | */ |
2188 | inode = new_inode(pfmfs_mnt->mnt_sb); | 2220 | inode = new_inode(pfmfs_mnt->mnt_sb); |
2189 | if (!inode) goto out; | 2221 | if (!inode) |
2222 | return ERR_PTR(-ENOMEM); | ||
2190 | 2223 | ||
2191 | DPRINT(("new inode ino=%ld @%p\n", inode->i_ino, inode)); | 2224 | DPRINT(("new inode ino=%ld @%p\n", inode->i_ino, inode)); |
2192 | 2225 | ||
@@ -2199,59 +2232,28 @@ pfm_alloc_fd(struct file **cfile) | |||
2199 | this.len = strlen(name); | 2232 | this.len = strlen(name); |
2200 | this.hash = inode->i_ino; | 2233 | this.hash = inode->i_ino; |
2201 | 2234 | ||
2202 | ret = -ENOMEM; | ||
2203 | |||
2204 | /* | 2235 | /* |
2205 | * allocate a new dcache entry | 2236 | * allocate a new dcache entry |
2206 | */ | 2237 | */ |
2207 | file->f_path.dentry = d_alloc(pfmfs_mnt->mnt_sb->s_root, &this); | 2238 | dentry = d_alloc(pfmfs_mnt->mnt_sb->s_root, &this); |
2208 | if (!file->f_path.dentry) goto out; | 2239 | if (!dentry) { |
2240 | iput(inode); | ||
2241 | return ERR_PTR(-ENOMEM); | ||
2242 | } | ||
2209 | 2243 | ||
2210 | file->f_path.dentry->d_op = &pfmfs_dentry_operations; | 2244 | dentry->d_op = &pfmfs_dentry_operations; |
2245 | d_add(dentry, inode); | ||
2211 | 2246 | ||
2212 | d_add(file->f_path.dentry, inode); | 2247 | file = alloc_file(pfmfs_mnt, dentry, FMODE_READ, &pfm_file_ops); |
2213 | file->f_path.mnt = mntget(pfmfs_mnt); | 2248 | if (!file) { |
2214 | file->f_mapping = inode->i_mapping; | 2249 | dput(dentry); |
2250 | return ERR_PTR(-ENFILE); | ||
2251 | } | ||
2215 | 2252 | ||
2216 | file->f_op = &pfm_file_ops; | ||
2217 | file->f_mode = FMODE_READ; | ||
2218 | file->f_flags = O_RDONLY; | 2253 | file->f_flags = O_RDONLY; |
2219 | file->f_pos = 0; | 2254 | file->private_data = ctx; |
2220 | |||
2221 | /* | ||
2222 | * may have to delay until context is attached? | ||
2223 | */ | ||
2224 | fd_install(fd, file); | ||
2225 | |||
2226 | /* | ||
2227 | * the file structure we will use | ||
2228 | */ | ||
2229 | *cfile = file; | ||
2230 | |||
2231 | return fd; | ||
2232 | out: | ||
2233 | if (file) put_filp(file); | ||
2234 | put_unused_fd(fd); | ||
2235 | return ret; | ||
2236 | } | ||
2237 | |||
2238 | static void | ||
2239 | pfm_free_fd(int fd, struct file *file) | ||
2240 | { | ||
2241 | struct files_struct *files = current->files; | ||
2242 | struct fdtable *fdt; | ||
2243 | 2255 | ||
2244 | /* | 2256 | return file; |
2245 | * there ie no fd_uninstall(), so we do it here | ||
2246 | */ | ||
2247 | spin_lock(&files->file_lock); | ||
2248 | fdt = files_fdtable(files); | ||
2249 | rcu_assign_pointer(fdt->fd[fd], NULL); | ||
2250 | spin_unlock(&files->file_lock); | ||
2251 | |||
2252 | if (file) | ||
2253 | put_filp(file); | ||
2254 | put_unused_fd(fd); | ||
2255 | } | 2257 | } |
2256 | 2258 | ||
2257 | static int | 2259 | static int |
@@ -2475,6 +2477,7 @@ pfm_setup_buffer_fmt(struct task_struct *task, struct file *filp, pfm_context_t | |||
2475 | 2477 | ||
2476 | /* link buffer format and context */ | 2478 | /* link buffer format and context */ |
2477 | ctx->ctx_buf_fmt = fmt; | 2479 | ctx->ctx_buf_fmt = fmt; |
2480 | ctx->ctx_fl_is_sampling = 1; /* assume record() is defined */ | ||
2478 | 2481 | ||
2479 | /* | 2482 | /* |
2480 | * check if buffer format wants to use perfmon buffer allocation/mapping service | 2483 | * check if buffer format wants to use perfmon buffer allocation/mapping service |
@@ -2669,78 +2672,45 @@ pfm_context_create(pfm_context_t *ctx, void *arg, int count, struct pt_regs *reg | |||
2669 | { | 2672 | { |
2670 | pfarg_context_t *req = (pfarg_context_t *)arg; | 2673 | pfarg_context_t *req = (pfarg_context_t *)arg; |
2671 | struct file *filp; | 2674 | struct file *filp; |
2675 | struct path path; | ||
2672 | int ctx_flags; | 2676 | int ctx_flags; |
2677 | int fd; | ||
2673 | int ret; | 2678 | int ret; |
2674 | 2679 | ||
2675 | /* let's check the arguments first */ | 2680 | /* let's check the arguments first */ |
2676 | ret = pfarg_is_sane(current, req); | 2681 | ret = pfarg_is_sane(current, req); |
2677 | if (ret < 0) return ret; | 2682 | if (ret < 0) |
2683 | return ret; | ||
2678 | 2684 | ||
2679 | ctx_flags = req->ctx_flags; | 2685 | ctx_flags = req->ctx_flags; |
2680 | 2686 | ||
2681 | ret = -ENOMEM; | 2687 | ret = -ENOMEM; |
2682 | 2688 | ||
2683 | ctx = pfm_context_alloc(); | 2689 | fd = get_unused_fd(); |
2684 | if (!ctx) goto error; | 2690 | if (fd < 0) |
2691 | return fd; | ||
2685 | 2692 | ||
2686 | ret = pfm_alloc_fd(&filp); | 2693 | ctx = pfm_context_alloc(ctx_flags); |
2687 | if (ret < 0) goto error_file; | 2694 | if (!ctx) |
2695 | goto error; | ||
2688 | 2696 | ||
2689 | req->ctx_fd = ctx->ctx_fd = ret; | 2697 | filp = pfm_alloc_file(ctx); |
2698 | if (IS_ERR(filp)) { | ||
2699 | ret = PTR_ERR(filp); | ||
2700 | goto error_file; | ||
2701 | } | ||
2690 | 2702 | ||
2691 | /* | 2703 | req->ctx_fd = ctx->ctx_fd = fd; |
2692 | * attach context to file | ||
2693 | */ | ||
2694 | filp->private_data = ctx; | ||
2695 | 2704 | ||
2696 | /* | 2705 | /* |
2697 | * does the user want to sample? | 2706 | * does the user want to sample? |
2698 | */ | 2707 | */ |
2699 | if (pfm_uuid_cmp(req->ctx_smpl_buf_id, pfm_null_uuid)) { | 2708 | if (pfm_uuid_cmp(req->ctx_smpl_buf_id, pfm_null_uuid)) { |
2700 | ret = pfm_setup_buffer_fmt(current, filp, ctx, ctx_flags, 0, req); | 2709 | ret = pfm_setup_buffer_fmt(current, filp, ctx, ctx_flags, 0, req); |
2701 | if (ret) goto buffer_error; | 2710 | if (ret) |
2711 | goto buffer_error; | ||
2702 | } | 2712 | } |
2703 | 2713 | ||
2704 | /* | ||
2705 | * init context protection lock | ||
2706 | */ | ||
2707 | spin_lock_init(&ctx->ctx_lock); | ||
2708 | |||
2709 | /* | ||
2710 | * context is unloaded | ||
2711 | */ | ||
2712 | ctx->ctx_state = PFM_CTX_UNLOADED; | ||
2713 | |||
2714 | /* | ||
2715 | * initialization of context's flags | ||
2716 | */ | ||
2717 | ctx->ctx_fl_block = (ctx_flags & PFM_FL_NOTIFY_BLOCK) ? 1 : 0; | ||
2718 | ctx->ctx_fl_system = (ctx_flags & PFM_FL_SYSTEM_WIDE) ? 1: 0; | ||
2719 | ctx->ctx_fl_is_sampling = ctx->ctx_buf_fmt ? 1 : 0; /* assume record() is defined */ | ||
2720 | ctx->ctx_fl_no_msg = (ctx_flags & PFM_FL_OVFL_NO_MSG) ? 1: 0; | ||
2721 | /* | ||
2722 | * will move to set properties | ||
2723 | * ctx->ctx_fl_excl_idle = (ctx_flags & PFM_FL_EXCL_IDLE) ? 1: 0; | ||
2724 | */ | ||
2725 | |||
2726 | /* | ||
2727 | * init restart semaphore to locked | ||
2728 | */ | ||
2729 | init_completion(&ctx->ctx_restart_done); | ||
2730 | |||
2731 | /* | ||
2732 | * activation is used in SMP only | ||
2733 | */ | ||
2734 | ctx->ctx_last_activation = PFM_INVALID_ACTIVATION; | ||
2735 | SET_LAST_CPU(ctx, -1); | ||
2736 | |||
2737 | /* | ||
2738 | * initialize notification message queue | ||
2739 | */ | ||
2740 | ctx->ctx_msgq_head = ctx->ctx_msgq_tail = 0; | ||
2741 | init_waitqueue_head(&ctx->ctx_msgq_wait); | ||
2742 | init_waitqueue_head(&ctx->ctx_zombieq); | ||
2743 | |||
2744 | DPRINT(("ctx=%p flags=0x%x system=%d notify_block=%d excl_idle=%d no_msg=%d ctx_fd=%d \n", | 2714 | DPRINT(("ctx=%p flags=0x%x system=%d notify_block=%d excl_idle=%d no_msg=%d ctx_fd=%d \n", |
2745 | ctx, | 2715 | ctx, |
2746 | ctx_flags, | 2716 | ctx_flags, |
@@ -2755,10 +2725,14 @@ pfm_context_create(pfm_context_t *ctx, void *arg, int count, struct pt_regs *reg | |||
2755 | */ | 2725 | */ |
2756 | pfm_reset_pmu_state(ctx); | 2726 | pfm_reset_pmu_state(ctx); |
2757 | 2727 | ||
2728 | fd_install(fd, filp); | ||
2729 | |||
2758 | return 0; | 2730 | return 0; |
2759 | 2731 | ||
2760 | buffer_error: | 2732 | buffer_error: |
2761 | pfm_free_fd(ctx->ctx_fd, filp); | 2733 | path = filp->f_path; |
2734 | put_filp(filp); | ||
2735 | path_put(&path); | ||
2762 | 2736 | ||
2763 | if (ctx->ctx_buf_fmt) { | 2737 | if (ctx->ctx_buf_fmt) { |
2764 | pfm_buf_fmt_exit(ctx->ctx_buf_fmt, current, NULL, regs); | 2738 | pfm_buf_fmt_exit(ctx->ctx_buf_fmt, current, NULL, regs); |
@@ -2767,6 +2741,7 @@ error_file: | |||
2767 | pfm_context_free(ctx); | 2741 | pfm_context_free(ctx); |
2768 | 2742 | ||
2769 | error: | 2743 | error: |
2744 | put_unused_fd(fd); | ||
2770 | return ret; | 2745 | return ret; |
2771 | } | 2746 | } |
2772 | 2747 | ||