Commit 5bb39e81 authored by Thomas Hellstrom's avatar Thomas Hellstrom Committed by Dave Airlie

vmwgfx: Handle device surface memory limit

Make surfaces swappable. Make sure we honor the maximum amount of surface
memory the device accepts. This is done by potentially reading back surface
contents not used by the current command submission and storing it
locally in buffer objects.
Signed-off-by: default avatarThomas Hellstrom <thellstrom@vmware.com>
Reviewed-by: default avatarJakob Bornecrantz <jakob@vmware.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 90ff18bc
......@@ -60,6 +60,11 @@ static uint32_t vram_gmr_placement_flags[] = {
VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED
};
static uint32_t gmr_vram_placement_flags[] = {
VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED,
TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED
};
struct ttm_placement vmw_vram_gmr_placement = {
.fpfn = 0,
.lpfn = 0,
......@@ -125,6 +130,15 @@ struct ttm_placement vmw_evictable_placement = {
.busy_placement = &sys_placement_flags
};
struct ttm_placement vmw_srf_placement = {
.fpfn = 0,
.lpfn = 0,
.num_placement = 1,
.num_busy_placement = 2,
.placement = &gmr_placement_flags,
.busy_placement = gmr_vram_placement_flags
};
struct vmw_ttm_backend {
struct ttm_backend backend;
struct page **pages;
......
......@@ -402,6 +402,8 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
init_waitqueue_head(&dev_priv->fifo_queue);
dev_priv->fence_queue_waiters = 0;
atomic_set(&dev_priv->fifo_queue_waiters, 0);
INIT_LIST_HEAD(&dev_priv->surface_lru);
dev_priv->used_memory_size = 0;
dev_priv->io_start = pci_resource_start(dev->pdev, 0);
dev_priv->vram_start = pci_resource_start(dev->pdev, 1);
......@@ -422,6 +424,10 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
dev_priv->capabilities = vmw_read(dev_priv, SVGA_REG_CAPABILITIES);
dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE);
dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE);
dev_priv->fb_max_width = vmw_read(dev_priv, SVGA_REG_MAX_WIDTH);
dev_priv->fb_max_height = vmw_read(dev_priv, SVGA_REG_MAX_HEIGHT);
if (dev_priv->capabilities & SVGA_CAP_GMR) {
dev_priv->max_gmr_descriptors =
vmw_read(dev_priv,
......@@ -434,13 +440,15 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
vmw_read(dev_priv, SVGA_REG_GMRS_MAX_PAGES);
dev_priv->memory_size =
vmw_read(dev_priv, SVGA_REG_MEMORY_SIZE);
dev_priv->memory_size -= dev_priv->vram_size;
} else {
/*
* An arbitrary limit of 512MiB on surface
* memory. But all HWV8 hardware supports GMR2.
*/
dev_priv->memory_size = 512*1024*1024;
}
dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE);
dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE);
dev_priv->fb_max_width = vmw_read(dev_priv, SVGA_REG_MAX_WIDTH);
dev_priv->fb_max_height = vmw_read(dev_priv, SVGA_REG_MAX_HEIGHT);
mutex_unlock(&dev_priv->hw_mutex);
vmw_print_capabilities(dev_priv->capabilities);
......@@ -454,8 +462,8 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
if (dev_priv->capabilities & SVGA_CAP_GMR2) {
DRM_INFO("Max number of GMR pages is %u\n",
(unsigned)dev_priv->max_gmr_pages);
DRM_INFO("Max dedicated hypervisor graphics memory is %u\n",
(unsigned)dev_priv->memory_size);
DRM_INFO("Max dedicated hypervisor surface memory is %u kiB\n",
(unsigned)dev_priv->memory_size / 1024);
}
DRM_INFO("VRAM at 0x%08x size is %u kiB\n",
dev_priv->vram_start, dev_priv->vram_size / 1024);
......
......@@ -79,6 +79,7 @@ struct vmw_resource {
int id;
enum ttm_object_type res_type;
bool avail;
void (*remove_from_lists) (struct vmw_resource *res);
void (*hw_destroy) (struct vmw_resource *res);
void (*res_free) (struct vmw_resource *res);
struct list_head validate_head;
......@@ -99,9 +100,11 @@ struct vmw_cursor_snooper {
};
struct vmw_framebuffer;
struct vmw_surface_offset;
struct vmw_surface {
struct vmw_resource res;
struct list_head lru_head; /* Protected by the resource lock */
uint32_t flags;
uint32_t format;
uint32_t mip_levels[DRM_VMW_MAX_SURFACE_FACES];
......@@ -112,6 +115,9 @@ struct vmw_surface {
/* TODO so far just a extra pointer */
struct vmw_cursor_snooper snooper;
struct ttm_buffer_object *backup;
struct vmw_surface_offset *offsets;
uint32_t backup_size;
};
struct vmw_marker_queue {
......@@ -310,6 +316,16 @@ struct vmw_private {
struct ttm_buffer_object *pinned_bo;
uint32_t query_cid;
bool dummy_query_bo_pinned;
/*
* Surface swapping. The "surface_lru" list is protected by the
* resource lock in order to be able to destroy a surface and take
* it off the lru atomically. "used_memory_size" is currently
* protected by the cmdbuf mutex for simplicity.
*/
struct list_head surface_lru;
uint32_t used_memory_size;
};
static inline struct vmw_private *vmw_priv(struct drm_device *dev)
......@@ -389,6 +405,8 @@ extern int vmw_surface_reference_ioctl(struct drm_device *dev, void *data,
extern int vmw_surface_check(struct vmw_private *dev_priv,
struct ttm_object_file *tfile,
uint32_t handle, int *id);
extern int vmw_surface_validate(struct vmw_private *dev_priv,
struct vmw_surface *srf);
extern void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo);
extern int vmw_dmabuf_init(struct vmw_private *dev_priv,
struct vmw_dma_buffer *vmw_bo,
......@@ -412,6 +430,7 @@ extern int vmw_user_stream_lookup(struct vmw_private *dev_priv,
struct ttm_object_file *tfile,
uint32_t *inout_id,
struct vmw_resource **out);
extern void vmw_resource_unreserve(struct list_head *list);
/**
* DMA buffer helper routines - vmwgfx_dmabuf.c
......@@ -486,6 +505,7 @@ extern struct ttm_placement vmw_vram_gmr_placement;
extern struct ttm_placement vmw_vram_gmr_ne_placement;
extern struct ttm_placement vmw_sys_placement;
extern struct ttm_placement vmw_evictable_placement;
extern struct ttm_placement vmw_srf_placement;
extern struct ttm_bo_driver vmw_bo_driver;
extern int vmw_dma_quiescent(struct drm_device *dev);
......@@ -508,6 +528,12 @@ extern void
vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv,
bool only_on_cid_match, uint32_t cid);
extern int vmw_execbuf_fence_commands(struct drm_file *file_priv,
struct vmw_private *dev_priv,
struct vmw_fence_obj **p_fence,
uint32_t *p_handle);
/**
* IRQs and wating - vmwgfx_irq.c
*/
......
......@@ -164,6 +164,14 @@ static int vmw_cmd_sid_check(struct vmw_private *dev_priv,
return ret;
}
ret = vmw_surface_validate(dev_priv, srf);
if (unlikely(ret != 0)) {
if (ret != -ERESTARTSYS)
DRM_ERROR("Could not validate surface.\n");
vmw_surface_unreference(&srf);
return ret;
}
sw_context->last_sid = *sid;
sw_context->sid_valid = true;
sw_context->sid_translation = srf->res.id;
......@@ -257,6 +265,7 @@ static int vmw_cmd_present_check(struct vmw_private *dev_priv,
SVGA3dCmdPresent body;
} *cmd;
cmd = container_of(header, struct vmw_sid_cmd, header);
if (unlikely(!sw_context->kernel)) {
......@@ -566,6 +575,13 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv,
goto out_no_reloc;
}
ret = vmw_surface_validate(dev_priv, srf);
if (unlikely(ret != 0)) {
if (ret != -ERESTARTSYS)
DRM_ERROR("Culd not validate surface.\n");
goto out_no_validate;
}
/*
* Patch command stream with device SID.
*/
......@@ -579,6 +595,8 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv,
return 0;
out_no_validate:
vmw_surface_unreference(&srf);
out_no_reloc:
vmw_dmabuf_unreference(&vmw_bo);
return ret;
......@@ -882,6 +900,7 @@ static void vmw_clear_validations(struct vmw_sw_context *sw_context)
/*
* Drop references to resources held during command submission.
*/
vmw_resource_unreserve(&sw_context->resource_list);
list_for_each_entry_safe(res, res_next, &sw_context->resource_list,
validate_head) {
list_del_init(&res->validate_head);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment