drm-misc-fixes for v7.2-rc5:

- Improve damage handling in appletbdrm.
 - Fix harmful fragmenting of MM by backing up TTM pages at native
   page order.
 - Fix timeout handling in amdxdna.
 - Fix imagination locking for map/unmap operations.
 - Fix mm leak in gpusvm eviction.
 - Properly zero page array in gpusvm mm scanning.
 - Prevent trusted shader bo's from being mapped again in vc4.
 - Validate shader array size in vmwgfx.
 - Fix length calculation bugs in ethosu.
 - Better error handling during pagemap migration.
 - Improve v3d suspend.
 - Kconfig updates for some panels.
 - Handle missing iovcc in ili9881c panel.
 - Fix vc4 unbind.
 - Add i2c error handling in gma500.
 - Fix kunit tests on pp64le and s390x.
 - Prevent rearming vc4 timer on shutdown.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEuXvWqAysSYEJGuVH/lWMcqZwE8MFAmpjDjgACgkQ/lWMcqZw
 E8NoIQ/+PaoFa25I+9e+RgtgjYWLKpYoRdDHi7u7zUPzr5Z7XzAGPUYG0lYc6Gxw
 zQicvLcMCiBW8+mwTBytLESIXn5SfjFP0KPYhCnyXSnDRdHiWj+nlXD1A0mG5YQ+
 cFDYyyXS2Ka1TsdPbXr6f3zyNDSuhmRJbo6qGvEYR+KimyHMU8zqXPD6F4GazHd2
 ocQrGzOP3608oJHRiGA5zs5IfupbO5uvQE9oJJ/mpdYxhd4B5hIK7t+8BfkqGJ16
 4v1A5Bb6Gjw9tWZvzEcyIZ/wECrJ7Wgj5Jf87aTWh23Ufdmno3k884ezkYYyTAof
 CJEQ6Rr1kuhbDDGfekzW+5/Z55OJfWDhwhBh9I1897Y9JWPGAArB6zJj1iRaGRJ2
 ictGAWqpQXq8rXqnYsKPmI610Ck1f1aQrC8KEEPHLqW/EfwzLz68YsS9qAnll1Z8
 1rgstWDAFouBeBoW1YNVrM5CtlKzLTrCUa2PVeqaFbxtPEoMzXPZfuP9bUr0AJG7
 uXbPpxoWtzL4BnsDoi1lpZbr02APcuwXC2n76JDj0wRZj+l6RWWWhDlEDHXMwb5s
 OTdtxSgYjiHCargTaL7i0gOFcHScZ901OcSdeLcUnwNDG6T4yLK1EhnX2SKE2lak
 xi0U5oDsnYdJ+WnHmlb1CIbUKRQWZjNENdRSKd48/OhLDaT4YQU=
 =cog2
 -----END PGP SIGNATURE-----

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

drm-misc-fixes for v7.2-rc5:
- Improve damage handling in appletbdrm.
- Fix harmful fragmenting of MM by backing up TTM pages at native
  page order.
- Fix timeout handling in amdxdna.
- Fix imagination locking for map/unmap operations.
- Fix mm leak in gpusvm eviction.
- Properly zero page array in gpusvm mm scanning.
- Prevent trusted shader bo's from being mapped again in vc4.
- Validate shader array size in vmwgfx.
- Fix length calculation bugs in ethosu.
- Better error handling during pagemap migration.
- Improve v3d suspend.
- Kconfig updates for some panels.
- Handle missing iovcc in ili9881c panel.
- Fix vc4 unbind.
- Add i2c error handling in gma500.
- Fix kunit tests on pp64le and s390x.
- Prevent rearming vc4 timer on shutdown.

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

From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patch.msgid.link/07284633-6b9b-40f9-8949-b1516a42a34c@linux.intel.com
This commit is contained in:
Dave Airlie 2026-07-24 18:30:23 +10:00
commit e24b02df4e
24 changed files with 355 additions and 148 deletions

View File

@ -540,6 +540,7 @@ Maciej W. Rozycki <macro@mips.com> <macro@imgtec.com>
Maciej W. Rozycki <macro@orcam.me.uk> <macro@linux-mips.org>
Maharaja Kennadyrajan <quic_mkenna@quicinc.com> <mkenna@codeaurora.org>
Maheshwar Ajja <quic_majja@quicinc.com> <majja@codeaurora.org>
Maíra Canal <mairacanal@riseup.net> <maira.canal@usp.br>
Malathi Gottam <quic_mgottam@quicinc.com> <mgottam@codeaurora.org>
Manikanta Pubbisetty <quic_mpubbise@quicinc.com> <mpubbise@codeaurora.org>
Manivannan Sadhasivam <mani@kernel.org> <manivannanece23@gmail.com>

View File

