code:
Chinese translated version of Documentation/driver-api/media/index.rst

If you have any comment or update to the content, please contact the
original document maintainer directly.  However, if you have a problem
communicating in English you can also ask the Chinese maintainer for
help.  Contact the Chinese maintainer if this translation is outdated
or if there is a problem with the translation.

Maintainer: Mauro Carvalho Chehab <mchehab@kernel.org>
Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
---------------------------------------------------------------------
Documentation/driver-api/media/index.rst 的中文翻译

如果想评论或更新本文的内容，请直接联系原文档的维护者。如果你使用英文
交流有困难的话，也可以向中文版维护者求助。如果本翻译更新不及时或者翻
译存在问题，请联系中文版维护者。
英文版维护者： Mauro Carvalho Chehab <mchehab@kernel.org>
中文版维护者： 傅炜 Fu Wei <tekkamanninja@gmail.com>
中文版翻译者： 傅炜 Fu Wei <tekkamanninja@gmail.com>
中文版校译者： 傅炜 Fu Wei <tekkamanninja@gmail.com>

以下为正文
---------------------------------------------------------------------
V4L2 驱动框架概览
==============

本文档描述 V4L2 框架所提供的各种结构和它们之间的关系。

介绍
----

大部分现代 V4L2 设备由多个 IC 组成，在 /dev 下导出多个设备节点，
并同时创建非 V4L2 设备（如 DVB、ALSA、FB、I2C 和红外输入设备）。
由于这种硬件的复杂性，V4L2 驱动也变得非常复杂。

尤其是 V4L2 必须支持 IC 实现音视频的多路复用和编解码，这就更增加了其
复杂性。通常这些 IC 通过一个或多个 I2C 总线连接到主桥驱动器，但也可
使用其他总线。这些设备称为“子设备”。

长期以来，这个框架仅限于通过 video_device 结构体创建 V4L 设备节点，
并使用 video_buf 处理视频缓冲（注：本文不讨论 video_buf 框架）。

这意味着所有驱动必须自己设置设备实例并连接到子设备。其中一部分要正确地
完成是比较复杂的，使得许多驱动都没有正确地实现。

由于框架的缺失，有很多通用代码都不可重复利用。

因此，这个框架构建所有驱动都需要的基本结构块，而统一的框架将使通用代码
创建成实用函数并在所有驱动中共享变得更加容易。

驱动结构
-------

所有 V4L2 驱动都有如下结构：

1) 每个设备实例的结构体--包含其设备状态。

2) 初始化和控制子设备的方法（如果有）。

3) 创建 V4L2 设备节点 (/dev/videoX、/dev/vbiX 和 /dev/radioX)
   并跟踪设备节点的特定数据。

4) 特定文件句柄结构体--包含每个文件句柄的数据。

5) 视频缓冲处理。

以下是它们的初略关系图：

    device instances（设备实例）
      |
      +-sub-device instances（子设备实例）
      |
      \-V4L2 device nodes（V4L2 设备节点）
	  |
	  \-filehandle instances（文件句柄实例）

框架结构
-------

该框架非常类似驱动结构：它有一个 v4l2_device 结构用于保存设备
实例的数据；一个 v4l2_subdev 结构体代表子设备实例；video_device
结构体保存 V4L2 设备节点的数据；将来 v4l2_fh 结构体将跟踪文件句柄
实例（暂未尚未实现）。

V4L2 框架也可与媒体框架整合（可选的）。如果驱动设置了 v4l2_device
结构体的 mdev 域，子设备和视频节点的入口将自动出现在媒体框架中。

v4l2_device 结构体
----------------

每个设备实例都通过 v4l2_device (v4l2-device.h)结构体来表示。
简单设备可以仅分配这个结构体，但在大多数情况下，都会将这个结构体
嵌入到一个更大的结构体中。

你必须注册这个设备实例：

	v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);

注册操作将会初始化 v4l2_device 结构体。如果 dev->driver_data 域
为 NULL，就将其指向 v4l2_dev。

需要与媒体框架整合的驱动必须手动设置 dev->driver_data，指向包含
v4l2_device 结构体实例的驱动特定设备结构体。这可以在注册 V4L2 设备
实例前通过 dev_set_drvdata() 函数完成。同时必须设置 v4l2_device
结构体的 mdev 域，指向适当的初始化并注册过的 media_device 实例。

如果 v4l2_dev->name 为空，则它将被设置为从 dev 中衍生出的值（为了
更加精确，形式为驱动名后跟 bus_id）。如果你在调用 v4l2_device_register
前已经设置好了，则不会被修改。如果 dev 为 NULL，则你*必须*在调用
v4l2_device_register 前设置 v4l2_dev->name。

你可以基于驱动名和驱动的全局 atomic_t 类型的实例编号，通过
v4l2_device_set_name() 设置 name。这样会生成类似 ivtv0、ivtv1 等
名字。若驱动名以数字结尾，则会在编号和驱动名间插入一个破折号，如：
cx18-0、cx18-1 等。此函数返回实例编号。

第一个 “dev” 参数通常是一个指向 pci_dev、usb_interface 或
platform_device 的指针。很少使其为 NULL，除非是一个ISA设备或者
当一个设备创建了多个 PCI 设备，使得 v4l2_dev 无法与一个特定的父设备
关联。

你也可以提供一个 notify() 回调，使子设备可以调用它实现事件通知。
但这个设置与子设备相关。子设备支持的任何通知必须在
include/media/<subdevice>.h 中定义一个消息头。

注销 v4l2_device 使用如下函数：

	v4l2_device_unregister(struct v4l2_device *v4l2_dev);

如果 dev->driver_data 域指向 v4l2_dev，将会被重置为 NULL。注销同时
会自动从设备中注销所有子设备。

如果你有一个热插拔设备（如USB设备），则当断开发生时，父设备将无效。
由于 v4l2_device 有一个指向父设备的指针必须被清除，同时标志父设备
已消失，所以必须调用以下函数：

	v4l2_device_disconnect(struct v4l2_device *v4l2_dev);

这个函数并*不*注销子设备，因此你依然要调用 v4l2_device_unregister()
函数。如果你的驱动器并非热插拔的，就没有必要调用 v4l2_device_disconnect()。

有时你需要遍历所有被特定驱动注册的设备。这通常发生在多个设备驱动使用
同一个硬件的情况下。如：ivtvfb 驱动是一个使用 ivtv 硬件的帧缓冲驱动，
同时 alsa 驱动也使用此硬件。

你可以使用如下例程遍历所有注册的设备：

static int callback(struct device *dev, void *p)
{
	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);

	/* 测试这个设备是否已经初始化 */
	if (v4l2_dev == NULL)
		return 0;
	...
	return 0;
}

int iterate(void *p)
{
	struct device_driver *drv;
	int err;

	/* 在PCI 总线上查找ivtv驱动。
	   pci_bus_type是全局的. 对于USB总线使用usb_bus_type。 */
	drv = driver_find("ivtv", &pci_bus_type);
	/* 遍历所有的ivtv设备实例 */
	err = driver_for_each_device(drv, NULL, p, callback);
	put_driver(drv);
	return err;
}

有时你需要一个设备实例的运行计数。这个通常用于映射一个设备实例到一个
模块选择数组的索引。

推荐方法如下：

static atomic_t drv_instance = ATOMIC_INIT(0);

static int drv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
{
	...
	state->instance = atomic_inc_return(&drv_instance) - 1;
}

如果你有多个设备节点，对于热插拔设备，知道何时注销 v4l2_device 结构体
就比较困难。为此 v4l2_device 有引用计数支持。当调用 video_register_device
时增加引用计数，而设备节点释放时减小引用计数。当引用计数为零，则
v4l2_device 的release() 回调将被执行。你就可以在此时做最后的清理工作。

如果创建了其他设备节点（比如 ALSA），则你可以通过以下函数手动增减
引用计数：

void v4l2_device_get(struct v4l2_device *v4l2_dev);

或:

int v4l2_device_put(struct v4l2_device *v4l2_dev);

由于引用技术初始化为 1 ，你也需要在 disconnect() 回调（对于 USB 设备）中
调用 v4l2_device_put，或者 remove() 回调（例如对于 PCI 设备），否则
引用计数将永远不会为 0 。

v4l2_subdev结构体
------------------

许多驱动需要与子设备通信。这些设备可以完成各种任务，但通常他们负责
音视频复用和编解码。如网络摄像头的子设备通常是传感器和摄像头控制器。

这些一般为 I2C 接口设备，但并不一定都是。为了给驱动提供调用子设备的
统一接口，v4l2_subdev 结构体（v4l2-subdev.h）产生了。

每个子设备驱动都必须有一个 v4l2_subdev 结构体。这个结构体可以单独
代表一个简单的子设备，也可以嵌入到一个更大的结构体中，与更多设备状态
信息保存在一起。通常有一个下级设备结构体（比如：i2c_client）包含了
内核创建的设备数据。建议使用 v4l2_set_subdevdata() 将这个结构体的
指针保存在 v4l2_subdev 的私有数据域(dev_priv)中。这使得通过 v4l2_subdev
找到实际的低层总线特定设备数据变得容易。

你同时需要一个从低层结构体获取 v4l2_subdev 指针的方法。对于常用的
i2c_client 结构体，i2c_set_clientdata() 函数可用于保存一个 v4l2_subdev
指针；对于其他总线你可能需要使用其他相关函数。

桥驱动中也应保存每个子设备的私有数据，比如一个指向特定桥的各设备私有
数据的指针。为此 v4l2_subdev 结构体提供主机私有数据域(host_priv)，
并可通过 v4l2_get_subdev_hostdata() 和 v4l2_set_subdev_hostdata()
访问。

从总线桥驱动的视角，驱动加载子设备模块并以某种方式获得 v4l2_subdev
结构体指针。对于 i2c 总线设备相对简单：调用 i2c_get_clientdata()。
对于其他总线也需要做类似的操作。针对 I2C 总线上的子设备辅助函数帮你
完成了大部分复杂的工作。

每个 v4l2_subdev 都包含子设备驱动需要实现的函数指针（如果对此设备
不适用，可为NULL）。由于子设备可完成许多不同的工作，而在一个庞大的
函数指针结构体中通常仅有少数有用的函数实现其功能肯定不合适。所以，
函数指针根据其实现的功能被分类，每一类都有自己的函数指针结构体。

顶层函数指针结构体包含了指向各类函数指针结构体的指针，如果子设备驱动
不支持该类函数中的任何一个功能，则指向该类结构体的指针为NULL。

这些结构体定义如下：

struct v4l2_subdev_core_ops {
	int (*log_status)(struct v4l2_subdev *sd);
	int (*init)(struct v4l2_subdev *sd, u32 val);
	...
};

struct v4l2_subdev_tuner_ops {
	...
};

struct v4l2_subdev_audio_ops {
	...
};

struct v4l2_subdev_video_ops {
	...
};

struct v4l2_subdev_pad_ops {
	...
};

struct v4l2_subdev_ops {
	const struct v4l2_subdev_core_ops  *core;
	const struct v4l2_subdev_tuner_ops *tuner;
	const struct v4l2_subdev_audio_ops *audio;
	const struct v4l2_subdev_video_ops *video;
	const struct v4l2_subdev_pad_ops *video;
};

其中 core（核心）函数集通常可用于所有子设备，其他类别的实现依赖于
子设备。如视频设备可能不支持音频操作函数，反之亦然。

这样的设置在限制了函数指针数量的同时，还使增加新的操作函数和分类
变得较为容易。

子设备驱动可使用如下函数初始化 v4l2_subdev 结构体：

	v4l2_subdev_init(sd, &ops);

然后，你必须用一个唯一的名字初始化 subdev->name，并初始化模块的
owner 域。若使用 i2c 辅助函数，这些都会帮你处理好。

若需同媒体框架整合，你必须调用 media_entity_pads_init() 初始化 v4l2_subdev
结构体中的 media_entity 结构体（entity 域）：

	struct media_pad *pads = &my_sd->pads;
	int err;

	err = media_entity_pads_init(&sd->entity, npads, pads);

pads 数组必须预先初始化。无须手动设置 media_entity 的 type 和
name 域，但如有必要，revision 域必须初始化。

当（任何）子设备节点被打开/关闭，对 entity 的引用将被自动获取/释放。

在子设备被注销之后，不要忘记清理 media_entity 结构体：

	media_entity_cleanup(&sd->entity);

如果子设备驱动趋向于处理视频并整合进了媒体框架，必须使用 v4l2_subdev_pad_ops
替代 v4l2_subdev_video_ops 实现格式相关的功能。

这种情况下，子设备驱动应该设置 link_validate 域，以提供它自身的链接
验证函数。链接验证函数应对管道（两端链接的都是 V4L2 子设备）中的每个
链接调用。驱动还要负责验证子设备和视频节点间格式配置的正确性。

如果 link_validate 操作没有设置，默认的 v4l2_subdev_link_validate_default()
函数将会被调用。这个函数保证宽、高和媒体总线像素格式在链接的收发两端
都一致。子设备驱动除了它们自己的检测外，也可以自由使用这个函数以执行
上面提到的检查。

设备（桥）驱动程序必须向 v4l2_device 注册 v4l2_subdev：

	int err = v4l2_device_register_subdev(v4l2_dev, sd);

如果子设备模块在它注册前消失，这个操作可能失败。在这个函数成功返回后，
subdev->dev 域就指向了 v4l2_device。

如果 v4l2_device 父设备的 mdev 域为非 NULL 值，则子设备实体将被自动
注册为媒体设备。

注销子设备则可用如下函数：

	v4l2_device_unregister_subdev(sd);

此后，子设备模块就可卸载，且 sd->dev == NULL。

注册之设备后，可通过以下方式直接调用其操作函数：

	err = sd->ops->core->g_std(sd, &norm);

但使用如下宏会比较容易且合适：

	err = v4l2_subdev_call(sd, core, g_std, &norm);

这个宏将会做 NULL 指针检查，如果 subdev 为 NULL，则返回-ENODEV；如果
subdev->core 或 subdev->core->g_std 为 NULL，则返回 -ENOIOCTLCMD；
否则将返回 subdev->ops->core->g_std ops 调用的实际结果。

有时也可能同时调用所有或一系列子设备的某个操作函数：

	v4l2_device_call_all(v4l2_dev, 0, core, g_std, &norm);

任何不支持此操作的子设备都会被跳过，并忽略错误返回值。但如果你需要
检查出错码，则可使用如下函数：

	err = v4l2_device_call_until_err(v4l2_dev, 0, core, g_std, &norm);

除 -ENOIOCTLCMD 外的任何错误都会跳出循环并返回错误值。如果（除 -ENOIOCTLCMD
外）没有错误发生，则返回 0。

对于以上两个函数的第二个参数为组 ID。如果为 0，则所有子设备都会执行
这个操作。如果为非 0 值，则只有那些组 ID 匹配的子设备才会执行此操作。
在桥驱动注册一个子设备前，可以设置 sd->grp_id 为任何期望值（默认值为
0）。这个值属于桥驱动，且子设备驱动将不会修改和使用它。

组 ID 赋予了桥驱动更多对于如何调用回调的控制。例如，电路板上有多个
音频芯片，每个都有改变音量的能力。但当用户想要改变音量的时候，通常
只有一个会被实际使用。你可以对这样的子设备设置组 ID 为（例如 AUDIO_CONTROLLER）
并在调用 v4l2_device_call_all() 时指定它为组 ID 值。这就保证了只有
需要的子设备才会执行这个回调。

如果子设备需要通知它的 v4l2_device 父设备一个事件，可以调用
v4l2_subdev_notify(sd, notification, arg)。这个宏检查是否有一个
notify() 回调被注册，如果没有，返回 -ENODEV。否则返回 notify() 调用
结果。

使用 v4l2_subdev 的好处在于它是一个通用结构体，且不包含任何底层硬件
信息。所有驱动可以包含多个 I2C 总线的子设备，但也有子设备是通过 GPIO
控制。这个区别仅在配置设备时有关系，一旦子设备注册完成，对于 v4l2
子系统来说就完全透明了。

V4L2 子设备用户空间API
--------------------

除了通过 v4l2_subdev_ops 结构导出的内核 API，V4L2 子设备也可以直接
通过用户空间应用程序来控制。

可以在 /dev 中创建名为 v4l-subdevX 设备节点，以通过其直接访问子设备。
如果子设备支持用户空间直接配置，必须在注册前设置 V4L2_SUBDEV_FL_HAS_DEVNODE
标志。

注册子设备之后， v4l2_device 驱动会通过调用 v4l2_device_register_subdev_nodes()
函数为所有已注册并设置了 V4L2_SUBDEV_FL_HAS_DEVNODE 的子设备创建
设备节点。这些设备节点会在子设备注销时自动删除。

这些设备节点处理 V4L2 API 的一个子集。

VIDIOC_QUERYCTRL
VIDIOC_QUERYMENU
VIDIOC_G_CTRL
VIDIOC_S_CTRL
VIDIOC_G_EXT_CTRLS
VIDIOC_S_EXT_CTRLS
VIDIOC_TRY_EXT_CTRLS

	这些 ioctls 控制与 V4L2 中定义的一致。他们行为相同，唯一的
	不同是他们只处理子设备的控制实现。根据驱动程序，这些控制也
	可以通过一个（或多个） V4L2 设备节点访问。

VIDIOC_DQEVENT
VIDIOC_SUBSCRIBE_EVENT
VIDIOC_UNSUBSCRIBE_EVENT

	这些  ioctls 事件与 V4L2 中定义的一致。他们行为相同，唯一的
	不同是他们只处理子设备产生的事件。根据驱动程序，这些事件也
	可以通过一个（或多个） V4L2 设备节点上报。

	要使用事件通知的子设备驱动，在注册子设备前必须在 v4l2_subdev::flags
	中设置 V4L2_SUBDEV_USES_EVENTS 并在 v4l2_subdev::nevents
	中初始化事件队列深度。注册完成后，事件会在 v4l2_subdev::devnode
	设备节点中像通常一样被排队。

	为正确支持事件机制，poll() 文件操作也应被实现。

私有 ioctls

	不在以上列表中的所有 ioctls 会通过 core::ioctl 操作直接传递
	给子设备驱动。

I2C 子设备驱动
-------------

由于这些驱动很常见，所以内特提供了特定的辅助函数(v4l2-common.h)让这些
设备的使用更加容易。

添加 v4l2_subdev 支持的推荐方法是让 I2C 驱动将 v4l2_subdev 结构体
嵌入到为每个 I2C 设备实例创建的状态结构体中。而最简单的设备没有状态
结构体，此时可以直接创建一个 v4l2_subdev 结构体。

一个典型的状态结构体如下所示（‘chipname’用芯片名代替）：

struct chipname_state {
	struct v4l2_subdev sd;
	...  /* 附加的状态域*/
};

初始化 v4l2_subdev 结构体的方法如下：

	v4l2_i2c_subdev_init(&state->sd, client, subdev_ops);

这个函数将填充 v4l2_subdev 结构体中的所有域，并保证 v4l2_subdev 和
i2c_client 都指向彼此。

同时，你也应该为从 v4l2_subdev 指针找到 chipname_state 结构体指针
添加一个辅助内联函数。

static inline struct chipname_state *to_state(struct v4l2_subdev *sd)
{
	return container_of(sd, struct chipname_state, sd);
}

使用以下函数可以通过 v4l2_subdev 结构体指针获得 i2c_client 结构体
指针：

	struct i2c_client *client = v4l2_get_subdevdata(sd);

而以下函数则相反，通过 i2c_client 结构体指针获得 v4l2_subdev 结构体
指针：

	struct v4l2_subdev *sd = i2c_get_clientdata(client);

当 remove()函数被调用前，必须保证先调用 v4l2_device_unregister_subdev(sd)。
此操作将会从桥驱动中注销子设备。即使子设备没有注册，调用此函数也是
安全的。

必须这样做的原因是：当桥驱动注销 i2c 适配器时，remove()回调函数
会被那个适配器上的 i2c 设备调用。此后，相应的 v4l2_subdev 结构体
就不存在了，所有它们必须先被注销。在 remove()回调函数中调用
v4l2_device_unregister_subdev(sd)，可以保证执行总是正确的。

桥驱动也有一些辅组函数可用：

struct v4l2_subdev *sd = v4l2_i2c_new_subdev(v4l2_dev, adapter,
	       "module_foo", "chipid", 0x36, NULL);

这个函数会加载给定的模块（如果没有模块需要加载，可以为 NULL），
并用给定的 i2c 适配器结构体指针（i2c_adapter）和 器件地址（chip/address）
作为参数调用 i2c_new_client_device()。如果一切顺利，则就在 v4l2_device
中注册了子设备。

你也可以利用 v4l2_i2c_new_subdev()的最后一个参数，传递一个可能的
I2C 地址数组，让函数自动探测。这些探测地址只有在前一个参数为 0 的
情况下使用。非零参数意味着你知道准确的 i2c 地址，所以此时无须进行
探测。

如果出错，两个函数都返回 NULL。

注意：传递给 v4l2_i2c_new_subdev()的 chipid 通常与模块名一致。
它允许你指定一个芯片的变体，比如“saa7114”或“saa7115”。一般通过
i2c 驱动自动探测。chipid 的使用是在今后需要深入了解的事情。这个与
i2c 驱动不同，较容易混淆。要知道支持哪些芯片变体，你可以查阅 i2c
驱动代码的 i2c_device_id 表，上面列出了所有可能支持的芯片。

还有两个辅助函数：

v4l2_i2c_new_subdev_cfg：这个函数添加新的 irq 和 platform_data
参数，并有‘addr’和‘probed_addrs’参数：如果 addr 非零，则被使用
（不探测变体），否则 probed_addrs 中的地址将用于自动探测。

例如：以下代码将会探测地址（0x10）：

struct v4l2_subdev *sd = v4l2_i2c_new_subdev_cfg(v4l2_dev, adapter,
	       "module_foo", "chipid", 0, NULL, 0, I2C_ADDRS(0x10));

v4l2_i2c_new_subdev_board 使用一个 i2c_board_info 结构体，将其
替代 irq、platform_data 和 add r参数传递给 i2c 驱动。

如果子设备支持 s_config 核心操作，这个操作会在子设备配置好之后以 irq 和
platform_data 为参数调用。早期的 v4l2_i2c_new_(probed_)subdev 函数
同样也会调用 s_config，但仅在 irq 为 0 且 platform_data 为 NULL 时。

video_device结构体
-----------------

在 /dev 目录下的实际设备节点根据 video_device 结构体(v4l2-dev.h)
创建。此结构体既可以动态分配也可以嵌入到一个更大的结构体中。

动态分配方法如下：

	struct video_device *vdev = video_device_alloc();

	if (vdev == NULL)
		return -ENOMEM;

	vdev->release = video_device_release;

如果将其嵌入到一个大结构体中，则必须自己实现 release()回调。

	struct video_device *vdev = &my_vdev->vdev;

	vdev->release = my_vdev_release;

release()回调必须被设置，且在最后一个 video_device 用户退出之后
被调用。

默认的 video_device_release()回调只是调用 kfree 来释放之前分配的
内存。

你应该设置这些域：

- v4l2_dev: 设置为 v4l2_device 父设备。

- name: 设置为唯一的描述性设备名。

- fops: 设置为已有的 v4l2_file_operations 结构体。

- ioctl_ops: 如果你使用v4l2_ioctl_ops 来简化 ioctl 的维护
  (强烈建议使用，且将来可能变为强制性的!)，然后设置你自己的
  v4l2_ioctl_ops 结构体.

- lock: 如果你要在驱动中实现所有的锁操作，则设为 NULL 。否则
  就要设置一个指向 struct mutex_lock 结构体的指针，这个锁将
  在 unlocked_ioctl 文件操作被调用前由内核获得，并在调用返回后
  释放。详见下一节。

- prio: 保持对优先级的跟踪。用于实现 VIDIOC_G/S_PRIORITY。如果
  设置为 NULL，则会使用 v4l2_device 中的 v4l2_prio_state 结构体。
  如果要对每个设备节点（组）实现独立的优先级，可以将其指向自己
  实现的 v4l2_prio_state 结构体。

- parent: 仅在使用 NULL 作为父设备结构体参数注册 v4l2_device 时
  设置此参数。只有在一个硬件设备包含多一个 PCI 设备，共享同一个
  v4l2_device 核心时才会发生。

  cx88 驱动就是一个例子：一个 v4l2_device 结构体核心，被一个裸的
  视频 PCI 设备(cx8800)和一个 MPEG PCI 设备(cx8802)共用。由于
  v4l2_device 无法与特定的 PCI 设备关联，所有没有设置父设备。但当
  video_device 配置后，就知道使用哪个父 PCI 设备了。

如果你使用 v4l2_ioctl_ops，则应该在 v4l2_file_operations 结构体中
设置 .unlocked_ioctl 指向 video_ioctl2。

请勿使用 .ioctl！它已被废弃，今后将消失。

某些情况下你要告诉核心：你在 v4l2_ioctl_ops 指定的某个函数应被忽略。
你可以在 video_device_register 被调用前通过以下函数标记这个 ioctls。

void v4l2_disable_ioctl(struct video_device *vdev, unsigned int cmd);

基于外部因素（例如某个板卡已被使用），在不创建新结构体的情况下，你想
要关闭 v4l2_ioctl_ops 中某个特性往往需要这个机制。

v4l2_file_operations 结构体是 file_operations 的一个子集。其主要
区别在于：因 inode 参数从未被使用，它将被忽略。

如果需要与媒体框架整合，你必须通过调用 media_entity_pads_init() 初始化
嵌入在 video_device 结构体中的 media_entity（entity 域）结构体：

	struct media_pad *pad = &my_vdev->pad;
	int err;

	err = media_entity_pads_init(&vdev->entity, 1, pad);

pads 数组必须预先初始化。没有必要手动设置 media_entity 的 type 和
name 域。

当（任何）子设备节点被打开/关闭，对 entity 的引用将被自动获取/释放。

v4l2_file_operations 与锁
--------------------------

你可以在 video_device 结构体中设置一个指向 mutex_lock 的指针。通常
这既可是一个顶层互斥锁也可为设备节点自身的互斥锁。默认情况下，此锁
用于 unlocked_ioctl，但为了使用 ioctls 你通过以下函数可禁用锁定：

	void v4l2_disable_ioctl_locking(struct video_device *vdev, unsigned int cmd);

例如: v4l2_disable_ioctl_locking(vdev, VIDIOC_DQBUF);

你必须在注册 video_device 前调用这个函数。

特别是对于 USB 驱动程序，某些命令（如设置控制）需要很长的时间，可能
需要自行为缓冲区队列的 ioctls 实现锁定。

如果你需要更细粒度的锁，你必须设置 mutex_lock 为 NULL，并完全自己实现
锁机制。

这完全由驱动开发者决定使用何种方法。然而，如果你的驱动存在长延时操作
（例如，改变 USB 摄像头的曝光时间可能需要较长时间），而你又想让用户
在等待长延时操作完成期间做其他的事，则你最好自己实现锁机制。

如果指定一个锁，则所有 ioctl 操作将在这个锁的作用下串行执行。如果你
使用 videobuf，则必须将同一个锁传递给 videobuf 队列初始化函数；如
videobuf 必须等待一帧的到达，则可临时解锁并在这之后重新上锁。如果驱动
也在代码执行期间等待，则可做同样的工作（临时解锁，再上锁）让其他进程
可以在第一个进程阻塞时访问设备节点。

在使用 videobuf2 的情况下，必须实现 wait_prepare 和 wait_finish 回调
在适当的时候解锁/加锁。进一步来说，如果你在 video_device 结构体中使用
锁，则必须在 wait_prepare 和 wait_finish 中对这个互斥锁进行解锁/加锁。

热插拔的断开实现也必须在调用 v4l2_device_disconnect 前获得锁。

video_device注册
---------------

接下来你需要注册视频设备：这会为你创建一个字符设备。

	err = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
	if (err) {
		video_device_release(vdev); /* or kfree(my_vdev); */
		return err;
	}

如果 v4l2_device 父设备的 mdev 域为非 NULL 值，视频设备实体将自动
注册为媒体设备。

注册哪种设备是根据类型（type）参数。存在以下类型：

VFL_TYPE_VIDEO: 用于视频输入/输出设备的 videoX
VFL_TYPE_VBI: 用于垂直消隐数据的 vbiX (例如，隐藏式字幕，图文电视)
VFL_TYPE_RADIO: 用于广播调谐器的 radioX

最后一个参数让你确定一个所控制设备的设备节点号数量(例如 videoX 中的 X)。
通常你可以传入-1，让 v4l2 框架自己选择第一个空闲的编号。但是有时用户
需要选择一个特定的节点号。驱动允许用户通过驱动模块参数选择一个特定的
设备节点号是很普遍的。这个编号将会传递给这个函数，且 video_register_device
将会试图选择这个设备节点号。如果这个编号被占用，下一个空闲的设备节点
编号将被选中，并向内核日志中发送一个警告信息。

另一个使用场景是当驱动创建多个设备时。这种情况下，对不同的视频设备在
编号上使用不同的范围是很有用的。例如，视频捕获设备从 0 开始，视频
输出设备从 16 开始。所以你可以使用最后一个参数来指定设备节点号最小值，
而 v4l2 框架会试图选择第一个的空闲编号（等于或大于你提供的编号）。
如果失败，则它会就选择第一个空闲的编号。

由于这种情况下，你会忽略无法选择特定设备节点号的警告，则可调用
video_register_device_no_warn() 函数避免警告信息的产生。

只要设备节点被创建，一些属性也会同时创建。在 /sys/class/video4linux
目录中你会找到这些设备。例如进入其中的 video0 目录，你会看到‘name’和
‘index’属性。‘name’属性值就是 video_device 结构体中的‘name’域。

‘index’属性值就是设备节点的索引值：每次调用 video_register_device()，
索引值都递增 1 。第一个视频设备节点总是从索引值 0 开始。

用户可以设置 udev 规则，利用索引属性生成花哨的设备名（例如：用‘mpegX’
代表 MPEG 视频捕获设备节点）。

在设备成功注册后，就可以使用这些域：

- vfl_type: 传递给 video_register_device 的设备类型。
- minor: 已指派的次设备号。
- num: 设备节点编号 (例如 videoX 中的 X)。
- index: 设备索引号。

如果注册失败，你必须调用 video_device_release() 来释放已分配的
video_device 结构体；如果 video_device 是嵌入在自己创建的结构体中，
你也必须释放它。vdev->release() 回调不会在注册失败之后被调用，
你也不应试图在注册失败后注销设备。

video_device 注销
----------------

当视频设备节点已被移除，不论是卸载驱动还是USB设备断开，你都应注销
它们：

	video_unregister_device(vdev);

这个操作将从 sysfs 中移除设备节点（导致 udev 将其从 /dev 中移除）。

video_unregister_device() 返回之后，就无法完成打开操作。尽管如此，
USB 设备的情况则不同，某些应用程序可能依然打开着其中一个已注销设备
节点。所以在注销之后，所有文件操作（当然除了 release ）也应返回错误值。

当最后一个视频设备节点的用户退出，则 vdev->release() 回调会被调用，
并且你可以做最后的清理操作。

不要忘记清理与视频设备相关的媒体入口（如果被初始化过）：

	media_entity_cleanup(&vdev->entity);

这可以在 release 回调中完成。

video_device 辅助函数
---------------------

一些有用的辅助函数如下：

- file/video_device 私有数据

你可以用以下函数在 video_device 结构体中设置/获取驱动私有数据：

void *video_get_drvdata(struct video_device *vdev);
void video_set_drvdata(struct video_device *vdev, void *data);

注意：在调用 video_register_device() 前执行 video_set_drvdata()
是安全的。

而以下函数：

struct video_device *video_devdata(struct file *file);

返回 file 结构体中拥有的的 video_device 指针。

video_drvdata 辅助函数结合了 video_get_drvdata 和 video_devdata
的功能：

void *video_drvdata(struct file *file);

你可以使用如下代码从 video_device 结构体中获取 v4l2_device 结构体
指针：

struct v4l2_device *v4l2_dev = vdev->v4l2_dev;

- 设备节点名

video_device 设备节点在内核中的名称可以通过以下函数获得

const char *video_device_node_name(struct video_device *vdev);

这个名字被用户空间工具（例如 udev）作为提示信息使用。应尽可能使用
此功能，而非访问 video_device::num 和 video_device::minor 域。

视频缓冲辅助函数
---------------

v4l2 核心 API 提供了一个处理视频缓冲的标准方法(称为“videobuf”)。
这些方法使驱动可以通过统一的方式实现 read()、mmap() 和 overlay()。
目前在设备上支持视频缓冲的方法有分散/聚集 DMA(videobuf-dma-sg)、
线性 DMA(videobuf-dma-contig)以及大多用于 USB 设备的用 vmalloc
分配的缓冲(videobuf-vmalloc)。

请参阅 Documentation/driver-api/media/v4l2-videobuf.rst，以获得更多关于 videobuf
层的使用信息。

v4l2_fh 结构体
-------------

v4l2_fh 结构体提供一个保存用于 V4L2 框架的文件句柄特定数据的简单方法。
如果 video_device 标志，新驱动
必须使用 v4l2_fh 结构体，因为它也用于实现优先级处理（VIDIOC_G/S_PRIORITY）。

v4l2_fh 的用户（位于 V4l2 框架中，并非驱动）可通过测试
video_device->flags 中的 V4L2_FL_USES_V4L2_FH 位得知驱动是否使用
v4l2_fh 作为他的 file->private_data 指针。这个位会在调用 v4l2_fh_init()
时被设置。

v4l2_fh 结构体作为驱动自身文件句柄结构体的一部分被分配，且驱动在
其打开函数中将 file->private_data 指向它。

在许多情况下，v4l2_fh 结构体会嵌入到一个更大的结构体中。这钟情况下，
应该在 open() 中调用 v4l2_fh_init+v4l2_fh_add，并在 release() 中
调用 v4l2_fh_del+v4l2_fh_exit。

驱动可以通过使用 container_of 宏提取他们自己的文件句柄结构体。例如：

struct my_fh {
	int blah;
	struct v4l2_fh fh;
};

...

int my_open(struct file *file)
{
	struct my_fh *my_fh;
	struct video_device *vfd;
	int ret;

	...

	my_fh = kzalloc(sizeof(*my_fh), GFP_KERNEL);

	...

	v4l2_fh_init(&my_fh->fh, vfd);

	...

	file->private_data = &my_fh->fh;
	v4l2_fh_add(&my_fh->fh);
	return 0;
}

int my_release(struct file *file)
{
	struct v4l2_fh *fh = file->private_data;
	struct my_fh *my_fh = container_of(fh, struct my_fh, fh);

	...
	v4l2_fh_del(&my_fh->fh);
	v4l2_fh_exit(&my_fh->fh);
	kfree(my_fh);
	return 0;
}

以下是 v4l2_fh 函数使用的简介：

void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)

  初始化文件句柄。这*必须*在驱动的 v4l2_file_operations->open()
  函数中执行。

void v4l2_fh_add(struct v4l2_fh *fh)

  添加一个 v4l2_fh 到 video_device 文件句柄列表。一旦文件句柄
  初始化完成就必须调用。

void v4l2_fh_del(struct v4l2_fh *fh)

  从 video_device() 中解除文件句柄的关联。文件句柄的退出函数也
  将被调用。

void v4l2_fh_exit(struct v4l2_fh *fh)

  清理文件句柄。在清理完 v4l2_fh 后，相关内存会被释放。

如果 v4l2_fh 不是嵌入在其他结构体中的，则可以用这些辅助函数：

int v4l2_fh_open(struct file *filp)

  分配一个 v4l2_fh 结构体空间，初始化并将其添加到 file 结构体相关的
  video_device 结构体中。

int v4l2_fh_release(struct file *filp)

  从 file 结构体相关的 video_device 结构体中删除 v4l2_fh ，清理
  v4l2_fh 并释放空间。

这两个函数可以插入到 v4l2_file_operation 的 open() 和 release()
操作中。

某些驱动需要在第一个文件句柄打开和最后一个文件句柄关闭的时候做些
工作。所以加入了两个辅助函数以检查 v4l2_fh 结构体是否是相关设备
节点打开的唯一文件句柄。

int v4l2_fh_is_singular(struct v4l2_fh *fh)

  如果此文件句柄是唯一打开的文件句柄，则返回 1 ，否则返回 0 。

int v4l2_fh_is_singular_file(struct file *filp)

  功能相同，但通过 filp->private_data 调用 v4l2_fh_is_singular。

V4L2 事件机制
-----------

V4L2 事件机制提供了一个通用的方法将事件传递到用户空间。驱动必须使用
v4l2_fh 才能支持 V4L2 事件机制。

事件通过一个类型和选择 ID 来定义。ID 对应一个 V4L2 对象，例如
一个控制 ID。如果未使用，则 ID 为 0。

当用户订阅一个事件，驱动会为此分配一些 kevent 结构体。所以每个
事件组（类型、ID）都会有自己的一套 kevent 结构体。这保证了如果
一个驱动短时间内产生了许多同类事件，不会覆盖其他类型的事件。

但如果你收到的事件数量大于同类事件 kevent 的保存数量，则最早的
事件将被丢弃，并加入新事件。

此外，v4l2_subscribed_event 结构体内部有可供驱动设置的 merge() 和
replace() 回调，这些回调会在新事件产生且没有多余空间的时候被调用。
replace() 回调让你可以将早期事件的净荷替换为新事件的净荷，将早期
净荷的相关数据合并到替换进来的新净荷中。当该类型的事件仅分配了一个
kevent 结构体时，它将被调用。merge() 回调让你可以合并最早的事件净荷
到在它之后的那个事件净荷中。当该类型的事件分配了两个或更多 kevent
结构体时，它将被调用。

这种方法不会有状态信息丢失，只会导致中间步骤信息丢失。

关于 replace/merge 回调的一个不错的例子在 v4l2-event.c 中：用于
控制事件的 ctrls_replace() 和 ctrls_merge() 回调。

注意：这些回调可以在中断上下文中调用，所以它们必须尽快完成并退出。

有用的函数：

void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)

  将事件加入视频设备的队列。驱动仅负责填充 type 和 data 域。
  其他域由 V4L2 填充。

int v4l2_event_subscribe(struct v4l2_fh *fh,
			 struct v4l2_event_subscription *sub, unsigned elems,
			 const struct v4l2_subscribed_event_ops *ops)

  video_device->ioctl_ops->vidioc_subscribe_event 必须检测驱动能
  产生特定 id 的事件。然后调用 v4l2_event_subscribe() 来订阅该事件。

  elems 参数是该事件的队列大小。若为 0，V4L2 框架将会（根据事件类型）
  填充默认值。

  ops 参数允许驱动指定一系列回调：
  * add:     当添加一个新监听者时调用（重复订阅同一个事件，此回调
             仅被执行一次）。
  * del:     当一个监听者停止监听时调用。
  * replace: 用‘新’事件替换‘早期‘事件。
  * merge:   将‘早期‘事件合并到‘新’事件中。
  这四个调用都是可选的，如果不想指定任何回调，则 ops 可为 NULL。

int v4l2_event_unsubscribe(struct v4l2_fh *fh,
			   struct v4l2_event_subscription *sub)

  v4l2_ioctl_ops 结构体中的 vidioc_unsubscribe_event 回调函数。
  驱动程序可以直接使用 v4l2_event_unsubscribe() 实现退订事件过程。

  特殊的 V4L2_EVENT_ALL 类型，可用于退订所有事件。驱动可能在特殊
  情况下需要做此操作。

int v4l2_event_pending(struct v4l2_fh *fh)

  返回未决事件的数量。有助于实现轮询（poll）操作。

事件通过 poll 系统调用传递到用户空间。驱动可用
v4l2_fh->wait (wait_queue_head_t 类型)作为参数调用 poll_wait()。

事件分为标准事件和私有事件。新的标准事件必须使用可用的最小事件类型
编号。驱动必须从他们本类型的编号起始处分配事件。类型的编号起始为
V4L2_EVENT_PRIVATE_START + n * 1000 ，其中 n 为可用最小编号。每个
类型中的第一个事件类型编号是为以后的使用保留的，所以第一个可用事件
类型编号是‘class base + 1’。

V4L2 事件机制的使用实例可以在 OMAP3 ISP 的驱动
(drivers/media/video/omap3isp)中找到。


code:
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../../disclaimer-zh_CN.rst

:Original: Documentation/virt/acrn/cpuid.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

 时奎亮 Alex Shi <alexs@kernel.org>

.. _cn_virt_acrn_cpuid:

==============
ACRN CPUID位域
==============

在ACRN超级管理器上运行的客户虚拟机可以使用CPUID检查其一些功能。

ACRN的cpuid函数是:

函数: 0x40000000

返回::

   eax = 0x40000010
   ebx = 0x4e524341
   ecx = 0x4e524341
   edx = 0x4e524341

注意，ebx，ecx和edx中的这个值对应于字符串“ACRNACRNACRN”。eax中的值对应于这个叶子
中存在的最大cpuid函数，如果将来有更多的函数加入，将被更新。

函数: define ACRN_CPUID_FEATURES (0x40000001)

返回::

          ebx, ecx, edx
          eax = an OR'ed group of (1 << flag)

其中 ``flag`` 的定义如下:

================================= =========== ================================
标志                              值          描述
================================= =========== ================================
ACRN_FEATURE_PRIVILEGED_VM        0           客户虚拟机是一个有特权的虚拟机
================================= =========== ================================

函数: 0x40000010

返回::

          ebx, ecx, edx
          eax = (Virtual) TSC frequency in kHz.

repo_name: angery/linux


code:
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../../disclaimer-zh_CN.rst

:Original: Documentation/virt/acrn/index.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

 时奎亮 Alex Shi <alexs@kernel.org>

.. _cn_virt_acrn_index:

==============
ACRN超级管理器
==============

.. toctree::
   :maxdepth: 1

   introduction
   io-request
   cpuid


code:
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../../disclaimer-zh_CN.rst

:Original: Documentation/virt/acrn/introduction.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

 时奎亮 Alex Shi <alexs@kernel.org>

.. _cn_virt_acrn_introduction:

ACRN超级管理器介绍
==================

ACRN超级管理器是一个第一类超级管理器，直接在裸机硬件上运行。它有一个特权管理虚拟机，称为服
务虚拟机，用于管理用户虚拟机和进行I/O仿真。

ACRN用户空间是一个运行在服务虚拟机中的应用程序，它根据命令行配置为用户虚拟机仿真设备。
ACRN管理程序服务模块（HSM）是服务虚拟机中的一个内核模块，为ACRN用户空间提供管理程序服
务。

下图展示了该架构。

::

                服务端VM                      用户端VM
      +----------------------------+  |  +------------------+
      |        +--------------+    |  |  |                  |
      |        |ACRN用户空间  |    |  |  |                  |
      |        +--------------+    |  |  |                  |
      |-----------------ioctl------|  |  |                  |   ...
      |内核空间       +----------+ |  |  |                  |
      |               |   HSM    | |  |  | 驱动             |
      |               +----------+ |  |  |                  |
      +--------------------|-------+  |  +------------------+
  +---------------------hypercall----------------------------------------+
  |                         ACRN超级管理器                               |
  +----------------------------------------------------------------------+
  |                          硬件                                        |
  +----------------------------------------------------------------------+

ACRN用户空间为用户虚拟机分配内存，配置和初始化用户虚拟机使用的设备，加载虚拟引导程序，
初始化虚拟CPU状态，处理来自用户虚拟机的I/O请求访问。它使用ioctls来与HSM通信。HSM通过
与ACRN超级管理器的hypercalls进行交互来实现管理服务。HSM向用户空间输出一个char设备接口
（/dev/acrn_hsm）。

ACRN超级管理器是开源的，任何人都可以贡献。源码库在
https://github.com/projectacrn/acrn-hypervisor。


code:
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../../disclaimer-zh_CN.rst

:Original: Documentation/virt/acrn/io-request.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

 时奎亮 Alex Shi <alexs@kernel.org>

.. _cn_virt_acrn_io-request:

I/O请求处理
===========

客户虚拟机的I/O请求由超级管理器构建，由ACRN超级管理器服务模块分发到与I/O请求的地址范
围相对应的I/O客户端。I/O请求处理的细节将在以下章节描述。

1. I/O请求
----------

对于每个客户虚拟机，有一个共享的4KB字节的内存区域，用于超级管理器和服务虚拟机之间的
I/O请求通信。一个I/O请求是一个256字节的结构体缓冲区，它是 "acrn_io_request" 结构
体，当客户虚拟机中发生被困的I/O访问时，由超级管理器的I/O处理器填充。服务虚拟机中的
ACRN用户空间首先分配一个4KB字节的页面，并将缓冲区的GPA（客户物理地址）传递给管理平
台。缓冲区被用作16个I/O请求槽的数组，每个I/O请求槽为256字节。这个数组是按vCPU ID
索引的。

2. I/O客户端
------------

一个I/O客户端负责处理客户虚拟机的I/O请求，其访问的GPA在一定范围内。每个客户虚拟机
可以关联多个I/O客户端。每个客户虚拟机都有一个特殊的客户端，称为默认客户端，负责处理
所有不在其他客户端范围内的I/O请求。ACRN用户空间充当每个客户虚拟机的默认客户端。

下面的图示显示了I/O请求共享缓冲区、I/O请求和I/O客户端之间的关系。

::

     +------------------------------------------------------+
     |                                       服务VM         |
     |+--------------------------------------------------+  |
     ||      +----------------------------------------+  |  |
     ||      | 共享页                 ACRN用户空间    |  |  |
     ||      |    +-----------------+  +------------+ |  |  |
     ||   +----+->| acrn_io_request |<-+  默认      | |  |  |
     ||   |  | |  +-----------------+  | I/O客户端  | |  |  |
     ||   |  | |  |       ...       |  +------------+ |  |  |
     ||   |  | |  +-----------------+                 |  |  |
     ||   |  +-|--------------------------------------+  |  |
     ||---|----|-----------------------------------------|  |
     ||   |    |                             内核        |  |
     ||   |    |            +----------------------+     |  |
     ||   |    |            | +-------------+  HSM |     |  |
     ||   |    +--------------+             |      |     |  |
     ||   |                 | | I/O客户端   |      |     |  |
     ||   |                 | |             |      |     |  |
     ||   |                 | +-------------+      |     |  |
     ||   |                 +----------------------+     |  |
     |+---|----------------------------------------------+  |
     +----|-------------------------------------------------+
          |
     +----|-------------------------------------------------+
     |  +-+-----------+                                     |
     |  | I/O处理     |              ACRN超级管理器         |
     |  +-------------+                                     |
     +------------------------------------------------------+

3. I/O请求状态转换
------------------

一个ACRN I/O请求的状态转换如下。

::

   FREE -> PENDING -> PROCESSING -> COMPLETE -> FREE -> ...

- FREE: 这个I/O请求槽是空的
- PENDING: 在这个槽位上有一个有效的I/O请求正在等待
- PROCESSING: 正在处理I/O请求
- COMPLETE: 该I/O请求已被处理

处于COMPLETE或FREE状态的I/O请求是由超级管理器拥有的。HSM和ACRN用户空间负责处理其
他的。

4. I/O请求的处理流程
--------------------

a. 当客户虚拟机中发生被陷入的I/O访问时，超级管理器的I/O处理程序将把I/O请求填充为
   PENDING状态。
b. 超级管理器向服务虚拟机发出一个向上调用，这是一个通知中断。
c. upcall处理程序会安排一个工作者来调度I/O请求。
d. 工作者寻找PENDING I/O请求，根据I/O访问的地址将其分配给不同的注册客户，将其
   状态更新为PROCESSING，并通知相应的客户进行处理。
e. 被通知的客户端处理指定的I/O请求。
f. HSM将I/O请求状态更新为COMPLETE，并通过hypercalls通知超级管理器完成。


code:
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/virt/guest-halt-polling.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

 时奎亮 Alex Shi <alexs@kernel.org>

.. _cn_virt_guest-halt-polling:

========================================
客户机停机轮询机制（Guest halt polling）
========================================

cpuidle_haltpoll驱动，与haltpoll管理器一起，允许客户机vcpus在停机前轮询
一定的时间。

这为物理机侧的轮询提供了以下好处:

	1) 在执行轮询时，POLL标志被设置，这允许远程vCPU在执行唤醒时避免发送
	   IPI（以及处理IPI的相关成本）。

	2) 可以避免虚拟机退出的成本。

客户机侧轮询的缺点是，即使在物理机中的其他可运行任务中也会进行轮询。

其基本逻辑如下。一个全局值，即guest_halt_poll_ns，是由用户配置的，表示允
许轮询的最大时间量。这个值是固定的。

每个vcpu都有一个可调整的guest_halt_poll_ns（"per-cpu guest_halt_poll_ns"），
它由算法响应事件进行调整（解释如下）。

模块参数
========

haltpoll管理器有5个可调整的模块参数:

1) guest_halt_poll_ns:

轮询停机前执行的最大时间，以纳秒为单位。

默认值: 200000

2) guest_halt_poll_shrink:

当唤醒事件发生在全局的guest_halt_poll_ns之后，用于缩减每个CPU的guest_halt_poll_ns
的划分系数。

默认值: 2

3) guest_halt_poll_grow:

当事件发生在per-cpu guest_halt_poll_ns之后但在global guest_halt_poll_ns之前，
用于增长per-cpu guest_halt_poll_ns的乘法系数。

默认值: 2

4) guest_halt_poll_grow_start:

在系统空闲的情况下，每个cpu guest_halt_poll_ns最终达到零。这个值设置了增长时的
初始每cpu guest_halt_poll_ns。这个值可以从10000开始增加，以避免在最初的增长阶
段出现失误。:

10k, 20k, 40k, ... (例如，假设guest_halt_poll_grow=2).

默认值: 50000

5) guest_halt_poll_allow_shrink:

允许缩减的Bool参数。设置为N以避免它（一旦达到全局的guest_halt_poll_ns值，每CPU的
guest_halt_poll_ns将保持高位）。

默认值: Y

模块参数可以从Debugfs文件中设置，在::

	/sys/module/haltpoll/parameters/

进一步说明
==========

- 在设置guest_halt_poll_ns参数时应该小心，因为一个大的值有可能使几乎是完全空闲机
  器上的cpu使用率达到100%。


code:
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/virt/index.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

 时奎亮 Alex Shi <alexs@kernel.org>

.. _cn_virt_index:

===============
Linux虚拟化支持
===============

.. toctree::
   :maxdepth: 2

   paravirt_ops
   guest-halt-polling
   ne_overview
   acrn/index

TODOLIST:

   kvm/index
   uml/user_mode_linux_howto_v2

.. only:: html and subproject

   Indices
   =======

   * :ref:`genindex`


code:
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/virt/ne_overview.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

 时奎亮 Alex Shi <alexs@kernel.org>

.. _cn_virt_ne_overview:

==============
Nitro Enclaves
==============

概述
====

Nitro Enclaves（NE）是亚马逊弹性计算云（EC2）的一项新功能，允许客户在EC2实
例中划分出孤立的计算环境[1]。

例如，一个处理敏感数据并在虚拟机中运行的应用程序，可以与在同一虚拟机中运行的
其他应用程序分开。然后，这个应用程序在一个独立于主虚拟机的虚拟机中运行，即
enclave。

一个enclave与催生它的虚拟机一起运行。这种设置符合低延迟应用的需要。为enclave
分配的资源，如内存和CPU，是从主虚拟机中分割出来的。每个enclave都被映射到一
个运行在主虚拟机中的进程，该进程通过一个ioctl接口与NE驱动进行通信。

在这个意义上，有两个组成部分。

1. 一个enclave抽象进程——一个运行在主虚拟机客体中的用户空间进程，它使用NE驱动
提供的ioctl接口来生成一个enclave虚拟机（这就是下面的2）。

有一个NE模拟的PCI设备暴露给主虚拟机。这个新的PCI设备的驱动被包含在NE驱动中。

ioctl逻辑被映射到PCI设备命令，例如，NE_START_ENCLAVE ioctl映射到一个enclave
启动PCI命令。然后，PCI设备命令被翻译成在管理程序方面采取的行动；也就是在运
行主虚拟机的主机上运行的Nitro管理程序。Nitro管理程序是基于KVM核心技术的。

2. enclave本身——一个运行在与催生它的主虚拟机相同的主机上的虚拟机。内存和CPU
从主虚拟机中分割出来，专门用于enclave虚拟机。enclave没有连接持久性存储。

从主虚拟机中分割出来并给enclave的内存区域需要对齐2 MiB/1 GiB物理连续的内存
区域（或这个大小的倍数，如8 MiB）。该内存可以通过使用hugetlbfs从用户空间分
配[2][3]。一个enclave的内存大小需要至少64 MiB。enclave内存和CPU需要来自同
一个NUMA节点。

一个enclave在专用的核心上运行。CPU 0及其同级别的CPU需要保持对主虚拟机的可用
性。CPU池必须由具有管理能力的用户为NE目的进行设置。关于CPU池的格式，请看内核
文档[4]中的cpu list部分。

enclave通过本地通信通道与主虚拟机进行通信，使用virtio-vsock[5]。主虚拟机有
virtio-pci vsock模拟设备，而飞地虚拟机有virtio-mmio vsock模拟设备。vsock
设备使用eventfd作为信令。enclave虚拟机看到通常的接口——本地APIC和IOAPIC——从
virtio-vsock设备获得中断。virtio-mmio设备被放置在典型的4 GiB以下的内存中。

在enclave中运行的应用程序需要和将在enclave虚拟机中运行的操作系统（如内核、
ramdisk、init）一起被打包到enclave镜像中。enclave虚拟机有自己的内核并遵循标
准的Linux启动协议[6]。

内核bzImage、内核命令行、ramdisk（s）是enclave镜像格式（EIF）的一部分；另外
还有一个EIF头，包括元数据，如magic number、eif版本、镜像大小和CRC。

哈希值是为整个enclave镜像（EIF）、内核和ramdisk（s）计算的。例如，这被用来检
查在enclave虚拟机中加载的enclave镜像是否是打算运行的那个。

这些加密测量包括在由Nitro超级管理器成的签名证明文件中，并进一步用来证明enclave
的身份；KMS是NE集成的服务的一个例子，它检查证明文件。

enclave镜像（EIF）被加载到enclave内存中，偏移量为8 MiB。enclave中的初始进程
连接到主虚拟机的vsock CID和一个预定义的端口--9000，以发送一个心跳值--0xb7。这
个机制用于在主虚拟机中检查enclave是否已经启动。主虚拟机的CID是3。

如果enclave虚拟机崩溃或优雅地退出，NE驱动会收到一个中断事件。这个事件会通过轮询
通知机制进一步发送到运行在主虚拟机中的用户空间enclave进程。然后，用户空间enclave
进程就可以退出了。

[1] https://aws.amazon.com/ec2/nitro/nitro-enclaves/
[2] https://www.kernel.org/doc/html/latest/admin-guide/mm/hugetlbpage.html
[3] https://lwn.net/Articles/807108/
[4] https://www.kernel.org/doc/html/latest/admin-guide/kernel-parameters.html
[5] https://man7.org/linux/man-pages/man7/vsock.7.html
[6] https://www.kernel.org/doc/html/latest/x86/boot.html


code:
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/virt/paravirt_ops.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

 陈飞杨 Feiyang Chen <chenfeiyang@loongson.cn>
 时奎亮 Alex Shi <alexs@kernel.org>

.. _cn_virt_paravirt_ops:

============
半虚拟化操作
============

Linux提供了对不同管理程序虚拟化技术的支持。历史上，为了支持不同的虚拟机超级管理器
（hypervisor，下文简称超级管理器），需要不同的二进制内核，这个限制已经被pv_ops移
除了。Linux pv_ops是一个虚拟化API，它能够支持不同的管理程序。它允许每个管理程序
优先于关键操作，并允许单一的内核二进制文件在所有支持的执行环境中运行，包括本机——没
有任何管理程序。

pv_ops提供了一组函数指针，代表了与低级关键指令和各领域高级功能相对应的操作。
pv-ops允许在运行时进行优化，在启动时对低级关键操作进行二进制修补。

pv_ops操作被分为三类:

- 简单的间接调用
   这些操作对应于高水平的函数，众所周知，间接调用的开销并不十分重要。

- 间接调用，允许用二进制补丁进行优化
   通常情况下，这些操作对应于低级别的关键指令。它们被频繁地调用，并且是对性能关
   键。开销是非常重要的。

- 一套用于手写汇编代码的宏程序
   手写的汇编代码（.S文件）也需要半虚拟化，因为它们包括敏感指令或其中的一些代
   码路径对性能非常关键。


code:
Chinese translated version of Documentation/core-api/irq/index.rst

If you have any comment or update to the content, please contact the
original document maintainer directly.  However, if you have a problem
communicating in English you can also ask the Chinese maintainer for
help.  Contact the Chinese maintainer if this translation is outdated
or if there is a problem with the translation.

Maintainer: Eric W. Biederman <ebiederman@xmission.com>
Traditional Chinese maintainer: Hu Haowen <src.res@email.cn>
---------------------------------------------------------------------
Documentation/core-api/irq/index.rst 的繁體中文翻譯

如果想評論或更新本文的內容，請直接聯繫原文檔的維護者。如果你使用英文
交流有困難的話，也可以向繁體中文版維護者求助。如果本翻譯更新不及時或
者翻譯存在問題，請聯繫繁體中文版維護者。

英文版維護者： Eric W. Biederman <ebiederman@xmission.com>
繁體中文版維護者： 胡皓文 Hu Haowen <src.res@email.cn>
繁體中文版翻譯者： 胡皓文 Hu Haowen <src.res@email.cn>
繁體中文版校譯者： 胡皓文 Hu Haowen <src.res@email.cn>

以下爲正文
---------------------------------------------------------------------
何爲 IRQ?

一個 IRQ 是來自某個設備的一個中斷請求。目前，它們可以來自一個硬體引腳，
或來自一個數據包。多個設備可能連接到同個硬體引腳，從而共享一個 IRQ。

一個 IRQ 編號是用於告知硬體中斷源的內核標識。通常情況下，這是一個
全局 irq_desc 數組的索引，但是除了在 linux/interrupt.h 中的實現，
具體的細節是體系結構特定的。

一個 IRQ 編號是設備上某個可能的中斷源的枚舉。通常情況下，枚舉的編號是
該引腳在系統內中斷控制器的所有輸入引腳中的編號。對於 ISA 總線中的情況，
枚舉的是在兩個 i8259 中斷控制器中 16 個輸入引腳。

架構可以對 IRQ 編號指定額外的含義，在硬體涉及任何手工配置的情況下，
是被提倡的。ISA 的 IRQ 是一個分配這類額外含義的典型例子。


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: Documentation/admin-guide/README.rst

:譯者:

 吳想成 Wu XiangCheng <bobwxc@email.cn>
 胡皓文 Hu Haowen <src.res@email.cn>

Linux內核5.x版本 <http://kernel.org/>
=========================================

以下是Linux版本5的發行註記。仔細閱讀它們，
它們會告訴你這些都是什麼，解釋如何安裝內核，以及遇到問題時該如何做。

什麼是Linux？
---------------

  Linux是Unix作業系統的克隆版本，由Linus Torvalds在一個鬆散的網絡黑客
  （Hacker，無貶義）團隊的幫助下從頭開始編寫。它旨在實現兼容POSIX和
  單一UNIX規範。

  它具有在現代成熟的Unix中應當具有的所有功能，包括真正的多任務處理、虛擬內存、
  共享庫、按需加載、共享的寫時拷貝（COW）可執行文件、恰當的內存管理以及包括
  IPv4和IPv6在內的複合網絡棧。

  Linux在GNU通用公共許可證，版本2（GNU GPLv2）下分發，詳見隨附的COPYING文件。

它能在什麼樣的硬體上運行？
-----------------------------

  雖然Linux最初是爲32位的x86 PC機（386或更高版本）開發的，但今天它也能運行在
  （至少）Compaq Alpha AXP、Sun SPARC與UltraSPARC、Motorola 68000、PowerPC、
  PowerPC64、ARM、Hitachi SuperH、Cell、IBM S/390、MIPS、HP PA-RISC、Intel
  IA-64、DEC VAX、AMD x86-64 Xtensa和ARC架構上。

  Linux很容易移植到大多數通用的32位或64位體系架構，只要它們有一個分頁內存管理
  單元（PMMU）和一個移植的GNU C編譯器（gcc；GNU Compiler Collection，GCC的一
  部分）。Linux也被移植到許多沒有PMMU的體系架構中，儘管功能顯然受到了一定的
  限制。
  Linux也被移植到了其自己上。現在可以將內核作爲用戶空間應用程式運行——這被
  稱爲用戶模式Linux（UML）。

文檔
-----
網際網路上和書籍上都有大量的電子文檔，既有Linux專屬文檔，也有與一般UNIX問題相關
的文檔。我建議在任何Linux FTP站點上查找LDP（Linux文檔項目）書籍的文檔子目錄。
本自述文件並不是關於系統的文檔：有更好的可用資源。

 - 網際網路上和書籍上都有大量的（電子）文檔，既有Linux專屬文檔，也有與普通
   UNIX問題相關的文檔。我建議在任何有LDP（Linux文檔項目）書籍的Linux FTP
   站點上查找文檔子目錄。本自述文件並不是關於系統的文檔：有更好的可用資源。

 - 文檔/子目錄中有各種自述文件：例如，這些文件通常包含一些特定驅動程序的
   內核安裝說明。請閱讀
   :ref:`Documentation/process/changes.rst <changes>` 文件，它包含了升級內核
   可能會導致的問題的相關信息。

安裝內核原始碼
---------------

 - 如果您要安裝完整的原始碼，請把內核tar檔案包放在您有權限的目錄中（例如您
   的主目錄）並將其解包::

     xz -cd linux-5.x.tar.xz | tar xvf -

   將「X」替換成最新內核的版本號。

   【不要】使用 /usr/src/linux 目錄！這裡有一組庫頭文件使用的內核頭文件
   （通常是不完整的）。它們應該與庫匹配，而不是被內核的變化搞得一團糟。

 - 您還可以通過打補丁在5.x版本之間升級。補丁以xz格式分發。要通過打補丁進行
   安裝，請獲取所有較新的補丁文件，進入內核原始碼（linux-5.x）的目錄並
   執行::

     xz -cd ../patch-5.x.xz | patch -p1

   請【按順序】替換所有大於當前原始碼樹版本的「x」，這樣就可以了。您可能想要
   刪除備份文件（文件名類似xxx~ 或 xxx.orig)，並確保沒有失敗的補丁（文件名
   類似xxx# 或 xxx.rej）。如果有，不是你就是我犯了錯誤。

   與5.x內核的補丁不同，5.x.y內核（也稱爲穩定版內核）的補丁不是增量的，而是
   直接應用於基本的5.x內核。例如，如果您的基本內核是5.0，並且希望應用5.0.3
   補丁，則不應先應用5.0.1和5.0.2的補丁。類似地，如果您運行的是5.0.2內核，
   並且希望跳轉到5.0.3，那麼在應用5.0.3補丁之前，必須首先撤銷5.0.2補丁
   （即patch -R）。更多關於這方面的內容，請閱讀
   :ref:`Documentation/process/applying-patches.rst <applying_patches>` 。

   或者，腳本 patch-kernel 可以用來自動化這個過程。它能確定當前內核版本並
   應用找到的所有補丁::

     linux/scripts/patch-kernel linux

   上面命令中的第一個參數是內核原始碼的位置。補丁是在當前目錄應用的，但是
   可以將另一個目錄指定爲第二個參數。

 - 確保沒有過時的 .o 文件和依賴項::

     cd linux
     make mrproper

   現在您應該已經正確安裝了原始碼。

軟體要求
---------

   編譯和運行5.x內核需要各種軟體包的最新版本。請參考
   :ref:`Documentation/process/changes.rst <changes>`
   來了解最低版本要求以及如何升級軟體包。請注意，使用過舊版本的這些包可能會
   導致很難追蹤的間接錯誤，因此不要以爲在生成或操作過程中出現明顯問題時可以
   只更新包。

爲內核建立目錄
---------------

   編譯內核時，默認情況下所有輸出文件都將與內核原始碼放在一起。使用
   ``make O=output/dir`` 選項可以爲輸出文件（包括 .config）指定備用位置。
   例如::

     kernel source code: /usr/src/linux-5.x
     build directory:    /home/name/build/kernel

   要配置和構建內核，請使用::

     cd /usr/src/linux-5.x
     make O=/home/name/build/kernel menuconfig
     make O=/home/name/build/kernel
     sudo make O=/home/name/build/kernel modules_install install

   請注意：如果使用了 ``O=output/dir`` 選項，那麼它必須用於make的所有調用。

配置內核
---------

   即使只升級一個小版本，也不要跳過此步驟。每個版本中都會添加新的配置選項，
   如果配置文件沒有按預定設置，就會出現奇怪的問題。如果您想以最少的工作量
   將現有配置升級到新版本，請使用 ``makeoldconfig`` ，它只會詢問您新配置
   選項的答案。

 - 其他配置命令包括::

     "make config"      純文本界面。

     "make menuconfig"  基於文本的彩色菜單、選項列表和對話框。

     "make nconfig"     增強的基於文本的彩色菜單。

     "make xconfig"     基於Qt的配置工具。

     "make gconfig"     基於GTK+的配置工具。

     "make oldconfig"   基於現有的 ./.config 文件選擇所有選項，並詢問
                        新配置選項。

     "make olddefconfig"
                        類似上一個，但不詢問直接將新選項設置爲默認值。

     "make defconfig"   根據體系架構，使用arch/$arch/defconfig或
                        arch/$arch/configs/${PLATFORM}_defconfig中的
                        默認選項值創建./.config文件。

     "make ${PLATFORM}_defconfig"
                        使用arch/$arch/configs/${PLATFORM}_defconfig中
                        的默認選項值創建一個./.config文件。
                        用「makehelp」來獲取您體系架構中所有可用平台的列表。

     "make allyesconfig"
                        通過儘可能將選項值設置爲「y」，創建一個
                        ./.config文件。

     "make allmodconfig"
                        通過儘可能將選項值設置爲「m」，創建一個
                        ./.config文件。

     "make allnoconfig" 通過儘可能將選項值設置爲「n」，創建一個
                        ./.config文件。

     "make randconfig"  通過隨機設置選項值來創建./.config文件。

     "make localmodconfig" 基於當前配置和加載的模塊（lsmod）創建配置。禁用
                           已加載的模塊不需要的任何模塊選項。

                           要爲另一台計算機創建localmodconfig，請將該計算機
                           的lsmod存儲到一個文件中，並將其作爲lsmod參數傳入。

                           此外，通過在參數LMC_KEEP中指定模塊的路徑，可以將
                           模塊保留在某些文件夾或kconfig文件中。

                   target$ lsmod > /tmp/mylsmod
                   target$ scp /tmp/mylsmod host:/tmp

                   host$ make LSMOD=/tmp/mylsmod \
                           LMC_KEEP="drivers/usb:drivers/gpu:fs" \
                           localmodconfig

                           上述方法在交叉編譯時也適用。

     "make localyesconfig" 與localmodconfig類似，只是它會將所有模塊選項轉換
                           爲內置（=y）。你可以同時通過LMC_KEEP保留模塊。

     "make kvmconfig"   爲kvm客體內核支持啓用其他選項。

     "make xenconfig"   爲xen dom0客體內核支持啓用其他選項。

     "make tinyconfig"  配置儘可能小的內核。

   更多關於使用Linux內核配置工具的信息，見文檔
   Documentation/kbuild/kconfig.rst。

 - ``make config`` 注意事項:

    - 包含不必要的驅動程序會使內核變大，並且在某些情況下會導致問題：
      探測不存在的控制器卡可能會混淆其他控制器。

    - 如果存在協處理器，則編譯了數學仿真的內核仍將使用協處理器：在
      這種情況下，數學仿真永遠不會被使用。內核會稍微大一點，但不管
      是否有數學協處理器，都可以在不同的機器上工作。

    - 「kernel hacking」配置細節通常會導致更大或更慢的內核（或兩者
      兼而有之），甚至可以通過配置一些例程來主動嘗試破壞壞代碼以發現
      內核問題，從而降低內核的穩定性（kmalloc()）。因此，您可能應該
      用於研究「開發」、「實驗」或「調試」特性相關問題。

