# Adding a compat check

Let's say we want to build this codebase for a new kernel, Linux 6.15. In this case we may need to add compat checks under compat/checks, because some of the kernel API has changed.

Here's one example.

```
make K_BUILD=/tmp/dan/driver-ops/linux/v6.15.11
```

We got a compilation error such as:

```
bundle/net/sunrpc/xprt.c: In function ‘xprt_request_enqueue_receive’:
bundle/net/sunrpc/xprt.c:1198:9: error: implicit declaration of function ‘del_timer_sync’; did you mean ‘dev_mc_sync’? [-Wimplicit-function-declaration]
 1198 |         del_timer_sync(&xprt->timer);
```


Let's go to our kernel source tree to example Git:

```
$ git log -p v6.15.8 -- net/sunrpc/xprt.c --oneline 30
8fa7292fee5c treewide: Switch/rename to timer_delete[_sync]()
3f7edeac0bbb SUNRPC: Add a transport callback to handle dequeuing of an RPC request
0c14584cdbdb SUNRPC: Don't try to send when the connection is shutting down
57331a59ac0d (tag: nfs-for-6.8-1) NFSv4.1: Use the nfs_client's rpc timeouts for backchannel
e6f533b61597 SUNRPC: Fixup v4.1 backchannel request timeouts
59464b262ff5 SUNRPC: SOFTCONN tasks should time out when on the sending list
9a5a30568697 timers: Get rid of del_singleshot_timer_sync()
f1947d7c8a61 Merge tag 'random-6.1-rc1-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/crng/random
a251c17aa558 treewide: use get_random_u32() when possible
d6abc719a213 SUNRPC: use max_t() to simplify open code
9947e57b22dd SUNRPC: Directly use ida_alloc()/free()
17814819ac98 SUNRPC: Fix call completion races with call_decode()
72691a269f0b SUNRPC: Don't reuse bvec on retransmission of the request
497e6464d6ad SUNRPC create an rpc function that allows xprt removal from rpc_clnt
7ffcdaa67016 SUNRPC expose functions for offline remote xprt functionality
1a3b1bba7c7a Merge tag 'nfs-for-5.18-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
f00432063db1 SUNRPC: Ensure we flush any closed sockets before xs_xprt_free()
eb07d5a4da04 SUNRPC: handle malloc failure in ->request_prepare
965181d7ef7e Merge tag 'nfs-for-5.18-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
3848e96edf47 SUNRPC: avoid race between mod_timer() and del_timer_sync()
b2648015d452 SUNRPC: Make the rpciod and xprtiod slab allocation modes consistent
8db55a032ac7 SUNRPC: improve 'swap' handling: scheduling and PF_MEMALLOC
a80a84618689 SUNRPC: remove scheduling boost for "SWAPPER" tasks.
a721035477fb SUNRPC/xprt: async tasks mustn't block waiting for memory
0adc87940618 SUNRPC: Convert GFP_NOFS to GFP_KERNEL
```

One of these seems relevant

```
$ git show -p 8fa7292fee5c -- net/sunrpc/xprt.c
commit 8fa7292fee5c5240402371ea89ab285ec856c916
Author: Thomas Gleixner <tglx@linutronix.de>
Date:   Sat Apr 5 10:17:26 2025 +0200

    treewide: Switch/rename to timer_delete[_sync]()

    timer_delete[_sync]() replaces del_timer[_sync](). Convert the whole tree
    over and remove the historical wrapper inlines.

    Conversion was done with coccinelle plus manual fixups where necessary.

    Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
    Signed-off-by: Ingo Molnar <mingo@kernel.org>

diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
index 09f245cda526..0eab15465511 100644
--- a/net/sunrpc/xprt.c
+++ b/net/sunrpc/xprt.c
@@ -1167,7 +1167,7 @@ xprt_request_enqueue_receive(struct rpc_task *task)
        spin_unlock(&xprt->queue_lock);

        /* Turn off autodisconnect */
-       del_timer_sync(&xprt->timer);
+       timer_delete_sync(&xprt->timer);
        return 0;
 }

@@ -2138,7 +2138,7 @@ static void xprt_destroy(struct rpc_xprt *xprt)
         * can only run *before* del_time_sync(), never after.
         */
        spin_lock(&xprt->transport_lock);
-       del_timer_sync(&xprt->timer);
+       timer_delete_sync(&xprt->timer);
        spin_unlock(&xprt->transport_lock);

        /*
```


