You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'for-airlied-next' of git://people.freedesktop.org/~mlankhorst/linux into drm-next
Merge the move to generic fences for TTM using drivers. * 'for-airlied-next' of git://people.freedesktop.org/~mlankhorst/linux: drm/nouveau: use shared fences for readable objects drm/nouveau: Keep only a single list for validation. drm/ttm: use rcu in core ttm drm/vmwgfx: use rcu in vmw_user_dmabuf_synccpu_grab drm/radeon: use rcu waits in some ioctls drm/nouveau: use rcu in nouveau_gem_ioctl_cpu_prep drm/ttm: flip the switch, and convert to dma_fence drm/qxl: rework to new fence interface drm/nouveau: rework to new fence interface drm/vmwgfx: rework to new fence interface, v2 drm/vmwgfx: get rid of different types of fence_flags entirely drm/radeon: use common fence implementation for fences, v4 drm/ttm: kill off some members to ttm_validate_buffer drm/ttm: add interruptible parameter to ttm_eu_reserve_buffers drm/ttm: kill fence_lock drm/ttm: call ttm_bo_wait while inside a reservation drm/nouveau: require reservations for nouveau_fence_sync and nouveau_bo_fence drm/nouveau: add reservation to nouveau_gem_ioctl_cpu_prep
This commit is contained in:
@@ -88,13 +88,13 @@ nv10_bo_get_tile_region(struct drm_device *dev, int i)
|
||||
|
||||
static void
|
||||
nv10_bo_put_tile_region(struct drm_device *dev, struct nouveau_drm_tile *tile,
|
||||
struct nouveau_fence *fence)
|
||||
struct fence *fence)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
|
||||
if (tile) {
|
||||
spin_lock(&drm->tile.lock);
|
||||
tile->fence = nouveau_fence_ref(fence);
|
||||
tile->fence = (struct nouveau_fence *)fence_get(fence);
|
||||
tile->used = false;
|
||||
spin_unlock(&drm->tile.lock);
|
||||
}
|
||||
@@ -970,13 +970,14 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
|
||||
}
|
||||
|
||||
mutex_lock_nested(&cli->mutex, SINGLE_DEPTH_NESTING);
|
||||
ret = nouveau_fence_sync(bo->sync_obj, chan);
|
||||
ret = nouveau_fence_sync(nouveau_bo(bo), chan, true);
|
||||
if (ret == 0) {
|
||||
ret = drm->ttm.move(chan, bo, &bo->mem, new_mem);
|
||||
if (ret == 0) {
|
||||
ret = nouveau_fence_new(chan, false, &fence);
|
||||
if (ret == 0) {
|
||||
ret = ttm_bo_move_accel_cleanup(bo, fence,
|
||||
ret = ttm_bo_move_accel_cleanup(bo,
|
||||
&fence->base,
|
||||
evict,
|
||||
no_wait_gpu,
|
||||
new_mem);
|
||||
@@ -1167,8 +1168,9 @@ nouveau_bo_vm_cleanup(struct ttm_buffer_object *bo,
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_bdev(bo->bdev);
|
||||
struct drm_device *dev = drm->dev;
|
||||
struct fence *fence = reservation_object_get_excl(bo->resv);
|
||||
|
||||
nv10_bo_put_tile_region(dev, *old_tile, bo->sync_obj);
|
||||
nv10_bo_put_tile_region(dev, *old_tile, fence);
|
||||
*old_tile = new_tile;
|
||||
}
|
||||
|
||||
@@ -1212,9 +1214,7 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr,
|
||||
}
|
||||
|
||||
/* Fallback to software copy. */
|
||||
spin_lock(&bo->bdev->fence_lock);
|
||||
ret = ttm_bo_wait(bo, true, intr, no_wait_gpu);
|
||||
spin_unlock(&bo->bdev->fence_lock);
|
||||
if (ret == 0)
|
||||
ret = ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem);
|
||||
|
||||
@@ -1458,47 +1458,14 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm)
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence)
|
||||
nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence, bool exclusive)
|
||||
{
|
||||
struct nouveau_fence *new_fence = nouveau_fence_ref(fence);
|
||||
struct nouveau_fence *old_fence = NULL;
|
||||
struct reservation_object *resv = nvbo->bo.resv;
|
||||
|
||||
spin_lock(&nvbo->bo.bdev->fence_lock);
|
||||
old_fence = nvbo->bo.sync_obj;
|
||||
nvbo->bo.sync_obj = new_fence;
|
||||
spin_unlock(&nvbo->bo.bdev->fence_lock);
|
||||
|
||||
nouveau_fence_unref(&old_fence);
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_bo_fence_unref(void **sync_obj)
|
||||
{
|
||||
nouveau_fence_unref((struct nouveau_fence **)sync_obj);
|
||||
}
|
||||
|
||||
static void *
|
||||
nouveau_bo_fence_ref(void *sync_obj)
|
||||
{
|
||||
return nouveau_fence_ref(sync_obj);
|
||||
}
|
||||
|
||||
static bool
|
||||
nouveau_bo_fence_signalled(void *sync_obj)
|
||||
{
|
||||
return nouveau_fence_done(sync_obj);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_bo_fence_wait(void *sync_obj, bool lazy, bool intr)
|
||||
{
|
||||
return nouveau_fence_wait(sync_obj, lazy, intr);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_bo_fence_flush(void *sync_obj)
|
||||
{
|
||||
return 0;
|
||||
if (exclusive)
|
||||
reservation_object_add_excl_fence(resv, &fence->base);
|
||||
else if (fence)
|
||||
reservation_object_add_shared_fence(resv, &fence->base);
|
||||
}
|
||||
|
||||
struct ttm_bo_driver nouveau_bo_driver = {
|
||||
@@ -1511,11 +1478,6 @@ struct ttm_bo_driver nouveau_bo_driver = {
|
||||
.move_notify = nouveau_bo_move_ntfy,
|
||||
.move = nouveau_bo_move,
|
||||
.verify_access = nouveau_bo_verify_access,
|
||||
.sync_obj_signaled = nouveau_bo_fence_signalled,
|
||||
.sync_obj_wait = nouveau_bo_fence_wait,
|
||||
.sync_obj_flush = nouveau_bo_fence_flush,
|
||||
.sync_obj_unref = nouveau_bo_fence_unref,
|
||||
.sync_obj_ref = nouveau_bo_fence_ref,
|
||||
.fault_reserve_notify = &nouveau_ttm_fault_reserve_notify,
|
||||
.io_mem_reserve = &nouveau_ttm_io_mem_reserve,
|
||||
.io_mem_free = &nouveau_ttm_io_mem_free,
|
||||
|
||||
@@ -78,7 +78,7 @@ u16 nouveau_bo_rd16(struct nouveau_bo *, unsigned index);
|
||||
void nouveau_bo_wr16(struct nouveau_bo *, unsigned index, u16 val);
|
||||
u32 nouveau_bo_rd32(struct nouveau_bo *, unsigned index);
|
||||
void nouveau_bo_wr32(struct nouveau_bo *, unsigned index, u32 val);
|
||||
void nouveau_bo_fence(struct nouveau_bo *, struct nouveau_fence *);
|
||||
void nouveau_bo_fence(struct nouveau_bo *, struct nouveau_fence *, bool exclusive);
|
||||
int nouveau_bo_validate(struct nouveau_bo *, bool interruptible,
|
||||
bool no_wait_gpu);
|
||||
|
||||
|
||||
@@ -658,7 +658,7 @@ nouveau_page_flip_emit(struct nouveau_channel *chan,
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
|
||||
/* Synchronize with the old framebuffer */
|
||||
ret = nouveau_fence_sync(old_bo->bo.sync_obj, chan);
|
||||
ret = nouveau_fence_sync(old_bo, chan, false);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
@@ -717,19 +717,24 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
}
|
||||
|
||||
mutex_lock(&cli->mutex);
|
||||
ret = ttm_bo_reserve(&new_bo->bo, true, false, false, NULL);
|
||||
if (ret)
|
||||
goto fail_unpin;
|
||||
|
||||
/* synchronise rendering channel with the kernel's channel */
|
||||
spin_lock(&new_bo->bo.bdev->fence_lock);
|
||||
fence = nouveau_fence_ref(new_bo->bo.sync_obj);
|
||||
spin_unlock(&new_bo->bo.bdev->fence_lock);
|
||||
ret = nouveau_fence_sync(fence, chan);
|
||||
nouveau_fence_unref(&fence);
|
||||
if (ret)
|
||||
ret = nouveau_fence_sync(new_bo, chan, false);
|
||||
if (ret) {
|
||||
ttm_bo_unreserve(&new_bo->bo);
|
||||
goto fail_unpin;
|
||||
}
|
||||
|
||||
ret = ttm_bo_reserve(&old_bo->bo, true, false, false, NULL);
|
||||
if (ret)
|
||||
goto fail_unpin;
|
||||
if (new_bo != old_bo) {
|
||||
ttm_bo_unreserve(&new_bo->bo);
|
||||
|
||||
ret = ttm_bo_reserve(&old_bo->bo, true, false, false, NULL);
|
||||
if (ret)
|
||||
goto fail_unpin;
|
||||
}
|
||||
|
||||
/* Initialize a page flip struct */
|
||||
*s = (struct nouveau_page_flip_state)
|
||||
@@ -775,7 +780,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
/* Update the crtc struct and cleanup */
|
||||
crtc->primary->fb = fb;
|
||||
|
||||
nouveau_bo_fence(old_bo, fence);
|
||||
nouveau_bo_fence(old_bo, fence, false);
|
||||
ttm_bo_unreserve(&old_bo->bo);
|
||||
if (old_bo != new_bo)
|
||||
nouveau_bo_unpin(old_bo);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,33 +1,35 @@
|
||||
#ifndef __NOUVEAU_FENCE_H__
|
||||
#define __NOUVEAU_FENCE_H__
|
||||
|
||||
#include <linux/fence.h>
|
||||
#include <nvif/notify.h>
|
||||
|
||||
struct nouveau_drm;
|
||||
struct nouveau_bo;
|
||||
|
||||
struct nouveau_fence {
|
||||
struct fence base;
|
||||
|
||||
struct list_head head;
|
||||
struct list_head work;
|
||||
struct kref kref;
|
||||
|
||||
bool sysmem;
|
||||
|
||||
struct nouveau_channel *channel;
|
||||
unsigned long timeout;
|
||||
u32 sequence;
|
||||
};
|
||||
|
||||
int nouveau_fence_new(struct nouveau_channel *, bool sysmem,
|
||||
struct nouveau_fence **);
|
||||
struct nouveau_fence *
|
||||
nouveau_fence_ref(struct nouveau_fence *);
|
||||
void nouveau_fence_unref(struct nouveau_fence **);
|
||||
|
||||
int nouveau_fence_emit(struct nouveau_fence *, struct nouveau_channel *);
|
||||
bool nouveau_fence_done(struct nouveau_fence *);
|
||||
void nouveau_fence_work(struct nouveau_fence *, void (*)(void *), void *);
|
||||
void nouveau_fence_work(struct fence *, void (*)(void *), void *);
|
||||
int nouveau_fence_wait(struct nouveau_fence *, bool lazy, bool intr);
|
||||
int nouveau_fence_sync(struct nouveau_fence *, struct nouveau_channel *);
|
||||
int nouveau_fence_sync(struct nouveau_bo *, struct nouveau_channel *, bool exclusive);
|
||||
|
||||
struct nouveau_fence_chan {
|
||||
spinlock_t lock;
|
||||
struct list_head pending;
|
||||
struct list_head flip;
|
||||
|
||||
@@ -38,8 +40,12 @@ struct nouveau_fence_chan {
|
||||
int (*emit32)(struct nouveau_channel *, u64, u32);
|
||||
int (*sync32)(struct nouveau_channel *, u64, u32);
|
||||
|
||||
spinlock_t lock;
|
||||
u32 sequence;
|
||||
u32 context;
|
||||
char name[24];
|
||||
|
||||
struct nvif_notify notify;
|
||||
int notify_ref;
|
||||
};
|
||||
|
||||
struct nouveau_fence_priv {
|
||||
@@ -49,13 +55,13 @@ struct nouveau_fence_priv {
|
||||
int (*context_new)(struct nouveau_channel *);
|
||||
void (*context_del)(struct nouveau_channel *);
|
||||
|
||||
wait_queue_head_t waiting;
|
||||
u32 contexts, context_base;
|
||||
bool uevent;
|
||||
};
|
||||
|
||||
#define nouveau_fence(drm) ((struct nouveau_fence_priv *)(drm)->fence)
|
||||
|
||||
void nouveau_fence_context_new(struct nouveau_fence_chan *);
|
||||
void nouveau_fence_context_new(struct nouveau_channel *, struct nouveau_fence_chan *);
|
||||
void nouveau_fence_context_del(struct nouveau_fence_chan *);
|
||||
|
||||
int nv04_fence_create(struct nouveau_drm *);
|
||||
|
||||
@@ -98,17 +98,23 @@ static void
|
||||
nouveau_gem_object_unmap(struct nouveau_bo *nvbo, struct nouveau_vma *vma)
|
||||
{
|
||||
const bool mapped = nvbo->bo.mem.mem_type != TTM_PL_SYSTEM;
|
||||
struct nouveau_fence *fence = NULL;
|
||||
struct reservation_object *resv = nvbo->bo.resv;
|
||||
struct reservation_object_list *fobj;
|
||||
struct fence *fence = NULL;
|
||||
|
||||
fobj = reservation_object_get_list(resv);
|
||||
|
||||
list_del(&vma->head);
|
||||
|
||||
if (mapped) {
|
||||
spin_lock(&nvbo->bo.bdev->fence_lock);
|
||||
fence = nouveau_fence_ref(nvbo->bo.sync_obj);
|
||||
spin_unlock(&nvbo->bo.bdev->fence_lock);
|
||||
}
|
||||
if (fobj && fobj->shared_count > 1)
|
||||
ttm_bo_wait(&nvbo->bo, true, false, false);
|
||||
else if (fobj && fobj->shared_count == 1)
|
||||
fence = rcu_dereference_protected(fobj->shared[0],
|
||||
reservation_object_held(resv));
|
||||
else
|
||||
fence = reservation_object_get_excl(nvbo->bo.resv);
|
||||
|
||||
if (fence) {
|
||||
if (fence && mapped) {
|
||||
nouveau_fence_work(fence, nouveau_gem_object_delete, vma);
|
||||
} else {
|
||||
if (mapped)
|
||||
@@ -116,7 +122,6 @@ nouveau_gem_object_unmap(struct nouveau_bo *nvbo, struct nouveau_vma *vma)
|
||||
nouveau_vm_put(vma);
|
||||
kfree(vma);
|
||||
}
|
||||
nouveau_fence_unref(&fence);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -288,24 +293,23 @@ nouveau_gem_set_domain(struct drm_gem_object *gem, uint32_t read_domains,
|
||||
}
|
||||
|
||||
struct validate_op {
|
||||
struct list_head vram_list;
|
||||
struct list_head gart_list;
|
||||
struct list_head both_list;
|
||||
struct list_head list;
|
||||
struct ww_acquire_ctx ticket;
|
||||
};
|
||||
|
||||
static void
|
||||
validate_fini_list(struct list_head *list, struct nouveau_fence *fence,
|
||||
struct ww_acquire_ctx *ticket)
|
||||
validate_fini_no_ticket(struct validate_op *op, struct nouveau_fence *fence,
|
||||
struct drm_nouveau_gem_pushbuf_bo *pbbo)
|
||||
{
|
||||
struct list_head *entry, *tmp;
|
||||
struct nouveau_bo *nvbo;
|
||||
struct drm_nouveau_gem_pushbuf_bo *b;
|
||||
|
||||
list_for_each_safe(entry, tmp, list) {
|
||||
nvbo = list_entry(entry, struct nouveau_bo, entry);
|
||||
while (!list_empty(&op->list)) {
|
||||
nvbo = list_entry(op->list.next, struct nouveau_bo, entry);
|
||||
b = &pbbo[nvbo->pbbo_index];
|
||||
|
||||
if (likely(fence))
|
||||
nouveau_bo_fence(nvbo, fence);
|
||||
nouveau_bo_fence(nvbo, fence, !!b->write_domains);
|
||||
|
||||
if (unlikely(nvbo->validate_mapped)) {
|
||||
ttm_bo_kunmap(&nvbo->kmap);
|
||||
@@ -314,23 +318,16 @@ validate_fini_list(struct list_head *list, struct nouveau_fence *fence,
|
||||
|
||||
list_del(&nvbo->entry);
|
||||
nvbo->reserved_by = NULL;
|
||||
ttm_bo_unreserve_ticket(&nvbo->bo, ticket);
|
||||
ttm_bo_unreserve_ticket(&nvbo->bo, &op->ticket);
|
||||
drm_gem_object_unreference_unlocked(&nvbo->gem);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
validate_fini_no_ticket(struct validate_op *op, struct nouveau_fence *fence)
|
||||
validate_fini(struct validate_op *op, struct nouveau_fence *fence,
|
||||
struct drm_nouveau_gem_pushbuf_bo *pbbo)
|
||||
{
|
||||
validate_fini_list(&op->vram_list, fence, &op->ticket);
|
||||
validate_fini_list(&op->gart_list, fence, &op->ticket);
|
||||
validate_fini_list(&op->both_list, fence, &op->ticket);
|
||||
}
|
||||
|
||||
static void
|
||||
validate_fini(struct validate_op *op, struct nouveau_fence *fence)
|
||||
{
|
||||
validate_fini_no_ticket(op, fence);
|
||||
validate_fini_no_ticket(op, fence, pbbo);
|
||||
ww_acquire_fini(&op->ticket);
|
||||
}
|
||||
|
||||
@@ -344,6 +341,9 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv,
|
||||
int trycnt = 0;
|
||||
int ret, i;
|
||||
struct nouveau_bo *res_bo = NULL;
|
||||
LIST_HEAD(gart_list);
|
||||
LIST_HEAD(vram_list);
|
||||
LIST_HEAD(both_list);
|
||||
|
||||
ww_acquire_init(&op->ticket, &reservation_ww_class);
|
||||
retry:
|
||||
@@ -360,9 +360,8 @@ retry:
|
||||
gem = drm_gem_object_lookup(dev, file_priv, b->handle);
|
||||
if (!gem) {
|
||||
NV_PRINTK(error, cli, "Unknown handle 0x%08x\n", b->handle);
|
||||
ww_acquire_done(&op->ticket);
|
||||
validate_fini(op, NULL);
|
||||
return -ENOENT;
|
||||
ret = -ENOENT;
|
||||
break;
|
||||
}
|
||||
nvbo = nouveau_gem_object(gem);
|
||||
if (nvbo == res_bo) {
|
||||
@@ -375,14 +374,16 @@ retry:
|
||||
NV_PRINTK(error, cli, "multiple instances of buffer %d on "
|
||||
"validation list\n", b->handle);
|
||||
drm_gem_object_unreference_unlocked(gem);
|
||||
ww_acquire_done(&op->ticket);
|
||||
validate_fini(op, NULL);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = ttm_bo_reserve(&nvbo->bo, true, false, true, &op->ticket);
|
||||
if (ret) {
|
||||
validate_fini_no_ticket(op, NULL);
|
||||
list_splice_tail_init(&vram_list, &op->list);
|
||||
list_splice_tail_init(&gart_list, &op->list);
|
||||
list_splice_tail_init(&both_list, &op->list);
|
||||
validate_fini_no_ticket(op, NULL, NULL);
|
||||
if (unlikely(ret == -EDEADLK)) {
|
||||
ret = ttm_bo_reserve_slowpath(&nvbo->bo, true,
|
||||
&op->ticket);
|
||||
@@ -390,12 +391,9 @@ retry:
|
||||
res_bo = nvbo;
|
||||
}
|
||||
if (unlikely(ret)) {
|
||||
ww_acquire_done(&op->ticket);
|
||||
ww_acquire_fini(&op->ticket);
|
||||
drm_gem_object_unreference_unlocked(gem);
|
||||
if (ret != -ERESTARTSYS)
|
||||
NV_PRINTK(error, cli, "fail reserve\n");
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,45 +402,32 @@ retry:
|
||||
nvbo->pbbo_index = i;
|
||||
if ((b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
|
||||
(b->valid_domains & NOUVEAU_GEM_DOMAIN_GART))
|
||||
list_add_tail(&nvbo->entry, &op->both_list);
|
||||
list_add_tail(&nvbo->entry, &both_list);
|
||||
else
|
||||
if (b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM)
|
||||
list_add_tail(&nvbo->entry, &op->vram_list);
|
||||
list_add_tail(&nvbo->entry, &vram_list);
|
||||
else
|
||||
if (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART)
|
||||
list_add_tail(&nvbo->entry, &op->gart_list);
|
||||
list_add_tail(&nvbo->entry, &gart_list);
|
||||
else {
|
||||
NV_PRINTK(error, cli, "invalid valid domains: 0x%08x\n",
|
||||
b->valid_domains);
|
||||
list_add_tail(&nvbo->entry, &op->both_list);
|
||||
ww_acquire_done(&op->ticket);
|
||||
validate_fini(op, NULL);
|
||||
return -EINVAL;
|
||||
list_add_tail(&nvbo->entry, &both_list);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (nvbo == res_bo)
|
||||
goto retry;
|
||||
}
|
||||
|
||||
ww_acquire_done(&op->ticket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
validate_sync(struct nouveau_channel *chan, struct nouveau_bo *nvbo)
|
||||
{
|
||||
struct nouveau_fence *fence = NULL;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock(&nvbo->bo.bdev->fence_lock);
|
||||
fence = nouveau_fence_ref(nvbo->bo.sync_obj);
|
||||
spin_unlock(&nvbo->bo.bdev->fence_lock);
|
||||
|
||||
if (fence) {
|
||||
ret = nouveau_fence_sync(fence, chan);
|
||||
nouveau_fence_unref(&fence);
|
||||
}
|
||||
|
||||
list_splice_tail(&vram_list, &op->list);
|
||||
list_splice_tail(&gart_list, &op->list);
|
||||
list_splice_tail(&both_list, &op->list);
|
||||
if (ret)
|
||||
validate_fini(op, NULL, NULL);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -474,9 +459,10 @@ validate_list(struct nouveau_channel *chan, struct nouveau_cli *cli,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = validate_sync(chan, nvbo);
|
||||
ret = nouveau_fence_sync(nvbo, chan, !!b->write_domains);
|
||||
if (unlikely(ret)) {
|
||||
NV_PRINTK(error, cli, "fail post-validate sync\n");
|
||||
if (ret != -ERESTARTSYS)
|
||||
NV_PRINTK(error, cli, "fail post-validate sync\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -513,11 +499,9 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
|
||||
struct validate_op *op, int *apply_relocs)
|
||||
{
|
||||
struct nouveau_cli *cli = nouveau_cli(file_priv);
|
||||
int ret, relocs = 0;
|
||||
int ret;
|
||||
|
||||
INIT_LIST_HEAD(&op->vram_list);
|
||||
INIT_LIST_HEAD(&op->gart_list);
|
||||
INIT_LIST_HEAD(&op->both_list);
|
||||
INIT_LIST_HEAD(&op->list);
|
||||
|
||||
if (nr_buffers == 0)
|
||||
return 0;
|
||||
@@ -529,34 +513,14 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = validate_list(chan, cli, &op->vram_list, pbbo, user_buffers);
|
||||
ret = validate_list(chan, cli, &op->list, pbbo, user_buffers);
|
||||
if (unlikely(ret < 0)) {
|
||||
if (ret != -ERESTARTSYS)
|
||||
NV_PRINTK(error, cli, "validate vram_list\n");
|
||||
validate_fini(op, NULL);
|
||||
NV_PRINTK(error, cli, "validating bo list\n");
|
||||
validate_fini(op, NULL, NULL);
|
||||
return ret;
|
||||
}
|
||||
relocs += ret;
|
||||
|
||||
ret = validate_list(chan, cli, &op->gart_list, pbbo, user_buffers);
|
||||
if (unlikely(ret < 0)) {
|
||||
if (ret != -ERESTARTSYS)
|
||||
NV_PRINTK(error, cli, "validate gart_list\n");
|
||||
validate_fini(op, NULL);
|
||||
return ret;
|
||||
}
|
||||
relocs += ret;
|
||||
|
||||
ret = validate_list(chan, cli, &op->both_list, pbbo, user_buffers);
|
||||
if (unlikely(ret < 0)) {
|
||||
if (ret != -ERESTARTSYS)
|
||||
NV_PRINTK(error, cli, "validate both_list\n");
|
||||
validate_fini(op, NULL);
|
||||
return ret;
|
||||
}
|
||||
relocs += ret;
|
||||
|
||||
*apply_relocs = relocs;
|
||||
*apply_relocs = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -659,9 +623,7 @@ nouveau_gem_pushbuf_reloc_apply(struct nouveau_cli *cli,
|
||||
data |= r->vor;
|
||||
}
|
||||
|
||||
spin_lock(&nvbo->bo.bdev->fence_lock);
|
||||
ret = ttm_bo_wait(&nvbo->bo, false, false, false);
|
||||
spin_unlock(&nvbo->bo.bdev->fence_lock);
|
||||
ret = ttm_bo_wait(&nvbo->bo, true, false, false);
|
||||
if (ret) {
|
||||
NV_PRINTK(error, cli, "reloc wait_idle failed: %d\n", ret);
|
||||
break;
|
||||
@@ -839,7 +801,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
|
||||
}
|
||||
|
||||
out:
|
||||
validate_fini(&op, fence);
|
||||
validate_fini(&op, fence, bo);
|
||||
nouveau_fence_unref(&fence);
|
||||
|
||||
out_prevalid:
|
||||
@@ -884,17 +846,29 @@ nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,
|
||||
struct drm_gem_object *gem;
|
||||
struct nouveau_bo *nvbo;
|
||||
bool no_wait = !!(req->flags & NOUVEAU_GEM_CPU_PREP_NOWAIT);
|
||||
int ret = -EINVAL;
|
||||
bool write = !!(req->flags & NOUVEAU_GEM_CPU_PREP_WRITE);
|
||||
int ret;
|
||||
|
||||
gem = drm_gem_object_lookup(dev, file_priv, req->handle);
|
||||
if (!gem)
|
||||
return -ENOENT;
|
||||
nvbo = nouveau_gem_object(gem);
|
||||
|
||||
spin_lock(&nvbo->bo.bdev->fence_lock);
|
||||
ret = ttm_bo_wait(&nvbo->bo, true, true, no_wait);
|
||||
spin_unlock(&nvbo->bo.bdev->fence_lock);
|
||||
if (no_wait)
|
||||
ret = reservation_object_test_signaled_rcu(nvbo->bo.resv, write) ? 0 : -EBUSY;
|
||||
else {
|
||||
long lret;
|
||||
|
||||
lret = reservation_object_wait_timeout_rcu(nvbo->bo.resv, write, true, 30 * HZ);
|
||||
if (!lret)
|
||||
ret = -EBUSY;
|
||||
else if (lret > 0)
|
||||
ret = 0;
|
||||
else
|
||||
ret = lret;
|
||||
}
|
||||
drm_gem_object_unreference_unlocked(gem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ nv04_fence_emit(struct nouveau_fence *fence)
|
||||
int ret = RING_SPACE(chan, 2);
|
||||
if (ret == 0) {
|
||||
BEGIN_NV04(chan, NvSubSw, 0x0150, 1);
|
||||
OUT_RING (chan, fence->sequence);
|
||||
OUT_RING (chan, fence->base.seqno);
|
||||
FIRE_RING (chan);
|
||||
}
|
||||
return ret;
|
||||
@@ -75,7 +75,7 @@ nv04_fence_context_new(struct nouveau_channel *chan)
|
||||
{
|
||||
struct nv04_fence_chan *fctx = kzalloc(sizeof(*fctx), GFP_KERNEL);
|
||||
if (fctx) {
|
||||
nouveau_fence_context_new(&fctx->base);
|
||||
nouveau_fence_context_new(chan, &fctx->base);
|
||||
fctx->base.emit = nv04_fence_emit;
|
||||
fctx->base.sync = nv04_fence_sync;
|
||||
fctx->base.read = nv04_fence_read;
|
||||
@@ -105,5 +105,7 @@ nv04_fence_create(struct nouveau_drm *drm)
|
||||
priv->base.dtor = nv04_fence_destroy;
|
||||
priv->base.context_new = nv04_fence_context_new;
|
||||
priv->base.context_del = nv04_fence_context_del;
|
||||
priv->base.contexts = 15;
|
||||
priv->base.context_base = fence_context_alloc(priv->base.contexts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ nv10_fence_emit(struct nouveau_fence *fence)
|
||||
int ret = RING_SPACE(chan, 2);
|
||||
if (ret == 0) {
|
||||
BEGIN_NV04(chan, 0, NV10_SUBCHAN_REF_CNT, 1);
|
||||
OUT_RING (chan, fence->sequence);
|
||||
OUT_RING (chan, fence->base.seqno);
|
||||
FIRE_RING (chan);
|
||||
}
|
||||
return ret;
|
||||
@@ -75,7 +75,7 @@ nv10_fence_context_new(struct nouveau_channel *chan)
|
||||
if (!fctx)
|
||||
return -ENOMEM;
|
||||
|
||||
nouveau_fence_context_new(&fctx->base);
|
||||
nouveau_fence_context_new(chan, &fctx->base);
|
||||
fctx->base.emit = nv10_fence_emit;
|
||||
fctx->base.read = nv10_fence_read;
|
||||
fctx->base.sync = nv10_fence_sync;
|
||||
@@ -106,6 +106,8 @@ nv10_fence_create(struct nouveau_drm *drm)
|
||||
priv->base.dtor = nv10_fence_destroy;
|
||||
priv->base.context_new = nv10_fence_context_new;
|
||||
priv->base.context_del = nv10_fence_context_del;
|
||||
priv->base.contexts = 31;
|
||||
priv->base.context_base = fence_context_alloc(priv->base.contexts);
|
||||
spin_lock_init(&priv->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ nv17_fence_context_new(struct nouveau_channel *chan)
|
||||
if (!fctx)
|
||||
return -ENOMEM;
|
||||
|
||||
nouveau_fence_context_new(&fctx->base);
|
||||
nouveau_fence_context_new(chan, &fctx->base);
|
||||
fctx->base.emit = nv10_fence_emit;
|
||||
fctx->base.read = nv10_fence_read;
|
||||
fctx->base.sync = nv17_fence_sync;
|
||||
@@ -124,6 +124,8 @@ nv17_fence_create(struct nouveau_drm *drm)
|
||||
priv->base.resume = nv17_fence_resume;
|
||||
priv->base.context_new = nv17_fence_context_new;
|
||||
priv->base.context_del = nv10_fence_context_del;
|
||||
priv->base.contexts = 31;
|
||||
priv->base.context_base = fence_context_alloc(priv->base.contexts);
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM,
|
||||
|
||||
@@ -46,7 +46,7 @@ nv50_fence_context_new(struct nouveau_channel *chan)
|
||||
if (!fctx)
|
||||
return -ENOMEM;
|
||||
|
||||
nouveau_fence_context_new(&fctx->base);
|
||||
nouveau_fence_context_new(chan, &fctx->base);
|
||||
fctx->base.emit = nv10_fence_emit;
|
||||
fctx->base.read = nv10_fence_read;
|
||||
fctx->base.sync = nv17_fence_sync;
|
||||
@@ -95,6 +95,8 @@ nv50_fence_create(struct nouveau_drm *drm)
|
||||
priv->base.resume = nv17_fence_resume;
|
||||
priv->base.context_new = nv50_fence_context_new;
|
||||
priv->base.context_del = nv10_fence_context_del;
|
||||
priv->base.contexts = 127;
|
||||
priv->base.context_base = fence_context_alloc(priv->base.contexts);
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM,
|
||||
|
||||
@@ -82,7 +82,7 @@ nv84_fence_emit(struct nouveau_fence *fence)
|
||||
else
|
||||
addr += fctx->vma.offset;
|
||||
|
||||
return fctx->base.emit32(chan, addr, fence->sequence);
|
||||
return fctx->base.emit32(chan, addr, fence->base.seqno);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -97,7 +97,7 @@ nv84_fence_sync(struct nouveau_fence *fence,
|
||||
else
|
||||
addr += fctx->vma.offset;
|
||||
|
||||
return fctx->base.sync32(chan, addr, fence->sequence);
|
||||
return fctx->base.sync32(chan, addr, fence->base.seqno);
|
||||
}
|
||||
|
||||
static u32
|
||||
@@ -139,12 +139,13 @@ nv84_fence_context_new(struct nouveau_channel *chan)
|
||||
if (!fctx)
|
||||
return -ENOMEM;
|
||||
|
||||
nouveau_fence_context_new(&fctx->base);
|
||||
nouveau_fence_context_new(chan, &fctx->base);
|
||||
fctx->base.emit = nv84_fence_emit;
|
||||
fctx->base.sync = nv84_fence_sync;
|
||||
fctx->base.read = nv84_fence_read;
|
||||
fctx->base.emit32 = nv84_fence_emit32;
|
||||
fctx->base.sync32 = nv84_fence_sync32;
|
||||
fctx->base.sequence = nv84_fence_read(chan);
|
||||
|
||||
ret = nouveau_bo_vma_add(priv->bo, cli->vm, &fctx->vma);
|
||||
if (ret == 0) {
|
||||
@@ -168,13 +169,12 @@ nv84_fence_context_new(struct nouveau_channel *chan)
|
||||
static bool
|
||||
nv84_fence_suspend(struct nouveau_drm *drm)
|
||||
{
|
||||
struct nouveau_fifo *pfifo = nvkm_fifo(&drm->device);
|
||||
struct nv84_fence_priv *priv = drm->fence;
|
||||
int i;
|
||||
|
||||
priv->suspend = vmalloc((pfifo->max + 1) * sizeof(u32));
|
||||
priv->suspend = vmalloc(priv->base.contexts * sizeof(u32));
|
||||
if (priv->suspend) {
|
||||
for (i = 0; i <= pfifo->max; i++)
|
||||
for (i = 0; i < priv->base.contexts; i++)
|
||||
priv->suspend[i] = nouveau_bo_rd32(priv->bo, i*4);
|
||||
}
|
||||
|
||||
@@ -184,12 +184,11 @@ nv84_fence_suspend(struct nouveau_drm *drm)
|
||||
static void
|
||||
nv84_fence_resume(struct nouveau_drm *drm)
|
||||
{
|
||||
struct nouveau_fifo *pfifo = nvkm_fifo(&drm->device);
|
||||
struct nv84_fence_priv *priv = drm->fence;
|
||||
int i;
|
||||
|
||||
if (priv->suspend) {
|
||||
for (i = 0; i <= pfifo->max; i++)
|
||||
for (i = 0; i < priv->base.contexts; i++)
|
||||
nouveau_bo_wr32(priv->bo, i*4, priv->suspend[i]);
|
||||
vfree(priv->suspend);
|
||||
priv->suspend = NULL;
|
||||
@@ -229,10 +228,11 @@ nv84_fence_create(struct nouveau_drm *drm)
|
||||
priv->base.context_new = nv84_fence_context_new;
|
||||
priv->base.context_del = nv84_fence_context_del;
|
||||
|
||||
init_waitqueue_head(&priv->base.waiting);
|
||||
priv->base.contexts = pfifo->max + 1;
|
||||
priv->base.context_base = fence_context_alloc(priv->base.contexts);
|
||||
priv->base.uevent = true;
|
||||
|
||||
ret = nouveau_bo_new(drm->dev, 16 * (pfifo->max + 1), 0,
|
||||
ret = nouveau_bo_new(drm->dev, 16 * priv->base.contexts, 0,
|
||||
TTM_PL_FLAG_VRAM, 0, 0, NULL, &priv->bo);
|
||||
if (ret == 0) {
|
||||
ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM);
|
||||
@@ -246,7 +246,7 @@ nv84_fence_create(struct nouveau_drm *drm)
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
ret = nouveau_bo_new(drm->dev, 16 * (pfifo->max + 1), 0,
|
||||
ret = nouveau_bo_new(drm->dev, 16 * priv->base.contexts, 0,
|
||||
TTM_PL_FLAG_TT, 0, 0, NULL,
|
||||
&priv->bo_gart);
|
||||
if (ret == 0) {
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
|
||||
ccflags-y := -Iinclude/drm
|
||||
|
||||
qxl-y := qxl_drv.o qxl_kms.o qxl_display.o qxl_ttm.o qxl_fb.o qxl_object.o qxl_gem.o qxl_cmd.o qxl_image.o qxl_draw.o qxl_debugfs.o qxl_irq.o qxl_dumb.o qxl_ioctl.o qxl_fence.o qxl_release.o
|
||||
qxl-y := qxl_drv.o qxl_kms.o qxl_display.o qxl_ttm.o qxl_fb.o qxl_object.o qxl_gem.o qxl_cmd.o qxl_image.o qxl_draw.o qxl_debugfs.o qxl_irq.o qxl_dumb.o qxl_ioctl.o qxl_release.o
|
||||
|
||||
obj-$(CONFIG_DRM_QXL)+= qxl.o
|
||||
|
||||
@@ -620,17 +620,10 @@ static int qxl_reap_surf(struct qxl_device *qdev, struct qxl_bo *surf, bool stal
|
||||
if (ret == -EBUSY)
|
||||
return -EBUSY;
|
||||
|
||||
if (surf->fence.num_active_releases > 0 && stall == false) {
|
||||
qxl_bo_unreserve(surf);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (stall)
|
||||
mutex_unlock(&qdev->surf_evict_mutex);
|
||||
|
||||
spin_lock(&surf->tbo.bdev->fence_lock);
|
||||
ret = ttm_bo_wait(&surf->tbo, true, true, !stall);
|
||||
spin_unlock(&surf->tbo.bdev->fence_lock);
|
||||
|
||||
if (stall)
|
||||
mutex_lock(&qdev->surf_evict_mutex);
|
||||
|
||||
@@ -57,11 +57,21 @@ qxl_debugfs_buffers_info(struct seq_file *m, void *data)
|
||||
struct qxl_device *qdev = node->minor->dev->dev_private;
|
||||
struct qxl_bo *bo;
|
||||
|
||||
spin_lock(&qdev->release_lock);
|
||||
list_for_each_entry(bo, &qdev->gem.objects, list) {
|
||||
seq_printf(m, "size %ld, pc %d, sync obj %p, num releases %d\n",
|
||||
(unsigned long)bo->gem_base.size, bo->pin_count,
|
||||
bo->tbo.sync_obj, bo->fence.num_active_releases);
|
||||
struct reservation_object_list *fobj;
|
||||
int rel;
|
||||
|
||||
rcu_read_lock();
|
||||
fobj = rcu_dereference(bo->tbo.resv->fence);
|
||||
rel = fobj ? fobj->shared_count : 0;
|
||||
rcu_read_unlock();
|
||||
|
||||
seq_printf(m, "size %ld, pc %d, num releases %d\n",
|
||||
(unsigned long)bo->gem_base.size,
|
||||
bo->pin_count, rel);
|
||||
}
|
||||
spin_unlock(&qdev->release_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
* Definitions taken from spice-protocol, plus kernel driver specific bits.
|
||||
*/
|
||||
|
||||
#include <linux/fence.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/platform_device.h>
|
||||
@@ -95,13 +96,6 @@ enum {
|
||||
QXL_INTERRUPT_IO_CMD |\
|
||||
QXL_INTERRUPT_CLIENT_MONITORS_CONFIG)
|
||||
|
||||
struct qxl_fence {
|
||||
struct qxl_device *qdev;
|
||||
uint32_t num_active_releases;
|
||||
uint32_t *release_ids;
|
||||
struct radix_tree_root tree;
|
||||
};
|
||||
|
||||
struct qxl_bo {
|
||||
/* Protected by gem.mutex */
|
||||
struct list_head list;
|
||||
@@ -113,13 +107,13 @@ struct qxl_bo {
|
||||
unsigned pin_count;
|
||||
void *kptr;
|
||||
int type;
|
||||
|
||||
/* Constant after initialization */
|
||||
struct drm_gem_object gem_base;
|
||||
bool is_primary; /* is this now a primary surface */
|
||||
bool hw_surf_alloc;
|
||||
struct qxl_surface surf;
|
||||
uint32_t surface_id;
|
||||
struct qxl_fence fence; /* per bo fence - list of releases */
|
||||
struct qxl_release *surf_create;
|
||||
};
|
||||
#define gem_to_qxl_bo(gobj) container_of((gobj), struct qxl_bo, gem_base)
|
||||
@@ -191,6 +185,8 @@ enum {
|
||||
* spice-protocol/qxl_dev.h */
|
||||
#define QXL_MAX_RES 96
|
||||
struct qxl_release {
|
||||
struct fence base;
|
||||
|
||||
int id;
|
||||
int type;
|
||||
uint32_t release_offset;
|
||||
@@ -284,7 +280,9 @@ struct qxl_device {
|
||||
uint8_t slot_gen_bits;
|
||||
uint64_t va_slot_mask;
|
||||
|
||||
spinlock_t release_lock;
|
||||
struct idr release_idr;
|
||||
uint32_t release_seqno;
|
||||
spinlock_t release_idr_lock;
|
||||
struct mutex async_io_mutex;
|
||||
unsigned int last_sent_io_cmd;
|
||||
@@ -561,10 +559,4 @@ qxl_surface_lookup(struct drm_device *dev, int surface_id);
|
||||
void qxl_surface_evict(struct qxl_device *qdev, struct qxl_bo *surf, bool freeing);
|
||||
int qxl_update_surface(struct qxl_device *qdev, struct qxl_bo *surf);
|
||||
|
||||
/* qxl_fence.c */
|
||||
void qxl_fence_add_release_locked(struct qxl_fence *qfence, uint32_t rel_id);
|
||||
int qxl_fence_remove_release(struct qxl_fence *qfence, uint32_t rel_id);
|
||||
int qxl_fence_init(struct qxl_device *qdev, struct qxl_fence *qfence);
|
||||
void qxl_fence_fini(struct qxl_fence *qfence);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors: Dave Airlie
|
||||
* Alon Levy
|
||||
*/
|
||||
|
||||
|
||||
#include "qxl_drv.h"
|
||||
|
||||
/* QXL fencing-
|
||||
|
||||
When we submit operations to the GPU we pass a release reference to the GPU
|
||||
with them, the release reference is then added to the release ring when
|
||||
the GPU is finished with that particular operation and has removed it from
|
||||
its tree.
|
||||
|
||||
So we have can have multiple outstanding non linear fences per object.
|
||||
|
||||
From a TTM POV we only care if the object has any outstanding releases on
|
||||
it.
|
||||
|
||||
we wait until all outstanding releases are processeed.
|
||||
|
||||
sync object is just a list of release ids that represent that fence on
|
||||
that buffer.
|
||||
|
||||
we just add new releases onto the sync object attached to the object.
|
||||
|
||||
This currently uses a radix tree to store the list of release ids.
|
||||
|
||||
For some reason every so often qxl hw fails to release, things go wrong.
|
||||
*/
|
||||
/* must be called with the fence lock held */
|
||||
void qxl_fence_add_release_locked(struct qxl_fence *qfence, uint32_t rel_id)
|
||||
{
|
||||
radix_tree_insert(&qfence->tree, rel_id, qfence);
|
||||
qfence->num_active_releases++;
|
||||
}
|
||||
|
||||
int qxl_fence_remove_release(struct qxl_fence *qfence, uint32_t rel_id)
|
||||
{
|
||||
void *ret;
|
||||
int retval = 0;
|
||||
struct qxl_bo *bo = container_of(qfence, struct qxl_bo, fence);
|
||||
|
||||
spin_lock(&bo->tbo.bdev->fence_lock);
|
||||
|
||||
ret = radix_tree_delete(&qfence->tree, rel_id);
|
||||
if (ret == qfence)
|
||||
qfence->num_active_releases--;
|
||||
else {
|
||||
DRM_DEBUG("didn't find fence in radix tree for %d\n", rel_id);
|
||||
retval = -ENOENT;
|
||||
}
|
||||
spin_unlock(&bo->tbo.bdev->fence_lock);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
int qxl_fence_init(struct qxl_device *qdev, struct qxl_fence *qfence)
|
||||
{
|
||||
qfence->qdev = qdev;
|
||||
qfence->num_active_releases = 0;
|
||||
INIT_RADIX_TREE(&qfence->tree, GFP_ATOMIC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void qxl_fence_fini(struct qxl_fence *qfence)
|
||||
{
|
||||
kfree(qfence->release_ids);
|
||||
qfence->num_active_releases = 0;
|
||||
}
|
||||
@@ -223,6 +223,7 @@ static int qxl_device_init(struct qxl_device *qdev,
|
||||
|
||||
idr_init(&qdev->release_idr);
|
||||
spin_lock_init(&qdev->release_idr_lock);
|
||||
spin_lock_init(&qdev->release_lock);
|
||||
|
||||
idr_init(&qdev->surf_id_idr);
|
||||
spin_lock_init(&qdev->surf_id_idr_lock);
|
||||
|
||||
@@ -36,7 +36,6 @@ static void qxl_ttm_bo_destroy(struct ttm_buffer_object *tbo)
|
||||
qdev = (struct qxl_device *)bo->gem_base.dev->dev_private;
|
||||
|
||||
qxl_surface_evict(qdev, bo, false);
|
||||
qxl_fence_fini(&bo->fence);
|
||||
mutex_lock(&qdev->gem.mutex);
|
||||
list_del_init(&bo->list);
|
||||
mutex_unlock(&qdev->gem.mutex);
|
||||
@@ -102,7 +101,6 @@ int qxl_bo_create(struct qxl_device *qdev,
|
||||
bo->type = domain;
|
||||
bo->pin_count = pinned ? 1 : 0;
|
||||
bo->surface_id = 0;
|
||||
qxl_fence_init(qdev, &bo->fence);
|
||||
INIT_LIST_HEAD(&bo->list);
|
||||
|
||||
if (surf)
|
||||
|
||||
@@ -76,12 +76,10 @@ static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type,
|
||||
}
|
||||
return r;
|
||||
}
|
||||
spin_lock(&bo->tbo.bdev->fence_lock);
|
||||
if (mem_type)
|
||||
*mem_type = bo->tbo.mem.mem_type;
|
||||
if (bo->tbo.sync_obj)
|
||||
r = ttm_bo_wait(&bo->tbo, true, true, no_wait);
|
||||
spin_unlock(&bo->tbo.bdev->fence_lock);
|
||||
|
||||
r = ttm_bo_wait(&bo->tbo, true, true, no_wait);
|
||||
ttm_bo_unreserve(&bo->tbo);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
#include "qxl_drv.h"
|
||||
#include "qxl_object.h"
|
||||
#include <trace/events/fence.h>
|
||||
|
||||
/*
|
||||
* drawable cmd cache - allocate a bunch of VRAM pages, suballocate
|
||||
@@ -39,6 +40,88 @@
|
||||
static const int release_size_per_bo[] = { RELEASE_SIZE, SURFACE_RELEASE_SIZE, RELEASE_SIZE };
|
||||
static const int releases_per_bo[] = { RELEASES_PER_BO, SURFACE_RELEASES_PER_BO, RELEASES_PER_BO };
|
||||
|
||||
static const char *qxl_get_driver_name(struct fence *fence)
|
||||
{
|
||||
return "qxl";
|
||||
}
|
||||
|
||||
static const char *qxl_get_timeline_name(struct fence *fence)
|
||||
{
|
||||
return "release";
|
||||
}
|
||||
|
||||
static bool qxl_nop_signaling(struct fence *fence)
|
||||
{
|
||||
/* fences are always automatically signaled, so just pretend we did this.. */
|
||||
return true;
|
||||
}
|
||||
|
||||
static long qxl_fence_wait(struct fence *fence, bool intr, signed long timeout)
|
||||
{
|
||||
struct qxl_device *qdev;
|
||||
struct qxl_release *release;
|
||||
int count = 0, sc = 0;
|
||||
bool have_drawable_releases;
|
||||
unsigned long cur, end = jiffies + timeout;
|
||||
|
||||
qdev = container_of(fence->lock, struct qxl_device, release_lock);
|
||||
release = container_of(fence, struct qxl_release, base);
|
||||
have_drawable_releases = release->type == QXL_RELEASE_DRAWABLE;
|
||||
|
||||
retry:
|
||||
sc++;
|
||||
|
||||
if (fence_is_signaled_locked(fence))
|
||||
goto signaled;
|
||||
|
||||
qxl_io_notify_oom(qdev);
|
||||
|
||||
for (count = 0; count < 11; count++) {
|
||||
if (!qxl_queue_garbage_collect(qdev, true))
|
||||
break;
|
||||
|
||||
if (fence_is_signaled_locked(fence))
|
||||
goto signaled;
|
||||
}
|
||||
|
||||
if (fence_is_signaled_locked(fence))
|
||||
goto signaled;
|
||||
|
||||
if (have_drawable_releases || sc < 4) {
|
||||
if (sc > 2)
|
||||
/* back off */
|
||||
usleep_range(500, 1000);
|
||||
|
||||
if (time_after(jiffies, end))
|
||||
return 0;
|
||||
|
||||
if (have_drawable_releases && sc > 300) {
|
||||
FENCE_WARN(fence, "failed to wait on release %d "
|
||||
"after spincount %d\n",
|
||||
fence->context & ~0xf0000000, sc);
|
||||
goto signaled;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
/*
|
||||
* yeah, original sync_obj_wait gave up after 3 spins when
|
||||
* have_drawable_releases is not set.
|
||||
*/
|
||||
|
||||
signaled:
|
||||
cur = jiffies;
|
||||
if (time_after(cur, end))
|
||||
return 0;
|
||||
return end - cur;
|
||||
}
|
||||
|
||||
static const struct fence_ops qxl_fence_ops = {
|
||||
.get_driver_name = qxl_get_driver_name,
|
||||
.get_timeline_name = qxl_get_timeline_name,
|
||||
.enable_signaling = qxl_nop_signaling,
|
||||
.wait = qxl_fence_wait,
|
||||
};
|
||||
|
||||
static uint64_t
|
||||
qxl_release_alloc(struct qxl_device *qdev, int type,
|
||||
struct qxl_release **ret)
|
||||
@@ -46,13 +129,13 @@ qxl_release_alloc(struct qxl_device *qdev, int type,
|
||||
struct qxl_release *release;
|
||||
int handle;
|
||||
size_t size = sizeof(*release);
|
||||
int idr_ret;
|
||||
|
||||
release = kmalloc(size, GFP_KERNEL);
|
||||
if (!release) {
|
||||
DRM_ERROR("Out of memory\n");
|
||||
return 0;
|
||||
}
|
||||
release->base.ops = NULL;
|
||||
release->type = type;
|
||||
release->release_offset = 0;
|
||||
release->surface_release_id = 0;
|
||||
@@ -60,44 +143,59 @@ qxl_release_alloc(struct qxl_device *qdev, int type,
|
||||
|
||||
idr_preload(GFP_KERNEL);
|
||||
spin_lock(&qdev->release_idr_lock);
|
||||
idr_ret = idr_alloc(&qdev->release_idr, release, 1, 0, GFP_NOWAIT);
|
||||
handle = idr_alloc(&qdev->release_idr, release, 1, 0, GFP_NOWAIT);
|
||||
release->base.seqno = ++qdev->release_seqno;
|
||||
spin_unlock(&qdev->release_idr_lock);
|
||||
idr_preload_end();
|
||||
handle = idr_ret;
|
||||
if (idr_ret < 0)
|
||||
goto release_fail;
|
||||
if (handle < 0) {
|
||||
kfree(release);
|
||||
*ret = NULL;
|
||||
return handle;
|
||||
}
|
||||
*ret = release;
|
||||
QXL_INFO(qdev, "allocated release %lld\n", handle);
|
||||
release->id = handle;
|
||||
release_fail:
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
static void
|
||||
qxl_release_free_list(struct qxl_release *release)
|
||||
{
|
||||
while (!list_empty(&release->bos)) {
|
||||
struct ttm_validate_buffer *entry;
|
||||
|
||||
entry = container_of(release->bos.next,
|
||||
struct ttm_validate_buffer, head);
|
||||
|
||||
list_del(&entry->head);
|
||||
kfree(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
qxl_release_free(struct qxl_device *qdev,
|
||||
struct qxl_release *release)
|
||||
{
|
||||
struct qxl_bo_list *entry, *tmp;
|
||||
QXL_INFO(qdev, "release %d, type %d\n", release->id,
|
||||
release->type);
|
||||
|
||||
if (release->surface_release_id)
|
||||
qxl_surface_id_dealloc(qdev, release->surface_release_id);
|
||||
|
||||
list_for_each_entry_safe(entry, tmp, &release->bos, tv.head) {
|
||||
struct qxl_bo *bo = to_qxl_bo(entry->tv.bo);
|
||||
QXL_INFO(qdev, "release %llx\n",
|
||||
drm_vma_node_offset_addr(&entry->tv.bo->vma_node)
|
||||
- DRM_FILE_OFFSET);
|
||||
qxl_fence_remove_release(&bo->fence, release->id);
|
||||
qxl_bo_unref(&bo);
|
||||
kfree(entry);
|
||||
}
|
||||
spin_lock(&qdev->release_idr_lock);
|
||||
idr_remove(&qdev->release_idr, release->id);
|
||||
spin_unlock(&qdev->release_idr_lock);
|
||||
kfree(release);
|
||||
|
||||
if (release->base.ops) {
|
||||
WARN_ON(list_empty(&release->bos));
|
||||
qxl_release_free_list(release);
|
||||
|
||||
fence_signal(&release->base);
|
||||
fence_put(&release->base);
|
||||
} else {
|
||||
qxl_release_free_list(release);
|
||||
kfree(release);
|
||||
}
|
||||
}
|
||||
|
||||
static int qxl_release_bo_alloc(struct qxl_device *qdev,
|
||||
@@ -142,6 +240,10 @@ static int qxl_release_validate_bo(struct qxl_bo *bo)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = reservation_object_reserve_shared(bo->tbo.resv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* allocate a surface for reserved + validated buffers */
|
||||
ret = qxl_bo_check_id(bo->gem_base.dev->dev_private, bo);
|
||||
if (ret)
|
||||
@@ -159,7 +261,7 @@ int qxl_release_reserve_list(struct qxl_release *release, bool no_intr)
|
||||
if (list_is_singular(&release->bos))
|
||||
return 0;
|
||||
|
||||
ret = ttm_eu_reserve_buffers(&release->ticket, &release->bos);
|
||||
ret = ttm_eu_reserve_buffers(&release->ticket, &release->bos, !no_intr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -199,6 +301,8 @@ int qxl_alloc_surface_release_reserved(struct qxl_device *qdev,
|
||||
|
||||
/* stash the release after the create command */
|
||||
idr_ret = qxl_release_alloc(qdev, QXL_RELEASE_SURFACE_CMD, release);
|
||||
if (idr_ret < 0)
|
||||
return idr_ret;
|
||||
bo = qxl_bo_ref(to_qxl_bo(entry->tv.bo));
|
||||
|
||||
(*release)->release_offset = create_rel->release_offset + 64;
|
||||
@@ -239,6 +343,11 @@ int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size,
|
||||
}
|
||||
|
||||
idr_ret = qxl_release_alloc(qdev, type, release);
|
||||
if (idr_ret < 0) {
|
||||
if (rbo)
|
||||
*rbo = NULL;
|
||||
return idr_ret;
|
||||
}
|
||||
|
||||
mutex_lock(&qdev->release_mutex);
|
||||
if (qdev->current_release_bo_offset[cur_idx] + 1 >= releases_per_bo[cur_idx]) {
|
||||
@@ -319,12 +428,13 @@ void qxl_release_unmap(struct qxl_device *qdev,
|
||||
|
||||
void qxl_release_fence_buffer_objects(struct qxl_release *release)
|
||||
{
|
||||
struct ttm_validate_buffer *entry;
|
||||
struct ttm_buffer_object *bo;
|
||||
struct ttm_bo_global *glob;
|
||||
struct ttm_bo_device *bdev;
|
||||
struct ttm_bo_driver *driver;
|
||||
struct qxl_bo *qbo;
|
||||
struct ttm_validate_buffer *entry;
|
||||
struct qxl_device *qdev;
|
||||
|
||||
/* if only one object on the release its the release itself
|
||||
since these objects are pinned no need to reserve */
|
||||
@@ -333,26 +443,32 @@ void qxl_release_fence_buffer_objects(struct qxl_release *release)
|
||||
|
||||
bo = list_first_entry(&release->bos, struct ttm_validate_buffer, head)->bo;
|
||||
bdev = bo->bdev;
|
||||
qdev = container_of(bdev, struct qxl_device, mman.bdev);
|
||||
|
||||
/*
|
||||
* Since we never really allocated a context and we don't want to conflict,
|
||||
* set the highest bits. This will break if we really allow exporting of dma-bufs.
|
||||
*/
|
||||
fence_init(&release->base, &qxl_fence_ops, &qdev->release_lock,
|
||||
release->id | 0xf0000000, release->base.seqno);
|
||||
trace_fence_emit(&release->base);
|
||||
|
||||
driver = bdev->driver;
|
||||
glob = bo->glob;
|
||||
|
||||
spin_lock(&glob->lru_lock);
|
||||
spin_lock(&bdev->fence_lock);
|
||||
/* acquire release_lock to protect bo->resv->fence and its contents */
|
||||
spin_lock(&qdev->release_lock);
|
||||
|
||||
list_for_each_entry(entry, &release->bos, head) {
|
||||
bo = entry->bo;
|
||||
qbo = to_qxl_bo(bo);
|
||||
|
||||
if (!entry->bo->sync_obj)
|
||||
entry->bo->sync_obj = &qbo->fence;
|
||||
|
||||
qxl_fence_add_release_locked(&qbo->fence, release->id);
|
||||
|
||||
reservation_object_add_shared_fence(bo->resv, &release->base);
|
||||
ttm_bo_add_to_lru(bo);
|
||||
__ttm_bo_unreserve(bo);
|
||||
entry->reserved = false;
|
||||
}
|
||||
spin_unlock(&bdev->fence_lock);
|
||||
spin_unlock(&qdev->release_lock);
|
||||
spin_unlock(&glob->lru_lock);
|
||||
ww_acquire_fini(&release->ticket);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user