編譯內核
---------

 - 確保您至少有gcc 5.1可用。
   有關更多信息，請參閱 :ref:`Documentation/process/changes.rst <changes>` 。

   請注意，您仍然可以使用此內核運行a.out用戶程序。

 - 執行 ``make`` 來創建壓縮內核映像。如果您安裝了lilo以適配內核makefile，
   那麼也可以進行 ``makeinstall`` ，但是您可能需要先檢查特定的lilo設置。

   實際安裝必須以root身份執行，但任何正常構建都不需要。
   無須徒然使用root身份。

 - 如果您將內核的任何部分配置爲模塊，那麼還必須執行 ``make modules_install`` 。

 - 詳細的內核編譯/生成輸出：

   通常，內核構建系統在相當安靜的模式下運行（但不是完全安靜）。但是有時您或
   其他內核開發人員需要看到編譯、連結或其他命令的執行過程。爲此，可使用
   「verbose（詳細）」構建模式。
   向 ``make`` 命令傳遞 ``V=1`` 來實現，例如::

     make V=1 all

   如需構建系統也給出內個目標重建的願意，請使用 ``V=2`` 。默認爲 ``V=0`` 。

 - 準備一個備份內核以防出錯。對於開發版本尤其如此，因爲每個新版本都包含
   尚未調試的新代碼。也要確保保留與該內核對應的模塊的備份。如果要安裝
   與工作內核版本號相同的新內核，請在進行 ``make modules_install`` 安裝
   之前備份modules目錄。

   或者，在編譯之前，使用內核配置選項「LOCALVERSION」向常規內核版本附加
   一個唯一的後綴。LOCALVERSION可以在「General Setup」菜單中設置。

 - 爲了引導新內核，您需要將內核映像（例如編譯後的
   .../linux/arch/x86/boot/bzImage）複製到常規可引導內核的位置。

 - 不再支持在沒有LILO等啓動裝載程序幫助的情況下直接從軟盤引導內核。

   如果從硬碟引導Linux，很可能使用LILO，它使用/etc/lilo.conf文件中
   指定的內核映像文件。內核映像文件通常是/vmlinuz、/boot/vmlinuz、
   /bzImage或/boot/bzImage。使用新內核前，請保存舊映像的副本，並複製
   新映像覆蓋舊映像。然後您【必須重新運行LILO】來更新加載映射！否則，
   將無法啓動新的內核映像。

   重新安裝LILO通常需要運行/sbin/LILO。您可能希望編輯/etc/lilo.conf
   文件爲舊內核映像指定一個條目（例如/vmlinux.old)防止新的不能正常
   工作。有關更多信息，請參閱LILO文檔。

   重新安裝LILO之後，您應該就已經準備好了。關閉系統，重新啓動，盡情
   享受吧！

   如果需要更改內核映像中的默認根設備、視頻模式等，請在適當的地方使用
   啓動裝載程序的引導選項。無需重新編譯內核即可更改這些參數。

 - 使用新內核重新啓動並享受它吧。

若遇到問題
-----------

 - 如果您發現了一些可能由於內核缺陷所導致的問題，請檢查MAINTAINERS（維護者）
   文件看看是否有人與令您遇到麻煩的內核部分相關。如果無人在此列出，那麼第二
   個最好的方案就是把它們發給我（torvalds@linux-foundation.org），也可能發送
   到任何其他相關的郵件列表或新聞組。

 - 在所有的缺陷報告中，【請】告訴我們您在說什麼內核，如何復現問題，以及您的
   設置是什麼的（使用您的常識）。如果問題是新的，請告訴我；如果問題是舊的，
   請嘗試告訴我您什麼時候首次注意到它。

 - 如果缺陷導致如下消息::

     unable to handle kernel paging request at address C0000010
     Oops: 0002
     EIP:   0010:XXXXXXXX
     eax: xxxxxxxx   ebx: xxxxxxxx   ecx: xxxxxxxx   edx: xxxxxxxx
     esi: xxxxxxxx   edi: xxxxxxxx   ebp: xxxxxxxx
     ds: xxxx  es: xxxx  fs: xxxx  gs: xxxx
     Pid: xx, process nr: xx
     xx xx xx xx xx xx xx xx xx xx

   或者類似的內核調試信息顯示在屏幕上或在系統日誌里，請【如實】複製它。
   可能對你來說轉儲（dump）看起來不可理解，但它確實包含可能有助於調試問題的
   信息。轉儲上方的文本也很重要：它說明了內核轉儲代碼的原因（在上面的示例中，
   是由於內核指針錯誤）。更多關於如何理解轉儲的信息，請參見
   Documentation/admin-guide/bug-hunting.rst。

 - 如果使用 CONFIG_KALLSYMS 編譯內核，則可以按原樣發送轉儲，否則必須使用
   ``ksymoops`` 程序來理解轉儲（但通常首選使用CONFIG_KALLSYMS編譯）。
   此實用程序可從
   https://www.kernel.org/pub/linux/utils/kernel/ksymoops/ 下載。
   或者，您可以手動執行轉儲查找：

 - 在調試像上面這樣的轉儲時，如果您可以查找EIP值的含義，這將非常有幫助。
   十六進位值本身對我或其他任何人都沒有太大幫助：它會取決於特定的內核設置。
   您應該做的是從EIP行獲取十六進位值（忽略 ``0010:`` ），然後在內核名字列表
   中查找它，以查看哪個內核函數包含有問題的地址。

   要找到內核函數名，您需要找到與顯示症狀的內核相關聯的系統二進位文件。就是
   文件「linux/vmlinux」。要提取名字列表並將其與內核崩潰中的EIP進行匹配，
   請執行::

     nm vmlinux | sort | less

   這將爲您提供一個按升序排序的內核地址列表，從中很容易找到包含有問題的地址
   的函數。請注意，內核調試消息提供的地址不一定與函數地址完全匹配（事實上，
   這是不可能的），因此您不能只「grep」列表：不過列表將爲您提供每個內核函數
   的起點，因此通過查找起始地址低於你正在搜索的地址，但後一個函數的高於的
   函數，你會找到您想要的。實際上，在您的問題報告中加入一些「上下文」可能是
   一個好主意，給出相關的上下幾行。

   如果您由於某些原因無法完成上述操作（如您使用預編譯的內核映像或類似的映像），
   請儘可能多地告訴我您的相關設置信息，這會有所幫助。有關詳細信息請閱讀
   『Documentation/admin-guide/reporting-issues.rst』。

 - 或者，您可以在正在運行的內核上使用gdb（只讀的；即不能更改值或設置斷點）。
   爲此，請首先使用-g編譯內核；適當地編輯arch/x86/Makefile，然後執行 ``make
   clean`` 。您還需要啓用CONFIG_PROC_FS（通過 ``make config`` ）。

   使用新內核重新啓動後，執行 ``gdb vmlinux /proc/kcore`` 。現在可以使用所有
   普通的gdb命令。查找系統崩潰點的命令是 ``l *0xXXXXXXXX`` （將xxx替換爲EIP
   值）。

   用gdb無法調試一個當前未運行的內核是由於gdb（錯誤地）忽略了編譯內核的起始
   偏移量。


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :doc:`../../../admin-guide/bug-bisect`

:譯者:

 吳想成 Wu XiangCheng <bobwxc@email.cn>
 胡皓文 Hu Haowen <src.res@email.cn>

二分（bisect）缺陷
+++++++++++++++++++

（英文版）最後更新：2016年10月28日

引言
=====

始終嘗試由來自kernel.org的原始碼構建的最新內核。如果您沒有信心這樣做，請將
錯誤報告給您的發行版供應商，而不是內核開發人員。

找到缺陷（bug）並不總是那麼容易，不過仍然得去找。如果你找不到它，不要放棄。
儘可能多的向相關維護人員報告您發現的信息。請參閱MAINTAINERS文件以了解您所
關注的子系統的維護人員。

在提交錯誤報告之前，請閱讀「Documentation/admin-guide/reporting-issues.rst」。

設備未出現（Devices not appearing）
====================================

這通常是由udev/systemd引起的。在將其歸咎於內核之前先檢查一下。

查找導致缺陷的補丁
===================

使用 ``git`` 提供的工具可以很容易地找到缺陷，只要缺陷是可復現的。

操作步驟：

- 從git原始碼構建內核
- 以此開始二分 [#f1]_::

	$ git bisect start

- 標記損壞的變更集::

	$ git bisect bad [commit]

- 標記正常工作的變更集::

	$ git bisect good [commit]

- 重新構建內核並測試
- 使用以下任一與git bisect進行交互::

	$ git bisect good

  或::

	$ git bisect bad

  這取決於您測試的變更集上是否有缺陷
- 在一些交互之後，git bisect將給出可能導致缺陷的變更集。

- 例如，如果您知道當前版本有問題，而4.8版本是正常的，則可以執行以下操作::

	$ git bisect start
	$ git bisect bad                 # Current version is bad
	$ git bisect good v4.8

.. [#f1] 您可以（可選地）在開始git bisect的時候提供good或bad參數
         ``git bisect start [BAD] [GOOD]``

如需進一步參考，請閱讀：

- ``git-bisect`` 的手冊頁
- `Fighting regressions with git bisect（用git bisect解決回歸）
  <https://www.kernel.org/pub/software/scm/git/docs/git-bisect-lk2009.html>`_
- `Fully automated bisecting with "git bisect run"（使用git bisect run
  來全自動二分） <https://lwn.net/Articles/317154>`_
- `Using Git bisect to figure out when brokenness was introduced
  （使用Git二分來找出何時引入了錯誤） <http://webchick.net/node/99>`_


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :doc:`../../../admin-guide/bug-hunting`

:譯者:

 吳想成 Wu XiangCheng <bobwxc@email.cn>
 胡皓文 Hu Haowen <src.res@email.cn>

追蹤缺陷
=========

內核錯誤報告通常附帶如下堆棧轉儲::

	------------[ cut here ]------------
	WARNING: CPU: 1 PID: 28102 at kernel/module.c:1108 module_put+0x57/0x70
	Modules linked in: dvb_usb_gp8psk(-) dvb_usb dvb_core nvidia_drm(PO) nvidia_modeset(PO) snd_hda_codec_hdmi snd_hda_intel snd_hda_codec snd_hwdep snd_hda_core snd_pcm snd_timer snd soundcore nvidia(PO) [last unloaded: rc_core]
	CPU: 1 PID: 28102 Comm: rmmod Tainted: P        WC O 4.8.4-build.1 #1
	Hardware name: MSI MS-7309/MS-7309, BIOS V1.12 02/23/2009
	 00000000 c12ba080 00000000 00000000 c103ed6a c1616014 00000001 00006dc6
	 c1615862 00000454 c109e8a7 c109e8a7 00000009 ffffffff 00000000 f13f6a10
	 f5f5a600 c103ee33 00000009 00000000 00000000 c109e8a7 f80ca4d0 c109f617
	Call Trace:
	 [<c12ba080>] ? dump_stack+0x44/0x64
	 [<c103ed6a>] ? __warn+0xfa/0x120
	 [<c109e8a7>] ? module_put+0x57/0x70
	 [<c109e8a7>] ? module_put+0x57/0x70
	 [<c103ee33>] ? warn_slowpath_null+0x23/0x30
	 [<c109e8a7>] ? module_put+0x57/0x70
	 [<f80ca4d0>] ? gp8psk_fe_set_frontend+0x460/0x460 [dvb_usb_gp8psk]
	 [<c109f617>] ? symbol_put_addr+0x27/0x50
	 [<f80bc9ca>] ? dvb_usb_adapter_frontend_exit+0x3a/0x70 [dvb_usb]
	 [<f80bb3bf>] ? dvb_usb_exit+0x2f/0xd0 [dvb_usb]
	 [<c13d03bc>] ? usb_disable_endpoint+0x7c/0xb0
	 [<f80bb48a>] ? dvb_usb_device_exit+0x2a/0x50 [dvb_usb]
	 [<c13d2882>] ? usb_unbind_interface+0x62/0x250
	 [<c136b514>] ? __pm_runtime_idle+0x44/0x70
	 [<c13620d8>] ? __device_release_driver+0x78/0x120
	 [<c1362907>] ? driver_detach+0x87/0x90
	 [<c1361c48>] ? bus_remove_driver+0x38/0x90
	 [<c13d1c18>] ? usb_deregister+0x58/0xb0
	 [<c109fbb0>] ? SyS_delete_module+0x130/0x1f0
	 [<c1055654>] ? task_work_run+0x64/0x80
	 [<c1000fa5>] ? exit_to_usermode_loop+0x85/0x90
	 [<c10013f0>] ? do_fast_syscall_32+0x80/0x130
	 [<c1549f43>] ? sysenter_past_esp+0x40/0x6a
	---[ end trace 6ebc60ef3981792f ]---

這樣的堆棧跟蹤提供了足夠的信息來識別內核原始碼中發生錯誤的那一行。根據問題的
嚴重性，它還可能包含 **「Oops」** 一詞，比如::

	BUG: unable to handle kernel NULL pointer dereference at   (null)
	IP: [<c06969d4>] iret_exc+0x7d0/0xa59
	*pdpt = 000000002258a001 *pde = 0000000000000000
	Oops: 0002 [#1] PREEMPT SMP
	...

儘管有 **Oops** 或其他類型的堆棧跟蹤，但通常需要找到出問題的行來識別和處理缺
陷。在本章中，我們將參考「Oops」來了解需要分析的各種堆棧跟蹤。

如果內核是用 ``CONFIG_DEBUG_INFO`` 編譯的，那麼可以使用文件：
`scripts/decode_stacktrace.sh` 。

連結的模塊
-----------

受到汙染或正在加載/卸載的模塊用「（…）」標記，汙染標誌在
`Documentation/admin-guide/tainted-kernels.rst` 文件中進行了描述，「正在被加
載」用「+」標註，「正在被卸載」用「-」標註。

Oops消息在哪？
---------------

通常，Oops文本由klogd從內核緩衝區讀取，然後交給 ``syslogd`` ，後者將其寫入
syslog文件，通常是 ``/var/log/messages`` （取決於 ``/etc/syslog.conf`` ）。
在使用systemd的系統上，它也可以由 ``journald`` 守護進程存儲，並通過運行
``journalctl`` 命令進行訪問。

有時 ``klogd`` 會掛掉，這種情況下您可以運行 ``dmesg > file`` 從內核緩衝區
讀取數據並保存它。或者您可以 ``cat /proc/kmsg > file`` ，但是您必須適時
中斷以停止傳輸，因爲 ``kmsg`` 是一個「永無止境的文件」。

如果機器嚴重崩潰，無法輸入命令或磁碟不可用，那還有三個選項：

(1) 手動複製屏幕上的文本，並在機器重新啓動後輸入。很難受，但這是突然崩潰下
    唯一的選擇。或者你可以用數位相機拍下屏幕——雖然不那麼好，但總比什麼都沒
    有好。如果消息滾動超出控制台頂部，使用更高解析度（例如 ``vga=791`` ）
    引導啓動將允許您閱讀更多文本。（警告：這需要 ``vesafb`` ，因此對「早期」
    的Oppses沒有幫助）

(2) 從串口終端啓動（參見
    :ref:`Documentation/admin-guide/serial-console.rst <serial_console>` ），
    在另一台機器上運行數據機然後用你喜歡的通信程序捕獲輸出。
    Minicom運行良好。

(3) 使用Kdump（參閱 Documentation/admin-guide/kdump/kdump.rst ），使用
    Documentation/admin-guide/kdump/gdbmacros.txt 中的dmesg gdbmacro從舊內存
    中提取內核環形緩衝區。

找到缺陷位置
-------------

如果你能指出缺陷在內核原始碼中的位置，則報告缺陷的效果會非常好。這有兩種方法。
通常來說使用 ``gdb`` 會比較容易，不過內核需要用調試信息來預編譯。

gdb
^^^^

GNU 調試器（GNU debugger， ``gdb`` ）是從 ``vmlinux`` 文件中找出OOPS的確切
文件和行號的最佳方法。

在使用 ``CONFIG_DEBUG_INFO`` 編譯的內核上使用gdb效果最好。可通過運行以下命令
進行設置::

  $ ./scripts/config -d COMPILE_TEST -e DEBUG_KERNEL -e DEBUG_INFO

在用 ``CONFIG_DEBUG_INFO`` 編譯的內核上，你可以直接從OOPS複製EIP值::

 EIP:    0060:[<c021e50e>]    Not tainted VLI

並使用GDB來將其翻譯成可讀形式::

  $ gdb vmlinux
  (gdb) l *0xc021e50e

如果沒有啓用 ``CONFIG_DEBUG_INFO`` ，則使用OOPS的函數偏移::

 EIP is at vt_ioctl+0xda8/0x1482

並在啓用 ``CONFIG_DEBUG_INFO`` 的情況下重新編譯內核::

  $ ./scripts/config -d COMPILE_TEST -e DEBUG_KERNEL -e DEBUG_INFO
  $ make vmlinux
  $ gdb vmlinux
  (gdb) l *vt_ioctl+0xda8
  0x1888 is in vt_ioctl (drivers/tty/vt/vt_ioctl.c:293).
  288	{
  289		struct vc_data *vc = NULL;
  290		int ret = 0;
  291
  292		console_lock();
  293		if (VT_BUSY(vc_num))
  294			ret = -EBUSY;
  295		else if (vc_num)
  296			vc = vc_deallocate(vc_num);
  297		console_unlock();

或者若您想要更詳細的顯示::

  (gdb) p vt_ioctl
  $1 = {int (struct tty_struct *, unsigned int, unsigned long)} 0xae0 <vt_ioctl>
  (gdb) l *0xae0+0xda8

您也可以使用對象文件作爲替代::

  $ make drivers/tty/
  $ gdb drivers/tty/vt/vt_ioctl.o
  (gdb) l *vt_ioctl+0xda8

如果你有調用跟蹤，類似::

     Call Trace:
      [<ffffffff8802c8e9>] :jbd:log_wait_commit+0xa3/0xf5
      [<ffffffff810482d9>] autoremove_wake_function+0x0/0x2e
      [<ffffffff8802770b>] :jbd:journal_stop+0x1be/0x1ee
      ...

這表明問題可能在 :jbd: 模塊中。您可以在gdb中加載該模塊並列出相關代碼::

  $ gdb fs/jbd/jbd.ko
  (gdb) l *log_wait_commit+0xa3

.. note::

     您還可以對堆棧跟蹤處的任何函數調用執行相同的操作，例如::

	 [<f80bc9ca>] ? dvb_usb_adapter_frontend_exit+0x3a/0x70 [dvb_usb]

     上述調用發生的位置可以通過以下方式看到::

	$ gdb drivers/media/usb/dvb-usb/dvb-usb.o
	(gdb) l *dvb_usb_adapter_frontend_exit+0x3a

objdump
^^^^^^^^

要調試內核，請使用objdump並從崩潰輸出中查找十六進位偏移，以找到有效的代碼/匯
編行。如果沒有調試符號，您將看到所示例程的彙編程序代碼，但是如果內核有調試
符號，C代碼也將可見（調試符號可以在內核配置菜單的hacking項中啓用）。例如::

    $ objdump -r -S -l --disassemble net/dccp/ipv4.o

.. note::

   您需要處於內核樹的頂層以便此獲得您的C文件。

如果您無法訪問原始碼，仍然可以使用以下方法調試一些崩潰轉儲（如Dave Miller的
示例崩潰轉儲輸出所示）::

     EIP is at 	+0x14/0x4c0
      ...
     Code: 44 24 04 e8 6f 05 00 00 e9 e8 fe ff ff 8d 76 00 8d bc 27 00 00
     00 00 55 57  56 53 81 ec bc 00 00 00 8b ac 24 d0 00 00 00 8b 5d 08
     <8b> 83 3c 01 00 00 89 44  24 14 8b 45 28 85 c0 89 44 24 18 0f 85

     Put the bytes into a "foo.s" file like this:

            .text
            .globl foo
     foo:
            .byte  .... /* bytes from Code: part of OOPS dump */

     Compile it with "gcc -c -o foo.o foo.s" then look at the output of
     "objdump --disassemble foo.o".

     Output:

     ip_queue_xmit:
         push       %ebp
         push       %edi
         push       %esi
         push       %ebx
         sub        $0xbc, %esp
         mov        0xd0(%esp), %ebp        ! %ebp = arg0 (skb)
         mov        0x8(%ebp), %ebx         ! %ebx = skb->sk
         mov        0x13c(%ebx), %eax       ! %eax = inet_sk(sk)->opt

`scripts/decodecode` 文件可以用來自動完成大部分工作，這取決於正在調試的CPU
體系結構。

報告缺陷
---------

一旦你通過定位缺陷找到了其發生的地方，你可以嘗試自己修復它或者向上游報告它。

爲了向上游報告，您應該找出用於開發受影響代碼的郵件列表。這可以使用 ``get_maintainer.pl`` 。

例如，您在gspca的sonixj.c文件中發現一個缺陷，則可以通過以下方法找到它的維護者::

	$ ./scripts/get_maintainer.pl -f drivers/media/usb/gspca/sonixj.c
	Hans Verkuil <hverkuil@xs4all.nl> (odd fixer:GSPCA USB WEBCAM DRIVER,commit_signer:1/1=100%)
	Mauro Carvalho Chehab <mchehab@kernel.org> (maintainer:MEDIA INPUT INFRASTRUCTURE (V4L/DVB),commit_signer:1/1=100%)
	Tejun Heo <tj@kernel.org> (commit_signer:1/1=100%)
	Bhaktipriya Shridhar <bhaktipriya96@gmail.com> (commit_signer:1/1=100%,authored:1/1=100%,added_lines:4/4=100%,removed_lines:9/9=100%)
	linux-media@vger.kernel.org (open list:GSPCA USB WEBCAM DRIVER)
	linux-kernel@vger.kernel.org (open list)

請注意它將指出：

- 最後接觸原始碼的開發人員（如果這是在git樹中完成的）。在上面的例子中是Tejun
  和Bhaktipriya（在這個特定的案例中，沒有人真正參與這個文件的開發）；
- 驅動維護人員（Hans Verkuil）；
- 子系統維護人員（Mauro Carvalho Chehab）；
- 驅動程序和/或子系統郵件列表（linux-media@vger.kernel.org）；
- Linux內核郵件列表（linux-kernel@vger.kernel.org）。

通常，修復缺陷的最快方法是將它報告給用於開發相關代碼的郵件列表（linux-media
ML），抄送驅動程序維護者（Hans）。

如果你完全不知道該把報告寄給誰，且 ``get_maintainer.pl`` 也沒有提供任何有用
的信息，請發送到linux-kernel@vger.kernel.org。

感謝您的幫助，這使Linux儘可能穩定:-)

修復缺陷
---------

如果你懂得編程，你不僅可以通過報告錯誤來幫助我們，還可以提供一個解決方案。
畢竟，開源就是分享你的工作，你不想因爲你的天才而被認可嗎？

如果你決定這樣做，請在制定解決方案後將其提交到上游。

請務必閱讀
:ref:`Documentation/process/submitting-patches.rst <submittingpatches>` ，
以幫助您的代碼被接受。

---------------------------------------------------------------------------

用 ``klogd`` 進行Oops跟蹤的注意事項
------------------------------------

爲了幫助Linus和其他內核開發人員， ``klogd`` 對保護故障的處理提供了大量支持。
爲了完整支持地址解析，至少應該使用 ``sysklogd`` 包的1.3-pl3版本。

當發生保護故障時， ``klogd`` 守護進程會自動將內核日誌消息中的重要地址轉換爲
它們的等效符號。然後通過 ``klogd`` 使用的任何報告機制來轉發這個已翻譯的內核
消息。保護錯誤消息可以直接從消息文件中剪切出來並轉發給內核開發人員。

``klogd`` 執行兩種類型的地址解析，靜態翻譯和動態翻譯。靜態翻譯使用System.map
文件。爲了進行靜態轉換， ``klogd`` 守護進程必須能夠在守護進程初始化時找到系
統映射文件。有關 ``klogd`` 如何搜索映射文件的信息，請參見klogd手冊頁。

當使用內核可加載模塊時，動態地址轉換非常重要。由於內核模塊的內存是從內核的
動態內存池中分配的，因此無論是模塊的開頭還是模塊中的函數和符號都沒有固定的
位置。

內核支持系統調用，允許程序確定加載哪些模塊及其在內存中的位置。klogd守護進程
使用這些系統調用構建了一個符號表，可用於調試可加載內核模塊中發生的保護錯誤。

klogd至少會提供產生保護故障的模塊的名稱。如果可加載模塊的開發人員選擇從模塊
導出符號信息，則可能會有其他可用的符號信息。

由於內核模塊環境可以是動態的，因此當模塊環境發生變化時，必須有一種通知
``klogd`` 守護進程的機制。有一些可用的命令行選項允許klogd向當前正在執行的守
護進程發出信號示意應該刷新符號信息。有關更多信息，請參閱 ``klogd`` 手冊頁。

sysklogd發行版附帶了一個補丁，它修改了 ``modules-2.0.0`` 包，以便在加載或
卸載模塊時自動向klogd發送信號。應用此補丁基本上可無縫支持調試內核可加載模塊
發生的保護故障。

以下是 ``klogd`` 處理的可加載模塊中的保護故障示例::

	Aug 29 09:51:01 blizard kernel: Unable to handle kernel paging request at virtual address f15e97cc
	Aug 29 09:51:01 blizard kernel: current->tss.cr3 = 0062d000, %cr3 = 0062d000
	Aug 29 09:51:01 blizard kernel: *pde = 00000000
	Aug 29 09:51:01 blizard kernel: Oops: 0002
	Aug 29 09:51:01 blizard kernel: CPU:    0
	Aug 29 09:51:01 blizard kernel: EIP:    0010:[oops:_oops+16/3868]
	Aug 29 09:51:01 blizard kernel: EFLAGS: 00010212
	Aug 29 09:51:01 blizard kernel: eax: 315e97cc   ebx: 003a6f80   ecx: 001be77b   edx: 00237c0c
	Aug 29 09:51:01 blizard kernel: esi: 00000000   edi: bffffdb3   ebp: 00589f90   esp: 00589f8c
	Aug 29 09:51:01 blizard kernel: ds: 0018   es: 0018   fs: 002b   gs: 002b   ss: 0018
	Aug 29 09:51:01 blizard kernel: Process oops_test (pid: 3374, process nr: 21, stackpage=00589000)
	Aug 29 09:51:01 blizard kernel: Stack: 315e97cc 00589f98 0100b0b4 bffffed4 0012e38e 00240c64 003a6f80 00000001
	Aug 29 09:51:01 blizard kernel:        00000000 00237810 bfffff00 0010a7fa 00000003 00000001 00000000 bfffff00
	Aug 29 09:51:01 blizard kernel:        bffffdb3 bffffed4 ffffffda 0000002b 0007002b 0000002b 0000002b 00000036
	Aug 29 09:51:01 blizard kernel: Call Trace: [oops:_oops_ioctl+48/80] [_sys_ioctl+254/272] [_system_call+82/128]
	Aug 29 09:51:01 blizard kernel: Code: c7 00 05 00 00 00 eb 08 90 90 90 90 90 90 90 90 89 ec 5d c3

---------------------------------------------------------------------------

::

  Dr. G.W. Wettstein           Oncology Research Div. Computing Facility
  Roger Maris Cancer Center    INTERNET: greg@wind.rmcc.com
  820 4th St. N.
  Fargo, ND  58122
  Phone: 701-234-7556


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Translator: 胡皓文 Hu Haowen <src.res@email.cn>

清除 WARN_ONCE
--------------

WARN_ONCE / WARN_ON_ONCE / printk_once 僅僅列印一次消息.

echo 1 > /sys/kernel/debug/clear_warn_once

可以清除這種狀態並且再次允許列印一次告警信息，這對於運行測試集後重現問題
很有用。


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Translator: 胡皓文 Hu Haowen <src.res@email.cn>

========
CPU 負載
========

Linux通過``/proc/stat``和``/proc/uptime``導出各種信息，用戶空間工具
如top(1)使用這些信息計算系統花費在某個特定狀態的平均時間。
例如：

    $ iostat
    Linux 2.6.18.3-exp (linmac)     02/20/2007

    avg-cpu:  %user   %nice %system %iowait  %steal   %idle
              10.01    0.00    2.92    5.44    0.00   81.63

    ...

這裡系統認爲在默認採樣周期內有10.01%的時間工作在用戶空間，2.92%的時
間用在系統空間，總體上有81.63%的時間是空閒的。

大多數情況下``/proc/stat``的信息幾乎真實反映了系統信息，然而，由於內
核採集這些數據的方式/時間的特點，有時這些信息根本不可靠。

那麼這些信息是如何被搜集的呢？每當時間中斷觸發時，內核查看此刻運行的
進程類型，並增加與此類型/狀態進程對應的計數器的值。這種方法的問題是
在兩次時間中斷之間系統（進程）能夠在多種狀態之間切換多次，而計數器只
增加最後一種狀態下的計數。

舉例
---

假設系統有一個進程以如下方式周期性地占用cpu::

     兩個時鐘中斷之間的時間線
    |-----------------------|
     ^                     ^
     |_ 開始運行           |
                           |_ 開始睡眠
                           （很快會被喚醒）

在上面的情況下，根據``/proc/stat``的信息（由於當系統處於空閒狀態時，
時間中斷經常會發生）系統的負載將會是0

大家能夠想像內核的這種行爲會發生在許多情況下，這將導致``/proc/stat``
中存在相當古怪的信息::

	/* gcc -o hog smallhog.c */
	#include <time.h>
	#include <limits.h>
	#include <signal.h>
	#include <sys/time.h>
	#define HIST 10

	static volatile sig_atomic_t stop;

	static void sighandler (int signr)
	{
	(void) signr;
	stop = 1;
	}
	static unsigned long hog (unsigned long niters)
	{
	stop = 0;
	while (!stop && --niters);
	return niters;
	}
	int main (void)
	{
	int i;
	struct itimerval it = { .it_interval = { .tv_sec = 0, .tv_usec = 1 },
				.it_value = { .tv_sec = 0, .tv_usec = 1 } };
	sigset_t set;
	unsigned long v[HIST];
	double tmp = 0.0;
	unsigned long n;
	signal (SIGALRM, &sighandler);
	setitimer (ITIMER_REAL, &it, NULL);

	hog (ULONG_MAX);
	for (i = 0; i < HIST; ++i) v[i] = ULONG_MAX - hog (ULONG_MAX);
	for (i = 0; i < HIST; ++i) tmp += v[i];
	tmp /= HIST;
	n = tmp - (tmp / 3.0);

	sigemptyset (&set);
	sigaddset (&set, SIGALRM);

	for (;;) {
		hog (n);
		sigwait (&set, &i);
	}
	return 0;
	}

參考
---

- https://lore.kernel.org/r/loom.20070212T063225-663@post.gmane.org
- Documentation/filesystems/proc.rst (1.8)

謝謝
---

Con Kolivas, Pavel Machek


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :doc:`../../../admin-guide/index`
:Translator: 胡皓文 Hu Haowen <src.res@email.cn>

Linux 內核用戶和管理員指南
==========================

下面是一組隨時間添加到內核中的面向用戶的文檔的集合。到目前爲止，還沒有一個
整體的順序或組織 - 這些材料不是一個單一的，連貫的文件！幸運的話，情況會隨著
時間的推移而迅速改善。

這個初始部分包含總體信息，包括描述內核的README， 關於內核參數的文檔等。

.. toctree::
   :maxdepth: 1

   README

Todolist:

   kernel-parameters
   devices
   sysctl/index

本節介紹CPU漏洞及其緩解措施。

Todolist:

   hw-vuln/index

下面的一組文檔，針對的是試圖跟蹤問題和bug的用戶。

.. toctree::
   :maxdepth: 1

   reporting-issues
   security-bugs
   bug-hunting
   bug-bisect
   tainted-kernels
   init

Todolist:

   reporting-bugs
   ramoops
   dynamic-debug-howto
   kdump/index
   perf/index

這是應用程式開發人員感興趣的章節的開始。可以在這裡找到涵蓋內核ABI各個
方面的文檔。

Todolist:

   sysfs-rules

本手冊的其餘部分包括各種指南，介紹如何根據您的喜好配置內核的特定行爲。

.. toctree::
   :maxdepth: 1

   clearing-warn-once
   cpu-load
   unicode

Todolist:

   acpi/index
   aoe/index
   auxdisplay/index
   bcache
   binderfs
   binfmt-misc
   blockdev/index
   bootconfig
   braille-console
   btmrvl
   cgroup-v1/index
   cgroup-v2
   cifs/index
   cputopology
   dell_rbu
   device-mapper/index
   edid
   efi-stub
   ext4
   nfs/index
   gpio/index
   highuid
   hw_random
   initrd
   iostats
   java
   jfs
   kernel-per-CPU-kthreads
   laptops/index
   lcd-panel-cgram
   ldm
   lockup-watchdogs
   LSM/index
   md
   media/index
   mm/index
   module-signing
   mono
   namespaces/index
   numastat
   parport
   perf-security
   pm/index
   pnp
   rapidio
   ras
   rtc
   serial-console
   svga
   sysrq
   thunderbolt
   ufs
   vga-softcursor
   video-output
   xfs

.. only::  subproject and html

   Indices
   =======

   * :ref:`genindex`


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :doc:`../../../admin-guide/init`

:譯者:

 吳想成 Wu XiangCheng <bobwxc@email.cn>
 胡皓文 Hu Haowen <src.res@email.cn>

解釋「No working init found.」啓動掛起消息
==========================================

:作者:

 Andreas Mohr <andi at lisas period de>

 Cristian Souza <cristianmsbr at gmail period com>

本文檔提供了加載初始化二進位（init binary）失敗的一些高層級原因（大致按執行
順序列出）。

1) **無法掛載根文件系統Unable to mount root FS** ：請設置「debug」內核參數（在
   引導加載程序bootloader配置文件或CONFIG_CMDLINE）以獲取更詳細的內核消息。

2) **初始化二進位不存在於根文件系統上init binary doesn't exist on rootfs** ：
   確保您的根文件系統類型正確（並且 ``root=`` 內核參數指向正確的分區）；擁有
   所需的驅動程序，例如SCSI或USB等存儲硬體；文件系統（ext3、jffs2等）是內建的
   （或者作爲模塊由initrd預加載）。

3) **控制台設備損壞Broken console device** ： ``console= setup`` 中可能存在
   衝突 --> 初始控制台不可用（initial console unavailable）。例如，由於串行
   IRQ問題（如缺少基於中斷的配置）導致的某些串行控制台不可靠。嘗試使用不同的
   ``console= device`` 或像 ``netconsole=`` 。

