diff mbox series

[v5,9/9] drm: selftest: convert drm_mm selftest to KUnit

Message ID 20220708203052.236290-10-maira.canal@usp.br (mailing list archive)
State Accepted
Commit fc8d29e298cf47e07c2764ec1c340c1df8e50431
Headers show
Series drm: selftest: Convert to KUnit | expand

Commit Message

Maíra Canal July 8, 2022, 8:30 p.m. UTC
From: Arthur Grillo <arthur.grillo@usp.br>

Considering the current adoption of the KUnit framework, convert the
DRM mm selftest to the KUnit API.

Signed-off-by: Arthur Grillo <arthur.grillo@usp.br>
Tested-by: David Gow <davidgow@google.com>
Acked-by: Daniel Latypov <dlatypov@google.com>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Signed-off-by: Maíra Canal <maira.canal@usp.br>
---
 Documentation/gpu/todo.rst                    |   11 -
 drivers/gpu/drm/Kconfig                       |   20 -
 drivers/gpu/drm/Makefile                      |    1 -
 drivers/gpu/drm/selftests/Makefile            |    2 -
 drivers/gpu/drm/selftests/drm_mm_selftests.h  |   28 -
 drivers/gpu/drm/selftests/drm_selftest.c      |  109 --
 drivers/gpu/drm/selftests/drm_selftest.h      |   41 -
 drivers/gpu/drm/tests/Makefile                |    2 +-
 .../test-drm_mm.c => tests/drm_mm_test.c}     | 1248 +++++++----------
 9 files changed, 509 insertions(+), 953 deletions(-)
 delete mode 100644 drivers/gpu/drm/selftests/Makefile
 delete mode 100644 drivers/gpu/drm/selftests/drm_mm_selftests.h
 delete mode 100644 drivers/gpu/drm/selftests/drm_selftest.c
 delete mode 100644 drivers/gpu/drm/selftests/drm_selftest.h
 rename drivers/gpu/drm/{selftests/test-drm_mm.c => tests/drm_mm_test.c} (55%)

Comments

Matthew Auld July 22, 2022, 10:35 a.m. UTC | #1
On Fri, 8 Jul 2022 at 21:32, Maíra Canal <maira.canal@usp.br> wrote:
>
> From: Arthur Grillo <arthur.grillo@usp.br>
>
> Considering the current adoption of the KUnit framework, convert the
> DRM mm selftest to the KUnit API.

Is there a plan to convert the corresponding selftest IGT that was
responsible for running this (also drm_buddy) to somehow work with
kunit? Previously these IGTs were always triggered as part of
intel-gfx CI, but it looks like they are no longer run[1].

[1] https://gitlab.freedesktop.org/drm/intel/-/issues/6433
Maíra Canal July 22, 2022, 11:04 a.m. UTC | #2
On 7/22/22 07:35, Matthew Auld wrote:
> On Fri, 8 Jul 2022 at 21:32, Maíra Canal <maira.canal@usp.br> wrote:
>>
>> From: Arthur Grillo <arthur.grillo@usp.br>
>>
>> Considering the current adoption of the KUnit framework, convert the
>> DRM mm selftest to the KUnit API.
> 
> Is there a plan to convert the corresponding selftest IGT that was
> responsible for running this (also drm_buddy) to somehow work with
> kunit? Previously these IGTs were always triggered as part of
> intel-gfx CI, but it looks like they are no longer run[1].
> 
> [1] https://gitlab.freedesktop.org/drm/intel/-/issues/6433

Hi Matthew,

Isabella sent a while ago a patch to IGT adding KUnit compatibility to
IGT [1], but there wasn't any feedback on the patch. I believe that soon
she will resend the series in order to make all KUnit DRM tests run on IGT.

Any feedback on the patch is welcomed so that we can fix this issue as
soon as possible.

[1] https://patchwork.freedesktop.org/patch/489985/

Best Regards,
- Maíra Canal
Michał Winiarski July 22, 2022, 4:25 p.m. UTC | #3
On Fri, Jul 22, 2022 at 08:04:51AM -0300, Maíra Canal wrote:
> On 7/22/22 07:35, Matthew Auld wrote:
> > On Fri, 8 Jul 2022 at 21:32, Maíra Canal <maira.canal@usp.br> wrote:
> >>
> >> From: Arthur Grillo <arthur.grillo@usp.br>
> >>
> >> Considering the current adoption of the KUnit framework, convert the
> >> DRM mm selftest to the KUnit API.
> > 
> > Is there a plan to convert the corresponding selftest IGT that was
> > responsible for running this (also drm_buddy) to somehow work with
> > kunit? Previously these IGTs were always triggered as part of
> > intel-gfx CI, but it looks like they are no longer run[1].
> > 
> > [1] https://gitlab.freedesktop.org/drm/intel/-/issues/6433
> 
> Hi Matthew,
> 
> Isabella sent a while ago a patch to IGT adding KUnit compatibility to
> IGT [1], but there wasn't any feedback on the patch. I believe that soon
> she will resend the series in order to make all KUnit DRM tests run on IGT.
> 
> Any feedback on the patch is welcomed so that we can fix this issue as
> soon as possible.
> 
> [1] https://patchwork.freedesktop.org/patch/489985/
> 
> Best Regards,
> - Maíra Canal

Hi.

Instead of going back to using IGT for *unit* tests, it would be a better idea
to adjust the CI to just run the tests once at "build" time (just like e.g.
checkpatch).

