@@ -2191,6 +2191,9 @@ struct drm_i915_private {
struct drm_i915_fence_reg fence_regs[I915_MAX_NUM_FENCES]; /* assume 965 */
int num_fence_regs; /* 8 on pre-965, 16 otherwise */
+ /* Command stream timestamp base - helps define watchdog threshold */
+ u32 cs_timestamp_base;
+
unsigned int fsb_freq, mem_freq, is_ddr3;
unsigned int skl_preferred_vco_freq;
unsigned int max_cdclk_freq;
@@ -3554,6 +3557,59 @@ i915_gem_context_lookup_timeline(struct i915_gem_context *ctx,
return &vm->timeline.engine[engine->id];
}
+/*
+ * BDW, CHV & SKL+ Timestamp timer resolution = 0.080 uSec,
+ * or 12500000 counts per second, or ~12 counts per microsecond.
+ *
+ * But BXT/GLK Timestamp timer resolution is different, 0.052 uSec,
+ * or 19200000 counts per second, or ~19 counts per microsecond.
+ *
+ * Future-proofing, some day it won't be as simple as just GEN & IS_LP.
+ */
+#define GEN8_TIMESTAMP_CNTS_PER_USEC 12
+#define GEN9_LP_TIMESTAMP_CNTS_PER_USEC 19
+static inline u32 cs_timestamp_in_us(struct drm_i915_private *dev_priv)
+{
+ u32 cs_timestamp_base = dev_priv->cs_timestamp_base;
+
+ if (cs_timestamp_base)
+ return cs_timestamp_base;
+
+ switch (INTEL_GEN(dev_priv)) {
+ default:
+ MISSING_CASE(INTEL_GEN(dev_priv));
+ /* fall through */
+ case 9:
+ cs_timestamp_base = IS_GEN9_LP(dev_priv) ?
+ GEN9_LP_TIMESTAMP_CNTS_PER_USEC :
+ GEN8_TIMESTAMP_CNTS_PER_USEC;
+ break;
+ case 8:
+ cs_timestamp_base = GEN8_TIMESTAMP_CNTS_PER_USEC;
+ break;
+ }
+
+ dev_priv->cs_timestamp_base = cs_timestamp_base;
+ return cs_timestamp_base;
+}
+
+static inline u32
+watchdog_to_us(struct drm_i915_private *dev_priv, u32 value_in_clock_counts)
+{
+ return value_in_clock_counts / cs_timestamp_in_us(dev_priv);
+}
+
+static inline u32
+watchdog_to_clock_counts(struct drm_i915_private *dev_priv, u64 value_in_us)
+{
+ u64 threshold = value_in_us * cs_timestamp_in_us(dev_priv);
+
+ if (overflows_type(threshold, u32))
+ return -EINVAL;
+
+ return threshold;
+}
+
int i915_perf_open_ioctl(struct drm_device *dev, void *data,
struct drm_file *file);
@@ -337,6 +337,95 @@ i915_gem_context_create_gvt(struct drm_device *dev)
return ctx;
}
+/* Return the timer count threshold in microseconds. */
+int i915_gem_context_get_watchdog(struct i915_gem_context *ctx,
+ struct drm_i915_gem_context_param *args)
+{
+ struct drm_i915_private *dev_priv = ctx->i915;
+ struct intel_engine_cs *engine;
+ enum intel_engine_id id;
+ u32 threshold_in_us[I915_NUM_ENGINES];
+
+ if (!dev_priv->engine[VCS]->emit_start_watchdog)
+ return -ENODEV;
+
+ for_each_engine(engine, dev_priv, id) {
+ struct intel_context *ce = &ctx->engine[id];
+
+ threshold_in_us[id] = watchdog_to_us(dev_priv,
+ ce->watchdog_threshold);
+ }
+
+ mutex_unlock(&dev_priv->drm.struct_mutex);
+ if (__copy_to_user(u64_to_user_ptr(args->value),
+ &threshold_in_us,
+ sizeof(threshold_in_us))) {
+ mutex_lock(&dev_priv->drm.struct_mutex);
+ return -EFAULT;
+ }
+ mutex_lock(&dev_priv->drm.struct_mutex);
+
+ args->size = sizeof(threshold_in_us);
+
+ return 0;
+}
+
+/*
+ * Based on time out value in microseconds (us) calculate
+ * timer count thresholds needed based on core frequency.
+ * Watchdog can be disabled by setting it to 0.
+ */
+int i915_gem_context_set_watchdog(struct i915_gem_context *ctx,
+ struct drm_i915_gem_context_param *args)
+{
+ struct drm_i915_private *dev_priv = ctx->i915;
+ struct intel_engine_cs *engine;
+ enum intel_engine_id id;
+ u32 threshold[I915_NUM_ENGINES];
+
+ if (!dev_priv->engine[VCS]->emit_start_watchdog)
+ return -ENODEV;
+
+ memset(threshold, 0, sizeof(threshold));
+
+ /* shortcut to disable in all engines */
+ if (args->size == 0)
+ goto set_watchdog;
+
+ if (args->size < sizeof(threshold))
+ return -EFAULT;
+
+ mutex_unlock(&dev_priv->drm.struct_mutex);
+ if (copy_from_user(threshold,
+ u64_to_user_ptr(args->value),
+ sizeof(threshold))) {
+ mutex_lock(&dev_priv->drm.struct_mutex);
+ return -EFAULT;
+ }
+ mutex_lock(&dev_priv->drm.struct_mutex);
+
+ /* not supported in blitter engine */
+ if (threshold[BCS] != 0)
+ return -EINVAL;
+
+ for_each_engine(engine, dev_priv, id) {
+ threshold[id] = watchdog_to_clock_counts(dev_priv,
+ threshold[id]);
+
+ if (threshold[id] == -EINVAL)
+ return -EINVAL;
+ }
+
+set_watchdog:
+ for_each_engine(engine, dev_priv, id) {
+ struct intel_context *ce = &ctx->engine[id];
+
+ ce->watchdog_threshold = threshold[id];
+ }
+
+ return 0;
+}
+
int i915_gem_context_init(struct drm_i915_private *dev_priv)
{
struct i915_gem_context *ctx;
@@ -957,6 +1046,9 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data,
case I915_CONTEXT_PARAM_BANNABLE:
args->value = i915_gem_context_is_bannable(ctx);
break;
+ case I915_CONTEXT_PARAM_WATCHDOG:
+ ret = i915_gem_context_get_watchdog(ctx, args);
+ break;
default:
ret = -EINVAL;
break;
@@ -1014,6 +1106,9 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data,
else
i915_gem_context_clear_bannable(ctx);
break;
+ case I915_CONTEXT_PARAM_WATCHDOG:
+ ret = i915_gem_context_set_watchdog(ctx, args);
+ break;
default:
ret = -EINVAL;
break;
@@ -1573,7 +1573,7 @@ static inline u32 get_watchdog_disable(struct intel_engine_cs *engine)
return GEN8_XCS_WATCHDOG_DISABLE;
}
-#define GEN8_WATCHDOG_1000US 0x2ee0 //XXX: Temp, replace with helper function
+#define GEN8_WATCHDOG_1000US(dev_priv) watchdog_to_clock_counts(dev_priv, 1000)
static void gen8_watchdog_irq_handler(unsigned long data)
{
struct intel_engine_cs *engine = (struct intel_engine_cs *)data;
@@ -1603,7 +1603,8 @@ static void gen8_watchdog_irq_handler(unsigned long data)
} else {
engine->hangcheck.watchdog = current_seqno;
/* Re-start the counter, if really hung, it will expire again */
- I915_WRITE_FW(RING_THRESH(engine->mmio_base), GEN8_WATCHDOG_1000US);
+ I915_WRITE_FW(RING_THRESH(engine->mmio_base),
+ GEN8_WATCHDOG_1000US(dev_priv));
I915_WRITE_FW(RING_CNTR(engine->mmio_base), GEN8_WATCHDOG_ENABLE);
}
@@ -1308,6 +1308,7 @@ struct drm_i915_gem_context_param {
#define I915_CONTEXT_PARAM_GTT_SIZE 0x3
#define I915_CONTEXT_PARAM_NO_ERROR_CAPTURE 0x4
#define I915_CONTEXT_PARAM_BANNABLE 0x5
+#define I915_CONTEXT_PARAM_WATCHDOG 0x6
__u64 value;
};