Commit 93392217 authored by Thomas Hellstrom's avatar Thomas Hellstrom

drm/vmwgfx: Implement an infrastructure for write-coherent resources

This infrastructure will, for coherent resources, make sure that
from the user-space point of view, data written by the CPU is immediately
automatically available to the GPU at resource validation time.
Signed-off-by: default avatarThomas Hellstrom <thellstrom@vmware.com>
Reviewed-by: default avatarDeepak Rawat <drawat@vmware.com>
parent 7a39f35c
...@@ -8,6 +8,7 @@ config DRM_VMWGFX ...@@ -8,6 +8,7 @@ config DRM_VMWGFX
select FB_CFB_IMAGEBLIT select FB_CFB_IMAGEBLIT
select DRM_TTM select DRM_TTM
select FB select FB
select AS_DIRTY_HELPERS
# Only needed for the transitional use of drm_crtc_init - can be removed # Only needed for the transitional use of drm_crtc_init - can be removed
# again once vmwgfx sets up the primary plane itself. # again once vmwgfx sets up the primary plane itself.
select DRM_KMS_HELPER select DRM_KMS_HELPER
......
...@@ -8,7 +8,7 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \ ...@@ -8,7 +8,7 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \
vmwgfx_cmdbuf_res.o vmwgfx_cmdbuf.o vmwgfx_stdu.o \ vmwgfx_cmdbuf_res.o vmwgfx_cmdbuf.o vmwgfx_stdu.o \
vmwgfx_cotable.o vmwgfx_so.o vmwgfx_binding.o vmwgfx_msg.o \ vmwgfx_cotable.o vmwgfx_so.o vmwgfx_binding.o vmwgfx_msg.o \
vmwgfx_simple_resource.o vmwgfx_va.o vmwgfx_blit.o \ vmwgfx_simple_resource.o vmwgfx_va.o vmwgfx_blit.o \
vmwgfx_validation.o \ vmwgfx_validation.o vmwgfx_page_dirty.o \
ttm_object.o ttm_lock.o ttm_object.o ttm_lock.o
obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o
...@@ -463,6 +463,7 @@ void vmw_bo_bo_free(struct ttm_buffer_object *bo) ...@@ -463,6 +463,7 @@ void vmw_bo_bo_free(struct ttm_buffer_object *bo)
{ {
struct vmw_buffer_object *vmw_bo = vmw_buffer_object(bo); struct vmw_buffer_object *vmw_bo = vmw_buffer_object(bo);
WARN_ON(vmw_bo->dirty);
vmw_bo_unmap(vmw_bo); vmw_bo_unmap(vmw_bo);
kfree(vmw_bo); kfree(vmw_bo);
} }
...@@ -476,8 +477,10 @@ void vmw_bo_bo_free(struct ttm_buffer_object *bo) ...@@ -476,8 +477,10 @@ void vmw_bo_bo_free(struct ttm_buffer_object *bo)
static void vmw_user_bo_destroy(struct ttm_buffer_object *bo) static void vmw_user_bo_destroy(struct ttm_buffer_object *bo)
{ {
struct vmw_user_buffer_object *vmw_user_bo = vmw_user_buffer_object(bo); struct vmw_user_buffer_object *vmw_user_bo = vmw_user_buffer_object(bo);
struct vmw_buffer_object *vbo = &vmw_user_bo->vbo;
vmw_bo_unmap(&vmw_user_bo->vbo); WARN_ON(vbo->dirty);
vmw_bo_unmap(vbo);
ttm_prime_object_kfree(vmw_user_bo, prime); ttm_prime_object_kfree(vmw_user_bo, prime);
} }
......
...@@ -833,6 +833,11 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) ...@@ -833,6 +833,11 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
DRM_ERROR("Failed initializing TTM buffer object driver.\n"); DRM_ERROR("Failed initializing TTM buffer object driver.\n");
goto out_no_bdev; goto out_no_bdev;
} }
dev_priv->vm_ops = *dev_priv->bdev.vm_ops;
dev_priv->vm_ops.fault = vmw_bo_vm_fault;
dev_priv->vm_ops.pfn_mkwrite = vmw_bo_vm_mkwrite;
dev_priv->vm_ops.page_mkwrite = vmw_bo_vm_mkwrite;
dev_priv->bdev.vm_ops = &dev_priv->vm_ops;
/* /*
* Enable VRAM, but initially don't use it until SVGA is enabled and * Enable VRAM, but initially don't use it until SVGA is enabled and
......
...@@ -94,6 +94,7 @@ struct vmw_fpriv { ...@@ -94,6 +94,7 @@ struct vmw_fpriv {
* @dx_query_ctx: DX context if this buffer object is used as a DX query MOB * @dx_query_ctx: DX context if this buffer object is used as a DX query MOB
* @map: Kmap object for semi-persistent mappings * @map: Kmap object for semi-persistent mappings
* @res_prios: Eviction priority counts for attached resources * @res_prios: Eviction priority counts for attached resources
* @dirty: structure for user-space dirty-tracking
*/ */
struct vmw_buffer_object { struct vmw_buffer_object {
struct ttm_buffer_object base; struct ttm_buffer_object base;
...@@ -104,6 +105,7 @@ struct vmw_buffer_object { ...@@ -104,6 +105,7 @@ struct vmw_buffer_object {
/* Protected by reservation */ /* Protected by reservation */
struct ttm_bo_kmap_obj map; struct ttm_bo_kmap_obj map;
u32 res_prios[TTM_MAX_BO_PRIORITY]; u32 res_prios[TTM_MAX_BO_PRIORITY];
struct vmw_bo_dirty *dirty;
}; };
/** /**
...@@ -134,7 +136,8 @@ struct vmw_res_func; ...@@ -134,7 +136,8 @@ struct vmw_res_func;
* @res_dirty: Resource contains data not yet in the backup buffer. Protected * @res_dirty: Resource contains data not yet in the backup buffer. Protected
* by resource reserved. * by resource reserved.
* @backup_dirty: Backup buffer contains data not yet in the HW resource. * @backup_dirty: Backup buffer contains data not yet in the HW resource.
* Protecte by resource reserved. * Protected by resource reserved.
* @coherent: Emulate coherency by tracking vm accesses.
* @backup: The backup buffer if any. Protected by resource reserved. * @backup: The backup buffer if any. Protected by resource reserved.
* @backup_offset: Offset into the backup buffer if any. Protected by resource * @backup_offset: Offset into the backup buffer if any. Protected by resource
* reserved. Note that only a few resource types can have a @backup_offset * reserved. Note that only a few resource types can have a @backup_offset
...@@ -151,14 +154,16 @@ struct vmw_res_func; ...@@ -151,14 +154,16 @@ struct vmw_res_func;
* @hw_destroy: Callback to destroy the resource on the device, as part of * @hw_destroy: Callback to destroy the resource on the device, as part of
* resource destruction. * resource destruction.
*/ */
struct vmw_resource_dirty;
struct vmw_resource { struct vmw_resource {
struct kref kref; struct kref kref;
struct vmw_private *dev_priv; struct vmw_private *dev_priv;
int id; int id;
u32 used_prio; u32 used_prio;
unsigned long backup_size; unsigned long backup_size;
bool res_dirty; u32 res_dirty : 1;
bool backup_dirty; u32 backup_dirty : 1;
u32 coherent : 1;
struct vmw_buffer_object *backup; struct vmw_buffer_object *backup;
unsigned long backup_offset; unsigned long backup_offset;
unsigned long pin_count; unsigned long pin_count;
...@@ -166,6 +171,7 @@ struct vmw_resource { ...@@ -166,6 +171,7 @@ struct vmw_resource {
struct list_head lru_head; struct list_head lru_head;
struct list_head mob_head; struct list_head mob_head;
struct list_head binding_head; struct list_head binding_head;
struct vmw_resource_dirty *dirty;
void (*res_free) (struct vmw_resource *res); void (*res_free) (struct vmw_resource *res);
void (*hw_destroy) (struct vmw_resource *res); void (*hw_destroy) (struct vmw_resource *res);
}; };
...@@ -606,6 +612,9 @@ struct vmw_private { ...@@ -606,6 +612,9 @@ struct vmw_private {
/* Validation memory reservation */ /* Validation memory reservation */
struct vmw_validation_mem vvm; struct vmw_validation_mem vvm;
/* VM operations */
struct vm_operations_struct vm_ops;
}; };
static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res) static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res)
...@@ -722,6 +731,8 @@ extern void vmw_resource_evict_all(struct vmw_private *dev_priv); ...@@ -722,6 +731,8 @@ extern void vmw_resource_evict_all(struct vmw_private *dev_priv);
extern void vmw_resource_unbind_list(struct vmw_buffer_object *vbo); extern void vmw_resource_unbind_list(struct vmw_buffer_object *vbo);
void vmw_resource_mob_attach(struct vmw_resource *res); void vmw_resource_mob_attach(struct vmw_resource *res);
void vmw_resource_mob_detach(struct vmw_resource *res); void vmw_resource_mob_detach(struct vmw_resource *res);
void vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start,
pgoff_t end);
/** /**
* vmw_resource_mob_attached - Whether a resource currently has a mob attached * vmw_resource_mob_attached - Whether a resource currently has a mob attached
...@@ -1410,6 +1421,15 @@ int vmw_host_log(const char *log); ...@@ -1410,6 +1421,15 @@ int vmw_host_log(const char *log);
#define VMW_DEBUG_USER(fmt, ...) \ #define VMW_DEBUG_USER(fmt, ...) \
DRM_DEBUG_DRIVER(fmt, ##__VA_ARGS__) DRM_DEBUG_DRIVER(fmt, ##__VA_ARGS__)
/* Resource dirtying - vmwgfx_page_dirty.c */
void vmw_bo_dirty_scan(struct vmw_buffer_object *vbo);
int vmw_bo_dirty_add(struct vmw_buffer_object *vbo);
void vmw_bo_dirty_transfer_to_res(struct vmw_resource *res);
void vmw_bo_dirty_clear_res(struct vmw_resource *res);
void vmw_bo_dirty_release(struct vmw_buffer_object *vbo);
vm_fault_t vmw_bo_vm_fault(struct vm_fault *vmf);
vm_fault_t vmw_bo_vm_mkwrite(struct vm_fault *vmf);
/** /**
* Inline helper functions * Inline helper functions
*/ */
......
...@@ -2560,7 +2560,6 @@ static int vmw_cmd_dx_check_subresource(struct vmw_private *dev_priv, ...@@ -2560,7 +2560,6 @@ static int vmw_cmd_dx_check_subresource(struct vmw_private *dev_priv,
offsetof(typeof(*cmd), sid)); offsetof(typeof(*cmd), sid));
cmd = container_of(header, typeof(*cmd), header); cmd = container_of(header, typeof(*cmd), header);
return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface,
VMW_RES_DIRTY_NONE, user_surface_converter, VMW_RES_DIRTY_NONE, user_surface_converter,
&cmd->sid, NULL); &cmd->sid, NULL);
......
This diff is collapsed.
...@@ -121,6 +121,10 @@ static void vmw_resource_release(struct kref *kref) ...@@ -121,6 +121,10 @@ static void vmw_resource_release(struct kref *kref)
} }
res->backup_dirty = false; res->backup_dirty = false;
vmw_resource_mob_detach(res); vmw_resource_mob_detach(res);
if (res->dirty)
res->func->dirty_free(res);
if (res->coherent)
vmw_bo_dirty_release(res->backup);
ttm_bo_unreserve(bo); ttm_bo_unreserve(bo);
vmw_bo_unreference(&res->backup); vmw_bo_unreference(&res->backup);
} }
...@@ -210,7 +214,9 @@ int vmw_resource_init(struct vmw_private *dev_priv, struct vmw_resource *res, ...@@ -210,7 +214,9 @@ int vmw_resource_init(struct vmw_private *dev_priv, struct vmw_resource *res,
res->backup_offset = 0; res->backup_offset = 0;
res->backup_dirty = false; res->backup_dirty = false;
res->res_dirty = false; res->res_dirty = false;
res->coherent = false;
res->used_prio = 3; res->used_prio = 3;
res->dirty = NULL;
if (delay_id) if (delay_id)
return 0; return 0;
else else
...@@ -397,6 +403,30 @@ static int vmw_resource_do_validate(struct vmw_resource *res, ...@@ -397,6 +403,30 @@ static int vmw_resource_do_validate(struct vmw_resource *res,
vmw_resource_mob_attach(res); vmw_resource_mob_attach(res);
} }
/*
* Handle the case where the backup mob is marked coherent but
* the resource isn't.
*/
if (func->dirty_alloc && vmw_resource_mob_attached(res) &&
!res->coherent) {
if (res->backup->dirty && !res->dirty) {
ret = func->dirty_alloc(res);
if (ret)
return ret;
} else if (!res->backup->dirty && res->dirty) {
func->dirty_free(res);
}
}
/*
* Transfer the dirty regions to the resource and update
* the resource.
*/
if (res->dirty) {
vmw_bo_dirty_transfer_to_res(res);
return func->dirty_sync(res);
}
return 0; return 0;
out_bind_failed: out_bind_failed:
...@@ -435,16 +465,28 @@ void vmw_resource_unreserve(struct vmw_resource *res, ...@@ -435,16 +465,28 @@ void vmw_resource_unreserve(struct vmw_resource *res,
if (switch_backup && new_backup != res->backup) { if (switch_backup && new_backup != res->backup) {
if (res->backup) { if (res->backup) {
vmw_resource_mob_detach(res); vmw_resource_mob_detach(res);
if (res->coherent)
vmw_bo_dirty_release(res->backup);
vmw_bo_unreference(&res->backup); vmw_bo_unreference(&res->backup);
} }
if (new_backup) { if (new_backup) {
res->backup = vmw_bo_reference(new_backup); res->backup = vmw_bo_reference(new_backup);
/*
* The validation code should already have added a
* dirty tracker here.
*/
WARN_ON(res->coherent && !new_backup->dirty);
vmw_resource_mob_attach(res); vmw_resource_mob_attach(res);
} else { } else {
res->backup = NULL; res->backup = NULL;
} }
} else if (switch_backup && res->coherent) {
vmw_bo_dirty_release(res->backup);
} }
if (switch_backup) if (switch_backup)
res->backup_offset = new_backup_offset; res->backup_offset = new_backup_offset;
...@@ -1010,3 +1052,18 @@ enum vmw_res_type vmw_res_type(const struct vmw_resource *res) ...@@ -1010,3 +1052,18 @@ enum vmw_res_type vmw_res_type(const struct vmw_resource *res)
{ {
return res->func->res_type; return res->func->res_type;
} }
/**
* vmw_resource_update_dirty - Update a resource's dirty tracker with a
* sequential range of touched backing store memory.
* @res: The resource.
* @start: The first page touched.
* @end: The last page touched + 1.
*/
void vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start,
pgoff_t end)
{
if (res->dirty)
res->func->dirty_range_add(res, start << PAGE_SHIFT,
end << PAGE_SHIFT);
}
...@@ -71,6 +71,12 @@ struct vmw_user_resource_conv { ...@@ -71,6 +71,12 @@ struct vmw_user_resource_conv {
* @commit_notify: If the resource is a command buffer managed resource, * @commit_notify: If the resource is a command buffer managed resource,
* callback to notify that a define or remove command * callback to notify that a define or remove command
* has been committed to the device. * has been committed to the device.
* @dirty_alloc: Allocate a dirty tracker. NULL if dirty-tracking is not
* supported.
* @dirty_free: Free the dirty tracker.
* @dirty_sync: Upload the dirty mob contents to the resource.
* @dirty_add_range: Add a sequential dirty range to the resource
* dirty tracker.
*/ */
struct vmw_res_func { struct vmw_res_func {
enum vmw_res_type res_type; enum vmw_res_type res_type;
...@@ -90,6 +96,11 @@ struct vmw_res_func { ...@@ -90,6 +96,11 @@ struct vmw_res_func {
struct ttm_validate_buffer *val_buf); struct ttm_validate_buffer *val_buf);
void (*commit_notify)(struct vmw_resource *res, void (*commit_notify)(struct vmw_resource *res,
enum vmw_cmdbuf_res_state state); enum vmw_cmdbuf_res_state state);
int (*dirty_alloc)(struct vmw_resource *res);
void (*dirty_free)(struct vmw_resource *res);
int (*dirty_sync)(struct vmw_resource *res);
void (*dirty_range_add)(struct vmw_resource *res, size_t start,
size_t end);
}; };
/** /**
......
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
* struct vmw_validation_bo_node - Buffer object validation metadata. * struct vmw_validation_bo_node - Buffer object validation metadata.
* @base: Metadata used for TTM reservation- and validation. * @base: Metadata used for TTM reservation- and validation.
* @hash: A hash entry used for the duplicate detection hash table. * @hash: A hash entry used for the duplicate detection hash table.
* @coherent_count: If switching backup buffers, number of new coherent
* resources that will have this buffer as a backup buffer.
* @as_mob: Validate as mob. * @as_mob: Validate as mob.
* @cpu_blit: Validate for cpu blit access. * @cpu_blit: Validate for cpu blit access.
* *
...@@ -42,6 +44,7 @@ ...@@ -42,6 +44,7 @@
struct vmw_validation_bo_node { struct vmw_validation_bo_node {
struct ttm_validate_buffer base; struct ttm_validate_buffer base;
struct drm_hash_item hash; struct drm_hash_item hash;
unsigned int coherent_count;
u32 as_mob : 1; u32 as_mob : 1;
u32 cpu_blit : 1; u32 cpu_blit : 1;
}; };
...@@ -459,6 +462,19 @@ int vmw_validation_res_reserve(struct vmw_validation_context *ctx, ...@@ -459,6 +462,19 @@ int vmw_validation_res_reserve(struct vmw_validation_context *ctx,
if (ret) if (ret)
goto out_unreserve; goto out_unreserve;
} }
if (val->switching_backup && val->new_backup &&
res->coherent) {
struct vmw_validation_bo_node *bo_node =
vmw_validation_find_bo_dup(ctx,
val->new_backup);
if (WARN_ON(!bo_node)) {
ret = -EINVAL;
goto out_unreserve;
}
bo_node->coherent_count++;
}
} }
return 0; return 0;
...@@ -562,6 +578,9 @@ int vmw_validation_bo_validate(struct vmw_validation_context *ctx, bool intr) ...@@ -562,6 +578,9 @@ int vmw_validation_bo_validate(struct vmw_validation_context *ctx, bool intr)
int ret; int ret;
list_for_each_entry(entry, &ctx->bo_list, base.head) { list_for_each_entry(entry, &ctx->bo_list, base.head) {
struct vmw_buffer_object *vbo =
container_of(entry->base.bo, typeof(*vbo), base);
if (entry->cpu_blit) { if (entry->cpu_blit) {
struct ttm_operation_ctx ctx = { struct ttm_operation_ctx ctx = {
.interruptible = intr, .interruptible = intr,
...@@ -576,6 +595,27 @@ int vmw_validation_bo_validate(struct vmw_validation_context *ctx, bool intr) ...@@ -576,6 +595,27 @@ int vmw_validation_bo_validate(struct vmw_validation_context *ctx, bool intr)
} }
if (ret) if (ret)
return ret; return ret;
/*
* Rather than having the resource code allocating the bo
* dirty tracker in resource_unreserve() where we can't fail,
* Do it here when validating the buffer object.
*/
if (entry->coherent_count) {
unsigned int coherent_count = entry->coherent_count;
while (coherent_count) {
ret = vmw_bo_dirty_add(vbo);
if (ret)
return ret;
coherent_count--;
}
entry->coherent_count -= coherent_count;
}
if (vbo->dirty)
vmw_bo_dirty_scan(vbo);
} }
return 0; return 0;
} }
...@@ -828,3 +868,34 @@ int vmw_validation_preload_res(struct vmw_validation_context *ctx, ...@@ -828,3 +868,34 @@ int vmw_validation_preload_res(struct vmw_validation_context *ctx,
ctx->mem_size_left += size; ctx->mem_size_left += size;
return 0; return 0;
} }
/**
* vmw_validation_bo_backoff - Unreserve buffer objects registered with a
* validation context
* @ctx: The validation context
*
* This function unreserves the buffer objects previously reserved using
* vmw_validation_bo_reserve. It's typically used as part of an error path
*/
void vmw_validation_bo_backoff(struct vmw_validation_context *ctx)
{
struct vmw_validation_bo_node *entry;
/*
* Switching coherent resource backup buffers failed.
* Release corresponding buffer object dirty trackers.
*/
list_for_each_entry(entry, &ctx->bo_list, base.head) {
if (entry->coherent_count) {
unsigned int coherent_count = entry->coherent_count;
struct vmw_buffer_object *vbo =
container_of(entry->base.bo, typeof(*vbo),
base);
while (coherent_count--)
vmw_bo_dirty_release(vbo);
}
}
ttm_eu_backoff_reservation(&ctx->ticket, &ctx->bo_list);
}
...@@ -172,20 +172,6 @@ vmw_validation_bo_reserve(struct vmw_validation_context *ctx, ...@@ -172,20 +172,6 @@ vmw_validation_bo_reserve(struct vmw_validation_context *ctx,
NULL, true); NULL, true);
} }
/**
* vmw_validation_bo_backoff - Unreserve buffer objects registered with a
* validation context
* @ctx: The validation context
*
* This function unreserves the buffer objects previously reserved using
* vmw_validation_bo_reserve. It's typically used as part of an error path
*/
static inline void
vmw_validation_bo_backoff(struct vmw_validation_context *ctx)
{
ttm_eu_backoff_reservation(&ctx->ticket, &ctx->bo_list);
}
/** /**
* vmw_validation_bo_fence - Unreserve and fence buffer objects registered * vmw_validation_bo_fence - Unreserve and fence buffer objects registered
* with a validation context * with a validation context
...@@ -268,4 +254,6 @@ int vmw_validation_preload_res(struct vmw_validation_context *ctx, ...@@ -268,4 +254,6 @@ int vmw_validation_preload_res(struct vmw_validation_context *ctx,
unsigned int size); unsigned int size);
void vmw_validation_res_set_dirty(struct vmw_validation_context *ctx, void vmw_validation_res_set_dirty(struct vmw_validation_context *ctx,
void *val_private, u32 dirty); void *val_private, u32 dirty);
void vmw_validation_bo_backoff(struct vmw_validation_context *ctx);
#endif #endif
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