From: Bhawanpreet Lakha <[email protected]>

Verify hdcp_destroy() cancels each link's delayed works, removes
the SRM sysfs file and frees the workqueue and SRM buffers. Cover
both a populated work-queue and the zero-link / NULL-SRM path.

Reviewed-by: Alex Hung <[email protected]>
Signed-off-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Fangzhi Zuo <[email protected]>
Tested-by: Dan Wheeler <[email protected]>
---
 .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c    |   1 +
 .../amdgpu_dm/tests/amdgpu_dm_hdcp_test.c     | 122 ++++++++++++++++++
 2 files changed, 123 insertions(+)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
index 896668c92888..e704b6b7fc0b 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
@@ -568,6 +568,7 @@ void hdcp_destroy(struct kobject *kobj, struct 
hdcp_workqueue *hdcp_work)
        kfree(hdcp_work->srm_temp);
        kfree(hdcp_work);
 }
+EXPORT_IF_KUNIT(hdcp_destroy);
 
 static bool enable_assr(void *handle, struct dc_link *link)
 {
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c
index a4097a4aec26..5eace7fa4100 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c
@@ -7,6 +7,9 @@
 
 #include <kunit/test.h>
 #include <linux/workqueue.h>
+#include <linux/kobject.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
 
 #include "amdgpu.h"
 #include "amdgpu_dm.h"
@@ -1007,6 +1010,122 @@ static void 
dm_test_hdcp_create_workqueue_zero_max_links_returns_null(struct kun
 
 /* End of tests for hdcp_create_workqueue() */
 
+/* Tests for hdcp_destroy() */
+
+static ssize_t test_srm_bin_read(struct file *filp, struct kobject *kobj,
+                                const struct bin_attribute *bin_attr, char 
*buffer,
+                                loff_t pos, size_t count)
+{
+       return 0;
+}
+
+static ssize_t test_srm_bin_write(struct file *filp, struct kobject *kobj,
+                                 const struct bin_attribute *bin_attr, char 
*buffer,
+                                 loff_t pos, size_t count)
+{
+       return count;
+}
+
+/**
+ * setup_destroy_sysfs - create a kobject with the SRM bin file attached
+ * @test: KUnit test context
+ * @work: workqueue whose attr will be registered
+ *
+ * hdcp_destroy() calls sysfs_remove_bin_file() on the first entry's attr, so
+ * a real kobject with the bin file created is required. Returns the kobject,
+ * which the caller must kobject_put() after hdcp_destroy() has run.
+ */
+static struct kobject *setup_destroy_sysfs(struct kunit *test,
+                                          struct hdcp_workqueue *work)
+{
+       struct kobject *kobj;
+       int ret;
+
+       kobj = kobject_create_and_add("amdgpu_dm_hdcp_test", NULL);
+       KUNIT_ASSERT_NOT_NULL(test, kobj);
+
+       sysfs_bin_attr_init(&work->attr);
+       work->attr.attr.name = "hdcp_srm";
+       work->attr.attr.mode = 0664;
+       work->attr.size = 16;
+       work->attr.read = test_srm_bin_read;
+       work->attr.write = test_srm_bin_write;
+
+       ret = sysfs_create_bin_file(kobj, &work->attr);
+       KUNIT_ASSERT_EQ(test, ret, 0);
+
+       return kobj;
+}
+
+/**
+ * dm_test_hdcp_destroy_frees_and_removes_sysfs - full teardown path
+ * @test: KUnit test context
+ *
+ * hdcp_destroy() must cancel every link's delayed works, remove the SRM
+ * sysfs bin file and free srm, srm_temp and the workqueue itself. The
+ * workqueue and SRM buffers use kzalloc() (not kunit-managed) because
+ * hdcp_destroy() frees them; KASAN/kmemleak validate there is no leak or
+ * use-after-free.
+ */
+static void dm_test_hdcp_destroy_frees_and_removes_sysfs(struct kunit *test)
+{
+       struct hdcp_workqueue *work;
+       struct kobject *kobj;
+
+       work = kzalloc_obj(*work);
+       KUNIT_ASSERT_NOT_NULL(test, work);
+
+       work->max_link = 1;
+       INIT_DELAYED_WORK(&work->callback_dwork, dummy_work_fn);
+       INIT_DELAYED_WORK(&work->watchdog_timer_dwork, dummy_work_fn);
+       INIT_DELAYED_WORK(&work->property_validate_dwork, dummy_work_fn);
+
+       work->srm = kzalloc(16, GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, work->srm);
+       work->srm_temp = kzalloc(16, GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, work->srm_temp);
+
+       kobj = setup_destroy_sysfs(test, work);
+
+       /* Pre-schedule a delayed work to exercise the cancel path. */
+       schedule_delayed_work(&work->callback_dwork, msecs_to_jiffies(10000));
+       KUNIT_ASSERT_TRUE(test, delayed_work_pending(&work->callback_dwork));
+
+       hdcp_destroy(kobj, work);
+
+       /* work is freed by hdcp_destroy(); only the kobject remains. */
+       kobject_put(kobj);
+}
+
+/**
+ * dm_test_hdcp_destroy_zero_links_null_srm - teardown with no links or SRM
+ * @test: KUnit test context
+ *
+ * With max_link == 0 the cancel loop is skipped, and NULL srm/srm_temp make
+ * the kfree() calls no-ops. hdcp_destroy() must still remove the sysfs bin
+ * file and free the workqueue without crashing.
+ */
+static void dm_test_hdcp_destroy_zero_links_null_srm(struct kunit *test)
+{
+       struct hdcp_workqueue *work;
+       struct kobject *kobj;
+
+       work = kzalloc_obj(*work);
+       KUNIT_ASSERT_NOT_NULL(test, work);
+
+       work->max_link = 0;
+       work->srm = NULL;
+       work->srm_temp = NULL;
+
+       kobj = setup_destroy_sysfs(test, work);
+
+       hdcp_destroy(kobj, work);
+
+       kobject_put(kobj);
+}
+
+/* End of tests for hdcp_destroy() */
+
 /* Tests for link_lock() */
 
 /**
@@ -1373,6 +1492,9 @@ static struct kunit_case dm_hdcp_test_cases[] = {
        
KUNIT_CASE(dm_test_hdcp_update_display_disable_resets_status_and_cancels_validate),
        /* hdcp_create_workqueue() */
        KUNIT_CASE(dm_test_hdcp_create_workqueue_zero_max_links_returns_null),
+       /* hdcp_destroy() */
+       KUNIT_CASE(dm_test_hdcp_destroy_frees_and_removes_sysfs),
+       KUNIT_CASE(dm_test_hdcp_destroy_zero_links_null_srm),
        /* link_lock() */
        KUNIT_CASE(dm_test_link_lock_locks_and_unlocks_all_links),
        KUNIT_CASE(dm_test_link_lock_zero_links_is_noop),
-- 
2.53.0

Reply via email to