diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 58a719989acc98..a8a9948eb5d3f7 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1372,6 +1372,7 @@ const struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_GETPARAM, i915_gem_context_getparam_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_SETPARAM, i915_gem_context_setparam_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_PERF_OPEN, i915_perf_open_ioctl, DRM_UNLOCKED|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 8a2de4d16b9d2d..652ad1609cefeb 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1975,6 +1975,8 @@ struct drm_i915_private { struct mutex lock; struct list_head streams; + struct idr metrics_idr; + spinlock_t hook_lock; struct { @@ -3193,6 +3195,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 intel_engine_cs *ring); diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c index a846ad9b0fbf48..4a8cef8d2fc188 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 >= I915_OA_METRICS_SET_MAX) + 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 >= I915_OA_METRICS_SET_MAX) + 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 >= I915_OA_METRICS_SET_MAX) + 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 >= I915_OA_METRICS_SET_MAX) + ret = select_dynamic_metric_set(dev_priv); + else + ret = i915_oa_select_metric_set_skl(dev_priv); if (ret) return ret; @@ -1434,8 +1493,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 >= I915_OA_METRICS_SET_MAX) { + if (!idr_find(&dev_priv->perf.metrics_idr, value)) { DRM_ERROR("Unknown OA metric set ID"); return -EINVAL; + } } props->metrics_set = value; break; @@ -1517,6 +1578,204 @@ 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) +{ + int err; + int i; + struct i915_oa_reg *oa_regs; + + if (!n_regs) { + DRM_ERROR("Warning: No i915 OA registers given"); + 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; + } + + 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; + 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; + u8 __user *uuid = to_user_ptr(args->uuid); + + /* Make sure the user can run as root */ + if (!capable(CAP_SYS_ADMIN)) { + DRM_ERROR("Insufficient privileges to add i915 OA config\n"); + return -EACCES; + } + + if (!uuid) { + DRM_ERROR("Invalid uuid for OA config\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; + } + + if (strncpy_from_user(oa_config->guid, uuid, + sizeof(oa_config->guid)) < 0) { + DRM_ERROR("Failed to copy uuid from OA config\n"); + err = -EFAULT; + 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; + } + + // TODO: currently if any sysfs entry has the same GUID we nope out, however + // this could later be changed to update the entry so we don't just bail + err = create_dynamic_oa_sysfs_entry(dev_priv, oa_config); + if (err) { + DRM_ERROR("Failed to create sysfs entry for OA config\n"); + err = -err; + goto sysfs_err; + } + + oa_config->id = args->id = idr_alloc(&dev_priv->perf.metrics_idr, + oa_config, I915_OA_METRICS_SET_MAX, + 0, GFP_KERNEL); + + return 0; + +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 +1844,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 1e0b57e70f62b8..0ab62f528b1714 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. @@ -1226,6 +1228,22 @@ struct drm_i915_perf_open_param { __u32 fd; }; +struct drm_i915_perf_oa_config { + __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; + + /* OUT */ + __u32 id; +}; + #define I915_PERF_IOCTL_ENABLE _IO('i', 0x0) #define I915_PERF_IOCTL_DISABLE _IO('i', 0x1)