Post

Sending My First Real Kernel Patch Upstream

What happened when I tried to send a patch to the IIO mailing list for the first time

Sending My First Real Kernel Patch Upstream

Introduction

This post is about my first real attempt at contributing a patch to the upstream Linux kernel. Not a tutorial exercise, not a dry-run — an actual patch sent to actual maintainers. It was a humbling experience, and I want to document everything that happened honestly, including the mistakes.

The patch targets drivers/iio/temperature/tsys02d.c, a temperature sensor driver in the IIO subsystem. The change is small: replacing a manual mutex_lock()/mutex_unlock() pair with guard(mutex)(), a scope-based cleanup helper from include/linux/cleanup.h.


The Patch

The target function was tsys02d_write_raw(). It had a classic pattern where ret existed only to hold the return value across a manual mutex_unlock() call:

1
2
3
4
5
6
// Before
mutex_lock(&dev_data->lock);
dev_data->res_index = i;
ret = ms_sensors_write_resolution(dev_data, i);
mutex_unlock(&dev_data->lock);
return ret;

With guard(mutex)(), the lock is released automatically when the function returns — no manual unlock needed, and no need for the intermediate variable:

1
2
3
4
// After
guard(mutex)(&dev_data->lock);
dev_data->res_index = i;
return ms_sensors_write_resolution(dev_data, i);

I also added #include <linux/cleanup.h> explicitly, since guard() lives there.


Building the Kernel

The first build attempt was against just the module:

1
make -j$(nproc) drivers/iio/temperature/tsys02d.ko

This gave a wall of ERROR: modpost: undefined! errors for symbols like ms_sensors_write_resolution and devm_iio_device_alloc. The problem is that compiling a single module in isolation doesn’t populate Module.symvers with the symbols from the IIO core and the ms_sensors library. The fix was a full kernel build:

1
make -j$(nproc) 2>&1 | tee build.log

After that finished, I confirmed the module compiled cleanly:

1
2
3
4
grep "tsys02d" build.log
# CC [M]  drivers/iio/temperature/tsys02d.mod.o
# LD [M]  drivers/iio/temperature/tsys02d.ko
# BTF [M] drivers/iio/temperature/tsys02d.ko

Sending to the Internal CI

After committing with git commit -s and running checkpatch.pl with zero errors, I sent the patch to the FLUSP internal CI:

1
kw send-patch --send -1 --private --to=kernel@lists.ime.usp.br
1
Result: 250

The 250 status means the email was accepted by the server. I then sent it directly upstream to the IIO mailing list.


The Reviews

Two maintainers replied. Here is what they said and what I learned from each.

Jonathan Cameron — IIO Maintainer

Jonathan caught a real bug. He pointed out that guard(mutex)() inside a case: block without curly braces is broken because of how C defines scope in switch/case statements. The cleanup variable created by guard() ends up in an ambiguous scope shared with the default: branch, which can produce incorrect behavior.

The fix is to add {} around the case body:

1
2
3
4
5
6
7
8
9
10
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ: {       // ← braces required
    ...
    guard(mutex)(&dev_data->lock);
    dev_data->res_index = i;
    return ms_sensors_write_resolution(dev_data, i);
}
default:
    return -EINVAL;
}

He also flagged two smaller issues:

  • I accidentally removed a blank line that shouldn’t have been touched — an unrelated change that should never appear in a focused patch.
  • The commit message body lines were too short. The kernel standard is around 75 characters per line.

Andy Shevchenko — IIO Reviewer

Andy raised three separate points.

First, I sent the patch using my display name lauraarakaki instead of my full legal name. The kernel requires real names in Signed-off-by: — it is a legal statement (the DCO), not just a label.

Second, he suggested that the #include <linux/cleanup.h> I added would be better introduced as a prerequisite patch that sorts the headers alphabetically, optionally followed by another patch applying IWYU (Include What You Use). In other words, my single patch was trying to do two things at once — the header addition and the guard() refactor — and ideally these should be separate commits.

Third — and this was the most important point — Andy suggested that I should participate in reviewing patches on the IIO mailing list (lore.kernel.org/linux-iio) before submitting my own. Reviewing other people’s patches teaches you what maintainers care about, what mistakes are common, and what good patches look like in practice. It also builds a visible track record in the community.


What I Need to Fix for v2

IssueFix
guard() in case: without {}Add braces around the case body
Blank line removed unintentionallyRestore it
Commit message line lengthRewrite to ~75 chars per line
Display name in Signed-off-byUse full legal name
Header addition mixed into the patchSplit into a prerequisite patch

What I Learned

This was my first contact with the real kernel review process, and it taught me more than any tutorial did.

The guard(mutex) + switch/case scoping issue is non-obvious and easy to miss. I would not have caught it by just reading the code — it took someone with deep C and kernel experience to spot it.

The feedback from Andy about reviewing first resonated with me. The IIO mailing list is public and reading it regularly would have already shown me these exact patterns before I made the same mistakes.

Real name in Signed-off-by is not a style preference — it is a legal requirement tied to the Developer’s Certificate of Origin. Small detail, big implication.

And the blank line: always run git diff carefully before committing. Every single line in a patch should be intentional.


Next Steps

  • Fix all four issues and amend the commit
  • Split the cleanup.h include into a separate prerequisite patch
  • Spend time reviewing patches on lore.kernel.org/linux-iio before resubmitting
  • Resubmit as [PATCH v2]
This post is licensed under CC BY 4.0 by the author.