4) **二進位存在但依賴項不可用Binary exists but dependencies not available** ：
   例如初始化二進位的必需庫依賴項，像 ``/lib/ld-linux.so.2`` 丟失或損壞。使用
   ``readelf -d <INIT>|grep NEEDED`` 找出需要哪些庫。

5) **無法加載二進位Binary cannot be loaded** ：請確保二進位的體系結構與您的
   硬體匹配。例如i386不匹配x86_64，或者嘗試在ARM硬體上加載x86。如果您嘗試在
   此處加載非二進位文件（shell腳本？），您應該確保腳本在其工作頭（shebang
   header）行 ``#!/...`` 中指定能正常工作的解釋器（包括其庫依賴項）。在處理
   腳本之前，最好先測試一個簡單的非腳本二進位文件，比如 ``/bin/sh`` ，並確認
   它能成功執行。要了解更多信息，請將代碼添加到 ``init/main.c`` 以顯示
   kernel_execve()的返回值。

當您發現新的失敗原因時，請擴展本解釋（畢竟加載初始化二進位是一個 **關鍵** 且
艱難的過渡步驟，需要儘可能無痛地進行），然後向LKML提交一個補丁。

待辦事項：

- 通過一個可以存儲 ``kernel_execve()`` 結果值的結構體數組實現各種
  ``run_init_process()`` 調用，並在失敗時通過疊代 **所有** 結果來記錄一切
  （非常重要的可用性修復）。
- 試著使實現本身在一般情況下更有幫助，例如在受影響的地方提供額外的錯誤消息。


code:
.. SPDX-License-Identifier: (GPL-2.0+ OR CC-BY-4.0)
..
   If you want to distribute this text under CC-BY-4.0 only, please use 'The
   Linux kernel developers' for author attribution and link this as source:
   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/Documentation/admin-guide/reporting-issues.rst
