FLUSP logo FLUSP - FLOSS at USP

Linux kernel patch suggestions (FSD 2026)

Written by David Tadokoro

Written on , last modified on

To finalise the first phase of the Free Software Development 2026 course, each group will send one or more contributions to the Linux project. There are no restrictions on which subsystems the groups can send patches to, and this post includes a few directions to help you devise your contributions. The mentors of FSD 2026 have curated these suggestions, but always rely on the Linux community and maintainers, as well as on your own critical sense when developing for the kernel.

Important: Before proceeding to the patch suggestions, make sure you are comfortable with the submission part of the process by consulting the patch submission guidelines pad: https://pad.riseup.net/p/DSL-USP-2026-kernel-patch-submission-keep

This is an WIP version that may be altered in the future.

Suggestions summary

1. Any subsystem/subproject

Suggestion 1.1) Replace mutex_lock(&lock) and mutex_unlock(&lock) calls with guard(mutex)(&lock)

Synchronisation is fundamental in the Linux kernel, as you need to be mindful of concurrency issues such as data inconsistency, race conditions, and deadlocks. A common synchronisation primitive is a mutex, which is implemented in Linux. An example of initialising a mutex, acquiring it, and unlocking it would be

struct mutex lock;   // declaring a mutex
mutex_init(&lock);   // dynamically initialising the mutex
mutex_lock(&lock);   // acquiring the mutex
...                  // critical section
mutex_unlock(&lock); // unlocking the mutex

However, there are two problems with this approach that compromise cleanup after calling mutex_lock():

  1. The developer needs to remember to call mutex_unlock()
  2. Depending on the conditional paths of the critical section, many mutex_unlock() calls need to be placed

To circumvent these problems, developers may use the goto error pattern, where the mutex_unlock() is centralised at the end of the critical section. For instance, this template of a function is pretty common:

int function_with_mutex_and_goto_error_pattern(void)
{
    int ret = 0;
    
    mutex_lock(&lock);  // lock is a struct mutex that has been initialised
    switch (var) {
    case FOO:
        do_foo(); // suppose this function never fails
        break;
    case BAR:
        ret = do_first_bar();  // suppose this function can fail
        if (ret)  // i.e., if do_first_bar() failed
            goto out;
            
        ret = do_second_bar();  // suppose this function can fail
        if (ret)  // i.e., if do_second_bar() failed
            goto out;
            
        do_third_bar(); // suppose this function never fails
        break;
    default:
        ret = -EINVAL;
    }
    
out:
    mutex_unlock(&lock);
    return ret;
}

The kernel provides what it calls Scope-based Cleanup Helpers (defined in include/linux/cleanup.h), which, in a very broad sense, allow transparent cleanup when resources go out of scope. For mutexes, the guard(mutex)(&lock) macro basically solves the two mentioned problems associated with cleaning up acquired mutexes. In the above example, if we were to use guard(mutex)(&lock), the code could be rewritten without any functional change as

int function_with_guard_mutex(void)
{
    int ret = 0;

    guard(mutex)(&lock);  // lock is a struct mutex that has been initialised
    switch (var) {
    case FOO:
        do_foo(); // suppose this function never fails
        return 0;
    case BAR:
        ret = do_first_bar();  // suppose this function can fail
        if (ret)  // i.e., if do_first_bar() failed
            return ret;
            
        ret = do_second_bar();  // suppose this function can fail
        if (ret)  // i.e., if do_second_bar() failed
            return ret;
        
        do_third_bar(); // suppose this function never fails
        return 0;
    default:
        return -EINVAL;
    }
}

Note that in this example, as guard(mutex)(&lock) is called “at the root scope” of the function, the mutex is unlocked if and only if it goes out of the function scope (in the example, in any of the return statements). There is a similar macro, scoped_guard(mutex, &lock), that creates an explicit block scope where the mutex is unlocked when it goes out of this block. For example, if we have a sequence in which we only want a small part of it to represent our critical section, we can use scoped_guard(mutex, &lock):

int function_with_scoped_guard_mutex(void)
{
    ...  // do some stuff with no mutex active
    scoped_guard(mutex, &lock) { // lock is a struct mutex that has been initialised
    ...  // inside these curly braces, the mutex is active
    }  // from here on, the mutex is unlocked
    ...  // do some other stuff with no mutex active
}