We would then stop executing the same test multiple times on different machines
(note that both DRM selftests and i915 "mock" selftests are pure unit tests - in
other words, they don't need the hardware to be present), which would save some
(small) amount of machine-time that can be utilized to do something that
actually needs the hardware.

Plus there's no need to maintain the kunit-runner in IGT.
Note - we're currently going to lose "DMESG-WARN" detection if we go this route,
but this is something that can be improved on the kunit-side.

-Michał
Isabella Basso Aug. 21, 2022, 10:22 p.m. UTC | #4
Hi Michał,

While I totally understand your point, we have talked about this in our GSoC
meetings with mentors, and have found a few reasons as to why a KUnit runner
integrated to IGT might be really useful. 

> Am 22/07/2022 um 1:25 PM schrieb Michał Winiarski <michal@hardline.pl>:
> 
> On Fri, Jul 22, 2022 at 08:04:51AM -0300, Maíra Canal wrote:
>> On 7/22/22 07:35, Matthew Auld wrote:
>>> On Fri, 8 Jul 2022 at 21:32, Maíra Canal <maira.canal@usp.br> wrote:
>>>> 
>>>> From: Arthur Grillo <arthur.grillo@usp.br>
>>>> 
>>>> Considering the current adoption of the KUnit framework, convert the
>>>> DRM mm selftest to the KUnit API.
>>> 
>>> Is there a plan to convert the corresponding selftest IGT that was
>>> responsible for running this (also drm_buddy) to somehow work with
>>> kunit? Previously these IGTs were always triggered as part of
>>> intel-gfx CI, but it looks like they are no longer run[1].
>>> 
>>> [1] https://gitlab.freedesktop.org/drm/intel/-/issues/6433
>> 
>> Hi Matthew,
>> 
>> Isabella sent a while ago a patch to IGT adding KUnit compatibility to
>> IGT [1], but there wasn't any feedback on the patch. I believe that soon
>> she will resend the series in order to make all KUnit DRM tests run on IGT.
>> 
>> Any feedback on the patch is welcomed so that we can fix this issue as
>> soon as possible.
>> 
>> [1] https://patchwork.freedesktop.org/patch/489985/
>> 
>> Best Regards,
>> - Maíra Canal
> 
> Hi.
> 
> Instead of going back to using IGT for *unit* tests, it would be a better idea
> to adjust the CI to just run the tests once at "build" time (just like e.g.
> checkpatch).

First, I’d like to point out that there would be some inherent overhead in
doing so, which might actually not be worth it, as KUnit tool would need to
compile HEAD in the UML arch, then we’d have to re-compile everything to a real
machine’s architecture, like x86_64 (in the least), having in mind still that
arch-dependent issues would not show up when we run tests in UML, so there’s
still a downside to it even if it’s quick enough.

Even if we don’t run them as UML and instead use a VM, there’s a VM being run
just for a couple of tests, which might be slower than adding a step to a
dedicated machine that’s (probably) already available, plus the setup and
hardware needed to run a VM inside of a CI runner are overheads in themselves,
needing dedicated, modern machines.

> We would then stop executing the same test multiple times on different machines
> (note that both DRM selftests and i915 "mock" selftests are pure unit tests - in
> other words, they don't need the hardware to be present), which would save some
> (small) amount of machine-time that can be utilized to do something that
> actually needs the hardware.

I totally agree with your solution in regards to arch-independent tests, though.

> Plus there's no need to maintain the kunit-runner in IGT.
> Note - we're currently going to lose "DMESG-WARN" detection if we go this route,
> but this is something that can be improved on the kunit-side.
> 
> -Michał

There’s also a point to be made on maintaining such a runner if we think about
companies like AMD, as they rely heavily on IGT, so they have lots of tests
written in there, and it'd be difficult for them to accommodate one more
non-trivial thing to their CI. Plus I think this might be a good starting point
for them to transition their CI to a KUnit-centered approach without stressing
engineers unnecessarily.

Cheers,
—
Isabella
Michał Winiarski Aug. 24, 2022, 12:46 a.m. UTC | #5
On Sun, Aug 21, 2022 at 07:22:30PM -0300, Isabella Basso wrote:
> Hi Michał,
> 
> While I totally understand your point, we have talked about this in our GSoC
> meetings with mentors, and have found a few reasons as to why a KUnit runner
> integrated to IGT might be really useful. 
> 
> > Am 22/07/2022 um 1:25 PM schrieb Michał Winiarski <michal@hardline.pl>:
> > 
> > On Fri, Jul 22, 2022 at 08:04:51AM -0300, Maíra Canal wrote:
> >> On 7/22/22 07:35, Matthew Auld wrote:
> >>> On Fri, 8 Jul 2022 at 21:32, Maíra Canal <maira.canal@usp.br> wrote:
> >>>> 
> >>>> From: Arthur Grillo <arthur.grillo@usp.br>
> >>>> 
> >>>> Considering the current adoption of the KUnit framework, convert the
> >>>> DRM mm selftest to the KUnit API.
> >>> 
> >>> Is there a plan to convert the corresponding selftest IGT that was
> >>> responsible for running this (also drm_buddy) to somehow work with
> >>> kunit? Previously these IGTs were always triggered as part of
> >>> intel-gfx CI, but it looks like they are no longer run[1].
> >>> 
> >>> [1] https://gitlab.freedesktop.org/drm/intel/-/issues/6433
> >> 
> >> Hi Matthew,
> >> 
> >> Isabella sent a while ago a patch to IGT adding KUnit compatibility to
> >> IGT [1], but there wasn't any feedback on the patch. I believe that soon
> >> she will resend the series in order to make all KUnit DRM tests run on IGT.
> >> 
> >> Any feedback on the patch is welcomed so that we can fix this issue as
> >> soon as possible.
> >> 
> >> [1] https://patchwork.freedesktop.org/patch/489985/
> >> 
> >> Best Regards,
> >> - Maíra Canal
> > 
> > Hi.
> > 
> > Instead of going back to using IGT for *unit* tests, it would be a better idea
> > to adjust the CI to just run the tests once at "build" time (just like e.g.
> > checkpatch).
> 
> First, I’d like to point out that there would be some inherent overhead in
> doing so, which might actually not be worth it, as KUnit tool would need to
> compile HEAD in the UML arch, then we’d have to re-compile everything to a real
> machine’s architecture, like x86_64 (in the least), having in mind still that
> arch-dependent issues would not show up when we run tests in UML, so there’s
> still a downside to it even if it’s quick enough.
> 
> Even if we don’t run them as UML and instead use a VM, there’s a VM being run
> just for a couple of tests, which might be slower than adding a step to a
> dedicated machine that’s (probably) already available, plus the setup and
> hardware needed to run a VM inside of a CI runner are overheads in themselves,
> needing dedicated, modern machines.

No - we don't need a dedicated machine for running kunit - the machine that we
just used to compile the code is perfectly fine.
Builders used in CI systems usually have beefy server-grade CPUs - pretty good
candidates for running unit tests (even with virtualization overhead).
Plus - if the unit tests fail, we can consider skipping the deployment and
not run any regular tests (just like the case where build has failed).
Meanwhile, one of the "dedicated machines" (ones that are used to run the tests)
can actually be a low-power device (think tablet). And if the test ends up
crashing the kernel, it needs to be rebooted. VMs are much easier to work with,
especially with kunit.py abstracting away all of the qemu interactions.

> 
> > We would then stop executing the same test multiple times on different machines
> > (note that both DRM selftests and i915 "mock" selftests are pure unit tests - in
> > other words, they don't need the hardware to be present), which would save some
> > (small) amount of machine-time that can be utilized to do something that
> > actually needs the hardware.
> 
> I totally agree with your solution in regards to arch-independent tests, though.

There are no arch-specific kunit tests in DRM-core. There shouldn't be any
arch-specific code in DRM-core. Same thing for drivers (at least for the purpose
of COMPILE_TEST and by extension, running kunit).
All of DRM kunit tests should pass on all architectures supported by kunit.

> 
> > Plus there's no need to maintain the kunit-runner in IGT.
> > Note - we're currently going to lose "DMESG-WARN" detection if we go this route,
> > but this is something that can be improved on the kunit-side.
> > 
> > -Michał
> 
> There’s also a point to be made on maintaining such a runner if we think about
> companies like AMD, as they rely heavily on IGT, so they have lots of tests
> written in there, and it'd be difficult for them to accommodate one more
> non-trivial thing to their CI. Plus I think this might be a good starting point
> for them to transition their CI to a KUnit-centered approach without stressing
> engineers unnecessarily.

I agree with the IGT-compatibility angle, however, that would only apply to test
content that gets converted from selftests to kunit (just like DRM selftests),
not newly introduced test content (as is the case with amdgpu).
I also wouldn't call interpreting exit code of "kunit.py run (...)" something
that's difficult to be added to various CI pipelines.
Also - do we really want to transition to KUnit-centered approach?
Regular IGTs are actually about exercising the HW through driver uAPI from
userspace, not about isolated unit testing (which is what KUnit is about).
Then we have selftests, which are implemented on the kernel side, and are about
internal implementation. Selftests may or may not require HW to operate (if HW
is needed, we're usually doing more of a functional/integration testing, if not
- it's most likely going to be a pure unit test).
I view regular IGTs and KUnit (and kselftests that are not isolated, and need
the HW to be present) as complementary mechanisms, not something to be replaced
(in other words - we only want to transition unit tests to KUnit).

When it comes to transition, I'm just worried that once the IGT KTAP parser is
adopted, the transition to kunit.py @ build time will never happen, and we'll
end up maintaining custom DRM-specific solution instead of participating in
wider kernel community.

-Michał

> 
> Cheers,
> —
> Isabella
>
Maxime Ripard April 27, 2023, 1:14 p.m. UTC | #6
Hi,

On Fri, Jul 08, 2022 at 05:30:52PM -0300, Maíra Canal wrote:
> From: Arthur Grillo <arthur.grillo@usp.br>
> 
> Considering the current adoption of the KUnit framework, convert the
> DRM mm selftest to the KUnit API.
> 
> Signed-off-by: Arthur Grillo <arthur.grillo@usp.br>
> Tested-by: David Gow <davidgow@google.com>
> Acked-by: Daniel Latypov <dlatypov@google.com>
> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
> Signed-off-by: Maíra Canal <maira.canal@usp.br>

I'm very late to the party, but I'd like to discuss that patch some more.

Two tests (drm_test_mm_reserve, drm_test_mm_insert) in it take a super
long time to run (about 30s each on my machine).

If we run all the DRM tests and VC4 tests, each of those two are longer
to run than all the ~300 tests combined. About 100 times longer.

I don't think that running for so long is reasonable, and for multiple
reasons:

  - While I don't know drm_mm well, it doesn't look like any of those
    tests do something that really should take this long. I'm especially
    skeptical about the fact that we test each operation 8192 times by
    default.

  - It makes using kunit more tedious than it should be. Like I said, on
    a very capable machine, running the all the DRM and VC4 tests takes
    about 50s with those two tests, ~0.4s without.

  - The corollary is that it will get in the way of people that really
    want to use kunit will just remove those tests before doing so,
    defeating the original intent.


I understand that it came from selftests initially, but I think we
should rewrite the tests entirely to have smaller, faster tests. It's
not clear to me why those tests are as complicated as they are though.

Also, going forward we should probably put disencourage tests running
that long. Could Kunit timeout/warn after a while if a test is taking
more than X seconds to run?

Maxime
Maxime Ripard July 25, 2023, 8:38 a.m. UTC | #7
Hi,

On Thu, Apr 27, 2023 at 03:14:39PM +0200, Maxime Ripard wrote:
> Hi,
> 
> On Fri, Jul 08, 2022 at 05:30:52PM -0300, Maíra Canal wrote:
> > From: Arthur Grillo <arthur.grillo@usp.br>
> > 
> > Considering the current adoption of the KUnit framework, convert the
> > DRM mm selftest to the KUnit API.
> > 
> > Signed-off-by: Arthur Grillo <arthur.grillo@usp.br>
> > Tested-by: David Gow <davidgow@google.com>
> > Acked-by: Daniel Latypov <dlatypov@google.com>
> > Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
> > Signed-off-by: Maíra Canal <maira.canal@usp.br>
> 
> I'm very late to the party, but I'd like to discuss that patch some more.
> 
> Two tests (drm_test_mm_reserve, drm_test_mm_insert) in it take a super
> long time to run (about 30s each on my machine).
> 
> If we run all the DRM tests and VC4 tests, each of those two are longer
> to run than all the ~300 tests combined. About 100 times longer.
> 
> I don't think that running for so long is reasonable, and for multiple
> reasons:
> 
>   - While I don't know drm_mm well, it doesn't look like any of those
>     tests do something that really should take this long. I'm especially
>     skeptical about the fact that we test each operation 8192 times by
>     default.
> 
>   - It makes using kunit more tedious than it should be. Like I said, on
>     a very capable machine, running the all the DRM and VC4 tests takes
>     about 50s with those two tests, ~0.4s without.
> 
>   - The corollary is that it will get in the way of people that really
>     want to use kunit will just remove those tests before doing so,
>     defeating the original intent.
> 
> 
> I understand that it came from selftests initially, but I think we
> should rewrite the tests entirely to have smaller, faster tests. It's
> not clear to me why those tests are as complicated as they are though.
> 
> Also, going forward we should probably put disencourage tests running
> that long. Could Kunit timeout/warn after a while if a test is taking
> more than X seconds to run?

I'd still like to address this. We spend ~90% of the DRM kunit tests
execution time executing those two tests, which doesn't seem like a
reasonable thing to do.

I'm fine with doing that work, but I'd still need to figure out what
those tests are doing exactly. Can someone help?

Maxime
David Gow July 25, 2023, 9:54 a.m. UTC | #8
On Tue, 25 Jul 2023 at 16:38, Maxime Ripard <mripard@kernel.org> wrote:
>
> Hi,
>
> On Thu, Apr 27, 2023 at 03:14:39PM +0200, Maxime Ripard wrote:
> > Hi,
> >
> > On Fri, Jul 08, 2022 at 05:30:52PM -0300, Maíra Canal wrote:
> > > From: Arthur Grillo <arthur.grillo@usp.br>
> > >
> > > Considering the current adoption of the KUnit framework, convert the
> > > DRM mm selftest to the KUnit API.
> > >
> > > Signed-off-by: Arthur Grillo <arthur.grillo@usp.br>
> > > Tested-by: David Gow <davidgow@google.com>
> > > Acked-by: Daniel Latypov <dlatypov@google.com>
> > > Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
> > > Signed-off-by: Maíra Canal <maira.canal@usp.br>
> >
> > I'm very late to the party, but I'd like to discuss that patch some more.
> >
> > Two tests (drm_test_mm_reserve, drm_test_mm_insert) in it take a super
> > long time to run (about 30s each on my machine).
> >
> > If we run all the DRM tests and VC4 tests, each of those two are longer
> > to run than all the ~300 tests combined. About 100 times longer.
> >
> > I don't think that running for so long is reasonable, and for multiple
> > reasons:
> >
> >   - While I don't know drm_mm well, it doesn't look like any of those
> >     tests do something that really should take this long. I'm especially
> >     skeptical about the fact that we test each operation 8192 times by
> >     default.
> >
> >   - It makes using kunit more tedious than it should be. Like I said, on
> >     a very capable machine, running the all the DRM and VC4 tests takes
> >     about 50s with those two tests, ~0.4s without.
> >
> >   - The corollary is that it will get in the way of people that really
> >     want to use kunit will just remove those tests before doing so,
> >     defeating the original intent.
> >
> >
> > I understand that it came from selftests initially, but I think we
> > should rewrite the tests entirely to have smaller, faster tests. It's
> > not clear to me why those tests are as complicated as they are though.
> >
> > Also, going forward we should probably put disencourage tests running
> > that long. Could Kunit timeout/warn after a while if a test is taking
> > more than X seconds to run?
>
> I'd still like to address this. We spend ~90% of the DRM kunit tests
> execution time executing those two tests, which doesn't seem like a
> reasonable thing to do.

FWIW, KUnit is going to add a "speed" attribute for tests, so that
it's easy to skip tests which are slow:
https://lore.kernel.org/linux-kselftest/20230724162834.1354164-3-rmoar@google.com/T/#u

This would allow the slow tests to be marked using KUNIT_CASE_SLOW(),
and then be run via kunit.py --filter "speed>slow".

It obviously doesn't make the tests themselves any faster, but could
at least make it possible to run only the fast tests during
development, and the full, slower set before sending the patches out
(or in CI), for example.

-- David
Maxime Ripard July 31, 2023, 12:18 p.m. UTC | #9
Hi David,

On Tue, Jul 25, 2023 at 05:54:32PM +0800, David Gow wrote:
> On Tue, 25 Jul 2023 at 16:38, Maxime Ripard <mripard@kernel.org> wrote:
> > On Thu, Apr 27, 2023 at 03:14:39PM +0200, Maxime Ripard wrote:
> > > Hi,
> > >
> > > On Fri, Jul 08, 2022 at 05:30:52PM -0300, Maíra Canal wrote:
> > > > From: Arthur Grillo <arthur.grillo@usp.br>
> > > >
> > > > Considering the current adoption of the KUnit framework, convert the
> > > > DRM mm selftest to the KUnit API.
> > > >
> > > > Signed-off-by: Arthur Grillo <arthur.grillo@usp.br>
> > > > Tested-by: David Gow <davidgow@google.com>
> > > > Acked-by: Daniel Latypov <dlatypov@google.com>
> > > > Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
> > > > Signed-off-by: Maíra Canal <maira.canal@usp.br>
> > >
> > > I'm very late to the party, but I'd like to discuss that patch some more.
> > >
> > > Two tests (drm_test_mm_reserve, drm_test_mm_insert) in it take a super
> > > long time to run (about 30s each on my machine).
> > >
> > > If we run all the DRM tests and VC4 tests, each of those two are longer
> > > to run than all the ~300 tests combined. About 100 times longer.
> > >
> > > I don't think that running for so long is reasonable, and for multiple
> > > reasons:
> > >
> > >   - While I don't know drm_mm well, it doesn't look like any of those
> > >     tests do something that really should take this long. I'm especially
> > >     skeptical about the fact that we test each operation 8192 times by
> > >     default.
> > >
> > >   - It makes using kunit more tedious than it should be. Like I said, on
> > >     a very capable machine, running the all the DRM and VC4 tests takes
> > >     about 50s with those two tests, ~0.4s without.
> > >
> > >   - The corollary is that it will get in the way of people that really
> > >     want to use kunit will just remove those tests before doing so,
> > >     defeating the original intent.
> > >
> > >
> > > I understand that it came from selftests initially, but I think we
> > > should rewrite the tests entirely to have smaller, faster tests. It's
> > > not clear to me why those tests are as complicated as they are though.
> > >
> > > Also, going forward we should probably put disencourage tests running
> > > that long. Could Kunit timeout/warn after a while if a test is taking
> > > more than X seconds to run?
> >
> > I'd still like to address this. We spend ~90% of the DRM kunit tests
> > execution time executing those two tests, which doesn't seem like a
> > reasonable thing to do.
> 
> FWIW, KUnit is going to add a "speed" attribute for tests, so that
> it's easy to skip tests which are slow:
> https://lore.kernel.org/linux-kselftest/20230724162834.1354164-3-rmoar@google.com/T/#u
> 
> This would allow the slow tests to be marked using KUNIT_CASE_SLOW(),
> and then be run via kunit.py --filter "speed>slow".
> 
> It obviously doesn't make the tests themselves any faster, but could
> at least make it possible to run only the fast tests during
> development, and the full, slower set before sending the patches out
> (or in CI), for example.

That's awesome, thanks

Speaking of which, should we detect in kunit.py tests that should be
marked as (super) slow but aren't?

Maxime
diff mbox series

Patch

diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst
index 513b20ccef1e..10bfb50908d1 100644
--- a/Documentation/gpu/todo.rst
+++ b/Documentation/gpu/todo.rst
@@ -617,17 +617,6 @@  Contact: Javier Martinez Canillas <javierm@redhat.com>
 
 Level: Intermediate
 
-Convert Kernel Selftests (kselftest) to KUnit tests when appropriate
---------------------------------------------------------------------
-
-Many of the `Kselftest <https://www.kernel.org/doc/html/latest/dev-tools/kselftest.html>`_
-tests in DRM could be converted to Kunit tests instead, since that framework
-is more suitable for unit testing.
-
-Contact: Javier Martinez Canillas <javierm@redhat.com>
-
-Level: Starter
-
 Enable trinity for DRM
 ----------------------
 
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 06822ecf51c6..1c91e1e861a5 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -50,26 +50,6 @@  config DRM_DEBUG_MM
 
 	  If in doubt, say "N".
 
-config DRM_DEBUG_SELFTEST
-	tristate "kselftests for DRM"
-	depends on DRM
-	depends on DEBUG_KERNEL
-	select PRIME_NUMBERS
-	select DRM_DISPLAY_DP_HELPER
-	select DRM_DISPLAY_HELPER
-	select DRM_LIB_RANDOM
-	select DRM_KMS_HELPER
-	select DRM_BUDDY
-	select DRM_EXPORT_FOR_TESTS if m
-	default n
-	help
-	  This option provides kernel modules that can be used to run
-	  various selftests on parts of the DRM api. This option is not
-	  useful for distributions or general kernels, but only for kernel
-	  developers working on DRM and associated drivers.
-
-	  If in doubt, say "N".
-
 config DRM_KUNIT_TEST
 	tristate "KUnit tests for DRM" if !KUNIT_ALL_TESTS
 	depends on DRM && KUNIT
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index e7af358e6dda..25016dcab55e 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -75,7 +75,6 @@  obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
 # Drivers and the rest
 #
 
-obj-$(CONFIG_DRM_DEBUG_SELFTEST) += selftests/
 obj-$(CONFIG_DRM_KUNIT_TEST) += tests/
 
 obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o
diff --git a/drivers/gpu/drm/selftests/Makefile b/drivers/gpu/drm/selftests/Makefile
deleted file mode 100644
index a4ebecb8146b..000000000000
--- a/drivers/gpu/drm/selftests/Makefile
+++ /dev/null
@@ -1,2 +0,0 @@ 
-# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o
diff --git a/drivers/gpu/drm/selftests/drm_mm_selftests.h b/drivers/gpu/drm/selftests/drm_mm_selftests.h
deleted file mode 100644
index 8c87c964176b..000000000000
--- a/drivers/gpu/drm/selftests/drm_mm_selftests.h
+++ /dev/null
@@ -1,28 +0,0 @@ 
-/* SPDX-License-Identifier: GPL-2.0 */
-/* List each unit test as selftest(name, function)
- *
- * The name is used as both an enum and expanded as igt__name to create
- * a module parameter. It must be unique and legal for a C identifier.
- *
- * Tests are executed in order by igt/drm_mm
- */
-selftest(sanitycheck, igt_sanitycheck) /* keep first (selfcheck for igt) */
-selftest(init, igt_init)
-selftest(debug, igt_debug)
-selftest(reserve, igt_reserve)
-selftest(insert, igt_insert)
-selftest(replace, igt_replace)
-selftest(insert_range, igt_insert_range)
-selftest(align, igt_align)
-selftest(frag, igt_frag)
-selftest(align32, igt_align32)
-selftest(align64, igt_align64)
-selftest(evict, igt_evict)
-selftest(evict_range, igt_evict_range)
-selftest(bottomup, igt_bottomup)
-selftest(lowest, igt_lowest)
-selftest(topdown, igt_topdown)
-selftest(highest, igt_highest)
-selftest(color, igt_color)
-selftest(color_evict, igt_color_evict)
-selftest(color_evict_range, igt_color_evict_range)
diff --git a/drivers/gpu/drm/selftests/drm_selftest.c b/drivers/gpu/drm/selftests/drm_selftest.c
deleted file mode 100644
index e29ed9faef5b..000000000000
--- a/drivers/gpu/drm/selftests/drm_selftest.c
+++ /dev/null
@@ -1,109 +0,0 @@ 
-/*
- * Copyright © 2016 Intel Corporation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#include <linux/compiler.h>
-
-#define selftest(name, func) __idx_##name,
-enum {
-#include TESTS
-};
-#undef selftest
-
-#define selftest(n, f) [__idx_##n] = { .name = #n, .func = f },
-static struct drm_selftest {
-	bool enabled;
-	const char *name;
-	int (*func)(void *);
-} selftests[] = {
-#include TESTS
-};
-#undef selftest
-
-/* Embed the line number into the parameter name so that we can order tests */
-#define param(n) __PASTE(igt__, __PASTE(__PASTE(__LINE__, __), n))
-#define selftest_0(n, func, id) \
-module_param_named(id, selftests[__idx_##n].enabled, bool, 0400);
-#define selftest(n, func) selftest_0(n, func, param(n))
-#include TESTS
-#undef selftest
-
-static void set_default_test_all(struct drm_selftest *st, unsigned long count)
-{
-	unsigned long i;
-
-	for (i = 0; i < count; i++)
-		if (st[i].enabled)
-			return;
-
-	for (i = 0; i < count; i++)
-		st[i].enabled = true;
-}
-
-static int run_selftests(struct drm_selftest *st,
-			 unsigned long count,
-			 void *data)
-{
-	int err = 0;
-
-	set_default_test_all(st, count);
-
-	/* Tests are listed in natural order in drm_*_selftests.h */
-	for (; count--; st++) {
-		if (!st->enabled)
-			continue;
-
-		pr_debug("drm: Running %s\n", st->name);
-		err = st->func(data);
-		if (err)
-			break;
-	}
-
-	if (WARN(err > 0 || err == -ENOTTY,
-		 "%s returned %d, conflicting with selftest's magic values!\n",
-		 st->name, err))
-		err = -1;
-
-	rcu_barrier();
-	return err;
-}
-
-static int __maybe_unused
-__drm_subtests(const char *caller,
-	       const struct drm_subtest *st,
-	       int count,
-	       void *data)
-{
-	int err;
-
-	for (; count--; st++) {
-		pr_debug("Running %s/%s\n", caller, st->name);
-		err = st->func(data);
-		if (err) {
-			pr_err("%s: %s failed with error %d\n",
-			       caller, st->name, err);
-			return err;
-		}
-	}
-
-	return 0;
-}
diff --git a/drivers/gpu/drm/selftests/drm_selftest.h b/drivers/gpu/drm/selftests/drm_selftest.h
deleted file mode 100644
index c784ec02ff53..000000000000
--- a/drivers/gpu/drm/selftests/drm_selftest.h
+++ /dev/null
@@ -1,41 +0,0 @@ 
-/*
- * Copyright © 2016 Intel Corporation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#ifndef __DRM_SELFTEST_H__
-#define __DRM_SELFTEST_H__
-
-struct drm_subtest {
-	int (*func)(void *data);
-	const char *name;
-};
-
-static int __drm_subtests(const char *caller,
-			  const struct drm_subtest *st,
-			  int count,
-			  void *data);
-#define drm_subtests(T, data) \
-	__drm_subtests(__func__, T, ARRAY_SIZE(T), data)
-
-#define SUBTEST(x) { x, #x }
-
-#endif /* __DRM_SELFTEST_H__ */
diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile
index cff59189598f..91b70f7d2769 100644
--- a/drivers/gpu/drm/tests/Makefile
+++ b/drivers/gpu/drm/tests/Makefile
@@ -2,4 +2,4 @@ 
 
 obj-$(CONFIG_DRM_KUNIT_TEST) += drm_format_helper_test.o drm_damage_helper_test.o \
 	drm_cmdline_parser_test.o drm_rect_test.o drm_format_test.o drm_plane_helper_test.o \
-	drm_dp_mst_helper_test.o drm_framebuffer_test.o drm_buddy_test.o
+	drm_dp_mst_helper_test.o drm_framebuffer_test.o drm_buddy_test.o drm_mm_test.o
diff --git a/drivers/gpu/drm/selftests/test-drm_mm.c b/drivers/gpu/drm/tests/drm_mm_test.c
similarity index 55%
rename from drivers/gpu/drm/selftests/test-drm_mm.c
rename to drivers/gpu/drm/tests/drm_mm_test.c
index b768b53c4aee..1e2c1aa524bd 100644
--- a/drivers/gpu/drm/selftests/test-drm_mm.c
+++ b/drivers/gpu/drm/tests/drm_mm_test.c
@@ -1,11 +1,12 @@ 
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Test cases for the drm_mm range manager
+ *
+ * Copyright (c) 2022 Arthur Grillo <arthur.grillo@usp.br>
  */
 
-#define pr_fmt(fmt) "drm_mm: " fmt
+#include <kunit/test.h>
 
-#include <linux/module.h>
 #include <linux/prime_numbers.h>
 #include <linux/slab.h>
 #include <linux/random.h>
@@ -16,9 +17,6 @@ 
 
 #include "../lib/drm_random.h"
 
-#define TESTS "drm_mm_selftests.h"
-#include "drm_selftest.h"
-
 static unsigned int random_seed;
 static unsigned int max_iterations = 8192;
 static unsigned int max_prime = 128;
@@ -45,13 +43,7 @@  static const struct insert_mode {
 	{}
 };
 
-static int igt_sanitycheck(void *ignored)
-{
-	pr_info("%s - ok!\n", __func__);
-	return 0;
-}
-
-static bool assert_no_holes(const struct drm_mm *mm)
+static bool assert_no_holes(struct kunit *test, const struct drm_mm *mm)
 {
 	struct drm_mm_node *hole;
 	u64 hole_start, __always_unused hole_end;
@@ -61,13 +53,14 @@  static bool assert_no_holes(const struct drm_mm *mm)
 	drm_mm_for_each_hole(hole, mm, hole_start, hole_end)
 		count++;
 	if (count) {
-		pr_err("Expected to find no holes (after reserve), found %lu instead\n", count);
+		KUNIT_FAIL(test,
+			   "Expected to find no holes (after reserve), found %lu instead\n", count);
 		return false;
 	}
 
 	drm_mm_for_each_node(hole, mm) {
 		if (drm_mm_hole_follows(hole)) {
-			pr_err("Hole follows node, expected none!\n");
+			KUNIT_FAIL(test, "Hole follows node, expected none!\n");
 			return false;
 		}
 	}
@@ -75,7 +68,7 @@  static bool assert_no_holes(const struct drm_mm *mm)
 	return true;
 }
 
-static bool assert_one_hole(const struct drm_mm *mm, u64 start, u64 end)
+static bool assert_one_hole(struct kunit *test, const struct drm_mm *mm, u64 start, u64 end)
 {
 	struct drm_mm_node *hole;
 	u64 hole_start, hole_end;
@@ -89,62 +82,62 @@  static bool assert_one_hole(const struct drm_mm *mm, u64 start, u64 end)
 	drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
 		if (start != hole_start || end != hole_end) {
 			if (ok)
-				pr_err("empty mm has incorrect hole, found (%llx, %llx), expect (%llx, %llx)\n",
-				       hole_start, hole_end,
-				       start, end);
+				KUNIT_FAIL(test,
+					   "empty mm has incorrect hole, found (%llx, %llx), expect (%llx, %llx)\n",
+					   hole_start, hole_end, start, end);
 			ok = false;
 		}
 		count++;
 	}
 	if (count != 1) {
-		pr_err("Expected to find one hole, found %lu instead\n", count);
+		KUNIT_FAIL(test, "Expected to find one hole, found %lu instead\n", count);
 		ok = false;
 	}
 
 	return ok;
 }
 
-static bool assert_continuous(const struct drm_mm *mm, u64 size)
+static bool assert_continuous(struct kunit *test, const struct drm_mm *mm, u64 size)
 {
 	struct drm_mm_node *node, *check, *found;
 	unsigned long n;
 	u64 addr;
 
-	if (!assert_no_holes(mm))
+	if (!assert_no_holes(test, mm))
 		return false;
 
 	n = 0;
 	addr = 0;
 	drm_mm_for_each_node(node, mm) {
 		if (node->start != addr) {
-			pr_err("node[%ld] list out of order, expected %llx found %llx\n",
-			       n, addr, node->start);
+			KUNIT_FAIL(test, "node[%ld] list out of order, expected %llx found %llx\n",
+				   n, addr, node->start);
 			return false;
 		}
 
 		if (node->size != size) {
-			pr_err("node[%ld].size incorrect, expected %llx, found %llx\n",
-			       n, size, node->size);
+			KUNIT_FAIL(test, "node[%ld].size incorrect, expected %llx, found %llx\n",
+				   n, size, node->size);
 			return false;
 		}
 
 		if (drm_mm_hole_follows(node)) {
-			pr_err("node[%ld] is followed by a hole!\n", n);
+			KUNIT_FAIL(test, "node[%ld] is followed by a hole!\n", n);
 			return false;
 		}
 
 		found = NULL;
 		drm_mm_for_each_node_in_range(check, mm, addr, addr + size) {
 			if (node != check) {
-				pr_err("lookup return wrong node, expected start %llx, found %llx\n",
-				       node->start, check->start);
+				KUNIT_FAIL(test,
+					   "lookup return wrong node, expected start %llx, found %llx\n",
+					   node->start, check->start);
 				return false;
 			}
 			found = check;
 		}
 		if (!found) {
-			pr_err("lookup failed for node %llx + %llx\n",
-			       addr, size);
+			KUNIT_FAIL(test, "lookup failed for node %llx + %llx\n", addr, size);
 			return false;
 		}
 
@@ -166,107 +159,96 @@  static u64 misalignment(struct drm_mm_node *node, u64 alignment)
 	return rem;
 }
 
-static bool assert_node(struct drm_mm_node *node, struct drm_mm *mm,
+static bool assert_node(struct kunit *test, struct drm_mm_node *node, struct drm_mm *mm,
 			u64 size, u64 alignment, unsigned long color)
 {
 	bool ok = true;
 
 	if (!drm_mm_node_allocated(node) || node->mm != mm) {
-		pr_err("node not allocated\n");
+		KUNIT_FAIL(test, "node not allocated\n");
 		ok = false;
 	}
 
 	if (node->size != size) {
-		pr_err("node has wrong size, found %llu, expected %llu\n",
-		       node->size, size);
+		KUNIT_FAIL(test, "node has wrong size, found %llu, expected %llu\n",
+			   node->size, size);
 		ok = false;
 	}
 
 	if (misalignment(node, alignment)) {
-		pr_err("node is misaligned, start %llx rem %llu, expected alignment %llu\n",
-		       node->start, misalignment(node, alignment), alignment);
+		KUNIT_FAIL(test,
+			   "node is misaligned, start %llx rem %llu, expected alignment %llu\n",
+			   node->start, misalignment(node, alignment), alignment);
 		ok = false;
 	}
 
 	if (node->color != color) {
-		pr_err("node has wrong color, found %lu, expected %lu\n",
-		       node->color, color);
+		KUNIT_FAIL(test, "node has wrong color, found %lu, expected %lu\n",
+			   node->color, color);
 		ok = false;
 	}
 
 	return ok;
 }
 
-#define show_mm(mm) do { \
-	struct drm_printer __p = drm_debug_printer(__func__); \
-	drm_mm_print((mm), &__p); } while (0)
-
-static int igt_init(void *ignored)
+static void igt_mm_init(struct kunit *test)
 {
 	const unsigned int size = 4096;
 	struct drm_mm mm;
 	struct drm_mm_node tmp;
-	int ret = -EINVAL;
 
 	/* Start with some simple checks on initialising the struct drm_mm */
 	memset(&mm, 0, sizeof(mm));
-	if (drm_mm_initialized(&mm)) {
-		pr_err("zeroed mm claims to be initialized\n");
-		return ret;
-	}
+	KUNIT_ASSERT_FALSE_MSG(test, drm_mm_initialized(&mm),
+			       "zeroed mm claims to be initialized\n");
 
 	memset(&mm, 0xff, sizeof(mm));
 	drm_mm_init(&mm, 0, size);
 	if (!drm_mm_initialized(&mm)) {
-		pr_err("mm claims not to be initialized\n");
+		KUNIT_FAIL(test, "mm claims not to be initialized\n");
 		goto out;
 	}
 
 	if (!drm_mm_clean(&mm)) {
-		pr_err("mm not empty on creation\n");
+		KUNIT_FAIL(test, "mm not empty on creation\n");
 		goto out;
 	}
 
 	/* After creation, it should all be one massive hole */
-	if (!assert_one_hole(&mm, 0, size)) {
-		ret = -EINVAL;
+	if (!assert_one_hole(test, &mm, 0, size)) {
+		KUNIT_FAIL(test, "");
 		goto out;
 	}
 
 	memset(&tmp, 0, sizeof(tmp));
 	tmp.start = 0;
 	tmp.size = size;
-	ret = drm_mm_reserve_node(&mm, &tmp);
-	if (ret) {
-		pr_err("failed to reserve whole drm_mm\n");
+	if (drm_mm_reserve_node(&mm, &tmp)) {
+		KUNIT_FAIL(test, "failed to reserve whole drm_mm\n");
 		goto out;
 	}
 
 	/* After filling the range entirely, there should be no holes */
-	if (!assert_no_holes(&mm)) {
-		ret = -EINVAL;
+	if (!assert_no_holes(test, &mm)) {
+		KUNIT_FAIL(test, "");
 		goto out;
 	}
 
 	/* And then after emptying it again, the massive hole should be back */
 	drm_mm_remove_node(&tmp);
-	if (!assert_one_hole(&mm, 0, size)) {
-		ret = -EINVAL;
+	if (!assert_one_hole(test, &mm, 0, size)) {
+		KUNIT_FAIL(test, "");
 		goto out;
 	}
 
 out:
-	if (ret)
-		show_mm(&mm);
 	drm_mm_takedown(&mm);
-	return ret;
 }
 
-static int igt_debug(void *ignored)
+static void igt_mm_debug(struct kunit *test)
 {
 	struct drm_mm mm;
 	struct drm_mm_node nodes[2];
-	int ret;
 
 	/* Create a small drm_mm with a couple of nodes and a few holes, and
 	 * check that the debug iterator doesn't explode over a trivial drm_mm.
@@ -277,24 +259,15 @@  static int igt_debug(void *ignored)
 	memset(nodes, 0, sizeof(nodes));
 	nodes[0].start = 512;
 	nodes[0].size = 1024;
-	ret = drm_mm_reserve_node(&mm, &nodes[0]);
-	if (ret) {
-		pr_err("failed to reserve node[0] {start=%lld, size=%lld)\n",
-		       nodes[0].start, nodes[0].size);
-		return ret;
-	}
+	KUNIT_ASSERT_FALSE_MSG(test, drm_mm_reserve_node(&mm, &nodes[0]),
+			       "failed to reserve node[0] {start=%lld, size=%lld)\n",
+			       nodes[0].start, nodes[0].size);
 
 	nodes[1].size = 1024;
 	nodes[1].start = 4096 - 512 - nodes[1].size;
-	ret = drm_mm_reserve_node(&mm, &nodes[1]);
-	if (ret) {
-		pr_err("failed to reserve node[1] {start=%lld, size=%lld)\n",
-		       nodes[1].start, nodes[1].size);
-		return ret;
-	}
-
-	show_mm(&mm);
-	return 0;
+	KUNIT_ASSERT_FALSE_MSG(test, drm_mm_reserve_node(&mm, &nodes[1]),
+			       "failed to reserve node[0] {start=%lld, size=%lld)\n",
+			       nodes[0].start, nodes[0].size);
 }
 
 static struct drm_mm_node *set_node(struct drm_mm_node *node,
@@ -305,7 +278,7 @@  static struct drm_mm_node *set_node(struct drm_mm_node *node,
 	return node;
 }
 
-static bool expect_reserve_fail(struct drm_mm *mm, struct drm_mm_node *node)
+static bool expect_reserve_fail(struct kunit *test, struct drm_mm *mm, struct drm_mm_node *node)
 {
 	int err;
 
@@ -314,17 +287,18 @@  static bool expect_reserve_fail(struct drm_mm *mm, struct drm_mm_node *node)
 		return true;
 
 	if (!err) {
-		pr_err("impossible reserve succeeded, node %llu + %llu\n",
-		       node->start, node->size);
+		KUNIT_FAIL(test, "impossible reserve succeeded, node %llu + %llu\n",
+			   node->start, node->size);
 		drm_mm_remove_node(node);
 	} else {
-		pr_err("impossible reserve failed with wrong error %d [expected %d], node %llu + %llu\n",
+		KUNIT_FAIL(test,
+			   "impossible reserve failed with wrong error %d [expected %d], node %llu + %llu\n",
 		       err, -ENOSPC, node->start, node->size);
 	}
 	return false;
 }
 
-static bool check_reserve_boundaries(struct drm_mm *mm,
+static bool check_reserve_boundaries(struct kunit *test, struct drm_mm *mm,
 				     unsigned int count,
 				     u64 size)
 {
@@ -339,29 +313,27 @@  static bool check_reserve_boundaries(struct drm_mm *mm,
 		B(size * count, 0),
 		B(-size, size),
 		B(-size, -size),
-		B(-size, 2*size),
+		B(-size, 2 * size),
 		B(0, -size),
 		B(size, -size),
-		B(count*size, size),
-		B(count*size, -size),
-		B(count*size, count*size),
-		B(count*size, -count*size),
-		B(count*size, -(count+1)*size),
-		B((count+1)*size, size),
-		B((count+1)*size, -size),
-		B((count+1)*size, -2*size),
+		B(count * size, size),
+		B(count * size, -size),
+		B(count * size, count * size),
+		B(count * size, -count * size),
+		B(count * size, -(count + 1) * size),
+		B((count + 1) * size, size),
+		B((count + 1) * size, -size),
+		B((count + 1) * size, -2 * size),
 #undef B
 	};
 	struct drm_mm_node tmp = {};
 	int n;
 
 	for (n = 0; n < ARRAY_SIZE(boundaries); n++) {
-		if (!expect_reserve_fail(mm,
-					 set_node(&tmp,
-						  boundaries[n].start,
-						  boundaries[n].size))) {
-			pr_err("boundary[%d:%s] failed, count=%u, size=%lld\n",
-			       n, boundaries[n].name, count, size);
+		if (!expect_reserve_fail(test, mm, set_node(&tmp, boundaries[n].start,
+							    boundaries[n].size))) {
+			KUNIT_FAIL(test, "boundary[%d:%s] failed, count=%u, size=%lld\n",
+				   n, boundaries[n].name, count, size);
 			return false;
 		}
 	}
@@ -369,7 +341,7 @@  static bool check_reserve_boundaries(struct drm_mm *mm,
 	return true;
 }
 
-static int __igt_reserve(unsigned int count, u64 size)
+static int __igt_reserve(struct kunit *test, unsigned int count, u64 size)
 {
 	DRM_RND_STATE(prng, random_seed);
 	struct drm_mm mm;
@@ -377,7 +349,7 @@  static int __igt_reserve(unsigned int count, u64 size)
 	unsigned int *order, n, m, o = 0;
 	int ret, err;
 
-	/* For exercising drm_mm_reserve_node(), we want to check that
+	/* For exercising drm_mm_reserve_node(struct kunit *test, ), we want to check that
 	 * reservations outside of the drm_mm range are rejected, and to
 	 * overlapping and otherwise already occupied ranges. Afterwards,
 	 * the tree and nodes should be intact.
@@ -392,13 +364,12 @@  static int __igt_reserve(unsigned int count, u64 size)
 		goto err;
 
 	nodes = vzalloc(array_size(count, sizeof(*nodes)));
-	if (!nodes)
-		goto err_order;
+	KUNIT_ASSERT_TRUE(test, nodes);
 
 	ret = -EINVAL;
 	drm_mm_init(&mm, 0, count * size);
 
-	if (!check_reserve_boundaries(&mm, count, size))
+	if (!check_reserve_boundaries(test, &mm, count, size))
 		goto out;
 
 	for (n = 0; n < count; n++) {
@@ -407,57 +378,53 @@  static int __igt_reserve(unsigned int count, u64 size)
 
 		err = drm_mm_reserve_node(&mm, &nodes[n]);
 		if (err) {
-			pr_err("reserve failed, step %d, start %llu\n",
-			       n, nodes[n].start);
+			KUNIT_FAIL(test, "reserve failed, step %d, start %llu\n",
+				   n, nodes[n].start);
 			ret = err;
 			goto out;
 		}
 
 		if (!drm_mm_node_allocated(&nodes[n])) {
-			pr_err("reserved node not allocated! step %d, start %llu\n",
-			       n, nodes[n].start);
+			KUNIT_FAIL(test, "reserved node not allocated! step %d, start %llu\n",
+				   n, nodes[n].start);
 			goto out;
 		}
 
-		if (!expect_reserve_fail(&mm, &nodes[n]))
+		if (!expect_reserve_fail(test, &mm, &nodes[n]))
 			goto out;
 	}
 
 	/* After random insertion the nodes should be in order */
-	if (!assert_continuous(&mm, size))
+	if (!assert_continuous(test, &mm, size))
 		goto out;
 
 	/* Repeated use should then fail */
 	drm_random_reorder(order, count, &prng);
 	for (n = 0; n < count; n++) {
-		if (!expect_reserve_fail(&mm,
-					 set_node(&tmp, order[n] * size, 1)))
+		if (!expect_reserve_fail(test, &mm, set_node(&tmp, order[n] * size, 1)))
 			goto out;
 
 		/* Remove and reinsert should work */
 		drm_mm_remove_node(&nodes[order[n]]);
 		err = drm_mm_reserve_node(&mm, &nodes[order[n]]);
 		if (err) {
-			pr_err("reserve failed, step %d, start %llu\n",
-			       n, nodes[n].start);
+			KUNIT_FAIL(test, "reserve failed, step %d, start %llu\n",
+				   n, nodes[n].start);
 			ret = err;
 			goto out;
 		}
 	}
 
-	if (!assert_continuous(&mm, size))
+	if (!assert_continuous(test, &mm, size))
 		goto out;
 
 	/* Overlapping use should then fail */
 	for (n = 0; n < count; n++) {
-		if (!expect_reserve_fail(&mm, set_node(&tmp, 0, size*count)))
+		if (!expect_reserve_fail(test, &mm, set_node(&tmp, 0, size * count)))
 			goto out;
 	}
 	for (n = 0; n < count; n++) {
-		if (!expect_reserve_fail(&mm,
-					 set_node(&tmp,
-						  size * n,
-						  size * (count - n))))
+		if (!expect_reserve_fail(test, &mm, set_node(&tmp, size * n, size * (count - n))))
 			goto out;
 	}
 
@@ -472,8 +439,8 @@  static int __igt_reserve(unsigned int count, u64 size)
 			node = &nodes[order[(o + m) % count]];
 			err = drm_mm_reserve_node(&mm, node);
 			if (err) {
-				pr_err("reserve failed, step %d/%d, start %llu\n",
-				       m, n, node->start);
+				KUNIT_FAIL(test, "reserve failed, step %d/%d, start %llu\n",
+					   m, n, node->start);
 				ret = err;
 				goto out;
 			}
@@ -481,7 +448,7 @@  static int __igt_reserve(unsigned int count, u64 size)
 
 		o += n;
 
-		if (!assert_continuous(&mm, size))
+		if (!assert_continuous(test, &mm, size))
 			goto out;
 	}
 
@@ -491,41 +458,30 @@  static int __igt_reserve(unsigned int count, u64 size)
 		drm_mm_remove_node(node);
 	drm_mm_takedown(&mm);
 	vfree(nodes);
-err_order:
 	kfree(order);
 err:
 	return ret;
 }
 
-static int igt_reserve(void *ignored)
+static void igt_mm_reserve(struct kunit *test)
 {
 	const unsigned int count = min_t(unsigned int, BIT(10), max_iterations);
-	int n, ret;
+	int n;
 
 	for_each_prime_number_from(n, 1, 54) {
 		u64 size = BIT_ULL(n);
 
-		ret = __igt_reserve(count, size - 1);
-		if (ret)
-			return ret;
-
-		ret = __igt_reserve(count, size);
-		if (ret)
-			return ret;
-
-		ret = __igt_reserve(count, size + 1);
-		if (ret)
-			return ret;
+		KUNIT_ASSERT_FALSE(test, __igt_reserve(test, count, size - 1));
+		KUNIT_ASSERT_FALSE(test, __igt_reserve(test, count, size));
+		KUNIT_ASSERT_FALSE(test, __igt_reserve(test, count, size + 1));
 
 		cond_resched();
 	}
-
-	return 0;
 }
 
-static bool expect_insert(struct drm_mm *mm, struct drm_mm_node *node,
-			  u64 size, u64 alignment, unsigned long color,
-			  const struct insert_mode *mode)
+static bool expect_insert(struct kunit *test, struct drm_mm *mm,
+			  struct drm_mm_node *node, u64 size, u64 alignment, unsigned long color,
+			const struct insert_mode *mode)
 {
 	int err;
 
@@ -533,12 +489,13 @@  static bool expect_insert(struct drm_mm *mm, struct drm_mm_node *node,
 					 size, alignment, color,
 					 mode->mode);
 	if (err) {
-		pr_err("insert (size=%llu, alignment=%llu, color=%lu, mode=%s) failed with err=%d\n",
-		       size, alignment, color, mode->name, err);
+		KUNIT_FAIL(test,
+			   "insert (size=%llu, alignment=%llu, color=%lu, mode=%s) failed with err=%d\n",
+			   size, alignment, color, mode->name, err);
 		return false;
 	}
 
-	if (!assert_node(node, mm, size, alignment, color)) {
+	if (!assert_node(test, node, mm, size, alignment, color)) {
 		drm_mm_remove_node(node);
 		return false;
 	}
@@ -546,7 +503,7 @@  static bool expect_insert(struct drm_mm *mm, struct drm_mm_node *node,
 	return true;
 }
 
-static bool expect_insert_fail(struct drm_mm *mm, u64 size)
+static bool expect_insert_fail(struct kunit *test, struct drm_mm *mm, u64 size)
 {
 	struct drm_mm_node tmp = {};
 	int err;
@@ -556,17 +513,18 @@  static bool expect_insert_fail(struct drm_mm *mm, u64 size)
 		return true;
 
 	if (!err) {
-		pr_err("impossible insert succeeded, node %llu + %llu\n",
-		       tmp.start, tmp.size);
+		KUNIT_FAIL(test, "impossible insert succeeded, node %llu + %llu\n",
+			   tmp.start, tmp.size);
 		drm_mm_remove_node(&tmp);
 	} else {
-		pr_err("impossible insert failed with wrong error %d [expected %d], size %llu\n",
-		       err, -ENOSPC, size);
+		KUNIT_FAIL(test,
+			   "impossible insert failed with wrong error %d [expected %d], size %llu\n",
+			   err, -ENOSPC, size);
 	}
 	return false;
 }
 
-static int __igt_insert(unsigned int count, u64 size, bool replace)
+static int __igt_insert(struct kunit *test, unsigned int count, u64 size, bool replace)
 {
 	DRM_RND_STATE(prng, random_seed);
 	const struct insert_mode *mode;
@@ -582,8 +540,7 @@  static int __igt_insert(unsigned int count, u64 size, bool replace)
 
 	ret = -ENOMEM;
 	nodes = vmalloc(array_size(count, sizeof(*nodes)));
-	if (!nodes)
-		goto err;
+	KUNIT_ASSERT_TRUE(test, nodes);
 
 	order = drm_random_order(count, &prng);
 	if (!order)
@@ -598,41 +555,43 @@  static int __igt_insert(unsigned int count, u64 size, bool replace)
 
 			node = replace ? &tmp : &nodes[n];
 			memset(node, 0, sizeof(*node));
-			if (!expect_insert(&mm, node, size, 0, n, mode)) {
-				pr_err("%s insert failed, size %llu step %d\n",
-				       mode->name, size, n);
+			if (!expect_insert(test, &mm, node, size, 0, n, mode)) {
+				KUNIT_FAIL(test, "%s insert failed, size %llu step %d\n",
+					   mode->name, size, n);
 				goto out;
 			}
 
 			if (replace) {
 				drm_mm_replace_node(&tmp, &nodes[n]);
 				if (drm_mm_node_allocated(&tmp)) {
-					pr_err("replaced old-node still allocated! step %d\n",
-					       n);
+					KUNIT_FAIL(test,
+						   "replaced old-node still allocated! step %d\n",
+						   n);
 					goto out;
 				}
 
-				if (!assert_node(&nodes[n], &mm, size, 0, n)) {
-					pr_err("replaced node did not inherit parameters, size %llu step %d\n",
-					       size, n);
+				if (!assert_node(test, &nodes[n], &mm, size, 0, n)) {
+					KUNIT_FAIL(test,
+						   "replaced node did not inherit parameters, size %llu step %d\n",
+						   size, n);
 					goto out;
 				}
 
 				if (tmp.start != nodes[n].start) {
-					pr_err("replaced node mismatch location expected [%llx + %llx], found [%llx + %llx]\n",
-					       tmp.start, size,
-					       nodes[n].start, nodes[n].size);
+					KUNIT_FAIL(test,
+						   "replaced node mismatch location expected [%llx + %llx], found [%llx + %llx]\n",
+						   tmp.start, size, nodes[n].start, nodes[n].size);
 					goto out;
 				}
 			}
 		}
 
 		/* After random insertion the nodes should be in order */
-		if (!assert_continuous(&mm, size))
+		if (!assert_continuous(test, &mm, size))
 			goto out;
 
 		/* Repeated use should then fail */
-		if (!expect_insert_fail(&mm, size))
+		if (!expect_insert_fail(test, &mm, size))
 			goto out;
 
 		/* Remove one and reinsert, as the only hole it should refill itself */
@@ -640,19 +599,20 @@  static int __igt_insert(unsigned int count, u64 size, bool replace)
 			u64 addr = nodes[n].start;
 
 			drm_mm_remove_node(&nodes[n]);
-			if (!expect_insert(&mm, &nodes[n], size, 0, n, mode)) {
-				pr_err("%s reinsert failed, size %llu step %d\n",
-				       mode->name, size, n);
+			if (!expect_insert(test, &mm, &nodes[n], size, 0, n, mode)) {
+				KUNIT_FAIL(test, "%s reinsert failed, size %llu step %d\n",
+					   mode->name, size, n);
 				goto out;
 			}
 
 			if (nodes[n].start != addr) {
-				pr_err("%s reinsert node moved, step %d, expected %llx, found %llx\n",
-				       mode->name, n, addr, nodes[n].start);
+				KUNIT_FAIL(test,
+					   "%s reinsert node moved, step %d, expected %llx, found %llx\n",
+					   mode->name, n, addr, nodes[n].start);
 				goto out;
 			}
 
-			if (!assert_continuous(&mm, size))
+			if (!assert_continuous(test, &mm, size))
 				goto out;
 		}
 
@@ -665,19 +625,20 @@  static int __igt_insert(unsigned int count, u64 size, bool replace)
 
 			for (m = 0; m < n; m++) {
 				node = &nodes[order[(o + m) % count]];
-				if (!expect_insert(&mm, node, size, 0, n, mode)) {
-					pr_err("%s multiple reinsert failed, size %llu step %d\n",
-					       mode->name, size, n);
+				if (!expect_insert(test, &mm, node, size, 0, n, mode)) {
+					KUNIT_FAIL(test,
+						   "%s multiple reinsert failed, size %llu step %d\n",
+							   mode->name, size, n);
 					goto out;
 				}
 			}
 
 			o += n;
 
-			if (!assert_continuous(&mm, size))
+			if (!assert_continuous(test, &mm, size))
 				goto out;
 
-			if (!expect_insert_fail(&mm, size))
+			if (!expect_insert_fail(test, &mm, size))
 				goto out;
 		}
 
@@ -696,42 +657,29 @@  static int __igt_insert(unsigned int count, u64 size, bool replace)
 	kfree(order);
 err_nodes:
 	vfree(nodes);
-err:
 	return ret;
 }
 
-static int igt_insert(void *ignored)
+static void igt_mm_insert(struct kunit *test)
 {
 	const unsigned int count = min_t(unsigned int, BIT(10), max_iterations);
 	unsigned int n;
-	int ret;
 
 	for_each_prime_number_from(n, 1, 54) {
 		u64 size = BIT_ULL(n);
 
-		ret = __igt_insert(count, size - 1, false);
-		if (ret)
-			return ret;
-
-		ret = __igt_insert(count, size, false);
-		if (ret)
-			return ret;
-
-		ret = __igt_insert(count, size + 1, false);
-		if (ret)
-			return ret;
+		KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size - 1, false));
+		KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size, false));
+		KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size + 1, false));
 
 		cond_resched();
 	}
-
-	return 0;
 }
 
-static int igt_replace(void *ignored)
+static void igt_mm_replace(struct kunit *test)
 {
 	const unsigned int count = min_t(unsigned int, BIT(10), max_iterations);
 	unsigned int n;
-	int ret;
 
 	/* Reuse igt_insert to exercise replacement by inserting a dummy node,
 	 * then replacing it with the intended node. We want to check that
@@ -742,28 +690,17 @@  static int igt_replace(void *ignored)
 	for_each_prime_number_from(n, 1, 54) {
 		u64 size = BIT_ULL(n);
 
-		ret = __igt_insert(count, size - 1, true);
-		if (ret)
-			return ret;
-
-		ret = __igt_insert(count, size, true);
-		if (ret)
-			return ret;
-
-		ret = __igt_insert(count, size + 1, true);
-		if (ret)
-			return ret;
+		KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size - 1, true));
+		KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size, true));
+		KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size + 1, true));
 
 		cond_resched();
 	}
-
-	return 0;
 }
 
-static bool expect_insert_in_range(struct drm_mm *mm, struct drm_mm_node *node,
+static bool expect_insert_in_range(struct kunit *test, struct drm_mm *mm, struct drm_mm_node *node,
 				   u64 size, u64 alignment, unsigned long color,
-				   u64 range_start, u64 range_end,
-				   const struct insert_mode *mode)
+				   u64 range_start, u64 range_end, const struct insert_mode *mode)
 {
 	int err;
 
@@ -772,13 +709,14 @@  static bool expect_insert_in_range(struct drm_mm *mm, struct drm_mm_node *node,
 					  range_start, range_end,
 					  mode->mode);
 	if (err) {
-		pr_err("insert (size=%llu, alignment=%llu, color=%lu, mode=%s) nto range [%llx, %llx] failed with err=%d\n",
-		       size, alignment, color, mode->name,
-		       range_start, range_end, err);
+		KUNIT_FAIL(test,
+			   "insert (size=%llu, alignment=%llu, color=%lu, mode=%s) nto range [%llx, %llx] failed with err=%d\n",
+				   size, alignment, color, mode->name,
+				   range_start, range_end, err);
 		return false;
 	}
 
-	if (!assert_node(node, mm, size, alignment, color)) {
+	if (!assert_node(test, node, mm, size, alignment, color)) {
 		drm_mm_remove_node(node);
 		return false;
 	}
@@ -786,67 +724,63 @@  static bool expect_insert_in_range(struct drm_mm *mm, struct drm_mm_node *node,
 	return true;
 }
 
-static bool expect_insert_in_range_fail(struct drm_mm *mm,
-					u64 size,
-					u64 range_start,
-					u64 range_end)
+static bool expect_insert_in_range_fail(struct kunit *test, struct drm_mm *mm,
+					u64 size, u64 range_start, u64 range_end)
 {
 	struct drm_mm_node tmp = {};
 	int err;
 
-	err = drm_mm_insert_node_in_range(mm, &tmp,
-					  size, 0, 0,
-					  range_start, range_end,
+	err = drm_mm_insert_node_in_range(mm, &tmp, size, 0, 0, range_start, range_end,
 					  0);
 	if (likely(err == -ENOSPC))
 		return true;
 
 	if (!err) {
-		pr_err("impossible insert succeeded, node %llx + %llu, range [%llx, %llx]\n",
-		       tmp.start, tmp.size, range_start, range_end);
+		KUNIT_FAIL(test,
+			   "impossible insert succeeded, node %llx + %llu, range [%llx, %llx]\n",
+				   tmp.start, tmp.size, range_start, range_end);
 		drm_mm_remove_node(&tmp);
 	} else {
-		pr_err("impossible insert failed with wrong error %d [expected %d], size %llu, range [%llx, %llx]\n",
-		       err, -ENOSPC, size, range_start, range_end);
+		KUNIT_FAIL(test,
+			   "impossible insert failed with wrong error %d [expected %d], size %llu, range [%llx, %llx]\n",
+				   err, -ENOSPC, size, range_start, range_end);
 	}
 
 	return false;
 }
 
-static bool assert_contiguous_in_range(struct drm_mm *mm,
-				       u64 size,
-				       u64 start,
-				       u64 end)
+static bool assert_contiguous_in_range(struct kunit *test, struct drm_mm *mm,
+				       u64 size, u64 start, u64 end)
 {
 	struct drm_mm_node *node;
 	unsigned int n;
 
-	if (!expect_insert_in_range_fail(mm, size, start, end))
+	if (!expect_insert_in_range_fail(test, mm, size, start, end))
 		return false;
 
 	n = div64_u64(start + size - 1, size);
 	drm_mm_for_each_node(node, mm) {
 		if (node->start < start || node->start + node->size > end) {
-			pr_err("node %d out of range, address [%llx + %llu], range [%llx, %llx]\n",
-			       n, node->start, node->start + node->size, start, end);
+			KUNIT_FAIL(test,
+				   "node %d out of range, address [%llx + %llu], range [%llx, %llx]\n",
+					   n, node->start, node->start + node->size, start, end);
 			return false;
 		}
 
 		if (node->start != n * size) {
-			pr_err("node %d out of order, expected start %llx, found %llx\n",
-			       n, n * size, node->start);
+			KUNIT_FAIL(test, "node %d out of order, expected start %llx, found %llx\n",
+				   n, n * size, node->start);
 			return false;
 		}
 
 		if (node->size != size) {
-			pr_err("node %d has wrong size, expected size %llx, found %llx\n",
-			       n, size, node->size);
+			KUNIT_FAIL(test, "node %d has wrong size, expected size %llx, found %llx\n",
+				   n, size, node->size);
 			return false;
 		}
 
-		if (drm_mm_hole_follows(node) &&
-		    drm_mm_hole_node_end(node) < end) {
-			pr_err("node %d is followed by a hole!\n", n);
+		if (drm_mm_hole_follows(node) && drm_mm_hole_node_end(node) < end) {
+			KUNIT_FAIL(test, "node %d is followed by a hole!\n", n);
 			return false;
 		}
 
@@ -856,8 +790,8 @@  static bool assert_contiguous_in_range(struct drm_mm *mm,
 	if (start > 0) {
 		node = __drm_mm_interval_first(mm, 0, start - 1);
 		if (drm_mm_node_allocated(node)) {
-			pr_err("node before start: node=%llx+%llu, start=%llx\n",
-			       node->start, node->size, start);
+			KUNIT_FAIL(test, "node before start: node=%llx+%llu, start=%llx\n",
+				   node->start, node->size, start);
 			return false;
 		}
 	}
@@ -865,8 +799,8 @@  static bool assert_contiguous_in_range(struct drm_mm *mm,
 	if (end < U64_MAX) {
 		node = __drm_mm_interval_first(mm, end, U64_MAX);
 		if (drm_mm_node_allocated(node)) {
-			pr_err("node after end: node=%llx+%llu, end=%llx\n",
-			       node->start, node->size, end);
+			KUNIT_FAIL(test, "node after end: node=%llx+%llu, end=%llx\n",
+				   node->start, node->size, end);
 			return false;
 		}
 	}
@@ -874,7 +808,7 @@  static bool assert_contiguous_in_range(struct drm_mm *mm,
 	return true;
 }
 
-static int __igt_insert_range(unsigned int count, u64 size, u64 start, u64 end)
+static int __igt_insert_range(struct kunit *test, unsigned int count, u64 size, u64 start, u64 end)
 {
 	const struct insert_mode *mode;
 	struct drm_mm mm;
@@ -886,14 +820,13 @@  static int __igt_insert_range(unsigned int count, u64 size, u64 start, u64 end)
 	DRM_MM_BUG_ON(!size);
 	DRM_MM_BUG_ON(end <= start);
 
-	/* Very similar to __igt_insert(), but now instead of populating the
+	/* Very similar to __igt_insert(struct kunit *test, ), but now instead of populating the
 	 * full range of the drm_mm, we try to fill a small portion of it.
 	 */
 
 	ret = -ENOMEM;
 	nodes = vzalloc(array_size(count, sizeof(*nodes)));
-	if (!nodes)
-		goto err;
+	KUNIT_ASSERT_TRUE(test, nodes);
 
 	ret = -EINVAL;
 	drm_mm_init(&mm, 0, count * size);
@@ -903,20 +836,19 @@  static int __igt_insert_range(unsigned int count, u64 size, u64 start, u64 end)
 
 	for (mode = insert_modes; mode->name; mode++) {
 		for (n = start_n; n <= end_n; n++) {
-			if (!expect_insert_in_range(&mm, &nodes[n],
-						    size, size, n,
+			if (!expect_insert_in_range(test, &mm, &nodes[n], size, size, n,
 						    start, end, mode)) {
-				pr_err("%s insert failed, size %llu, step %d [%d, %d], range [%llx, %llx]\n",
-				       mode->name, size, n,
-				       start_n, end_n,
-				       start, end);
+				KUNIT_FAIL(test,
+					   "%s insert failed, size %llu, step %d [%d, %d], range [%llx, %llx]\n",
+						   mode->name, size, n, start_n, end_n, start, end);
 				goto out;
 			}
 		}
 
-		if (!assert_contiguous_in_range(&mm, size, start, end)) {
-			pr_err("%s: range [%llx, %llx] not full after initialisation, size=%llu\n",
-			       mode->name, start, end, size);
+		if (!assert_contiguous_in_range(test, &mm, size, start, end)) {
+			KUNIT_FAIL(test,
+				   "%s: range [%llx, %llx] not full after initialisation, size=%llu\n",
+				   mode->name, start, end, size);
 			goto out;
 		}
 
@@ -925,23 +857,24 @@  static int __igt_insert_range(unsigned int count, u64 size, u64 start, u64 end)
 			u64 addr = nodes[n].start;
 
 			drm_mm_remove_node(&nodes[n]);
-			if (!expect_insert_in_range(&mm, &nodes[n],
-						    size, size, n,
+			if (!expect_insert_in_range(test, &mm, &nodes[n], size, size, n,
 						    start, end, mode)) {
-				pr_err("%s reinsert failed, step %d\n", mode->name, n);
+				KUNIT_FAIL(test, "%s reinsert failed, step %d\n", mode->name, n);
 				goto out;
 			}
 
 			if (nodes[n].start != addr) {
-				pr_err("%s reinsert node moved, step %d, expected %llx, found %llx\n",
-				       mode->name, n, addr, nodes[n].start);
+				KUNIT_FAIL(test,
+					   "%s reinsert node moved, step %d, expected %llx, found %llx\n",
+					   mode->name, n, addr, nodes[n].start);
 				goto out;
 			}
 		}
 
-		if (!assert_contiguous_in_range(&mm, size, start, end)) {
-			pr_err("%s: range [%llx, %llx] not full after reinsertion, size=%llu\n",
-			       mode->name, start, end, size);
+		if (!assert_contiguous_in_range(test, &mm, size, start, end)) {
+			KUNIT_FAIL(test,
+				   "%s: range [%llx, %llx] not full after reinsertion, size=%llu\n",
+				   mode->name, start, end, size);
 			goto out;
 		}
 
@@ -958,11 +891,10 @@  static int __igt_insert_range(unsigned int count, u64 size, u64 start, u64 end)
 		drm_mm_remove_node(node);
 	drm_mm_takedown(&mm);
 	vfree(nodes);
-err:
 	return ret;
 }
 
-static int insert_outside_range(void)
+static int insert_outside_range(struct kunit *test)
 {
 	struct drm_mm mm;
 	const unsigned int start = 1024;
@@ -971,81 +903,58 @@  static int insert_outside_range(void)
 
 	drm_mm_init(&mm, start, size);
 
-	if (!expect_insert_in_range_fail(&mm, 1, 0, start))
+	if (!expect_insert_in_range_fail(test, &mm, 1, 0, start))
 		return -EINVAL;
 
-	if (!expect_insert_in_range_fail(&mm, size,
-					 start - size/2, start + (size+1)/2))
+	if (!expect_insert_in_range_fail(test, &mm, size,
+					 start - size / 2, start + (size + 1) / 2))
 		return -EINVAL;
 
-	if (!expect_insert_in_range_fail(&mm, size,
-					 end - (size+1)/2, end + size/2))
+	if (!expect_insert_in_range_fail(test, &mm, size,
+					 end - (size + 1) / 2, end + size / 2))
 		return -EINVAL;
 
-	if (!expect_insert_in_range_fail(&mm, 1, end, end + size))
+	if (!expect_insert_in_range_fail(test, &mm, 1, end, end + size))
 		return -EINVAL;
 
 	drm_mm_takedown(&mm);
 	return 0;
 }
 
-static int igt_insert_range(void *ignored)
+static void igt_mm_insert_range(struct kunit *test)
 {
 	const unsigned int count = min_t(unsigned int, BIT(13), max_iterations);
 	unsigned int n;
-	int ret;
 
 	/* Check that requests outside the bounds of drm_mm are rejected. */
-	ret = insert_outside_range();
-	if (ret)
-		return ret;
+	KUNIT_ASSERT_FALSE(test, insert_outside_range(test));
 
 	for_each_prime_number_from(n, 1, 50) {
 		const u64 size = BIT_ULL(n);
 		const u64 max = count * size;
 
-		ret = __igt_insert_range(count, size, 0, max);
-		if (ret)
-			return ret;
-
-		ret = __igt_insert_range(count, size, 1, max);
-		if (ret)
-			return ret;
-
-		ret = __igt_insert_range(count, size, 0, max - 1);
-		if (ret)
-			return ret;
-
-		ret = __igt_insert_range(count, size, 0, max/2);
-		if (ret)
-			return ret;
-
-		ret = __igt_insert_range(count, size, max/2, max);
-		if (ret)
-			return ret;
-
-		ret = __igt_insert_range(count, size, max/4+1, 3*max/4-1);
-		if (ret)
-			return ret;
+		KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size, 0, max));
+		KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size, 1, max));
+		KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size, 0, max - 1));
+		KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size, 0, max / 2));
+		KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size, max / 2, max / 2));
+		KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size,
+							    max / 4 + 1, 3 * max / 4 - 1));
 
 		cond_resched();
 	}
-
-	return 0;
 }
 