..
   Note: Only the content of this RST file as found in the Linux kernel sources
   is available under CC-BY-4.0, as versions of this text that were processed
   (for example by the kernel's build system) might contain content taken from
   files which use a more restrictive license.

.. include:: ../disclaimer-zh_TW.rst

:Original: Documentation/admin-guide/reporting-issues.rst

:譯者:

 吳想成 Wu XiangCheng <bobwxc@email.cn>
 胡皓文 Hu Haowen <src.res@email.cn>

報告問題
+++++++++

簡明指南（亦即 太長不看）
==========================

您面臨的是否爲同系列穩定版或長期支持內核的普通內核的回歸？是否仍然受支持？
請搜索 `LKML內核郵件列表 <https://lore.kernel.org/lkml/>`_ 和
`Linux穩定版郵件列表 <https://lore.kernel.org/stable/>`_ 存檔中匹配的報告並
加入討論。如果找不到匹配的報告，請安裝該系列的最新版本。如果它仍然出現問題，
報告給穩定版郵件列表（stable@vger.kernel.org）。

在所有其他情況下，請儘可能猜測是哪個內核部分導致了問題。查看MAINTAINERS文件，
了解開發人員希望如何得知問題，大多數情況下，報告問題都是通過電子郵件和抄送
相關郵件列表進行的。檢查報告目的地的存檔中是否已有匹配的報告；也請搜索
`LKML <https://lore.kernel.org/lkml/>`_ 和網絡。如果找不到可加入的討論，請
安裝 `最新的主線內核 <https://kernel.org/>`_ 。如果仍存在問題，請發送報告。

問題已經解決了，但是您希望看到它在一個仍然支持的穩定版或長期支持系列中得到
解決？請安裝其最新版本。如果它出現了問題，那麼在主線中搜索修復它的更改，並
檢查是否正在回傳（backporting）或者已放棄；如果兩者都沒有，那麼可詢問處理
更改的人員。

**通用提醒** ：當安裝和測試上述內核時，請確保它是普通的（即：沒有補丁，也沒
有使用附加模塊）。還要確保它是在一個正常的環境中構建和運行，並且在問題發生
之前沒有被汙染（tainted）。

在編寫報告時，要涵蓋與問題相關的所有信息，如使用的內核和發行版。在碰見回歸時，
嘗試給出引入它的更改的提交ID，二分可以找到它。如果您同時面臨Linux內核的多個
問題，請分別報告每個問題。

一旦報告發出，請回答任何出現的問題，並儘可能地提供幫助。這包括通過不時重新
測試新版本並發送狀態更新來推動進展。

如何向內核維護人員報告問題的逐步指南
=====================================

上面的簡明指南概述了如何向Linux內核開發人員報告問題。對於已經熟悉向自由和開
源軟體（FLOSS）項目報告問題的人來說，這可能是他們所需要的全部內容。對於其他
人，本部分更爲詳細，並一步一步地描述。爲了便於閱讀，它仍然儘量簡潔，並省略
了許多細節；這些在逐步指南後的參考章節中進行了描述，該章節更詳細地解釋了每
個步驟。

注意：本節涉及的方面比簡明指南多，順序也稍有不同。這符合你的利益，以確保您
儘早意識到看起來像Linux內核毛病的問題可能實際上是由其他原因引起的。這些步驟
可以確保你最終不會覺得在這一過程中投入的時間是浪費：

 * 您是否面臨硬體或軟體供應商提供的Linux內核的問題？那麼基本上您最好停止閱讀
   本文檔，轉而向您的供應商報告問題，除非您願意自己安裝最新的Linux版本。尋找
   和解決問題往往需要後者。

 * 使用您喜愛的網絡搜尋引擎對現有報告進行粗略搜索；此外，請檢查
   `Linux內核郵件列表（LKML） <https://lore.kernel.org/lkml/>`_ 的存檔。如果
   找到匹配的報告，請加入討論而不是發送新報告。

 * 看看你正在處理的問題是否爲回歸問題、安全問題或非常嚴重的問題：這些都是需
   要在接下來的一些步驟中特別處理的「高優先級問題」。

 * 確保不是內核環境導致了您面臨的問題。

 * 創建一個新的備份，並將系統修復和恢復工具放在手邊。

 * 確保您的系統不會通過動態構建額外的內核模塊來增強其內核，像DKMS這樣的解決
   方案可能在您不知情的情況下就在本地進行了這樣的工作。

 * 當問題發生時，檢查您的內核是否被「汙染」，因爲使內核設置這個標誌的事件可能
   會導致您面臨的問題。

 * 粗略地寫下如何重現這個問題。如果您同時處理多個問題，請爲每個問題單獨寫注
   釋，並確保它們在新啓動的系統上獨立出現。這是必要的，因爲每個問題都需要分
   別報告給內核開發人員，除非它們嚴重糾纏在一起。

 * 如果您正面臨穩定版或長期支持版本線的回歸（例如從5.10.4更新到5.10.5時出現
   故障），請查看後文「報告穩定版和長期支持內核線的回歸」小節。

 * 定位可能引起問題的驅動程序或內核子系統。找出其開發人員期望的報告的方式和
   位置。注意：大多數情況下不會是 bugzilla.kernel.org，因爲問題通常需要通
   過郵件發送給維護人員和公共郵件列表。

 * 在缺陷追蹤器或問題相關郵件列表的存檔中徹底搜索可能與您的問題匹配的報告。
   如果你發現了一些相關討論，請加入討論而不是發送新的報告。

在完成這些準備之後，你將進入主要部分：

 * 除非您已經在運行最新的「主線」Linux內核，否則最好在報告流程前安裝它。在某些
   情況下，使用最新的「穩定版」Linux進行測試和報告也是可以接受的替代方案；在
   合併窗口期間，這實際上可能是最好的方法，但在開發階段最好還是暫停幾天。無論
   你選擇什麼版本，最好使用「普通」構建。忽略這些建議會大大增加您的報告被拒絕
   或忽略的風險。

 * 確保您剛剛安裝的內核在運行時不會「汙染」自己。

 * 在您剛剛安裝的內核中復現這個問題。如果它沒有出現，請查看下方只發生在
   穩定版和長期支持內核的問題的說明。

 * 優化你的筆記：試著找到並寫出最直接的復現問題的方法。確保最終結果包含所有
   重要的細節，同時讓第一次聽說的人容易閱讀和理解。如果您在此過程中學到了一
   些東西，請考慮再次搜索關於該問題的現有報告。

 * 如果失敗涉及「panic」、「Oops」、「warning」或「BUG」，請考慮解碼內核日誌以查找觸
   發錯誤的代碼行。

 * 如果您的問題是回歸問題，請儘可能縮小引入問題時的範圍。

 * 通過詳細描述問題來開始編寫報告。記得包括以下條目：您爲復現而安裝的最新內
   核版本、使用的Linux發行版以及關於如何復現該問題的說明。如果可能，將內核
   構建配置（.config）和 ``dmesg`` 的輸出放在網上的某個地方，並連結到它。包
   含或上傳所有其他可能相關的信息，如Oops的輸出/截圖或來自 ``lspci`` 的輸出
   。一旦你寫完了這個主要部分，請在上方插入一個正常長度的段落快速概述問題和
   影響。再在此之上添加一個簡單描述問題的句子，以得到人們的閱讀。現在給出一
   個更短的描述性標題或主題。然後就可以像MAINTAINERS文件告訴你的那樣發送或
   提交報告了，除非你在處理一個「高優先級問題」：它們需要按照下面「高優先級問
   題的特殊處理」所述特別關照。

 * 等待別人的反應，繼續推進事情，直到你能夠接受這樣或那樣的結果。因此，請公
   開和及時地回應任何詢問。測試提出的修復。積極地測試：至少重新測試每個新主
   線版本的首個候選版本（RC），並報告你的結果。如果出現拖延，就友好地提醒一
   下。如果你沒有得到任何幫助或者未能滿意，請試著自己幫助自己。

報告穩定版和長期支持內核線的回歸
----------------------------------

如果您發現了穩定版或長期支持內核版本線中的回歸問題並按上述流程跳到這裡，那麼
請閱讀本小節。即例如您在從5.10.4更新到5.10.5時出現了問題（從5.9.15到5.10.5則
不是）。開發人員希望儘快修復此類回歸，因此有一個簡化流程來報告它們：

 * 檢查內核開發人員是否仍然維護你關心的Linux內核版本線：去 `kernel.org 的首頁
   <https://kernel.org/>`_ ，確保此特定版本線的最新版沒有「[EOL]」標記。

 * 檢查 `Linux穩定版郵件列表 <https://lore.kernel.org/stable/>`_ 中的現有報告。

 * 從特定的版本線安裝最新版本作爲純淨內核。確保這個內核沒有被汙染，並且仍然
   存在問題，因爲問題可能已經在那裡被修復了。如果您第一次發現供應商內核的問題，
   請檢查已知最新版本的普通構建是否可以正常運行。

 * 向Linux穩定版郵件列表發送一個簡短的問題報告(stable@vger.kernel.org)。大致
   描述問題，並解釋如何復現。講清楚首個出現問題的版本和最後一個工作正常的版本。
   然後等待進一步的指示。

下面的參考章節部分詳細解釋了這些步驟中的每一步。

報告只發生在較舊內核版本線的問題
----------------------------------

若您嘗試了上述的最新主線內核，但未能在那裡復現問題，那麼本小節適用於您；以下
流程有助於使問題在仍然支持的穩定版或長期支持版本線，或者定期基於最新穩定版或
長期支持內核的供應商內核中得到修復。如果是這種情況，請執行以下步驟：

 * 請做好準備，接下來的幾個步驟可能無法在舊版本中解決問題：修復可能太大或太
   冒險，無法移植到那裡。

 * 執行前節「報告穩定版和長期支持內核線的回歸」中的前三個步驟。

 * 在Linux內核版本控制系統中搜索修復主線問題的更改，因爲它的提交消息可能會
   告訴你修復是否已經計劃好了支持。如果你沒有找到，搜索適當的郵件列表，尋找
   討論此類問題或同行評議可能修復的帖子；然後檢查討論是否認爲修復不適合支持。
   如果支持根本不被考慮，加入最新的討論，詢問是否有可能。

 * 前面的步驟之一應該會給出一個解決方案。如果仍未能成功，請向可能引起問題的
   子系統的維護人員詢問建議；抄送特定子系統的郵件列表以及穩定版郵件列表

下面的參考章節部分詳細解釋了這些步驟中的每一步。

參考章節：向內核維護者報告問題
===============================

上面的詳細指南簡要地列出了所有主要步驟，這對大多數人來說應該足夠了。但有時，
即使是有經驗的用戶也可能想知道如何實際執行這些步驟之一。這就是本節的目的，
因爲它將提供關於上述每個步驟的更多細節。請將此作爲參考文檔：可以從頭到尾
閱讀它。但它主要是爲了瀏覽和查找如何實際執行這些步驟的詳細信息。

在深入挖掘細節之前，我想先給你一些一般性建議：

 * Linux內核開發人員很清楚這個過程很複雜，比其他的FLOSS項目要求更多。我們很
   希望讓它更簡單。但這需要在不同的地方以及一些基礎設施上付諸努力，這些基礎
   設施需要持續的維護；尚未有人站出來做這些工作，所以目前情況就是這樣。

 * 與某些供應商簽訂的保證或支持合同並不能使您有權要求上游Linux內核社區的開
   發人員進行修復：這樣的合同完全在Linux內核、其開發社區和本文檔的範圍之外。
   這就是爲什麼在這種情況下，你不能要求任何契約保證，即使開發人員處理的問
   題對供應商有效。如果您想主張您的權利，使用供應商的支持渠道代替。當這樣做
   的時候，你可能想提出你希望看到這個問題在上游Linux內核中修復；可以這是確
   保最終修復將被納入所有Linux發行版的唯一方法來鼓勵他們。

 * 如果您從未向FLOSS項目報告過任何問題，那麼您應該考慮閱讀 `如何有效地報告
   缺陷 <https://www.chiark.greenend.org.uk/~sgtatham/bugs.html>`_ ， `如何
   以明智的方式提問 <http://www.catb.org/esr/faqs/smart-questions.html>`_ ，
   和 `如何提出好問題 <https://jvns.ca/blog/good-questions/>`_ 。

解決這些問題之後，可以在下面找到如何正確地向Linux內核報告問題的詳細信息。

確保您使用的是上游Linux內核
----------------------------

   *您是否面臨硬體或軟體供應商提供的Linux內核的問題？那麼基本上您最好停止閱
   讀本文檔，轉而向您的供應商報告問題，除非您願意自己安裝最新的Linux版本。
   尋找和解決問題往往需要後者。*

與大多數程式設計師一樣，Linux內核開發人員不喜歡花時間處理他們維護的原始碼中根本
不會發生的問題的報告。這只會浪費每個人的時間，尤其是你的時間。不幸的是，當
涉及到內核時，這樣的情況很容易發生，並且常常導致雙方氣餒。這是因爲幾乎所有預
裝在設備（台式機、筆記本電腦、智慧型手機、路由器等）上的Linux內核，以及大多數
由Linux發行商提供的內核，都與由kernel.org發行的官方Linux內核相距甚遠：從Linux
開發的角度來看，這些供應商提供的內核通常是古老的或者經過了大量修改，通常兩點
兼具。

大多數供應商內核都不適合用來向Linux內核開發人員報告問題：您在其中遇到的問題
可能已經由Linux內核開發人員在數月或數年前修復；此外，供應商的修改和增強可能
會導致您面臨的問題，即使它們看起來很小或者完全不相關。這就是爲什麼您應該向
供應商報告這些內核的問題。它的開發者應該查看報告，如果它是一個上游問題，直接
於上游修復或將報告轉發到那裡。在實踐中，這有時行不通。因此，您可能需要考慮
通過自己安裝最新的Linux內核內核來繞過供應商。如果如果您選擇此方法，那麼本指
南後面的步驟將解釋如何在排除了其他可能導致您的問題的原因後執行此操作。

注意前段使用的詞語是「大多數」，因爲有時候開發人員實際上願意處理供應商內核出現
的問題報告。他們是否這麼做很大程度上取決於開發人員和相關問題。如果發行版只
根據最近的Linux版本對內核進行了較小修改，那麼機會就比較大；例如對於Debian
GNU/Linux Sid或Fedora Rawhide所提供的主線內核。一些開發人員還將接受基於最新
穩定內核的發行版內核問題報告，只要它改動不大；例如Arch Linux、常規Fedora版本
和openSUSE Turboweed。但是請記住，您最好使用主線Linux，並避免在此流程中使用
穩定版內核，如「安裝一個新的內核進行測試」一節中所詳述。

當然，您可以忽略所有這些建議，並向上游Linux開發人員報告舊的或經過大量修改的
供應商內核的問題。但是注意，這樣的報告經常被拒絕或忽視，所以自行小心考慮一下。
不過這還是比根本不報告問題要好：有時候這樣的報告會直接或間接地幫助解決之後的
問題。

搜索現有報告（第一部分）
-------------------------

    *使用您喜愛的網絡搜尋引擎對現有報告進行粗略搜索；此外，請檢查Linux內核
    郵件列表（LKML）的存檔。如果找到匹配的報告，請加入討論而不是發送新報告。*

報告一個別人已經提出的問題，對每個人來說都是浪費時間，尤其是作爲報告人的你。
所以徹底檢查是否有人已經報告了這個問題，這對你自己是有利的。在流程中的這一步，
可以只執行一個粗略的搜索：一旦您知道您的問題需要報告到哪裡，稍後的步驟將告訴
您如何詳細搜索。儘管如此，不要倉促完成這一步，它可以節省您的時間和減少麻煩。

只需先用你最喜歡的搜尋引擎在網際網路上搜索。然後再搜索Linux內核郵件列表（LKML）
存檔。

如果搜索結果實在太多，可以考慮讓你的搜尋引擎將搜索時間範圍限制在過去的一個
月或一年。而且無論你在哪裡搜索，一定要用恰當的搜索關鍵詞；也要變化幾次關鍵
詞。同時，試著從別人的角度看問題：這將幫助你想出其他的關鍵詞。另外，一定不
要同時使用過多的關鍵詞。記住搜索時要同時嘗試包含和不包含內核驅動程序的名稱
或受影響的硬體組件的名稱等信息。但其確切的品牌名稱（比如說「華碩紅魔 Radeon
RX 5700 XT Gaming OC」）往往幫助不大，因爲它太具體了。相反，嘗試搜索術語，如
型號（Radeon 5700 或 Radeon 5000）和核心代號（「Navi」或「Navi10」），以及包含
和不包含其製造商（「AMD」）。

如果你發現了關於你的問題的現有報告，請加入討論，因爲你可能會提供有價值的額
外信息。這一點很重要，即使是在修復程序已經準備好或處於最後階段，因爲開發人
員可能會尋找能夠提供額外信息或測試建議修復程序的人。跳到「發布報告後的責任」
一節，了解有關如何正確參與的細節。

注意，搜索 `bugzilla.kernel.org <https://bugzilla.kernel.org/>`_ 網站可能
也是一個好主意，因爲這可能會提供有價值的見解或找到匹配的報告。如果您發現後者，
請記住：大多數子系統都希望在不同的位置報告，如下面「你需要將問題報告到何處」
一節中所述。因此本應處理這個問題的開發人員甚至可能不知道bugzilla的工單。所以
請檢查工單中的問題是否已經按照本文檔所述得到報告，如果沒有，請考慮這樣做。

高優先級的問題？
-----------------

    *看看你正在處理的問題是否是回歸問題、安全問題或非常嚴重的問題：這些都是
    需要在接下來的一些步驟中特別處理的「高優先級問題」。*

Linus Torvalds和主要的Linux內核開發人員希望看到一些問題儘快得到解決，因此在
報告過程中有一些「高優先級問題」的處理略有不同。有三種情況符合條件:回歸、安全
問題和非常嚴重的問題。

如果在舊版本的Linux內核中工作的東西不能在新版本的Linux內核中工作，或者某種
程度上在新版本的Linux內核中工作得更差，那麼你就需要處理「回歸」。因此，當一個
在Linux 5.7中表現良好的WiFi驅動程序在5.8中表現不佳或根本不能工作時，這是一
種回歸。如果應用程式在新的內核中出現不穩定的現象，這也是一種回歸，這可能是
由於內核和用戶空間之間的接口（如procfs和sysfs）發生不兼容的更改造成的。顯著
的性能降低或功耗增加也可以稱爲回歸。但是請記住:新內核需要使用與舊內核相似的
配置來構建（參見下面如何實現這一點）。這是因爲內核開發人員在實現新特性時有
時無法避免不兼容性；但是爲了避免回歸，這些特性必須在構建配置期間顯式地啓用。

什麼是安全問題留給您自己判斷。在繼續之前，請考慮閱讀
「Documentation/translations/zh_TW/admin-guide/security-bugs.rst」，
因爲它提供了如何最恰當地處理安全問題的額外細節。

當發生了完全無法接受的糟糕事情時，此問題就是一個「非常嚴重的問題」。例如，
Linux內核破壞了它處理的數據或損壞了它運行的硬體。當內核突然顯示錯誤消息
（「kernel panic」）並停止工作，或者根本沒有任何停止信息時，您也在處理一個嚴重
的問題。注意：不要混淆「panic」（內核停止自身的致命錯誤）和「Oops」（可恢復錯誤），
因爲顯示後者之後內核仍然在運行。

確保環境健康
--------------

    *確保不是內核所處環境導致了你所面臨的問題。*

看起來很像內核問題的問題有時是由構建或運行時環境引起的。很難完全排除這種問
題，但你應該儘量減少這種問題：

 * 構建內核時，請使用經過驗證的工具，因爲編譯器或二進位文件中的錯誤可能會導
   致內核出現錯誤行爲。

 * 確保您的計算機組件在其設計規範內運行；這對處理器、內存和主板尤爲重要。因
   此，當面臨潛在的內核問題時，停止低電壓或超頻。

 * 儘量確保不是硬體故障導致了你的問題。例如，內存損壞會導致大量的問題，這些
   問題會表現爲看起來像內核問題。

 * 如果你正在處理一個文件系統問題，你可能需要用 ``fsck`` 檢查一下文件系統，
   因爲它可能會以某種方式被損壞，從而導致無法預期的內核行爲。

 * 在處理回歸問題時，要確保沒有在更新內核的同時發生了其他變化。例如，這個問
   題可能是由同時更新的其他軟體引起的。也有可能是在你第一次重啓進入新內核時，
   某個硬體巧合地壞了。更新系統 BIOS 或改變 BIOS 設置中的某些內容也會導致
   一些看起來很像內核回歸的問題。

爲緊急情況做好準備
-------------------

    *創建一個全新的備份，並將系統修復和還原工具放在手邊*

我得提醒您，您正在和計算機打交道，計算機有時會出現意想不到的事情，尤其是當
您折騰其作業系統的內核等關鍵部件時。而這就是你在這個過程中要做的事情。因此，
一定要創建一個全新的備份；還要確保你手頭有修復或重裝作業系統的所有工具，
以及恢復備份所需的一切。

確保你的內核不會被增強
------------------------

    *確保您的系統不會通過動態構建額外的內核模塊來增強其內核，像DKMS這樣的解
    決方案可能在您不知情的情況下就在本地進行了這樣的工作。*

如果內核以任何方式得到增強，那麼問題報告被忽略或拒絕的風險就會急劇增加。這就
是爲什麼您應該刪除或禁用像akmods和DKMS這樣的機制：這些機制會自動構建額外內核
模塊，例如當您安裝新的Linux內核或第一次引導它時。也要記得同時刪除他們可能安裝
的任何模塊。然後重新啓動再繼續。

注意，你可能不知道你的系統正在使用這些解決方案之一：當你安裝 Nvidia 專有圖
形驅動程序、VirtualBox 或其他需要 Linux 內核以外的模塊支持的軟體時，它們通
常會靜默設置。這就是爲什麼你可能需要卸載這些軟體的軟體包，以擺脫任何第三方
內核模塊。

檢測「汙染」標誌
----------------

    *當問題發生時，檢查您的內核是否被「汙染」，因爲使內核設置這個標誌的事件可
    能會導致您面臨的問題。*

當某些可能會導致看起來完全不相關的後續錯誤的事情發生時，內核會用「汙染
（taint）」標誌標記自己。如果您的內核受到汙染，那麼您面臨的可能是這樣的錯誤。
因此在投入更多時間到這個過程中之前，儘早排除此情況可能對你有好處。這是這個
步驟出現在這裡的唯一原因，因爲這個過程稍後會告訴您安裝最新的主線內核；然後
您將需要再次檢查汙染標誌，因爲當它出問題的時候內核報告會關注它。

在正在運行的系統上檢查內核是否汙染非常容易：如果 ``cat /proc/sys/kernel/tainted``
返回「0」，那麼內核沒有被汙染，一切正常。在某些情況下無法檢查該文件；這就是
爲什麼當內核報告內部問題（「kernel bug」）、可恢復錯誤（「kernel Oops」）或停止
操作前不可恢復的錯誤（「kernel panic」）時，它也會提到汙染狀態。當其中一個錯
誤發生時，查看列印的錯誤消息的頂部，搜索以「CPU:」開頭的行。如果發現問題時內
核未被汙染，那麼它應該以「Not infected」結束；如果你看到「Tainted:」且後跟一些
空格和字母，那就被汙染了。

如果你的內核被汙染了，請閱讀「Documentation/translations/zh_TW/admin-guide/tainted-kernels.rst」
以找出原因。設法消除汙染因素。通常是由以下三種因素之一引起的：

 1. 發生了一個可恢復的錯誤（「kernel Oops」），內核汙染了自己，因爲內核知道在
    此之後它可能會出現奇怪的行爲錯亂。在這種情況下，檢查您的內核或系統日誌，
    並尋找以下列文字開頭的部分::

       Oops: 0000 [#1] SMP

    如方括號中的「#1」所示，這是自啓動以來的第一次Oops。每個Oops和此後發生的
    任何其他問題都可能是首個Oops的後續問題，即使這兩個問題看起來完全不相關。
    通過消除首個Oops的原因並在之後復現該問題，可以排除這種情況。有時僅僅
    重新啓動就足夠了，有時更改配置後重新啓動可以消除Oops。但是在這個流程中
    不要花費太多時間在這一點上，因爲引起Oops的原因可能已經在您稍後將按流程
    安裝的新Linux內核版本中修復了。

 2. 您的系統使用的軟體安裝了自己的內核模塊，例如Nvidia的專有圖形驅動程序或
    VirtualBox。當內核從外部源（即使它們是開源的）加載此類模塊時，它會汙染
    自己：它們有時會在不相關的內核區域導致錯誤，從而可能導致您面臨的問題。
    因此，當您想要向Linux內核開發人員報告問題時，您必須阻止這些模塊加載。
    大多數情況下最簡單的方法是：臨時卸載這些軟體，包括它們可能已經安裝的任
    何模塊。之後重新啓動。

 3. 當內核加載駐留在Linux內核原始碼staging樹中的模塊時，它也會汙染自身。這
    是一個特殊的區域，代碼（主要是驅動程序）還沒有達到正常Linux內核的質量
    標準。當您報告此種模塊的問題時，內核受到汙染顯然是沒有問題的；只需確保
    問題模塊是造成汙染的唯一原因。如果問題發生在一個不相關的區域，重新啓動
    並通過指定 ``foo.blacklist=1`` 作爲內核參數臨時阻止該模塊被加載（用有
    問題的模塊名替換「foo」）。

記錄如何重現問題
------------------

    *粗略地寫下如何重現這個問題。如果您同時處理多個問題，請爲每個問題單獨寫
    注釋，並確保它們在新啓動的系統上獨立出現。這是必要的，因爲每個問題都需
    要分別報告給內核開發人員，除非它們嚴重糾纏在一起。*

如果你同時處理多個問題，必須分別報告每個問題，因爲它們可能由不同的開發人員
處理。在一份報告中描述多種問題，也會讓其他人難以將其分開。因此只有在問題嚴
重糾纏的情況下，才能將問題合併在一份報告中。

此外，在報告過程中，你必須測試該問題是否發生在其他內核版本上。因此，如果您
知道如何在一個新啓動的系統上快速重現問題，將使您的工作更加輕鬆。

注意：報告只發生過一次的問題往往是沒有結果的，因爲它們可能是由於宇宙輻射導
致的位翻轉。所以你應該嘗試通過重現問題來排除這種情況，然後再繼續。如果你有
足夠的經驗來區分由於硬體故障引起的一次性錯誤和難以重現的罕見內核問題，可以
忽略這個建議。

穩定版或長期支持內核的回歸？
-----------------------------

    *如果您正面臨穩定版或長期支持版本線的回歸（例如從5.10.4更新到5.10.5時出現
    故障），請查看後文「報告穩定版和長期支持內核線的回歸」小節。*

穩定版和長期支持內核版本線中的回歸是Linux開發人員非常希望解決的問題，這樣的
問題甚至比主線開發分支中的回歸更不應出現，因爲它們會很快影響到很多人。開發人員
希望儘快了解此類問題，因此有一個簡化流程來報告這些問題。注意，使用更新內核版
本線的回歸（比如從5.9.15切換到5.10.5時出現故障）不符合條件。

你需要將問題報告到何處
------------------------

    *定位可能引起問題的驅動程序或內核子系統。找出其開發人員期望的報告的方式
    和位置。注意：大多數情況下不會是bugzilla.kernel.org，因爲問題通常需要通
    過郵件發送給維護人員和公共郵件列表。*

將報告發送給合適的人是至關重要的，因爲Linux內核是一個大項目，大多數開發人員
只熟悉其中的一小部分。例如，相當多的程式設計師只關心一個驅動程序，比如一個WiFi
晶片驅動程序；它的開發人員可能對疏遠的或不相關的「子系統」（如TCP堆棧、
PCIe/PCI子系統、內存管理或文件系統）的內部知識了解很少或完全不了解。

問題在於：Linux內核缺少一個，可以簡單地將問題歸檔並讓需要了解它的開發人員了
解它的，中心化缺陷跟蹤器。這就是爲什麼你必須找到正確的途徑來自己報告問題。
您可以在腳本的幫助下做到這一點（見下文），但它主要針對的是內核開發人員和專
家。對於其他人來說，MAINTAINERS（維護人員）文件是更好的選擇。

如何閱讀MAINTAINERS維護者文件
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

爲了說明如何使用 :ref:`MAINTAINERS <maintainers>` 文件，讓我們假設您的筆記
本電腦中的WiFi在更新內核後突然出現了錯誤行爲。這種情況下可能是WiFi驅動的問
題。顯然，它也可能由於驅動基於的某些代碼，但除非你懷疑有這樣的東西會附著在
驅動程序上。如果真的是其他的問題，驅動程序的開發人員會讓合適的人參與進來。

遺憾的是，沒有通用且簡單的辦法來檢查哪個代碼驅動了特定硬體組件。

在WiFi驅動出現問題的情況下，你可能想查看 ``lspci -k`` 的輸出，因爲它列出了
PCI/PCIe總線上的設備和驅動它的內核模塊::

       [user@something ~]$ lspci -k
       [...]
       3a:00.0 Network controller: Qualcomm Atheros QCA6174 802.11ac Wireless Network Adapter (rev 32)
         Subsystem: Bigfoot Networks, Inc. Device 1535
         Kernel driver in use: ath10k_pci
         Kernel modules: ath10k_pci
       [...]

但如果你的WiFi晶片通過USB或其他內部總線連接，這種方法就行不通了。在這種情況
下，您可能需要檢查您的WiFi管理器或 ``ip link`` 的輸出。尋找有問題的網絡接口
的名稱，它可能類似於「wlp58s0」。此名稱可以用來找到驅動它的模塊::

       [user@something ~]$ realpath --relative-to=/sys/module//sys/class/net/wlp58s0/device/driver/module
       ath10k_pci

如果這些技巧不能進一步幫助您，請嘗試在網上搜索如何縮小相關驅動程序或子系統
的範圍。如果你不確定是哪一個：試著猜一下，即使你猜得不好，也會有人會幫助你
的。

一旦您知道了相應的驅動程序或子系統，您就希望在MAINTAINERS文件中搜索它。如果
是「ath10k_pci」，您不會找到任何東西，因爲名稱太具體了。有時你需要在網上尋找
幫助；但在此之前，請嘗試使用一個稍短或修改過的名稱來搜索MAINTAINERS文件，因
爲這樣你可能會發現類似這樣的東西::

       QUALCOMM ATHEROS ATH10K WIRELESS DRIVER
       Mail:          A. Some Human <shuman@example.com>
       Mailing list:  ath10k@lists.infradead.org
       Status:        Supported
       Web-page:      https://wireless.wiki.kernel.org/en/users/Drivers/ath10k
       SCM:           git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git
       Files:         drivers/net/wireless/ath/ath10k/

注意：如果您閱讀在Linux原始碼樹的根目錄中找到的原始維護者文件，則行描述將是
縮寫。例如，「Mail:（郵件）」將是「M:」，「Mailing list:（郵件列表）」將是「L」，
「Status:（狀態）」將是「S:」。此文件頂部有一段解釋了這些和其他縮寫。

首先查看「Status」狀態行。理想情況下，它應該得到「Supported（支持）」或
「Maintained（維護）」。如果狀態爲「Obsolete（過時的）」，那麼你在使用一些過時的
方法，需要轉換到新的解決方案上。有時候，只有在感到有動力時，才會有人爲代碼
提供「Odd Fixes」。如果碰見「Orphan」，你就完全不走運了，因爲再也沒有人關心代碼
了，只剩下這些選項:準備好與問題共存，自己修復它，或者找一個願意修復它的程式設計師。

檢查狀態後，尋找以「bug:」開頭的一行：它將告訴你在哪裡可以找到子系統特定的缺
陷跟蹤器來提交你的問題。上面的例子沒有此行。大多數部分都是這樣，因爲 Linux
內核的開發完全是由郵件驅動的。很少有子系統使用缺陷跟蹤器，且其中只有一部分
依賴於 bugzilla.kernel.org。

在這種以及其他很多情況下，你必須尋找以「Mail:」開頭的行。這些行提到了特定代碼
的維護者的名字和電子郵件地址。也可以查找以「Mailing list:」開頭的行，它告訴你
開發代碼的公共郵件列表。你的報告之後需要通過郵件發到這些地址。另外，對於所有
通過電子郵件發送的問題報告，一定要抄送 Linux Kernel Mailing List（LKML）
<linux-kernel@vger.kernel.org>。在以後通過郵件發送問題報告時，不要遺漏任何
一個郵件列表!維護者都是大忙人，可能會把一些工作留給子系統特定列表上的其他開
發者；而 LKML 很重要，因爲需要一個可以找到所有問題報告的地方。

藉助腳本找到維護者
~~~~~~~~~~~~~~~~~~~~

對於手頭有Linux源碼的人來說，有第二個可以找到合適的報告地點的選擇：腳本
「scripts/get_maintainer.pl」，它嘗試找到所有要聯繫的人。它會查詢MAINTAINERS
文件，並需要用相關原始碼的路徑來調用。對於編譯成模塊的驅動程序，經常可以用
這樣的命令找到::

       $ modinfo ath10k_pci | grep filename | sed 's!/lib/modules/.*/kernel/!!; s!filename:!!; s!\.ko\(\|\.xz\)!!'
       drivers/net/wireless/ath/ath10k/ath10k_pci.ko

將其中的部分內容傳遞給腳本::

       $ ./scripts/get_maintainer.pl -f drivers/net/wireless/ath/ath10k*
       Some Human <shuman@example.com> (supporter:QUALCOMM ATHEROS ATH10K WIRELESS DRIVER)
       Another S. Human <asomehuman@example.com> (maintainer:NETWORKING DRIVERS)
       ath10k@lists.infradead.org (open list:QUALCOMM ATHEROS ATH10K WIRELESS DRIVER)
       linux-wireless@vger.kernel.org (open list:NETWORKING DRIVERS (WIRELESS))
       netdev@vger.kernel.org (open list:NETWORKING DRIVERS)
       linux-kernel@vger.kernel.org (open list)

不要把你的報告發給所有的人。發送給維護者，腳本稱之爲「supporter:」；另外抄送
代碼最相關的郵件列表，以及 Linux 內核郵件列表（LKML）。在此例中，你需要將報
告發送給 「Some Human <shuman@example.com>」 ，並抄送
「ath10k@lists.infradead.org」和「linux-kernel@vger.kernel.org」。

注意：如果你用 git 克隆了 Linux 原始碼，你可能需要用--git 再次調用
get_maintainer.pl。腳本會查看提交歷史，以找到最近哪些人參與了相關代碼的編寫，
因爲他們可能會提供幫助。但要小心使用這些結果，因爲它很容易讓你誤入歧途。
例如，這種情況常常會發生在很少被修改的地方（比如老舊的或未維護的驅動程序）：
有時這樣的代碼會在樹級清理期間被根本不關心此驅動程序的開發者修改。

搜索現有報告（第二部分）
--------------------------

    *在缺陷追蹤器或問題相關郵件列表的存檔中徹底搜索可能與您的問題匹配的報告。
    如果找到匹配的報告，請加入討論而不是發送新報告。*

如前所述：報告一個別人已經提出的問題，對每個人來說都是浪費時間，尤其是作爲報告
人的你。這就是爲什麼你應該再次搜索現有的報告。現在你已經知道問題需要報告到哪裡。
如果是郵件列表，那麼一般在 `lore.kernel.org <https://lore.kernel.org/>`_ 可以
找到相應存檔。

但有些列表運行在其他地方。例如前面步驟中當例子的ath10k WiFi驅動程序就是這種
情況。但是你通常可以在網上很容易地找到這些列表的檔案。例如搜索「archive
ath10k@lists.infradead.org」，將引導您到ath10k郵件列表的信息頁，該頁面頂部連結
到其 `列表存檔 <https://lists.infradead.org/pipermail/ath10k/>`_ 。遺憾的是，
這個列表和其他一些列表缺乏搜索其存檔的功能。在這種情況下可以使用常規的網際網路
搜尋引擎，並添加類似「site:lists.infadead.org/pipermail/ath10k/」這
樣的搜索條件，這會把結果限制在該連結中的檔案。

也請進一步搜索網絡、LKML和bugzilla.kernel.org網站。

有關如何搜索以及在找到匹配報告時如何操作的詳細信息，請參閱上面的「搜索現有報告
（第一部分）」。

不要急著完成報告過程的這一步：花30到60分鐘甚至更多的時間可以爲你和其他人節省 /
減少相當多的時間和麻煩。

安裝一個新的內核進行測試
--------------------------

    *除非您已經在運行最新的「主線」Linux內核，否則最好在報告流程前安裝它。在
    某些情況下，使用最新的「穩定版」Linux進行測試和報告也是可以接受的替代方案；
    在合併窗口期間，這實際上可能是最好的方法，但在開發階段最好還是暫停幾天。
    無論你選擇什麼版本，最好使用「普通」構建。忽略這些建議會大大增加您的報告
    被拒絕或忽略的風險。*

正如第一步的詳細解釋中所提到的：與大多數程式設計師一樣，與大多數程式設計師一樣，Linux
內核開發人員不喜歡花時間處理他們維護的原始碼中根本不會發生的問題的報告。這隻
會浪費每個人的時間，尤其是你的時間。這就是爲什麼在報告問題之前，您必須先確認
問題仍然存在於最新的上游代碼中，這符合每個人的利益。您可以忽略此建議，但如前
所述：這樣做會極大地增加問題報告被拒絕或被忽略的風險。

內核「最新上游」的範圍通常指：

 * 安裝一個主線內核；最新的穩定版內核也可以是一個選擇，但大多數時候都最好避免。
   長期支持內核（有時稱爲「LTS內核」）不適合此流程。下一小節將更詳細地解釋所有
   這些。

 * 下一小節描述獲取和安裝這樣一個內核的方法。它還指出了使用預編譯內核是可以的，
   但普通的內核更好，這意味著：它是直接使用從 `kernel.org <https://kernel.org/>`_
   獲得的Linux原始碼構建並且沒有任何方式修改或增強。

選擇適合測試的版本
~~~~~~~~~~~~~~~~~~~~

前往 `kernel.org <https://kernel.org/>`_ 來決定使用哪個版本。忽略那個寫著
「Latest release最新版本」的巨大黃色按鈕，往下看有一個表格。在表格的頂部，你會
看到一行以「mainline」開頭的字樣，大多數情況下它會指向一個版本號類似「5.8-rc2」
的預發布版本。如果是這樣的話，你將需要使用這個主線內核進行測試。不要讓「rc」
嚇到你，這些「開發版內核」實際上非常可靠——而且你已經按照上面的指示做了備份，
不是嗎？

大概每九到十周，「mainline」可能會給你指出一個版本號類似「5.7」的正式版本。如果
碰見這種情況，請考慮暫停報告過程，直到下一個版本的第一個預發布（5.8-rc1）出
現在 `kernel.org <https://kernel.org/>`_ 上。這是因爲 Linux 的開發周期正在
兩周的「合併窗口」內。大部分的改動和所有干擾性的改動都會在這段時間內被合併到
下一個版本中。在此期間使用主線是比較危險的。內核開發者通常也很忙，可能沒有
多餘的時間來處理問題報告。這也是很有可能在合併窗口中應用了許多修改來修復你
所面臨的問題；這就是爲什麼你很快就得用一個新的內核版本重新測試，就像下面「發
布報告後的責任」一節中所述的那樣。

這就是爲什麼要等到合併窗口結束後才去做。但是如果你處理的是一些不應該等待的
東西，則無需這樣做。在這種情況下，可以考慮通過 git 獲取最新的主線內核（見下
文），或者使用 kernel.org 上提供的最新穩定版本。如果 mainline 因爲某些原因
不無法正常工作，那麼使用它也是可以接受的。總的來說：用它來重現問題也比完全
不報告問題要好。

最好避免在合併窗口外使用最新的穩定版內核，因爲所有修復都必須首先應用於主線。
這就是爲什麼檢查最新的主線內核是如此重要：你希望看到在舊版本線修復的任何問題
需要先在主線修復，然後才能得到回傳，這可能需要幾天或幾周。另一個原因是：您
希望的修復對於回傳來說可能太難或太冒險；因此再次報告問題不太可能改變任何事情。

這些方面也部分表明了爲什麼長期支持內核（有時稱爲「LTS內核」）不適合報告流程：
它們與當前代碼的距離太遠。因此，先去測試主線，然後再按流程走：如果主線沒有
出現問題，流程將指導您如何在舊版本線中修復它。

如何獲得新的 Linux 內核
~~~~~~~~~~~~~~~~~~~~~~~~~

你可以使用預編譯或自編譯的內核進行測試；如果你選擇後者，可以使用 git 獲取源
代碼，或者下載其 tar 存檔包。

**使用預編譯的內核** ：這往往是最快速、最簡單、最安全的方法——尤其是在你不熟
悉 Linux 內核的情況下。問題是：發行商或附加存儲庫提供的大多數版本都是從修改
過的Linux原始碼構建的。因此它們不是普通的，通常不適合於測試和問題報告：這些
更改可能會導致您面臨的問題或以某種方式影響問題。

但是如果您使用的是流行的Linux發行版，那麼您就很幸運了：對於大部分的發行版，
您可以在網上找到包含最新主線或穩定版本Linux內核包的存儲庫。使用這些是完全可
以的，只要從存儲庫的描述中確認它們是普通的或者至少接近普通。此外，請確保軟體
包包含kernel.org上提供的最新版本內核。如果這些軟體包的時間超過一周，那麼它們
可能就不合適了，因爲新的主線和穩定版內核通常至少每周發布一次。

請注意，您以後可能需要手動構建自己的內核：有時這是調試或測試修復程序所必需的，
如後文所述。還要注意，預編譯的內核可能缺少在出現panic、Oops、warning或BUG時
解碼內核列印的消息所需的調試符號；如果您計劃解碼這些消息，最好自己編譯內核
（有關詳細信息，請參閱本小節結尾和「解碼失敗信息」小節）。

**使用git** ：熟悉 git 的開發者和有經驗的 Linux 用戶通常最好直接從
`kernel.org 上的官方開發倉庫
<https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/>`_
中獲取最新的 Linux 內核原始碼。這些很可能比最新的主線預發布版本更新一些。不
用擔心：它們和正式的預發布版本一樣可靠，除非內核的開發周期目前正處於合併窗
口中。不過即便如此，它們也是相當可靠的。

**常規方法** ：不熟悉 git 的人通常最好從 `kernel.org <https://kernel.org/>`_
下載源碼的tar 存檔包。

如何實際構建一個內核並不在這裡描述，因爲許多網站已經解釋了必要的步驟。如果
你是新手，可以考慮按照那些建議使用 ``make localmodconfig`` 來做，它將嘗試獲
取你當前內核的配置，然後根據你的系統進行一些調整。這樣做並不能使編譯出來的
內核更好，但可以更快地編譯。

注意：如果您正在處理來自內核的pannc、Oops、warning或BUG，請在配置內核時嘗試
啓用 CONFIG_KALLSYMS 選項。此外，還可以啓用 CONFIG_DEBUG_KERNEL 和
CONFIG_DEBUG_INFO；後者是相關選項，但只有啓用前者才能開啓。請注意，
CONFIG_DEBUG_INFO 會需要更多儲存空間來構建內核。但這是值得的，因爲這些選項將
允許您稍後精確定位觸發問題的確切代碼行。下面的「解碼失敗信息」一節對此進行了更
詳細的解釋。

但請記住：始終記錄遇到的問題，以防難以重現。發送未解碼的報告總比不報告要好。

檢查「汙染」標誌
----------------

    *確保您剛剛安裝的內核在運行時不會「汙染」自己。*

正如上面已經詳細介紹過的：當發生一些可能會導致一些看起來完全不相關的後續錯
誤的事情時，內核會設置一個「汙染」標誌。這就是爲什麼你需要檢查你剛剛安裝的內
核是否有設置此標誌。如果有的話，幾乎在任何情況下你都需要在報告問題之前先消
除它。詳細的操作方法請看上面的章節。

用新內核重現問題
------------------

    *在您剛剛安裝的內核中復現這個問題。如果它沒有出現，請查看下方只發生在
    穩定版和長期支持內核的問題的說明。*

檢查這個問題是否發生在你剛剛安裝的新 Linux 內核版本上。如果新內核已經修復了，
可以考慮使用此版本線，放棄報告問題。但是請記住，只要它沒有在 `kernel.org
<https://kernel.org/>`_ 的穩定版和長期版（以及由這些版本衍生出來的廠商內核）
中得到修復，其他用戶可能仍然會受到它的困擾。如果你喜歡使用其中的一個，或
者只是想幫助它們的用戶，請前往下面的「報告只發生在較舊內核版本線的問題」一節。

優化復現問題的描述
--------------------

    *優化你的筆記：試著找到並寫出最直接的復現問題的方法。確保最終結果包含所
    有重要的細節，同時讓第一次聽說的人容易閱讀和理解。如果您在此過程中學到
    了一些東西，請考慮再次搜索關於該問題的現有報告。*

過於複雜的報告會讓別人很難理解。因此請儘量找到一個可以直接描述、易於以書面
形式理解的再現方法。包含所有重要的細節，但同時也要儘量保持簡短。

在這在前面的步驟中，你很可能已經了解了一些關於你所面臨的問題的點。利用這些
知識，再次搜索可以轉而加入的現有報告。

解碼失敗信息
-------------

    *如果失敗涉及「panic」、「Oops」、「warning」或「BUG」，請考慮解碼內核日誌以查找
    觸發錯誤的代碼行。*

當內核檢測到內部問題時，它會記錄一些有關已執行代碼的信息。這使得在原始碼中精
確定位觸發問題的行並顯示如何調用它成爲可能。但只有在配置內核時啓用了
CONFIG_DEBUG_INFO 和 CONFIG_KALLSYMS選項時，這種方法才起效。如果已啓用此選項，
請考慮解碼內核日誌中的信息。這將使我們更容易理解是什麼導致了「panic」、「Oops」、
「warning」或「BUG」，從而增加了有人提供修復的機率。

解碼可以通過Linux原始碼樹中的腳本來完成。如果您運行的內核是之前自己編譯的，
這樣這樣調用它::

	[user@something ~]$ sudo dmesg | ./linux-5.10.5/scripts/decode_stacktrace.sh ./linux-5.10.5/vmlinux
	/usr/lib/debug/lib/modules/5.10.10-4.1.x86_64/vmlinux /usr/src/kernels/5.10.10-4.1.x86_64/

如果您運行的是打包好的普通內核，則可能需要安裝帶有調試符號的相應包。然後按以下
方式調用腳本（如果發行版未打包，則可能需要從Linux原始碼獲取）::

	[user@something ~]$ sudo dmesg | ./linux-5.10.5/scripts/decode_stacktrace.sh \
	/usr/lib/debug/lib/modules/5.10.10-4.1.x86_64/vmlinux /usr/src/kernels/5.10.10-4.1.x86_64/

腳本將解碼如下的日誌行，這些日誌行顯示內核在發生錯誤時正在執行的代碼的地址::

	[   68.387301] RIP: 0010:test_module_init+0x5/0xffa [test_module]

解碼之後，這些行將變成這樣::

	[   68.387301] RIP: 0010:test_module_init (/home/username/linux-5.10.5/test-module/test-module.c:16) test_module

在本例中，執行的代碼是從文件「~/linux-5.10.5/test-module/test-module.c」構建的，
錯誤出現在第16行的指令中。

該腳本也會如此解碼以「Call trace」開頭的部分中提到的地址，該部分顯示出現問題的
函數的路徑。此外，腳本還會顯示內核正在執行的代碼部分的彙編輸出。

注意，如果你沒法做到這一點，只需跳過這一步，並在報告中說明原因。如果你幸運的
話，可能無需解碼。如果需要的話，也許有人會幫你做這件事情。還要注意，這只是解
碼內核堆棧跟蹤的幾種方法之一。有時需要採取不同的步驟來檢索相關的詳細信息。
別擔心，如果您碰到的情況需要這樣做，開發人員會告訴您該怎麼做。

對回歸的特別關照
-----------------

    *如果您的問題是回歸問題，請儘可能縮小引入問題時的範圍。*

Linux 首席開發者 Linus Torvalds 認爲 Linux 內核永遠不應惡化，這就是爲什麼他
認爲回歸是不可接受的，並希望看到它們被迅速修復。這就是爲什麼引入了回歸的改
動導致的問題若無法通過其他方式快速解決，通常會被迅速撤銷。因此，報告回歸有
點像「王炸」，會迅速得到修復。但要做到這一點，需要知道導致回歸的變化。通常情
況下，要由報告者來追查罪魁禍首，因爲維護者往往沒有時間或手頭設置不便來自行
重現它。

有一個叫做「二分」的過程可以來尋找變化，這在
「Documentation/translations/zh_TW/admin-guide/bug-bisect.rst」文檔中進行了詳細
的描述，這個過程通常需要你構建十到二十個內核鏡像，每次都嘗試在構建下一個鏡像
之前重現問題。是的，這需要花費一些時間，但不用擔心，它比大多數人想像的要快得多。
多虧了「binary search二進位搜索」，這將引導你在原始碼管理系統中找到導致回歸的提交。
一旦你找到它，就在網上搜索其主題、提交ID和縮短的提交ID（提交ID的前12個字符）。
如果有的話，這將引導您找到關於它的現有報告。

需要注意的是，二分法需要一點竅門，不是每個人都懂得訣竅，也需要相當多的努力，
不是每個人都願意投入。儘管如此，還是強烈建議自己進行一次二分。如果你真的
不能或者不想走這條路，至少要找出是哪個主線內核引入的回歸。比如說從 5.5.15
切換到 5.8.4 的時候出現了一些問題，那麼至少可以嘗試一下相近的所有的主線版本
（5.6、5.7 和 5.8）來檢查它是什麼時候出現的。除非你想在一個穩定版或長期支持
內核中找到一個回歸，否則要避免測試那些編號有三段的版本（5.6.12、5.7.8），因
爲那會使結果難以解釋，可能會讓你的測試變得無用。一旦你找到了引入回歸的主要
版本，就可以放心地繼續報告了。但請記住：在不知道罪魁禍首的情況下，開發人員
是否能夠提供幫助取決於手頭的問題。有時他們可能會從報告中確認是什麼出現了問
題，並能修復它；有時他們可能無法提供幫助，除非你進行二分。

當處理回歸問題時，請確保你所面臨的問題真的是由內核引起的，而不是由其他東西
引起的，如上文所述。

在整個過程中，請記住：只有當舊內核和新內核的配置相似時，問題才算回歸。最好
的方法是：把配置文件（``.config``）從舊的工作內核直接複製到你嘗試的每個新內
核版本。之後運行 ``make oldnoconfig`` 來調整它以適應新版本的需要，而不啓用
任何新的功能，因爲那些功能也可能導致回歸。

撰寫並發送報告
---------------

    *通過詳細描述問題來開始編寫報告。記得包括以下條目：您爲復現而安裝的最新
    內核版本、使用的Linux發行版以及關於如何復現該問題的說明。如果可能，將內
    核構建配置（.config）和 ``dmesg`` 的輸出放在網上的某個地方，並連結到它。
    包含或上傳所有其他可能相關的信息，如Oops的輸出/截圖或來自 ``lspci``
    的輸出。一旦你寫完了這個主要部分，請在上方插入一個正常長度的段落快速概
    述問題和影響。再在此之上添加一個簡單描述問題的句子，以得到人們的閱讀。
    現在給出一個更短的描述性標題或主題。然後就可以像MAINTAINERS文件告訴你的
    那樣發送或提交報告了，除非你在處理一個「高優先級問題」：它們需要按照下面
    「高優先級問題的特殊處理」所述特別關照。*

現在你已經準備好了一切，是時候寫你的報告了。上文前言中連結的三篇文檔對如何
寫報告做了部分解釋。這就是爲什麼本文將只提到一些基本的內容以及 Linux 內核特
有的東西。

有一點是符合這兩類的：你的報告中最關鍵的部分是標題/主題、第一句話和第一段。
開發者經常會收到許多郵件。因此，他們往往只是花幾秒鐘的時間瀏覽一下郵件，然
後再決定繼續下一封或仔細查看。因此，你報告的開頭越好，有人研究並幫助你的機
會就越大。這就是爲什麼你應該暫時忽略他們，先寫出詳細的報告。;-)

每份報告都應提及的事項
~~~~~~~~~~~~~~~~~~~~~~~~

詳細描述你的問題是如何發生在你安裝的新純淨內核上的。試著包含你之前寫的和優
化過的分步說明，概述你和其他人如何重現這個問題；在極少數無法重現的情況下，
儘量描述你做了什麼來觸發它。

還應包括其他人爲了解該問題及其環境而可能需要的所有相關信息。實際需要的東西
在很大程度上取決於具體問題，但有些事項你總是應該包括在內：

 * ``cat /proc/version`` 的輸出，其中包含 Linux 內核版本號和構建時的編譯器。

 * 機器正在運行的 Linux 發行版（ ``hostnamectl | grep 「Operating System「`` ）

 * CPU 和作業系統的架構（ ``uname -mi`` ）

 * 如果您正在處理回歸，並進行了二分，請提及導致回歸的變更的主題和提交ID。

許多情況下，讓讀你報告的人多了解兩件事也是明智之舉：

 * 用於構建 Linux 內核的配置（「.config」文件）

 * 內核的信息，你從 ``dmesg`` 得到的信息寫到一個文件里。確保它以像「Linux
   version 5.8-1 (foobar@example.com) (gcc (GCC) 10.2.1, GNU ld version
   2.34) #1 SMP Mon Aug 3 14:54:37 UTC 2020」這樣的行開始，如果沒有，那麼第
   一次啓動階段的重要信息已經被丟棄了。在這種情況下，可以考慮使用
   ``journalctl -b 0 -k`` ；或者你也可以重啓，重現這個問題，然後調用
   ``dmesg`` 。

這兩個文件很大，所以直接把它們放到你的報告中是個壞主意。如果你是在缺陷跟蹤
器中提交問題，那麼將它們附加到工單中。如果你通過郵件報告問題，不要用附件附
上它們，因爲那會使郵件變得太大，可以按下列之一做：

 * 將文件上傳到某個公開的地方（你的網站，公共文件粘貼服務，在
   `bugzilla.kernel.org <https://bugzilla.kernel.org/>`_ 上創建的工單……），
   並在你的報告中放上連結。理想情況下請使用允許這些文件保存很多年的地方，因
   爲它們可能在很多年後對別人有用；例如 5 年或 10 年後，一個開發者正在修改
   一些代碼，而這些代碼正是爲了修復你的問題。

 * 把文件放在一邊，然後說明你會在他人回復時再單獨發送。只要記得報告發出去後，
   真正做到這一點就可以了。;-)