In this case, if we simply add a wrapper to the old API, we would solve the compilation error.

Let's first get a description of the commit:

```
linux$ git describe --contains 8fa7292fee5c5240402371ea89ab285ec856c916

v6.15-rc1~7^2~10
```

Let's add: `compat/checks/timer_delete_sync_6_15.c`

```
#include <linux/module.h>
#include <linux/timer.h>

/*
 * v6.15-rc1~7^2~10 "treewide: Switch/rename to timer_delete[_sync]("
 */
static int test_dummy(struct timer_list *timer)
{
       return timer_delete_sync(timer);
}

EXPORT_SYMBOL(test_dummy);
MODULE_LICENSE("GPL");
```

Notice a few things:

- Mention of the original commit in the Linux kernel as generated via `git describe --contains <commit>` in the Linux kernel, with its subject line. You must run `git describe --contains <commit>` to generate this.
- The major version of which this was added.

Now let's add the wrapper `compat/include/linux/timer.h`:

```
#ifndef __COMPAT_LINUX_TIMER_H__
#define __COMPAT_LINUX_TIMER_H__

#include_next <linux/timer.h>

#ifdef COMPAT_DETECT_TIMER_DELETE_SYNC_6_15
#define del_timer_sync timer_delete_sync
#endif /* COMPAT_DETECT_TIMER_DELETE_SYNC_6_15 */

#endif /* __COMPAT_LINUX_TIMER_H__ */
```

The generated define `COMPAT_DETECT_TIMER_DELETE_SYNC_6_15` refers to the file we added earlier.

## Important Guidelines for compat/include Headers

When creating compatibility headers in `compat/include/`, it is advised to only use `#include_next` directives. Avoid implementing complex functions directly in these headers, as this can lead to symbol visibility issues and linking problems.

Instead:
- Use `#include_next` to include the original kernel header
- Provide only simple macros, typedefs, or `extern` function declarations
- Implement actual function bodies in `compat/support/*.c` files with proper `EXPORT_SYMBOL_GPL()` declarations
- Avoid using regular `#include` directives, as they can cause unexpected header order issues with the target kernel

This approach ensures proper symbol visibility and avoids issues with undefined references during module loading.

When we run `make` again, we will see:

```
#define COMPAT_DETECT_TIMER_DELETE_SYNC_6_15
```

We will see `/* #undef COMPAT_DETECT_TIMER_DELETE_SYNC_6_15 */` for older kernels without this change. If there's a bug in the compat check, we can example `compat/errors.log`.

## Consolidating Redundant Compat Checks

Sometimes multiple functions are changed in the same kernel commit. In such cases, we only need one compat check file to detect the commit, rather than creating separate checks for each affected function.

For example, if a single kernel commit renames multiple related functions like `lookup_one_len()`, `lookup_one_len_unlocked()`, and `lookup_positive_unlocked()`, we should:

1. **Create only one compat check file** (e.g., `lookup_one_len_6_16.c`) that tests for any one of the changed functions
2. **Remove redundant check files** for the other functions affected by the same commit
3. **Consolidate implementations** in `compat/support/*.c` under a single `#ifndef` guard

Example consolidation in `compat/support/inode.c`:
```c
#ifndef COMPAT_DETECT_LOOKUP_ONE_LEN_6_16
struct dentry *lookup_one_len_unlocked(const char *name,
                                       struct dentry *base,
                                       int len)
{
    // implementation
}
EXPORT_SYMBOL_GPL(lookup_one_len_unlocked);

struct dentry *lookup_one_len(const char *name,
                             struct dentry *base, int len)
{
    // implementation  
}
EXPORT_SYMBOL_GPL(lookup_one_len);

struct dentry *lookup_positive_unlocked(const char *name,
                                        struct dentry *base,
                                        int len)
{
    // implementation
}
EXPORT_SYMBOL_GPL(lookup_positive_unlocked);
#endif /* COMPAT_DETECT_LOOKUP_ONE_LEN_6_16 */
```

This approach:
- **Reduces build time** by avoiding redundant compilation checks
- **Simplifies maintenance** with fewer files to manage  
- **Prevents inconsistencies** between related compat implementations
- **Follows the principle** that we only need to detect commits in the target kernel, not individual function changes