@ -43,20 +43,22 @@ struct aie2_ctx_health {
static inline void aie2_tdr_signal(struct amdxdna_dev *xdna)
{
WRITE_ONCE(xdna->dev_handle->tdr_status, AIE2_TDR_SIGNALED);
WRITE_ONCE(xdna->dev_handle->last_signal_ts, jiffies);
}
static bool aie2_tdr_detect(struct amdxdna_dev *xdna)
{
struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
unsigned long last = READ_ONCE(ndev->last_signal_ts);
if (READ_ONCE(ndev->tdr_status) == AIE2_TDR_WAIT) {
XDNA_ERR(xdna, "TDR timeout detected");
return true;
}
if (!tdr_timeout_ms)
return false;
WRITE_ONCE(ndev->tdr_status, AIE2_TDR_WAIT);
return false;
if (!time_after(jiffies, last + msecs_to_jiffies(tdr_timeout_ms)))
return false;
XDNA_ERR(xdna, "TDR timeout detected");
return true;
}
static void aie2_cmd_release(struct kref *ref)
@ -434,6 +436,12 @@ aie2_sched_job_run(struct drm_sched_job *sched_job)
mmput(job->mm);
fence = ERR_PTR(ret);
} else {
/*
* Command is successfully posted to hardware, update the
* tdr timestamp. The total pending commands are limited.
* So there will not be a case that driver keeps posting
* commands without getting any hardware respond.
*/
aie2_tdr_signal(hwctx->client->xdna);
}
trace_xdna_job(sched_job, hwctx->name, "sent to device",
@ -658,7 +666,9 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
const struct drm_sched_init_args args = {
.ops = &sched_ops,
.credit_limit = HWCTX_MAX_CMDS,
.timeout = msecs_to_jiffies(tdr_timeout_ms),
.timeout = tdr_timeout_ms ?
msecs_to_jiffies(tdr_timeout_ms) :
MAX_SCHEDULE_TIMEOUT,
.name = "amdxdna_js",
.dev = xdna->ddev.dev,
};

View File

@ -420,6 +420,7 @@ static int aie2_hw_start(struct amdxdna_dev *xdna)
goto stop_fw;
}
WRITE_ONCE(ndev->last_signal_ts, jiffies);
ndev->dev_status = AIE2_DEV_START;
return 0;

View File

@ -143,11 +143,6 @@ struct aie2_exec_msg_ops {
u32 (*get_chain_msg_op)(u32 cmd_op);
};
enum aie2_tdr_status {
AIE2_TDR_WAIT,
AIE2_TDR_SIGNALED,
};
struct amdxdna_dev_hdl {
struct aie_device aie;
const struct amdxdna_dev_priv *priv;
@ -179,7 +174,7 @@ struct amdxdna_dev_hdl {
u32 hwctx_num;
struct amdxdna_async_error last_async_err;
enum aie2_tdr_status tdr_status;
unsigned long last_signal_ts;
};
struct aie2_hw_ops {

View File

@ -192,9 +192,17 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info,
return len;
}
static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
static bool feat_matrix_chained(struct ethosu_device *edev, struct feat_matrix *fm)
{
u32 storage = fm->precision >> 14;
return !ethosu_is_u65(edev) && storage == 2;
}
static u64 feat_matrix_length(struct ethosu_device *edev,
struct ethosu_validated_cmdstream_info *info,
struct feat_matrix *fm,
u32 x, u32 y, u32 c)
u32 x, u32 y, u32 c, bool ofm)
{
u32 element_size, storage = fm->precision >> 14;
int tile = 0;
@ -203,6 +211,9 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
if (fm->region < 0)
return U64_MAX;
if (feat_matrix_chained(edev, fm))
return 0;
switch (storage) {
case 0:
if (x >= fm->width0 + 1) {
@ -223,6 +234,8 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
tile = 1;
}
break;
default:
return U64_MAX;
}
if (fm->base[tile] == U64_MAX)
return U64_MAX;
@ -231,10 +244,11 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
switch ((fm->precision >> 6) & 0x3) { // format
case 0: //nhwc:
addr += x * fm->stride_x + c;
element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3);
addr += x * fm->stride_x + c * element_size;
break;
case 1: //nhcwb16:
element_size = BIT((fm->precision >> 1) & 0x3);
element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3);
addr += (c / 16) * fm->stride_c + (16 * x + (c & 0xf)) * element_size;
break;
@ -250,6 +264,7 @@ static int calc_sizes(struct drm_device *ddev,
u16 op, struct cmd_state *st,
bool ifm, bool ifm2, bool weight, bool scale)
{
struct ethosu_device *edev = to_ethosu_device(ddev);
u64 len;
if (ifm) {
@ -267,8 +282,8 @@ static int calc_sizes(struct drm_device *ddev,
if (ifm_height < 0 || ifm_width < 0)
return -EINVAL;
len = feat_matrix_length(info, &st->ifm, ifm_width,
ifm_height, st->ifm.depth);
len = feat_matrix_length(edev, info, &st->ifm, ifm_width,
ifm_height, st->ifm.depth, false);
dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
op, st->ifm.region, st->ifm.base[0], len);
if (len == U64_MAX)
@ -276,8 +291,8 @@ static int calc_sizes(struct drm_device *ddev,
}
if (ifm2) {
len = feat_matrix_length(info, &st->ifm2, st->ifm.depth,
0, st->ofm.depth);
len = feat_matrix_length(edev, info, &st->ifm2, st->ifm.depth,
0, st->ofm.depth, false);
dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
op, st->ifm2.region, st->ifm2.base[0], len);
if (len == U64_MAX)
@ -308,13 +323,14 @@ static int calc_sizes(struct drm_device *ddev,
st->scale[0].base + st->scale[0].length);
}
len = feat_matrix_length(info, &st->ofm, st->ofm.width,
st->ofm.height[2], st->ofm.depth);
len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width,
st->ofm.height[2], st->ofm.depth, true);
dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
op, st->ofm.region, st->ofm.base[0], len);
if (len == U64_MAX)
return -EINVAL;
info->output_region[st->ofm.region] = true;
if (!feat_matrix_chained(edev, &st->ofm))
info->output_region[st->ofm.region] = true;
return 0;
}
@ -324,6 +340,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
u16 op, struct cmd_state *st,
bool ifm, bool ifm2)
{
struct ethosu_device *edev = to_ethosu_device(ddev);
u32 height, width, depth;
u64 len;
@ -332,8 +349,8 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
width = st->ifm.broadcast & 0x2 ? 0 : st->ofm.width;
depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth;
len = feat_matrix_length(info, &st->ifm, width,
height, depth);
len = feat_matrix_length(edev, info, &st->ifm, width,
height, depth, false);
dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
op, st->ifm.region, st->ifm.base[0], len);
if (len == U64_MAX)
@ -345,21 +362,22 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
width = st->ifm2.broadcast & 0x2 ? 0 : st->ofm.width;
depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth;
len = feat_matrix_length(info, &st->ifm2, width,
height, depth);
len = feat_matrix_length(edev, info, &st->ifm2, width,
height, depth, false);
dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
op, st->ifm2.region, st->ifm2.base[0], len);
if (len == U64_MAX)
return -EINVAL;
}
len = feat_matrix_length(info, &st->ofm, st->ofm.width,
st->ofm.height[2], st->ofm.depth);
len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width,
st->ofm.height[2], st->ofm.depth, true);
dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
op, st->ofm.region, st->ofm.base[0], len);
if (len == U64_MAX)
return -EINVAL;
info->output_region[st->ofm.region] = true;
if (!feat_matrix_chained(edev, &st->ofm))
info->output_region[st->ofm.region] = true;
return 0;
}

