Two ttm fixes for ttm_tt_swapout(), one page-alignment and one overflow

fix for dma-buf, a drm_pending_vblank_event leak fix for drm,
 suspend/resume fixes for nouveau, one out-of-bounds access fix for gud,
 a use-after-free fix for vc4, a fence signaling fix, a race condition
 fix for sched, planes formats fixes for verisilicon, and add the blend
 mode property for loongson
 -----BEGIN PGP SIGNATURE-----
 
 iJUEABMJAB0WIQTkHFbLp4ejekA/qfgnX84Zoj2+dgUCaqvVAAAKCRAnX84Zoj2+
 dtThAX9JC7SWXw+n4o9EUsxNqvlpviwe8AS7aJR++rq/sZM0an7zl0aCJu/E8p+/
 JRkO+X8BfjE+2CX8RWKH63ed95vA+9DF8JfryBTSJiSz4forppFfwH7uovT3nqq2
 eOon6nJQzg==
 =Utup
 -----END PGP SIGNATURE-----

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

Two ttm fixes for ttm_tt_swapout(), one page-alignment and one overflow
fix for dma-buf, a drm_pending_vblank_event leak fix for drm,
suspend/resume fixes for nouveau, one out-of-bounds access fix for gud,
a use-after-free fix for vc4, a fence signaling fix, a race condition
fix for sched, planes formats fixes for verisilicon, and add the blend
mode property for loongson

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

From: Maxime Ripard <self@mripard.dev>
Link: https://patch.msgid.link/aqvVENQ4ksJEIcdb@houat
This commit is contained in:
Dave Airlie 2026-09-19 06:48:28 +10:00
commit 71f370e9ee
21 changed files with 110 additions and 85 deletions

View File

@ -43,7 +43,7 @@ config UDMABUF
config DMABUF_DEBUG
bool "DMA-BUF debug checks"
depends on DMA_SHARED_BUFFER
default y if DEBUG
default y if DEBUG_KERNEL
help
This option enables additional checks for DMA-BUF importers and
exporters. Specifically it validates that importers do not peek at the

View File