提供這些東西可能是明智的
~~~~~~~~~~~~~~~~~~~~~~~~~~

根據問題的不同，你可能需要提供更多的背景數據。這裡有一些關於提供什麼比較好
的建議：

 * 如果你處理的是內核的「warning」、「OOPS」或「panic」，請包含它。如果你不能複製
   粘貼它，試著用netconsole網絡終端遠程跟蹤或者至少拍一張屏幕的照片。

 * 如果問題可能與你的電腦硬體有關，請說明你使用的是什麼系統。例如，如果你的
   顯卡有問題，請提及它的製造商，顯卡的型號，以及使用的晶片。如果是筆記本電
   腦，請提及它的型號名稱，但儘量確保意義明確。例如「戴爾 XPS 13」就不很明確，
   因爲它可能是 2012 年的那款，那款除了看起來和現在銷售的沒有什麼不同之外，
   兩者沒有任何共同之處。因此，在這種情況下，要加上準確的型號，例如 2019
   年內推出的 XPS 13 型號爲「9380」或「7390」。像「聯想 Thinkpad T590」這樣的名字
   也有些含糊不清：這款筆記本有帶獨立顯卡和不帶的子型號，所以要儘量找到準確
   的型號名稱或註明主要部件。

 * 說明正在使用的相關軟體。如果你在加載模塊時遇到了問題，你要說明正在使用的
   kmod、systemd 和 udev 的版本。如果其中一個 DRM 驅動出現問題，你要說明
   libdrm 和 Mesa 的版本；還要說明你的 Wayland 合成器或 X-Server 及其驅動。
   如果你有文件系統問題，請註明相應的文件系統實用程序的版本（e2fsprogs,
   btrfs-progs, xfsprogs……）。

 * 從內核中收集可能有用的額外信息。例如， ``lspci -nn`` 的輸出可以幫助別人
   識別你使用的硬體。如果你的硬體有問題，你甚至可以給出 ``sudo lspci -vvv``
   的結果，因爲它提供了組件是如何配置的信息。對於一些問題，可能最好包含
   ``/proc/cpuinfo`` ， ``/proc/ioports`` ， ``/proc/iomem`` ，
   ``/proc/modules`` 或 ``/proc/scsi/scsi`` 等文件的內容。一些子系統還提
   供了收集相關信息的工具。 ``alsa-info.sh`` `就是這樣一個工具，它是音頻/聲
   音子系統開發者提供的  <https://www.alsa-project.org/wiki/AlsaInfo>`_ 。

這些例子應該會給你一些知識點，讓你知道附上什麼數據可能是明智的，但你自己也
要想一想，哪些數據對別人會有幫助。不要太擔心忘記一些東西，因爲開發人員會要
求提供他們需要的額外細節。但從一開始就把所有重要的東西都提供出來，會增加別
人仔細查看的機會。

重要部分：報告的開頭
~~~~~~~~~~~~~~~~~~~~~~

現在你已經準備好了報告的詳細部分，讓我們進入最重要的部分：開頭幾句。現在到
報告的最前面，在你剛才寫的部分之前加上類似「The detailed description:」（詳細
描述）這樣的內容，並在最前面插入兩個新行。現在寫一個正常長度的段落，大致概
述這個問題。去掉所有枯燥的細節，把重點放在讀者需要知道的關鍵部分，以讓人了
解這是怎麼回事；如果你認爲這個缺陷影響了很多用戶，就提一下這點來吸引大家關
注。

