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 at linux.intel.com> CC: Bryan Bell <bryan.j.bell at intel.com> Signed-off-by: Ben Widawsky <ben at 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