Checkpatch

Checkpatch (scripts/checkpatch.pl) 是一个 perl 脚本,用于检查补丁中微不足道的样式违规,并可选择对其进行更正。Checkpatch 也可以在文件上下文中使用,并且无需内核树。

Checkpatch 并不总是正确的。您的判断优先于 checkpatch 消息。如果您的代码在违规的情况下看起来更好,那么最好不要管它。

选项

本节将介绍 checkpatch 可以运行的选项。

用法

./scripts/checkpatch.pl [OPTION]... [FILE]...

可用选项

  • -q, --quiet

    启用静默模式。

  • -v, --verbose 启用详细模式。将输出额外的详细测试描述,以便提供有关为什么显示特定消息的信息。

  • --no-tree

    在没有内核树的情况下运行 checkpatch。

  • --no-signoff

    禁用“Signed-off-by”行检查。签名是补丁说明末尾的简单一行,证明您编写了它或有权将其作为开源补丁传递。

    示例

    Signed-off-by: Random J Developer <[email protected]>
    

    设置此标志可有效地停止在补丁上下文中缺少 signed-off-by 行的消息。

  • --patch

    将 FILE 视为补丁。这是默认选项,无需显式指定。

  • --emacs

    将输出设置为 emacs 编译窗口格式。这允许 emacs 用户直接从编译窗口中的错误跳转到补丁中的错误行。

  • --terse

    每个报告仅输出一行。

  • --showfile

    显示差异文件的位置,而不是输入文件的位置。

  • -g, --git

    将 FILE 视为单个提交或 git 修订范围。

    单个提交,使用

    • <rev>

    • <rev>^

    • <rev>~n

    多个提交,使用

    • <rev1>..<rev2>

    • <rev1>...<rev2>

    • <rev>-<count>

  • -f, --file

    将 FILE 视为常规源文件。在内核中对源文件运行 checkpatch 时必须使用此选项。

  • --subjective, --strict

    在 checkpatch 中启用更严格的测试。默认情况下,作为 CHECK 发出的测试默认情况下不会激活。使用此标志可激活 CHECK 测试。

  • --list-types

    checkpatch 发出的每个消息都有一个关联的 TYPE。添加此标志以显示 checkpatch 中的所有类型。

    请注意,当此标志处于活动状态时,checkpatch 不会读取输入 FILE,并且不会发出任何消息。仅输出 checkpatch 中的类型列表。

  • --types TYPE(,TYPE2...)

    仅显示具有给定类型的消息。

    示例

    ./scripts/checkpatch.pl mypatch.patch --types EMAIL_SUBJECT,BRACES
    
  • --ignore TYPE(,TYPE2...)

    Checkpatch 将不会发出指定类型的消息。

    示例

    ./scripts/checkpatch.pl mypatch.patch --ignore EMAIL_SUBJECT,BRACES
    
  • --show-types

    默认情况下,checkpatch 不会显示与消息关联的类型。设置此标志以在输出中显示消息类型。

  • --max-line-length=n

    设置最大行长度(默认值为 100)。如果某行超过指定长度,则会发出 LONG_LINE 消息。

    补丁和文件上下文的消息级别不同。对于补丁,将发出 WARNING。而对于文件,将发出较温和的 CHECK。因此,对于文件上下文,还必须启用 --strict 标志。

  • --min-conf-desc-length=n

    设置 Kconfig 条目的最小描述长度,如果较短,则发出警告。

  • --tab-size=n

    设置制表符的空格数(默认值为 8)。

  • --root=PATH

    内核树根的 PATH。

    从内核根目录外部调用 checkpatch 时必须指定此选项。

  • --no-summary

    禁止每个文件的摘要。

  • --mailback

    仅在出现警告或错误时生成报告。较温和的检查将不包括在内。

  • --summary-file

    在摘要中包含文件名。

  • --debug KEY=[0|1]

    打开/关闭 KEY 的调试,其中 KEY 是“values”、“possible”、“type”和“attr”之一(默认为全部关闭)。

  • --fix

    这是一个实验性功能。如果存在可更正的错误,则会创建一个文件 <inputfile>.EXPERIMENTAL-checkpatch-fixes,其中已更正了可自动修复的错误。

  • --fix-inplace

    实验性 - 类似于 --fix,但输入文件会被修复覆盖。

    除非您绝对确定并已备份,否则请勿使用此标志。

  • --ignore-perl-version

    覆盖 perl 版本的检查。如果 perl 版本不符合指定的最低要求,则启用此标志后可能会遇到运行时错误。

  • --codespell

    使用 codespell 字典检查拼写错误。

  • --codespellfile

    使用指定的 codespell 文件。默认值为“/usr/share/codespell/dictionary.txt”。

  • --typedefsfile

    从此文件读取其他类型。

  • --color[=WHEN]

    使用颜色“always”、“never”,或仅当输出是终端时使用“auto”。默认值为“auto”。

  • --kconfig-prefix=WORD

    使用 WORD 作为 Kconfig 符号的前缀(默认值为 CONFIG_)。

  • -h, --help, --version

    显示帮助文本。

