I’ve been working on writing a Virtual KMS driver this past two months (with other cool people from the DRM community) and I was planning to write a post to explain VKMS, but that is taking me so much time since I need to write a pre-post to explain the DRM Subsystem first.

Thus, I have decided to write a small note to explain the iterative work process and the tools that helps me along the way.

VKMS is new a module to the DRM Subsystem that is underdevelopment in the drm-misc repository [1]

We have been using the IGT Test suit [2] to help us in adding new functionality to the VKMS and test its correctness. I have been asigned to work on adding CRC support to the VKMS with the goal to pass the kms_pipe_crc_basic test in the IGT test suit. I am almost done from adding the basic CRC support and I am currently waiting for feedback on my patch [3]. In the meanwhile, I’m working to extend the VKMS to pass CRC test with cursor support.

Since I’m pretty new to the DRM Subsystem, I’ve been using trial and error approach for VKMS development. The following script helps to give me a feedback on what went wrong when running the IGT test which allows me to identify where in the code I should fix.

$ cat trace.sh
# trace.sh - need root priviliges
modprobe -r vkms		# in case vkms was loaded before
modprobe vkms			# to enable Ftrace to find its symbols for filtering
echo 0x3f > /sys/module/drm/parameters/debug # to enable all log messages from drm
cd /sys/kernel/debug/tracing
echo 0 > tracing_on		# disable tracing
echo function_graph > current_tracer
echo drm* > set_ftrace_filter	# only record sys calls with 'drm' prefix
echo vkms* >> set_ftrace_filter	# append another filter with '>>'
echo > trace			# to clean the tracing log
echo 1 > tracing_on		# enable tracing again
rm ~/trace.log			# remove old tracing log
cat trace_pipe | tee ~/trace.log # wait for sys calls with drm and vkms prefix

We need first to load the vkms module to enable Ftrace to find its symbols for filtering.

The drm module has different levels of debug messages which can be found in drm_print.h. To enable all messages we need to set drm.debug = 0x3f, which can be done at run-time by echoing the value to /sys/module/drm/parameters/debug.

The rest uses Ftrace to record the function calls that start with either drm or vkms using the function_graph tracer and log it in trace.log.

To start debugging, we need to run the script first:

$ sudo su
$ sh ~/trace.sh 

Then on a separate terminal, run the IGT kms_cursor_crc test:

$ ./igt-gpu-tools/scripts/run-tests.sh -t kms_cursor_crc
Found test list: "/home/haneen/git/igt-gpu-tools/build/tests/test-list.txt"
[45/45] fail: 45  
Thank you for running Piglit!
Results have been written to /home/haneen/git/igt-gpu-tools/results

The test kms_cursor_crc has many subtests within it which you can check with --list-subtest option as follow:

$ ./tests/kms_cursor_crc --list-subtest
cursor-size-change
cursor-64x64-onscreen
cursor-64x64-offscreen
cursor-64x64-sliding
cursor-64x64-random
cursor-64x64-dpms
cursor-64x64-suspend
cursor-64x64-rapid-movement
...

It appears that all the tests are failing. To isolate the problem, it is easier to focus on one test.

We can run cursor-size-change subtest from the kms_cursor_crc test using the --debug option and --force-module that has been added recently by Rodrigo Siqueria to enable the IGT to choose a specific module to run the tests on it [4].

$ ./tests/kms_cursor_crc --run-subtest cursor-size-change --debug --force-module vkms

With that we get the following feedback:

****  END  ****
Subtest cursor-size-change: FAIL (0.247s)
(kms_cursor_crc:5550) igt_fb-DEBUG: igt_create_fb_with_bo_size(width=64, height=65, format=0x34325241, tiling=0x0, size=0)
(kms_cursor_crc:5550) igt_fb-DEBUG: igt_create_fb_with_bo_size(handle=3, pitch=256)
(kms_cursor_crc:5550) igt_fb-DEBUG: igt_create_fb_with_bo_size(width=64, height=22, format=0x34325241, tiling=0x0, size=0)
(kms_cursor_crc:5550) igt_fb-DEBUG: igt_create_fb_with_bo_size(handle=3, pitch=256)
(kms_cursor_crc:5550) igt_fb-CRITICAL: Test assertion failure function igt_create_fb_with_bo_size, file ../lib/igt_fb.c:866:
(kms_cursor_crc:5550) igt_fb-CRITICAL: Failed assertion: (drmModeAddFB2(fd, width, height, format, handles, pitches, fb->offsets, &fb_id, 0)) == 0
(kms_cursor_crc:5550) igt_fb-CRITICAL: Last errno: 22, Invalid argument
Stack trace:
  #0 [__igt_fail_assert+0x180]
  #1 [igt_create_fb_with_bo_size+0x324]
  #2 [igt_create_fb+0x41]
  #3 [igt_create_color_fb+0x4c]
  #4 [create_cursor_fb+0x7c]
  #5 [run_test_generic+0x5cf]
  #6 [__real_main716+0x2d5]
  #7 [main+0x44]
  #8 [__libc_start_main+0xe7]
  #9 [_start+0x2a]
Test kms_cursor_crc failed.
**** DEBUG ****

The IGT test feedback shows that drmModeAddFB2 has failed. We can check Ftrace and dmesg log files to get more details and understand why the function fails.

Ftrace is helpful for getting an overview of the test’s functions calls, in this case, since the function fails early, it does not give valuable feedback other than that the function failed @drm_internal_framebuffer_create, but in different cases, like to understand how the atomic commit works, I’ve found it really helpful.

# part of trace.sh
 1)               |  drm_ioctl [drm]() {
 1)   0.152 us    |    drm_dev_enter [drm]();
 1)   0.150 us    |    drm_dev_exit [drm]();
 1)   2.498 us    |    drm_dbg [drm]();
 1)               |    drm_ioctl_kernel [drm]() {
 1)   0.130 us    |      drm_dev_enter [drm]();
 1)   0.101 us    |      drm_dev_exit [drm]();
 1)   0.036 us    |      drm_ioctl_permit [drm]();
 1)               |      drm_mode_addfb2 [drm]() {
 1)               |        drm_internal_framebuffer_create [drm]() {
 1)   2.570 us    |          drm_dbg [drm]();
 1)   2.934 us    |        }
 1)   3.334 us    |      }
 1)   4.951 us    |    }
 1)   2.106 us    |    drm_dbg [drm]();
 1) + 11.993 us   |  }

After that, we can check dmesg logs. Enabling the drm to print all debug messages allow us to see the ioctl calls to drm and their arguments and return values as well.

$ dmesg | grep -A 3 DRM_IOCTL_MODE_ADDFB2
[drm:return_to_handler] pid=2759, dev=0xe200, auth=1, DRM_IOCTL_MODE_ADDFB2
[drm:return_to_handler] bad framebuffer height 22, should be >= 32 && <= 8192
[drm:return_to_handler] pid=2759, ret = -22
[IGT] kms_cursor_crc: exiting, ret=99

Upon checking the drm_internal_framebuffer_create in drm_framebuffer.c we can see that the passed height value is less than config->min_height

So all we need to do is find where min_height is updated and change that value, which happens to be during the initialization step of vkms.

After updating min_height and running kms_cursor_crc test again, we can see now we have progressed a little bit and the test skip 33 and fail 12 tests.

$ ./scripts/run-tests.sh -t kms_cursor_crc
Found test list: "/home/haneen/git/igt-gpu-tools/build/tests/test-list.txt"
[45/45] skip: 33, fail: 12  
Thank you for running Piglit!
Results have been written to /home/haneen/git/igt-gpu-tools/results

When repeating the above steps again after fixing the error, the log messages gives the next clue that I can use to advance VKMS a little bit again:

Invalid pixel format AR24 little-endian (0x34325241), modifier 0x0

Refrences:

[1] [drm-misc] drm/vkms: Introduce basic VKMS driver

[2] IGT GPU Tools - git repository

[3] [PATCH 2/2] drm/vkms: Implement CRC debugfs API

[4] [i-g-t,2/2] Add support for forcing specific module