View File

@ -781,7 +781,7 @@ enum drm_gpusvm_scan_result drm_gpusvm_scan_mm(struct drm_gpusvm_range *range,
const struct dev_pagemap *other = NULL;
int err, i;
pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL);
pfns = kvcalloc(npages, sizeof(*pfns), GFP_KERNEL);
if (!pfns)
return DRM_GPUSVM_SCAN_UNPOPULATED;
@ -1753,8 +1753,10 @@ int drm_gpusvm_range_evict(struct drm_gpusvm *gpusvm,
return -EFAULT;
pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL);
if (!pfns)
if (!pfns) {
mmput(mm);
return -ENOMEM;
}
hmm_range.hmm_pfns = pfns;
while (!time_after(jiffies, timeout)) {

View File

@ -727,8 +727,10 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,
}
err = ops->populate_devmem_pfn(devmem_allocation, npages, migrate.dst);
if (err)
goto err_aborted_migration;
if (err) {
npages = 0;
goto err_finalize;
}
own_pages = 0;
@ -807,8 +809,11 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,
msecs_to_jiffies(mdetails->timeslice_ms);
err_finalize:
if (err)
if (err) {
drm_pagemap_migration_unlock_put_pages(npages, migrate.dst);
for (i = npages; i < npages_in_range(start, end); ++i)
migrate.dst[i] = 0;
}
err_aborted_migration:
migrate_vma_pages(&migrate);

View File

@ -98,6 +98,7 @@ static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg)
struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap);
struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
u32 temp;
int ret;
i2c_dev->status = I2C_STAT_INIT;
i2c_dev->msg = pmsg;
@ -109,9 +110,14 @@ static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg)
HDMI_WRITE(HDMI_HI2CHCR, temp);
HDMI_READ(HDMI_HI2CHCR);
while (i2c_dev->status != I2C_TRANSACTION_DONE)
wait_for_completion_interruptible_timeout(&i2c_dev->complete,
while (i2c_dev->status != I2C_TRANSACTION_DONE) {
ret = wait_for_completion_interruptible_timeout(&i2c_dev->complete,
10 * HZ);
if (ret < 0)
return ret;
if (!ret)
return -ETIMEDOUT;
}
return 0;
}
@ -130,7 +136,7 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap,
{
struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap);
struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
int i;
int i, ret = 0;
mutex_lock(&i2c_dev->i2c_lock);
@ -142,9 +148,11 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap,
for (i = 0; i < num; i++) {
if (pmsg->len && pmsg->buf) {
if (pmsg->flags & I2C_M_RD)
xfer_read(adap, pmsg);
ret = xfer_read(adap, pmsg);
else
xfer_write(adap, pmsg);
ret = xfer_write(adap, pmsg);
if (ret)
break;
}
pmsg++; /* next message */
}
@ -154,6 +162,9 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap,
mutex_unlock(&i2c_dev->i2c_lock);
if (ret)
return ret;
return i;
}

