Takeaways taken from the kernel's coding style guide. This is only for my own reference. Please read the original article instead.
https://www.kernel.org/doc/html/v4.10/process/coding-style.html

Indentation:

  • Tabs are Tabs, don't expand them into spaces.
  • Tabs are 8 characters.
  • No more than 3 levels of indentation should be necessary.
  • switch and case labels on the same level
  • Don’t put multiple statements on a single line

Breaking long lines and strings:

  • Textwidth = 80 chars
  • Never break user-visible strings such as printk messages, because that breaks the ability to grep for them.

Braces and Spaces:

  • For non function blocks put the opening brace last on the line, and put the closing brace first
  • For function blocks opening brace at the beginning of the next line, thus:
  • the closing brace is empty on a line of its own, except in the cases where it is followed by a continuation of the same statement
  • Do not unnecessarily use braces where a single statement will do.
  • The above DOES NOT apply if only one branch is a single statement.
  • Use a space after keywords: if, switch, case, for, do, while
  • No space after sizeof, __attribute__etc. because they look like functions.
  • Do not add spaces around (inside) parenthesized expressions.
  • * for pointers attach to the data name or function name, not to the type name.
  • One space around most binary and ternary operators: = + - < > * / % | & ^ <= >= == != ? :
  • No space after unary operators: & * + - ~ ! sizeof typeof alignof __attribute__ defined
  • No space before the postfix/prefix increment & decrement unary operators: ++ --
  • No space around the . and -> structure member operators.
  • No trailing whitespace at end of lines.

Naming

  • Do not encode type of a function into the name (Hungarian notation) - the compiler knows the types anyways.
  • GLOBAL variables need to have descriptive names, as do global functions.
  • LOCAL variable names should be short, and to the point

Typedefs

  • It’s a mistake to use typedef for structures and pointers. (let struct be struct and let pointers be pointers)
  • NEVER use a typedef UNLESS one of the following applies
    • totally opaque objects (where the typedef is actively used to hide what the object is). e.t. pte_t
    • Clear integer types, where the abstraction helps avoid confusion whether it is int or long.
    • when you use sparse to literally create a new type for type-checking.
    • New types which are identical to standard C99 types, in certain exceptional circumstances.
    • Types safe for use in userspace. e.t. __u32

Functions

  • In function prototypes, include parameter names with their data types.
  • In source files, separate functions with one blank line. If the function is exported, the EXPORT macro for it should follow immediately after the closing function brace line.

more like "you should" than "you must":

  • Functions should be short and sweet, and do just one thing (but it’s OK to have a longer function).
  • Use helper functions with descriptive names (you can ask the compiler to in-line them if you think it’s performance-critical)
  • the number of local variables. They shouldn’t exceed 5-10, or you’re doing something wrong.

Function return values and names

  • Don't mix the two practice:
    • error-code integer (-Exxx = failure, 0 = success) and
    • succeeded boolean (0 = failure, non-zero = success)
  • Convention:
    • error-code integer for action or imperative command (e.g. work() )
    • boolean for predicate (e.g. pci_dev_present() )
  • All EXPORTed and public functions must respect this convention

If the name of a function is an action or an imperative command, the function should return an error-code integer. If the name is a predicate, the function should return a "succeeded" boolean. [...] Functions whose return value is the actual result of a computation, rather than an indication of whether the computation succeeded, are not subject to this rule.

Centralized exiting of functions

  • just keep using gotos...
  • The goto statement comes in handy when a function exits from multiple locations and some common work such as cleanup has to be done. If there is no cleanup needed then just return directly.
  • Choose label names which say what the goto does or why the goto exists.

Commenting

  • Don't over-comment
  • NEVER try to explain HOW your code works in a comment.
  • Put the comments at the head of the function, telling people what it does, and possibly WHY it does it. NOT HOW.
  • Avoid putting comments inside a function body.
  • You can make small comments to note or warn about something particularly. clever (or ugly), but try to avoid excess.
  • Kernel-doc format for kernel API functions
  • Comment data, whether they are basic types or derived types.

