From cb8c58ea3c0464256edc08147b078a0f8ea270e9 Mon Sep 17 00:00:00 2001 From: Matthew Auld Date: Fri, 22 Jan 2016 14:37:16 +0000 Subject: [PATCH 1/2] drm/i915: Implement I915_PERF_ADD_CONFIG interface The motivation behind this new interface is expose at runtime the creation of new OA configs which can be used as part of the i915 perf open interface. This will enable the kernel to learn new configs which may be experimental, or otherwise not part of the core set currently available through the i915 perf interface. Signed-off-by: Matthew Auld --- drivers/gpu/drm/i915/i915_dma.c | 1 + drivers/gpu/drm/i915/i915_drv.h | 4 + drivers/gpu/drm/i915/i915_perf.c | 258 ++++++++++++++++++++++++++++++- include/uapi/drm/i915_drm.h | 16 ++ 4 files changed, 273 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index d1365ee4d8f7df..166c330efdad6a 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1368,6 +1368,7 @@ const struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_GETPARAM, i915_gem_context_getparam_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_SETPARAM, i915_gem_context_setparam_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_PERF_OPEN, i915_perf_open_ioctl, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(I915_PERF_ADD_CONFIG, i915_perf_add_config_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), }; int i915_max_ioctl = ARRAY_SIZE(i915_ioctls); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c409c8f1db38ee..6e52faca7ea048 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2044,6 +2044,8 @@ struct drm_i915_private { struct mutex lock; struct list_head streams; + struct idr metrics_idr; + spinlock_t hook_lock; struct { @@ -3324,6 +3326,8 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data, int i915_perf_open_ioctl(struct drm_device *dev, void *data, struct drm_file *file); +int i915_perf_add_config_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); void i915_oa_context_pin_notify(struct drm_i915_private *dev_priv, struct intel_context *context); void i915_oa_legacy_ctx_switch_notify(struct drm_i915_gem_request *req); diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c index c5447b4382907d..13f0f87c2995f2 100644 --- a/drivers/gpu/drm/i915/i915_perf.c +++ b/drivers/gpu/drm/i915/i915_perf.c @@ -95,6 +95,23 @@ struct perf_open_properties u32 oa_period_exponent; }; +struct i915_oa_config +{ + char guid[40]; + int id; + + const struct i915_oa_reg *mux_regs; + int mux_regs_len; + const struct i915_oa_reg *b_counter_regs; + int b_counter_regs_len; + const struct i915_oa_reg *flex_regs; + int flex_regs_len; + + struct attribute_group sysfs_metric; + struct attribute *attrs[2]; + struct device_attribute sysfs_metric_id; +}; + static bool gen8_oa_buffer_is_empty(struct drm_i915_private *dev_priv) { u32 head = I915_READ(GEN8_OAHEADPTR); @@ -540,9 +557,36 @@ static void config_oa_regs(struct drm_i915_private *dev_priv, } } +int select_dynamic_metric_set(struct drm_i915_private *dev_priv) +{ + struct i915_oa_config *oa_config; + + oa_config = idr_find(&dev_priv->perf.metrics_idr, + dev_priv->perf.oa.metrics_set); + if (!oa_config) { + return -EINVAL; + } + + dev_priv->perf.oa.mux_regs = oa_config->mux_regs; + dev_priv->perf.oa.mux_regs_len = oa_config->mux_regs_len; + + dev_priv->perf.oa.b_counter_regs = oa_config->b_counter_regs; + dev_priv->perf.oa.b_counter_regs_len = oa_config->b_counter_regs_len; + + dev_priv->perf.oa.flex_regs = oa_config->flex_regs; + dev_priv->perf.oa.flex_regs_len = oa_config->flex_regs_len; + + return 0; +} + static int hsw_enable_metric_set(struct drm_i915_private *dev_priv) { - int ret = i915_oa_select_metric_set_hsw(dev_priv); + int ret; + + if (dev_priv->perf.oa.metrics_set > dev_priv->perf.oa.n_builtin_sets) + ret = select_dynamic_metric_set(dev_priv); + else + ret = i915_oa_select_metric_set_hsw(dev_priv); if (ret) return ret; @@ -648,7 +692,12 @@ static int configure_all_contexts(struct drm_i915_private *dev_priv) static int bdw_enable_metric_set(struct drm_i915_private *dev_priv) { - int ret = i915_oa_select_metric_set_bdw(dev_priv); + int ret; + + if (dev_priv->perf.oa.metrics_set > dev_priv->perf.oa.n_builtin_sets) + ret = select_dynamic_metric_set(dev_priv); + else + ret = i915_oa_select_metric_set_bdw(dev_priv); if (ret) return ret; @@ -676,7 +725,12 @@ static void bdw_disable_metric_set(struct drm_i915_private *dev_priv) static int chv_enable_metric_set(struct drm_i915_private *dev_priv) { - int ret = i915_oa_select_metric_set_chv(dev_priv); + int ret; + + if (dev_priv->perf.oa.metrics_set > dev_priv->perf.oa.n_builtin_sets) + ret = select_dynamic_metric_set(dev_priv); + else + ret = i915_oa_select_metric_set_chv(dev_priv); if (ret) return ret; @@ -704,7 +758,12 @@ static void chv_disable_metric_set(struct drm_i915_private *dev_priv) static int skl_enable_metric_set(struct drm_i915_private *dev_priv) { - int ret = i915_oa_select_metric_set_skl(dev_priv); + int ret; + + if (dev_priv->perf.oa.metrics_set > dev_priv->perf.oa.n_builtin_sets) + ret = select_dynamic_metric_set(dev_priv); + else + ret = i915_oa_select_metric_set_skl(dev_priv); if (ret) return ret; @@ -1447,8 +1506,10 @@ static int read_properties_unlocked(struct drm_i915_private *dev_priv, break; case DRM_I915_PERF_OA_METRICS_SET_PROP: if (value == 0 || value > dev_priv->perf.oa.n_builtin_sets) { - DRM_ERROR("Unknown OA metric set ID"); - return -EINVAL; + if (!idr_find(&dev_priv->perf.metrics_idr, value)) { + DRM_ERROR("Unknown OA metric set ID"); + return -EINVAL; + } } props->metrics_set = value; break; @@ -1521,6 +1582,189 @@ int i915_perf_open_ioctl(struct drm_device *dev, void *data, return ret; } +static struct i915_oa_reg *alloc_oa_regs(struct drm_i915_private *dev_priv, + u32 __user *regs, + u32 n_regs) +{ + int err; + int i; + struct i915_oa_reg *oa_regs; + + if (!n_regs) { + return NULL; + } + + oa_regs = kmalloc(sizeof(*oa_regs) * n_regs, GFP_KERNEL); + if (!oa_regs) { + return ERR_PTR(-ENOMEM); + } + + for (i = 0; i < n_regs; i++) { + int ret; + u32 addr, value; + + ret = get_user(addr, regs); + if (ret) { + err = ret; + goto addr_err; + } + + ret = get_user(value, regs + 1); + if (ret) { + err = ret; + goto value_err; + } + + oa_regs[i].addr = addr; + oa_regs[i].value = value; + + regs += 2; + } + + return oa_regs; + +addr_err: +value_err: + kfree(oa_regs); + return ERR_PTR(err); +} + +static ssize_t show_dynamic_id(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i915_oa_config *oa_config = + container_of(attr, typeof(*oa_config), sysfs_metric_id); + + return sprintf(buf, "%d\n", oa_config->id); +} + +static int create_dynamic_oa_sysfs_entry(struct drm_i915_private *dev_priv, + struct i915_oa_config *oa_config) +{ + oa_config->sysfs_metric_id.attr.name = "id"; + oa_config->sysfs_metric_id.attr.mode = S_IRUGO; + oa_config->sysfs_metric_id.show = show_dynamic_id; + oa_config->sysfs_metric_id.store = NULL; + + oa_config->attrs[0] = &oa_config->sysfs_metric_id.attr; + oa_config->attrs[1] = NULL; + + oa_config->sysfs_metric.name = oa_config->guid; + oa_config->sysfs_metric.attrs = oa_config->attrs; + + return sysfs_create_group(dev_priv->perf.metrics_kobj, + &oa_config->sysfs_metric); +} + +int i915_perf_add_config_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + int err; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_perf_oa_config *args = data; + struct i915_oa_config *oa_config, *tmp; + int id; + unsigned int x1, x2, x3, x4, x5; + + if (!capable(CAP_SYS_ADMIN)) { + DRM_ERROR("Insufficient privileges to add i915 OA config\n"); + return -EACCES; + } + + if (!args->mux_regs && !args->boolean_regs && !args->flex_regs) { + DRM_ERROR("No OA registers given\n"); + return -EINVAL; + } + + oa_config = kzalloc(sizeof(*oa_config), GFP_KERNEL); + if (!oa_config) { + DRM_ERROR("Failed to allocate memory for the OA config\n"); + return -ENOMEM; + } + + err = strncpy_from_user(oa_config->guid, to_user_ptr(args->uuid), + sizeof(oa_config->guid)); + if (err < 0) { + DRM_ERROR("Failed to copy uuid from OA config\n"); + goto uuid_err; + } + + idr_for_each_entry(&dev_priv->perf.metrics_idr, tmp, id) { + if (!strcmp(tmp->guid, oa_config->guid)) { + DRM_ERROR("OA config already exists with this uuid\n"); + err = -EADDRINUSE; + goto uuid_err; + } + } + + if (sscanf(oa_config->guid, "%08x-%04x-%04x-%04x-%012x", &x1, &x2, &x3, + &x4, &x5) != 5) { + DRM_ERROR("Invalid uuid format for OA config\n"); + err = -EINVAL; + goto uuid_err; + } + + oa_config->mux_regs_len = args->n_mux_regs; + oa_config->mux_regs = alloc_oa_regs(dev_priv, + to_user_ptr(args->mux_regs), + args->n_mux_regs); + + if (IS_ERR(oa_config->mux_regs)) { + DRM_ERROR("Failed to create OA config for mux_regs\n"); + err = PTR_ERR(oa_config->mux_regs); + goto mux_err; + } + + oa_config->b_counter_regs_len = args->n_boolean_regs; + oa_config->b_counter_regs + = alloc_oa_regs(dev_priv, + to_user_ptr(args->boolean_regs), + args->n_boolean_regs); + + if (IS_ERR(oa_config->b_counter_regs)) { + DRM_ERROR("Failed to create OA config for b_counter_regs\n"); + err = PTR_ERR(oa_config->b_counter_regs); + goto boolean_err; + } + + oa_config->flex_regs_len = args->n_flex_regs; + oa_config->flex_regs = alloc_oa_regs(dev_priv, + to_user_ptr(args->flex_regs), + args->n_flex_regs); + + if (IS_ERR(oa_config->flex_regs)) { + DRM_ERROR("Failed to create OA config for flex_regs\n"); + err = PTR_ERR(oa_config->flex_regs); + goto flex_err; + } + + err = create_dynamic_oa_sysfs_entry(dev_priv, oa_config); + if (err) { + DRM_ERROR("Failed to create sysfs entry for OA config\n"); + goto sysfs_err; + } + + oa_config->id = idr_alloc(&dev_priv->perf.metrics_idr, + oa_config, + dev_priv->perf.oa.n_builtin_sets + 1, + 0, GFP_KERNEL); + + return oa_config->id; + +sysfs_err: + kfree(oa_config->flex_regs); +flex_err: + kfree(oa_config->b_counter_regs); +boolean_err: + kfree(oa_config->mux_regs); +mux_err: +uuid_err: + kfree(oa_config); + + DRM_ERROR("Failed to add new OA config\n"); + return err; +} static struct ctl_table oa_table[] = { { @@ -1585,6 +1829,8 @@ void i915_perf_init(struct drm_device *dev) mutex_init(&dev_priv->perf.lock); spin_lock_init(&dev_priv->perf.hook_lock); + idr_init(&dev_priv->perf.metrics_idr); + if (IS_HASWELL(dev)) { dev_priv->perf.oa.ops.init_oa_buffer = gen7_init_oa_buffer; dev_priv->perf.oa.ops.enable_metric_set = hsw_enable_metric_set; diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 4a6789548d7e42..0520df92ff9718 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -231,6 +231,7 @@ typedef struct _drm_i915_sarea { #define DRM_I915_GEM_CONTEXT_GETPARAM 0x34 #define DRM_I915_GEM_CONTEXT_SETPARAM 0x35 #define DRM_I915_PERF_OPEN 0x36 +#define DRM_I915_PERF_ADD_CONFIG 0x37 #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) @@ -285,6 +286,7 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_GEM_CONTEXT_GETPARAM DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_GETPARAM, struct drm_i915_gem_context_param) #define DRM_IOCTL_I915_GEM_CONTEXT_SETPARAM DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_SETPARAM, struct drm_i915_gem_context_param) #define DRM_IOCTL_I915_PERF_OPEN DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_PERF_OPEN, struct drm_i915_perf_open_param) +#define DRM_IOCTL_I915_PERF_ADD_CONFIG DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_PERF_ADD_CONFIG, struct drm_i915_perf_oa_config) /* Allow drivers to submit batchbuffers directly to hardware, relying * on the security mechanisms provided by hardware. @@ -1206,6 +1208,20 @@ struct drm_i915_perf_open_param { __u32 n_properties; }; +struct drm_i915_perf_oa_config { + /* string formatted like "%08x-%04x-%04x-%04x-%012x" **/ + __u64 __user uuid; + + __u32 n_mux_regs; + __u64 __user mux_regs; + + __u32 n_boolean_regs; + __u64 __user boolean_regs; + + __u32 n_flex_regs; + __u64 __user flex_regs; +}; + #define I915_PERF_IOCTL_ENABLE _IO('i', 0x0) #define I915_PERF_IOCTL_DISABLE _IO('i', 0x1) From 58aa293c888c052fd7da14640f8562d58f01b191 Mon Sep 17 00:00:00 2001 From: Matthew Auld Date: Fri, 22 Jan 2016 14:37:33 +0000 Subject: [PATCH 2/2] drm/i915: whitelist possible oa reg addresses Signed-off-by: Matthew Auld --- drivers/gpu/drm/i915/i915_perf.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c index 13f0f87c2995f2..35e4efb5d846b5 100644 --- a/drivers/gpu/drm/i915/i915_perf.c +++ b/drivers/gpu/drm/i915/i915_perf.c @@ -1582,6 +1582,29 @@ int i915_perf_open_ioctl(struct drm_device *dev, void *data, return ret; } +static inline bool is_valid_flex_addr(struct drm_device *dev, u32 addr) +{ + return (addr >= 0xE458 && addr <= 0xE758); +} + +static inline bool is_valid_b_counter_addr(struct drm_device *dev, u32 addr) +{ + return (addr >= 0x2360 && addr <= 0x2B14); +} + +static inline bool is_valid_mux_addr(struct drm_device *dev, u32 addr) +{ + return (addr == 0x9888 || addr == 0xD24 || addr == 0xD28 || + (addr >= 0x25100 && addr <= 0x2FB9C)); +} + +static inline bool is_valid_oa_addr_range(struct drm_device *dev, u32 addr) +{ + return (is_valid_mux_addr(dev, addr) || + is_valid_b_counter_addr(dev, addr) || + is_valid_flex_addr(dev, addr)); +} + static struct i915_oa_reg *alloc_oa_regs(struct drm_i915_private *dev_priv, u32 __user *regs, u32 n_regs) @@ -1609,6 +1632,12 @@ static struct i915_oa_reg *alloc_oa_regs(struct drm_i915_private *dev_priv, goto addr_err; } + if (!is_valid_oa_addr_range(dev_priv->dev, addr)) { + DRM_ERROR("Invalid oa_reg address: %X\n", addr); + err = -EINVAL; + goto addr_err; + } + ret = get_user(value, regs + 1); if (ret) { err = ret;