15a993507STvrtko Ursulin /* SPDX-License-Identifier: GPL-2.0 */ 25a993507STvrtko Ursulin /* Copyright (c) 2025 Valve Corporation */ 35a993507STvrtko Ursulin 45a993507STvrtko Ursulin #ifndef _SCHED_TESTS_H_ 55a993507STvrtko Ursulin #define _SCHED_TESTS_H_ 65a993507STvrtko Ursulin 75a993507STvrtko Ursulin #include <kunit/test.h> 85a993507STvrtko Ursulin #include <linux/atomic.h> 95a993507STvrtko Ursulin #include <linux/completion.h> 105a993507STvrtko Ursulin #include <linux/dma-fence.h> 115a993507STvrtko Ursulin #include <linux/hrtimer.h> 125a993507STvrtko Ursulin #include <linux/ktime.h> 135a993507STvrtko Ursulin #include <linux/list.h> 145a993507STvrtko Ursulin #include <linux/atomic.h> 155a993507STvrtko Ursulin #include <linux/mutex.h> 165a993507STvrtko Ursulin #include <linux/types.h> 175a993507STvrtko Ursulin 185a993507STvrtko Ursulin #include <drm/gpu_scheduler.h> 195a993507STvrtko Ursulin 205a993507STvrtko Ursulin /* 215a993507STvrtko Ursulin * DOC: Mock DRM scheduler data structures 225a993507STvrtko Ursulin * 235a993507STvrtko Ursulin * drm_mock_* data structures are used to implement a mock "GPU". 245a993507STvrtko Ursulin * 255a993507STvrtko Ursulin * They subclass the core DRM scheduler objects and add their data on top, which 265a993507STvrtko Ursulin * enables tracking the submitted jobs and simulating their execution with the 275a993507STvrtko Ursulin * attributes as specified by the test case. 285a993507STvrtko Ursulin */ 295a993507STvrtko Ursulin 305a993507STvrtko Ursulin /** 315a993507STvrtko Ursulin * struct drm_mock_scheduler - implements a trivial mock GPU execution engine 325a993507STvrtko Ursulin * 335a993507STvrtko Ursulin * @base: DRM scheduler base class 345a993507STvrtko Ursulin * @test: Backpointer to owning the kunit test case 355a993507STvrtko Ursulin * @lock: Lock to protect the simulated @hw_timeline, @job_list and @done_list 365a993507STvrtko Ursulin * @job_list: List of jobs submitted to the mock GPU 375a993507STvrtko Ursulin * @done_list: List of jobs completed by the mock GPU 385a993507STvrtko Ursulin * @hw_timeline: Simulated hardware timeline has a @context, @next_seqno and 395a993507STvrtko Ursulin * @cur_seqno for implementing a struct dma_fence signaling the 405a993507STvrtko Ursulin * simulated job completion. 415a993507STvrtko Ursulin * 425a993507STvrtko Ursulin * Trivial mock GPU execution engine tracks submitted jobs and enables 435a993507STvrtko Ursulin * completing them strictly in submission order. 445a993507STvrtko Ursulin */ 455a993507STvrtko Ursulin struct drm_mock_scheduler { 465a993507STvrtko Ursulin struct drm_gpu_scheduler base; 475a993507STvrtko Ursulin 485a993507STvrtko Ursulin struct kunit *test; 495a993507STvrtko Ursulin 505a993507STvrtko Ursulin spinlock_t lock; 515a993507STvrtko Ursulin struct list_head job_list; 525a993507STvrtko Ursulin 535a993507STvrtko Ursulin struct { 545a993507STvrtko Ursulin u64 context; 555a993507STvrtko Ursulin atomic_t next_seqno; 565a993507STvrtko Ursulin unsigned int cur_seqno; 575a993507STvrtko Ursulin } hw_timeline; 585a993507STvrtko Ursulin }; 595a993507STvrtko Ursulin 605a993507STvrtko Ursulin /** 615a993507STvrtko Ursulin * struct drm_mock_sched_entity - implements a mock GPU sched entity 625a993507STvrtko Ursulin * 635a993507STvrtko Ursulin * @base: DRM scheduler entity base class 645a993507STvrtko Ursulin * @test: Backpointer to owning the kunit test case 655a993507STvrtko Ursulin * 665a993507STvrtko Ursulin * Mock GPU sched entity is used by the test cases to submit jobs to the mock 675a993507STvrtko Ursulin * scheduler. 685a993507STvrtko Ursulin */ 695a993507STvrtko Ursulin struct drm_mock_sched_entity { 705a993507STvrtko Ursulin struct drm_sched_entity base; 715a993507STvrtko Ursulin 725a993507STvrtko Ursulin struct kunit *test; 735a993507STvrtko Ursulin }; 745a993507STvrtko Ursulin 755a993507STvrtko Ursulin /** 765a993507STvrtko Ursulin * struct drm_mock_sched_job - implements a mock GPU job 775a993507STvrtko Ursulin * 785a993507STvrtko Ursulin * @base: DRM sched job base class 795a993507STvrtko Ursulin * @done: Completion signaling job completion. 805a993507STvrtko Ursulin * @flags: Flags designating job state. 815a993507STvrtko Ursulin * @link: List head element used by job tracking by the drm_mock_scheduler 825a993507STvrtko Ursulin * @timer: Timer used for simulating job execution duration 835a993507STvrtko Ursulin * @duration_us: Simulated job duration in micro seconds, or zero if in manual 845a993507STvrtko Ursulin * timeline advance mode 855a993507STvrtko Ursulin * @finish_at: Absolute time when the jobs with set duration will complete 865a993507STvrtko Ursulin * @lock: Lock used for @hw_fence 875a993507STvrtko Ursulin * @hw_fence: Fence returned to DRM scheduler as the hardware fence 885a993507STvrtko Ursulin * @test: Backpointer to owning the kunit test case 895a993507STvrtko Ursulin * 905a993507STvrtko Ursulin * Mock GPU sched job is used by the test cases to submit jobs to the mock 915a993507STvrtko Ursulin * scheduler. 925a993507STvrtko Ursulin */ 935a993507STvrtko Ursulin struct drm_mock_sched_job { 945a993507STvrtko Ursulin struct drm_sched_job base; 955a993507STvrtko Ursulin 965a993507STvrtko Ursulin struct completion done; 975a993507STvrtko Ursulin 985a993507STvrtko Ursulin #define DRM_MOCK_SCHED_JOB_DONE 0x1 9953e65974STvrtko Ursulin #define DRM_MOCK_SCHED_JOB_TIMEDOUT 0x2 100*1472e754SMaíra Canal #define DRM_MOCK_SCHED_JOB_DONT_RESET 0x4 1015a993507STvrtko Ursulin unsigned long flags; 1025a993507STvrtko Ursulin 1035a993507STvrtko Ursulin struct list_head link; 1045a993507STvrtko Ursulin struct hrtimer timer; 1055a993507STvrtko Ursulin 1065a993507STvrtko Ursulin unsigned int duration_us; 1075a993507STvrtko Ursulin ktime_t finish_at; 1085a993507STvrtko Ursulin 1095a993507STvrtko Ursulin struct dma_fence hw_fence; 1105a993507STvrtko Ursulin 1115a993507STvrtko Ursulin struct kunit *test; 1125a993507STvrtko Ursulin }; 1135a993507STvrtko Ursulin 1145a993507STvrtko Ursulin static inline struct drm_mock_scheduler * 1155a993507STvrtko Ursulin drm_sched_to_mock_sched(struct drm_gpu_scheduler *sched) 1165a993507STvrtko Ursulin { 1175a993507STvrtko Ursulin return container_of(sched, struct drm_mock_scheduler, base); 1185a993507STvrtko Ursulin }; 1195a993507STvrtko Ursulin 1205a993507STvrtko Ursulin static inline struct drm_mock_sched_entity * 1215a993507STvrtko Ursulin drm_sched_entity_to_mock_entity(struct drm_sched_entity *sched_entity) 1225a993507STvrtko Ursulin { 1235a993507STvrtko Ursulin return container_of(sched_entity, struct drm_mock_sched_entity, base); 1245a993507STvrtko Ursulin }; 1255a993507STvrtko Ursulin 1265a993507STvrtko Ursulin static inline struct drm_mock_sched_job * 1275a993507STvrtko Ursulin drm_sched_job_to_mock_job(struct drm_sched_job *sched_job) 1285a993507STvrtko Ursulin { 1295a993507STvrtko Ursulin return container_of(sched_job, struct drm_mock_sched_job, base); 1305a993507STvrtko Ursulin }; 1315a993507STvrtko Ursulin 13253e65974STvrtko Ursulin struct drm_mock_scheduler *drm_mock_sched_new(struct kunit *test, 13353e65974STvrtko Ursulin long timeout); 1345a993507STvrtko Ursulin void drm_mock_sched_fini(struct drm_mock_scheduler *sched); 1355a993507STvrtko Ursulin unsigned int drm_mock_sched_advance(struct drm_mock_scheduler *sched, 1365a993507STvrtko Ursulin unsigned int num); 1375a993507STvrtko Ursulin 1385a993507STvrtko Ursulin struct drm_mock_sched_entity * 1395a993507STvrtko Ursulin drm_mock_sched_entity_new(struct kunit *test, 1405a993507STvrtko Ursulin enum drm_sched_priority priority, 1415a993507STvrtko Ursulin struct drm_mock_scheduler *sched); 1425a993507STvrtko Ursulin void drm_mock_sched_entity_free(struct drm_mock_sched_entity *entity); 1435a993507STvrtko Ursulin 1445a993507STvrtko Ursulin struct drm_mock_sched_job * 1455a993507STvrtko Ursulin drm_mock_sched_job_new(struct kunit *test, 1465a993507STvrtko Ursulin struct drm_mock_sched_entity *entity); 1475a993507STvrtko Ursulin 1485a993507STvrtko Ursulin /** 1495a993507STvrtko Ursulin * drm_mock_sched_job_submit - Arm and submit a job in one go 1505a993507STvrtko Ursulin * 1515a993507STvrtko Ursulin * @job: Job to arm and submit 1525a993507STvrtko Ursulin */ 1535a993507STvrtko Ursulin static inline void drm_mock_sched_job_submit(struct drm_mock_sched_job *job) 1545a993507STvrtko Ursulin { 1555a993507STvrtko Ursulin drm_sched_job_arm(&job->base); 1565a993507STvrtko Ursulin drm_sched_entity_push_job(&job->base); 1575a993507STvrtko Ursulin } 1585a993507STvrtko Ursulin 1595a993507STvrtko Ursulin /** 1605a993507STvrtko Ursulin * drm_mock_sched_job_set_duration_us - Set a job duration 1615a993507STvrtko Ursulin * 1625a993507STvrtko Ursulin * @job: Job to set the duration for 1635a993507STvrtko Ursulin * @duration_us: Duration in micro seconds 1645a993507STvrtko Ursulin * 1655a993507STvrtko Ursulin * Jobs with duration set will be automatically completed by the mock scheduler 1665a993507STvrtko Ursulin * as the timeline progresses, unless a job without a set duration is 1675a993507STvrtko Ursulin * encountered in the timelime in which case calling drm_mock_sched_advance() 1685a993507STvrtko Ursulin * will be required to bump the timeline. 1695a993507STvrtko Ursulin */ 1705a993507STvrtko Ursulin static inline void 1715a993507STvrtko Ursulin drm_mock_sched_job_set_duration_us(struct drm_mock_sched_job *job, 1725a993507STvrtko Ursulin unsigned int duration_us) 1735a993507STvrtko Ursulin { 1745a993507STvrtko Ursulin job->duration_us = duration_us; 1755a993507STvrtko Ursulin } 1765a993507STvrtko Ursulin 1775a993507STvrtko Ursulin /** 1785a993507STvrtko Ursulin * drm_mock_sched_job_is_finished - Check if a job is finished 1795a993507STvrtko Ursulin * 1805a993507STvrtko Ursulin * @job: Job to check 1815a993507STvrtko Ursulin * 1825a993507STvrtko Ursulin * Returns: true if finished 1835a993507STvrtko Ursulin */ 1845a993507STvrtko Ursulin static inline bool 1855a993507STvrtko Ursulin drm_mock_sched_job_is_finished(struct drm_mock_sched_job *job) 1865a993507STvrtko Ursulin { 1875a993507STvrtko Ursulin return job->flags & DRM_MOCK_SCHED_JOB_DONE; 1885a993507STvrtko Ursulin } 1895a993507STvrtko Ursulin 1905a993507STvrtko Ursulin /** 1915a993507STvrtko Ursulin * drm_mock_sched_job_wait_finished - Wait until a job is finished 1925a993507STvrtko Ursulin * 1935a993507STvrtko Ursulin * @job: Job to wait for 1945a993507STvrtko Ursulin * @timeout: Wait time in jiffies 1955a993507STvrtko Ursulin * 1965a993507STvrtko Ursulin * Returns: true if finished within the timeout provided, otherwise false 1975a993507STvrtko Ursulin */ 1985a993507STvrtko Ursulin static inline bool 1995a993507STvrtko Ursulin drm_mock_sched_job_wait_finished(struct drm_mock_sched_job *job, long timeout) 2005a993507STvrtko Ursulin { 2015a993507STvrtko Ursulin if (job->flags & DRM_MOCK_SCHED_JOB_DONE) 2025a993507STvrtko Ursulin return true; 2035a993507STvrtko Ursulin 2045a993507STvrtko Ursulin return wait_for_completion_timeout(&job->done, timeout) != 0; 2055a993507STvrtko Ursulin } 2065a993507STvrtko Ursulin 2075a993507STvrtko Ursulin /** 2085a993507STvrtko Ursulin * drm_mock_sched_job_wait_scheduled - Wait until a job is scheduled 2095a993507STvrtko Ursulin * 2105a993507STvrtko Ursulin * @job: Job to wait for 2115a993507STvrtko Ursulin * @timeout: Wait time in jiffies 2125a993507STvrtko Ursulin * 2135a993507STvrtko Ursulin * Returns: true if scheduled within the timeout provided, otherwise false 2145a993507STvrtko Ursulin */ 2155a993507STvrtko Ursulin static inline bool 2165a993507STvrtko Ursulin drm_mock_sched_job_wait_scheduled(struct drm_mock_sched_job *job, long timeout) 2175a993507STvrtko Ursulin { 2185a993507STvrtko Ursulin KUNIT_ASSERT_EQ(job->test, job->flags & DRM_MOCK_SCHED_JOB_DONE, 0); 2195a993507STvrtko Ursulin 2205a993507STvrtko Ursulin return dma_fence_wait_timeout(&job->base.s_fence->scheduled, 2215a993507STvrtko Ursulin false, 2225a993507STvrtko Ursulin timeout) != 0; 2235a993507STvrtko Ursulin } 2245a993507STvrtko Ursulin 2255a993507STvrtko Ursulin #endif 226