[PATCH 05/59] drm/amd/display: Test colorop error paths
Alex Hung <[email protected]>
| Newsgroups | org.freedesktop.lists.amd-gfx |
|---|---|
| Message-ID | <[email protected]> |
[WHAT] Add KUnit tests for the failure paths of amdgpu_dm_build_default_pipeline(): a failed allocation and a failed initialization at each of the eight pipeline steps. [HOW] Add struct amdgpu_dm_colorop_kunit_ops, a KUnit-only indirection table for the colorop allocation and the five DRM colorop constructors. The constructors cannot be made to fail through their arguments, so tests install a fake allocator and spies that delegate to the real constructors and override the return value of one selected call. Non-KUnit builds call the same functions directly. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha <[email protected]> Signed-off-by: Alex Hung <[email protected]> --- .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 110 ++++++--- .../amd/display/amdgpu_dm/amdgpu_dm_colorop.h | 28 +++ .../amdgpu_dm/tests/amdgpu_dm_colorop_test.c | 232 ++++++++++++++++-- 3 files changed, 312 insertions(+), 58 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c index fae9007b9998..d7260518a23e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c @@ -63,6 +63,48 @@ static const struct drm_colorop_funcs dm_colorop_funcs = { .destroy = drm_colorop_destroy, }; +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +static struct drm_colorop *amdgpu_dm_colorop_kzalloc(void) +{ + return kzalloc_obj(struct drm_colorop); +} + +static const struct amdgpu_dm_colorop_kunit_ops amdgpu_dm_colorop_default_ops = { + .colorop_kzalloc_obj = amdgpu_dm_colorop_kzalloc, + .curve_1d_init = drm_plane_colorop_curve_1d_init, + .curve_1d_lut_init = drm_plane_colorop_curve_1d_lut_init, + .ctm_3x4_init = drm_plane_colorop_ctm_3x4_init, + .mult_init = drm_plane_colorop_mult_init, + .lut3d_init = drm_plane_colorop_3dlut_init, +}; + +static const struct amdgpu_dm_colorop_kunit_ops *amdgpu_dm_colorop_ops = + &amdgpu_dm_colorop_default_ops; + +void amdgpu_dm_colorop_kunit_set_ops(const struct amdgpu_dm_colorop_kunit_ops *ops) +{ + amdgpu_dm_colorop_ops = ops ? ops : &amdgpu_dm_colorop_default_ops; +} +EXPORT_IF_KUNIT(amdgpu_dm_colorop_kunit_set_ops); + +#define colorop_kzalloc_obj amdgpu_dm_colorop_ops->colorop_kzalloc_obj +#define colorop_curve_1d_init amdgpu_dm_colorop_ops->curve_1d_init +#define colorop_curve_1d_lut_init amdgpu_dm_colorop_ops->curve_1d_lut_init +#define colorop_ctm_3x4_init amdgpu_dm_colorop_ops->ctm_3x4_init +#define colorop_mult_init amdgpu_dm_colorop_ops->mult_init +#define colorop_3dlut_init amdgpu_dm_colorop_ops->lut3d_init + +#else + +#define colorop_kzalloc_obj() kzalloc_obj(struct drm_colorop) +#define colorop_curve_1d_init drm_plane_colorop_curve_1d_init +#define colorop_curve_1d_lut_init drm_plane_colorop_curve_1d_lut_init +#define colorop_ctm_3x4_init drm_plane_colorop_ctm_3x4_init +#define colorop_mult_init drm_plane_colorop_mult_init +#define colorop_3dlut_init drm_plane_colorop_3dlut_init + +#endif + STATIC_IFN_KUNIT int amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane, bool hw_3d_lut, struct drm_prop_enum_list *list) @@ -74,15 +116,15 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane memset(ops, 0, sizeof(ops)); /* 1D curve - DEGAM TF */ - ops[i] = kzalloc_obj(*ops[0]); + ops[i] = colorop_kzalloc_obj(); if (!ops[i]) { ret = -ENOMEM; goto cleanup; } - ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane, &dm_colorop_funcs, - amdgpu_dm_supported_degam_tfs, - DRM_COLOROP_FLAG_ALLOW_BYPASS); + ret = colorop_curve_1d_init(dev, ops[i], plane, &dm_colorop_funcs, + amdgpu_dm_supported_degam_tfs, + DRM_COLOROP_FLAG_ALLOW_BYPASS); if (ret) goto cleanup; @@ -91,14 +133,14 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane i++; /* Multiplier */ - ops[i] = kzalloc_obj(struct drm_colorop); + ops[i] = colorop_kzalloc_obj(); if (!ops[i]) { ret = -ENOMEM; goto cleanup; } - ret = drm_plane_colorop_mult_init(dev, ops[i], plane, &dm_colorop_funcs, - DRM_COLOROP_FLAG_ALLOW_BYPASS); + ret = colorop_mult_init(dev, ops[i], plane, &dm_colorop_funcs, + DRM_COLOROP_FLAG_ALLOW_BYPASS); if (ret) goto cleanup; @@ -107,15 +149,14 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane i++; /* 3x4 matrix */ - ops[i] = kzalloc_obj(struct drm_colorop); + ops[i] = colorop_kzalloc_obj(); if (!ops[i]) { ret = -ENOMEM; goto cleanup; } - ret = drm_plane_colorop_ctm_3x4_init(dev, ops[i], plane, - &dm_colorop_funcs, - DRM_COLOROP_FLAG_ALLOW_BYPASS); + ret = colorop_ctm_3x4_init(dev, ops[i], plane, &dm_colorop_funcs, + DRM_COLOROP_FLAG_ALLOW_BYPASS); if (ret) goto cleanup; @@ -125,15 +166,15 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane if (hw_3d_lut) { /* 1D curve - SHAPER TF */ - ops[i] = kzalloc_obj(*ops[0]); + ops[i] = colorop_kzalloc_obj(); if (!ops[i]) { ret = -ENOMEM; goto cleanup; } - ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane, &dm_colorop_funcs, - amdgpu_dm_supported_shaper_tfs, - DRM_COLOROP_FLAG_ALLOW_BYPASS); + ret = colorop_curve_1d_init(dev, ops[i], plane, &dm_colorop_funcs, + amdgpu_dm_supported_shaper_tfs, + DRM_COLOROP_FLAG_ALLOW_BYPASS); if (ret) goto cleanup; @@ -142,17 +183,16 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane i++; /* 1D LUT - SHAPER LUT */ - ops[i] = kzalloc_obj(*ops[0]); + ops[i] = colorop_kzalloc_obj(); if (!ops[i]) { ret = -ENOMEM; goto cleanup; } - ret = drm_plane_colorop_curve_1d_lut_init(dev, ops[i], plane, - &dm_colorop_funcs, - MAX_COLOR_LUT_ENTRIES, - DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR, - DRM_COLOROP_FLAG_ALLOW_BYPASS); + ret = colorop_curve_1d_lut_init(dev, ops[i], plane, &dm_colorop_funcs, + MAX_COLOR_LUT_ENTRIES, + DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR, + DRM_COLOROP_FLAG_ALLOW_BYPASS); if (ret) goto cleanup; @@ -161,16 +201,16 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane i++; /* 3D LUT */ - ops[i] = kzalloc_obj(*ops[0]); + ops[i] = colorop_kzalloc_obj(); if (!ops[i]) { ret = -ENOMEM; goto cleanup; } - ret = drm_plane_colorop_3dlut_init(dev, ops[i], plane, - &dm_colorop_funcs, LUT3D_SIZE, - DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL, - DRM_COLOROP_FLAG_ALLOW_BYPASS); + ret = colorop_3dlut_init(dev, ops[i], plane, &dm_colorop_funcs, + LUT3D_SIZE, + DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL, + DRM_COLOROP_FLAG_ALLOW_BYPASS); if (ret) goto cleanup; @@ -180,15 +220,15 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane } /* 1D curve - BLND TF */ - ops[i] = kzalloc_obj(*ops[0]); + ops[i] = colorop_kzalloc_obj(); if (!ops[i]) { ret = -ENOMEM; goto cleanup; } - ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane, &dm_colorop_funcs, - amdgpu_dm_supported_blnd_tfs, - DRM_COLOROP_FLAG_ALLOW_BYPASS); + ret = colorop_curve_1d_init(dev, ops[i], plane, &dm_colorop_funcs, + amdgpu_dm_supported_blnd_tfs, + DRM_COLOROP_FLAG_ALLOW_BYPASS); if (ret) goto cleanup; @@ -197,16 +237,16 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane i++; /* 1D LUT - BLND LUT */ - ops[i] = kzalloc_obj(struct drm_colorop); + ops[i] = colorop_kzalloc_obj(); if (!ops[i]) { ret = -ENOMEM; goto cleanup; } - ret = drm_plane_colorop_curve_1d_lut_init(dev, ops[i], plane, &dm_colorop_funcs, - MAX_COLOR_LUT_ENTRIES, - DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR, - DRM_COLOROP_FLAG_ALLOW_BYPASS); + ret = colorop_curve_1d_lut_init(dev, ops[i], plane, &dm_colorop_funcs, + MAX_COLOR_LUT_ENTRIES, + DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR, + DRM_COLOROP_FLAG_ALLOW_BYPASS); if (ret) goto cleanup; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h index 77364d954d3b..ff46c4f7b126 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h @@ -34,8 +34,36 @@ extern const u64 amdgpu_dm_supported_blnd_tfs; int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_prop_enum_list *list); #if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +#include <drm/drm_colorop.h> + +struct amdgpu_dm_colorop_kunit_ops { + struct drm_colorop *(*colorop_kzalloc_obj)(void); + int (*curve_1d_init)(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, const struct drm_colorop_funcs *funcs, + u64 supported_tfs, uint32_t flags); + int (*curve_1d_lut_init)(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, + const struct drm_colorop_funcs *funcs, + uint32_t lut_size, + enum drm_colorop_lut1d_interpolation_type interpolation, + uint32_t flags); + int (*ctm_3x4_init)(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, const struct drm_colorop_funcs *funcs, + uint32_t flags); + int (*mult_init)(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, const struct drm_colorop_funcs *funcs, + uint32_t flags); + int (*lut3d_init)(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, + const struct drm_colorop_funcs *funcs, + uint32_t lut_size, + enum drm_colorop_lut3d_interpolation_type interpolation, + uint32_t flags); +}; + int amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane, bool hw_3d_lut, struct drm_prop_enum_list *list); +void amdgpu_dm_colorop_kunit_set_ops(const struct amdgpu_dm_colorop_kunit_ops *ops); #endif #endif /* __AMDGPU_DM_COLOROP_H__*/ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_colorop_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_colorop_test.c index 2e557ff66818..90cc5592ef5d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_colorop_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_colorop_test.c @@ -136,6 +136,30 @@ static void kunit_colorop_pipeline_destroy(void *drm) drm_colorop_pipeline_destroy((struct drm_device *)drm); } +/* + * A plain drm_device (not an amdgpu_device) is enough: building the pipeline + * only needs the DRM mode-config infrastructure. The pipeline is destroyed + * before the DRM device so the colorops are freed while the device is valid. + */ +static struct drm_plane *dm_colorop_alloc_plane(struct kunit *test, struct drm_device **drm) +{ + struct drm_plane *plane; + struct device *dev; + + dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + *drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(**drm), 0, 0); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, *drm); + + plane = drm_kunit_helper_create_primary_plane(test, *drm, NULL, NULL, NULL, 0, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane); + + kunit_add_action(test, kunit_colorop_pipeline_destroy, *drm); + + return plane; +} + static void dm_expect_colorop_pipeline(struct kunit *test, struct drm_device *drm, const struct drm_prop_enum_list *list, const enum drm_colorop_type *expected, @@ -177,34 +201,12 @@ static void dm_test_initialize_default_pipeline(struct kunit *test) DRM_COLOROP_1D_CURVE, /* blnd TF */ DRM_COLOROP_1D_LUT, /* blnd LUT */ }; - struct device *dev; struct drm_device *drm; struct drm_plane *plane; struct drm_prop_enum_list list = {}; int ret; - dev = drm_kunit_helper_alloc_device(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); - - /* - * Allocate a plain drm_device (not an amdgpu_device) — sufficient - * because amdgpu_dm_build_default_pipeline() only needs the DRM - * mode-config infrastructure, not the amdgpu device wrapper. - */ - drm = __drm_kunit_helper_alloc_drm_device(test, dev, - sizeof(*drm), 0, 0); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); - - plane = drm_kunit_helper_create_primary_plane(test, drm, - NULL, NULL, NULL, 0, NULL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane); - - /* - * Destroy the pipeline before the DRM device is cleaned up so - * that the colorop objects (kzalloc'd inside the function) are - * freed while the device is still valid. - */ - kunit_add_action(test, kunit_colorop_pipeline_destroy, drm); + plane = dm_colorop_alloc_plane(test, &drm); ret = amdgpu_dm_build_default_pipeline(drm, plane, true, &list); KUNIT_ASSERT_EQ(test, ret, 0); @@ -308,6 +310,187 @@ static void dm_test_initialize_default_pipeline_no_3d_lut(struct kunit *test) expected, ARRAY_SIZE(expected)); } +/* Failure paths of amdgpu_dm_build_default_pipeline() */ + +/* + * The DRM colorop constructors cannot be made to fail through their arguments, + * so failures are injected with a fake allocator and with spies that delegate + * to the real constructors and override the return value of one call. + */ +struct dm_colorop_fault { + int alloc_fail_at; /* allocation index returning NULL, -1 to disable */ + int init_fail_at; /* init call index returning an error, -1 to disable */ + int allocs; + int inits; +}; + +static struct dm_colorop_fault dm_colorop_fault; + +static struct drm_colorop *dm_colorop_test_alloc(void) +{ + if (dm_colorop_fault.allocs++ == dm_colorop_fault.alloc_fail_at) + return NULL; + + return kzalloc_obj(struct drm_colorop); +} + +static int dm_colorop_test_init_ret(int ret) +{ + if (ret) + return ret; + + if (dm_colorop_fault.inits++ == dm_colorop_fault.init_fail_at) + return -EINVAL; + + return 0; +} + +static int dm_colorop_test_curve_1d_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, + const struct drm_colorop_funcs *funcs, + u64 supported_tfs, uint32_t flags) +{ + return dm_colorop_test_init_ret(drm_plane_colorop_curve_1d_init(dev, colorop, plane, + funcs, supported_tfs, + flags)); +} + +static int dm_colorop_test_1d_lut_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, + const struct drm_colorop_funcs *funcs, + uint32_t lut_size, + enum drm_colorop_lut1d_interpolation_type interpolation, + uint32_t flags) +{ + return dm_colorop_test_init_ret(drm_plane_colorop_curve_1d_lut_init(dev, colorop, plane, + funcs, lut_size, + interpolation, + flags)); +} + +static int dm_colorop_test_ctm_3x4_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, + const struct drm_colorop_funcs *funcs, uint32_t flags) +{ + return dm_colorop_test_init_ret(drm_plane_colorop_ctm_3x4_init(dev, colorop, plane, + funcs, flags)); +} + +static int dm_colorop_test_mult_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, + const struct drm_colorop_funcs *funcs, uint32_t flags) +{ + return dm_colorop_test_init_ret(drm_plane_colorop_mult_init(dev, colorop, plane, + funcs, flags)); +} + +static int dm_colorop_test_3dlut_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, + const struct drm_colorop_funcs *funcs, uint32_t lut_size, + enum drm_colorop_lut3d_interpolation_type interpolation, + uint32_t flags) +{ + return dm_colorop_test_init_ret(drm_plane_colorop_3dlut_init(dev, colorop, plane, funcs, + lut_size, interpolation, + flags)); +} + +static const struct amdgpu_dm_colorop_kunit_ops dm_colorop_test_ops = { + .colorop_kzalloc_obj = dm_colorop_test_alloc, + .curve_1d_init = dm_colorop_test_curve_1d_init, + .curve_1d_lut_init = dm_colorop_test_1d_lut_init, + .ctm_3x4_init = dm_colorop_test_ctm_3x4_init, + .mult_init = dm_colorop_test_mult_init, + .lut3d_init = dm_colorop_test_3dlut_init, +}; + +static void dm_colorop_test_reset_ops(void *unused) +{ + amdgpu_dm_colorop_kunit_set_ops(NULL); +} + +/* + * Build the full (3D LUT) pipeline with one injected failure and return the + * error reported by amdgpu_dm_build_default_pipeline(). + */ +static int dm_colorop_build_with_fault(struct kunit *test, struct drm_device **drm, + int alloc_fail_at, int init_fail_at) +{ + struct drm_prop_enum_list list = {}; + struct drm_plane *plane; + int ret; + + plane = dm_colorop_alloc_plane(test, drm); + + dm_colorop_fault = (struct dm_colorop_fault){ + .alloc_fail_at = alloc_fail_at, + .init_fail_at = init_fail_at, + }; + + amdgpu_dm_colorop_kunit_set_ops(&dm_colorop_test_ops); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_colorop_test_reset_ops, NULL), 0); + + ret = amdgpu_dm_build_default_pipeline(*drm, plane, true, &list); + kfree(list.name); + + return ret; +} + +struct dm_colorop_step_param { + const char *name; + int index; +}; + +/* Pipeline steps in creation order when the 3D LUT block is built. */ +static const struct dm_colorop_step_param dm_colorop_step_params[] = { + { "degam_tf", 0 }, + { "multiplier", 1 }, + { "ctm_3x4", 2 }, + { "shaper_tf", 3 }, + { "shaper_lut", 4 }, + { "lut3d", 5 }, + { "blnd_tf", 6 }, + { "blnd_lut", 7 }, +}; + +KUNIT_ARRAY_PARAM_DESC(dm_colorop_step, dm_colorop_step_params, name); + +/** + * dm_test_build_pipeline_alloc_fail() - Verify a failed alloc returns -ENOMEM and cleans up. + * @test: KUnit test context. + */ +static void dm_test_build_pipeline_alloc_fail(struct kunit *test) +{ + const struct dm_colorop_step_param *param = test->param_value; + struct drm_device *drm; + int ret; + + ret = dm_colorop_build_with_fault(test, &drm, param->index, -1); + + KUNIT_EXPECT_EQ(test, ret, -ENOMEM); + KUNIT_EXPECT_EQ(test, dm_colorop_fault.allocs, param->index + 1); + KUNIT_EXPECT_EQ(test, dm_colorop_fault.inits, param->index); + KUNIT_EXPECT_EQ(test, drm->mode_config.num_colorop, 0); +} + +/** + * dm_test_build_pipeline_init_fail() - Verify a failed init propagates the error and cleans up. + * @test: KUnit test context. + */ +static void dm_test_build_pipeline_init_fail(struct kunit *test) +{ + const struct dm_colorop_step_param *param = test->param_value; + struct drm_device *drm; + int ret; + + ret = dm_colorop_build_with_fault(test, &drm, -1, param->index); + + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + KUNIT_EXPECT_EQ(test, dm_colorop_fault.allocs, param->index + 1); + KUNIT_EXPECT_EQ(test, dm_colorop_fault.inits, param->index + 1); + KUNIT_EXPECT_EQ(test, drm->mode_config.num_colorop, 0); +} + static struct kunit_case dm_colorop_test_cases[] = { /* degam TFs */ KUNIT_CASE(dm_test_supported_degam_tfs_has_srgb_eotf), @@ -334,6 +517,9 @@ static struct kunit_case dm_colorop_test_cases[] = { KUNIT_CASE(dm_test_initialize_default_pipeline_dpp_3d_lut), KUNIT_CASE(dm_test_initialize_default_pipeline_mpc_preblend), KUNIT_CASE(dm_test_initialize_default_pipeline_no_3d_lut), + /* amdgpu_dm_build_default_pipeline failure paths */ + KUNIT_CASE_PARAM(dm_test_build_pipeline_alloc_fail, dm_colorop_step_gen_params), + KUNIT_CASE_PARAM(dm_test_build_pipeline_init_fail, dm_colorop_step_gen_params), {} }; -- 2.43.0