A number of fixes:

- bridge:
     - samsung-dsim: fix GPIO lifetime
   - client: Null pointer dereference fix
   - imagination: error handling fix, page handling fix
   - nouveau: fix reference leaks, double-frees, out-of-bounds accesses,
     use-after-frees, don't reject config without SCDC,  a number of
     workarounds
   - virtio: fix memory leak, reference leaks, null pointer dereference,
     add pixel blend mode, cache coherency fix
 -----BEGIN PGP SIGNATURE-----
 
 iJUEABMJAB0WIQTkHFbLp4ejekA/qfgnX84Zoj2+dgUCarU2wwAKCRAnX84Zoj2+
 djqrAYCyMUsPcuCxM0A/RWQj9DpZ2W2P6UNoglWsC8QdUtE1MRmx2BbdXFN0OMJn
 UiJ33s4BfRLkJWTCBoHbTHl1jjZTQeRY0jIvFYUEIUXeRuXYQg5P8SAktSz6+ZRc
 xBaV09iwgA==
 =/tKR
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-fixes-2026-09-24' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-fixes

A number of fixes:
  - bridge:
    - samsung-dsim: fix GPIO lifetime
  - client: Null pointer dereference fix
  - imagination: error handling fix, page handling fix
  - nouveau: fix reference leaks, double-frees, out-of-bounds accesses,
    use-after-frees, don't reject config without SCDC,  a number of
    workarounds
  - virtio: fix memory leak, reference leaks, null pointer dereference,
    add pixel blend mode, cache coherency fix

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Maxime Ripard <self@mripard.dev>
Link: https://patch.msgid.link/arU22zzqUGDEco1y@houat
This commit is contained in:
Dave Airlie 2026-09-26 07:57:59 +10:00
commit a9ed3aa9b8
46 changed files with 531 additions and 61 deletions

View File

@ -515,6 +515,7 @@ void ivpu_prepare_for_reset(struct ivpu_device *vdev)
{
ivpu_hw_irq_disable(vdev);
disable_irq(vdev->irq);
atomic_set(&vdev->job_timeout_detected, 0);
flush_work(&vdev->irq_dct_work);
flush_work(&vdev->context_abort_work);
flush_work(&vdev->job_destroy_work);
@ -710,7 +711,7 @@ static int ivpu_dev_init(struct ivpu_device *vdev)
vdev->context_xa_limit.max = IVPU_USER_CONTEXT_MAX_SSID;
atomic64_set(&vdev->unique_id_counter, 0);
atomic_set(&vdev->job_timeout_counter, 0);
atomic_set(&vdev->faults_detected, 0);
atomic_set(&vdev->job_timeout_detected, 0);
xa_init_flags(&vdev->context_xa, XA_FLAGS_ALLOC | XA_FLAGS_LOCK_IRQ);
xa_init_flags(&vdev->submitted_jobs_xa, XA_FLAGS_ALLOC1);
xa_init_flags(&vdev->db_xa, XA_FLAGS_ALLOC1);

View File

@ -171,7 +171,7 @@ struct ivpu_device {
struct xarray submitted_jobs_xa;
struct ivpu_ipc_consumer job_done_consumer;
atomic_t job_timeout_counter;
atomic_t faults_detected;
atomic_t job_timeout_detected;
atomic64_t unique_id_counter;

View File

@ -621,7 +621,6 @@ bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_
* status and ensure both are handled in the same way
*/
job->file_priv->has_mmu_faults = true;
atomic_set(&vdev->faults_detected, 1);
queue_work(system_percpu_wq, &vdev->context_abort_work);
return true;
}
@ -1175,10 +1174,10 @@ static int reset_engine_and_mark_faulty_contexts(struct ivpu_device *vdev)
return ret;
/*
* If faults are detected, ignore guilty contexts from engine reset as NPU may not be stuck
* and could return currently running good context and faulty contexts are already marked
* If job timeout is detected, read guilty context from engine reset, for other reasons
* faulty context is already known
*/
if (atomic_cmpxchg(&vdev->faults_detected, 1, 0) == 1)
if (atomic_cmpxchg(&vdev->job_timeout_detected, 1, 0) == 0)
return 0;
num_impacted_contexts = resp.payload.engine_reset_done.num_impacted_contexts;

View File

@ -964,7 +964,6 @@ void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev)
file_priv = xa_load(&vdev->context_xa, ssid);
if (file_priv) {
if (!READ_ONCE(file_priv->has_mmu_faults)) {
atomic_set(&vdev->faults_detected, 1);
ivpu_mmu_dump_event(vdev, event);
WRITE_ONCE(file_priv->has_mmu_faults, true);
}

View File

@ -229,6 +229,7 @@ static void ivpu_job_timeout_work(struct work_struct *work)
ivpu_jsm_state_dump(vdev);
ivpu_dev_coredump(vdev);
atomic_set(&vdev->job_timeout_detected, 1);
queue_work(system_percpu_wq, &vdev->context_abort_work);
}

View File

@ -1862,7 +1862,7 @@ static int samsung_dsim_register_te_irq(struct samsung_dsim *dsi, struct device
int te_gpio_irq;
int ret;
dsi->te_gpio = devm_gpiod_get_optional(dev, "te", GPIOD_IN);
dsi->te_gpio = gpiod_get_optional(dev, "te", GPIOD_IN);
if (!dsi->te_gpio)
return 0;
else if (IS_ERR(dsi->te_gpio))

View File

@ -42,6 +42,14 @@ static int drm_fbdev_client_restore(struct drm_client_dev *client, bool force)
{
struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
/*
* The client is registered before the initial fbdev probe.
* If probing failed, the client remains registered but there
* is no valid fbdev framebuffer to restore.
*/
if (!fb_helper->info || !fb_helper->fb)
return 0;
drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force);
return 0;

View File

@ -8,6 +8,7 @@
#include "pvr_vm.h"
#include <drm/drm_gem.h>
#include <drm/drm_print.h>
#include <linux/slab.h>
#include <linux/xarray.h>
#include <uapi/drm/pvr_drm.h>
@ -612,13 +613,21 @@ pvr_free_list_process_reconstruct_req(struct pvr_device *pvr_dev,
};
struct rogue_fwif_freelists_reconstruction_data *resp =
&resp_cmd.cmd_data.free_lists_reconstruction_data;
u32 count = min_t(u32, req->freelist_count,
ARRAY_SIZE(req->freelist_ids));
for (u32 i = 0; i < req->freelist_count; i++)
if (count != req->freelist_count) {
drm_warn_once(from_pvr_device(pvr_dev),
"Requested reconstruction of %u freelists, limiting to %u\n",
req->freelist_count, count);
}
for (u32 i = 0; i < count; i++)
pvr_free_list_reconstruct(pvr_dev, req->freelist_ids[i]);
resp->freelist_count = req->freelist_count;
resp->freelist_count = count;
memcpy(resp->freelist_ids, req->freelist_ids,
req->freelist_count * sizeof(resp->freelist_ids[0]));
count * sizeof(resp->freelist_ids[0]));
WARN_ON(pvr_kccb_send_cmd(pvr_dev, &resp_cmd, NULL));
}