消息级别

checkpatch 中的消息分为三个级别。checkpatch 中的消息级别表示错误的严重性。它们是

  • ERROR

    这是最严格的级别。必须认真对待 ERROR 类型的消息,因为它们表示很可能存在错误的地方。

  • WARNING

    这是下一个更严格的级别。WARNING 类型的消息需要更仔细的审查。但它比 ERROR 更温和。

  • CHECK

    这是最温和的级别。这些是可能需要一些思考的事情。

类型描述

本节包含 checkpatch 中所有消息类型的描述。

分配样式

ALLOC_ARRAY_ARGS

kcalloc 或 kmalloc_array 的第一个参数应该是元素数量。将 sizeof() 作为第一个参数通常是错误的。

请参阅:https://linuxkernel.org.cn/doc/html/latest/core-api/memory-allocation.html

ALLOC_SIZEOF_STRUCT

分配样式不好。通常对于使用 sizeof() 获取内存大小的一系列分配函数,如下构造

p = alloc(sizeof(struct foo), ...)

应为

p = alloc(sizeof(*p), ...)

请参阅:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#allocating-memory

ALLOC_WITH_MULTIPLY

优先使用 kmalloc_array/kcalloc 而不是带有 sizeof 乘法的 kmalloc/kzalloc。

请参阅:https://linuxkernel.org.cn/doc/html/latest/core-api/memory-allocation.html

API 用法

ARCH_DEFINES

应尽可能避免使用特定于架构的定义。

ARCH_INCLUDE_LINUX

每当包含 asm/file.h 并且存在 linux/file.h 时,如果 linux/file.h 包含 asm/file.h,则可以进行转换。但是,情况并非总是如此(请参阅 signal.h)。此消息类型仅针对来自 arch/ 的包含发出。

AVOID_BUG

应完全避免使用 BUG() 或 BUG_ON()。请改用 WARN() 和 WARN_ON(),并尽可能优雅地处理“不可能”的错误条件。

请参阅:https://linuxkernel.org.cn/doc/html/latest/process/deprecated.html#bug-and-bug-on

CONSIDER_KSTRTO

simple_strtol()simple_strtoll()simple_strtoul()simple_strtoull() 函数显式忽略溢出,这可能会导致调用者出现意外结果。相应的 kstrtol()kstrtoll()kstrtoul()kstrtoull() 函数往往是正确的替代项。

请参阅:https://linuxkernel.org.cn/doc/html/latest/process/deprecated.html#simple-strtol-simple-strtoll-simple-strtoul-simple-strtoull

CONSTANT_CONVERSION

不鼓励对以下函数使用 __constant_<foo> 形式

__constant_cpu_to_be[x]
__constant_cpu_to_le[x]
__constant_be[x]_to_cpu
__constant_le[x]_to_cpu
__constant_htons
__constant_ntohs

在 include/uapi/ 之外使用这些中的任何一个都不可取,因为当参数为常量时,不使用 __constant_ 的函数是相同的。

在 big endian 系统中,诸如 __constant_cpu_to_be32(x) 和 cpu_to_be32(x) 之类的宏会扩展为相同的表达式

#define __constant_cpu_to_be32(x) ((__force __be32)(__u32)(x))
#define __cpu_to_be32(x)          ((__force __be32)(__u32)(x))

在 little endian 系统中,宏 __constant_cpu_to_be32(x) 和 cpu_to_be32(x) 会扩展为 __constant_swab32 和 __swab32。__swab32 有一个 __builtin_constant_p 检查

#define __swab32(x)                               \
  (__builtin_constant_p((__u32)(x)) ?     \
  ___constant_swab32(x) :                 \
  __fswab32(x))

因此,他们最终对常量有一个特殊情况。列表中的所有宏也是如此。因此,使用 __constant_... 形式是不必要的冗长,并且不应在 include/uapi 之外使用。

参见:https://lore.kernel.org/lkml/1400106425.12666.6.camel@joe-AO725/