View File

@ -747,6 +747,7 @@ pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj,
pvr_gem_object_get(pvr_obj);
mutex_lock(&vm_ctx->lock);
err = drm_gpuvm_exec_lock(&vm_exec);
if (err)
goto err_cleanup;
@ -756,6 +757,7 @@ pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj,
drm_gpuvm_exec_unlock(&vm_exec);
err_cleanup:
mutex_unlock(&vm_ctx->lock);
pvr_vm_bind_op_fini(&bind_op);
return err;

View File

@ -232,6 +232,7 @@ config DRM_PANEL_HIMAX_HX83121A
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
select DRM_DISPLAY_HELPER
select DRM_DISPLAY_DSC_HELPER
select DRM_KMS_HELPER
help
@ -332,6 +333,7 @@ config DRM_PANEL_ILITEK_ILI9882T
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
select DRM_DISPLAY_HELPER
select DRM_DISPLAY_DSC_HELPER
help
Say Y if you want to enable support for panels based on the
@ -954,6 +956,7 @@ config DRM_PANEL_SAMSUNG_S6E3HA8
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
select DRM_DISPLAY_HELPER
select DRM_DISPLAY_DSC_HELPER
help
Say Y or M here if you want to enable support for the

View File

@ -2562,9 +2562,12 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi)
"Couldn't get our power regulator\n");
ctx->iovcc = devm_regulator_get_optional(&dsi->dev, "iovcc");
if (IS_ERR(ctx->iovcc))
return dev_err_probe(&dsi->dev, PTR_ERR(ctx->iovcc),
if (IS_ERR(ctx->iovcc)) {
if (PTR_ERR(ctx->iovcc) != -ENODEV)
return dev_err_probe(&dsi->dev, PTR_ERR(ctx->iovcc),
"Couldn't get our iovcc regulator\n");
ctx->iovcc = NULL;
}
ctx->reset = devm_gpiod_get_optional(&dsi->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ctx->reset))

View File