View File

@ -12,6 +12,7 @@
#include "pvr_rogue_mmu_defs.h"
#include <drm/drm_drv.h>
#include <drm/drm_print.h>
#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/dma-mapping.h>
@ -2335,6 +2336,7 @@ void pvr_mmu_op_context_destroy(struct pvr_mmu_op_context *op_ctx)
* pvr_mmu_op_context_create() - Create an MMU op context.
* @ctx: MMU context associated with owning VM context.
* @sgt: Scatter gather table containing pages pinned for use by this context.
* @device_addr: Virtual device address at the start of the requested mapping.
* @sgt_offset: Start offset of the requested device-virtual memory mapping.
* @size: Size in bytes of the requested device-virtual memory mapping. For an
* unmapping, this should be zero so that no page tables are allocated.
@ -2346,8 +2348,9 @@ void pvr_mmu_op_context_destroy(struct pvr_mmu_op_context *op_ctx)
*/
struct pvr_mmu_op_context *
pvr_mmu_op_context_create(struct pvr_mmu_context *ctx, struct sg_table *sgt,
u64 sgt_offset, u64 size)
u64 device_addr, u64 sgt_offset, u64 size)
{
u64 start_addr = device_addr + sgt_offset;
int err;
struct pvr_mmu_op_context *op_ctx = kzalloc_obj(*op_ctx);
@ -2363,16 +2366,16 @@ pvr_mmu_op_context_create(struct pvr_mmu_context *ctx, struct sg_table *sgt,
if (size) {
/*
* The number of page table objects we need to prealloc is
* indicated by the mapping size, start offset and the sizes
* indicated by the mapping size, start address and the sizes
* of the areas mapped per PT or PD. The range calculation is
* identical to that for the index into a table for a device
* address, so we reuse those functions here.
*/
const u32 l1_start_idx = pvr_page_table_l2_idx(sgt_offset);
const u32 l1_end_idx = pvr_page_table_l2_idx(sgt_offset + size);
const u32 l1_start_idx = pvr_page_table_l2_idx(start_addr);
const u32 l1_end_idx = pvr_page_table_l2_idx(start_addr + size);
const u32 l1_count = l1_end_idx - l1_start_idx + 1;
const u32 l0_start_idx = pvr_page_table_l1_idx(sgt_offset);
const u32 l0_end_idx = pvr_page_table_l1_idx(sgt_offset + size);
const u32 l0_start_idx = pvr_page_table_l1_idx(start_addr);
const u32 l0_end_idx = pvr_page_table_l1_idx(start_addr + size);
const u32 l0_count = l0_end_idx - l0_start_idx + 1;
/*
@ -2553,7 +2556,9 @@ pvr_mmu_map_sgl(struct pvr_mmu_op_context *op_ctx, struct scatterlist *sgl,
err_destroy_pages:
memcpy(&op_ctx->curr_page, &ptr_copy, sizeof(op_ctx->curr_page));
err = pvr_mmu_op_context_unmap_curr_page(op_ctx, page);
if (pvr_mmu_op_context_unmap_curr_page(op_ctx, page))
drm_err(from_pvr_device(op_ctx->mmu_ctx->pvr_dev),
"%s : Failure in unmapping pages\n", __func__);
return err;
}

View File

@ -99,7 +99,7 @@ dma_addr_t pvr_mmu_get_root_table_dma_addr(struct pvr_mmu_context *ctx);
void pvr_mmu_op_context_destroy(struct pvr_mmu_op_context *op_ctx);
struct pvr_mmu_op_context *
pvr_mmu_op_context_create(struct pvr_mmu_context *ctx,
struct sg_table *sgt, u64 sgt_offset, u64 size);
struct sg_table *sgt, u64 device_addr, u64 sgt_offset, u64 size);
int pvr_mmu_map(struct pvr_mmu_op_context *op_ctx, u64 size, u64 flags,
u64 device_addr);

View File

@ -276,7 +276,7 @@ pvr_vm_bind_op_map_init(struct pvr_vm_bind_op *bind_op,
goto err_bind_op_fini;
bind_op->mmu_op_ctx =
pvr_mmu_op_context_create(vm_ctx->mmu_ctx, sgt, offset, size);
pvr_mmu_op_context_create(vm_ctx->mmu_ctx, sgt, device_addr, offset, size);
err = PTR_ERR_OR_ZERO(bind_op->mmu_op_ctx);
if (err) {
bind_op->mmu_op_ctx = NULL;
@ -318,7 +318,7 @@ pvr_vm_bind_op_unmap_init(struct pvr_vm_bind_op *bind_op,
}
bind_op->mmu_op_ctx =
pvr_mmu_op_context_create(vm_ctx->mmu_ctx, NULL, 0, 0);
pvr_mmu_op_context_create(vm_ctx->mmu_ctx, NULL, device_addr, 0, 0);
err = PTR_ERR_OR_ZERO(bind_op->mmu_op_ctx);
if (err) {
bind_op->mmu_op_ctx = NULL;

View File

@ -4,6 +4,7 @@
#define NV_DEVICE_V0_INFO 0x00
#define NV_DEVICE_V0_TIME 0x01
#define NV_DEVICE_V0_GCX_READY 0x02
struct nv_device_info_v0 {
__u8 version;
@ -55,6 +56,15 @@ struct nv_device_time_v0 {
__u64 time;
};
#define NV_DEVICE_GC6_READY BIT(0)
#define NV_DEVICE_GCOFF_READY BIT(1)
struct nv_device_gcx_ready_v0 {
__u8 version;
__u8 pad01[6];
__u8 ready;
};
#define NV_DEVICE_INFO_UNIT (0xffffffffULL << 32)
#define NV_DEVICE_INFO(n) ((n) | (0x00000000ULL << 32))
#define NV_DEVICE_HOST(n) ((n) | (0x00000001ULL << 32))

View File

@ -22,4 +22,5 @@ int nvif_device_ctor(struct nvif_client *, const char *name, struct nvif_device
void nvif_device_dtor(struct nvif_device *);
int nvif_device_map(struct nvif_device *);
u64 nvif_device_time(struct nvif_device *);
int nvif_device_gcx_ready(struct nvif_device *);
#endif

View File

@ -156,6 +156,10 @@ struct nvkm_gsp {
struct sg_table fbsr;
} sr;
struct {
bool use_raw_mode_comptagline_alloc;
} memsys;
struct {
struct nvkm_gsp_mem mem;
@ -495,6 +499,8 @@ nvkm_gsp_event_dtor(struct nvkm_gsp_event *event)
int nvkm_gsp_intr_stall(struct nvkm_gsp *, enum nvkm_subdev_type, int);
int nvkm_gsp_intr_nonstall(struct nvkm_gsp *, enum nvkm_subdev_type, int);
int nvkm_gsp_gcx_ready(struct nvkm_gsp *gsp);
int gv100_gsp_new(struct nvkm_device *, enum nvkm_subdev_type, int, struct nvkm_gsp **);
int tu102_gsp_new(struct nvkm_device *, enum nvkm_subdev_type, int, struct nvkm_gsp **);
int tu116_gsp_new(struct nvkm_device *, enum nvkm_subdev_type, int, struct nvkm_gsp **);

View File

@ -578,8 +578,9 @@ int nouveau_bo_pin_locked(struct nouveau_bo *nvbo, uint32_t domain, bool contig)
"0x%08x vs 0x%08x\n", bo,
bo->resource->mem_type, domain);
ret = -EBUSY;
} else {
ttm_bo_pin(&nvbo->bo);
}
ttm_bo_pin(&nvbo->bo);
goto out;
}

View File

@ -600,8 +600,11 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
new_edid = drm_get_edid(connector, nv_encoder->i2c);
} else {
ret = nvif_outp_edid_get(&nv_encoder->outp, (u8 **)&new_edid);
if (ret < 0)
if (ret < 0) {
pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return connector_status_disconnected;
}
}
nouveau_connector_set_edid(nv_connector, new_edid);

View File

@ -339,8 +339,8 @@ nouveau_dmem_chunk_alloc(struct nouveau_drm *drm, struct page **ppage,
chunk->pagemap.ops = &nouveau_dmem_pagemap_ops;
chunk->pagemap.owner = drm->dev;
ret = nouveau_bo_new_pin(&drm->client, NOUVEAU_GEM_DOMAIN_VRAM, DMEM_CHUNK_SIZE,
&chunk->bo);
ret = nouveau_bo_new_pin(&drm->client, NOUVEAU_GEM_DOMAIN_VRAM,
DMEM_CHUNK_SIZE * NR_CHUNKS, &chunk->bo);
if (ret)
goto out_release;

View File

@ -585,6 +585,7 @@ nouveau_drm_device_fini(struct nouveau_drm *drm)
if (nouveau_pmops_runtime()) {
pm_runtime_get_sync(dev->dev);
pm_runtime_forbid(dev->dev);
pm_runtime_dont_use_autosuspend(dev->dev);
}
nouveau_led_fini(dev);
@ -1148,6 +1149,7 @@ nouveau_pmops_runtime_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct nouveau_drm *drm = pci_get_drvdata(pdev);
struct nvif_device *nvif = &drm->client.device;
int ret;
if (!nouveau_pmops_runtime()) {
@ -1155,6 +1157,18 @@ nouveau_pmops_runtime_suspend(struct device *dev)
return -EBUSY;
}
// Check if the GPU itself is ready for runtime suspend, otherwise mark as busy and check
// again in a bit.
ret = nvif_device_gcx_ready(nvif);
if (ret < 0) {
NV_ERROR(drm, "Failed to query GCX readiness (returned %d)\n", ret);
return -EBUSY;
} else if (!(ret & NV_DEVICE_GCOFF_READY)) {
NV_DEBUG(drm, "GPU isn't ready for suspend yet, delaying...\n");
pm_runtime_mark_last_busy(dev);
return -EBUSY;
}
nouveau_switcheroo_optimus_dsm();
ret = nouveau_do_suspend(drm, true);
pci_save_state(pdev);
@ -1250,10 +1264,8 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
mutex_unlock(&drm->clients_lock);
done:
if (ret && cli) {
nouveau_cli_fini(cli);
if (ret && cli)
kfree(cli);
}
pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);

View File

@ -522,6 +522,7 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv,
if (unlikely(ret)) {
if (ret != -ERESTARTSYS)
NV_PRINTK(err, cli, "fail reserve\n");
drm_gem_object_put(gem);
break;
}
}
@ -531,6 +532,7 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv,
struct nouveau_vma *vma = nouveau_vma_find(nvbo, vmm);
if (!vma) {
NV_PRINTK(err, cli, "vma not found!\n");
drm_gem_object_put(gem);
ret = -EINVAL;
break;
}

View File

@ -517,7 +517,7 @@ nouveau_sched_destroy(struct nouveau_sched **psched)
struct nouveau_sched *sched = *psched;
nouveau_sched_fini(sched);
kfree(sched);
kfree_rcu(sched, rcu);
*psched = NULL;
}

View File

@ -98,6 +98,7 @@ void nouveau_job_free(struct nouveau_job *job);
struct nouveau_sched {
struct drm_gpu_scheduler base;
struct rcu_head rcu;
struct drm_sched_entity entity;
struct workqueue_struct *wq;
struct mutex mutex;

View File

@ -846,6 +846,9 @@ op_map(struct nouveau_uvma *uvma)
{
struct nouveau_bo *nvbo = nouveau_gem_object(uvma->va.gem.obj);
if (drm_gpuva_invalidated(&uvma->va))
return;
nouveau_uvma_map(uvma, nouveau_mem(nvbo->bo.resource));
}
@ -1232,6 +1235,7 @@ bind_lock_validate(struct nouveau_job *job, struct drm_exec *exec,
drm_gpuva_for_each_op(va_op, op->ops) {
struct drm_gem_object *obj = op_gem_obj(va_op);
struct nouveau_bo *nvbo;
if (unlikely(!obj))
continue;
@ -1246,8 +1250,13 @@ bind_lock_validate(struct nouveau_job *job, struct drm_exec *exec,
if (va_op->op == DRM_GPUVA_OP_UNMAP)
continue;
ret = nouveau_bo_validate(nouveau_gem_object(obj),
true, false);
nvbo = nouveau_gem_object(obj);
if (!(nvbo->valid_domains &
(NOUVEAU_GEM_DOMAIN_VRAM | NOUVEAU_GEM_DOMAIN_GART)))
return -EINVAL;
nouveau_bo_placement_set(nvbo, nvbo->valid_domains, 0);
ret = nouveau_bo_validate(nvbo, true, false);
if (ret)
return ret;
}

View File

@ -38,6 +38,19 @@ nvif_device_time(struct nvif_device *device)
return device->user.func->time(&device->user);
}
int
nvif_device_gcx_ready(struct nvif_device *device)
{
struct nv_device_gcx_ready_v0 args = {};
int ret;
ret = nvif_object_mthd(&device->object, NV_DEVICE_V0_GCX_READY, &args, sizeof(args));
if (ret)
return ret;
return args.ready;
}
int
nvif_device_map(struct nvif_device *device)
{

View File

@ -192,6 +192,7 @@ void
nvif_vmm_dtor(struct nvif_vmm *vmm)
{
kfree(vmm->page);
vmm->page = NULL;
nvif_object_dtor(&vmm->object);
}

View File

@ -74,6 +74,7 @@ nvkm_control_mthd_pstate_attr(struct nvkm_control *ctrl, void *data, u32 size)
const struct nvkm_domain *domain;
struct nvkm_pstate *pstate;
struct nvkm_cstate *cstate;
bool found = false;
int i = 0, j = -1;
u32 lo, hi;
int ret = -ENOSYS;
@ -104,10 +105,15 @@ nvkm_control_mthd_pstate_attr(struct nvkm_control *ctrl, void *data, u32 size)
if (args->v0.state != NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT) {
list_for_each_entry(pstate, &clk->states, head) {
if (i++ == args->v0.state)
if (i++ == args->v0.state) {
found = true;
break;
}
}
if (!found)
return -EINVAL;
lo = pstate->base.domain[domain->name];
hi = lo;
list_for_each_entry(cstate, &pstate->list, head) {

View File

@ -27,6 +27,7 @@
#include <core/client.h>
#include <subdev/fb.h>
#include <subdev/gsp.h>
#include <subdev/instmem.h>
#include <subdev/timer.h>
@ -189,6 +190,38 @@ nvkm_udevice_time(struct nvkm_udevice *udev, void *data, u32 size)
return ret;
}
static int
nvkm_udevice_gcx_ready(struct nvkm_udevice *udev, void *data, u32 size)
{
struct nvkm_object *object = &udev->object;
struct nvkm_device *device = udev->device;
struct nvkm_gsp *gsp = device->gsp;
union {
struct nv_device_gcx_ready_v0 v0;
} *args = data;
int ret = -ENOSYS;
if (!gsp) {
args->v0.ready = NV_DEVICE_GC6_READY | NV_DEVICE_GCOFF_READY;
return 0;
}
nvif_ioctl(object, "device gcx ready size %d\n", size);
ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false);
if (!ret) {
nvif_ioctl(object, "device gcx ready vers %d\n", args->v0.version);
ret = nvkm_gsp_gcx_ready(gsp);
if (ret < 0)
return ret;
args->v0.ready = ret;
ret = 0;
}
return ret;
}
static int
nvkm_udevice_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
{
@ -199,6 +232,8 @@ nvkm_udevice_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
return nvkm_udevice_info(udev, data, size);
case NV_DEVICE_V0_TIME:
return nvkm_udevice_time(udev, data, size);
case NV_DEVICE_V0_GCX_READY:
return nvkm_udevice_gcx_ready(udev, data, size);
default:
break;
}

View File

@ -253,8 +253,7 @@ nvkm_uoutp_mthd_hdmi(struct nvkm_outp *outp, void *argv, u32 argc)
if (!ior->func->hdmi ||
args->v0.max_ac_packet > 0x1f ||
args->v0.rekey > 0x7f ||
(args->v0.scdc && !ior->func->hdmi->scdc))
args->v0.rekey > 0x7f)
return -EINVAL;
if (!args->v0.enable) {

View File

@ -199,16 +199,18 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei)
}
if (volt) {
ret = nvkm_volt_set_id(volt, cstate->voltage,
pstate->base.voltage, clk->temp, -1);
if (ret && ret != -ENODEV)
nvkm_error(subdev, "failed to lower voltage: %d\n", ret);
int err = nvkm_volt_set_id(volt, cstate->voltage,
pstate->base.voltage, clk->temp, -1);
if (err && err != -ENODEV)
nvkm_error(subdev, "failed to lower voltage: %d\n", err);
}
if (therm) {
ret = nvkm_therm_cstate(therm, pstate->fanspeed, -1);
if (ret && ret != -ENODEV)
nvkm_error(subdev, "failed to lower fan speed: %d\n", ret);
int err = nvkm_therm_cstate(therm, pstate->fanspeed, -1);
if (err && err != -ENODEV)
nvkm_error(subdev, "failed to lower fan speed: %d\n", err);
}
return ret;
@ -270,13 +272,19 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei)
struct nvkm_fb *fb = subdev->device->fb;
struct nvkm_pci *pci = subdev->device->pci;
struct nvkm_pstate *pstate;
bool found = false;
int ret, idx = 0;
list_for_each_entry(pstate, &clk->states, head) {
if (idx++ == pstatei)
if (idx++ == pstatei) {
found = true;
break;
}
}
if (!found)
return -EINVAL;
nvkm_debug(subdev, "setting performance state %d\n", pstatei);
clk->pstate = pstatei;
@ -473,6 +481,7 @@ static int
nvkm_clk_ustate_update(struct nvkm_clk *clk, int req)
{
struct nvkm_pstate *pstate;
bool found = false;
int i = 0;
if (!clk->allow_reclock)
@ -480,12 +489,14 @@ nvkm_clk_ustate_update(struct nvkm_clk *clk, int req)
if (req != -1 && req != -2) {
list_for_each_entry(pstate, &clk->states, head) {
if (pstate->pstate == req)
if (pstate->pstate == req) {
found = true;
break;
}
i++;
}
if (pstate->pstate != req)
if (!found)
return -EINVAL;
req = i;
}

View File

@ -51,6 +51,8 @@ nv1a_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram)
mib = ((mem >> 4) & 127) + 1;
}
pci_dev_put(bridge);
return nvkm_ram_new_(&nv04_ram_func, fb, NVKM_RAM_TYPE_STOLEN,
mib * 1024 * 1024, pram);
}

View File

@ -20,6 +20,7 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "priv.h"
#include <nvif/cl0080.h>
int
nvkm_gsp_intr_nonstall(struct nvkm_gsp *gsp, enum nvkm_subdev_type type, int inst)
@ -47,6 +48,15 @@ nvkm_gsp_intr_stall(struct nvkm_gsp *gsp, enum nvkm_subdev_type type, int inst)
return -ENOENT;
}
int
nvkm_gsp_gcx_ready(struct nvkm_gsp *gsp)
{
if (!gsp->rm->api->gsp->gcx_ready)
return NV_DEVICE_GC6_READY | NV_DEVICE_GCOFF_READY;
return gsp->rm->api->gsp->gcx_ready(gsp);
}
static int
nvkm_gsp_fini(struct nvkm_subdev *subdev, enum nvkm_suspend_state suspend)
{

View File

@ -93,6 +93,7 @@ void r535_gsp_dtor(struct nvkm_gsp *);
int r535_gsp_oneinit(struct nvkm_gsp *);
int r535_gsp_init(struct nvkm_gsp *);
int r535_gsp_fini(struct nvkm_gsp *, enum nvkm_suspend_state suspend);
int r535_gsp_gcx_ready(struct nvkm_gsp *gsp);
int nvkm_gsp_new_(const struct nvkm_gsp_fwif *, struct nvkm_device *, enum nvkm_subdev_type, int,
struct nvkm_gsp **);

View File

@ -1782,6 +1782,23 @@ r535_gsp_fini(struct nvkm_gsp *gsp, enum nvkm_suspend_state suspend)
return 0;
}
int
r535_gsp_get_static_memsys_info(struct nvkm_gsp *gsp)
{
NV2080_CTRL_INTERNAL_MEMSYS_GET_STATIC_CONFIG_PARAMS *ctrl;
ctrl = nvkm_gsp_rm_ctrl_rd(&gsp->internal.device.subdevice,
NV2080_CTRL_CMD_INTERNAL_MEMSYS_GET_STATIC_CONFIG,
sizeof(*ctrl));
if (IS_ERR(ctrl))
return PTR_ERR(ctrl);
gsp->memsys.use_raw_mode_comptagline_alloc = ctrl->bUseRawModeComptaglineAllocation;
nvkm_gsp_rm_ctrl_done(&gsp->internal.device.subdevice, ctrl);
return 0;
}
int
r535_gsp_init(struct nvkm_gsp *gsp)
{

View File

@ -782,6 +782,51 @@ typedef struct NV2080_CTRL_INTERNAL_INTR_GET_KERNEL_TABLE_PARAMS {
#define GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE ((48 << 10) * 2048) // Support 2048 channels
typedef struct NV2080_CTRL_INTERNAL_MEMSYS_GET_STATIC_CONFIG_PARAMS {
/*! Determines if RM should use 1 to 1 Comptagline allocation policy */
NvBool bOneToOneComptagLineAllocation;
/*! Determines if RM should use 1 to 4 Comptagline allocation policy */
NvBool bUseOneToFourComptagLineAllocation;
/*! Determines if RM should use raw Comptagline allocation policy */
NvBool bUseRawModeComptaglineAllocation;
/*! Has COMPBIT_BACKING_SIZE been overridden to zero (i.e. disabled)? */
NvBool bDisableCompbitBacking;
/*! Determine if we need to disable post L2 compression */
NvBool bDisablePostL2Compression;
/*! Is ECC DRAM feature supported? */
NvBool bEnabledEccFBPA;
NvBool bL2PreFill;
/*! L2 cache size */
NV_DECLARE_ALIGNED(NvU64 l2CacheSize, 8);
/*! Indicate whether fpba is present or not */
NvBool bFbpaPresent;
/*! Size covered by one comptag */
NvU32 comprPageSize;
/*! log32(comprPageSize) */
NvU32 comprPageShift;
/*! RAM type */
NvU32 ramType;
/*! LTC count */
NvU32 ltcCount;
/*! LTS per LTC count */
NvU32 ltsPerLtcCount;
} NV2080_CTRL_INTERNAL_MEMSYS_GET_STATIC_CONFIG_PARAMS;
#define NV2080_CTRL_CMD_INTERNAL_MEMSYS_GET_STATIC_CONFIG (0x20800a1c) /* finn: Evaluated from "(FINN_NV20_SUBDEVICE_0_INTERNAL_INTERFACE_ID << 8) | NV2080_CTRL_INTERNAL_MEMSYS_GET_STATIC_CONFIG_PARAMS_MESSAGE_ID" */
typedef union rpc_message_rpc_union_field_v03_00
{
NvU32 spare;

View File

@ -26,6 +26,52 @@ r570_fbsr_suspend_channels(struct nvkm_gsp *gsp, bool suspend)
return nvkm_gsp_rm_ctrl_wr(&gsp->internal.device.subdevice, ctrl);
}
static int
r570_fb_get_compbit_store_size(struct nvkm_gsp *gsp, u64 *size)
{
NV0080_CTRL_FB_GET_COMPBIT_STORE_INFO_PARAMS *ctrl;
ctrl = nvkm_gsp_rm_ctrl_rd(&gsp->internal.device.object,
NV0080_CTRL_CMD_FB_GET_COMPBIT_STORE_INFO,
sizeof(*ctrl));
if (IS_ERR(ctrl))
return PTR_ERR(ctrl);
*size = ctrl->Size;
nvkm_gsp_rm_ctrl_done(&gsp->internal.device.object, ctrl);
return 0;
}
static int
r570_memsys_enable_raw_comp_mode(struct nvkm_gsp *gsp, bool enable)
{
NV2080_CTRL_INTERNAL_MEMSYS_PROGRAM_RAW_COMPRESSION_MODE_PARAMS *ctrl;
int ret;
ctrl = nvkm_gsp_rm_ctrl_get(&gsp->internal.device.subdevice,
NV2080_CTRL_CMD_INTERNAL_MEMSYS_PROGRAM_RAW_COMPRESSION_MODE,
sizeof(*ctrl));
if (IS_ERR(ctrl))
return PTR_ERR(ctrl);
ctrl->bRawMode = enable;
ret = nvkm_gsp_rm_ctrl_wr(&gsp->internal.device.subdevice, ctrl);
if (!ret)
nvkm_debug(&gsp->subdev, "memsys: Raw compression mode %s\n",
str_enabled_disabled(enable));
return ret;
}
static bool
r570_need_raw_comp_war(struct nvkm_gsp *gsp, struct nvkm_device *device)
{
return (device->card_type == GA100 || device->card_type == AD100) &&
gsp->memsys.use_raw_mode_comptagline_alloc;
}
static void
r570_fbsr_resume(struct nvkm_gsp *gsp)
{
@ -33,6 +79,7 @@ r570_fbsr_resume(struct nvkm_gsp *gsp)
struct nvkm_instmem *imem = device->imem;
struct nvkm_instobj *iobj;
struct nvkm_vmm *vmm;
int ret;
/* Restore BAR2 page tables via BAR0 window, and re-enable BAR2. */
list_for_each_entry(iobj, &imem->boot, head) {
@ -54,6 +101,13 @@ r570_fbsr_resume(struct nvkm_gsp *gsp)
vmm = nvkm_bar_bar1_vmm(device);
vmm->func->flush(vmm, 0);
/* Re-enable raw mode if it was previously disabled */
if (r570_need_raw_comp_war(gsp, device)) {
ret = r570_memsys_enable_raw_comp_mode(gsp, true);
if (ret)
nvkm_error(&gsp->subdev, "Failed to re-enable raw comp mode\n");
}
/* Resume channel scheduling. */
r570_fbsr_suspend_channels(device->gsp, false);
@ -81,7 +135,7 @@ r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size)
ctrl->hClient = gsp->internal.client.object.handle;
ctrl->hSysMem = memlist.handle;
ctrl->sysmemAddrOfSuspendResumeData = gsp->sr.meta.addr;
ctrl->bEnteringGcoffState = 0;
ctrl->bEnteringGcoffState = 1;
ret = nvkm_gsp_rm_ctrl_wr(&gsp->internal.device.subdevice, ctrl);
if (ret)
@ -98,12 +152,29 @@ r570_fbsr_suspend(struct nvkm_gsp *gsp)
struct nvkm_device *device = subdev->device;
struct nvkm_instmem *imem = device->imem;
struct nvkm_instobj *iobj;
u64 size;
u64 size, compbit_store_size;
int ret;
/* Stop channel scheduling. */
r570_fbsr_suspend_channels(gsp, true);
/* Temporarily disable raw mode to prevent FBSR restore operations from corrupting
* compressed surfaces. Required for ampere and ada.
*
* Nvidia bug #3172217
*/
if (r570_need_raw_comp_war(gsp, device)) {
ret = r570_memsys_enable_raw_comp_mode(gsp, false);
if (ret)
return ret;
}
ret = r570_fb_get_compbit_store_size(gsp, &compbit_store_size);
if (ret < 0)
return ret;
nvkm_debug(&gsp->subdev, "fbsr: Compbit backing store size: 0x%llx bytes\n",
compbit_store_size);
/* Save BAR2 allocations to system memory. */
list_for_each_entry(iobj, &imem->list, head) {
if (iobj->preserve) {
@ -126,6 +197,8 @@ r570_fbsr_suspend(struct nvkm_gsp *gsp)
size = gsp->fb.heap.size;
size += gsp->fb.rsvd_size;
size += gsp->fb.bios.vga_workspace.size;
size += compbit_store_size;
nvkm_debug(subdev, "fbsr: size: 0x%llx bytes\n", size);
ret = nvkm_gsp_sg(device, size, &gsp->sr.fbsr);

View File

@ -10,6 +10,7 @@
#include "nvrm/gsp.h"
#include "nvrm/rpcfn.h"
#include "nvrm/msgfn.h"
#include "nvif/cl0080.h"
#include <core/pci.h>
#include <subdev/pci/priv.h>
@ -137,6 +138,14 @@ r570_gsp_get_static_info(struct nvkm_gsp *gsp)
}
}
ret = r535_gsp_get_static_memsys_info(gsp);
if (ret) {
nvkm_error(&gsp->subdev, "Retrieving static memsys info failed\n");
return ret;
}
nvkm_debug(&gsp->subdev, "memsys: Use raw mode for comptag allocations? %s\n",
str_yes_no(gsp->memsys.use_raw_mode_comptagline_alloc));
return 0;
}
@ -215,6 +224,32 @@ r570_gsp_set_rmargs(struct nvkm_gsp *gsp, bool resume)
args->bDmemStack = 1;
}
int
r570_gsp_gcx_ready(struct nvkm_gsp *gsp)
{
NV2080_CTRL_INTERNAL_GCX_ENTRY_PREREQUISITE_PARAMS *ctrl;
int ret = 0;
ctrl = nvkm_gsp_rm_ctrl_rd(&gsp->internal.device.subdevice,
NV2080_CTRL_CMD_INTERNAL_GCX_ENTRY_PREREQUISITE,
sizeof(*ctrl));
if (IS_ERR(ctrl))
return PTR_ERR(ctrl);
if (ctrl->bIsGC6Satisfied)
ret |= NV_DEVICE_GC6_READY;
if (ctrl->bIsGCOFFSatisfied)
ret |= NV_DEVICE_GCOFF_READY;
nvkm_debug(&gsp->subdev,
"GCX ready status: GC6=%s GCOFF=%s\n",
str_yes_no(ctrl->bIsGC6Satisfied), str_yes_no(ctrl->bIsGCOFFSatisfied));
nvkm_gsp_rm_ctrl_done(&gsp->internal.device.subdevice, ctrl);
return ret;
}
const struct nvkm_rm_api_gsp
r570_gsp = {
.set_rmargs = r570_gsp_set_rmargs,
@ -223,4 +258,5 @@ r570_gsp = {
.xlat_mc_engine_idx = r570_gsp_xlat_mc_engine_idx,
.drop_post_nocat_record = r570_gsp_drop_post_nocat_record,
.sr_data_size = r570_gsp_sr_data_size,
.gcx_ready = r570_gsp_gcx_ready,
};

View File

@ -16,4 +16,33 @@ typedef struct NV2080_CTRL_INTERNAL_FBSR_INIT_PARAMS {
NV_DECLARE_ALIGNED(NvU64 sysmemAddrOfSuspendResumeData, 8);
} NV2080_CTRL_INTERNAL_FBSR_INIT_PARAMS;
#define NV0080_CTRL_CMD_FB_GET_COMPBIT_STORE_INFO (0x801306) /* finn: Evaluated from "(FINN_NV01_DEVICE_0_FB_INTERFACE_ID << 8) | NV0080_CTRL_FB_GET_COMPBIT_STORE_INFO_PARAMS_MESSAGE_ID" */
typedef struct NV0080_CTRL_FB_GET_COMPBIT_STORE_INFO_PARAMS {
NV_DECLARE_ALIGNED(NvU64 Size, 8);
NV_DECLARE_ALIGNED(NvU64 Address, 8);
NvU32 AddressSpace;
NvU32 MaxCompbitLine;
NvU32 comptagsPerCacheLine;
NvU32 cacheLineSize;
NvU32 cacheLineSizePerSlice;
NvU32 cacheLineFetchAlignment;
NV_DECLARE_ALIGNED(NvU64 backingStoreBase, 8);
NvU32 gobsPerComptagPerSlice;
NvU32 backingStoreCbcBase;
NvU32 comptaglineAllocationPolicy;
NV_DECLARE_ALIGNED(NvU64 privRegionStartOffset, 8);
NvU32 cbcCoveragePerSlice;
} NV0080_CTRL_FB_GET_COMPBIT_STORE_INFO_PARAMS;
#define NV0080_CTRL_CMD_FB_GET_COMPBIT_STORE_INFO_ADDRESS_SPACE_UNKNOWN 0 // ADDR_UNKNOWN
#define NV0080_CTRL_CMD_FB_GET_COMPBIT_STORE_INFO_ADDRESS_SPACE_SYSMEM 1 // ADDR_SYSMEM
#define NV0080_CTRL_CMD_FB_GET_COMPBIT_STORE_INFO_ADDRESS_SPACE_FBMEM 2 // ADDR_FBMEM
#define NV2080_CTRL_CMD_INTERNAL_MEMSYS_PROGRAM_RAW_COMPRESSION_MODE (0x20800a6f) /* finn: Evaluated from "(FINN_NV20_SUBDEVICE_0_INTERNAL_INTERFACE_ID << 8) | NV2080_CTRL_INTERNAL_MEMSYS_PROGRAM_RAW_COMPRESSION_MODE_PARAMS_MESSAGE_ID" */
typedef struct NV2080_CTRL_INTERNAL_MEMSYS_PROGRAM_RAW_COMPRESSION_MODE_PARAMS {
NvBool bRawMode;
} NV2080_CTRL_INTERNAL_MEMSYS_PROGRAM_RAW_COMPRESSION_MODE_PARAMS;
#endif

View File

@ -639,4 +639,11 @@ typedef struct GSP_FMC_BOOT_PARAMS
} GSP_FMC_BOOT_PARAMS;
#define GSP_FW_HEAP_PARAM_BASE_RM_SIZE_GH100 (14 << 20) // Hopper+
#define NV2080_CTRL_CMD_INTERNAL_GCX_ENTRY_PREREQUISITE (0x2080a7d7)
typedef struct NV2080_CTRL_INTERNAL_GCX_ENTRY_PREREQUISITE_PARAMS {
NvBool bIsGC6Satisfied;
NvBool bIsGCOFFSatisfied;
} NV2080_CTRL_INTERNAL_GCX_ENTRY_PREREQUISITE_PARAMS;
#endif

View File

@ -40,6 +40,7 @@ struct nvkm_rm_api {
void (*drop_send_user_shared_data)(struct nvkm_gsp *);
void (*drop_post_nocat_record)(struct nvkm_gsp *);
u32 (*sr_data_size)(struct nvkm_gsp *);
int (*gcx_ready)(struct nvkm_gsp *gsp);
} *gsp;
const struct nvkm_rm_api_rpc {
@ -174,6 +175,8 @@ int r535_gr_chan_new(struct nvkm_gr *, struct nvkm_chan *, const struct nvkm_ocl
int r535_gr_promote_ctx(struct r535_gr *, bool golden, struct nvkm_vmm *,
struct nvkm_memory **pctxbuf_mem, struct nvkm_vma **pctxbuf_vma,
struct nvkm_gsp_object *chan);
int r570_gsp_gcx_ready(struct nvkm_gsp *gsp);
int r535_gsp_get_static_memsys_info(struct nvkm_gsp *gsp);
extern const struct nvkm_rm_api_engine r535_nvdec;
extern const struct nvkm_rm_api_engine r535_nvenc;
extern const struct nvkm_rm_api_engine r535_nvjpg;

View File

@ -114,6 +114,8 @@ struct virtio_gpu_object {
bool dumb;
bool created;
bool attached;
/* a guest-bound transfer is queued and its mapping not yet synced */
bool from_host_pending;
bool host3d_blob, guest_blob;
uint32_t blob_mem, blob_flags;
@ -196,6 +198,9 @@ struct virtio_gpu_vbuffer {
struct list_head list;
uint32_t seqno;
/* guest-bound transfer whose shmem backing needs a CPU sync */
bool sync_for_cpu;
};
struct virtio_gpu_output {

View File

@ -45,7 +45,7 @@ static int virtio_gpu_gem_create(struct drm_file *file,
ret = drm_gem_handle_create(file, &obj->base.base, &handle);
if (ret) {
drm_gem_object_release(&obj->base.base);
drm_gem_object_put(&obj->base.base);
return ret;
}

View File

@ -185,7 +185,7 @@ static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data,
ret = drm_gem_handle_create(file, obj, &handle);
if (ret) {
drm_gem_object_release(obj);
drm_gem_object_put(obj);
return ret;
}
@ -261,6 +261,27 @@ static int virtio_gpu_transfer_from_host_ioctl(struct drm_device *dev,
if (ret != 0)
goto err_put_free;
if (virtio_gpu_is_shmem(bo) && virtio_gpu_use_dma_api(vgdev->vdev)) {
/*
* The sync on completion restores the whole mapping, so an
* earlier transfer has to be done before this one snapshots it.
* Otherwise the snapshot predates anything the CPU wrote once
* that transfer's fence signalled, and the later sync would
* discard it. Nothing can add a fence behind our back here,
* since doing so takes the reservation we already hold.
* This writes the pages, so it waits as a writer does. READ
* usage covers existing readers.
*/
long wait = dma_resv_wait_timeout(objs->objs[0]->resv,
DMA_RESV_USAGE_READ, true,
MAX_SCHEDULE_TIMEOUT);
if (wait < 0) {
ret = wait;
goto err_unlock;
}
}
fence = virtio_gpu_fence_alloc(vgdev, vgdev->fence_drv.context, 0);
if (!fence) {
ret = -ENOMEM;
@ -320,6 +341,28 @@ static int virtio_gpu_transfer_to_host_ioctl(struct drm_device *dev, void *data,
if (ret != 0)
goto err_put_free;
/*
* A transfer the other way may have queued without yet syncing
* its mapping. Pushing the guest pages into it now would
* discard what the device wrote there, so wait for that sync:
* it runs before the fence it belongs to is signalled. The
* flag is only set under this reservation, so it cannot appear
* behind our back, and the acquire pairs with the release in
* that sync, so finding it clear means the pages it wrote are
* visible here too.
*/
if (smp_load_acquire(&bo->from_host_pending)) {
long wait = dma_resv_wait_timeout(objs->objs[0]->resv,
DMA_RESV_USAGE_WRITE,
true,
MAX_SCHEDULE_TIMEOUT);
if (wait < 0) {
ret = wait;
goto err_unlock;
}
}
ret = -ENOMEM;
fence = virtio_gpu_fence_alloc(vgdev, vgdev->fence_drv.context,
0);
@ -557,14 +600,14 @@ static int virtio_gpu_resource_create_blob_ioctl(struct drm_device *dev,
if (params.blob_flags & VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE) {
ret = virtio_gpu_resource_assign_uuid(vgdev, bo);
if (ret) {
drm_gem_object_release(obj);
drm_gem_object_put(obj);
return ret;
}
}
ret = drm_gem_handle_create(file, obj, &handle);
if (ret) {
drm_gem_object_release(obj);
drm_gem_object_put(obj);
return ret;
}

View File

@ -589,6 +589,7 @@ struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev,
struct drm_plane *plane;
const uint32_t *formats;
int nformats;
int ret;
if (type == DRM_PLANE_TYPE_CURSOR) {
formats = virtio_gpu_cursor_formats;
@ -614,5 +615,17 @@ struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev,
drm_plane_create_blend_mode_property(plane,
BIT(DRM_MODE_BLEND_PREMULTI));
if (type == DRM_PLANE_TYPE_CURSOR) {
/*
* The cursor plane exposes a format with an alpha channel,
* which requires a blend mode property. The host blends
* premultiplied alpha, matching the property's default.
*/
ret = drm_plane_create_blend_mode_property(plane,
BIT(DRM_MODE_BLEND_PREMULTI));
if (ret)
return ERR_PTR(ret);
}
return plane;
}

View File

@ -349,7 +349,7 @@ struct drm_gem_object *virtgpu_gem_prime_import(struct drm_device *dev,
}
}
if (!vgdev->has_resource_blob)
if (!vgdev->has_resource_blob || vgdev->has_virgl_3d)
return drm_gem_prime_import(dev, buf);
bo = kzalloc_obj(*bo);

View File

@ -389,10 +389,13 @@ static int virtio_gpu_init_submit(struct virtio_gpu_submit *submit,
if ((exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) ||
exbuf->num_out_syncobjs ||
exbuf->num_bo_handles ||
drm_fence_event)
drm_fence_event) {
out_fence = virtio_gpu_fence_alloc(vgdev, fence_ctx, ring_idx);
else
if (!out_fence)
return -ENOMEM;
} else {
out_fence = NULL;
}
if (drm_fence_event) {
err = virtio_gpu_fence_event_create(dev, file, out_fence, ring_idx);
@ -538,6 +541,10 @@ int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
virtio_gpu_process_post_deps(&submit);
virtio_gpu_complete_submit(&submit);
cleanup:
if (ret && submit.out_fence && submit.out_fence->e) {
drm_event_cancel_free(dev, &submit.out_fence->e->base);
submit.out_fence->e = NULL;
}
virtio_gpu_cleanup_submit(&submit);
return ret;

View File

@ -256,6 +256,33 @@ void virtio_gpu_dequeue_ctrl_func(struct work_struct *work)
} while (!virtqueue_enable_cb(vgdev->ctrlq.vq));
spin_unlock(&vgdev->ctrlq.qlock);
/*
* Sync guest-bound transfers before signalling anything, so that a
* waiter cannot read the backing pages while what the device wrote is
* still in a bounce buffer. This cannot be folded into the loop below:
* virtio_gpu_fence_event_process() also signals every earlier fence in
* the same context, so any entry there may signal this entry's fence.
*/
list_for_each_entry(entry, &reclaim_list, list) {
if (entry->sync_for_cpu) {
struct virtio_gpu_object *bo =
gem_to_virtio_gpu_obj(entry->objs->objs[0]);
dma_sync_sgtable_for_cpu(vgdev->vdev->dev.parent,
bo->base.sgt, DMA_FROM_DEVICE);
/*
* Release, so a transfer the other way that skips its
* wait on the strength of this cannot go on to read
* the backing pages before the sync above is visible.
* Nothing orders the two otherwise: where the mapping
* bounces on a coherent device the sync is a plain
* copy, and dma_direct_sync_sg_for_cpu() emits its
* barrier only for the non-coherent case.
*/
smp_store_release(&bo->from_host_pending, false);
}
}
list_for_each_entry(entry, &reclaim_list, list) {
resp = (struct virtio_gpu_ctrl_hdr *)entry->resp_buf;
@ -1278,12 +1305,31 @@ void virtio_gpu_cmd_transfer_from_host_3d(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]);
struct virtio_gpu_transfer_host_3d *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
bool use_dma_api = virtio_gpu_use_dma_api(vgdev->vdev);
cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
memset(cmd_p, 0, sizeof(*cmd_p));
vbuf->objs = objs;
if (virtio_gpu_is_shmem(bo) && use_dma_api) {
/*
* The device writes only the requested box, so prime the
* mapping with the current contents: otherwise the sync on
* completion would hand back whatever a bounce buffer held for
* the regions the device does not touch.
*/
dma_sync_sgtable_for_device(vgdev->vdev->dev.parent,
bo->base.sgt, DMA_TO_DEVICE);
vbuf->sync_for_cpu = true;
/*
* Set under the reservation the caller holds, so a transfer
* the other way cannot miss it and push the guest pages into
* the mapping while the device still owns it.
*/
WRITE_ONCE(bo->from_host_pending, true);
}
cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D);
cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id);
cmd_p->resource_id = cpu_to_le32(bo->hw_res_handle);

View File

@ -215,16 +215,12 @@ int virtio_gpu_vram_create(struct virtio_gpu_device *vgdev,
/* Create fake offset */
ret = drm_gem_create_mmap_offset(obj);
if (ret) {
kfree(vram);
return ret;
}
if (ret)
goto err_release_obj;
ret = virtio_gpu_resource_id_get(vgdev, &vram->base.hw_res_handle);
if (ret) {
kfree(vram);
return ret;
}
if (ret)
goto err_release_obj;
virtio_gpu_cmd_resource_create_blob(vgdev, &vram->base, params, NULL,
0);
@ -240,6 +236,11 @@ int virtio_gpu_vram_create(struct virtio_gpu_device *vgdev,
*bo_ptr = &vram->base;
return 0;
err_release_obj:
drm_gem_object_release(obj);
kfree(vram);
return ret;
}
void virtio_gpu_vram_map_deferred(struct virtio_gpu_object_vram *vram)