做好這一點後，在頂部再插入兩行，寫一句話的摘要，快速解釋報告的內容。之後你
要更加抽象，爲報告寫一個更短的主題/標題。

現在你已經寫好了這部分，請花點時間來優化它，因爲它是你的報告中最重要的部分：
很多人會先讀這部分，然後才會決定是否值得花時間閱讀其他部分。

現在就像 :ref:`MAINTAINERS <maintainers>` 維護者文件告訴你的那樣發送或提交
報告，除非它是前面概述的那些「高優先級問題」之一：在這種情況下，請先閱讀下一
小節，然後再發送報告。

高優先級問題的特殊處理
~~~~~~~~~~~~~~~~~~~~~~~~

高優先級問題的報告需要特殊處理。

**非常嚴重的缺陷** ：確保在主題或工單標題以及第一段中明顯標出 severeness
（非常嚴重的）。

**回歸** ：如果問題是一個回歸，請在郵件的主題或缺陷跟蹤器的標題中添加
[REGRESSION]。如果您沒有進行二分，請至少註明您測試的最新主線版本（比如 5.7）
和出現問題的最新版本（比如 5.8）。如果您成功地進行了二分，請註明導致回歸
的提交ID和主題。也請添加該變更的作者到你的報告中；如果您需要將您的缺陷提交
到缺陷跟蹤器中，請將報告以私人郵件的形式轉發給他，並註明報告提交地點。

**安全問題** ：對於這種問題，你將必須評估：如果細節被公開披露，是否會對其他
用戶產生短期風險。如果不會，只需按照所述繼續報告問題。如果有此風險，你需要
稍微調整一下報告流程。

 * 如果 MAINTAINERS 文件指示您通過郵件報告問題，請不要抄送任何公共郵件列表。

 * 如果你應該在缺陷跟蹤器中提交問題，請確保將工單標記爲「私有」或「安全問題」。
   如果缺陷跟蹤器沒有提供保持報告私密性的方法，那就別想了，把你的報告以私人
   郵件的形式發送給維護者吧。

在這兩種情況下，都一定要將報告發到 MAINTAINERS 文件中「安全聯絡」部分列出的
地址。理想的情況是在發送報告的時候直接抄送他們。如果您在缺陷跟蹤器中提交了
報告，請將報告的文本轉發到這些地址；但請在報告的頂部加上注釋，表明您提交了
報告，並附上工單連結。

更多信息請參見「Documentation/translations/zh_TW/admin-guide/security-bugs.rst」。

發布報告後的責任
------------------

    *等待別人的反應，繼續推進事情，直到你能夠接受這樣或那樣的結果。因此，請
    公開和及時地回應任何詢問。測試提出的修復。積極地測試：至少重新測試每個
    新主線版本的首個候選版本（RC），並報告你的結果。如果出現拖延，就友好地
    提醒一下。如果你沒有得到任何幫助或者未能滿意，請試著自己幫助自己。*

如果你的報告非常優秀，而且你真的很幸運，那麼某個開發者可能會立即發現導致問
題的原因；然後他們可能會寫一個補丁來修復、測試它，並直接發送給主線集成，同
時標記它以便以後回溯到需要它的穩定版和長期支持內核。那麼你需要做的就是回復
一句「Thank you very much」（非常感謝），然後在發布後換上修復好的版本。

但這種理想狀況很少發生。這就是爲什麼你把報告拿出來之後工作才開始。你要做的
事情要視情況而定，但通常會是下面列出的事情。但在深入研究細節之前，這裡有幾
件重要的事情，你需要記住這部分的過程。

關於進一步互動的一般建議
~~~~~~~~~~~~~~~~~~~~~~~~~~

**總是公開回復** ：當你在缺陷跟蹤器中提交問題時，一定要在那裡回復，不要私下
聯繫任何開發者。對於郵件報告，在回復您收到的任何郵件時，總是使用「全部回復」
功能。這包括帶有任何你可能想要添加到你的報告中的額外數據的郵件：進入郵件應
用程序「已發送」文件夾，並在郵件上使用「全部回復」來回復報告。這種方法可以確保
公共郵件列表和其他所有參與者都能及時了解情況；它還能保持郵件線程的完整性，
這對於郵件列表將所有相關郵件歸爲一類是非常重要的。

只有兩種情況不適合在缺陷跟蹤器或「全部回復」中發表評論：

 * 有人讓你私下發東西。

 * 你被告知要發送一些東西，但注意到其中包含需要保密的敏感信息。在這種情況下，
   可以私下發送給要求發送的開發者。但要在工單或郵件中註明你是這麼做的，這
   樣其他人就知道你尊重了這個要求。

**在請求解釋或幫助之前先研究一下** ：在這部分過程中，有人可能會告訴你用尚未
掌握的技能做一些事情。例如你可能會被要求使用一些你從未聽說過的測試工具；或
者你可能會被要求在 Linux 內核原始碼上應用一個補丁來測試它是否有幫助。在某些
情況下，發個回復詢問如何做就可以了。但在走這條路之前，儘量通過在網際網路上搜
索自行找到答案；或者考慮在其他地方詢問建議。比如詢問朋友，或者到你平時常去
的聊天室或論壇發帖諮詢。

**要有耐心** ：如果你真的很幸運，你可能會在幾個小時內收到對你的報告的答覆。
但大多數情況下會花費更多的時間，因爲維護者分散在全球各地，因此可能在不同的
時區——在那裡他們已經享受著遠離鍵盤的夜晚。

一般來說，內核開發者需要一到五個工作日來回復報告。有時會花費更長的時間，因
爲他們可能正忙於合併窗口、其他工作、參加開發者會議，或者只是在享受一個漫長
的暑假。

「高優先級的問題」（見上面的解釋）例外：維護者應該儘快解決這些問題；這就是爲
什麼你應該最多等待一個星期（如果是緊急的事情，則只需兩天），然後再發送友好
的提醒。

有時維護者可能沒有及時回復；有時候可能會出現分歧，例如一個問題是否符合回歸
的條件。在這種情況下，在郵件列表上提出你的顧慮，並請求其他人公開或私下回復
如何繼續推進。如果失敗了，可能應該讓更高級別的維護者介入。如果是 WiFi 驅動，
那就是無線維護者；如果沒有更高級別的維護者，或者其他一切努力都失敗了，那
這可能是一種罕見的、可以讓 Linus Torvalds 參與進來的情況。

**主動測試** ：每當一個新的主線內核版本的第一個預發布版本（rc1）發布的時候，
去檢查一下這個問題是否得到了解決，或者是否有什麼重要的變化。在工單中或在
回復報告的郵件中提及結果（確保所有參與討論的人都被抄送）。這將表明你的承諾
和你願意幫忙。如果問題持續存在，它也會提醒開發者確保他們不會忘記它。其他一
些不定期的重新測試（例如用rc3、rc5 和最終版本）也是一個好主意，但只有在相關
的東西發生變化或者你正在寫什麼東西的時候才報告你的結果。

這些些常規的事情就不說了，我們來談談報告後如何幫助解決問題的細節。

查詢和測試請求
~~~~~~~~~~~~~~~

如果你的報告得到了回復則需履行以下責任：

**檢查與你打交道的人** ：大多數情況下，會是維護者或特定代碼區域的開發人員對
你的報告做出回應。但由於問題通常是公開報告的，所以回復的可能是任何人——包括
那些想要幫忙的人，但最後可能會用他們的問題或請求引導你完全偏離軌道。這很少
發生，但這是快速上網搜搜看你正在與誰互動是明智之舉的許多原因之一。通過這樣
做，你也可以知道你的報告是否被正確的人聽到，因爲如果討論沒有導致滿意的問題
解決方案而淡出，之後可能需要提醒維護者（見下文）。

**查詢數據** ：通常你會被要求測試一些東西或提供更多細節。儘快提供所要求的信
息，因爲你已經得到了可能會幫助你的人的注意，你等待的時間越長就有越可能失去
關注；如果你不在數個工作日內提供信息，甚至可能出現這種結果。

**測試請求** ：當你被要求測試一個診斷補丁或可能的修復時，也要儘量及時測試。
但要做得恰當，一定不要急於求成：混淆事情很容易發生，這會給所有人帶來許多困
惑。例如一個常見的錯誤是以爲應用了一個帶修復的建議補丁，但事實上並沒有。即
使是有經驗的測試人員也會偶爾發生這樣的事情，但當有修復的內核和沒有修復的內
核表現得一樣時，他們大多時候會注意到。

當沒有任何實質性進展時該怎麼辦
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

有些報告不會得到負有相關責任的 Linux 內核開發者的任何反應；或者圍繞這個問題
的討論有所發展，但漸漸淡出，沒有任何實質內容產出。

在這種情況下，要等兩個星期（最好是三個星期）後再發出友好的提醒：也許當你的
報告到達時，維護者剛剛離開鍵盤一段時間，或者有更重要的事情要處理。在寫提醒
信的時候，要善意地問一下，是否還需要你這邊提供什麼來讓事情推進下去。如果報
告是通過郵件發出來的，那就在郵件的第一行回覆你的初始郵件（見上文），其中包
括下方的原始報告的完整引用：這是少數幾種情況下，這樣的「TOFU」（Text Over,
Fullquote Under文字在上，完整引用在下）是正確的做法，因爲這樣所有的收件人都
會以適當的順序立即讓細節到手頭上來。

在提醒之後，再等三周的回覆。如果你仍然沒有得到適當的反饋，你首先應該重新考
慮你的方法。你是否可能嘗試接觸了錯誤的人？是不是報告也許令人反感或者太混亂，
以至於人們決定完全遠離它？排除這些因素的最好方法是：把報告給一兩個熟悉
FLOSS 問題報告的人看，詢問他們的意見。同時徵求他們關於如何繼續推進的建議。
這可能意味著：準備一份更好的報告，讓這些人在你發出去之前對它進行審查。這樣
的方法完全可以；只需說明這是關於這個問題的第二份改進的報告，並附上第一份報
告的連結。

如果報告是恰當的，你可以發送第二封提醒信；在其中詢問爲什麼報告沒有得到任何
回復。第二封提醒郵件的好時機是在新 Linux 內核版本的首個預發布版本（'rc1'）
發布後不久，因爲無論如何你都應該在那個時候重新測試並提供狀態更新（見上文）。

如果第二次提醒的結果又在一周內沒有任何反應，可以嘗試聯繫上級維護者詢問意見：
即使再忙的維護者在這時候也至少應該發過某種確認。

記住要做好失望的準備：理想狀況下維護者最好對每一個問題報告做出回應，但他們
只有義務解決之前列出的「高優先級問題」。所以，如果你得到的回覆是「謝謝你的報告，
我目前有更重要的問題要處理，在可預見的未來沒有時間去研究這個問題」，那請不
要太沮喪。

也有可能在缺陷跟蹤器或列表中進行了一些討論之後，什麼都沒有發生，提醒也無助
於激勵大家進行修復。這種情況可能是毀滅性的，但在 Linux 內核開發中確實會發生。
這些和其他得不到幫助的原因在本文結尾處的「爲什麼有些問題在被報告後沒有得到
任何回應或者仍然沒有修復」中進行了解釋。

如果你沒有得到任何幫助或問題最終沒有得到解決，不要沮喪：Linux 內核是 FLOSS，
因此你仍然可以自己幫助自己。例如，你可以試著找到其他受影響的人，和他們一
起合作來解決這個問題。這樣的團隊可以一起準備一份新的報告，提到團隊有多少人，
爲什麼你們認爲這是應該得到解決的事情。也許你們還可以一起縮小確切原因或引
入回歸的變化，這往往會使修復更容易。而且如果運氣好的話，團隊中可能會有懂點
編程的人，也許能寫出一個修複方案。

「報告穩定版和長期支持內核線的回歸」的參考
------------------------------------------

本小節提供了在穩定版和長期支持內核線中面對回歸時需要執行的步驟的詳細信息。

確保特定版本線仍然受支持
~~~~~~~~~~~~~~~~~~~~~~~~~

    *檢查內核開發人員是否仍然維護你關心的Linux內核版本線：去 kernel.org 的
    首頁，確保此特定版本線的最新版沒有「[EOL]」標記。*

大多數內核版本線只支持三個月左右，因爲延長維護時間會帶來相當多的工作。因此，
每年只會選擇一個版本來支持至少兩年（通常是六年）。這就是爲什麼你需要檢查
內核開發者是否還支持你關心的版本線。

注意，如果 `kernel.org <https://kernel.org/>`_ 在首頁上列出了兩個「穩定」版本，
你應該考慮切換到較新的版本，而忘掉較舊的版本：對它的支持可能很快就會結束。
然後，它將被標記爲「生命周期結束」（EOL）。達到這個程度的版本線仍然會在
`kernel.org <https://kernel.org/>`_ 首頁上被顯示一兩周，但不適合用於測試和
報告。

搜索穩定版郵件列表
~~~~~~~~~~~~~~~~~~~

    *檢查Linux穩定版郵件列表中的現有報告。*

也許你所面臨的問題已經被發現，並且已經或即將被修復。因此，請在 `Linux 穩定
版郵件列表的檔案 <https://lore.kernel.org/stable/>`_ 中搜索類似問題的報告。
如果你找到任何匹配的問題，可以考慮加入討論，除非修復工作已經完成並計劃很快
得到應用。

用最新版本復現問題
~~~~~~~~~~~~~~~~~~~

    *從特定的版本線安裝最新版本作爲純淨內核。確保這個內核沒有被汙染，並且仍
    然存在問題，因爲問題可能已經在那裡被修復了。*

在投入更多時間到這個過程中之前，你要檢查這個問題是否在你關注的版本線的最新
版本中已經得到了修復。這個內核需要是純淨的，在問題發生之前不應該被汙染，正
如上面已經在測試主線的過程中詳細介紹過的一樣。

您是否是第一次注意到供應商內核的回歸？供應商的更改可能會發生變化。你需要重新
檢查排除來這個問題。當您從5.10.4-vendor.42更新到5.10.5-vendor.43時，記錄損壞
的信息。然後在測試了前一段中所述的最新5.10版本之後，檢查Linux 5.10.4的普通版本
是否也可以正常工作。如果問題在那裡出現，那就不符合上游回歸的條件，您需要切換
回主逐步指南來報告問題。

報告回歸
~~~~~~~~~~

    *向Linux穩定版郵件列表發送一個簡短的問題報告(stable@vger.kernel.org)。
    大致描述問題，並解釋如何復現。講清楚首個出現問題的版本和最後一個工作正常
    的版本。然後等待進一步的指示。*

當報告在穩定版或長期支持內核線內發生的回歸（例如在從5.10.4更新到5.10.5時），
一份簡短的報告足以快速報告問題。因此只需要粗略的描述。

但是請注意，如果您能夠指明引入問題的確切版本，這將對開發人員有很大幫助。因此
如果有時間的話，請嘗試使用普通內核找到該版本。讓我們假設發行版發布Linux內核
5.10.5到5.10.8的更新時發生了故障。那麼按照上面的指示，去檢查該版本線中的最新
內核，比如5.10.9。如果問題出現，請嘗試普通5.10.5，以確保供應商應用的補丁不會
干擾。如果問題沒有出現，那麼嘗試5.10.7，然後直到5.10.8或5.10.6（取決於結果）
找到第一個引入問題的版本。在報告中寫明這一點，並指出5.10.9仍然存在故障。

前一段基本粗略地概述了「二分」方法。一旦報告出來，您可能會被要求做一個正確的
報告，因爲它允許精確地定位導致問題的確切更改（然後很容易被恢復以快速修復問題）。
因此如果時間允許，考慮立即進行適當的二分。有關如何詳細信息，請參閱「對回歸的
特別關照」部分和文檔「Documentation/translations/zh_TW/admin-guide/bug-bisect.rst」。

「報告僅在舊內核版本線中發生的問題」的參考
------------------------------------------

本節詳細介紹了如果無法用主線內核重現問題，但希望在舊版本線（又稱穩定版內核和
長期支持內核）中修復問題時需要採取的步驟。

有些修復太複雜
~~~~~~~~~~~~~~~

    *請做好準備，接下來的幾個步驟可能無法在舊版本中解決問題：修復可能太大或
    太冒險，無法移植到那裡。*

即使是微小的、看似明顯的代碼變化，有時也會帶來新的、完全意想不到的問題。穩
定版和長期支持內核的維護者非常清楚這一點，因此他們只對這些內核進行符合
「Documentation/translations/zh_TW/process/stable-kernel-rules.rst」中所列出的
規則的修改。

複雜或有風險的修改不符合條件，因此只能應用於主線。其他的修復很容易被回溯到
最新的穩定版和長期支持內核，但是風險太大，無法集成到舊版內核中。所以要注意
你所希望的修復可能是那些不會被回溯到你所關心的版本線的修復之一。在這種情況
下，你將別無選擇，要麼忍受這個問題，要麼切換到一個較新的 Linux 版本，除非你
想自己把修復補丁應用到你的內核中。

通用準備
~~~~~~~~~~

    *執行上面「報告僅在舊內核版本線中發生的問題」一節中的前三個步驟。*

您需要執行本指南另一節中已經描述的幾個步驟。這些步驟將讓您：

 * 檢查內核開發人員是否仍然維護您關心的Linux內核版本行。

 * 在Linux穩定郵件列表中搜索退出的報告。

 * 檢查最新版本。

檢查代碼歷史和搜索現有的討論
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    *在Linux內核版本控制系統中搜索修復主線問題的更改，因爲它的提交消息可能
    會告訴你修復是否已經計劃好了支持。如果你沒有找到，搜索適當的郵件列表，
    尋找討論此類問題或同行評議可能修復的帖子；然後檢查討論是否認爲修復不適
    合支持。如果支持根本不被考慮，加入最新的討論，詢問是否有可能。*

在許多情況下，你所處理的問題會發生在主線上，但已在主線上得到了解決。修正它
的提交也需要被回溯才能解決這個問題。這就是爲什麼你要搜索它或任何相關討論。

 * 首先嘗試在存放 Linux 內核原始碼的 Git 倉庫中找到修復。你可以通過
   `kernel.org 上的網頁
   <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/>`_
   或 `GitHub 上的鏡像 <https://github.com/torvalds/linux>`_ 來實現；如果你
   有一個本地克隆，你也可以在命令行用 ``git log --grep=<pattern>`` 來搜索。

   如果你找到了修復，請查看提交消息的尾部是否包含了類似這樣的「穩定版標籤」：

          Cc: <stable@vger.kernel.org> # 5.4+

   像上面這行，開發者標記了安全修復可以回傳到 5.4 及以後的版本。大多數情況
   下，它會在兩周內被應用到那裡，但有時需要更長的時間。

 * 如果提交沒有告訴你任何東西，或者你找不到修復，請再找找關於這個問題的討論。
   用你最喜歡的搜尋引擎搜索網絡，以及 `Linux kernel developers mailing
   list 內核開發者郵件列表 <https://lore.kernel.org/lkml/>`_ 的檔案。也可以
   閱讀上面的 `定位導致問題的內核區域` 一節，然後按照說明找到導致問題的子系
   統：它的缺陷跟蹤器或郵件列表存檔中可能有你要找的答案。

 * 如果你看到了一個計劃的修復，請按上所述在版本控制系統中搜索它，因爲提交可
   能會告訴你是否可以進行回溯。

   * 檢查討論中是否有任何跡象表明，該修復程序可能風險太大，無法回溯到你關心
     的版本線。如果是這樣的話，你必須忍受這個問題，或者切換到應用了修復的內
     核版本線。

   * 如果修復的問題未包含穩定版標籤，並且沒有討論過回溯問題，請加入討論：如
     果合適的話，請提及你所面對的問題的版本，以及你希望看到它被修復。

請求建議
~~~~~~~~~

    *前面的步驟之一應該會給出一個解決方案。如果仍未能成功，請向可能引起問題
    的子系統的維護人員詢問建議；抄送特定子系統的郵件列表以及穩定版郵件列表。*

如果前面的三個步驟都沒有讓你更接近解決方案，那麼只剩下一個選擇：請求建議。
在你發給可能是問題根源的子系統的維護者的郵件中這樣做；抄送子系統的郵件列表
以及穩定版郵件列表（stable@vger.kernel.org）。

爲什麼有些問題在報告後沒有任何回應或仍未解決？
===============================================

當向 Linux 開發者報告問題時，要注意只有「高優先級的問題」（回歸、安全問題、嚴
重問題）才一定會得到解決。如果維護者或其他人都失敗了，Linus Torvalds 他自己
會確保這一點。他們和其他內核開發者也會解決很多其他問題。但是要知道，有時他
們也會不能或不願幫忙；有時甚至沒有人發報告給他們。

最好的解釋就是那些內核開發者常常是在業餘時間爲 Linux 內核做出貢獻。內核中的
不少驅動程序都是由這樣的程式設計師編寫的，往往只是因爲他們想讓自己的硬體可以在
自己喜歡的作業系統上使用。

這些程式設計師大多數時候會很樂意修復別人報告的問題。但是沒有人可以強迫他們這樣
做，因爲他們是自願貢獻的。

還有一些情況下，這些開發者真的很想解決一個問題，但卻不能解決：有時他們缺乏
硬體編程文檔來解決問題。這種情況往往由於公開的文檔太簡陋，或者驅動程序是通
過逆向工程編寫的。

業餘開發者遲早也會不再關心某驅動。也許他們的測試硬體壞了，被更高級的玩意取
代了，或者是太老了以至於只能在計算機博物館裡找到。有時開發者根本就不關心他
們的代碼和 Linux 了，因爲在他們的生活中一些不同的東西變得更重要了。在某些情
況下，沒有人願意接手維護者的工作——也沒有人可以被強迫，因爲對 Linux 內核的貢
獻是自願的。然而被遺棄的驅動程序仍然存在於內核中：它們對人們仍然有用，刪除
它們可能導致回歸。

對於那些爲 Linux 內核工作而獲得報酬的開發者來說，情況並沒有什麼不同。這些人
現在貢獻了大部分的變更。但是他們的僱主遲早也會停止關注他們的代碼或者讓程序
員專注於其他事情。例如，硬體廠商主要通過銷售新硬體來賺錢；因此，他們中的不
少人並沒有投入太多時間和精力來維護他們多年前就停止銷售的東西的 Linux 內核驅
動。企業級 Linux 發行商往往持續維護的時間比較長，但在新版本中往往會把對老舊
和稀有硬體的支持放在一邊，以限制範圍。一旦公司拋棄了一些代碼，往往由業餘貢
獻者接手，但正如上面提到的：他們遲早也會放下代碼。

優先級是一些問題沒有被修復的另一個原因，因爲維護者相當多的時候是被迫設置這
些優先級的，因爲在 Linux 上工作的時間是有限的。對於業餘時間或者僱主給予他們
的開發人員用於上游內核維護工作的時間也是如此。有時維護人員也會被報告淹沒，
即使一個驅動程序幾乎完美地工作。爲了不被完全纏住，程式設計師可能別無選擇，只能
對問題報告進行優先級排序而拒絕其中的一些報告。

不過這些都不用太過擔心，很多驅動都有積極的維護者，他們對儘可能多的解決問題
相當感興趣。

結束語
=======

與其他免費/自由&開源軟體（Free/Libre & Open Source Software，FLOSS）相比，
向 Linux 內核開發者報告問題是很難的：這個文檔的長度和複雜性以及字裡行間的內
涵都說明了這一點。但目前就是這樣了。這篇文字的主要作者希望通過記錄現狀來爲
以後改善這種狀況打下一些基礎。