Important observations

Patch examples

2. Industrial I/O (IIO)

The suggestions below were pointed by Marcelo Schmitt and refined by us.

Suggestion 2.1) Replace manual bitfield manipulations with FIELD_GET(), FIELD_PREP(), and FIELD_MODIFY()

Although we normally think of numbers in the decimal system, we can represent them in different bases. Take the number 26, for example. In binary, it is:

\[\begin{aligned} (11010)_2 &= (2^4*1 + 2^3*1 + 2^2*0 + 2^1*1 + 2^0*0)_{10} \\ &= (16*1 + 8*1 + 4*0 + 2*1 + 1*0)_{10} \\ &= (16 + 8 + 0 + 2 + 0)_{10} \\ &= (26)_{10} \end{aligned}\]

We can also represent 26 in the hexadecimal system:

\[\begin{aligned} (1\textrm{A})_{16} &= (16^1*1 + 16^0*10)_{10} \\ &= (16*1 + 1*10)_{10} \\ &= (16 + 10)_{10} \\ &= (26)_{10} \end{aligned}\]

In kernel development, it is common to treat numbers as bitfields, where each bit or bit sequence encodes a different attribute. As an analogy, imagine a 4-bit bitfield dish that encodes an order at a restaurant serving 4 dishes, with each dish having two sizes (small and large) and two spice levels (regular and spicy). We can define how the bits in dish represent every possible dish as such (“bigger bits” to the left):

  1. Bits 0-1 -> Dish ID: \((00)_2\) is dish 0, \((01)_2\) is dish 1, \((10)_2\) is dish 2, and \((11)_2\) is dish 3.
  2. Bit 2 -> Dish size: \((0)_2\) is the small size and \((1)_2\) is the large size.
  3. Bit 3 -> Dish spiciness: \((0)_2\) is regular and \((1)_2\) is spicy.

In this analogy, the bitfield dish that represents the spicy small dish 1 is \((1001)_2\) (or \((9)_{10}\), in decimal). Now, imagine I want to query the bitfield dish for the dish size, i.e., isolate bit 2 from this 4-bit bitfield. The way to compute this value is to perform a mask (target specific bits) and a shift (move the bits left/right) operation. More specifically, these are the operations in pseudocode:

size = dish AND (0100)_2  // MASK: "selects" only bit 2
size = size >> 2          // SHIFT: "moves" bit 2 to bit 0

Alternatively, if you were handed a bit spiceness and you had to modify dish to have this spiceness. In this case, there would be a shift, a mask, then an OR operation:

val = spiceness << 3    // SHIFT: "moves" spiceness to bit 3
val = val AND (1000)_2  // MASK: "selects" only bit 3
dish = val OR dish      // MODIFY: "updates" dish with spiceness

This simple analogy illustrates the core common bitfield manipulations in the kernel, and the pseudocodes aren’t too far from the real implementations. These manipulations are so frequent that the kernel provides macros that simplify them, while making them more secure. These macros are the FIELD_GET(mask, bitfield) (e.g., get dish size), FIELD_PREP(mask, field_val) (e.g., prepare dish spiceness), and FIELD_MODIFY(mask, bitfield, field_val) (e.g., prepare dish spiceness and modify dish). These macros are defined in include/linux/bitfield.h.

In the context of IIO, you can check for manual bitfield (also referred to as registers) manipulations and see if they can be replaced by one of the above macros.

Patch examples

Suggestion 2.2) Update IIO drivers to be Include-What-You-Use (IWYU) compliant

IWYU is a Clang-based tool developed by Google to optimize #include directives in C/C++ code (https://include-what-you-use.org/). Check the patch examples for guidelines on how to pursue this suggestion.

Patch examples

Suggestion 2.3) Improvements in the IIO area of staging

Linux has its own “quarantine zone” for drivers, where code that is either entering or leaving the kernel lives, due to it not being quite refined or being problematic, respectively. This area is located in the drivers/staging directory, and the IIO-related drivers of staging are in drivers/staging/iio.