DEPRECATED_API

检测到使用了已弃用的 RCU API。建议使用新的 vanilla-RCU 对等 API 替换旧的、具有多种风格的 RCU API。

完整的 RCU API 列表可以从内核文档中查看。

参见:https://linuxkernel.org.cn/doc/html/latest/RCU/whatisRCU.html#full-list-of-rcu-apis

DEPRECATED_VARIABLE

EXTRA_{A,C,CPP,LD}FLAGS 已弃用,应替换为通过 commit f77bf01425b1(“kbuild: introduce ccflags-y, asflags-y and ldflags-y”)添加的新标志。

可以使用以下转换方案

EXTRA_AFLAGS    ->  asflags-y
EXTRA_CFLAGS    ->  ccflags-y
EXTRA_CPPFLAGS  ->  cppflags-y
EXTRA_LDFLAGS   ->  ldflags-y

参见

DEVICE_ATTR_FUNCTIONS

DEVICE_ATTR 中使用的函数名称不常见。通常,store 和 show 函数与 <attr>_store 和 <attr>_show 一起使用,其中 <attr> 是设备的命名属性变量。

请考虑以下示例

static DEVICE_ATTR(type, 0444, type_show, NULL);
static DEVICE_ATTR(power, 0644, power_show, power_store);

函数名称最好遵循上述模式。

参见:https://linuxkernel.org.cn/doc/html/latest/driver-api/driver-model/device.html#attributes

DEVICE_ATTR_RO

可以使用 DEVICE_ATTR_RO(name) 辅助宏来代替 DEVICE_ATTR(name, 0444, name_show, NULL);

请注意,该宏会自动将 _show 附加到设备的命名属性变量以用于 show 方法。

参见:https://linuxkernel.org.cn/doc/html/latest/driver-api/driver-model/device.html#attributes

DEVICE_ATTR_RW

可以使用 DEVICE_ATTR_RW(name) 辅助宏来代替 DEVICE_ATTR(name, 0644, name_show, name_store);

请注意,该宏会自动将 _show 和 _store 附加到设备的命名属性变量以用于 show 和 store 方法。

参见:https://linuxkernel.org.cn/doc/html/latest/driver-api/driver-model/device.html#attributes

DEVICE_ATTR_WO

可以使用 DEVICE_AATR_WO(name) 辅助宏来代替 DEVICE_ATTR(name, 0200, NULL, name_store);

请注意,该宏会自动将 _store 附加到设备的命名属性变量以用于 store 方法。

参见:https://linuxkernel.org.cn/doc/html/latest/driver-api/driver-model/device.html#attributes

DUPLICATED_SYSCTL_CONST

Commit d91bff3011cf (“proc/sysctl: add shared variables for range check”) 添加了一些共享的 const 变量,用于代替每个源文件中的本地副本。

请考虑使用 include/linux/sysctl.h 中的共享变量替换 sysctl 范围检查值。可以使用以下转换方案

&zero     ->  SYSCTL_ZERO
&one      ->  SYSCTL_ONE
&int_max  ->  SYSCTL_INT_MAX

参见

ENOSYS

ENOSYS 表示调用了不存在的系统调用。以前,它被错误地用于对其他有效的系统调用进行无效操作之类的事情。这应该在新代码中避免。

参见:https://lore.kernel.org/lkml/5eb299021dec23c1a48fa7d9f2c8b794e967766d.1408730669.git.luto@amacapital.net/

ENOTSUPP

ENOTSUPP 不是一个标准的错误代码,应在新补丁中避免。应使用 EOPNOTSUPP 代替。

参见:https://lore.kernel.org/netdev/[email protected]/

EXPORT_SYMBOL

EXPORT_SYMBOL 应紧跟要导出的符号。

IN_ATOMIC

in_atomic() 不适用于驱动程序,因此任何此类使用都会报告为错误。此外,in_atomic() 通常用于确定是否允许休眠,但在此使用模型中不可靠。因此,强烈建议不要使用它。

但是,in_atomic() 对于核心内核使用是可以的。

参见:https://lore.kernel.org/lkml/[email protected]/

LOCKDEP

添加 lockdep_no_validate 类是为了临时措施,以防止在将 device->sem 转换为 device->mutex 时出现警告。它不应被用于任何其他目的。

参见:https://lore.kernel.org/lkml/1268959062.9440.467.camel@laptop/

MALFORMED_INCLUDE

#include 语句的路径格式不正确。发生这种情况是因为作者在路径名中意外地包含了双斜杠“//”。