@ -5,16 +5,18 @@
*/
#include <linux/dma-buf-mapping.h>
#include <linux/dma-resv.h>
#include <linux/overflow.h>
#include <linux/align.h>
#define MAX_SG_ENT_SZ ALIGN_DOWN(UINT_MAX, PAGE_SIZE)
static struct scatterlist *fill_sg_entry(struct scatterlist *sgl, size_t length,
dma_addr_t addr)
{
unsigned int len, nents;
int i;
size_t len;
nents = DIV_ROUND_UP(length, UINT_MAX);
for (i = 0; i < nents; i++) {
len = min_t(size_t, length, UINT_MAX);
while (length) {
len = min(length, MAX_SG_ENT_SZ);
length -= len;
/*
* DMABUF abuses scatterlist to create a scatterlist
@ -24,8 +26,10 @@ static struct scatterlist *fill_sg_entry(struct scatterlist *sgl, size_t length,
* does not require the CPU list for mapping or unmapping.
*/
sg_set_page(sgl, NULL, 0, 0);
sg_dma_address(sgl) = addr + (dma_addr_t)i * UINT_MAX;
sg_dma_address(sgl) = addr;
sg_dma_len(sgl) = len;
addr += len;
/* Unconditionally advance. On last segment, this becomes NULL */
sgl = sg_next(sgl);
}
@ -40,15 +44,19 @@ static unsigned int calc_sg_nents(struct dma_iova_state *state,
size_t i;
if (!state || !dma_use_iova(state)) {
for (i = 0; i < nr_ranges; i++)
nents += DIV_ROUND_UP(phys_vec[i].len, UINT_MAX);
for (i = 0; i < nr_ranges; i++) {
unsigned int added = DIV_ROUND_UP(phys_vec[i].len, MAX_SG_ENT_SZ);
if (check_add_overflow(nents, added, &nents))
return 0;
}
} else {
/*
* In IOVA case, there is only one SG entry which spans
* for whole IOVA address space, but we need to make sure
* that it fits sg->length, maybe we need more.
*/
nents = DIV_ROUND_UP(size, UINT_MAX);
nents = DIV_ROUND_UP(size, MAX_SG_ENT_SZ);
}
return nents;
@ -95,9 +103,10 @@ struct sg_table *dma_buf_phys_vec_to_sgt(struct dma_buf_attachment *attach,
size_t nr_ranges, size_t size,
enum dma_data_direction dir)
{
unsigned int nents, mapped_len = 0;
struct dma_buf_dma *dma;
struct scatterlist *sgl;
size_t mapped_len = 0;
unsigned int nents;
dma_addr_t addr;
size_t i;
int ret;
@ -133,6 +142,8 @@ struct sg_table *dma_buf_phys_vec_to_sgt(struct dma_buf_attachment *attach,
}
nents = calc_sg_nents(dma->state, phys_vec, nr_ranges, size);
/* sg_alloc_table will cleanly fail and return -EINVAL if nents == 0 */
ret = sg_alloc_table(&dma->sgt, nents, GFP_KERNEL | __GFP_ZERO);
if (ret)
goto err_free_state;

View File

@ -1170,7 +1170,12 @@ const char __rcu *dma_fence_driver_name(struct dma_fence *fence)
/* RCU protection is required for safe access to returned string */
ops = rcu_dereference(fence->ops);
if (ops)
/*
* Make load ordering irrelevant by checking both signaled state and ops
* pointer and ops pointer is only set to NULL on newer implementations.
*/
if (!dma_fence_test_signaled_flag(fence) && ops)
return (const char __rcu *)ops->get_driver_name(fence);
else
return (const char __rcu *)"detached-driver";
@ -1203,7 +1208,12 @@ const char __rcu *dma_fence_timeline_name(struct dma_fence *fence)
/* RCU protection is required for safe access to returned string */
ops = rcu_dereference(fence->ops);
if (ops)
/*
* Make load ordering irrelevant by checking both signaled state and ops
* pointer and ops pointer is only set to NULL on newer implementations.
*/
if (!dma_fence_test_signaled_flag(fence) && ops)
return (const char __rcu *)ops->get_timeline_name(fence);
else
return (const char __rcu *)"signaled-timeline";

View File

@ -1462,10 +1462,12 @@ static int prepare_signaling(struct drm_device *dev,
struct dma_fence *fence;
struct drm_out_fence_state *f;
ret = -ENOMEM;
f = krealloc(*fence_state, sizeof(**fence_state) *
(*num_fences + 1), GFP_KERNEL);
if (!f)
return -ENOMEM;
goto err_free_event;
memset(&f[*num_fences], 0, sizeof(*f));
@ -1474,12 +1476,12 @@ static int prepare_signaling(struct drm_device *dev,
fence = drm_crtc_create_fence(crtc);
if (!fence)
return -ENOMEM;
goto err_free_event;
ret = setup_out_fence(&f[(*num_fences)++], fence);
if (ret) {
dma_fence_put(fence);
return ret;
goto err_free_event;
}
crtc_state->event->base.fence = fence;
@ -1535,6 +1537,11 @@ static int prepare_signaling(struct drm_device *dev,
}
return 0;
err_free_event:
drm_event_cancel_free(dev, &crtc_state->event->base);
crtc_state->event = NULL;
return ret;
}
static void complete_signaling(struct drm_device *dev,

View File

@ -482,6 +482,9 @@ int gud_plane_atomic_check(struct drm_plane *plane,
if (!new_plane_state->visible)
return 0;
if (gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE)
new_plane_state->ignore_damage_clips = true;
if (old_plane_state->rotation != new_plane_state->rotation)
crtc_state->mode_changed = true;
@ -562,8 +565,8 @@ int gud_plane_atomic_check(struct drm_plane *plane,
goto out;
}
req->properties[num_properties + i].prop = cpu_to_le16(prop);
req->properties[num_properties + i].val = cpu_to_le64(val);
req->properties[num_properties].prop = cpu_to_le16(prop);
req->properties[num_properties].val = cpu_to_le64(val);
num_properties++;
}

View File

@ -7,6 +7,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_blend.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_print.h>
@ -765,7 +766,7 @@ int ls7a1000_cursor_plane_init(struct drm_device *ddev,
drm_plane_helper_add(plane, &ls7a1000_cursor_plane_helper_funcs);
return 0;
return drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_COVERAGE));
}
int ls7a2000_cursor_plane_init(struct drm_device *ddev,
@ -790,5 +791,5 @@ int ls7a2000_cursor_plane_init(struct drm_device *ddev,
drm_plane_helper_add(plane, &ls7a2000_cursor_plane_helper_funcs);
return 0;
return drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_COVERAGE));
}

View File

@ -208,7 +208,7 @@ r535_fbsr_resume(struct nvkm_gsp *gsp)
}
static int
r535_fbsr_suspend(struct nvkm_gsp *gsp, bool runtime)
r535_fbsr_suspend(struct nvkm_gsp *gsp)
{
struct nvkm_subdev *subdev = &gsp->subdev;
struct nvkm_device *device = subdev->device;

View File

@ -1749,7 +1749,7 @@ r535_gsp_fini(struct nvkm_gsp *gsp, enum nvkm_suspend_state suspend)
sr->sysmemAddrOfSuspendResumeData = gsp->sr.radix3.lvl0.addr;
sr->sizeOfSuspendResumeData = len;
ret = rm->api->fbsr->suspend(gsp, suspend == NVKM_RUNTIME_SUSPEND);
ret = rm->api->fbsr->suspend(gsp);
if (ret) {
nvkm_gsp_mem_dtor(&gsp->sr.meta);
nvkm_gsp_radix3_dtor(gsp, &gsp->sr.radix3);
@ -1761,8 +1761,12 @@ r535_gsp_fini(struct nvkm_gsp *gsp, enum nvkm_suspend_state suspend)
* TODO: Debug the GSP firmware / RPC handling to find out why
* without this Turing (but none of the other architectures)
* ends up resetting all channels after resume.
* Additionally, runtime suspend on other architectures quickly
* becomes unreliable without this sleep. If you're experiencing
* issues with runtime suspend, try bumping this delay up and
* sending a patch if it fixes your GPU.
*/
msleep(50);
msleep(200);
}
ret = r535_gsp_rpc_unloading_guest_driver(gsp, suspend);

View File

@ -62,7 +62,7 @@ r570_fbsr_resume(struct nvkm_gsp *gsp)
}
static int
r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size, bool runtime)
r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size)
{
NV2080_CTRL_INTERNAL_FBSR_INIT_PARAMS *ctrl;
struct nvkm_gsp_object memlist;
@ -81,7 +81,7 @@ r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size, bool runtim
ctrl->hClient = gsp->internal.client.object.handle;
ctrl->hSysMem = memlist.handle;
ctrl->sysmemAddrOfSuspendResumeData = gsp->sr.meta.addr;
ctrl->bEnteringGcoffState = runtime ? 1 : 0;
ctrl->bEnteringGcoffState = 0;
ret = nvkm_gsp_rm_ctrl_wr(&gsp->internal.device.subdevice, ctrl);
if (ret)
@ -92,7 +92,7 @@ r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size, bool runtim
}
static int
r570_fbsr_suspend(struct nvkm_gsp *gsp, bool runtime)
r570_fbsr_suspend(struct nvkm_gsp *gsp)
{
struct nvkm_subdev *subdev = &gsp->subdev;
struct nvkm_device *device = subdev->device;
@ -133,7 +133,7 @@ r570_fbsr_suspend(struct nvkm_gsp *gsp, bool runtime)
return ret;
/* Initialise FBSR on RM. */
ret = r570_fbsr_init(gsp, &gsp->sr.fbsr, size, runtime);
ret = r570_fbsr_init(gsp, &gsp->sr.fbsr, size);
if (ret) {
nvkm_gsp_sg_free(device, &gsp->sr.fbsr);
return ret;

View File

@ -207,7 +207,8 @@ r570_gsp_set_rmargs(struct nvkm_gsp *gsp, bool resume)
args->srInitArguments.bInPMTransition = 0;
} else {
args->srInitArguments.oldLevel = NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_3;
args->srInitArguments.flags = 0;
args->srInitArguments.flags =
GPU_STATE_FLAGS_PRESERVING | GPU_STATE_FLAGS_PM_TRANSITION;
args->srInitArguments.bInPMTransition = 1;
}

View File

@ -523,6 +523,14 @@ typedef struct
#define NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_3 (0x00000003U)
#define GPU_STATE_FLAGS_PRESERVING BIT(0) // GPU state is preserved
#define GPU_STATE_FLAGS_VGA_TRANSITION BIT(1) // To be used with GPU_STATE_FLAGS_PRESERVING.
#define GPU_STATE_FLAGS_PM_TRANSITION BIT(2) // To be used with GPU_STATE_FLAGS_PRESERVING.
#define GPU_STATE_FLAGS_PM_SUSPEND BIT(3)
#define GPU_STATE_FLAGS_PM_HIBERNATE BIT(4)
#define GPU_STATE_FLAGS_GC6_TRANSITION BIT(5) // To be used with GPU_STATE_FLAGS_PRESERVING.
#define GPU_STATE_FLAGS_FAST_UNLOAD BIT(6) // Used during windows restart, skips stateDestroy steps
typedef struct
{
// Magic for verification by secure ucode

View File

@ -79,7 +79,7 @@ struct nvkm_rm_api {
} *device;
const struct nvkm_rm_api_fbsr {
int (*suspend)(struct nvkm_gsp *, bool runtime);
int (*suspend)(struct nvkm_gsp *);
void (*resume)(struct nvkm_gsp *);
} *fbsr;

View File

@ -559,9 +559,10 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity)
*/
smp_wmb();
spin_lock(&entity->lock);
spsc_queue_pop(&entity->job_queue);
drm_sched_rq_pop_entity(entity);
spin_unlock(&entity->lock);
/* Jobs and entities might have different lifecycles. Since we're
* removing the job from the entities queue, set the jobs entity pointer
@ -647,6 +648,9 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job)
* Make sure to set the submit_ts first, to avoid a race.
*/
sched_job->submit_ts = submit_ts = ktime_get();
spin_lock(&entity->lock);
first = spsc_queue_push(&entity->job_queue, &sched_job->queue_node);
/* first job wakes up scheduler */
@ -657,5 +661,7 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job)
if (sched)
drm_sched_wakeup(sched);
}
spin_unlock(&entity->lock);
}
EXPORT_SYMBOL(drm_sched_entity_push_job);

