From: Matthew Auld <matthew.auld@xxxxxxxxx> 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. This will relieve the kernel from holding all the possible configs, so we can leave only the test configs here. Signed-off-by: Matthew Auld <matthew.auld@xxxxxxxxx> Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@xxxxxxxxx> Signed-off-by: Andrzej Datczuk <andrzej.datczuk@xxxxxxxxx> --- drivers/gpu/drm/i915/i915_drv.c | 2 + drivers/gpu/drm/i915/i915_drv.h | 30 +++ drivers/gpu/drm/i915/i915_perf.c | 391 ++++++++++++++++++++++++++++++++++++++- drivers/gpu/drm/i915/i915_reg.h | 2 + include/uapi/drm/i915_drm.h | 21 +++ 5 files changed, 441 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index d310d8245dca..a3d339670ec1 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -2730,6 +2730,8 @@ static 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), + DRM_IOCTL_DEF_DRV(I915_PERF_REMOVE_CONFIG, i915_perf_remove_config_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), }; static struct drm_driver driver = { diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 81cd21ecfa7d..ce733c2db963 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2009,6 +2009,11 @@ struct i915_perf_stream { * type of configured stream. */ const struct i915_perf_stream_ops *ops; + + /** + * @metrics_set: The ID of the config used by the stream. + */ + int metrics_set; }; /** @@ -2016,6 +2021,25 @@ struct i915_perf_stream { */ struct i915_oa_ops { /** + * @is_valid_b_counter_reg: Validates register's address for + * programming boolean counters for a particular platform. + */ + bool (*is_valid_b_counter_reg)(struct drm_i915_private *dev_priv, + u32 addr); + + /** + * @is_valid_mux_reg: Validates register's address for programming mux + * for a particular platform. + */ + bool (*is_valid_mux_reg)(struct drm_i915_private *dev_priv, u32 addr); + + /** + * @is_valid_flex_reg: Validates register's address for programming + * flex EU filtering for a particular platform. + */ + bool (*is_valid_flex_reg)(struct drm_i915_private *dev_priv, u32 addr); + + /** * @init_oa_buffer: Resets the head and tail pointers of the * circular buffer for periodic OA reports. * @@ -2413,6 +2437,8 @@ struct drm_i915_private { struct mutex lock; struct list_head streams; + struct idr metrics_idr; + struct { struct i915_perf_stream *exclusive_stream; @@ -3589,6 +3615,10 @@ i915_gem_context_lookup_timeline(struct i915_gem_context *ctx, 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); +int i915_perf_remove_config_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); void i915_oa_init_reg_state(struct intel_engine_cs *engine, struct i915_gem_context *ctx, uint32_t *reg_state); diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c index d9f77a4d85db..92cb6a7568e7 100644 --- a/drivers/gpu/drm/i915/i915_perf.c +++ b/drivers/gpu/drm/i915/i915_perf.c @@ -357,6 +357,23 @@ struct perf_open_properties { int oa_period_exponent; }; +struct i915_oa_dynamic_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 u32 gen8_oa_hw_tail_read(struct drm_i915_private *dev_priv) { return I915_READ(GEN8_OATAILPTR) & GEN8_OATAILPTR_MASK; @@ -1451,11 +1468,39 @@ 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_dynamic_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.n_mux_configs = 1; + dev_priv->perf.oa.mux_regs[0] = oa_config->mux_regs; + dev_priv->perf.oa.mux_regs_lens[0] = 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; int i; + 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; @@ -1776,8 +1821,12 @@ static int gen8_configure_all_contexts(struct drm_i915_private *dev_priv, static int gen8_enable_metric_set(struct drm_i915_private *dev_priv) { - int ret = dev_priv->perf.oa.ops.select_metric_set(dev_priv); - int i; + int ret, i; + + if (dev_priv->perf.oa.metrics_set > dev_priv->perf.oa.n_builtin_sets) + ret = select_dynamic_metric_set(dev_priv); + else + ret = dev_priv->perf.oa.ops.select_metric_set(dev_priv); if (ret) return ret; @@ -2055,7 +2104,7 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream, dev_priv->perf.oa.oa_buffer.format = dev_priv->perf.oa.oa_formats[props->oa_format].format; - dev_priv->perf.oa.metrics_set = props->metrics_set; + dev_priv->perf.oa.metrics_set = stream->metrics_set = props->metrics_set; dev_priv->perf.oa.periodic = props->oa_periodic; if (dev_priv->perf.oa.periodic) @@ -2713,7 +2762,9 @@ static int read_properties_unlocked(struct drm_i915_private *dev_priv, break; case DRM_I915_PERF_PROP_OA_METRICS_SET: if (value == 0 || - value > dev_priv->perf.oa.n_builtin_sets) { + (value > dev_priv->perf.oa.n_builtin_sets && + idr_find(&dev_priv->perf.metrics_idr, + value) == NULL)) { DRM_DEBUG("Unknown OA metric set ID\n"); return -EINVAL; } @@ -2957,6 +3008,321 @@ void i915_perf_unregister(struct drm_i915_private *dev_priv) dev_priv->perf.metrics_kobj = NULL; } +static bool gen8_is_valid_flex_addr(struct drm_i915_private *dev_priv, u32 addr) +{ + static const i915_reg_t flex_eu_regs[] = { + EU_PERF_CNTL0, + EU_PERF_CNTL1, + EU_PERF_CNTL2, + EU_PERF_CNTL3, + EU_PERF_CNTL4, + EU_PERF_CNTL5, + EU_PERF_CNTL6, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(flex_eu_regs); i++) { + if (flex_eu_regs[i].reg == addr) + return true; + } + return false; +} + +static bool gen7_is_valid_b_counter_addr(struct drm_i915_private *dev_priv, u32 addr) +{ + return (addr >= 0x2380 && addr <= 0x27ac); +} + +static bool gen7_is_valid_mux_addr(struct drm_i915_private *dev_priv, u32 addr) +{ + return addr == NOA_WRITE.reg || + (addr >= 0xd0c && addr <= 0xd3c) || + (addr >= 0x25100 && addr <= 0x2FB9C); +} + +static bool hsw_is_valid_mux_addr(struct drm_i915_private *dev_priv, u32 addr) +{ + return (addr >= 0x25100 && addr <= 0x2FF90) || + gen7_is_valid_mux_addr(dev_priv, addr); +} + +static bool chv_is_valid_mux_addr(struct drm_i915_private *dev_priv, u32 addr) +{ + return (addr >= 0x182300 && addr <= 0x1823A4) || + gen7_is_valid_mux_addr(dev_priv, addr); +} + +static struct i915_oa_reg *alloc_oa_regs(struct drm_i915_private *dev_priv, + bool (*is_valid)(struct drm_i915_private *dev_priv, u32 addr), + u32 __user *regs, + u32 n_regs) +{ + int err; + int i; + struct i915_oa_reg *oa_regs; + + if (!n_regs) + return NULL; + + if (!is_valid) + return ERR_PTR(-EINVAL); + + 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; + } + + if (!is_valid(dev_priv, 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; + goto value_err; + } + + oa_regs[i].addr = _MMIO(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_dynamic_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_dynamic_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_dynamic_config *oa_config, *tmp; + int id; + unsigned int x1, x2, x3, x4, x5; + + if (!dev_priv->perf.initialized) { + DRM_DEBUG("i915 perf interface not available for this system\n"); + return -ENOTSUPP; + } + + if (!capable(CAP_SYS_ADMIN)) { + DRM_ERROR("Insufficient privileges to add i915 OA config\n"); + return -EACCES; + } + + if ((!args->mux_regs || !args->n_mux_regs) && + (!args->boolean_regs || !args->n_boolean_regs) && + (!args->flex_regs || !args->n_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, u64_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; + } + + 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, + dev_priv->perf.oa.ops.is_valid_mux_reg, + u64_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, + dev_priv->perf.oa.ops.is_valid_b_counter_reg, + u64_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; + } + + if (INTEL_GEN(dev_priv) < 8) { + if (args->n_flex_regs != 0) + goto flex_err; + } else { + oa_config->flex_regs_len = args->n_flex_regs; + oa_config->flex_regs = + alloc_oa_regs(dev_priv, + dev_priv->perf.oa.ops.is_valid_flex_reg, + u64_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 = i915_mutex_lock_interruptible(dev); + if (err) + goto lock_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 sysfs_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); + + mutex_unlock(&dev->struct_mutex); + + return oa_config->id; + +sysfs_err: + mutex_unlock(&dev->struct_mutex); +lock_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; +} + +int i915_perf_remove_config_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u64 *arg = data; + struct i915_oa_dynamic_config *oa_config; + int ret; + + if (!dev_priv->perf.initialized) { + DRM_DEBUG("i915 perf interface not available for this system\n"); + return -ENOTSUPP; + } + + if (!capable(CAP_SYS_ADMIN)) { + DRM_ERROR("Insufficient privileges to remove i915 OA config\n"); + return -EACCES; + } + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + goto lock_err; + + if (dev_priv->perf.oa.exclusive_stream && + dev_priv->perf.oa.exclusive_stream->metrics_set == *arg) { + DRM_ERROR("Failed to remove config in use\n"); + ret = -EADDRINUSE; + goto config_err; + } + + oa_config = idr_find(&dev_priv->perf.metrics_idr, *arg); + if (!oa_config) { + DRM_ERROR("Failed to remove unknown config\n"); + ret = -EINVAL; + goto config_err; + } + + idr_remove(&dev_priv->perf.metrics_idr, *arg); + + sysfs_remove_group(dev_priv->perf.metrics_kobj, + &oa_config->sysfs_metric); + if (oa_config->flex_regs) + kfree(oa_config->flex_regs); + if (oa_config->b_counter_regs) + kfree(oa_config->b_counter_regs); + if (oa_config->mux_regs) + kfree(oa_config->mux_regs); + kfree(oa_config); + +config_err: + mutex_unlock(&dev->struct_mutex); +lock_err: + return ret; +} + static struct ctl_table oa_table[] = { { .procname = "perf_stream_paranoid", @@ -3011,8 +3377,14 @@ static struct ctl_table dev_root[] = { void i915_perf_init(struct drm_i915_private *dev_priv) { dev_priv->perf.oa.n_builtin_sets = 0; + idr_init(&dev_priv->perf.metrics_idr); if (IS_HASWELL(dev_priv)) { + dev_priv->perf.oa.ops.is_valid_b_counter_reg = + gen7_is_valid_b_counter_addr; + dev_priv->perf.oa.ops.is_valid_mux_reg = + hsw_is_valid_mux_addr; + dev_priv->perf.oa.ops.is_valid_flex_reg = NULL; dev_priv->perf.oa.ops.init_oa_buffer = gen7_init_oa_buffer; dev_priv->perf.oa.ops.enable_metric_set = hsw_enable_metric_set; dev_priv->perf.oa.ops.disable_metric_set = hsw_disable_metric_set; @@ -3036,6 +3408,13 @@ void i915_perf_init(struct drm_i915_private *dev_priv) * execlist mode by default. */ + dev_priv->perf.oa.ops.is_valid_b_counter_reg = + gen7_is_valid_b_counter_addr; + dev_priv->perf.oa.ops.is_valid_mux_reg = + gen7_is_valid_mux_addr; + dev_priv->perf.oa.ops.is_valid_flex_reg = + gen8_is_valid_flex_addr; + if (IS_GEN8(dev_priv)) { dev_priv->perf.oa.ctx_oactxctrl_offset = 0x120; dev_priv->perf.oa.ctx_flexeu0_offset = 0x2ce; @@ -3050,6 +3429,8 @@ void i915_perf_init(struct drm_i915_private *dev_priv) dev_priv->perf.oa.ops.select_metric_set = i915_oa_select_metric_set_bdw; } else if (IS_CHERRYVIEW(dev_priv)) { + dev_priv->perf.oa.ops.is_valid_mux_reg = + chv_is_valid_mux_addr; dev_priv->perf.oa.n_builtin_sets = i915_oa_n_builtin_metric_sets_chv; dev_priv->perf.oa.ops.select_metric_set = diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 21ab12f4e72a..433fcd7beb72 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -732,6 +732,8 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg) #define GDT_CHICKEN_BITS _MMIO(0x9840) #define GT_NOA_ENABLE 0x00000080 +#define NOA_WRITE _MMIO(0x9888) + /* * OA Boolean state */ diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 7ccbd6a2bbe0..e2ff47732303 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -260,6 +260,8 @@ 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_I915_PERF_REMOVE_CONFIG 0x38 #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) @@ -315,6 +317,8 @@ 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_IOW(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) +#define DRM_IOCTL_I915_PERF_REMOVE_CONFIG DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_PERF_REMOVE_CONFIG, __u64) /* Allow drivers to submit batchbuffers directly to hardware, relying * on the security mechanisms provided by hardware. @@ -1467,6 +1471,23 @@ enum drm_i915_perf_record_type { DRM_I915_PERF_RECORD_MAX /* non-ABI */ }; +/** + * Structure to upload perf dynamic configuration into the kernel. + */ +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; +}; + #if defined(__cplusplus) } #endif -- 2.13.2 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx