Re: [PATCH v3 2/8] drm/i915: Adds graphic address space ballooning logic

Tvrtko Ursulin
2014-12-16T13:41:36Z

On 12/16/2014 01:22 PM, Yu, Zhang wrote:
> On 12/12/2014 9:00 PM, Tvrtko Ursulin wrote:
>>
>> On 11/13/2014 12:02 PM, Yu Zhang wrote:
>>> With Intel GVT-g, the global graphic memory space is partitioned by
>>> multiple vGPU instances in different VMs. The ballooning code is called
>>> in i915_gem_setup_global_gtt(), utilizing the drm mm allocator APIs to
>>> mark the graphic address space which are partitioned out to other vGPUs
>>> as reserved.
>>>
>>> v2:
>>> take Chris and Daniel's comments:
>>> - no guard page between different VMs
>>> - use drm_mm_reserve_node() to do the reservation for ballooning,
>>> instead of the previous drm_mm_insert_node_in_range_generic()
>>>
>>> v3:
>>> take Daniel's comments:
>>> - move ballooning functions into i915_vgpu.c
>>> - add kerneldoc to ballooning functions
>>>
>>> Signed-off-by: Yu Zhang <yu.c.zhang@linux.intel.com>
>>> Signed-off-by: Jike Song <jike.song@intel.com>
>>> Signed-off-by: Zhi Wang <zhi.a.wang@intel.com>
>>> Signed-off-by: Eddie Dong <eddie.dong@intel.com>
>>> ---
>>> drivers/gpu/drm/i915/i915_gem_gtt.c | 17 +++-
>>> drivers/gpu/drm/i915/i915_vgpu.c | 149
>>> ++++++++++++++++++++++++++++++++++++
>>> drivers/gpu/drm/i915/i915_vgpu.h | 2 +
>>> 3 files changed, 165 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c
>>> b/drivers/gpu/drm/i915/i915_gem_gtt.c
>>> index de12017..2dfac13 100644
>>> --- a/drivers/gpu/drm/i915/i915_gem_gtt.c
>>> +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
>>> @@ -27,6 +27,7 @@
>>> #include <drm/drmP.h>
>>> #include <drm/i915_drm.h>
>>> #include "i915_drv.h"
>>> +#include "i915_vgpu.h"
>>> #include "i915_trace.h"
>>> #include "intel_drv.h"
>>>
>>> @@ -1683,6 +1684,16 @@ int i915_gem_setup_global_gtt(struct drm_device
>>> *dev,
>>>
>>> /* Subtract the guard page ... */
>>> drm_mm_init(&ggtt_vm->mm, start, end - start - PAGE_SIZE);
>>> +
>>> + dev_priv->gtt.base.start = start;
>>> + dev_priv->gtt.base.total = end - start;
>>> +
>>> + if (intel_vgpu_active(dev)) {
>>> + ret = intel_vgt_balloon(dev);
>>> + if (ret)
>>> + return ret;
>>> + }
>>> +
>>
>> Out of curiosity, what will be the mechanism to prevent a vGPU instance
>> from ignoring the ballooning data? Must be something in the hypervisor
>> blocking pass-through access to such domains?
> Well, although we have range check logic in the host side(which checks
> the legality of a GM address), the correctness of a GM from vGPU side

Oh, I thought that part is direct for performance reasons (avoiding
translation). It's not then?

> are supposed to be guaranteed by the drm mm allocator - all those
> ballooned out spaces are marked as reserved.

I was thinking about a malicious i915 driver instance, or simply a bug
in DRM or i915 which breaks the blocked ranges assumption.

If that is possible then it sounds pretty bad if one VM could snoop the
surfaces from another.

>> And probably GPU reset should also be disallowed for vGPU instances?
>>
>>> if (!HAS_LLC(dev))
>>> dev_priv->gtt.base.mm.color_adjust = i915_gtt_color_adjust;
>>>
>>> @@ -1702,9 +1713,6 @@ int i915_gem_setup_global_gtt(struct drm_device
>>> *dev,
>>> vma->bound |= GLOBAL_BIND;
>>> }
>>>
>>> - dev_priv->gtt.base.start = start;
>>> - dev_priv->gtt.base.total = end - start;
>>> -
>>> /* Clear any non-preallocated blocks */
>>> drm_mm_for_each_hole(entry, &ggtt_vm->mm, hole_start, hole_end) {
>>> DRM_DEBUG_KMS("clearing unused GTT space: [%lx, %lx]\n",
>>> @@ -1756,6 +1764,9 @@ void i915_global_gtt_cleanup(struct drm_device
>>> *dev)
>>> }
>>>
>>> if (drm_mm_initialized(&vm->mm)) {
>>> + if (intel_vgpu_active(dev))
>>> + intel_vgt_deballoon();
>>> +
>>> drm_mm_takedown(&vm->mm);
>>> list_del(&vm->global_link);
>>> }
>>> diff --git a/drivers/gpu/drm/i915/i915_vgpu.c
>>> b/drivers/gpu/drm/i915/i915_vgpu.c
>>> index 3f6b797..ff5fba3 100644
>>> --- a/drivers/gpu/drm/i915/i915_vgpu.c
>>> +++ b/drivers/gpu/drm/i915/i915_vgpu.c
>>> @@ -83,3 +83,152 @@ void i915_check_vgpu(struct drm_device *dev)
>>> dev_priv->vgpu.active = true;
>>> DRM_INFO("Virtual GPU for Intel GVT-g detected.\n");
>>> }
>>> +
>>> +struct _balloon_info_ {
>>> + /*
>>> + * There are up to 2 regions per low/high graphic memory that
>>> + * might be ballooned. Here, index 0/1 is for low
>>> + * graphic memory, 2/3 for high graphic memory.
>>> + */
>>> + struct drm_mm_node space[4];
>>> +} bl_info;
>>
>> This should be static I think.
> Yes, you are right.
>>
>>> +/**
>>> + * intel_vgt_deballoon - deballoon reserved graphics address trunks
>>> + *
>>> + * This function is called to deallocate the ballooned-out graphic
>>> memory, when
>>> + * driver is unloaded or when ballooning fails.
>>> + */
>>> +void intel_vgt_deballoon(void)
>>> +{
>>> + int i;
>>> +
>>> + DRM_INFO("VGT deballoon.\n");
>>
>> Would debug be more appropriate? I don't see much value of saying this
>> on driver unload - it's not that it is optional at this point.
> OK. :)
>>
>> Also for all logging, is intended human readable name VGT or vGT? If the
>> latter it would be nicer to log it in that form.
> Well, I prefer VGT. :)

So it's vGPU and VGT.. OK... :) (and GVT-d) :)

>>> +
>>> + for (i = 0; i < 4; i++) {
>>> + if (bl_info.space[i].allocated)
>>> + drm_mm_remove_node(&bl_info.space[i]);
>>> + }
>>> +
>>> + memset(&bl_info, 0, sizeof(bl_info));
>>> +}
>>> +
>>> +static int vgt_balloon_space(struct drm_mm *mm,
>>> + struct drm_mm_node *node,
>>> + unsigned long start, unsigned long end)
>>> +{
>>> + unsigned long size = end - start;
>>> +
>>> + if (start == end)
>>> + return -EINVAL;
>>> +
>>> + DRM_INFO("balloon space: range [ 0x%lx - 0x%lx ] %lu KB.\n",
>>> + start, end, size / 1024);
>>
>> KiB ?
> Yes. Thanks. :)
>>
>>> + node->start = start;
>>> + node->size = size;
>>> +
>>> + return drm_mm_reserve_node(mm, node);
>>> +}
>>> +
>>> +/**
>>> + * intel_vgt_balloon - balloon out reserved graphics address trunks
>>> + * @dev: drm device
>>> + *
>>> + * This function is called at the initialization stage, to balloon
>>> out the
>>> + * graphic address space allocated to other VMs, by marking these
>>> spaces as
>>> + * reserved.
>>> + *
>>> + * The ballooning related knowledges(starting address and size of the
>>> low/high
>>
>> s/knowledges\(/knowledge /
> Got it.
>>
>>> + * graphic memory) are depicted in the vgt_if structure in a reserved
>>> MMIO
>>> + * range.
>>> + *
>>> + * Returns:
>>> + * zero on success, non-zero if configuration invalid or ballooning
>>> failed
>>> + */
>>> +int intel_vgt_balloon(struct drm_device *dev)
>>> +{
>>> + struct drm_i915_private *dev_priv = to_i915(dev);
>>> + struct i915_address_space *ggtt_vm = &dev_priv->gtt.base;
>>> + unsigned long ggtt_vm_end = ggtt_vm->start + ggtt_vm->total;
>>> +
>>> + unsigned long low_gm_base, low_gm_size, low_gm_end;
>>> + unsigned long high_gm_base, high_gm_size, high_gm_end;
>>> + int ret;
>>> +
>>> + low_gm_base = I915_READ(vgtif_reg(avail_rs.low_gmadr.my_base));
>>> + low_gm_size = I915_READ(vgtif_reg(avail_rs.low_gmadr.my_size));
>>> + high_gm_base = I915_READ(vgtif_reg(avail_rs.high_gmadr.my_base));
>>> + high_gm_size = I915_READ(vgtif_reg(avail_rs.high_gmadr.my_size));
>>
>> Get rid of my_ prefix ?
> OK. Will do. :)
>>
>>> +
>>> + low_gm_end = low_gm_base + low_gm_size;
>>> + high_gm_end = high_gm_base + high_gm_size;
>>> +
>>> + DRM_INFO("VGT ballooning configuration:\n");
>>> + DRM_INFO("Low graphic memory: base 0x%lx size %ldKB\n",
>>> + low_gm_base, low_gm_size / 1024);
>>> + DRM_INFO("High graphic memory: base 0x%lx size %ldKB\n",
>>> + high_gm_base, high_gm_size / 1024);
>>> +
>>> + if (low_gm_base < ggtt_vm->start
>>> + || low_gm_end > dev_priv->gtt.mappable_end
>>> + || high_gm_base < dev_priv->gtt.mappable_end
>>> + || high_gm_end > ggtt_vm_end) {
>>> + DRM_ERROR("Invalid ballooning configuration!\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + memset(&bl_info, 0, sizeof(bl_info));
>>
>> If bl_info is static then you don't need this memset?
> Oh, yes. :)
>>
>>> + /* High graphic memory ballooning */
>>> + if (high_gm_base > dev_priv->gtt.mappable_end) {
>>> + ret = vgt_balloon_space(&ggtt_vm->mm,
>>> + &bl_info.space[2],
>>> + dev_priv->gtt.mappable_end,
>>> + high_gm_base);
>>> +
>>> + if (ret)
>>> + goto err;
>>> + }
>>> +
>>> + /*
>>> + * No need to partition out the last physical page,
>>> + * because it is reserved to the guard page.
>>> + */
>>> + if (high_gm_end < ggtt_vm_end - PAGE_SIZE) {
>>> + ret = vgt_balloon_space(&ggtt_vm->mm,
>>> + &bl_info.space[3],
>>> + high_gm_end,
>>> + ggtt_vm_end - PAGE_SIZE);
>>> + if (ret)
>>> + goto err;
>>> + }
>>> +
>>> + /* Low graphic memory ballooning */
>>> + if (low_gm_base > ggtt_vm->start) {
>>> + ret = vgt_balloon_space(&ggtt_vm->mm,
>>> + &bl_info.space[0],
>>> + ggtt_vm->start, low_gm_base);
>>> +
>>> + if (ret)
>>> + goto err;
>>> + }
>>> +
>>> + if (low_gm_end < dev_priv->gtt.mappable_end) {
>>> + ret = vgt_balloon_space(&ggtt_vm->mm,
>>> + &bl_info.space[1],
>>> + low_gm_end,
>>> + dev_priv->gtt.mappable_end);
>>> +
>>> + if (ret)
>>> + goto err;
>>> + }
>>
>> Okay, I've figured it out. :) I suppose going back to patch 1, where it
>> says "Each VM can only have one portion of continuous area for now",
>> with the emphasis on _one_. That threw me off thinking you have two
>> "balloons" meaning forbidden areas. And then with low and high naming I
>> got the wrong idea that one balloon marks the bottom inaccessible part,
>> and the other top. I didn't figure out the whole low == mappable, high
>> == non-mappable split. I suppose it was my inexperience, but if you can
>> think of a way on how to improve the comment, even ASCII art would be
>> very nice if possible.
> Hah. Guess I need a better comments above. Let me think how to
> organize. :)

Cool thanks!

>> Actually ballooning in the first place confuses me because I thought
>> prior use for that in virtualisation was for memory which can shrink and
>> grow at runtime. Did I get that wrong?
>>
> Well, for memory virtualization, the word 'ballooning' does mean this -
> it can shrink and grow.
> Yet for our Intel GVT-g, the 'ballooning' is used, to emphasis that we
> are not just partitioning the GM space to each vgpu, the vgpu can also
> perceive its GM address space as starting from zero(although the usable
> GM spaces may probably not).
> By now, we do not grow/shrink. :)

Unless there are plan to make it grow and shrink it seems that we agree
it is confusing. But I suppose it is too late to change the term now?

Regards,

Tvrtko