@@ -39,6 +39,7 @@
#include <linux/backlight.h>
#include <linux/hashtable.h>
#include <linux/intel-iommu.h>
+#include <linux/intel-svm.h>
#include <linux/kref.h>
#include <linux/pm_qos.h>
#include <linux/reservation.h>
@@ -787,6 +788,7 @@ struct intel_csr {
func(has_fpga_dbg); \
func(has_full_ppgtt); \
func(has_full_48bit_ppgtt); \
+ func(has_svm); \
func(has_gmbus_irq); \
func(has_gmch_display); \
func(has_guc); \
@@ -2784,6 +2786,7 @@ intel_info(const struct drm_i915_private *dev_priv)
#define USES_PPGTT(dev_priv) (i915.enable_ppgtt)
#define USES_FULL_PPGTT(dev_priv) (i915.enable_ppgtt >= 2)
#define USES_FULL_48BIT_PPGTT(dev_priv) (i915.enable_ppgtt == 3)
+#define HAS_SVM(dev_priv) ((dev_priv)->info.has_svm)
#define HAS_OVERLAY(dev_priv) ((dev_priv)->info.has_overlay)
#define OVERLAY_NEEDS_PHYSICAL(dev_priv) \
@@ -134,6 +134,16 @@ static int get_context_size(struct drm_i915_private *dev_priv)
return ret;
}
+static void i915_gem_context_svm_fini(struct i915_gem_context *ctx)
+{
+ struct device *dev = &ctx->i915->drm.pdev->dev;
+
+ GEM_BUG_ON(!ctx->task);
+
+ intel_svm_unbind_mm(dev, ctx->pasid);
+ put_task_struct(ctx->task);
+}
+
void i915_gem_context_free(struct kref *ctx_ref)
{
struct i915_gem_context *ctx = container_of(ctx_ref, typeof(*ctx), ref);
@@ -143,6 +153,9 @@ void i915_gem_context_free(struct kref *ctx_ref)
trace_i915_context_free(ctx);
GEM_BUG_ON(!i915_gem_context_is_closed(ctx));
+ if (i915_gem_context_is_svm(ctx))
+ i915_gem_context_svm_fini(ctx);
+
i915_ppgtt_put(ctx->ppgtt);
for (i = 0; i < I915_NUM_ENGINES; i++) {
@@ -272,6 +285,61 @@ static u32 create_default_ctx_desc(const struct drm_i915_private *dev_priv)
return desc;
}
+static u32 create_svm_ctx_desc(void)
+{
+ /*
+ * Switch to stream once we have a scheduler and can
+ * re-submit contexts.
+ */
+ return GEN8_CTX_VALID |
+ INTEL_ADVANCED_CONTEXT << GEN8_CTX_ADDRESSING_MODE_SHIFT |
+ FAULT_AND_HALT << GEN8_CTX_FAULT_SHIFT;
+}
+
+static int i915_gem_context_svm_init(struct i915_gem_context *ctx)
+{
+ struct device *dev = &ctx->i915->drm.pdev->dev;
+ int ret;
+
+ GEM_BUG_ON(ctx->task);
+
+ if (!HAS_SVM(ctx->i915))
+ return -ENODEV;
+
+ get_task_struct(current);
+
+ ret = intel_svm_bind_mm(dev, &ctx->pasid, 0, NULL);
+ if (ret) {
+ DRM_DEBUG_DRIVER("pasid alloc fail: %d\n", ret);
+ put_task_struct(current);
+ return ret;
+ }
+
+ ctx->task = current;
+ i915_gem_context_set_svm(ctx);
+
+ return 0;
+}
+
+static int i915_gem_context_enable_svm(struct i915_gem_context *ctx)
+{
+ int ret;
+
+ if (!HAS_SVM(ctx->i915))
+ return -ENODEV;
+
+ if (i915_gem_context_is_svm(ctx))
+ return -EINVAL;
+
+ ret = i915_gem_context_svm_init(ctx);
+ if (ret)
+ return ret;
+
+ ctx->desc_template = create_svm_ctx_desc();
+
+ return 0;
+}
+
static struct i915_gem_context *
__create_hw_context(struct drm_i915_private *dev_priv,
struct drm_i915_file_private *file_priv)
@@ -1071,6 +1139,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_SVM:
+ args->value = i915_gem_context_is_svm(ctx);
+ break;
default:
ret = -EINVAL;
break;
@@ -1128,6 +1199,13 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data,
else
i915_gem_context_clear_bannable(ctx);
break;
+ case I915_CONTEXT_PARAM_SVM:
+ /* There is no coming back once svm is enabled */
+ if (args->value && !args->size)
+ ret = i915_gem_context_enable_svm(ctx);
+ else
+ ret = -EINVAL;
+ break;
default:
ret = -EINVAL;
break;
@@ -108,6 +108,7 @@ struct i915_gem_context {
#define CONTEXT_BANNABLE 3
#define CONTEXT_BANNED 4
#define CONTEXT_FORCE_SINGLE_SUBMISSION 5
+#define CONTEXT_SVM 6
/**
* @hw_id: - unique identifier for the context
@@ -140,6 +141,25 @@ struct i915_gem_context {
*/
int priority;
+ /**
+ * @pasid: process address space identifier
+ *
+ * Unique identifier for the shared address space with cpu.
+ * Used by the gpu to associate context's (ppgtt) address
+ * space with the corresponding process's address space for
+ * Shared Virtual Memory (SVM). 20 bits.
+ */
+ u32 pasid;
+
+ /**
+ * @task: user space task struct for this context
+ *
+ * If this is svm context, @task is the corresponding
+ * user space process with which we share the vm. See
+ * @pasid.
+ */
+ struct task_struct *task;
+
/** ggtt_alignment: alignment restriction for context objects */
u32 ggtt_alignment;
/** ggtt_offset_bias: placement restriction for context objects */
@@ -241,6 +261,16 @@ static inline void i915_gem_context_set_force_single_submission(struct i915_gem_
__set_bit(CONTEXT_FORCE_SINGLE_SUBMISSION, &ctx->flags);
}
+static inline bool i915_gem_context_is_svm(const struct i915_gem_context *ctx)
+{
+ return test_bit(CONTEXT_SVM, &ctx->flags);
+}
+
+static inline void i915_gem_context_set_svm(struct i915_gem_context *ctx)
+{
+ __set_bit(CONTEXT_SVM, &ctx->flags);
+}
+
static inline bool i915_gem_context_is_default(const struct i915_gem_context *c)
{
return c->user_handle == DEFAULT_CONTEXT_HANDLE;
@@ -3375,6 +3375,7 @@ enum {
INTEL_LEGACY_64B_CONTEXT : \
INTEL_LEGACY_32B_CONTEXT)
+#define GEN8_CTX_FAULT_SHIFT 6
#define GEN8_CTX_ID_SHIFT 32
#define GEN8_CTX_ID_WIDTH 21
@@ -414,6 +414,10 @@ void intel_device_info_runtime_init(struct drm_i915_private *dev_priv)
if (IS_BXT_REVID(dev_priv, 0, BXT_REVID_A1))
info->has_snoop = false;
+ info->has_svm = INTEL_GEN(dev_priv) >= 9 &&
+ info->has_full_48bit_ppgtt &&
+ intel_svm_available(&dev_priv->drm.pdev->dev);
+
DRM_DEBUG_DRIVER("slice mask: %04x\n", info->sseu.slice_mask);
DRM_DEBUG_DRIVER("slice total: %u\n", hweight8(info->sseu.slice_mask));
DRM_DEBUG_DRIVER("subslice total: %u\n",
@@ -429,4 +433,6 @@ void intel_device_info_runtime_init(struct drm_i915_private *dev_priv)
info->sseu.has_subslice_pg ? "y" : "n");
DRM_DEBUG_DRIVER("has EU power gating: %s\n",
info->sseu.has_eu_pg ? "y" : "n");
+ DRM_DEBUG_DRIVER("has svm: %s\n",
+ info->has_svm ? "y" : "n");
}
@@ -209,6 +209,13 @@
#define GEN8_CTX_RCS_INDIRECT_CTX_OFFSET_DEFAULT 0x17
#define GEN9_CTX_RCS_INDIRECT_CTX_OFFSET_DEFAULT 0x26
+#define ASSIGN_CTX_SVM(reg_state, ctx, engine) do { \
+ ASSIGN_CTX_REG(reg_state, CTX_PDP0_UDW, \
+ GEN8_RING_PDP_UDW((engine), 0), 0); \
+ ASSIGN_CTX_REG(reg_state, CTX_PDP0_LDW, \
+ GEN8_RING_PDP_LDW((engine), 0), (ctx)->pasid); \
+} while (0)
+
/* Typical size of the average request (2 pipecontrols and a MI_BB) */
#define EXECLISTS_REQUEST_SIZE 64 /* bytes */
@@ -2084,7 +2091,9 @@ static void execlists_init_reg_state(u32 *reg_state,
ASSIGN_CTX_REG(reg_state, CTX_PDP0_LDW, GEN8_RING_PDP_LDW(engine, 0),
0);
- if (USES_FULL_48BIT_PPGTT(ppgtt->base.dev)) {
+ if (i915_gem_context_is_svm(ctx)) {
+ ASSIGN_CTX_SVM(reg_state, ctx, engine);
+ } else if (USES_FULL_48BIT_PPGTT(ppgtt->base.dev)) {
/* 64b PPGTT (48bit canonical)
* PDP0_DESCRIPTOR contains the base address to PML4 and
* other PDP Descriptors are ignored.
@@ -1227,6 +1227,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_SVM 0x6
__u64 value;
};