-static int prepare_igt_frag(struct drm_mm *mm,
-			    struct drm_mm_node *nodes,
-			    unsigned int num_insert,
+static int prepare_igt_frag(struct kunit *test, struct drm_mm *mm,
+			    struct drm_mm_node *nodes, unsigned int num_insert,
 			    const struct insert_mode *mode)
 {
 	unsigned int size = 4096;
 	unsigned int i;
 
 	for (i = 0; i < num_insert; i++) {
-		if (!expect_insert(mm, &nodes[i], size, 0, i,
-				   mode) != 0) {
-			pr_err("%s insert failed\n", mode->name);
+		if (!expect_insert(test, mm, &nodes[i], size, 0, i, mode) != 0) {
+			KUNIT_FAIL(test, "%s insert failed\n", mode->name);
 			return -EINVAL;
 		}
 	}
@@ -1057,12 +966,10 @@  static int prepare_igt_frag(struct drm_mm *mm,
 	}
 
 	return 0;
-
 }
 
-static u64 get_insert_time(struct drm_mm *mm,
-			   unsigned int num_insert,
-			   struct drm_mm_node *nodes,
+static u64 get_insert_time(struct kunit *test, struct drm_mm *mm,
+			   unsigned int num_insert, struct drm_mm_node *nodes,
 			   const struct insert_mode *mode)
 {
 	unsigned int size = 8192;
@@ -1071,8 +978,8 @@  static u64 get_insert_time(struct drm_mm *mm,
 
 	start = ktime_get();
 	for (i = 0; i < num_insert; i++) {
-		if (!expect_insert(mm, &nodes[i], size, 0, i, mode) != 0) {
-			pr_err("%s insert failed\n", mode->name);
+		if (!expect_insert(test, mm, &nodes[i], size, 0, i, mode) != 0) {
+			KUNIT_FAIL(test, "%s insert failed\n", mode->name);
 			return 0;
 		}
 	}
@@ -1080,28 +987,26 @@  static u64 get_insert_time(struct drm_mm *mm,
 	return ktime_to_ns(ktime_sub(ktime_get(), start));
 }
 
-static int igt_frag(void *ignored)
+static void igt_mm_frag(struct kunit *test)
 {
 	struct drm_mm mm;
 	const struct insert_mode *mode;
 	struct drm_mm_node *nodes, *node, *next;
 	unsigned int insert_size = 10000;
 	unsigned int scale_factor = 4;
-	int ret = -EINVAL;
 
 	/* We need 4 * insert_size nodes to hold intermediate allocated
 	 * drm_mm nodes.
-	 * 1 times for prepare_igt_frag()
-	 * 1 times for get_insert_time()
-	 * 2 times for get_insert_time()
+	 * 1 times for prepare_igt_frag(struct kunit *test, )
+	 * 1 times for get_insert_time(struct kunit *test, )
+	 * 2 times for get_insert_time(struct kunit *test, )
 	 */
 	nodes = vzalloc(array_size(insert_size * 4, sizeof(*nodes)));
-	if (!nodes)
-		return -ENOMEM;
+	KUNIT_ASSERT_TRUE(test, nodes);
 
 	/* For BOTTOMUP and TOPDOWN, we first fragment the
-	 * address space using prepare_igt_frag() and then try to verify
-	 * that that insertions scale quadratically from 10k to 20k insertions
+	 * address space using prepare_igt_frag(struct kunit *test, ) and then try to verify
+	 * that insertions scale quadratically from 10k to 20k insertions
 	 */
 	drm_mm_init(&mm, 1, U64_MAX - 2);
 	for (mode = insert_modes; mode->name; mode++) {
@@ -1111,28 +1016,25 @@  static int igt_frag(void *ignored)
 		    mode->mode != DRM_MM_INSERT_HIGH)
 			continue;
 
-		ret = prepare_igt_frag(&mm, nodes, insert_size, mode);
-		if (ret)
+		if (prepare_igt_frag(test, &mm, nodes, insert_size, mode))
 			goto err;
 
-		insert_time1 = get_insert_time(&mm, insert_size,
+		insert_time1 = get_insert_time(test, &mm, insert_size,
 					       nodes + insert_size, mode);
 		if (insert_time1 == 0)
 			goto err;
 
-		insert_time2 = get_insert_time(&mm, (insert_size * 2),
+		insert_time2 = get_insert_time(test, &mm, (insert_size * 2),
 					       nodes + insert_size * 2, mode);
 		if (insert_time2 == 0)
 			goto err;
 
-		pr_info("%s fragmented insert of %u and %u insertions took %llu and %llu nsecs\n",
-			mode->name, insert_size, insert_size * 2,
-			insert_time1, insert_time2);
+		kunit_info(test, "%s fragmented insert of %u and %u insertions took %llu and %llu nsecs\n",
+			   mode->name, insert_size, insert_size * 2, insert_time1, insert_time2);
 
 		if (insert_time2 > (scale_factor * insert_time1)) {
-			pr_err("%s fragmented insert took %llu nsecs more\n",
-			       mode->name,
-			       insert_time2 - (scale_factor * insert_time1));
+			KUNIT_FAIL(test, "%s fragmented insert took %llu nsecs more\n",
+				   mode->name, insert_time2 - (scale_factor * insert_time1));
 			goto err;
 		}
 
@@ -1140,24 +1042,20 @@  static int igt_frag(void *ignored)
 			drm_mm_remove_node(node);
 	}
 
-	ret = 0;
 err:
 	drm_mm_for_each_node_safe(node, next, &mm)
 		drm_mm_remove_node(node);
 	drm_mm_takedown(&mm);
 	vfree(nodes);
-
-	return ret;
 }
 
-static int igt_align(void *ignored)
+static void igt_mm_align(struct kunit *test)
 {
 	const struct insert_mode *mode;
 	const unsigned int max_count = min(8192u, max_prime);
 	struct drm_mm mm;
 	struct drm_mm_node *nodes, *node, *next;
 	unsigned int prime;
-	int ret = -EINVAL;
 
 	/* For each of the possible insertion modes, we pick a few
 	 * arbitrary alignments and check that the inserted node
@@ -1165,8 +1063,7 @@  static int igt_align(void *ignored)
 	 */
 
 	nodes = vzalloc(array_size(max_count, sizeof(*nodes)));
-	if (!nodes)
-		goto err;
+	KUNIT_ASSERT_TRUE(test, nodes);
 
 	drm_mm_init(&mm, 1, U64_MAX - 2);
 
@@ -1176,11 +1073,9 @@  static int igt_align(void *ignored)
 		for_each_prime_number_from(prime, 1, max_count) {
 			u64 size = next_prime_number(prime);
 
-			if (!expect_insert(&mm, &nodes[i],
-					   size, prime, i,
-					   mode)) {
-				pr_err("%s insert failed with alignment=%d",
-				       mode->name, prime);
+			if (!expect_insert(test, &mm, &nodes[i], size, prime, i, mode)) {
+				KUNIT_FAIL(test, "%s insert failed with alignment=%d",
+					   mode->name, prime);
 				goto out;
 			}
 
@@ -1194,22 +1089,18 @@  static int igt_align(void *ignored)
 		cond_resched();
 	}
 
-	ret = 0;
 out:
 	drm_mm_for_each_node_safe(node, next, &mm)
 		drm_mm_remove_node(node);
 	drm_mm_takedown(&mm);
 	vfree(nodes);
-err:
-	return ret;
 }
 
-static int igt_align_pot(int max)
+static void igt_align_pot(struct kunit *test, int max)
 {
 	struct drm_mm mm;
 	struct drm_mm_node *node, *next;
 	int bit;
-	int ret = -EINVAL;
 
 	/* Check that we can align to the full u64 address space */
 
@@ -1220,51 +1111,45 @@  static int igt_align_pot(int max)
 
 		node = kzalloc(sizeof(*node), GFP_KERNEL);
 		if (!node) {
-			ret = -ENOMEM;
+			KUNIT_FAIL(test, "failed to allocate node");
 			goto out;
 		}
 
 		align = BIT_ULL(bit);
-		size = BIT_ULL(bit-1) + 1;
-		if (!expect_insert(&mm, node,
-				   size, align, bit,
-				   &insert_modes[0])) {
-			pr_err("insert failed with alignment=%llx [%d]",
-			       align, bit);
+		size = BIT_ULL(bit - 1) + 1;
+		if (!expect_insert(test, &mm, node, size, align, bit, &insert_modes[0])) {
+			KUNIT_FAIL(test, "insert failed with alignment=%llx [%d]", align, bit);
 			goto out;
 		}
 
 		cond_resched();
 	}
 
-	ret = 0;
 out:
 	drm_mm_for_each_node_safe(node, next, &mm) {
 		drm_mm_remove_node(node);
 		kfree(node);
 	}
 	drm_mm_takedown(&mm);
-	return ret;
 }
 
-static int igt_align32(void *ignored)
+static void igt_mm_align32(struct kunit *test)
 {
-	return igt_align_pot(32);
+	igt_align_pot(test, 32);
 }
 
-static int igt_align64(void *ignored)
+static void igt_mm_align64(struct kunit *test)
 {
-	return igt_align_pot(64);
+	igt_align_pot(test, 64);
 }
 
-static void show_scan(const struct drm_mm_scan *scan)
+static void show_scan(struct kunit *test, const struct drm_mm_scan *scan)
 {
-	pr_info("scan: hit [%llx, %llx], size=%lld, align=%lld, color=%ld\n",
-		scan->hit_start, scan->hit_end,
-		scan->size, scan->alignment, scan->color);
+	kunit_info(test, "scan: hit [%llx, %llx], size=%lld, align=%lld, color=%ld\n",
+		   scan->hit_start, scan->hit_end, scan->size, scan->alignment, scan->color);
 }
 
-static void show_holes(const struct drm_mm *mm, int count)
+static void show_holes(struct kunit *test, const struct drm_mm *mm, int count)
 {
 	u64 hole_start, hole_end;
 	struct drm_mm_node *hole;
@@ -1274,19 +1159,15 @@  static void show_holes(const struct drm_mm *mm, int count)
 		const char *node1 = NULL, *node2 = NULL;
 
 		if (drm_mm_node_allocated(hole))
-			node1 = kasprintf(GFP_KERNEL,
-					  "[%llx + %lld, color=%ld], ",
+			node1 = kasprintf(GFP_KERNEL, "[%llx + %lld, color=%ld], ",
 					  hole->start, hole->size, hole->color);
 
 		if (drm_mm_node_allocated(next))
-			node2 = kasprintf(GFP_KERNEL,
-					  ", [%llx + %lld, color=%ld]",
+			node2 = kasprintf(GFP_KERNEL, ", [%llx + %lld, color=%ld]",
 					  next->start, next->size, next->color);
 
-		pr_info("%sHole [%llx - %llx, size %lld]%s\n",
-			node1,
-			hole_start, hole_end, hole_end - hole_start,
-			node2);
+		kunit_info(test, "%sHole [%llx - %llx, size %lld]%s\n", node1,
+			   hole_start, hole_end, hole_end - hole_start, node2);
 
 		kfree(node2);
 		kfree(node1);
@@ -1301,12 +1182,9 @@  struct evict_node {
 	struct list_head link;
 };
 
-static bool evict_nodes(struct drm_mm_scan *scan,
-			struct evict_node *nodes,
-			unsigned int *order,
-			unsigned int count,
-			bool use_color,
-			struct list_head *evict_list)
+static bool evict_nodes(struct kunit *test, struct drm_mm_scan *scan,
+			struct evict_node *nodes, unsigned int *order, unsigned int count,
+			bool use_color, struct list_head *evict_list)
 {
 	struct evict_node *e, *en;
 	unsigned int i;
@@ -1322,8 +1200,9 @@  static bool evict_nodes(struct drm_mm_scan *scan,
 			list_del(&e->link);
 	}
 	if (list_empty(evict_list)) {
-		pr_err("Failed to find eviction: size=%lld [avail=%d], align=%lld (color=%lu)\n",
-		       scan->size, count, scan->alignment, scan->color);
+		KUNIT_FAIL(test,
+			   "Failed to find eviction: size=%lld [avail=%d], align=%lld (color=%lu)\n",
+			   scan->size, count, scan->alignment, scan->color);
 		return false;
 	}
 
@@ -1340,7 +1219,8 @@  static bool evict_nodes(struct drm_mm_scan *scan,
 		}
 	} else {
 		if (drm_mm_scan_color_evict(scan)) {
-			pr_err("drm_mm_scan_color_evict unexpectedly reported overlapping nodes!\n");
+			KUNIT_FAIL(test,
+				   "drm_mm_scan_color_evict unexpectedly reported overlapping nodes!\n");
 			return false;
 		}
 	}
@@ -1348,9 +1228,8 @@  static bool evict_nodes(struct drm_mm_scan *scan,
 	return true;
 }
 
-static bool evict_nothing(struct drm_mm *mm,
-			  unsigned int total_size,
-			  struct evict_node *nodes)
+static bool evict_nothing(struct kunit *test, struct drm_mm *mm,
+			  unsigned int total_size, struct evict_node *nodes)
 {
 	struct drm_mm_scan scan;
 	LIST_HEAD(evict_list);
@@ -1371,7 +1250,7 @@  static bool evict_nothing(struct drm_mm *mm,
 		e = &nodes[n];
 
 		if (!drm_mm_node_allocated(&e->node)) {
-			pr_err("node[%d] no longer allocated!\n", n);
+			KUNIT_FAIL(test, "node[%d] no longer allocated!\n", n);
 			return false;
 		}
 
@@ -1387,17 +1266,16 @@  static bool evict_nothing(struct drm_mm *mm,
 		e = &nodes[n];
 
 		if (!e->link.next) {
-			pr_err("node[%d] no longer connected!\n", n);
+			KUNIT_FAIL(test, "node[%d] no longer connected!\n", n);
 			return false;
 		}
 	}
 
-	return assert_continuous(mm, nodes[0].node.size);
+	return assert_continuous(test, mm, nodes[0].node.size);
 }
 
-static bool evict_everything(struct drm_mm *mm,
-			     unsigned int total_size,
-			     struct evict_node *nodes)
+static bool evict_everything(struct kunit *test, struct drm_mm *mm,
+			     unsigned int total_size, struct evict_node *nodes)
 {
 	struct drm_mm_scan scan;
 	LIST_HEAD(evict_list);
@@ -1417,8 +1295,8 @@  static bool evict_everything(struct drm_mm *mm,
 	list_for_each_entry(e, &evict_list, link) {
 		if (!drm_mm_scan_remove_block(&scan, &e->node)) {
 			if (!err) {
-				pr_err("Node %lld not marked for eviction!\n",
-				       e->node.start);
+				KUNIT_FAIL(test, "Node %lld not marked for eviction!\n",
+					   e->node.start);
 				err = -EINVAL;
 			}
 		}
@@ -1429,29 +1307,25 @@  static bool evict_everything(struct drm_mm *mm,
 	list_for_each_entry(e, &evict_list, link)
 		drm_mm_remove_node(&e->node);
 
-	if (!assert_one_hole(mm, 0, total_size))
+	if (!assert_one_hole(test, mm, 0, total_size))
 		return false;
 
 	list_for_each_entry(e, &evict_list, link) {
 		err = drm_mm_reserve_node(mm, &e->node);
 		if (err) {
-			pr_err("Failed to reinsert node after eviction: start=%llx\n",
-			       e->node.start);
+			KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n",
+				   e->node.start);
 			return false;
 		}
 	}
 
-	return assert_continuous(mm, nodes[0].node.size);
+	return assert_continuous(test, mm, nodes[0].node.size);
 }
 
-static int evict_something(struct drm_mm *mm,
-			   u64 range_start, u64 range_end,
-			   struct evict_node *nodes,
-			   unsigned int *order,
-			   unsigned int count,
-			   unsigned int size,
-			   unsigned int alignment,
-			   const struct insert_mode *mode)
+static int evict_something(struct kunit *test, struct drm_mm *mm,
+			   u64 range_start, u64 range_end, struct evict_node *nodes,
+			   unsigned int *order, unsigned int count, unsigned int size,
+			   unsigned int alignment, const struct insert_mode *mode)
 {
 	struct drm_mm_scan scan;
 	LIST_HEAD(evict_list);
@@ -1459,38 +1333,35 @@  static int evict_something(struct drm_mm *mm,
 	struct drm_mm_node tmp;
 	int err;
 
-	drm_mm_scan_init_with_range(&scan, mm,
-				    size, alignment, 0,
-				    range_start, range_end,
-				    mode->mode);
-	if (!evict_nodes(&scan,
-			 nodes, order, count, false,
-			 &evict_list))
+	drm_mm_scan_init_with_range(&scan, mm, size, alignment, 0, range_start,
+				    range_end, mode->mode);
+	if (!evict_nodes(test, &scan, nodes, order, count, false, &evict_list))
 		return -EINVAL;
 
 	memset(&tmp, 0, sizeof(tmp));
 	err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, 0,
 					 DRM_MM_INSERT_EVICT);
 	if (err) {
-		pr_err("Failed to insert into eviction hole: size=%d, align=%d\n",
-		       size, alignment);
-		show_scan(&scan);
-		show_holes(mm, 3);
+		KUNIT_FAIL(test, "Failed to insert into eviction hole: size=%d, align=%d\n",
+			   size, alignment);
+		show_scan(test, &scan);
+		show_holes(test, mm, 3);
 		return err;
 	}
 
 	if (tmp.start < range_start || tmp.start + tmp.size > range_end) {
-		pr_err("Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n",
-		       tmp.start, tmp.size, range_start, range_end);
+		KUNIT_FAIL(test,
+			   "Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n",
+			   tmp.start, tmp.size, range_start, range_end);
 		err = -EINVAL;
 	}
 
-	if (!assert_node(&tmp, mm, size, alignment, 0) ||
+	if (!assert_node(test, &tmp, mm, size, alignment, 0) ||
 	    drm_mm_hole_follows(&tmp)) {
-		pr_err("Inserted did not fill the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx, hole-follows?=%d\n",
-		       tmp.size, size,
-		       alignment, misalignment(&tmp, alignment),
-		       tmp.start, drm_mm_hole_follows(&tmp));
+		KUNIT_FAIL(test,
+			   "Inserted did not fill the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx, hole-follows?=%d\n",
+			   tmp.size, size, alignment, misalignment(&tmp, alignment),
+			   tmp.start, drm_mm_hole_follows(&tmp));
 		err = -EINVAL;
 	}
 
@@ -1501,21 +1372,21 @@  static int evict_something(struct drm_mm *mm,
 	list_for_each_entry(e, &evict_list, link) {
 		err = drm_mm_reserve_node(mm, &e->node);
 		if (err) {
-			pr_err("Failed to reinsert node after eviction: start=%llx\n",
-			       e->node.start);
+			KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n",
+				   e->node.start);
 			return err;
 		}
 	}
 
-	if (!assert_continuous(mm, nodes[0].node.size)) {
-		pr_err("range is no longer continuous\n");
+	if (!assert_continuous(test, mm, nodes[0].node.size)) {
+		KUNIT_FAIL(test, "range is no longer continuous\n");
 		return -EINVAL;
 	}
 
 	return 0;
 }
 
-static int igt_evict(void *ignored)
+static void igt_mm_evict(struct kunit *test)
 {
 	DRM_RND_STATE(prng, random_seed);
 	const unsigned int size = 8192;
@@ -1524,7 +1395,6 @@  static int igt_evict(void *ignored)
 	struct evict_node *nodes;
 	struct drm_mm_node *node, *next;
 	unsigned int *order, n;
-	int ret, err;
 
 	/* Here we populate a full drm_mm and then try and insert a new node
 	 * by evicting other nodes in a random order. The drm_mm_scan should
@@ -1533,61 +1403,49 @@  static int igt_evict(void *ignored)
 	 * sizes to try and stress the hole finder.
 	 */
 
-	ret = -ENOMEM;
 	nodes = vzalloc(array_size(size, sizeof(*nodes)));
-	if (!nodes)
-		goto err;
+	KUNIT_ASSERT_TRUE(test, nodes);
 
 	order = drm_random_order(size, &prng);
 	if (!order)
 		goto err_nodes;
 
-	ret = -EINVAL;
 	drm_mm_init(&mm, 0, size);
 	for (n = 0; n < size; n++) {
-		err = drm_mm_insert_node(&mm, &nodes[n].node, 1);
-		if (err) {
-			pr_err("insert failed, step %d\n", n);
-			ret = err;
+		if (drm_mm_insert_node(&mm, &nodes[n].node, 1)) {
+			KUNIT_FAIL(test, "insert failed, step %d\n", n);
 			goto out;
 		}
 	}
 
 	/* First check that using the scanner doesn't break the mm */
-	if (!evict_nothing(&mm, size, nodes)) {
-		pr_err("evict_nothing() failed\n");
+	if (!evict_nothing(test, &mm, size, nodes)) {
+		KUNIT_FAIL(test, "evict_nothing() failed\n");
 		goto out;
 	}
-	if (!evict_everything(&mm, size, nodes)) {
-		pr_err("evict_everything() failed\n");
+	if (!evict_everything(test, &mm, size, nodes)) {
+		KUNIT_FAIL(test, "evict_everything() failed\n");
 		goto out;
 	}
 
 	for (mode = evict_modes; mode->name; mode++) {
 		for (n = 1; n <= size; n <<= 1) {
 			drm_random_reorder(order, size, &prng);
-			err = evict_something(&mm, 0, U64_MAX,
-					      nodes, order, size,
-					      n, 1,
-					      mode);
-			if (err) {
-				pr_err("%s evict_something(size=%u) failed\n",
-				       mode->name, n);
-				ret = err;
+			if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size, n, 1,
+					    mode)) {
+				KUNIT_FAIL(test, "%s evict_something(size=%u) failed\n",
+					   mode->name, n);
 				goto out;
 			}
 		}
 
 		for (n = 1; n < size; n <<= 1) {
 			drm_random_reorder(order, size, &prng);
-			err = evict_something(&mm, 0, U64_MAX,
-					      nodes, order, size,
-					      size/2, n,
-					      mode);
-			if (err) {
-				pr_err("%s evict_something(size=%u, alignment=%u) failed\n",
-				       mode->name, size/2, n);
-				ret = err;
+			if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size,
+					    size / 2, n, mode)) {
+				KUNIT_FAIL(test,
+					   "%s evict_something(size=%u, alignment=%u) failed\n",
+					   mode->name, size / 2, n);
 				goto out;
 			}
 		}
@@ -1598,14 +1456,11 @@  static int igt_evict(void *ignored)
 			DRM_MM_BUG_ON(!nsize);
 
 			drm_random_reorder(order, size, &prng);
-			err = evict_something(&mm, 0, U64_MAX,
-					      nodes, order, size,
-					      nsize, n,
-					      mode);
-			if (err) {
-				pr_err("%s evict_something(size=%u, alignment=%u) failed\n",
-				       mode->name, nsize, n);
-				ret = err;
+			if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size,
+					    nsize, n, mode)) {
+				KUNIT_FAIL(test,
+					   "%s evict_something(size=%u, alignment=%u) failed\n",
+					   mode->name, nsize, n);
 				goto out;
 			}
 		}
@@ -1613,7 +1468,6 @@  static int igt_evict(void *ignored)
 		cond_resched();
 	}
 
-	ret = 0;
 out:
 	drm_mm_for_each_node_safe(node, next, &mm)
 		drm_mm_remove_node(node);
@@ -1621,11 +1475,9 @@  static int igt_evict(void *ignored)
 	kfree(order);
 err_nodes:
 	vfree(nodes);
-err:
-	return ret;
 }
 
-static int igt_evict_range(void *ignored)
+static void igt_mm_evict_range(struct kunit *test)
 {
 	DRM_RND_STATE(prng, random_seed);
 	const unsigned int size = 8192;
@@ -1637,28 +1489,22 @@  static int igt_evict_range(void *ignored)
 	struct evict_node *nodes;
 	struct drm_mm_node *node, *next;
 	unsigned int *order, n;
-	int ret, err;
 
 	/* Like igt_evict() but now we are limiting the search to a
 	 * small portion of the full drm_mm.
 	 */
 
-	ret = -ENOMEM;
 	nodes = vzalloc(array_size(size, sizeof(*nodes)));
-	if (!nodes)
-		goto err;
+	KUNIT_ASSERT_TRUE(test, nodes);
 
 	order = drm_random_order(size, &prng);
 	if (!order)
 		goto err_nodes;
 
-	ret = -EINVAL;
 	drm_mm_init(&mm, 0, size);
 	for (n = 0; n < size; n++) {
-		err = drm_mm_insert_node(&mm, &nodes[n].node, 1);
-		if (err) {
-			pr_err("insert failed, step %d\n", n);
-			ret = err;
+		if (drm_mm_insert_node(&mm, &nodes[n].node, 1)) {
+			KUNIT_FAIL(test, "insert failed, step %d\n", n);
 			goto out;
 		}
 	}
@@ -1666,26 +1512,22 @@  static int igt_evict_range(void *ignored)
 	for (mode = evict_modes; mode->name; mode++) {
 		for (n = 1; n <= range_size; n <<= 1) {
 			drm_random_reorder(order, size, &prng);
-			err = evict_something(&mm, range_start, range_end,
-					      nodes, order, size,
-					      n, 1,
-					      mode);
-			if (err) {
-				pr_err("%s evict_something(size=%u) failed with range [%u, %u]\n",
-				       mode->name, n, range_start, range_end);
+			if (evict_something(test, &mm, range_start, range_end, nodes,
+					    order, size, n, 1, mode)) {
+				KUNIT_FAIL(test,
+					   "%s evict_something(size=%u) failed with range [%u, %u]\n",
+					   mode->name, n, range_start, range_end);
 				goto out;
 			}
 		}
 
 		for (n = 1; n <= range_size; n <<= 1) {
 			drm_random_reorder(order, size, &prng);
-			err = evict_something(&mm, range_start, range_end,
-					      nodes, order, size,
-					      range_size/2, n,
-					      mode);
-			if (err) {
-				pr_err("%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n",
-				       mode->name, range_size/2, n, range_start, range_end);
+			if (evict_something(test, &mm, range_start, range_end, nodes,
+					    order, size, range_size / 2, n, mode)) {
+				KUNIT_FAIL(test,
+					   "%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n",
+					   mode->name, range_size / 2, n, range_start, range_end);
 				goto out;
 			}
 		}
@@ -1696,13 +1538,11 @@  static int igt_evict_range(void *ignored)
 			DRM_MM_BUG_ON(!nsize);
 
 			drm_random_reorder(order, size, &prng);
-			err = evict_something(&mm, range_start, range_end,
-					      nodes, order, size,
-					      nsize, n,
-					      mode);
-			if (err) {
-				pr_err("%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n",
-				       mode->name, nsize, n, range_start, range_end);
+			if (evict_something(test, &mm, range_start, range_end, nodes,
+					    order, size, nsize, n, mode)) {
+				KUNIT_FAIL(test,
+					   "%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n",
+					   mode->name, nsize, n, range_start, range_end);
 				goto out;
 			}
 		}
@@ -1710,7 +1550,6 @@  static int igt_evict_range(void *ignored)
 		cond_resched();
 	}
 
-	ret = 0;
 out:
 	drm_mm_for_each_node_safe(node, next, &mm)
 		drm_mm_remove_node(node);
@@ -1718,8 +1557,6 @@  static int igt_evict_range(void *ignored)
 	kfree(order);
 err_nodes:
 	vfree(nodes);
-err:
-	return ret;
 }
 
 static unsigned int node_index(const struct drm_mm_node *node)
@@ -1727,9 +1564,10 @@  static unsigned int node_index(const struct drm_mm_node *node)
 	return div64_u64(node->start, node->size);
 }
 
-static int igt_topdown(void *ignored)
+static void igt_mm_topdown(struct kunit *test)
 {
 	const struct insert_mode *topdown = &insert_modes[TOPDOWN];
+
 	DRM_RND_STATE(prng, random_seed);
 	const unsigned int count = 8192;
 	unsigned int size;
@@ -1737,17 +1575,14 @@  static int igt_topdown(void *ignored)
 	struct drm_mm mm;
 	struct drm_mm_node *nodes, *node, *next;
 	unsigned int *order, n, m, o = 0;
-	int ret;
 
 	/* When allocating top-down, we expect to be returned a node
 	 * from a suitable hole at the top of the drm_mm. We check that
 	 * the returned node does match the highest available slot.
 	 */
 
-	ret = -ENOMEM;
 	nodes = vzalloc(array_size(count, sizeof(*nodes)));
-	if (!nodes)
-		goto err;
+	KUNIT_ASSERT_TRUE(test, nodes);
 
 	bitmap = bitmap_zalloc(count, GFP_KERNEL);
 	if (!bitmap)
@@ -1757,28 +1592,26 @@  static int igt_topdown(void *ignored)
 	if (!order)
 		goto err_bitmap;
 
-	ret = -EINVAL;
 	for (size = 1; size <= 64; size <<= 1) {
-		drm_mm_init(&mm, 0, size*count);
+		drm_mm_init(&mm, 0, size * count);
 		for (n = 0; n < count; n++) {
-			if (!expect_insert(&mm, &nodes[n],
-					   size, 0, n,
-					   topdown)) {
-				pr_err("insert failed, size %u step %d\n", size, n);
+			if (!expect_insert(test, &mm, &nodes[n], size, 0, n, topdown)) {
+				KUNIT_FAIL(test, "insert failed, size %u step %d\n", size, n);
 				goto out;
 			}
 
 			if (drm_mm_hole_follows(&nodes[n])) {
-				pr_err("hole after topdown insert %d, start=%llx\n, size=%u",
-				       n, nodes[n].start, size);
+				KUNIT_FAIL(test,
+					   "hole after topdown insert %d, start=%llx\n, size=%u",
+					   n, nodes[n].start, size);
 				goto out;
 			}
 
-			if (!assert_one_hole(&mm, 0, size*(count - n - 1)))
+			if (!assert_one_hole(test, &mm, 0, size * (count - n - 1)))
 				goto out;
 		}
 
-		if (!assert_continuous(&mm, size))
+		if (!assert_continuous(test, &mm, size))
 			goto out;
 
 		drm_random_reorder(order, count, &prng);
@@ -1793,23 +1626,23 @@  static int igt_topdown(void *ignored)
 				unsigned int last;
 
 				node = &nodes[order[(o + m) % count]];
-				if (!expect_insert(&mm, node,
-						   size, 0, 0,
-						   topdown)) {
-					pr_err("insert failed, step %d/%d\n", m, n);
+				if (!expect_insert(test, &mm, node, size, 0, 0, topdown)) {
+					KUNIT_FAIL(test, "insert failed, step %d/%d\n", m, n);
 					goto out;
 				}
 
 				if (drm_mm_hole_follows(node)) {
-					pr_err("hole after topdown insert %d/%d, start=%llx\n",
-					       m, n, node->start);
+					KUNIT_FAIL(test,
+						   "hole after topdown insert %d/%d, start=%llx\n",
+						   m, n, node->start);
 					goto out;
 				}
 
 				last = find_last_bit(bitmap, count);
 				if (node_index(node) != last) {
-					pr_err("node %d/%d, size %d, not inserted into upmost hole, expected %d, found %d\n",
-					       m, n, size, last, node_index(node));
+					KUNIT_FAIL(test,
+						   "node %d/%d, size %d, not inserted into upmost hole, expected %d, found %d\n",
+						   m, n, size, last, node_index(node));
 					goto out;
 				}
 
@@ -1827,7 +1660,6 @@  static int igt_topdown(void *ignored)
 		cond_resched();
 	}
 
-	ret = 0;
 out:
 	drm_mm_for_each_node_safe(node, next, &mm)
 		drm_mm_remove_node(node);
@@ -1837,13 +1669,12 @@  static int igt_topdown(void *ignored)
 	bitmap_free(bitmap);
 err_nodes:
 	vfree(nodes);
-err:
-	return ret;
 }
 
-static int igt_bottomup(void *ignored)
+static void igt_mm_bottomup(struct kunit *test)
 {
 	const struct insert_mode *bottomup = &insert_modes[BOTTOMUP];
+
 	DRM_RND_STATE(prng, random_seed);
 	const unsigned int count = 8192;
 	unsigned int size;
@@ -1851,16 +1682,13 @@  static int igt_bottomup(void *ignored)
 	struct drm_mm mm;
 	struct drm_mm_node *nodes, *node, *next;
 	unsigned int *order, n, m, o = 0;
-	int ret;
 
 	/* Like igt_topdown, but instead of searching for the last hole,
 	 * we search for the first.
 	 */
 
-	ret = -ENOMEM;
 	nodes = vzalloc(array_size(count, sizeof(*nodes)));
-	if (!nodes)
-		goto err;
+	KUNIT_ASSERT_TRUE(test, nodes);
 
 	bitmap = bitmap_zalloc(count, GFP_KERNEL);
 	if (!bitmap)
@@ -1870,22 +1698,20 @@  static int igt_bottomup(void *ignored)
 	if (!order)
 		goto err_bitmap;
 
-	ret = -EINVAL;
 	for (size = 1; size <= 64; size <<= 1) {
-		drm_mm_init(&mm, 0, size*count);
+		drm_mm_init(&mm, 0, size * count);
 		for (n = 0; n < count; n++) {
-			if (!expect_insert(&mm, &nodes[n],
-					   size, 0, n,
-					   bottomup)) {
-				pr_err("bottomup insert failed, size %u step %d\n", size, n);
+			if (!expect_insert(test, &mm, &nodes[n], size, 0, n, bottomup)) {
+				KUNIT_FAIL(test,
+					   "bottomup insert failed, size %u step %d\n", size, n);
 				goto out;
 			}
 
-			if (!assert_one_hole(&mm, size*(n + 1), size*count))
+			if (!assert_one_hole(test, &mm, size * (n + 1), size * count))
 				goto out;
 		}
 
-		if (!assert_continuous(&mm, size))
+		if (!assert_continuous(test, &mm, size))
 			goto out;
 
 		drm_random_reorder(order, count, &prng);
@@ -1900,17 +1726,16 @@  static int igt_bottomup(void *ignored)
 				unsigned int first;
 
 				node = &nodes[order[(o + m) % count]];
-				if (!expect_insert(&mm, node,
-						   size, 0, 0,
-						   bottomup)) {
-					pr_err("insert failed, step %d/%d\n", m, n);
+				if (!expect_insert(test, &mm, node, size, 0, 0, bottomup)) {
+					KUNIT_FAIL(test, "insert failed, step %d/%d\n", m, n);
 					goto out;
 				}
 
 				first = find_first_bit(bitmap, count);
 				if (node_index(node) != first) {
-					pr_err("node %d/%d not inserted into bottom hole, expected %d, found %d\n",
-					       m, n, first, node_index(node));
+					KUNIT_FAIL(test,
+						   "node %d/%d not inserted into bottom hole, expected %d, found %d\n",
+						   m, n, first, node_index(node));
 					goto out;
 				}
 				__clear_bit(first, bitmap);
@@ -1927,7 +1752,6 @@  static int igt_bottomup(void *ignored)
 		cond_resched();
 	}
 
-	ret = 0;
 out:
 	drm_mm_for_each_node_safe(node, next, &mm)
 		drm_mm_remove_node(node);
@@ -1937,47 +1761,39 @@  static int igt_bottomup(void *ignored)
 	bitmap_free(bitmap);
 err_nodes:
 	vfree(nodes);
-err:
-	return ret;
 }
 
-static int __igt_once(unsigned int mode)
+static void __igt_once(struct kunit *test, unsigned int mode)
 {
 	struct drm_mm mm;
 	struct drm_mm_node rsvd_lo, rsvd_hi, node;
-	int err;
 
 	drm_mm_init(&mm, 0, 7);
 
 	memset(&rsvd_lo, 0, sizeof(rsvd_lo));
 	rsvd_lo.start = 1;
 	rsvd_lo.size = 1;
-	err = drm_mm_reserve_node(&mm, &rsvd_lo);
-	if (err) {
-		pr_err("Could not reserve low node\n");
+	if (drm_mm_reserve_node(&mm, &rsvd_lo)) {
+		KUNIT_FAIL(test, "Could not reserve low node\n");
 		goto err;
 	}
 
 	memset(&rsvd_hi, 0, sizeof(rsvd_hi));
 	rsvd_hi.start = 5;
 	rsvd_hi.size = 1;
-	err = drm_mm_reserve_node(&mm, &rsvd_hi);
-	if (err) {
-		pr_err("Could not reserve low node\n");
+	if (drm_mm_reserve_node(&mm, &rsvd_hi)) {
+		KUNIT_FAIL(test, "Could not reserve low node\n");
 		goto err_lo;
 	}
 
 	if (!drm_mm_hole_follows(&rsvd_lo) || !drm_mm_hole_follows(&rsvd_hi)) {
-		pr_err("Expected a hole after lo and high nodes!\n");
-		err = -EINVAL;
+		KUNIT_FAIL(test, "Expected a hole after lo and high nodes!\n");
 		goto err_hi;
 	}
 
 	memset(&node, 0, sizeof(node));
-	err = drm_mm_insert_node_generic(&mm, &node, 2, 0, 0, mode);
-	if (err) {
-		pr_err("Could not insert the node into the available hole!\n");
-		err = -EINVAL;
+	if (drm_mm_insert_node_generic(&mm, &node, 2, 0, 0, mode)) {
+		KUNIT_FAIL(test, "Could not insert the node into the available hole!\n");
 		goto err_hi;
 	}
 
@@ -1988,23 +1804,20 @@  static int __igt_once(unsigned int mode)
 	drm_mm_remove_node(&rsvd_lo);
 err:
 	drm_mm_takedown(&mm);
-	return err;
 }
 
-static int igt_lowest(void *ignored)
+static void igt_mm_lowest(struct kunit *test)
 {
-	return __igt_once(DRM_MM_INSERT_LOW);
+	__igt_once(test, DRM_MM_INSERT_LOW);
 }
 
-static int igt_highest(void *ignored)
+static void igt_mm_highest(struct kunit *test)
 {
-	return __igt_once(DRM_MM_INSERT_HIGH);
+	__igt_once(test, DRM_MM_INSERT_HIGH);
 }
 
 static void separate_adjacent_colors(const struct drm_mm_node *node,
-				     unsigned long color,
-				     u64 *start,
-				     u64 *end)
+				     unsigned long color, u64 *start, u64 *end)
 {
 	if (drm_mm_node_allocated(node) && node->color != color)
 		++*start;
@@ -2014,12 +1827,12 @@  static void separate_adjacent_colors(const struct drm_mm_node *node,
 		--*end;
 }
 
-static bool colors_abutt(const struct drm_mm_node *node)
+static bool colors_abutt(struct kunit *test, const struct drm_mm_node *node)
 {
 	if (!drm_mm_hole_follows(node) &&
 	    drm_mm_node_allocated(list_next_entry(node, node_list))) {
-		pr_err("colors abutt; %ld [%llx + %llx] is next to %ld [%llx + %llx]!\n",
-		       node->color, node->start, node->size,
+		KUNIT_FAIL(test, "colors abutt; %ld [%llx + %llx] is next to %ld [%llx + %llx]!\n",
+			   node->color, node->start, node->size,
 		       list_next_entry(node, node_list)->color,
 		       list_next_entry(node, node_list)->start,
 		       list_next_entry(node, node_list)->size);
@@ -2029,14 +1842,13 @@  static bool colors_abutt(const struct drm_mm_node *node)
 	return false;
 }
 
-static int igt_color(void *ignored)
+static void igt_mm_color(struct kunit *test)
 {
 	const unsigned int count = min(4096u, max_iterations);
 	const struct insert_mode *mode;
 	struct drm_mm mm;
 	struct drm_mm_node *node, *nn;
 	unsigned int n;
-	int ret = -EINVAL, err;
 
 	/* Color adjustment complicates everything. First we just check
 	 * that when we insert a node we apply any color_adjustment callback.
@@ -2049,15 +1861,11 @@  static int igt_color(void *ignored)
 
 	for (n = 1; n <= count; n++) {
 		node = kzalloc(sizeof(*node), GFP_KERNEL);
-		if (!node) {
-			ret = -ENOMEM;
+		if (!node)
 			goto out;
-		}
 
-		if (!expect_insert(&mm, node,
-				   n, 0, n,
-				   &insert_modes[0])) {
-			pr_err("insert failed, step %d\n", n);
+		if (!expect_insert(test, &mm, node, n, 0, n, &insert_modes[0])) {
+			KUNIT_FAIL(test, "insert failed, step %d\n", n);
 			kfree(node);
 			goto out;
 		}
@@ -2065,8 +1873,8 @@  static int igt_color(void *ignored)
 
 	drm_mm_for_each_node_safe(node, nn, &mm) {
 		if (node->color != node->size) {
-			pr_err("invalid color stored: expected %lld, found %ld\n",
-			       node->size, node->color);
+			KUNIT_FAIL(test, "invalid color stored: expected %lld, found %ld\n",
+				   node->size, node->color);
 
 			goto out;
 		}
@@ -2081,18 +1889,14 @@  static int igt_color(void *ignored)
 		u64 last;
 
 		node = kzalloc(sizeof(*node), GFP_KERNEL);
-		if (!node) {
-			ret = -ENOMEM;
+		if (!node)
 			goto out;
-		}
 
-		node->size = 1 + 2*count;
+		node->size = 1 + 2 * count;
 		node->color = node->size;
 
-		err = drm_mm_reserve_node(&mm, node);
-		if (err) {
-			pr_err("initial reserve failed!\n");
-			ret = err;
+		if (drm_mm_reserve_node(&mm, node)) {
+			KUNIT_FAIL(test, "initial reserve failed!\n");
 			goto out;
 		}
 
@@ -2102,19 +1906,15 @@  static int igt_color(void *ignored)
 			int rem;
 
 			node = kzalloc(sizeof(*node), GFP_KERNEL);
-			if (!node) {
-				ret = -ENOMEM;
+			if (!node)
 				goto out;
-			}
 
 			node->start = last;
 			node->size = n + count;
 			node->color = node->size;
 
-			err = drm_mm_reserve_node(&mm, node);
-			if (err != -ENOSPC) {
-				pr_err("reserve %d did not report color overlap! err=%d\n",
-				       n, err);
+			if (drm_mm_reserve_node(&mm, node) != -ENOSPC) {
+				KUNIT_FAIL(test, "reserve %d did not report color overlap!", n);
 				goto out;
 			}
 
@@ -2122,10 +1922,8 @@  static int igt_color(void *ignored)
 			rem = misalignment(node, n + count);
 			node->start += n + count - rem;
 
-			err = drm_mm_reserve_node(&mm, node);
-			if (err) {
-				pr_err("reserve %d failed, err=%d\n", n, err);
-				ret = err;
+			if (drm_mm_reserve_node(&mm, node)) {
+				KUNIT_FAIL(test, "reserve %d failed", n);
 				goto out;
 			}
 
@@ -2134,16 +1932,11 @@  static int igt_color(void *ignored)
 
 		for (n = 1; n <= count; n++) {
 			node = kzalloc(sizeof(*node), GFP_KERNEL);
-			if (!node) {
-				ret = -ENOMEM;
+			if (!node)
 				goto out;
-			}
 
-			if (!expect_insert(&mm, node,
-					   n, n, n,
-					   mode)) {
-				pr_err("%s insert failed, step %d\n",
-				       mode->name, n);
+			if (!expect_insert(test, &mm, node, n, n, n, mode)) {
+				KUNIT_FAIL(test, "%s insert failed, step %d\n", mode->name, n);
 				kfree(node);
 				goto out;
 			}
@@ -2153,19 +1946,21 @@  static int igt_color(void *ignored)
 			u64 rem;
 
 			if (node->color != node->size) {
-				pr_err("%s invalid color stored: expected %lld, found %ld\n",
-				       mode->name, node->size, node->color);
+				KUNIT_FAIL(test,
+					   "%s invalid color stored: expected %lld, found %ld\n",
+					   mode->name, node->size, node->color);
 
 				goto out;
 			}
 
-			if (colors_abutt(node))
+			if (colors_abutt(test, node))
 				goto out;
 
 			div64_u64_rem(node->start, node->size, &rem);
 			if (rem) {
-				pr_err("%s colored node misaligned, start=%llx expected alignment=%lld [rem=%lld]\n",
-				       mode->name, node->start, node->size, rem);
+				KUNIT_FAIL(test,
+					   "%s colored node misaligned, start=%llx expected alignment=%lld [rem=%lld]\n",
+					   mode->name, node->start, node->size, rem);
 				goto out;
 			}
 
@@ -2176,25 +1971,18 @@  static int igt_color(void *ignored)
 		cond_resched();
 	}
 
-	ret = 0;
 out:
 	drm_mm_for_each_node_safe(node, nn, &mm) {
 		drm_mm_remove_node(node);
 		kfree(node);
 	}
 	drm_mm_takedown(&mm);
-	return ret;
 }
 
-static int evict_color(struct drm_mm *mm,
-		       u64 range_start, u64 range_end,
-		       struct evict_node *nodes,
-		       unsigned int *order,
-		       unsigned int count,
-		       unsigned int size,
-		       unsigned int alignment,
-		       unsigned long color,
-		       const struct insert_mode *mode)
+static int evict_color(struct kunit *test, struct drm_mm *mm, u64 range_start,
+		       u64 range_end, struct evict_node *nodes, unsigned int *order,
+		unsigned int count, unsigned int size, unsigned int alignment,
+		unsigned long color, const struct insert_mode *mode)
 {
 	struct drm_mm_scan scan;
 	LIST_HEAD(evict_list);
@@ -2202,39 +1990,37 @@  static int evict_color(struct drm_mm *mm,
 	struct drm_mm_node tmp;
 	int err;
 
-	drm_mm_scan_init_with_range(&scan, mm,
-				    size, alignment, color,
-				    range_start, range_end,
-				    mode->mode);
-	if (!evict_nodes(&scan,
-			 nodes, order, count, true,
-			 &evict_list))
+	drm_mm_scan_init_with_range(&scan, mm, size, alignment, color, range_start,
+				    range_end, mode->mode);
+	if (!evict_nodes(test, &scan, nodes, order, count, true, &evict_list))
 		return -EINVAL;
 
 	memset(&tmp, 0, sizeof(tmp));
 	err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, color,
 					 DRM_MM_INSERT_EVICT);
 	if (err) {
-		pr_err("Failed to insert into eviction hole: size=%d, align=%d, color=%lu, err=%d\n",
-		       size, alignment, color, err);
-		show_scan(&scan);
-		show_holes(mm, 3);
+		KUNIT_FAIL(test,
+			   "Failed to insert into eviction hole: size=%d, align=%d, color=%lu, err=%d\n",
+			   size, alignment, color, err);
+		show_scan(test, &scan);
+		show_holes(test, mm, 3);
 		return err;
 	}
 
 	if (tmp.start < range_start || tmp.start + tmp.size > range_end) {
-		pr_err("Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n",
-		       tmp.start, tmp.size, range_start, range_end);
+		KUNIT_FAIL(test,
+			   "Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n",
+			   tmp.start, tmp.size, range_start, range_end);
 		err = -EINVAL;
 	}
 
-	if (colors_abutt(&tmp))
+	if (colors_abutt(test, &tmp))
 		err = -EINVAL;
 
-	if (!assert_node(&tmp, mm, size, alignment, color)) {
-		pr_err("Inserted did not fit the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx\n",
-		       tmp.size, size,
-		       alignment, misalignment(&tmp, alignment), tmp.start);
+	if (!assert_node(test, &tmp, mm, size, alignment, color)) {
+		KUNIT_FAIL(test,
+			   "Inserted did not fit the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx\n",
+			   tmp.size, size, alignment, misalignment(&tmp, alignment), tmp.start);
 		err = -EINVAL;
 	}
 
@@ -2245,8 +2031,8 @@  static int evict_color(struct drm_mm *mm,
 	list_for_each_entry(e, &evict_list, link) {
 		err = drm_mm_reserve_node(mm, &e->node);
 		if (err) {
-			pr_err("Failed to reinsert node after eviction: start=%llx\n",
-			       e->node.start);
+			KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n",
+				   e->node.start);
 			return err;
 		}
 	}
@@ -2255,7 +2041,7 @@  static int evict_color(struct drm_mm *mm,
 	return 0;
 }
 
-static int igt_color_evict(void *ignored)
+static void igt_mm_color_evict(struct kunit *test)
 {
 	DRM_RND_STATE(prng, random_seed);
 	const unsigned int total_size = min(8192u, max_iterations);
@@ -2265,7 +2051,6 @@  static int igt_color_evict(void *ignored)
 	struct evict_node *nodes;
 	struct drm_mm_node *node, *next;
 	unsigned int *order, n;
-	int ret, err;
 
 	/* Check that the drm_mm_scan also honours color adjustment when
 	 * choosing its victims to create a hole. Our color_adjust does not
@@ -2273,23 +2058,20 @@  static int igt_color_evict(void *ignored)
 	 * enlarging the set of victims that must be evicted.
 	 */
 
-	ret = -ENOMEM;
 	nodes = vzalloc(array_size(total_size, sizeof(*nodes)));
-	if (!nodes)
-		goto err;
+	KUNIT_ASSERT_TRUE(test, nodes);
 
 	order = drm_random_order(total_size, &prng);
 	if (!order)
 		goto err_nodes;
 
-	ret = -EINVAL;
-	drm_mm_init(&mm, 0, 2*total_size - 1);
+	drm_mm_init(&mm, 0, 2 * total_size - 1);
 	mm.color_adjust = separate_adjacent_colors;
 	for (n = 0; n < total_size; n++) {
-		if (!expect_insert(&mm, &nodes[n].node,
+		if (!expect_insert(test, &mm, &nodes[n].node,
 				   1, 0, color++,
 				   &insert_modes[0])) {
-			pr_err("insert failed, step %d\n", n);
+			KUNIT_FAIL(test, "insert failed, step %d\n", n);
 			goto out;
 		}
 	}
@@ -2297,26 +2079,19 @@  static int igt_color_evict(void *ignored)
 	for (mode = evict_modes; mode->name; mode++) {
 		for (n = 1; n <= total_size; n <<= 1) {
 			drm_random_reorder(order, total_size, &prng);
-			err = evict_color(&mm, 0, U64_MAX,
-					  nodes, order, total_size,
-					  n, 1, color++,
-					  mode);
-			if (err) {
-				pr_err("%s evict_color(size=%u) failed\n",
-				       mode->name, n);
+			if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size,
+					n, 1, color++, mode)) {
+				KUNIT_FAIL(test, "%s evict_color(size=%u) failed\n", mode->name, n);
 				goto out;
 			}
 		}
 
 		for (n = 1; n < total_size; n <<= 1) {
 			drm_random_reorder(order, total_size, &prng);
-			err = evict_color(&mm, 0, U64_MAX,
-					  nodes, order, total_size,
-					  total_size/2, n, color++,
-					  mode);
-			if (err) {
-				pr_err("%s evict_color(size=%u, alignment=%u) failed\n",
-				       mode->name, total_size/2, n);
+			if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size,
+					total_size / 2, n, color++, mode)) {
+				KUNIT_FAIL(test, "%s evict_color(size=%u, alignment=%u) failed\n",
+					   mode->name, total_size / 2, n);
 				goto out;
 			}
 		}
@@ -2327,13 +2102,10 @@  static int igt_color_evict(void *ignored)
 			DRM_MM_BUG_ON(!nsize);
 
 			drm_random_reorder(order, total_size, &prng);
-			err = evict_color(&mm, 0, U64_MAX,
-					  nodes, order, total_size,
-					  nsize, n, color++,
-					  mode);
-			if (err) {
-				pr_err("%s evict_color(size=%u, alignment=%u) failed\n",
-				       mode->name, nsize, n);
+			if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size,
+					nsize, n, color++, mode)) {
+				KUNIT_FAIL(test, "%s evict_color(size=%u, alignment=%u) failed\n",
+					   mode->name, nsize, n);
 				goto out;
 			}
 		}
@@ -2341,21 +2113,16 @@  static int igt_color_evict(void *ignored)
 		cond_resched();
 	}
 
-	ret = 0;
 out:
-	if (ret)
-		show_mm(&mm);
 	drm_mm_for_each_node_safe(node, next, &mm)
 		drm_mm_remove_node(node);
 	drm_mm_takedown(&mm);
 	kfree(order);
 err_nodes:
 	vfree(nodes);
-err:
-	return ret;
 }
 
-static int igt_color_evict_range(void *ignored)
+static void igt_mm_color_evict_range(struct kunit *test)
 {
 	DRM_RND_STATE(prng, random_seed);
 	const unsigned int total_size = 8192;
@@ -2368,29 +2135,25 @@  static int igt_color_evict_range(void *ignored)
 	struct evict_node *nodes;
 	struct drm_mm_node *node, *next;
 	unsigned int *order, n;
-	int ret, err;
 
 	/* Like igt_color_evict(), but limited to small portion of the full
 	 * drm_mm range.
 	 */
 
-	ret = -ENOMEM;
 	nodes = vzalloc(array_size(total_size, sizeof(*nodes)));
-	if (!nodes)
-		goto err;
+	KUNIT_ASSERT_TRUE(test, nodes);
 
 	order = drm_random_order(total_size, &prng);
 	if (!order)
 		goto err_nodes;
 
-	ret = -EINVAL;
-	drm_mm_init(&mm, 0, 2*total_size - 1);
+	drm_mm_init(&mm, 0, 2 * total_size - 1);
 	mm.color_adjust = separate_adjacent_colors;
 	for (n = 0; n < total_size; n++) {
-		if (!expect_insert(&mm, &nodes[n].node,
+		if (!expect_insert(test, &mm, &nodes[n].node,
 				   1, 0, color++,
 				   &insert_modes[0])) {
-			pr_err("insert failed, step %d\n", n);
+			KUNIT_FAIL(test, "insert failed, step %d\n", n);
 			goto out;
 		}
 	}
@@ -2398,26 +2161,22 @@  static int igt_color_evict_range(void *ignored)
 	for (mode = evict_modes; mode->name; mode++) {
 		for (n = 1; n <= range_size; n <<= 1) {
 			drm_random_reorder(order, range_size, &prng);
-			err = evict_color(&mm, range_start, range_end,
-					  nodes, order, total_size,
-					  n, 1, color++,
-					  mode);
-			if (err) {
-				pr_err("%s evict_color(size=%u) failed for range [%x, %x]\n",
-				       mode->name, n, range_start, range_end);
+			if (evict_color(test, &mm, range_start, range_end, nodes, order,
+					total_size, n, 1, color++, mode)) {
+				KUNIT_FAIL(test,
+					   "%s evict_color(size=%u) failed for range [%x, %x]\n",
+						mode->name, n, range_start, range_end);
 				goto out;
 			}
 		}
 
 		for (n = 1; n < range_size; n <<= 1) {
 			drm_random_reorder(order, total_size, &prng);
-			err = evict_color(&mm, range_start, range_end,
-					  nodes, order, total_size,
-					  range_size/2, n, color++,
-					  mode);
-			if (err) {
-				pr_err("%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n",
-				       mode->name, total_size/2, n, range_start, range_end);
+			if (evict_color(test, &mm, range_start, range_end, nodes, order,
+					total_size, range_size / 2, n, color++, mode)) {
+				KUNIT_FAIL(test,
+					   "%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n",
+					   mode->name, total_size / 2, n, range_start, range_end);
 				goto out;
 			}
 		}
@@ -2428,13 +2187,11 @@  static int igt_color_evict_range(void *ignored)
 			DRM_MM_BUG_ON(!nsize);
 
 			drm_random_reorder(order, total_size, &prng);
-			err = evict_color(&mm, range_start, range_end,
-					  nodes, order, total_size,
-					  nsize, n, color++,
-					  mode);
-			if (err) {
-				pr_err("%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n",
-				       mode->name, nsize, n, range_start, range_end);
+			if (evict_color(test, &mm, range_start, range_end, nodes, order,
+					total_size, nsize, n, color++, mode)) {
+				KUNIT_FAIL(test,
+					   "%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n",
+					   mode->name, nsize, n, range_start, range_end);
 				goto out;
 			}
 		}
@@ -2442,46 +2199,57 @@  static int igt_color_evict_range(void *ignored)
 		cond_resched();
 	}
 
-	ret = 0;
 out:
-	if (ret)
-		show_mm(&mm);
 	drm_mm_for_each_node_safe(node, next, &mm)
 		drm_mm_remove_node(node);
 	drm_mm_takedown(&mm);
 	kfree(order);
 err_nodes:
 	vfree(nodes);
-err:
-	return ret;
 }
 
-#include "drm_selftest.c"
-
-static int __init test_drm_mm_init(void)
+static int drm_mm_init_test(struct kunit *test)
 {
-	int err;
-
 	while (!random_seed)
 		random_seed = get_random_int();
 
-	pr_info("Testing DRM range manager (struct drm_mm), with random_seed=0x%x max_iterations=%u max_prime=%u\n",
-		random_seed, max_iterations, max_prime);
-	err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL);
-
-	return err > 0 ? 0 : err;
-}
-
-static void __exit test_drm_mm_exit(void)
-{
+	return 0;
 }
 
-module_init(test_drm_mm_init);
-module_exit(test_drm_mm_exit);
-
 module_param(random_seed, uint, 0400);
 module_param(max_iterations, uint, 0400);
 module_param(max_prime, uint, 0400);
 
+static struct kunit_case drm_mm_tests[] = {
+	KUNIT_CASE(igt_mm_init),
+	KUNIT_CASE(igt_mm_debug),
+	KUNIT_CASE(igt_mm_reserve),
+	KUNIT_CASE(igt_mm_insert),
+	KUNIT_CASE(igt_mm_replace),
+	KUNIT_CASE(igt_mm_insert_range),
+	KUNIT_CASE(igt_mm_frag),
+	KUNIT_CASE(igt_mm_align),
+	KUNIT_CASE(igt_mm_align32),
+	KUNIT_CASE(igt_mm_align64),
+	KUNIT_CASE(igt_mm_evict),
+	KUNIT_CASE(igt_mm_evict_range),
+	KUNIT_CASE(igt_mm_topdown),
+	KUNIT_CASE(igt_mm_bottomup),
+	KUNIT_CASE(igt_mm_lowest),
+	KUNIT_CASE(igt_mm_highest),
+	KUNIT_CASE(igt_mm_color),
+	KUNIT_CASE(igt_mm_color_evict),
+	KUNIT_CASE(igt_mm_color_evict_range),
+	{}
+};
+
+static struct kunit_suite drm_mm_test_suite = {
+	.name = "drm_mm",
+	.init = drm_mm_init_test,
+	.test_cases = drm_mm_tests,
+};
+
+kunit_test_suite(drm_mm_test_suite);
+
 MODULE_AUTHOR("Intel Corporation");
 MODULE_LICENSE("GPL");