USE_LOCKDEP

应优先使用基于 lockdep_assert_held() 的注释,而不是基于 spin_is_locked() 的断言。

参见:https://linuxkernel.org.cn/doc/html/latest/locking/lockdep-design.html#annotations

UAPI_INCLUDE

include/uapi 中的任何 #include 语句都不应使用 uapi/ 路径。

USLEEP_RANGE

应优先使用 usleep_range() 而不是 udelay()。内核文档中提到了使用 usleep_range() 的正确方法。

注释

BLOCK_COMMENT_STYLE

注释样式不正确。多行注释的首选样式为

/*
* This is the preferred style
* for multi line comments.
*/

网络注释样式略有不同,第一行不像前者那样为空

/* This is the preferred comment style
* for files in net/ and drivers/net/
*/

参见:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#commenting

C99_COMMENTS

不应使用 C99 样式的单行注释 (//)。请改用块注释样式。

参见:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#commenting

DATA_RACE

data_race() 的应用应附有注释,以记录其被认为安全的原因。

参见:https://lore.kernel.org/lkml/[email protected]/

FSF_MAILING_ADDRESS

内核维护人员拒绝新的 GPL 样板段落,该段落指示人们写信给 FSF 以获取 GPL 的副本,因为 FSF 过去已搬迁,并且可能会再次搬迁。因此,不要写关于写信给自由软件基金会的邮寄地址的段落。

参见:https://lore.kernel.org/lkml/20131006222342.GT19510@leaf/

提交消息

BAD_SIGN_OFF

signed-off-by 行不符合社区指定的标准。

参见:https://linuxkernel.org.cn/doc/html/latest/process/submitting-patches.html#developer-s-certificate-of-origin-1-1

BAD_STABLE_ADDRESS_STYLE

stable 的电子邮件格式不正确。一些有效的 stable 地址选项是

对于添加版本信息,应使用以下注释样式

[email protected] # version info
COMMIT_COMMENT_SYMBOL

git 会忽略以“#”开头的提交日志行作为注释。要解决此问题,只需在日志行前面添加一个空格即可。

COMMIT_MESSAGE

补丁缺少提交说明。应添加对补丁所做更改的简要说明。

参见:https://linuxkernel.org.cn/doc/html/latest/process/submitting-patches.html#describe-your-changes

EMAIL_SUBJECT

在主题行中命名发现问题的工具不是很有用。一个好的主题行总结了补丁带来的更改。

参见:https://linuxkernel.org.cn/doc/html/latest/process/submitting-patches.html#describe-your-changes

FROM_SIGN_OFF_MISMATCH

作者的电子邮件与 Signed-off-by: 行中的电子邮件不匹配。这有时可能是由于电子邮件客户端配置不当造成的。

发出此消息的原因如下

- The email names do not match.
- The email addresses do not match.
- The email subaddresses do not match.
- The email comments do not match.
MISSING_SIGN_OFF

补丁缺少 Signed-off-by 行。应根据开发者原创证书添加 signed-off-by 行。

参见:https://linuxkernel.org.cn/doc/html/latest/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin

NO_AUTHOR_SIGN_OFF

补丁的作者没有签署补丁。需要在补丁的解释末尾添加一个简单的签署行,以表示作者编写了它,或者有权将其作为开源补丁传递。

参见:https://linuxkernel.org.cn/doc/html/latest/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin

DIFF_IN_COMMIT_MSG

避免在提交消息中包含 diff 内容。当尝试应用包含变更日志和 diff 的文件时,这会导致问题,因为 patch(1) 会尝试应用在变更日志中找到的 diff。

参见:https://lore.kernel.org/lkml/[email protected]/

GERRIT_CHANGE_ID

为了被 gerrit 选中,提交消息的页脚可能包含一个类似这样的 Change-Id

Change-Id: Ic8aaa0728a43936cd4c6e1ed590e01ba8f0fbf5b
Signed-off-by: A. U. Thor <[email protected]>

Change-Id 行必须在提交前删除。

GIT_COMMIT_ID

引用提交 ID 的正确方法是:commit <sha1 的 12 个以上字符> (“<标题行>”)

一个例子可能是

Commit e21d2170f36602ae2708 ("video: remove unnecessary
platform_set_drvdata()") removed the unnecessary
platform_set_drvdata(), but left the variable "dev" unused,
delete it.

参见:https://linuxkernel.org.cn/doc/html/latest/process/submitting-patches.html#describe-your-changes

BAD_FIXES_TAG

Fixes: 标签格式不正确或不符合社区惯例。如果标签被分成多行(例如,在启用了自动换行的电子邮件程序中粘贴时),则可能会发生这种情况。

参见:https://linuxkernel.org.cn/doc/html/latest/process/submitting-patches.html#describe-your-changes

比较样式

ASSIGN_IN_IF

不要在 if 条件中使用赋值。例如

if ((foo = bar(...)) < BAZ) {

应该写成

foo = bar(...);
if (foo < BAZ) {
BOOL_COMPARISON

将 A 与 true 和 false 进行比较最好写成 A 和 !A。

参见:https://lore.kernel.org/lkml/1365563834.27174.12.camel@joe-AO722/

COMPARISON_TO_NULL

形式为 (foo == NULL) 或 (foo != NULL) 的 NULL 比较最好写成 (!foo) 和 (foo)。

CONSTANT_COMPARISON

应避免测试左侧为常量或大写标识符的比较。

缩进和换行符

CODE_INDENT

代码缩进应使用制表符而不是空格。在注释、文档和 Kconfig 之外,空格永远不用作缩进。

参见:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#indentation

DEEP_INDENTATION

使用 6 个或更多制表符的缩进通常表示代码缩进过多。

建议重构 if/else/for/do/while/switch 语句的过度缩进。

参见:https://lore.kernel.org/lkml/1328311239.21255.24.camel@joe2Laptop/

SWITCH_CASE_INDENT_LEVEL

switch 应与 case 的缩进级别相同。例如

switch (suffix) {
case 'G':
case 'g':
        mem <<= 30;
        break;
case 'M':
case 'm':
        mem <<= 20;
        break;
case 'K':
case 'k':
        mem <<= 10;
        fallthrough;
default:
        break;
}

参见:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#indentation

LONG_LINE

该行已超出指定的最大长度。要使用不同的最大行长度,可以在调用 checkpatch 时添加 --max-line-length=n 选项。

以前,默认行长度为 80 列。Commit bdc48fa11e46 (“checkpatch/coding-style: deprecate 80-column warning”) 将限制增加到 100 列。这也不是一个硬性限制,并且最好尽可能保持在 80 列内。

参见:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#breaking-long-lines-and-strings

LONG_LINE_STRING

字符串在最大行长度之前开始,但超出了最大行长度。要使用不同的最大行长度,可以在调用 checkpatch 时添加 --max-line-length=n 选项。

参见:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#breaking-long-lines-and-strings

LONG_LINE_COMMENT

注释在最大行长度之前开始,但超出了最大行长度。要使用不同的最大行长度,可以在调用 checkpatch 时添加 --max-line-length=n 选项。

参见:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#breaking-long-lines-and-strings

SPLIT_STRING

在用户空间中显示为消息并且可以被 grep 的带引号的字符串,不应跨多行拆分。

参见:https://lore.kernel.org/lkml/20120203052727.GA15035@leaf/

MULTILINE_DEREFERENCE

像这样的单个取消引用标识符跨越多行

struct_identifier->member[index].
member = <foo>;

通常难以理解。它很容易导致拼写错误,从而使代码容易出现错误。

如果修复多行解引用导致违反 80 列的限制,那么要么以更简单的方式重写代码,要么如果解引用标识符的起始部分相同并在多个地方使用,则将其存储在一个临时变量中,并在所有地方仅使用该临时变量。例如,如果有两个解引用标识符

member1->member2->member3.foo1;
member1->member2->member3.foo2;

那么将 member1->member2->member3 部分存储在一个临时变量中。这不仅有助于避免违反 80 列的限制,还可以通过删除不必要的解引用来减少程序大小。

但是,如果以上方法都无效,则忽略 80 列的限制,因为在一行上读取解引用标识符要容易得多。

尾随语句

尾随语句(例如,在任何条件语句之后)应位于下一行。例如,像这样的语句:

if (x == y) break;

应为

if (x == y)
        break;

宏、属性和符号

ARRAY_SIZE

为了查找数组中的元素数量,应该优先使用 ARRAY_SIZE(foo) 宏,而不是 sizeof(foo)/sizeof(foo[0])。

该宏定义在 include/linux/kernel.h 中

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
避免使用 EXTERNS

函数原型不需要在 .h 文件中声明为 extern。编译器会假定它是 extern,因此没有必要声明。

避免使用 L 前缀

应避免使用以 .L 为前缀的局部符号名称,因为这对于汇编器具有特殊含义;符号条目不会被发出到符号表中。这可能会阻止 objtool 生成正确的展开信息。

仍然可以使用具有 STB_LOCAL 绑定的符号,并且在函数中通常仍然可以使用以 .L 为前缀的局部符号名称,但是不应使用以 .L 为前缀的局部符号名称来通过 SYM_CODE_START_LOCAL/SYM_CODE_END 来表示代码区域的开始或结束。

BIT_MACRO

像 1 << 这样的定义可以使用 BIT(digit)。BIT() 宏通过 include/linux/bits.h 定义。

#define BIT(nr)         (1UL << (nr))
CONST_READ_MOSTLY

当一个变量用 __read_mostly 注解标记时,这是给编译器的信号,表明对该变量的访问主要为读取,很少(但不是从不)写入。

const __read_mostly 没有意义,因为 const 数据已经是只读的。因此,应删除 __read_mostly 注解。

DATE_TIME

通常,希望使用同一组工具构建相同的源代码是可重现的,即输出始终完全相同。

内核使用 __DATE____TIME__ 宏,并且如果使用了这些宏,会启用警告,因为它们会导致非确定性构建。

请参阅:https://linuxkernel.org.cn/doc/html/latest/kbuild/reproducible-builds.html#timestamps

DEFINE_ARCH_HAS

ARCH_HAS_xyz 和 ARCH_HAVE_xyz 模式是错误的。

对于较大的概念性功能,请改用 Kconfig 符号。对于较小的事情,如果我们有兼容的后备函数,但希望体系结构能够用优化的函数覆盖它们,我们应该使用弱函数(适用于某些情况),或者保护它们的符号应该是我们使用的相同符号。

请参阅:https://lore.kernel.org/lkml/CA+55aFycQ9XJvEOsiM3txHL5bjUc8CeKWJNR_H+MiicaddB42Q@mail.gmail.com/

DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON

do {} while(0) 宏不应有尾随分号。

INIT_ATTRIBUTE

const init 定义应使用 __initconst 而不是 __initdata。

类似地,没有 const 的 init 定义需要单独使用 const。

INLINE_LOCATION

inline 关键字应位于存储类和类型之间。

例如,以下代码段

inline static int example_function(void)
{
        ...
}

应为

static inline int example_function(void)
{
        ...
}
MISPLACED_INIT

可以在变量上使用段标记,而 gcc 不理解(或者至少不是开发人员打算的方式)。

static struct __initdata samsung_pll_clock exynos4_plls[nr_plls] = {

不会将 exynos4_plls 放入 .initdata 段中。__initdata 标记几乎可以在该行的任何位置,除了紧跟在“struct”之后。首选位置是在“=”符号之前(如果有的话),否则是在尾随的“;”之前。

请参阅:https://lore.kernel.org/lkml/1377655732.3619.19.camel@joe-AO722/

MULTISTATEMENT_MACRO_USE_DO_WHILE

具有多个语句的宏应包含在 do - while 代码块中。以 if 开头的宏也应如此,以避免逻辑缺陷。

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

请参阅:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#macros-enums-and-rtl

PREFER_FALLTHROUGH

使用 fallthrough; 伪关键字代替 /* fallthrough */ 这样的注释。

尾随分号

宏定义不应以分号结尾。宏调用样式应与函数调用保持一致。这可以防止任何意外的代码路径。

#define MAC do_something;

如果此宏在 if else 语句中使用,例如

if (some_condition)
        MAC;

else
        do_something;

那么会出现编译错误,因为当宏展开时,会出现两个尾随分号,因此 else 分支被孤立。

请参阅:https://lore.kernel.org/lkml/1399671106.2912.21.camel@joe-AO725/

MACRO_ARG_UNUSED

如果类似函数的宏没有使用参数,则可能会导致构建警告。我们提倡使用静态内联函数来替换此类宏。例如,对于如下的宏

#define test(a) do { } while (0)

会出现如下警告

WARNING: Argument 'a' is not used in function-like macro.

请参阅:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#macros-enums-and-rtl

SINGLE_STATEMENT_DO_WHILE_MACRO

对于多语句宏,有必要使用 do-while 循环来避免不可预测的代码路径。do-while 循环有助于将多个语句分组到一个语句中,以便可以将类似函数的宏仅用作函数。

但是对于单语句宏,没有必要使用 do-while 循环。虽然代码在语法上是正确的,但是使用 do-while 循环是多余的。因此,对于单语句宏,请删除 do-while 循环。

WEAK_DECLARATION

使用像 __attribute__((weak)) 或 __weak 这样的弱声明可能会导致意外的链接缺陷。避免使用它们。

函数和变量

CAMELCASE

避免使用 CamelCase 标识符。

请参阅:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#naming

CONST_CONST

通常,const <type> const * 应该写成 const <type> * const

CONST_STRUCT

通常,使用 const 是一个好主意。Checkpatch 会读取一个常用结构体的列表,这些结构体始终或几乎始终是常量。

可以从 scripts/const_structs.checkpatch 中查看现有的结构体列表。

请参阅:https://lore.kernel.org/lkml/alpine.DEB.2.10.1608281509480.3321@hadrien/

EMBEDDED_FUNCTION_NAME

嵌入式函数名称不太适合使用,因为重构可能会导致函数重命名。最好使用“%s”、__func__ 来嵌入函数名称。

请注意,这不适用于 -f (--file) checkpatch 选项,因为它取决于提供函数名称的补丁上下文。

FUNCTION_ARGUMENTS

发出此警告的原因是以下任一原因:

  1. 函数声明的参数不遵循标识符名称。例如

    void foo
    (int bar, int baz)
    

    应将其更正为

    void foo(int bar, int baz)
    
  2. 函数定义的一些参数没有标识符名称。例如

    void foo(int)
    

    所有参数都应具有标识符名称。

FUNCTION_WITHOUT_ARGS

没有参数的函数声明,例如

int foo()

应为

int foo(void)
GLOBAL_INITIALISERS

全局变量不应显式初始化为 0(或 NULL、false 等)。您的编译器(或者更确切地说是您的加载程序,它负责将相关部分归零)会自动为您执行此操作。

INITIALISED_STATIC

静态变量不应显式初始化为零。您的编译器(或者更确切地说是您的加载程序)会自动为您执行此操作。

MULTIPLE_ASSIGNMENTS

在一行上进行多次赋值会使代码变得不必要地复杂。因此,在一行上仅为单个变量赋值,这会使代码更具可读性,并有助于避免拼写错误。

RETURN_PARENTHESES

return 不是函数,因此不需要括号

return (bar);

可以简单地写成

return bar;

权限

DEVICE_ATTR_PERMS

DEVICE_ATTR 中使用的权限不常见。通常只使用三种权限 - 0644 (RW)、0444 (RO) 和 0200 (WO)。

请参阅:https://linuxkernel.org.cn/doc/html/latest/filesystems/sysfs.html#attributes

EXECUTE_PERMISSIONS

源文件没有理由是可执行的。可以安全地删除可执行位。

EXPORTED_WORLD_WRITABLE

导出全局可写的 sysfs/debugfs 文件通常是不好的。随意执行此操作可能会引入严重的安全性错误。过去,一些 debugfs 漏洞似乎允许任何本地用户将任意值写入设备寄存器 - 这是一种不太可能产生好处的情况。

请参阅:https://lore.kernel.org/linux-arm-kernel/[email protected]/

NON_OCTAL_PERMISSIONS

权限位应使用 4 位八进制权限(例如 0700 或 0444)。避免使用任何其他进制,例如十进制。

SYMBOLIC_PERMS

八进制形式的权限位比它们的符号对应项更具可读性且更易于理解,因为许多命令行工具都使用此表示法。经验丰富的内核开发人员已经使用了这些传统的 Unix 权限位数十年,因此他们发现理解八进制表示法比符号宏更容易。例如,读取 S_IWUSR|S_IRUGO 比读取 0644 更难,这会模糊开发人员的意图,而不是阐明它。

请参阅:https://lore.kernel.org/lkml/CA+55aFw5v23T-zvDZp-MmD_EYxF8WbafwwB59934FV7g21uMGQ@mail.gmail.com/

空格和括号

ASSIGNMENT_CONTINUATIONS

赋值运算符不应写在一行的开头,而应遵循上一行的操作数。

BRACES

大括号的放置样式不正确。首选的方式是将左大括号放在行尾,并将右大括号放在行首。

if (x is true) {
        we do y
}

这适用于所有非功能性代码块。但是,有一种特殊情况,即函数:它们的左大括号在下一行的开头,因此

int function(int x)
{
        body of function
}

请参阅:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#placing-braces-and-spaces

BRACKET_SPACE

禁止在左括号 '[' 前使用空格。有一些例外情况

  1. 左侧带有类型

    int [] a;
    
  2. 在切片初始化器的行首

    [0...10] = 5,
    
  3. 在花括号内

    = { [0...10] = 5 }
    
CONCATENATED_STRING

串联的元素之间应该有空格。例如

printk(KERN_INFO"bar");

应为

printk(KERN_INFO "bar");
ELSE_AFTER_BRACE

else { 应在同一行上的右大括号 } 之后。

请参阅:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#placing-braces-and-spaces

LINE_SPACING

当使用多个空行时,由于编辑器窗口可以显示的行数有限,垂直空间会被浪费。

参考:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#spaces

OPEN_BRACE

左大括号应该在函数定义的下一行。对于任何非函数块,它应该与最后一个构造在同一行。

请参阅:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#placing-braces-and-spaces

POINTER_LOCATION

当使用指针数据或返回指针类型的函数时,首选的 * 用法是紧邻数据名或函数名,而不是紧邻类型名。示例:

char *linux_banner;
unsigned long long memparse(char *ptr, char **retptr);
char *match_strdup(substring_t *s);

参考:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#spaces

SPACING

内核源代码中使用的空白风格在内核文档中有描述。

参考:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#spaces

TRAILING_WHITESPACE

应该始终删除尾随空格。一些编辑器会高亮显示尾随空格,并在编辑文件时造成视觉干扰。

参考:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#spaces

UNNECESSARY_PARENTHESES

以下情况下不需要括号:

  1. 函数指针的使用

    (foo->bar)();
    

可以是

foo->bar();
  1. if 中的比较

    if ((foo->bar) && (foo->baz))
    if ((foo == bar))
    

可以是

if (foo->bar && foo->baz)
if (foo == bar)
  1. addressof/dereference 单个左值

    &(foo->bar)
    *(foo->bar)
    

可以是

&foo->bar
*foo->bar
WHILE_AFTER_BRACE

while 应该与右括号在同一行

do {
        ...
} while(something);

请参阅:https://linuxkernel.org.cn/doc/html/latest/process/coding-style.html#placing-braces-and-spaces

其他

CONFIG_DESCRIPTION

Kconfig 符号应该有一个完整描述它的帮助文本。

CORRUPTED_PATCH

补丁似乎已损坏或行已换行。请在将其发送给维护者之前重新生成补丁文件。

CVS_KEYWORD

由于 Linux 已迁移到 git,CVS 标记不再使用。因此,不应添加 CVS 风格的关键字($Id$, $Revision$, $Log$)。

DEFAULT_NO_BREAK

switch 的 default 情况有时写为 “default:;”。这会导致在 default 下面添加的新情况出现缺陷。

应该在空的 default 语句后添加“break;”以避免不必要的 fallthrough。

DOS_LINE_ENDINGS

对于 DOS 格式的补丁,每行末尾都有额外的 ^M 符号。这些应该删除。

DT_SCHEMA_BINDING_PATCH

DT bindings 已迁移到基于 json-schema 的格式,而不是自由形式的文本。

参考:https://linuxkernel.org.cn/doc/html/latest/devicetree/bindings/writing-schema.html

DT_SPLIT_BINDING_PATCH

Devicetree bindings 应该有自己的补丁。这是因为 bindings 在逻辑上独立于驱动程序实现,它们有不同的维护者(即使它们通常是通过同一棵树应用),并且它使得在用 git-filter-branch 创建的 DT only 树中历史记录更清晰。

参考:https://linuxkernel.org.cn/doc/html/latest/devicetree/bindings/submitting-patches.html#i-for-patch-submitters

EMBEDDED_FILENAME

将完整的文件名路径嵌入到文件中并不是特别有用,因为路径通常会移动,并且会变得不正确。

FILE_PATH_CHANGES

每当添加、移动或删除文件时,MAINTAINERS 文件模式可能会不同步或过时。

因此,在这些情况下可能需要更新 MAINTAINERS 文件。

MEMSET

memset 的使用似乎不正确。这可能是由于参数顺序错误造成的。请重新检查用法。

NOT_UNIFIED_DIFF

补丁文件似乎不是 unified-diff 格式。请在将其发送给维护者之前重新生成补丁文件。

PRINTF_0XDECIMAL

用十进制输出前缀 0x 是有缺陷的,应该纠正。

SPDX_LICENSE_TAG

源文件缺少或具有不正确的 SPDX 标识符标记。Linux 内核要求所有源文件中都有精确的 SPDX 标识符,并且在内核文档中有详细的记录。

参考:https://linuxkernel.org.cn/doc/html/latest/process/license-rules.html

TYPO_SPELLING

某些单词可能拼写错误。请考虑复查它们。