perfmon: context creation
PATCH 15/24 perfmon: context creation
This patch adds perfmon context management functions to create and destroy a perfmon context which encapsulates the entire PMU state.
--
perfmon/perfmon_ctx.c +236 -0
@@ -38,6 +38,7 @@*/ #include <linux/kernel.h> #include <linux/fs.h> +#include <linux/fdtable.h> #include <linux/perfmon_kern.h> #include "perfmon_priv.h"
@@ -162,3 +163,238 @@ }return 0 ; } + +/** +* pfm_ctx_permissions - check authorization to create new context +* @ctx_flags: context flags passed by user +* +* check for permissions to create a context. +* +* A sysadmin may decide to restrict creation of per-thread +* context to a group of users using the group id via +* /sys/kernel/perfmon/task_group +* +* Once we identify a user level package which can be used +* to grant/revoke Linux capabilites at login via PAM, we will +* be able to use capabilities. We would also need to increase +* the size of cap_t to support more than 32 capabilities (it +* is currently defined as u32 and 32 capabilities are alrady +* defined). +*/ +static inline int pfm_ctx_permissions (u32 ctx_flags) +{ +if (pfm_controls.task_group !=PFM_GROUP_PERM_ANY + && !in_group_p (pfm_controls.task_group)) { +PFM_DBG ("user group not allowed to create a task context" ); +return -EPERM ; + } +return 0 ; +} + +/** +* pfm_create_initial_set - create initial set from __pfm_c reate_context +* @ctx: context to atatched the set to +*/ +static void pfm_create_initial_set (struct pfm_context *ctx) +{ +struct pfm_event_set *set; +u64 *impl_pmcs; +u16 i, max_pmc; + + set = ctx->active_set; + max_pmc = ctx->regs.max_pmc; + impl_pmcs = ctx->regs.pmcs; + +/* +* install default values for all PMC registers +*/ +for (i =0 ; i < max_pmc; i++) { +if (pfm_arch_bv_test_bit (i, impl_pmcs)) { + set->pmcs[i] = pfm_pmu_conf->pmc_desc[i].dfl_val; +PFM_DBG ("pmc%u=0x%llx" , + i, + (unsigned long long )set->pmcs[i]); + } + } +/* +* PMD registers are set to 0 when the event set is allocated, +* hence we do not need to explicitly initialize them. +* +* For virtual PMD registers (i.e., those tied to a SW resource) +* their value becomes meaningful once the context is attached. +*/ +} + +/** +* __pfm_create_context - allocate and initialize a perfmon context +* @ctx_flags : user context flags +* @sif: pointer to pfarg_sinfo to be updated +* @new_ctx: will contain new context address on return +* +* function used to allocate a new context. A context is allocated along +* with the default event set. If a sampling format is used, the buffer +* may be allocated and initialized. +* +* The file descriptor identifying the context is allocated and returned +* to caller. +* +* This function operates with no locks and interrupts are enabled. +* return: +* >=0: the file descriptor to identify the context +* <0 : the error code +*/ +int __pfm_create_context (__u32 ctx_flags, +struct pfarg_sinfo *sif, +struct pfm_context **new_ctx) +{ +struct pfm_context *ctx; +struct file *filp =NULL ; +int fd =0 , ret = -EINVAL ; + +if (!pfm_pmu_conf) +return -ENOSYS ; + +/* no context flags supported yet */ +if (ctx_flags) + goto error_alloc; + + ret =pfm_ctx_permissions (ctx_flags); +if (ret <0 ) + goto error_alloc; + +/* +* we can use GFP_KERNEL and potentially sleep because we do +* not hold any lock at this point. +*/ +might_sleep (); + ret = -ENOMEM ; + ctx =kmem_cache_zalloc (pfm_ctx_cachep,GFP_KERNEL ); +if (!ctx) + goto error_alloc; + +PFM_DBG ("alloc ctx @0x%p" , ctx); + + ctx->active_set = &ctx->_set0; + +spin_lock_init (&ctx->lock); + +/* +* context is unloaded +*/ + ctx->state =PFM_CTX_UNLOADED ; + + + ret =pfm_pmu_acquire (ctx); +if (ret) + goto error_file; +/* +* check if PMU is usable +*/ +if (!(ctx->regs.num_pmcs && ctx->regs.num_pmcs)) { +PFM_DBG ("no usable PMU registers" ); + ret = -EBUSY ; + goto error_file; + } + + ret = -ENFILE ; + fd =pfm_alloc_fd (&filp); +if (fd <0 ) + goto error_file; + +/* +* initialize arch-specific section +* must be done before fmt_init() +*/ + ret =pfm_arch_context_create (ctx, ctx_flags); +if (ret) + goto error_set; + + ret = -ENOMEM ; + +/* +* add initial set +*/ +pfm_create_initial_set (ctx); + + filp->private_data = ctx; + + ctx->last_act =PFM_INVALID_ACTIVATION ; + ctx->last_cpu =-1 ; + +PFM_DBG ("flags=0x%x fd=%d" , ctx_flags, fd); + +if (new_ctx) + *new_ctx = ctx; + +/* +* copy bitmask of available PMU registers +* +* must copy over the entire vector to avoid +* returning bogus upper bits pass by user +*/ +pfm_arch_bv_copy (sif->sif_avail_pmcs, + ctx->regs.pmcs, +PFM_MAX_PMCS ); + +pfm_arch_bv_copy (sif->sif_avail_pmds, + ctx->regs.pmds, +PFM_MAX_PMDS ); + +/* +* we defer the fd_install until we are certain the call succeeded +* to ensure we do not have to undo its effect. Neither put_filp() +* nor put_unused_fd() undoes the effect of fd_install(). +*/ +fd_install (fd, filp); + +return fd; + +error_set: +put_filp (filp); +put_unused_fd (fd); +error_file: +/* +* calls the right *_put() functions +* calls pfm_release_pmu() +*/ +pfm_free_context (ctx); +return ret; +error_alloc: +return ret; +} + +/** +* pfm_undo_create -- undo context creation +* @fd: file descriptor to close +* @ctx: newly created context +* +* upon return neither fd nor ctx are useable +*/ +void pfm_undo_create (int fd,struct pfm_context *ctx) +{ +struct files_struct *files = current->files; +struct file *file; +int fput_needed; + + file =fget_light (fd, &fput_needed); +/* +* there is no fd_uninstall(), so we do it +* here. put_unused_fd() does not remove the +* effect of fd_install(). +*/ + +spin_lock (&files->file_lock); + files->fd_array[fd] =NULL ; +spin_unlock (&files->file_lock); + +fput_light (file, fput_needed); + +/* +* decrement ref count and kill file +*/ +put_filp (file); + +put_unused_fd (fd); + +pfm_free_context (ctx); +}
perfmon/perfmon_priv.h +5 -0
@@ -55,7 +55,10 @@int pfm_init_sysfs (void ); +int __pfm_create_context (__u32 ctx_flags,struct pfarg_sinfo *sif, +struct pfm_context **new_ctx);void pfm_free_context (struct pfm_context *ctx); +void pfm_undo_create (int fd,struct pfm_context *ctx);int __pfm_stop (struct pfm_context *ctx);int __pfm_start (struct pfm_context *ctx);
@@ -63,6 +66,8 @@int __pfm_load_context (struct pfm_context *ctx,struct task_struct *task);int __pfm_unload_context (struct pfm_context *ctx); +int pfm_alloc_fd (struct file **cfile); +ssize_t pfm_sysfs_res_show (char *buf,size_t sz,int what);int pfm_pmu_acquire (struct pfm_context *ctx);
perfmon/perfmon_file.c +212 -0
221 lines not on this page · raw message
include/linux/perfmon.h +10 -0
@@ -46,6 +46,16 @@#define PFM_PMC_BV PFM_BVSIZE(PFM_MAX_PMCS)/* +* argument to pfm_create +* populated on return +*/ +struct pfarg_sinfo { +__u64 sif_avail_pmcs[PFM_PMC_BV ];/* out: available PMCs */ +__u64 sif_avail_pmds[PFM_PMD_BV ];/* out: available PMDs */ +__u64 sif_reserved1[4 ];/* for future use */ +}; + +/* * PMC and PMD generic register description */ struct pfarg_pmr {
Quoted 25 lines · * [email protected] <[email protected]> wrote:
perfmon/perfmon_ctx.c +25
-0+/** +* pfm_undo_create -- undo context creation +* @fd: file descriptor to close +* @ctx: newly created context +* +* upon return neither fd nor ctx are useable +*/ +void pfm_undo_create (int fd,struct pfm_context *ctx) +{ +struct files_struct *files = current->files; +struct file *file; +int fput_needed; + + file =fget_light (fd, &fput_needed); +/* +* there is no fd_uninstall(), so we do it +* here. put_unused_fd() does not remove the +* effect of fd_install(). +*/ + +spin_lock (&files->file_lock); + files->fd_array[fd] =NULL ; +spin_unlock (&files->file_lock); + +fput_light (file, fput_needed);
perfmon/perfmon_ctx.c line 400
Quoted 10 lines
perfmon/perfmon_ctx.c +10
-0+ +/* +* decrement ref count and kill file +*/ +put_filp (file); + +put_unused_fd (fd); + +pfm_free_context (ctx); +}
This function is superfluous.
The only place where this is used is when a fresh sys_pfm_create() fails to do a user-copy of the sif:
+ if (ureq && copy_to_user(ureq, &sif, sizeof(sif))) {
+ pfm_undo_create(ret, new_ctx);
+ ret = -EFAULT;
+ }
but this code should be calling sys_close(fd) instead - the fd is already set up with pfm_fs so it will tear down the context if this was the last user of the perfmon context.
So basically pfm_undo_create() is an open-coded sys_close(fd).
Ingo