mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 20:22:09 +00:00
Several fixes for amdgpu (all addressing issues with fences), yet
another orientation quirk for a Lenovo device, a use-after-free fix for virtio, a regression fix in TTM and a performance regression in drm buddy. -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQRcEzekXsqa64kGDp7j7w1vZxhRxQUCY8AGjQAKCRDj7w1vZxhR xcrzAQCqyykgLij4IkADIm2q3IVTbWQkoyHXn/zt5MZQT3hHjgD/YYxlXTcnp/CT v5IhL8O5AbBbPC8x9IoK0rbLiUrriQU= =5g22 -----END PGP SIGNATURE----- Merge tag 'drm-misc-fixes-2023-01-12' of git://anongit.freedesktop.org/drm/drm-misc into drm-fixes Several fixes for amdgpu (all addressing issues with fences), yet another orientation quirk for a Lenovo device, a use-after-free fix for virtio, a regression fix in TTM and a performance regression in drm buddy. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maxime Ripard <maxime@cerno.tech> Link: https://patchwork.freedesktop.org/patch/msgid/20230112130954.pxt77g3a7rokha42@houat
This commit is contained in:
commit
a283773308
@ -61,6 +61,8 @@ static int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p,
|
||||
amdgpu_ctx_put(p->ctx);
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
amdgpu_sync_create(&p->sync);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -452,18 +454,6 @@ static int amdgpu_syncobj_lookup_and_add(struct amdgpu_cs_parser *p,
|
||||
}
|
||||
|
||||
r = amdgpu_sync_fence(&p->sync, fence);
|
||||
if (r)
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* When we have an explicit dependency it might be necessary to insert a
|
||||
* pipeline sync to make sure that all caches etc are flushed and the
|
||||
* next job actually sees the results from the previous one.
|
||||
*/
|
||||
if (fence->context == p->gang_leader->base.entity->fence_context)
|
||||
r = amdgpu_sync_fence(&p->gang_leader->explicit_sync, fence);
|
||||
|
||||
error:
|
||||
dma_fence_put(fence);
|
||||
return r;
|
||||
}
|
||||
@ -1188,10 +1178,19 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p)
|
||||
static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p)
|
||||
{
|
||||
struct amdgpu_fpriv *fpriv = p->filp->driver_priv;
|
||||
struct drm_gpu_scheduler *sched;
|
||||
struct amdgpu_bo_list_entry *e;
|
||||
struct dma_fence *fence;
|
||||
unsigned int i;
|
||||
int r;
|
||||
|
||||
r = amdgpu_ctx_wait_prev_fence(p->ctx, p->entities[p->gang_leader_idx]);
|
||||
if (r) {
|
||||
if (r != -ERESTARTSYS)
|
||||
DRM_ERROR("amdgpu_ctx_wait_prev_fence failed.\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
list_for_each_entry(e, &p->validated, tv.head) {
|
||||
struct amdgpu_bo *bo = ttm_to_amdgpu_bo(e->tv.bo);
|
||||
struct dma_resv *resv = bo->tbo.base.resv;
|
||||
@ -1211,10 +1210,24 @@ static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = amdgpu_ctx_wait_prev_fence(p->ctx, p->entities[p->gang_leader_idx]);
|
||||
if (r && r != -ERESTARTSYS)
|
||||
DRM_ERROR("amdgpu_ctx_wait_prev_fence failed.\n");
|
||||
return r;
|
||||
sched = p->gang_leader->base.entity->rq->sched;
|
||||
while ((fence = amdgpu_sync_get_fence(&p->sync))) {
|
||||
struct drm_sched_fence *s_fence = to_drm_sched_fence(fence);
|
||||
|
||||
/*
|
||||
* When we have an dependency it might be necessary to insert a
|
||||
* pipeline sync to make sure that all caches etc are flushed and the
|
||||
* next job actually sees the results from the previous one
|
||||
* before we start executing on the same scheduler ring.
|
||||
*/
|
||||
if (!s_fence || s_fence->sched != sched)
|
||||
continue;
|
||||
|
||||
r = amdgpu_sync_fence(&p->gang_leader->explicit_sync, fence);
|
||||
if (r)
|
||||
return r;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void amdgpu_cs_post_dependencies(struct amdgpu_cs_parser *p)
|
||||
@ -1254,9 +1267,12 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
|
||||
continue;
|
||||
|
||||
fence = &p->jobs[i]->base.s_fence->scheduled;
|
||||
dma_fence_get(fence);
|
||||
r = drm_sched_job_add_dependency(&leader->base, fence);
|
||||
if (r)
|
||||
if (r) {
|
||||
dma_fence_put(fence);
|
||||
goto error_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (p->gang_size > 1) {
|
||||
@ -1344,6 +1360,7 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
amdgpu_sync_free(&parser->sync);
|
||||
for (i = 0; i < parser->num_post_deps; i++) {
|
||||
drm_syncobj_put(parser->post_deps[i].syncobj);
|
||||
kfree(parser->post_deps[i].chain);
|
||||
|
@ -391,8 +391,10 @@ int amdgpu_sync_push_to_job(struct amdgpu_sync *sync, struct amdgpu_job *job)
|
||||
|
||||
dma_fence_get(f);
|
||||
r = drm_sched_job_add_dependency(&job->base, f);
|
||||
if (r)
|
||||
if (r) {
|
||||
dma_fence_put(f);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -38,6 +38,25 @@ static void drm_block_free(struct drm_buddy *mm,
|
||||
kmem_cache_free(slab_blocks, block);
|
||||
}
|
||||
|
||||
static void list_insert_sorted(struct drm_buddy *mm,
|
||||
struct drm_buddy_block *block)
|
||||
{
|
||||
struct drm_buddy_block *node;
|
||||
struct list_head *head;
|
||||
|
||||
head = &mm->free_list[drm_buddy_block_order(block)];
|
||||
if (list_empty(head)) {
|
||||
list_add(&block->link, head);
|
||||
return;
|
||||
}
|
||||
|
||||
list_for_each_entry(node, head, link)
|
||||
if (drm_buddy_block_offset(block) < drm_buddy_block_offset(node))
|
||||
break;
|
||||
|
||||
__list_add(&block->link, node->link.prev, &node->link);
|
||||
}
|
||||
|
||||
static void mark_allocated(struct drm_buddy_block *block)
|
||||
{
|
||||
block->header &= ~DRM_BUDDY_HEADER_STATE;
|
||||
@ -52,8 +71,7 @@ static void mark_free(struct drm_buddy *mm,
|
||||
block->header &= ~DRM_BUDDY_HEADER_STATE;
|
||||
block->header |= DRM_BUDDY_FREE;
|
||||
|
||||
list_add(&block->link,
|
||||
&mm->free_list[drm_buddy_block_order(block)]);
|
||||
list_insert_sorted(mm, block);
|
||||
}
|
||||
|
||||
static void mark_split(struct drm_buddy_block *block)
|
||||
@ -387,20 +405,26 @@ err_undo:
|
||||
}
|
||||
|
||||
static struct drm_buddy_block *
|
||||
get_maxblock(struct list_head *head)
|
||||
get_maxblock(struct drm_buddy *mm, unsigned int order)
|
||||
{
|
||||
struct drm_buddy_block *max_block = NULL, *node;
|
||||
unsigned int i;
|
||||
|
||||
max_block = list_first_entry_or_null(head,
|
||||
struct drm_buddy_block,
|
||||
link);
|
||||
if (!max_block)
|
||||
return NULL;
|
||||
for (i = order; i <= mm->max_order; ++i) {
|
||||
if (!list_empty(&mm->free_list[i])) {
|
||||
node = list_last_entry(&mm->free_list[i],
|
||||
struct drm_buddy_block,
|
||||
link);
|
||||
if (!max_block) {
|
||||
max_block = node;
|
||||
continue;
|
||||
}
|
||||
|
||||
list_for_each_entry(node, head, link) {
|
||||
if (drm_buddy_block_offset(node) >
|
||||
drm_buddy_block_offset(max_block))
|
||||
max_block = node;
|
||||
if (drm_buddy_block_offset(node) >
|
||||
drm_buddy_block_offset(max_block)) {
|
||||
max_block = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return max_block;
|
||||
@ -412,20 +436,23 @@ alloc_from_freelist(struct drm_buddy *mm,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct drm_buddy_block *block = NULL;
|
||||
unsigned int i;
|
||||
unsigned int tmp;
|
||||
int err;
|
||||
|
||||
for (i = order; i <= mm->max_order; ++i) {
|
||||
if (flags & DRM_BUDDY_TOPDOWN_ALLOCATION) {
|
||||
block = get_maxblock(&mm->free_list[i]);
|
||||
if (block)
|
||||
break;
|
||||
} else {
|
||||
block = list_first_entry_or_null(&mm->free_list[i],
|
||||
struct drm_buddy_block,
|
||||
link);
|
||||
if (block)
|
||||
break;
|
||||
if (flags & DRM_BUDDY_TOPDOWN_ALLOCATION) {
|
||||
block = get_maxblock(mm, order);
|
||||
if (block)
|
||||
/* Store the obtained block order */
|
||||
tmp = drm_buddy_block_order(block);
|
||||
} else {
|
||||
for (tmp = order; tmp <= mm->max_order; ++tmp) {
|
||||
if (!list_empty(&mm->free_list[tmp])) {
|
||||
block = list_last_entry(&mm->free_list[tmp],
|
||||
struct drm_buddy_block,
|
||||
link);
|
||||
if (block)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -434,18 +461,18 @@ alloc_from_freelist(struct drm_buddy *mm,
|
||||
|
||||
BUG_ON(!drm_buddy_block_is_free(block));
|
||||
|
||||
while (i != order) {
|
||||
while (tmp != order) {
|
||||
err = split_block(mm, block);
|
||||
if (unlikely(err))
|
||||
goto err_undo;
|
||||
|
||||
block = block->right;
|
||||
i--;
|
||||
tmp--;
|
||||
}
|
||||
return block;
|
||||
|
||||
err_undo:
|
||||
if (i != order)
|
||||
if (tmp != order)
|
||||
__drm_buddy_free(mm, block);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
@ -304,6 +304,12 @@ static const struct dmi_system_id orientation_data[] = {
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGM"),
|
||||
},
|
||||
.driver_data = (void *)&lcd1200x1920_rightside_up,
|
||||
}, { /* Lenovo Ideapad D330-10IGL (HD) */
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGL"),
|
||||
},
|
||||
.driver_data = (void *)&lcd800x1280_rightside_up,
|
||||
}, { /* Lenovo Yoga Book X90F / X91F / X91L */
|
||||
.matches = {
|
||||
/* Non exact match to match all versions */
|
||||
|
@ -1,613 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2007 David Airlie
|
||||
*
|
||||
* 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 (including the next
|
||||
* paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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:
|
||||
* David Airlie
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/screen_info.h>
|
||||
#include <linux/vga_switcheroo.h>
|
||||
#include <linux/console.h>
|
||||
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_gem.h"
|
||||
#include "nouveau_bo.h"
|
||||
#include "nouveau_fbcon.h"
|
||||
#include "nouveau_chan.h"
|
||||
#include "nouveau_vmm.h"
|
||||
|
||||
#include "nouveau_crtc.h"
|
||||
|
||||
MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration");
|
||||
int nouveau_nofbaccel = 0;
|
||||
module_param_named(nofbaccel, nouveau_nofbaccel, int, 0400);
|
||||
|
||||
MODULE_PARM_DESC(fbcon_bpp, "fbcon bits-per-pixel (default: auto)");
|
||||
static int nouveau_fbcon_bpp;
|
||||
module_param_named(fbcon_bpp, nouveau_fbcon_bpp, int, 0400);
|
||||
|
||||
static void
|
||||
nouveau_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
|
||||
{
|
||||
struct nouveau_fbdev *fbcon = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
|
||||
struct nvif_device *device = &drm->client.device;
|
||||
int ret;
|
||||
|
||||
if (info->state != FBINFO_STATE_RUNNING)
|
||||
return;
|
||||
|
||||
ret = -ENODEV;
|
||||
if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) &&
|
||||
mutex_trylock(&drm->client.mutex)) {
|
||||
if (device->info.family < NV_DEVICE_INFO_V0_TESLA)
|
||||
ret = nv04_fbcon_fillrect(info, rect);
|
||||
else
|
||||
if (device->info.family < NV_DEVICE_INFO_V0_FERMI)
|
||||
ret = nv50_fbcon_fillrect(info, rect);
|
||||
else
|
||||
ret = nvc0_fbcon_fillrect(info, rect);
|
||||
mutex_unlock(&drm->client.mutex);
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
return;
|
||||
|
||||
if (ret != -ENODEV)
|
||||
nouveau_fbcon_gpu_lockup(info);
|
||||
drm_fb_helper_cfb_fillrect(info, rect);
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *image)
|
||||
{
|
||||
struct nouveau_fbdev *fbcon = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
|
||||
struct nvif_device *device = &drm->client.device;
|
||||
int ret;
|
||||
|
||||
if (info->state != FBINFO_STATE_RUNNING)
|
||||
return;
|
||||
|
||||
ret = -ENODEV;
|
||||
if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) &&
|
||||
mutex_trylock(&drm->client.mutex)) {
|
||||
if (device->info.family < NV_DEVICE_INFO_V0_TESLA)
|
||||
ret = nv04_fbcon_copyarea(info, image);
|
||||
else
|
||||
if (device->info.family < NV_DEVICE_INFO_V0_FERMI)
|
||||
ret = nv50_fbcon_copyarea(info, image);
|
||||
else
|
||||
ret = nvc0_fbcon_copyarea(info, image);
|
||||
mutex_unlock(&drm->client.mutex);
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
return;
|
||||
|
||||
if (ret != -ENODEV)
|
||||
nouveau_fbcon_gpu_lockup(info);
|
||||
drm_fb_helper_cfb_copyarea(info, image);
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
|
||||
{
|
||||
struct nouveau_fbdev *fbcon = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
|
||||
struct nvif_device *device = &drm->client.device;
|
||||
int ret;
|
||||
|
||||
if (info->state != FBINFO_STATE_RUNNING)
|
||||
return;
|
||||
|
||||
ret = -ENODEV;
|
||||
if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) &&
|
||||
mutex_trylock(&drm->client.mutex)) {
|
||||
if (device->info.family < NV_DEVICE_INFO_V0_TESLA)
|
||||
ret = nv04_fbcon_imageblit(info, image);
|
||||
else
|
||||
if (device->info.family < NV_DEVICE_INFO_V0_FERMI)
|
||||
ret = nv50_fbcon_imageblit(info, image);
|
||||
else
|
||||
ret = nvc0_fbcon_imageblit(info, image);
|
||||
mutex_unlock(&drm->client.mutex);
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
return;
|
||||
|
||||
if (ret != -ENODEV)
|
||||
nouveau_fbcon_gpu_lockup(info);
|
||||
drm_fb_helper_cfb_imageblit(info, image);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_fbcon_sync(struct fb_info *info)
|
||||
{
|
||||
struct nouveau_fbdev *fbcon = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
|
||||
struct nouveau_channel *chan = drm->channel;
|
||||
int ret;
|
||||
|
||||
if (!chan || !chan->accel_done || in_interrupt() ||
|
||||
info->state != FBINFO_STATE_RUNNING ||
|
||||
info->flags & FBINFO_HWACCEL_DISABLED)
|
||||
return 0;
|
||||
|
||||
if (!mutex_trylock(&drm->client.mutex))
|
||||
return 0;
|
||||
|
||||
ret = nouveau_channel_idle(chan);
|
||||
mutex_unlock(&drm->client.mutex);
|
||||
if (ret) {
|
||||
nouveau_fbcon_gpu_lockup(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
chan->accel_done = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_fbcon_open(struct fb_info *info, int user)
|
||||
{
|
||||
struct nouveau_fbdev *fbcon = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
|
||||
int ret = pm_runtime_get_sync(drm->dev->dev);
|
||||
if (ret < 0 && ret != -EACCES) {
|
||||
pm_runtime_put(drm->dev->dev);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_fbcon_release(struct fb_info *info, int user)
|
||||
{
|
||||
struct nouveau_fbdev *fbcon = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
|
||||
pm_runtime_put(drm->dev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fb_ops nouveau_fbcon_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
DRM_FB_HELPER_DEFAULT_OPS,
|
||||
.fb_open = nouveau_fbcon_open,
|
||||
.fb_release = nouveau_fbcon_release,
|
||||
.fb_fillrect = nouveau_fbcon_fillrect,
|
||||
.fb_copyarea = nouveau_fbcon_copyarea,
|
||||
.fb_imageblit = nouveau_fbcon_imageblit,
|
||||
.fb_sync = nouveau_fbcon_sync,
|
||||
};
|
||||
|
||||
static const struct fb_ops nouveau_fbcon_sw_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
DRM_FB_HELPER_DEFAULT_OPS,
|
||||
.fb_open = nouveau_fbcon_open,
|
||||
.fb_release = nouveau_fbcon_release,
|
||||
.fb_fillrect = drm_fb_helper_cfb_fillrect,
|
||||
.fb_copyarea = drm_fb_helper_cfb_copyarea,
|
||||
.fb_imageblit = drm_fb_helper_cfb_imageblit,
|
||||
};
|
||||
|
||||
void
|
||||
nouveau_fbcon_accel_save_disable(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
if (drm->fbcon && drm->fbcon->helper.info) {
|
||||
drm->fbcon->saved_flags = drm->fbcon->helper.info->flags;
|
||||
drm->fbcon->helper.info->flags |= FBINFO_HWACCEL_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_fbcon_accel_restore(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
if (drm->fbcon && drm->fbcon->helper.info)
|
||||
drm->fbcon->helper.info->flags = drm->fbcon->saved_flags;
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_fbcon_accel_fini(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_fbdev *fbcon = drm->fbcon;
|
||||
if (fbcon && drm->channel) {
|
||||
console_lock();
|
||||
if (fbcon->helper.info)
|
||||
fbcon->helper.info->flags |= FBINFO_HWACCEL_DISABLED;
|
||||
console_unlock();
|
||||
nouveau_channel_idle(drm->channel);
|
||||
nvif_object_dtor(&fbcon->twod);
|
||||
nvif_object_dtor(&fbcon->blit);
|
||||
nvif_object_dtor(&fbcon->gdi);
|
||||
nvif_object_dtor(&fbcon->patt);
|
||||
nvif_object_dtor(&fbcon->rop);
|
||||
nvif_object_dtor(&fbcon->clip);
|
||||
nvif_object_dtor(&fbcon->surf2d);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_fbcon_accel_init(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_fbdev *fbcon = drm->fbcon;
|
||||
struct fb_info *info = fbcon->helper.info;
|
||||
int ret;
|
||||
|
||||
if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA)
|
||||
ret = nv04_fbcon_accel_init(info);
|
||||
else
|
||||
if (drm->client.device.info.family < NV_DEVICE_INFO_V0_FERMI)
|
||||
ret = nv50_fbcon_accel_init(info);
|
||||
else
|
||||
ret = nvc0_fbcon_accel_init(info);
|
||||
|
||||
if (ret == 0)
|
||||
info->fbops = &nouveau_fbcon_ops;
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbdev *fbcon)
|
||||
{
|
||||
struct fb_info *info = fbcon->helper.info;
|
||||
struct fb_fillrect rect;
|
||||
|
||||
/* Clear the entire fbcon. The drm will program every connector
|
||||
* with it's preferred mode. If the sizes differ, one display will
|
||||
* quite likely have garbage around the console.
|
||||
*/
|
||||
rect.dx = rect.dy = 0;
|
||||
rect.width = info->var.xres_virtual;
|
||||
rect.height = info->var.yres_virtual;
|
||||
rect.color = 0;
|
||||
rect.rop = ROP_COPY;
|
||||
info->fbops->fb_fillrect(info, &rect);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_fbcon_create(struct drm_fb_helper *helper,
|
||||
struct drm_fb_helper_surface_size *sizes)
|
||||
{
|
||||
struct nouveau_fbdev *fbcon =
|
||||
container_of(helper, struct nouveau_fbdev, helper);
|
||||
struct drm_device *dev = fbcon->helper.dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nvif_device *device = &drm->client.device;
|
||||
struct fb_info *info;
|
||||
struct drm_framebuffer *fb;
|
||||
struct nouveau_channel *chan;
|
||||
struct nouveau_bo *nvbo;
|
||||
struct drm_mode_fb_cmd2 mode_cmd = {};
|
||||
int ret;
|
||||
|
||||
mode_cmd.width = sizes->surface_width;
|
||||
mode_cmd.height = sizes->surface_height;
|
||||
|
||||
mode_cmd.pitches[0] = mode_cmd.width * (sizes->surface_bpp >> 3);
|
||||
mode_cmd.pitches[0] = roundup(mode_cmd.pitches[0], 256);
|
||||
|
||||
mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
|
||||
sizes->surface_depth);
|
||||
|
||||
ret = nouveau_gem_new(&drm->client, mode_cmd.pitches[0] *
|
||||
mode_cmd.height, 0, NOUVEAU_GEM_DOMAIN_VRAM,
|
||||
0, 0x0000, &nvbo);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "failed to allocate framebuffer\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = nouveau_framebuffer_new(dev, &mode_cmd, &nvbo->bo.base, &fb);
|
||||
if (ret)
|
||||
goto out_unref;
|
||||
|
||||
ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, false);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "failed to pin fb: %d\n", ret);
|
||||
goto out_unref;
|
||||
}
|
||||
|
||||
ret = nouveau_bo_map(nvbo);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "failed to map fb: %d\n", ret);
|
||||
goto out_unpin;
|
||||
}
|
||||
|
||||
chan = nouveau_nofbaccel ? NULL : drm->channel;
|
||||
if (chan && device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
|
||||
ret = nouveau_vma_new(nvbo, chan->vmm, &fbcon->vma);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "failed to map fb into chan: %d\n", ret);
|
||||
chan = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
info = drm_fb_helper_alloc_info(helper);
|
||||
if (IS_ERR(info)) {
|
||||
ret = PTR_ERR(info);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* setup helper */
|
||||
fbcon->helper.fb = fb;
|
||||
|
||||
if (!chan)
|
||||
info->flags = FBINFO_HWACCEL_DISABLED;
|
||||
else
|
||||
info->flags = FBINFO_HWACCEL_COPYAREA |
|
||||
FBINFO_HWACCEL_FILLRECT |
|
||||
FBINFO_HWACCEL_IMAGEBLIT;
|
||||
info->fbops = &nouveau_fbcon_sw_ops;
|
||||
info->fix.smem_start = nvbo->bo.resource->bus.offset;
|
||||
info->fix.smem_len = nvbo->bo.base.size;
|
||||
|
||||
info->screen_base = nvbo_kmap_obj_iovirtual(nvbo);
|
||||
info->screen_size = nvbo->bo.base.size;
|
||||
|
||||
drm_fb_helper_fill_info(info, &fbcon->helper, sizes);
|
||||
|
||||
/* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
|
||||
|
||||
if (chan)
|
||||
nouveau_fbcon_accel_init(dev);
|
||||
nouveau_fbcon_zfill(dev, fbcon);
|
||||
|
||||
/* To allow resizeing without swapping buffers */
|
||||
NV_INFO(drm, "allocated %dx%d fb: 0x%llx, bo %p\n",
|
||||
fb->width, fb->height, nvbo->offset, nvbo);
|
||||
|
||||
if (dev_is_pci(dev->dev))
|
||||
vga_switcheroo_client_fb_set(to_pci_dev(dev->dev), info);
|
||||
|
||||
return 0;
|
||||
|
||||
out_unlock:
|
||||
if (chan)
|
||||
nouveau_vma_del(&fbcon->vma);
|
||||
nouveau_bo_unmap(nvbo);
|
||||
out_unpin:
|
||||
nouveau_bo_unpin(nvbo);
|
||||
out_unref:
|
||||
nouveau_bo_ref(NULL, &nvbo);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *fbcon)
|
||||
{
|
||||
struct drm_framebuffer *fb = fbcon->helper.fb;
|
||||
struct nouveau_bo *nvbo;
|
||||
|
||||
drm_fb_helper_unregister_info(&fbcon->helper);
|
||||
drm_fb_helper_fini(&fbcon->helper);
|
||||
|
||||
if (fb && fb->obj[0]) {
|
||||
nvbo = nouveau_gem_object(fb->obj[0]);
|
||||
nouveau_vma_del(&fbcon->vma);
|
||||
nouveau_bo_unmap(nvbo);
|
||||
nouveau_bo_unpin(nvbo);
|
||||
drm_framebuffer_put(fb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nouveau_fbcon_gpu_lockup(struct fb_info *info)
|
||||
{
|
||||
struct nouveau_fbdev *fbcon = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
|
||||
|
||||
NV_ERROR(drm, "GPU lockup - switching to software fbcon\n");
|
||||
info->flags |= FBINFO_HWACCEL_DISABLED;
|
||||
}
|
||||
|
||||
static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
|
||||
.fb_probe = nouveau_fbcon_create,
|
||||
};
|
||||
|
||||
static void
|
||||
nouveau_fbcon_set_suspend_work(struct work_struct *work)
|
||||
{
|
||||
struct nouveau_drm *drm = container_of(work, typeof(*drm), fbcon_work);
|
||||
int state = READ_ONCE(drm->fbcon_new_state);
|
||||
|
||||
if (state == FBINFO_STATE_RUNNING)
|
||||
pm_runtime_get_sync(drm->dev->dev);
|
||||
|
||||
console_lock();
|
||||
if (state == FBINFO_STATE_RUNNING)
|
||||
nouveau_fbcon_accel_restore(drm->dev);
|
||||
drm_fb_helper_set_suspend(&drm->fbcon->helper, state);
|
||||
if (state != FBINFO_STATE_RUNNING)
|
||||
nouveau_fbcon_accel_save_disable(drm->dev);
|
||||
console_unlock();
|
||||
|
||||
if (state == FBINFO_STATE_RUNNING) {
|
||||
nouveau_fbcon_hotplug_resume(drm->fbcon);
|
||||
pm_runtime_mark_last_busy(drm->dev->dev);
|
||||
pm_runtime_put_autosuspend(drm->dev->dev);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_fbcon_set_suspend(struct drm_device *dev, int state)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
|
||||
if (!drm->fbcon)
|
||||
return;
|
||||
|
||||
drm->fbcon_new_state = state;
|
||||
/* Since runtime resume can happen as a result of a sysfs operation,
|
||||
* it's possible we already have the console locked. So handle fbcon
|
||||
* init/deinit from a seperate work thread
|
||||
*/
|
||||
schedule_work(&drm->fbcon_work);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_fbcon_output_poll_changed(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_fbdev *fbcon = drm->fbcon;
|
||||
int ret;
|
||||
|
||||
if (!fbcon)
|
||||
return;
|
||||
|
||||
mutex_lock(&fbcon->hotplug_lock);
|
||||
|
||||
ret = pm_runtime_get(dev->dev);
|
||||
if (ret == 1 || ret == -EACCES) {
|
||||
drm_fb_helper_hotplug_event(&fbcon->helper);
|
||||
|
||||
pm_runtime_mark_last_busy(dev->dev);
|
||||
pm_runtime_put_autosuspend(dev->dev);
|
||||
} else if (ret == 0) {
|
||||
/* If the GPU was already in the process of suspending before
|
||||
* this event happened, then we can't block here as we'll
|
||||
* deadlock the runtime pmops since they wait for us to
|
||||
* finish. So, just defer this event for when we runtime
|
||||
* resume again. It will be handled by fbcon_work.
|
||||
*/
|
||||
NV_DEBUG(drm, "fbcon HPD event deferred until runtime resume\n");
|
||||
fbcon->hotplug_waiting = true;
|
||||
pm_runtime_put_noidle(drm->dev->dev);
|
||||
} else {
|
||||
DRM_WARN("fbcon HPD event lost due to RPM failure: %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
mutex_unlock(&fbcon->hotplug_lock);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_fbcon_hotplug_resume(struct nouveau_fbdev *fbcon)
|
||||
{
|
||||
struct nouveau_drm *drm;
|
||||
|
||||
if (!fbcon)
|
||||
return;
|
||||
drm = nouveau_drm(fbcon->helper.dev);
|
||||
|
||||
mutex_lock(&fbcon->hotplug_lock);
|
||||
if (fbcon->hotplug_waiting) {
|
||||
fbcon->hotplug_waiting = false;
|
||||
|
||||
NV_DEBUG(drm, "Handling deferred fbcon HPD events\n");
|
||||
drm_fb_helper_hotplug_event(&fbcon->helper);
|
||||
}
|
||||
mutex_unlock(&fbcon->hotplug_lock);
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_fbcon_init(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_fbdev *fbcon;
|
||||
int preferred_bpp = nouveau_fbcon_bpp;
|
||||
int ret;
|
||||
|
||||
if (!dev->mode_config.num_crtc ||
|
||||
(to_pci_dev(dev->dev)->class >> 8) != PCI_CLASS_DISPLAY_VGA)
|
||||
return 0;
|
||||
|
||||
fbcon = kzalloc(sizeof(struct nouveau_fbdev), GFP_KERNEL);
|
||||
if (!fbcon)
|
||||
return -ENOMEM;
|
||||
|
||||
drm->fbcon = fbcon;
|
||||
INIT_WORK(&drm->fbcon_work, nouveau_fbcon_set_suspend_work);
|
||||
mutex_init(&fbcon->hotplug_lock);
|
||||
|
||||
drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs);
|
||||
|
||||
ret = drm_fb_helper_init(dev, &fbcon->helper);
|
||||
if (ret)
|
||||
goto free;
|
||||
|
||||
if (preferred_bpp != 8 && preferred_bpp != 16 && preferred_bpp != 32) {
|
||||
if (drm->client.device.info.ram_size <= 32 * 1024 * 1024)
|
||||
preferred_bpp = 8;
|
||||
else
|
||||
if (drm->client.device.info.ram_size <= 64 * 1024 * 1024)
|
||||
preferred_bpp = 16;
|
||||
else
|
||||
preferred_bpp = 32;
|
||||
}
|
||||
|
||||
/* disable all the possible outputs/crtcs before entering KMS mode */
|
||||
if (!drm_drv_uses_atomic_modeset(dev))
|
||||
drm_helper_disable_unused_functions(dev);
|
||||
|
||||
ret = drm_fb_helper_initial_config(&fbcon->helper, preferred_bpp);
|
||||
if (ret)
|
||||
goto fini;
|
||||
|
||||
if (fbcon->helper.info)
|
||||
fbcon->helper.info->pixmap.buf_align = 4;
|
||||
return 0;
|
||||
|
||||
fini:
|
||||
drm_fb_helper_fini(&fbcon->helper);
|
||||
free:
|
||||
kfree(fbcon);
|
||||
drm->fbcon = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_fbcon_fini(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
|
||||
if (!drm->fbcon)
|
||||
return;
|
||||
|
||||
drm_kms_helper_poll_fini(dev);
|
||||
nouveau_fbcon_accel_fini(dev);
|
||||
nouveau_fbcon_destroy(dev, drm->fbcon);
|
||||
kfree(drm->fbcon);
|
||||
drm->fbcon = NULL;
|
||||
}
|
@ -173,7 +173,7 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo,
|
||||
|
||||
clear = src_iter->ops->maps_tt && (!ttm || !ttm_tt_is_populated(ttm));
|
||||
if (!(clear && ttm && !(ttm->page_flags & TTM_TT_FLAG_ZERO_ALLOC)))
|
||||
ttm_move_memcpy(clear, ttm->num_pages, dst_iter, src_iter);
|
||||
ttm_move_memcpy(clear, PFN_UP(dst_mem->size), dst_iter, src_iter);
|
||||
|
||||
if (!src_iter->ops->maps_tt)
|
||||
ttm_kmap_iter_linear_io_fini(&_src_iter.io, bdev, src_mem);
|
||||
|
@ -358,10 +358,18 @@ static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data,
|
||||
drm_gem_object_release(obj);
|
||||
return ret;
|
||||
}
|
||||
drm_gem_object_put(obj);
|
||||
|
||||
rc->res_handle = qobj->hw_res_handle; /* similiar to a VM address */
|
||||
rc->bo_handle = handle;
|
||||
|
||||
/*
|
||||
* The handle owns the reference now. But we must drop our
|
||||
* remaining reference *after* we no longer need to dereference
|
||||
* the obj. Otherwise userspace could guess the handle and
|
||||
* race closing it from another thread.
|
||||
*/
|
||||
drm_gem_object_put(obj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -723,11 +731,18 @@ static int virtio_gpu_resource_create_blob_ioctl(struct drm_device *dev,
|
||||
drm_gem_object_release(obj);
|
||||
return ret;
|
||||
}
|
||||
drm_gem_object_put(obj);
|
||||
|
||||
rc_blob->res_handle = bo->hw_res_handle;
|
||||
rc_blob->bo_handle = handle;
|
||||
|
||||
/*
|
||||
* The handle owns the reference now. But we must drop our
|
||||
* remaining reference *after* we no longer need to dereference
|
||||
* the obj. Otherwise userspace could guess the handle and
|
||||
* race closing it from another thread.
|
||||
*/
|
||||
drm_gem_object_put(obj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -254,40 +254,6 @@ void ttm_base_object_unref(struct ttm_base_object **p_base)
|
||||
kref_put(&base->refcount, ttm_release_base);
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_base_object_noref_lookup - look up a base object without reference
|
||||
* @tfile: The struct ttm_object_file the object is registered with.
|
||||
* @key: The object handle.
|
||||
*
|
||||
* This function looks up a ttm base object and returns a pointer to it
|
||||
* without refcounting the pointer. The returned pointer is only valid
|
||||
* until ttm_base_object_noref_release() is called, and the object
|
||||
* pointed to by the returned pointer may be doomed. Any persistent usage
|
||||
* of the object requires a refcount to be taken using kref_get_unless_zero().
|
||||
* Iff this function returns successfully it needs to be paired with
|
||||
* ttm_base_object_noref_release() and no sleeping- or scheduling functions
|
||||
* may be called inbetween these function callse.
|
||||
*
|
||||
* Return: A pointer to the object if successful or NULL otherwise.
|
||||
*/
|
||||
struct ttm_base_object *
|
||||
ttm_base_object_noref_lookup(struct ttm_object_file *tfile, uint64_t key)
|
||||
{
|
||||
struct vmwgfx_hash_item *hash;
|
||||
int ret;
|
||||
|
||||
rcu_read_lock();
|
||||
ret = ttm_tfile_find_ref_rcu(tfile, key, &hash);
|
||||
if (ret) {
|
||||
rcu_read_unlock();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__release(RCU);
|
||||
return hlist_entry(hash, struct ttm_ref_object, hash)->obj;
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_base_object_noref_lookup);
|
||||
|
||||
struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile,
|
||||
uint64_t key)
|
||||
{
|
||||
@ -295,15 +261,16 @@ struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile,
|
||||
struct vmwgfx_hash_item *hash;
|
||||
int ret;
|
||||
|
||||
rcu_read_lock();
|
||||
ret = ttm_tfile_find_ref_rcu(tfile, key, &hash);
|
||||
spin_lock(&tfile->lock);
|
||||
ret = ttm_tfile_find_ref(tfile, key, &hash);
|
||||
|
||||
if (likely(ret == 0)) {
|
||||
base = hlist_entry(hash, struct ttm_ref_object, hash)->obj;
|
||||
if (!kref_get_unless_zero(&base->refcount))
|
||||
base = NULL;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
spin_unlock(&tfile->lock);
|
||||
|
||||
|
||||
return base;
|
||||
}
|
||||
|
@ -307,18 +307,4 @@ extern int ttm_prime_handle_to_fd(struct ttm_object_file *tfile,
|
||||
#define ttm_prime_object_kfree(__obj, __prime) \
|
||||
kfree_rcu(__obj, __prime.base.rhead)
|
||||
|
||||
struct ttm_base_object *
|
||||
ttm_base_object_noref_lookup(struct ttm_object_file *tfile, uint64_t key);
|
||||
|
||||
/**
|
||||
* ttm_base_object_noref_release - release a base object pointer looked up
|
||||
* without reference
|
||||
*
|
||||
* Releases a base object pointer looked up with ttm_base_object_noref_lookup().
|
||||
*/
|
||||
static inline void ttm_base_object_noref_release(void)
|
||||
{
|
||||
__acquire(RCU);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
#endif
|
||||
|
@ -715,44 +715,6 @@ int vmw_user_bo_lookup(struct drm_file *filp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* vmw_user_bo_noref_lookup - Look up a vmw user buffer object without reference
|
||||
* @filp: The TTM object file the handle is registered with.
|
||||
* @handle: The user buffer object handle.
|
||||
*
|
||||
* This function looks up a struct vmw_bo and returns a pointer to the
|
||||
* struct vmw_buffer_object it derives from without refcounting the pointer.
|
||||
* The returned pointer is only valid until vmw_user_bo_noref_release() is
|
||||
* called, and the object pointed to by the returned pointer may be doomed.
|
||||
* Any persistent usage of the object requires a refcount to be taken using
|
||||
* ttm_bo_reference_unless_doomed(). Iff this function returns successfully it
|
||||
* needs to be paired with vmw_user_bo_noref_release() and no sleeping-
|
||||
* or scheduling functions may be called in between these function calls.
|
||||
*
|
||||
* Return: A struct vmw_buffer_object pointer if successful or negative
|
||||
* error pointer on failure.
|
||||
*/
|
||||
struct vmw_buffer_object *
|
||||
vmw_user_bo_noref_lookup(struct drm_file *filp, u32 handle)
|
||||
{
|
||||
struct vmw_buffer_object *vmw_bo;
|
||||
struct ttm_buffer_object *bo;
|
||||
struct drm_gem_object *gobj = drm_gem_object_lookup(filp, handle);
|
||||
|
||||
if (!gobj) {
|
||||
DRM_ERROR("Invalid buffer object handle 0x%08lx.\n",
|
||||
(unsigned long)handle);
|
||||
return ERR_PTR(-ESRCH);
|
||||
}
|
||||
vmw_bo = gem_to_vmw_bo(gobj);
|
||||
bo = ttm_bo_get_unless_zero(&vmw_bo->base);
|
||||
vmw_bo = vmw_buffer_object(bo);
|
||||
drm_gem_object_put(gobj);
|
||||
|
||||
return vmw_bo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* vmw_bo_fence_single - Utility function to fence a single TTM buffer
|
||||
* object without unreserving it.
|
||||
|
@ -830,12 +830,7 @@ extern int vmw_user_resource_lookup_handle(
|
||||
uint32_t handle,
|
||||
const struct vmw_user_resource_conv *converter,
|
||||
struct vmw_resource **p_res);
|
||||
extern struct vmw_resource *
|
||||
vmw_user_resource_noref_lookup_handle(struct vmw_private *dev_priv,
|
||||
struct ttm_object_file *tfile,
|
||||
uint32_t handle,
|
||||
const struct vmw_user_resource_conv *
|
||||
converter);
|
||||
|
||||
extern int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
extern int vmw_stream_unref_ioctl(struct drm_device *dev, void *data,
|
||||
@ -874,15 +869,6 @@ static inline bool vmw_resource_mob_attached(const struct vmw_resource *res)
|
||||
return !RB_EMPTY_NODE(&res->mob_node);
|
||||
}
|
||||
|
||||
/**
|
||||
* vmw_user_resource_noref_release - release a user resource pointer looked up
|
||||
* without reference
|
||||
*/
|
||||
static inline void vmw_user_resource_noref_release(void)
|
||||
{
|
||||
ttm_base_object_noref_release();
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffer object helper functions - vmwgfx_bo.c
|
||||
*/
|
||||
@ -934,8 +920,6 @@ extern void vmw_bo_unmap(struct vmw_buffer_object *vbo);
|
||||
extern void vmw_bo_move_notify(struct ttm_buffer_object *bo,
|
||||
struct ttm_resource *mem);
|
||||
extern void vmw_bo_swap_notify(struct ttm_buffer_object *bo);
|
||||
extern struct vmw_buffer_object *
|
||||
vmw_user_bo_noref_lookup(struct drm_file *filp, u32 handle);
|
||||
|
||||
/**
|
||||
* vmw_bo_adjust_prio - Adjust the buffer object eviction priority
|
||||
|
@ -290,20 +290,26 @@ static void vmw_execbuf_rcache_update(struct vmw_res_cache_entry *rcache,
|
||||
rcache->valid_handle = 0;
|
||||
}
|
||||
|
||||
enum vmw_val_add_flags {
|
||||
vmw_val_add_flag_none = 0,
|
||||
vmw_val_add_flag_noctx = 1 << 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* vmw_execbuf_res_noref_val_add - Add a resource described by an unreferenced
|
||||
* rcu-protected pointer to the validation list.
|
||||
* vmw_execbuf_res_val_add - Add a resource to the validation list.
|
||||
*
|
||||
* @sw_context: Pointer to the software context.
|
||||
* @res: Unreferenced rcu-protected pointer to the resource.
|
||||
* @dirty: Whether to change dirty status.
|
||||
* @flags: specifies whether to use the context or not
|
||||
*
|
||||
* Returns: 0 on success. Negative error code on failure. Typical error codes
|
||||
* are %-EINVAL on inconsistency and %-ESRCH if the resource was doomed.
|
||||
*/
|
||||
static int vmw_execbuf_res_noref_val_add(struct vmw_sw_context *sw_context,
|
||||
struct vmw_resource *res,
|
||||
u32 dirty)
|
||||
static int vmw_execbuf_res_val_add(struct vmw_sw_context *sw_context,
|
||||
struct vmw_resource *res,
|
||||
u32 dirty,
|
||||
u32 flags)
|
||||
{
|
||||
struct vmw_private *dev_priv = res->dev_priv;
|
||||
int ret;
|
||||
@ -318,24 +324,30 @@ static int vmw_execbuf_res_noref_val_add(struct vmw_sw_context *sw_context,
|
||||
if (dirty)
|
||||
vmw_validation_res_set_dirty(sw_context->ctx,
|
||||
rcache->private, dirty);
|
||||
vmw_user_resource_noref_release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
priv_size = vmw_execbuf_res_size(dev_priv, res_type);
|
||||
ret = vmw_validation_add_resource(sw_context->ctx, res, priv_size,
|
||||
dirty, (void **)&ctx_info,
|
||||
&first_usage);
|
||||
vmw_user_resource_noref_release();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (priv_size && first_usage) {
|
||||
ret = vmw_cmd_ctx_first_setup(dev_priv, sw_context, res,
|
||||
ctx_info);
|
||||
if (ret) {
|
||||
VMW_DEBUG_USER("Failed first usage context setup.\n");
|
||||
if ((flags & vmw_val_add_flag_noctx) != 0) {
|
||||
ret = vmw_validation_add_resource(sw_context->ctx, res, 0, dirty,
|
||||
(void **)&ctx_info, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
} else {
|
||||
priv_size = vmw_execbuf_res_size(dev_priv, res_type);
|
||||
ret = vmw_validation_add_resource(sw_context->ctx, res, priv_size,
|
||||
dirty, (void **)&ctx_info,
|
||||
&first_usage);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (priv_size && first_usage) {
|
||||
ret = vmw_cmd_ctx_first_setup(dev_priv, sw_context, res,
|
||||
ctx_info);
|
||||
if (ret) {
|
||||
VMW_DEBUG_USER("Failed first usage context setup.\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -343,43 +355,6 @@ static int vmw_execbuf_res_noref_val_add(struct vmw_sw_context *sw_context,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* vmw_execbuf_res_noctx_val_add - Add a non-context resource to the resource
|
||||
* validation list if it's not already on it
|
||||
*
|
||||
* @sw_context: Pointer to the software context.
|
||||
* @res: Pointer to the resource.
|
||||
* @dirty: Whether to change dirty status.
|
||||
*
|
||||
* Returns: Zero on success. Negative error code on failure.
|
||||
*/
|
||||
static int vmw_execbuf_res_noctx_val_add(struct vmw_sw_context *sw_context,
|
||||
struct vmw_resource *res,
|
||||
u32 dirty)
|
||||
{
|
||||
struct vmw_res_cache_entry *rcache;
|
||||
enum vmw_res_type res_type = vmw_res_type(res);
|
||||
void *ptr;
|
||||
int ret;
|
||||
|
||||
rcache = &sw_context->res_cache[res_type];
|
||||
if (likely(rcache->valid && rcache->res == res)) {
|
||||
if (dirty)
|
||||
vmw_validation_res_set_dirty(sw_context->ctx,
|
||||
rcache->private, dirty);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = vmw_validation_add_resource(sw_context->ctx, res, 0, dirty,
|
||||
&ptr, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vmw_execbuf_rcache_update(rcache, res, ptr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* vmw_view_res_val_add - Add a view and the surface it's pointing to to the
|
||||
* validation list
|
||||
@ -398,13 +373,13 @@ static int vmw_view_res_val_add(struct vmw_sw_context *sw_context,
|
||||
* First add the resource the view is pointing to, otherwise it may be
|
||||
* swapped out when the view is validated.
|
||||
*/
|
||||
ret = vmw_execbuf_res_noctx_val_add(sw_context, vmw_view_srf(view),
|
||||
vmw_view_dirtying(view));
|
||||
ret = vmw_execbuf_res_val_add(sw_context, vmw_view_srf(view),
|
||||
vmw_view_dirtying(view), vmw_val_add_flag_noctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return vmw_execbuf_res_noctx_val_add(sw_context, view,
|
||||
VMW_RES_DIRTY_NONE);
|
||||
return vmw_execbuf_res_val_add(sw_context, view, VMW_RES_DIRTY_NONE,
|
||||
vmw_val_add_flag_noctx);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -475,8 +450,9 @@ static int vmw_resource_context_res_add(struct vmw_private *dev_priv,
|
||||
if (IS_ERR(res))
|
||||
continue;
|
||||
|
||||
ret = vmw_execbuf_res_noctx_val_add(sw_context, res,
|
||||
VMW_RES_DIRTY_SET);
|
||||
ret = vmw_execbuf_res_val_add(sw_context, res,
|
||||
VMW_RES_DIRTY_SET,
|
||||
vmw_val_add_flag_noctx);
|
||||
if (unlikely(ret != 0))
|
||||
return ret;
|
||||
}
|
||||
@ -490,9 +466,9 @@ static int vmw_resource_context_res_add(struct vmw_private *dev_priv,
|
||||
if (vmw_res_type(entry->res) == vmw_res_view)
|
||||
ret = vmw_view_res_val_add(sw_context, entry->res);
|
||||
else
|
||||
ret = vmw_execbuf_res_noctx_val_add
|
||||
(sw_context, entry->res,
|
||||
vmw_binding_dirtying(entry->bt));
|
||||
ret = vmw_execbuf_res_val_add(sw_context, entry->res,
|
||||
vmw_binding_dirtying(entry->bt),
|
||||
vmw_val_add_flag_noctx);
|
||||
if (unlikely(ret != 0))
|
||||
break;
|
||||
}
|
||||
@ -658,7 +634,8 @@ vmw_cmd_res_check(struct vmw_private *dev_priv,
|
||||
{
|
||||
struct vmw_res_cache_entry *rcache = &sw_context->res_cache[res_type];
|
||||
struct vmw_resource *res;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
bool needs_unref = false;
|
||||
|
||||
if (p_res)
|
||||
*p_res = NULL;
|
||||
@ -683,17 +660,18 @@ vmw_cmd_res_check(struct vmw_private *dev_priv,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
res = vmw_user_resource_noref_lookup_handle
|
||||
(dev_priv, sw_context->fp->tfile, *id_loc, converter);
|
||||
if (IS_ERR(res)) {
|
||||
ret = vmw_user_resource_lookup_handle
|
||||
(dev_priv, sw_context->fp->tfile, *id_loc, converter, &res);
|
||||
if (ret != 0) {
|
||||
VMW_DEBUG_USER("Could not find/use resource 0x%08x.\n",
|
||||
(unsigned int) *id_loc);
|
||||
return PTR_ERR(res);
|
||||
}
|
||||
|
||||
ret = vmw_execbuf_res_noref_val_add(sw_context, res, dirty);
|
||||
if (unlikely(ret != 0))
|
||||
return ret;
|
||||
}
|
||||
needs_unref = true;
|
||||
|
||||
ret = vmw_execbuf_res_val_add(sw_context, res, dirty, vmw_val_add_flag_none);
|
||||
if (unlikely(ret != 0))
|
||||
goto res_check_done;
|
||||
|
||||
if (rcache->valid && rcache->res == res) {
|
||||
rcache->valid_handle = true;
|
||||
@ -708,7 +686,11 @@ vmw_cmd_res_check(struct vmw_private *dev_priv,
|
||||
if (p_res)
|
||||
*p_res = res;
|
||||
|
||||
return 0;
|
||||
res_check_done:
|
||||
if (needs_unref)
|
||||
vmw_resource_unreference(&res);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1171,9 +1153,9 @@ static int vmw_translate_mob_ptr(struct vmw_private *dev_priv,
|
||||
int ret;
|
||||
|
||||
vmw_validation_preload_bo(sw_context->ctx);
|
||||
vmw_bo = vmw_user_bo_noref_lookup(sw_context->filp, handle);
|
||||
if (IS_ERR(vmw_bo)) {
|
||||
VMW_DEBUG_USER("Could not find or use MOB buffer.\n");
|
||||
ret = vmw_user_bo_lookup(sw_context->filp, handle, &vmw_bo);
|
||||
if (ret != 0) {
|
||||
drm_dbg(&dev_priv->drm, "Could not find or use MOB buffer.\n");
|
||||
return PTR_ERR(vmw_bo);
|
||||
}
|
||||
ret = vmw_validation_add_bo(sw_context->ctx, vmw_bo, true, false);
|
||||
@ -1225,9 +1207,9 @@ static int vmw_translate_guest_ptr(struct vmw_private *dev_priv,
|
||||
int ret;
|
||||
|
||||
vmw_validation_preload_bo(sw_context->ctx);
|
||||
vmw_bo = vmw_user_bo_noref_lookup(sw_context->filp, handle);
|
||||
if (IS_ERR(vmw_bo)) {
|
||||
VMW_DEBUG_USER("Could not find or use GMR region.\n");
|
||||
ret = vmw_user_bo_lookup(sw_context->filp, handle, &vmw_bo);
|
||||
if (ret != 0) {
|
||||
drm_dbg(&dev_priv->drm, "Could not find or use GMR region.\n");
|
||||
return PTR_ERR(vmw_bo);
|
||||
}
|
||||
ret = vmw_validation_add_bo(sw_context->ctx, vmw_bo, false, false);
|
||||
@ -2025,8 +2007,9 @@ static int vmw_cmd_set_shader(struct vmw_private *dev_priv,
|
||||
res = vmw_shader_lookup(vmw_context_res_man(ctx),
|
||||
cmd->body.shid, cmd->body.type);
|
||||
if (!IS_ERR(res)) {
|
||||
ret = vmw_execbuf_res_noctx_val_add(sw_context, res,
|
||||
VMW_RES_DIRTY_NONE);
|
||||
ret = vmw_execbuf_res_val_add(sw_context, res,
|
||||
VMW_RES_DIRTY_NONE,
|
||||
vmw_val_add_flag_noctx);
|
||||
if (unlikely(ret != 0))
|
||||
return ret;
|
||||
|
||||
@ -2273,8 +2256,9 @@ static int vmw_cmd_dx_set_shader(struct vmw_private *dev_priv,
|
||||
return PTR_ERR(res);
|
||||
}
|
||||
|
||||
ret = vmw_execbuf_res_noctx_val_add(sw_context, res,
|
||||
VMW_RES_DIRTY_NONE);
|
||||
ret = vmw_execbuf_res_val_add(sw_context, res,
|
||||
VMW_RES_DIRTY_NONE,
|
||||
vmw_val_add_flag_noctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@ -2777,8 +2761,8 @@ static int vmw_cmd_dx_bind_shader(struct vmw_private *dev_priv,
|
||||
return PTR_ERR(res);
|
||||
}
|
||||
|
||||
ret = vmw_execbuf_res_noctx_val_add(sw_context, res,
|
||||
VMW_RES_DIRTY_NONE);
|
||||
ret = vmw_execbuf_res_val_add(sw_context, res, VMW_RES_DIRTY_NONE,
|
||||
vmw_val_add_flag_noctx);
|
||||
if (ret) {
|
||||
VMW_DEBUG_USER("Error creating resource validation node.\n");
|
||||
return ret;
|
||||
@ -3098,8 +3082,8 @@ static int vmw_cmd_dx_bind_streamoutput(struct vmw_private *dev_priv,
|
||||
|
||||
vmw_dx_streamoutput_set_size(res, cmd->body.sizeInBytes);
|
||||
|
||||
ret = vmw_execbuf_res_noctx_val_add(sw_context, res,
|
||||
VMW_RES_DIRTY_NONE);
|
||||
ret = vmw_execbuf_res_val_add(sw_context, res, VMW_RES_DIRTY_NONE,
|
||||
vmw_val_add_flag_noctx);
|
||||
if (ret) {
|
||||
DRM_ERROR("Error creating resource validation node.\n");
|
||||
return ret;
|
||||
@ -3148,8 +3132,8 @@ static int vmw_cmd_dx_set_streamoutput(struct vmw_private *dev_priv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = vmw_execbuf_res_noctx_val_add(sw_context, res,
|
||||
VMW_RES_DIRTY_NONE);
|
||||
ret = vmw_execbuf_res_val_add(sw_context, res, VMW_RES_DIRTY_NONE,
|
||||
vmw_val_add_flag_noctx);
|
||||
if (ret) {
|
||||
DRM_ERROR("Error creating resource validation node.\n");
|
||||
return ret;
|
||||
@ -4066,22 +4050,26 @@ static int vmw_execbuf_tie_context(struct vmw_private *dev_priv,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
res = vmw_user_resource_noref_lookup_handle
|
||||
ret = vmw_user_resource_lookup_handle
|
||||
(dev_priv, sw_context->fp->tfile, handle,
|
||||
user_context_converter);
|
||||
if (IS_ERR(res)) {
|
||||
user_context_converter, &res);
|
||||
if (ret != 0) {
|
||||
VMW_DEBUG_USER("Could not find or user DX context 0x%08x.\n",
|
||||
(unsigned int) handle);
|
||||
return PTR_ERR(res);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = vmw_execbuf_res_noref_val_add(sw_context, res, VMW_RES_DIRTY_SET);
|
||||
if (unlikely(ret != 0))
|
||||
ret = vmw_execbuf_res_val_add(sw_context, res, VMW_RES_DIRTY_SET,
|
||||
vmw_val_add_flag_none);
|
||||
if (unlikely(ret != 0)) {
|
||||
vmw_resource_unreference(&res);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sw_context->dx_ctx_node = vmw_execbuf_info_from_res(sw_context, res);
|
||||
sw_context->man = vmw_context_res_man(res);
|
||||
|
||||
vmw_resource_unreference(&res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -281,39 +281,6 @@ out_bad_resource:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* vmw_user_resource_noref_lookup_handle - lookup a struct resource from a
|
||||
* TTM user-space handle and perform basic type checks
|
||||
*
|
||||
* @dev_priv: Pointer to a device private struct
|
||||
* @tfile: Pointer to a struct ttm_object_file identifying the caller
|
||||
* @handle: The TTM user-space handle
|
||||
* @converter: Pointer to an object describing the resource type
|
||||
*
|
||||
* If the handle can't be found or is associated with an incorrect resource
|
||||
* type, -EINVAL will be returned.
|
||||
*/
|
||||
struct vmw_resource *
|
||||
vmw_user_resource_noref_lookup_handle(struct vmw_private *dev_priv,
|
||||
struct ttm_object_file *tfile,
|
||||
uint32_t handle,
|
||||
const struct vmw_user_resource_conv
|
||||
*converter)
|
||||
{
|
||||
struct ttm_base_object *base;
|
||||
|
||||
base = ttm_base_object_noref_lookup(tfile, handle);
|
||||
if (!base)
|
||||
return ERR_PTR(-ESRCH);
|
||||
|
||||
if (unlikely(ttm_base_object_type(base) != converter->object_type)) {
|
||||
ttm_base_object_noref_release();
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return converter->base_obj_to_res(base);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function that looks either a surface or bo.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user