Usually, staging drivers are really rough, in terms of following the kernel conventions, coding style, and other requirements, so some patch opportunities can be considered newcomer friendly. (Un)fortunately, the IIO staging drivers are already well developed, making these easier tasks more rare.

Nevertheless, see this message from Marcelo Schmitt on the mailing list regarding possible tasks for IIO staging drivers: https://lore.kernel.org/linux-iio/Z8U0lsntJpTuBzyT@debian-BULLSEYE-live-builder-AMD64/

Patch examples

3. DRM AMDGPU DRIVERS

The suggestions below were taken from https://dri.freedesktop.org/docs/drm/gpu/todo.html and from https://dri.freedesktop.org/docs/drm/gpu/amdgpu/display/display-contributing.html. If you want to dig for other TODOs feel free to investigate these lists.

Suggestion 3.1) Convert logging to drm_* functions with drm_device parameter

For drivers which could have multiple instances, it is necessary to differentiate between which is which in the logs. Since DRM_INFO/WARN/ERROR don’t do this, drivers used dev_info/warn/err to make this differentiation. We now have drm_* variants of the DRM print functions, so we can start to convert those drivers back to using DRM-formatted specific log messages. This should only work in files inside drivers/gpu/drm/amd/display/amdgpu_dm/.

Before you start this conversion please contact the relevant maintainers to make sure your work will be merged - not everyone agrees that the DRM dmesg macros are better. The documentation mentions contacting Sean Paul and the maintainer of the driver you plan to convert.

Patch examples

Suggestion 3.2) Read and expand the subsystem documentation

Graphics is already a complicated field, but when it comes to 3D-accelerated graphics in the kernel, things can be get really complex really fast. So reading through the GPU Driver Developer’s Guide (https://docs.kernel.org/gpu/index.html), the DRM AMDGPU drivers doc (https://docs.kernel.org/gpu/amdgpu/index.html), and the display part of DRM AMDGPU drivers doc (https://docs.kernel.org/gpu/amdgpu/display/index.html), can be a great way to grasp some knowledge, whilst contributing to it.

An important observation is that we don’t encourage you to simply correct typos, but to dive deep into the documentation, reading the code, and updating/expanding parts that seem incorrect/unclear.

Patch examples

4. Code duplications

Suggestion 4.1) Duplications with 100% similarity

1) 3 sets of correlated duplications. Feel free to try to solve one or all of them. The specified files are in the directory drivers/gpu/drm/amd/amdgpu/:

jpeg_v5_0_1.c::jpeg_v5_0_1_process_interrupt AND jpeg_v5_0_2.c::jpeg_v5_0_2_process_interrupt , TOTAL NUMBER LINES IN FUNCTIONS: 59

jpeg_v5_0_0.c::jpeg_v5_0_0_process_interrupt AND jpeg_v5_3_0.c::jpeg_v5_3_0_process_interrupt , TOTAL NUMBER LINES IN FUNCTIONS: 18

jpeg_v2_0.c::jpeg_v2_0_process_interrupt AND jpeg_v3_0.c::jpeg_v3_0_process_interrupt , TOTAL NUMBER LINES IN FUNCTIONS: 18

2) The specified files are in the directory drivers/gpu/drm/amd/amdgpu/:

gfx_v11_0.c::gfx_v11_0_eop_irq AND gfx_v12_0.c::gfx_v12_0_eop_irq , TOTAL NUMBER LINES IN FUNCTIONS: 52

3) The specified files are in the directory drivers/gpu/drm/amd/amdgpu/:

gfx_v11_0.c::gfx_v11_0_ring_preempt_ib AND gfx_v12_0.c::gfx_v12_0_ring_preempt_ib , TOTAL NUMBER LINES IN FUNCTIONS: 49

4) The specified files are in the directory drivers/gpu/drm/amd/amdgpu/:

gmc_v10_0.c::gmc_v10_0_get_vm_pte AND gmc_v11_0.c::gmc_v11_0_get_vm_pte , TOTAL NUMBER LINES IN FUNCTIONS: 46

5) The specified files are in the directory drivers/gpu/drm/amd/amdgpu/:

gfx_v11_0.c::gfx_v11_0_set_userq_eop_interrupts AND gfx_v12_0.c::gfx_v12_0_set_userq_eop_interrupts , TOTAL NUMBER LINES IN FUNCTIONS: 42