Macros, Enums and RTL

  • Names of macros defining constants and labels in enums are capitalized.
  • CAPITALIZED macro names are appreciated but macros resembling functions may be named in lower case.
  • Generally, inline functions are preferable to macros resembling functions.
  • Macros with multiple statements should be enclosed in a do - while block:
  • macros defining constants using expressions must enclose the expression in parentheses. Beware of similar issues with macros using parameters.
  • Things to avoid when using macros:
    • macros that affect control flow:
    • macros that depend on having a local variable with a magic name: #define FOO(val) bar(index, val)
    • macros with arguments that are used as l-values: FOO(x) = y; will bite you if somebody e.g. turns FOO into an inline function.
    • namespace collisions when defining local variables in macros resembling functions:
  • Don’t re-invent the kernel macros (check include/linux/kernel.h).

inline asm

  • Don't use when C can do the same.
  • Large, non-trivial assembly functions should go in .S files, with corresponding C prototypes defined in C header files. The C prototypes for assembly functions should use asmlinkage.
  • mark asm statement as volatile, to prevent GCC from removing it if GCC does not notice any side effects. (not always necessarily)
  • each instruction on a separate line in a separate quoted string; end each string except the last with nt to properly indent the next instruction in the assembly output:

The inline disease

do not abuse inline - it's often not appropriate and it makes bigger program.

  • Bigger icache footprint
  • less memory available for pagecache.

A reasonable rule of thumb is to not put inline at functions that have more than 3 lines of code in them. An exception to this rule are the cases where a parameter is known to be a compiletime constant, and as a result of this constantness you know the compiler will be able to optimize most of your function away at compile time. For a good example of this later case, see the kmalloc() inline function.

Conditional Compilation

  • Wherever possible, don’t use preprocessor conditionals (#if, #ifdef) in .c files;

Instead, use such conditionals in a header file defining functions for use in those .c files, providing no-op stub versions in the #else case, and then call those functions unconditionally from .c files.

  • Prefer to compile out entire functions, rather than portions of functions or portions of expressions.
  • Rather than putting an ifdef in an expression, factor out part or all of the expression into a separate helper function and apply the conditional to that function.
  • mark variables __maybe_unused that go unused in a particular configuration.
  • where possible, use the IS_ENABLED macro to convert a Kconfig symbol into a C boolean expression, and use it in a normal C conditional:
  • close non-trivial #if and #ifdef block with comments:
#ifdef CONFIG_SOMETHING
...
#endif /* CONFIG_SOMETHING */

Not included here:

  • Kconfig
  • Data structures
  • Printing kernel messages
  • Allocating memory (TODO)

Snippets

Switch/case:

switch (suffix) {
case 'G':
case 'g':
        mem <<= 30;
        break;
case 'K':
case 'k':
        mem <<= 10;
        /* fall through */
default:
        break;
}

Non-function block:

if (x) {
        // do ...
}

Non-function block w. addition terms:

if (x == y) {
        // ..
} else if (x > y) {
        // ...
} else {
        // ....
}

Function block:

int function(int x)
{
        // body of function
}

Single branch cond, no brakckets:

if (condition)
        action();

Multi branch cond, some are not single stmt:

if (condition) {
        do_this();
        do_that();
} else {
        otherwise();
}

Do not add spaces around (inside) parenthesized expressions. This is bad practice:

s = sizeof( struct file );

Export a function w. Macro:

int system_is_up(void)
{
        return system_state == SYSTEM_RUNNING;
}
EXPORT_SYMBOL(system_is_up);

Macros with multiple statements should be enclosed in a do - while block:

#define macrofun(a, b, c)                       \
        do {                                    \
                if (a == 5)                     \
                        do_this(b, c);          \
        } while (0)

macros that affect control flow: BELOW IS BAD

#define FOO(x)                                  \
        do {                                    \
                if (blah(x) < 0)                \
                        return -EBUGGERED;      \
        } while (0)

enclose the expression (in macros) in parentheses:

#define CONSTANT 0x4000
#define CONSTEXP (CONSTANT | 3)

namespace collisions when defining local variables in macros resembling functions: ret is a common name for a local variable - __foo_ret is less likely to collide with an existing variable.:

#define FOO(x)                          \
({                                      \
        typeof(x) ret;                  \
        ret = calc_ret(x);              \
        (ret);                          \
})

multi-line inline asm example:

asm ("magic %reg1, #42\n\t"
     "more_magic %reg2, %reg3"
     : /* outputs */ : /* inputs */ : /* clobbers */);

where possible, use the IS_ENABLED macro to convert a Kconfig symbol into a C boolean expression, and use it in a normal C conditional. The compiler will constant-fold the conditional away, and include or exclude the block of code just as with an #ifdef, so this will not add any runtime overhead.

if (IS_ENABLED(CONFIG_SOMETHING)) {
        ...
}