@ -95,13 +95,9 @@ static void drm_gem_shmem_test_obj_create_private(struct kunit *test)
sg_init_one(sgt->sgl, buf, TEST_SIZE);
/*
* Set the DMA mask to 64-bits and map the sgtables
* otherwise drm_gem_shmem_free will cause a warning
* on debug kernels.
* Map the sgtables otherwise drm_gem_shmem_free will cause a warning on
* debug kernels.
*/
ret = dma_set_mask(drm_dev->dev, DMA_BIT_MASK(64));
KUNIT_ASSERT_EQ(test, ret, 0);
ret = dma_map_sgtable(drm_dev->dev, sgt, DMA_BIDIRECTIONAL, 0);
KUNIT_ASSERT_EQ(test, ret, 0);
@ -352,11 +348,19 @@ static int drm_gem_shmem_test_init(struct kunit *test)
{
struct device *dev;
struct drm_device *drm_dev;
int ret;
/* Allocate a parent device */
dev = drm_kunit_helper_alloc_device(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
/*
* Set the DMA mask to 64-bits to avoid intermittent failures calling
* drm_gem_shmem_get_pages_sgt().
*/
ret = dma_set_mask(dev, DMA_BIT_MASK(64));
KUNIT_ASSERT_EQ(test, ret, 0);
/*
* The DRM core will automatically initialize the GEM core and create
* a DRM Memory Manager object which provides an address space pool

View File

@ -315,33 +315,16 @@ static const u32 appletbdrm_primary_plane_formats[] = {
DRM_FORMAT_XRGB8888, /* emulated */
};
static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane,
struct drm_atomic_commit *state)
static int appletbdrm_primary_plane_helper_begin_fb_access(struct drm_plane *plane,
struct drm_plane_state *new_plane_state)
{
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_crtc *new_crtc = new_plane_state->crtc;
struct drm_crtc_state *new_crtc_state = NULL;
struct appletbdrm_plane_state *appletbdrm_state = to_appletbdrm_plane_state(new_plane_state);
size_t frames_size = 0;
struct drm_atomic_helper_damage_iter iter;
struct drm_rect damage;
size_t frames_size = 0;
size_t request_size;
int ret;
if (new_crtc)
new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
DRM_PLANE_NO_SCALING,
DRM_PLANE_NO_SCALING,
false, false);
if (ret)
return ret;
else if (!new_plane_state->visible)
return 0;
drm_atomic_helper_damage_iter_init(&iter, old_plane_state, new_plane_state);
drm_atomic_helper_damage_iter_init(&iter, plane->state, new_plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
frames_size += struct_size((struct appletbdrm_frame *)0, buf, rect_size(&damage));
}
@ -366,6 +349,29 @@ static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane,
appletbdrm_state->request_size = request_size;
appletbdrm_state->frames_size = frames_size;
return drm_gem_begin_shadow_fb_access(plane, new_plane_state);
}
static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane,
struct drm_atomic_commit *state)
{
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
struct drm_crtc *new_crtc = new_plane_state->crtc;
struct drm_crtc_state *new_crtc_state = NULL;
int ret;
if (new_crtc)
new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
DRM_PLANE_NO_SCALING,
DRM_PLANE_NO_SCALING,
false, false);
if (ret)
return ret;
else if (!new_plane_state->visible)
return 0;
return 0;
}
@ -468,7 +474,7 @@ static int appletbdrm_flush_damage(struct appletbdrm_device *adev,
}
static void appletbdrm_primary_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_atomic_commit *old_state)
struct drm_atomic_commit *old_state)
{
struct appletbdrm_device *adev = drm_to_adev(plane->dev);
struct drm_device *drm = plane->dev;
@ -552,7 +558,8 @@ static void appletbdrm_primary_plane_destroy_state(struct drm_plane *plane,
}
static const struct drm_plane_helper_funcs appletbdrm_primary_plane_helper_funcs = {
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
.begin_fb_access = appletbdrm_primary_plane_helper_begin_fb_access,
.end_fb_access = drm_gem_end_shadow_fb_access,
.atomic_check = appletbdrm_primary_plane_helper_atomic_check,
.atomic_update = appletbdrm_primary_plane_helper_atomic_update,
.atomic_disable = appletbdrm_primary_plane_helper_atomic_disable,

View File

@ -6,9 +6,10 @@
#include <drm/ttm/ttm_backup.h>
#include <linux/export.h>
#include <linux/page-flags.h>
#include <linux/swap.h>
#include "ttm_pool_internal.h"
/*
* Need to map shmem indices to handle since a handle value
* of 0 means error, following the swp_entry_t convention.
@ -68,17 +69,23 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst,
}
/**
* ttm_backup_backup_page() - Backup a page
* ttm_backup_backup_folio() - Backup a folio
* @backup: The struct backup pointer to use.
* @page: The page to back up.
* @writeback: Whether to perform immediate writeback of the page.
* @folio: The folio to back up.
* @order: The allocation order of @folio. Since TTM allocates higher-order
* pages without __GFP_COMP, folio_nr_pages(@folio) would always
* return 1; the caller must pass the true order explicitly.
* @writeback: Whether to perform immediate writeback of the folio's pages.
* This may have performance implications.
* @idx: A unique integer for each page and each struct backup.
* @idx: A unique integer for the first page of the folio and each struct backup.
* This allows the backup implementation to avoid managing
* its address space separately.
* @page_gfp: The gfp value used when the page was allocated.
* This is used for accounting purposes.
* @folio_gfp: The gfp value used when the folio was allocated.
* Currently unused.
* @alloc_gfp: The gfp to be used when allocating memory.
* @nr_pages_backed: Output. On a successful return, set to the number of
* pages actually backed up, which may be less than (1 << @order)
* if an -ENOMEM was encountered mid-folio.
*
* Context: If called from reclaim context, the caller needs to
* assert that the shrinker gfp has __GFP_FS set, to avoid
@ -87,53 +94,87 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst,
* that the shrinker gfp has __GFP_IO set, since without it,
* we're not allowed to start backup IO.
*
* Return: A handle on success. Negative error code on failure.
*
* Note: This function could be extended to back up a folio and
* implementations would then split the folio internally if needed.
* Drawback is that the caller would then have to keep track of
* the folio size- and usage.
* Return: A handle for the first backed-up page on success (handles for
* subsequent pages follow sequentially). -ENOMEM if no pages could be backed
* up. Any other negative error code if a non-ENOMEM failure occurred; in that
* case any pages backed up so far are truncated before returning.
*/
s64
ttm_backup_backup_page(struct file *backup, struct page *page,
bool writeback, pgoff_t idx, gfp_t page_gfp,
gfp_t alloc_gfp)
ttm_backup_backup_folio(struct file *backup, struct folio *folio,
unsigned int order, bool writeback, pgoff_t idx,
gfp_t folio_gfp, gfp_t alloc_gfp,
pgoff_t *nr_pages_backed)
{
struct address_space *mapping = backup->f_mapping;
unsigned long handle = 0;
int nr_pages = 1 << order;
struct folio *to_folio;
int ret;
int ret, i;
to_folio = shmem_read_folio_gfp(mapping, idx, alloc_gfp);
if (IS_ERR(to_folio))
return PTR_ERR(to_folio);
*nr_pages_backed = 0;
folio_mark_accessed(to_folio);
folio_lock(to_folio);
folio_mark_dirty(to_folio);
copy_highpage(folio_file_page(to_folio, idx), page);
handle = ttm_backup_shmem_idx_to_handle(idx);
for (i = 0; i < nr_pages; ) {
int to_nr, j;
if (writeback && !folio_mapped(to_folio) &&
folio_clear_dirty_for_io(to_folio)) {
folio_set_reclaim(to_folio);
ret = shmem_writeout(to_folio, NULL, NULL);
if (!folio_test_writeback(to_folio))
folio_clear_reclaim(to_folio);
/*
* If writeout succeeds, it unlocks the folio. errors
* are otherwise dropped, since writeout is only best
* effort here.
* Only inject past the first subpage so *nr_pages_backed is
* always > 0 here, matching a genuine mid-compound -ENOMEM
* and driving the caller's reactive split fallback instead
* of an early, no-progress failure.
*/
if (ret)
if (IS_ENABLED(CONFIG_FAULT_INJECTION) && i &&
ttm_backup_fault_inject_folio())
to_folio = ERR_PTR(-ENOMEM);
else
to_folio = shmem_read_folio_gfp(mapping, idx + i, alloc_gfp);
if (IS_ERR(to_folio)) {
int err = PTR_ERR(to_folio);
if (err == -ENOMEM && *nr_pages_backed)
return ttm_backup_shmem_idx_to_handle(idx);
if (*nr_pages_backed) {
shmem_truncate_range(file_inode(backup),
(loff_t)idx << PAGE_SHIFT,
((loff_t)(idx + i) << PAGE_SHIFT) - 1);
/*
* The pages just truncated are no longer
* backed up; don't let the caller mistake
* them for valid handles.
*/
*nr_pages_backed = 0;
}
return err;
}
to_nr = min_t(int, nr_pages - i,
folio_next_index(to_folio) - (idx + i));
folio_mark_accessed(to_folio);
folio_lock(to_folio);
folio_mark_dirty(to_folio);
for (j = 0; j < to_nr; j++)
copy_highpage(folio_file_page(to_folio, idx + i + j),
folio_page(folio, i + j));
if (writeback && !folio_mapped(to_folio) &&
folio_clear_dirty_for_io(to_folio)) {
folio_set_reclaim(to_folio);
ret = shmem_writeout(to_folio, NULL, NULL);
if (!folio_test_writeback(to_folio))
folio_clear_reclaim(to_folio);
if (ret == AOP_WRITEPAGE_ACTIVATE)
folio_unlock(to_folio);
} else {
folio_unlock(to_folio);
} else {
folio_unlock(to_folio);
}
folio_put(to_folio);
i += to_nr;
*nr_pages_backed = i;
}
folio_put(to_folio);
return handle;
return ttm_backup_shmem_idx_to_handle(idx);
}
/**

View File

@ -53,8 +53,23 @@
#ifdef CONFIG_FAULT_INJECTION
#include <linux/fault-inject.h>
static DECLARE_FAULT_ATTR(backup_fault_inject);
/*
* Exposed to ttm_backup.c so a mid-compound subpage can be made to fail
* with -ENOMEM, exercising the reactive split-and-retry fallback in
* ttm_pool_backup() for high-order backups.
*/
bool ttm_backup_fault_inject_folio(void)
{
return should_fail(&backup_fault_inject, 1);
}
#else
#define should_fail(...) false
bool ttm_backup_fault_inject_folio(void)
{
return false;
}
#endif
/**
@ -487,7 +502,7 @@ static void ttm_pool_split_for_swap(struct ttm_pool *pool, struct page *p)
/**
* DOC: Partial backup and restoration of a struct ttm_tt.
*
* Swapout using ttm_backup_backup_page() and swapin using
* Swapout using ttm_backup_backup_folio() and swapin using
* ttm_backup_copy_page() may fail.
* The former most likely due to lack of swap-space or memory, the latter due
* to lack of memory or because of signal interruption during waits.
@ -1036,12 +1051,12 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
{
struct file *backup = tt->backup;
struct page *page;
unsigned long handle;
gfp_t alloc_gfp;
gfp_t gfp;
int ret = 0;
pgoff_t shrunken = 0;
pgoff_t i, num_pages;
pgoff_t i, j, num_pages, npages;
pgoff_t nr_backed;
if (WARN_ON(ttm_tt_is_backed_up(tt)))
return -EINVAL;
@ -1119,9 +1134,11 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
if (IS_ENABLED(CONFIG_FAULT_INJECTION) && should_fail(&backup_fault_inject, 1))
num_pages = DIV_ROUND_UP(num_pages, 2);
for (i = 0; i < num_pages; ++i) {
s64 shandle;
for (i = 0; i < num_pages; i += npages) {
unsigned int order;
s64 handle;
npages = 1;
page = tt->pages[i];
if (unlikely(!page))
continue;
@ -1130,19 +1147,61 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
if (unlikely(ttm_backup_page_ptr_is_handle(page)))
continue;
ttm_pool_split_for_swap(pool, page);
order = ttm_pool_page_order(pool, page);
npages = 1UL << order;
shandle = ttm_backup_backup_page(backup, page, flags->writeback, i,
gfp, alloc_gfp);
if (shandle < 0) {
/* We allow partially shrunken tts */
ret = shandle;
/*
* We don't allow dipping kernel reserves for high order backup
*/
if (order)
alloc_gfp |= __GFP_NOMEMALLOC;
else
alloc_gfp &= ~__GFP_NOMEMALLOC;
/*
* Back up the compound atomically at its native order. If
* fault injection truncated num_pages mid-compound, skip
* the partial tail rather than splitting.
*/
if (unlikely(i + npages > num_pages))
break;
handle = ttm_backup_backup_folio(backup, page_folio(page),
order, flags->writeback, i,
gfp, alloc_gfp,
&nr_backed);
/*
* Zero progress on this compound (whether order 0 or a
* high-order compound that failed before backing up even
* its first subpage) is unrecoverable: bail out rather than
* looping forever with npages == nr_backed == 0 below.
*/
if (unlikely(handle < 0 && !nr_backed)) {
ret = handle;
break;
}
handle = shandle;
tt->pages[i] = ttm_backup_handle_to_page_ptr(handle);
__free_pages_gpu_account(page, 0, false);
shrunken++;
for (j = 0; j < nr_backed; j++)
tt->pages[i + j] = ttm_backup_handle_to_page_ptr(handle + j);
shrunken += nr_backed;
if (unlikely(nr_backed < npages)) {
/*
* Partial OOM backup: split the compound and free the
* subpages whose content is now in shmem. Continue the
* loop from the first un-backed order-0 page.
*/
ttm_pool_split_for_swap(pool, page);
for (j = 0; j < nr_backed; j++)
__free_pages_gpu_account(page + j, 0, false);
npages = nr_backed;
continue;
}
/* Fully backed up: free at native order. */
page->private = 0;
__free_pages_gpu_account(page, order, false);
}
return shrunken ? shrunken : ret;

View File

@ -22,4 +22,12 @@ static inline unsigned int ttm_pool_beneficial_order(struct ttm_pool *pool)
return pool->alloc_flags & 0xff;
}
/*
* Implemented in ttm_pool.c, used by ttm_backup.c. Returns true if a fault
* should be injected mid-compound to test the reactive split-and-retry
* fallback in ttm_pool_backup(). Always returns false when
* CONFIG_FAULT_INJECTION is disabled.
*/
bool ttm_backup_fault_inject_folio(void);
#endif

View File

@ -571,6 +571,8 @@ extern bool super_pages;
void v3d_init_hw_state(struct v3d_dev *v3d);
int v3d_gem_init(struct drm_device *dev);
void v3d_gem_destroy(struct drm_device *dev);
void v3d_idle_axi(struct v3d_dev *v3d, int core);
void v3d_idle_gca(struct v3d_dev *v3d);
void v3d_reset_sms(struct v3d_dev *v3d);
void v3d_reset(struct v3d_dev *v3d);
void v3d_invalidate_caches(struct v3d_dev *v3d);

View File

@ -36,9 +36,21 @@ v3d_init_core(struct v3d_dev *v3d, int core)
V3D_CORE_WRITE(core, V3D_CTL_L2TFLEND, ~0);
}
static void
void
v3d_idle_axi(struct v3d_dev *v3d, int core)
{
if (v3d->ver >= V3D_GEN_71) {
V3D_WRITE(V3D_GMP_CFG(v3d->ver), V3D_GMP_CFG_STOP_REQ);
if (wait_for((V3D_READ(V3D_GMP_STATUS(v3d->ver)) &
(V3D_GMP_STATUS_RD_COUNT_MASK |
V3D_GMP_STATUS_WR_COUNT_MASK |
V3D_GMP_STATUS_CFG_BUSY)) == 0, 100)) {
drm_err(&v3d->drm, "Failed to wait for safe GMP shutdown\n");
}
return;
}
V3D_CORE_WRITE(core, V3D_GMP_CFG(v3d->ver), V3D_GMP_CFG_STOP_REQ);
if (wait_for((V3D_CORE_READ(core, V3D_GMP_STATUS(v3d->ver)) &
@ -49,7 +61,7 @@ v3d_idle_axi(struct v3d_dev *v3d, int core)
}
}
static void
void
v3d_idle_gca(struct v3d_dev *v3d)
{
if (v3d->ver >= V3D_GEN_41)

View File

@ -54,8 +54,15 @@ int v3d_power_suspend(struct device *dev)
v3d_clean_caches(v3d);
/* Wait until V3D has no active or pending AXI transactions. */
v3d_idle_axi(v3d, 0);
v3d_idle_gca(v3d);
ret = v3d_suspend_sms(v3d);
if (ret) {
/* Staying active: undo the GMP STOP_REQ from v3d_idle_axi(). */
V3D_WRITE(V3D_GMP_CFG(v3d->ver),
V3D_READ(V3D_GMP_CFG(v3d->ver)) & ~V3D_GMP_CFG_STOP_REQ);
v3d_irq_enable(v3d);
return ret;
}

View File

@ -732,9 +732,13 @@ static int vc4_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct
{
struct vc4_bo *bo = to_vc4_bo(obj);
if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) {
DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n");
return -EINVAL;
if (bo->validated_shader) {
if (vma->vm_flags & VM_WRITE) {
DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n");
return -EINVAL;
}
vm_flags_clear(vma, VM_MAYWRITE);
}
mutex_lock(&bo->madv_lock);
@ -1044,7 +1048,7 @@ static void vc4_bo_cache_destroy(struct drm_device *dev, void *unused)
struct vc4_dev *vc4 = to_vc4_dev(dev);
int i;
timer_delete(&vc4->bo_cache.time_timer);
timer_shutdown_sync(&vc4->bo_cache.time_timer);
cancel_work_sync(&vc4->bo_cache.time_work);
vc4_bo_cache_purge(dev);

View File

@ -1752,7 +1752,7 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
static void vc4_hvs_unbind(struct device *dev, struct device *master,
void *data)
{
struct drm_device *drm = dev_get_drvdata(master);
struct drm_device *drm = data;
struct vc4_dev *vc4 = to_vc4_dev(drm);
struct vc4_hvs *hvs = vc4->hvs;
struct drm_mm_node *node, *next;

View File

@ -494,7 +494,7 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data)
static void vc4_v3d_unbind(struct device *dev, struct device *master,
void *data)
{
struct drm_device *drm = dev_get_drvdata(master);
struct drm_device *drm = data;
struct vc4_dev *vc4 = to_vc4_dev(drm);
vc4_irq_uninstall(drm);

View File

@ -78,7 +78,7 @@ static int vmw_gb_surface_unbind(struct vmw_resource *res,
static int vmw_gb_surface_destroy(struct vmw_resource *res);
static int
vmw_gb_surface_define_internal(struct drm_device *dev,
struct drm_vmw_gb_surface_create_ext_req *req,
const struct drm_vmw_gb_surface_create_ext_req *req,
struct drm_vmw_gb_surface_create_rep *rep,
struct drm_file *file_priv);
static int
@ -1503,7 +1503,7 @@ int vmw_gb_surface_reference_ext_ioctl(struct drm_device *dev, void *data,
*/
static int
vmw_gb_surface_define_internal(struct drm_device *dev,
struct drm_vmw_gb_surface_create_ext_req *req,
const struct drm_vmw_gb_surface_create_ext_req *req,
struct drm_vmw_gb_surface_create_rep *rep,
struct drm_file *file_priv)
{
@ -1521,9 +1521,21 @@ vmw_gb_surface_define_internal(struct drm_device *dev,
req->base.svga3d_flags);
/* array_size must be null for non-GL3 host. */
if (req->base.array_size > 0 && !has_sm4_context(dev_priv)) {
VMW_DEBUG_USER("SM4 surface not supported.\n");
return -EINVAL;
if (req->base.array_size > 0) {
if (has_sm5_context(dev_priv)) {
if (req->base.array_size > SVGA3D_SM5_MAX_SURFACE_ARRAYSIZE) {
VMW_DEBUG_USER("Invalid Surface Array Size.\n");
return -EINVAL;
}
} else if (has_sm4_context(dev_priv)) {
if (req->base.array_size > SVGA3D_SM4_MAX_SURFACE_ARRAYSIZE) {
VMW_DEBUG_USER("Invalid Surface Array Size.\n");
return -EINVAL;
}
} else {
VMW_DEBUG_USER("SM4+ surface not supported.\n");
return -EINVAL;
}
}
if (!has_sm4_1_context(dev_priv)) {

View File

@ -13,9 +13,8 @@
* ttm_backup_handle_to_page_ptr() - Convert handle to struct page pointer
* @handle: The handle to convert.
*
* Converts an opaque handle received from the
* ttm_backup_backup_page() function to an (invalid)
* struct page pointer suitable for a struct page array.
* Converts an opaque handle received from a ttm_backup_backup_*()
* function to an (invalid) struct page pointer suitable for a struct page array.
*
* Return: An (invalid) struct page pointer.
*/
@ -59,9 +58,10 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst,
pgoff_t handle, bool intr, gfp_t additional_gfp);
s64
ttm_backup_backup_page(struct file *backup, struct page *page,
bool writeback, pgoff_t idx, gfp_t page_gfp,
gfp_t alloc_gfp);
ttm_backup_backup_folio(struct file *backup, struct folio *folio,
unsigned int order, bool writeback, pgoff_t idx,
gfp_t folio_gfp, gfp_t alloc_gfp,
pgoff_t *nr_pages_backed);
void ttm_backup_fini(struct file *backup);