6) The specified files are in the directory drivers/gpu/drm/amd/amdgpu/:

gfx_v11_0.c::gfx11_kiq_map_queues AND gfx_v12_0.c::gfx_v12_0_kiq_map_queues , TOTAL NUMBER LINES IN FUNCTIONS: 42

7) 2 sets of correlated duplications. Feel free to try to solve one or all of them. The specified files are in the directory drivers/gpu/drm/amd/amdgpu/:

Functions find: cik_sdma.c::cik_sdma_process_trap_irq AND sdma_v2_4.c::sdma_v2_4_process_trap_irq , TOTAL NUMBER LINES IN FUNCTIONS: 40

Functions find: cik_sdma.c::cik_sdma_process_trap_irq AND sdma_v3_0.c::sdma_v3_0_process_trap_irq , TOTAL NUMBER LINES IN FUNCTIONS: 40

8) The specified files are in the directory drivers/gpu/drm/amd/amdgpu/:

gmc_v10_0.c::gmc_v10_0_vm_fault_interrupt_state AND gmc_v12_0.c::gmc_v12_0_vm_fault_interrupt_state , TOTAL NUMBER LINES IN FUNCTIONS: 36

9) The specified files are in the directory drivers/gpu/drm/amd/amdgpu/:

gfx_v11_0.c::gfx_v11_0_handle_priv_fault AND gfx_v12_0.c::gfx_v12_0_handle_priv_fault , TOTAL NUMBER LINES IN FUNCTIONS: 36

10) drivers/iio/: the cluster only identifies these 6 files. However, it is possible to find other functions that exhibit minor variations in their signature, for example, receiving only a pointer to a channel (chan) or using an explicit index (channel) within the channels array.

Cluster #3 (Files: 6, Pairs: 15, Lines: 270)
---------------------
Function find: accel/hid-sensor-accel-3d.c::accel_3d_adjust_channel_bit_mask, TOTAL NUMBER LINES IN FUNCTION: 9
Function find: pressure/hid-sensor-press.c::press_adjust_channel_bit_mask, TOTAL NUMBER LINES IN FUNCTION: 9
Function find: magnetometer/hid-sensor-magn-3d.c::magn_3d_adjust_channel_bit_mask, TOTAL NUMBER LINES IN FUNCTION: 9
Function find: light/hid-sensor-prox.c::prox_adjust_channel_bit_mask, TOTAL NUMBER LINES IN FUNCTION: 9
Function find: light/hid-sensor-als.c::als_adjust_channel_bit_mask, TOTAL NUMBER LINES IN FUNCTION: 9
Function find: gyro/hid-sensor-gyro-3d.c::gyro_3d_adjust_channel_bit_mask, TOTAL NUMBER LINES IN FUNCTION: 9

11) drivers/iio/: bidirectional mappings of the same table.

Cluster #5 (Files: 2, Pairs: 1, Lines: 74)
---------------------
Function find: light/tsl2591.c::tsl2591_persist_cycle_to_lit, TOTAL NUMBER LINES IN FUNCTION: 37
Function find: light/tsl2591.c::tsl2591_persist_lit_to_cycle, TOTAL NUMBER LINES IN FUNCTION: 37

Suggestion 4.2) Duplications with 90% similarity

1) drivers/iio/: There is a common configuration sanitization step for both functions (retained from 2025)

imu/inv_icm42600/inv_icm42600_core.c::inv_icm42600_set_accel_conf AND imu/inv_icm42600/inv_icm42600_core.c::inv_icm42600_set_gyro_conf , TOTAL NUMBER LINES IN FUNCTIONS: 60

2) drivers/iio/: Possibly a refactoring opportunity (retained from 2025)

light/veml6030.c::veml6030_hw_init AND light/veml6030.c::veml6035_hw_init , TOTAL NUMBER LINES IN FUNCTIONS: 49

Difference in the configuration of register 0x00: https://www.vishay.com/docs/84366/veml6030.pdf and https://www.vishay.com/docs/84889/veml6035.pdf

Deduplications from last year (2025)

https://github.com/arkanjo-tool/arkanjo/blob/main/docs/PATCHES_MAC0470_5856_2025.md

History


comments powered by Disqus