[PATCH] [v2] drm/i915: Expose LLC size to user space

Ben Widawsky
2013-07-11T18:48:58Z

The algorithm/information was originally written by Chad, though I
changed the control flow, and I think his original code had a couple of
bugs, though I didn't look very hard before rewriting. That could have
also been different interpretations of the spec.

I've tested this on two platforms, and it seems to perform how I want.

With this patch is a small tool for igt to query the size. This can be
used as a reference for DRI clients wishing to query the information.

v2: Update name of the SDM location (Bryan)
Dissent: Use a new param instead of reusing HAS_LLC param (Chris, Chad)
Fix unicode multiply symbol (Ben)

CC: Chad Versace <chad.versace@linux.intel.com>
CC: Bryan Bell <bryan.j.bell@intel.com>
Signed-off-by: Ben Widawsky <ben@bwidawsk.net>
---
drivers/gpu/drm/i915/i915_dma.c | 3 +++
drivers/gpu/drm/i915/i915_drv.h | 2 ++
drivers/gpu/drm/i915/i915_gem.c | 55 +++++++++++++++++++++++++++++++++++++++++
include/uapi/drm/i915_drm.h | 1 +
4 files changed, 61 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 0e22142..1224586 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1000,6 +1000,9 @@ static int i915_getparam(struct drm_device *dev, void *data,
case I915_PARAM_HAS_EXEC_HANDLE_LUT:
value = 1;
break;
+ case I915_PARAM_LLC_SIZE:
+ value = dev_priv->llc_size;
+ break;
default:
DRM_DEBUG("Unknown parameter %d\n", param->param);
return -EINVAL;
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index c8d6104..43a549d 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1187,6 +1187,8 @@ typedef struct drm_i915_private {
/* Old dri1 support infrastructure, beware the dragons ya fools entering
* here! */
struct i915_dri1_state dri1;
+
+ size_t llc_size;
} drm_i915_private_t;

/* Iterate over initialised rings */
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index af61be8..629837d 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -4282,6 +4282,59 @@ i915_gem_lastclose(struct drm_device *dev)
DRM_ERROR("failed to idle hardware: %d\n", ret);
}

+/**
+ * Return the size, in bytes, of the CPU L3 cache size. If the CPU has no L3
+ * cache, or if an error occurs in obtaining the cache size, then return 0.
+ * From Intel 64 and IA32 Architectures Developer's Manual: Vol. 2A -> section
+ * 3.2 -> CPUID
+ *
+ * Deterministic Cache Parmaeters (Function 04h)":
+ * When EAX is initialized to a value of 4, the CPUID instruction returns
+ * deterministic cache information in the EAX, EBX, ECX and EDX registers.
+ * This function requires ECX be initialized with an index which indicates
+ * which cache to return information about. The OS is expected to call this
+ * function (CPUID.4) with ECX = 0, 1, 2, until EAX[4:0] == 0, indicating no
+ * more caches. The order in which the caches are returned is not specified
+ * and may change at Intel's discretion.
+ *
+ * Calculating the Cache Size in bytes:
+ * = (Ways +1) * (Partitions +1) * (Line Size +1) * (Sets +1)
+ * = (EBX[31:22] +1) * (EBX[21:12] +1) * (EBX[11:0] +1 * (ECX + 1)
+ */
+static size_t get_llc_size(struct drm_device *dev)
+{
+ u8 cnt = 0;
+ unsigned int eax, ebx, ecx, edx;
+
+ if (!HAS_LLC(dev))
+ return 0;
+
+ do {
+ uint32_t cache_level;
+ uint32_t associativity, line_partitions, line_size, sets;
+
+ eax = 4;
+ ecx = cnt;
+ __cpuid(&eax, &ebx, &ecx, &edx);
+
+ cache_level = (eax >> 5) & 0x7;
+ if (cache_level != 3)
+ continue;
+
+ associativity = ((ebx >> 22) & 0x3ff) + 1;
+ line_partitions = ((ebx >> 12) & 0x3ff) + 1;
+ line_size = (ebx & 0xfff) + 1;
+ sets = ecx + 1;
+
+ return associativity * line_partitions * line_size * sets;
+ } while (eax & 0x1f && ++cnt);
+
+ /* Let user space know we have non-zero LLC, we can't figure it out */
+ DRM_DEBUG_DRIVER("Couldn't find LLC size. Bug?\n");
+ return 1;
+}
+
+
static void
init_ring_lists(struct intel_ring_buffer *ring)
{
@@ -4333,6 +4386,8 @@ i915_gem_load(struct drm_device *dev)
else
dev_priv->num_fence_regs = 8;

+ dev_priv->llc_size = get_llc_size(dev);
+
/* Initialize fence registers to zero */
INIT_LIST_HEAD(&dev_priv->mm.fence_list);
i915_gem_restore_fences(dev);
diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h
index 923ed7f..c54559e 100644
--- a/include/uapi/drm/i915_drm.h
+++ b/include/uapi/drm/i915_drm.h
@@ -310,6 +310,7 @@ typedef struct drm_i915_irq_wait {
#define I915_PARAM_HAS_PINNED_BATCHES 24
#define I915_PARAM_HAS_EXEC_NO_RELOC 25
#define I915_PARAM_HAS_EXEC_HANDLE_LUT 26
+#define I915_PARAM_LLC_SIZE 27

typedef struct drm_i915_getparam {
int param;
--
1.8.3.2