View File

@ -257,19 +257,17 @@ static ktime_t drm_sched_entity_get_job_ts(struct drm_sched_entity *entity)
struct drm_gpu_scheduler *
drm_sched_rq_add_entity(struct drm_sched_entity *entity, ktime_t ts)
{
struct drm_sched_rq *rq = entity->rq;
struct drm_gpu_scheduler *sched;
struct drm_sched_rq *rq;
/* Add the entity to the run queue */
spin_lock(&entity->lock);
if (entity->stopped) {
spin_unlock(&entity->lock);
lockdep_assert_held(&entity->lock);
if (entity->stopped) {
DRM_ERROR("Trying to push to a killed entity\n");
return NULL;
}
rq = entity->rq;
spin_lock(&rq->lock);
sched = rq->sched;
@ -289,7 +287,6 @@ drm_sched_rq_add_entity(struct drm_sched_entity *entity, ktime_t ts)
drm_sched_rq_update_fifo_locked(entity, rq, ts);
spin_unlock(&rq->lock);
spin_unlock(&entity->lock);
return sched;
}
@ -343,16 +340,17 @@ drm_sched_rq_next_rr_ts(struct drm_sched_rq *rq,
*/
void drm_sched_rq_pop_entity(struct drm_sched_entity *entity)
{
struct drm_sched_rq *rq = entity->rq;
struct drm_sched_job *next_job;
struct drm_sched_rq *rq;
lockdep_assert_held(&entity->lock);
spin_lock(&rq->lock);
/*
* Update the entity's location in the min heap according to
* the timestamp of the next job, if any.
*/
spin_lock(&entity->lock);
rq = entity->rq;
spin_lock(&rq->lock);
next_job = drm_sched_entity_queue_peek(entity);
if (next_job) {
ktime_t ts;
@ -375,8 +373,8 @@ void drm_sched_rq_pop_entity(struct drm_sched_entity *entity)
drm_sched_entity_save_vruntime(entity, min_vruntime);
}
}
spin_unlock(&rq->lock);
spin_unlock(&entity->lock);
}
/**

View File

@ -1434,7 +1434,7 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo)
if (ttm_tt_is_populated(tt)) {
ret = ttm_tt_swapout(bdev, tt, swapout_walk->gfp_flags);
if (!ret) {
if (ret > 0) {
spin_lock(&bdev->lru_lock);
ttm_resource_del_bulk_move_unevictable(bo->resource, bo);
ttm_resource_move_to_lru_tail(bo->resource);

View File

@ -1188,7 +1188,7 @@ int vc4_kms_load(struct drm_device *dev)
drm_mode_config_reset(dev);
drm_kms_helper_poll_init(dev);
drmm_kms_helper_poll_init(dev);
return 0;
}

View File

@ -11,6 +11,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_blend.h>
#include <drm/drm_crtc.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
@ -267,6 +268,7 @@ struct drm_plane *vs_cursor_plane_init(struct drm_device *drm_dev,
return plane;
drm_plane_helper_add(plane, &vs_cursor_plane_helper_funcs);
drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_COVERAGE));
return plane;
}

View File

@ -10,82 +10,50 @@
#include "vs_dc_top_regs.h"
#include "vs_hwdb.h"
static const u32 vs_formats_array_no_yuv444[] = {
static const u32 vs_primary_formats_array_no_yuv444[] = {
DRM_FORMAT_XRGB4444,
DRM_FORMAT_XBGR4444,
DRM_FORMAT_RGBX4444,
DRM_FORMAT_BGRX4444,
DRM_FORMAT_ARGB4444,
DRM_FORMAT_ABGR4444,
DRM_FORMAT_RGBA4444,
DRM_FORMAT_BGRA4444,
DRM_FORMAT_XRGB1555,
DRM_FORMAT_XBGR1555,
DRM_FORMAT_RGBX5551,
DRM_FORMAT_BGRX5551,
DRM_FORMAT_ARGB1555,
DRM_FORMAT_ABGR1555,
DRM_FORMAT_RGBA5551,
DRM_FORMAT_BGRA5551,
DRM_FORMAT_RGB565,
DRM_FORMAT_BGR565,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_RGBX8888,
DRM_FORMAT_BGRX8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_RGBA8888,
DRM_FORMAT_BGRA8888,
DRM_FORMAT_ARGB2101010,
DRM_FORMAT_ABGR2101010,
DRM_FORMAT_RGBA1010102,
DRM_FORMAT_BGRA1010102,
/* TODO: non-RGB formats */
};
static const u32 vs_formats_array_with_yuv444[] = {
static const u32 vs_primary_formats_array_with_yuv444[] = {
DRM_FORMAT_XRGB4444,
DRM_FORMAT_XBGR4444,
DRM_FORMAT_RGBX4444,
DRM_FORMAT_BGRX4444,
DRM_FORMAT_ARGB4444,
DRM_FORMAT_ABGR4444,
DRM_FORMAT_RGBA4444,
DRM_FORMAT_BGRA4444,
DRM_FORMAT_XRGB1555,
DRM_FORMAT_XBGR1555,
DRM_FORMAT_RGBX5551,
DRM_FORMAT_BGRX5551,
DRM_FORMAT_ARGB1555,
DRM_FORMAT_ABGR1555,
DRM_FORMAT_RGBA5551,
DRM_FORMAT_BGRA5551,
DRM_FORMAT_RGB565,
DRM_FORMAT_BGR565,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_RGBX8888,
DRM_FORMAT_BGRX8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_RGBA8888,
DRM_FORMAT_BGRA8888,
DRM_FORMAT_ARGB2101010,
DRM_FORMAT_ABGR2101010,
DRM_FORMAT_RGBA1010102,
DRM_FORMAT_BGRA1010102,
/* TODO: non-RGB formats */
};
static const struct vs_formats vs_formats_no_yuv444 = {
.array = vs_formats_array_no_yuv444,
.num = ARRAY_SIZE(vs_formats_array_no_yuv444)
.primary_array = vs_primary_formats_array_no_yuv444,
.primary_num = ARRAY_SIZE(vs_primary_formats_array_no_yuv444)
};
static const struct vs_formats vs_formats_with_yuv444 = {
.array = vs_formats_array_with_yuv444,
.num = ARRAY_SIZE(vs_formats_array_with_yuv444)
.primary_array = vs_primary_formats_array_with_yuv444,
.primary_num = ARRAY_SIZE(vs_primary_formats_array_with_yuv444)
};
static struct vs_chip_identity vs_chip_identities[] = {

View File

@ -10,8 +10,8 @@
#include <linux/types.h>
struct vs_formats {
const u32 *array;
unsigned int num;
const u32 *primary_array;
unsigned int primary_num;
};
struct vs_chip_identity {

View File

@ -168,8 +168,8 @@ struct drm_plane *vs_primary_plane_init(struct drm_device *drm_dev, struct vs_dc
plane = drmm_universal_plane_alloc(drm_dev, struct drm_plane, dev, 0,
&vs_primary_plane_funcs,
dc->identity.formats->array,
dc->identity.formats->num,
dc->identity.formats->primary_array,
dc->identity.formats->primary_num,
NULL,
DRM_PLANE_TYPE_PRIMARY,
NULL);

View File

@ -141,6 +141,9 @@ struct dma_fence_ops {
* compute the name at runtime, without having it to store permanently
* for each fence, or build a cache of some sort.
*
* The returned string is RCU protected and can be freed after the fence
* signaled and a RCU grace period passed.
*
* This callback is mandatory.
*/
const char * (*get_driver_name)(struct dma_fence *fence);
@ -153,6 +156,9 @@ struct dma_fence_ops {
* having it to store permanently for each fence, or build a cache of
* some sort.
*
* The returned string is RCU protected and can be freed after the fence
* signaled and a RCU grace period passed.
*
* This callback is mandatory.
*/
const char * (*get_timeline_name)(struct dma_fence *fence);