NVMe Linux驱动系列一:host端[sysfs.c]<23>

文章描述了Linux内核中NVMe控制器在sysfs中的两个设备属性:reset_controller用于同步重置控制器,rescan_controller用于重新扫描队列。还介绍了如何通过dev_to_ns_head函数获取NVMe命名空间头部结构,以及展示命名空间唯一标识符(如WWID、UUID、NGUID、EUI和NSID)的sysfs属性读取函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

备注:代码路径https://github.com/torvalds/linux/blob/v6.5-rc6/drivers/nvme/host/sysfs.c

nvme_sysfs_reset

static ssize_t nvme_sysfs_reset(struct device *dev,
				struct device_attribute *attr, const char *buf,
				size_t count)
{
	struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
	int ret;

	ret = nvme_reset_ctrl_sync(ctrl);
	if (ret < 0)
		return ret;
	return count;
}
static DEVICE_ATTR(reset_controller, S_IWUSR, NULL, nvme_sysfs_reset);

这段代码是用于在 sysfs 中为 NVMe 控制器添加一个属性以执行重置操作的。让我解释一下这段代码的作用:

  1. nvme_sysfs_reset 函数是一个用于执行控制器重置的回调函数。当用户在 sysfs 上写入 reset_controller 属性时,该函数将被调用。它接收以下参数:

    • dev: 代表设备的指针,即 NVMe 控制器的设备结构体。
    • attr: 代表设备属性的指针,即要执行的属性(在此为 reset_controller)。
    • buf: 用户传递的数据,通常是用户写入属性的值。
    • count: 传递的数据的大小。
  2. nvme_sysfs_reset 函数中,首先获取与设备相关的 nvme_ctrl 结构体,然后调用 nvme_reset_ctrl_sync 函数来同步地重置控制器。如果重置失败,函数将返回负数;如果成功,函数将返回非负数。无论重置是否成功,都返回 count,通常是传递的数据的大小。

  3. DEVICE_ATTR(reset_controller, S_IWUSR, NULL, nvme_sysfs_reset); 这一行用于定义一个名为 reset_controller 的设备属性。它的参数分别是属性名称、权限标志、显示函数和存储函数。在此代码中,S_IWUSR 标志表示用户具有写权限。

    • NULL 表示没有显示函数,因此用户将无法读取此属性。
    • nvme_sysfs_reset 函数是存储函数,它将在用户写入属性时被调用。

此代码段的效果是:在 sysfs 中为 NVMe 控制器创建一个名为 reset_controller 的属性,允许用户通过写入该属性来触发控制器的同步重置。写入属性后,nvme_sysfs_reset 函数将被调用以执行重置操作。无论是否成功,都会返回写入的数据大小。

nvme_sysfs_rescan

static ssize_t nvme_sysfs_rescan(struct device *dev,
				struct device_attribute *attr, const char *buf,
				size_t count)
{
	struct nvme_ctrl *ctrl = dev_get_drvdata(dev);

	nvme_queue_scan(ctrl);
	return count;
}
static DEVICE_ATTR(rescan_controller, S_IWUSR, NULL, nvme_sysfs_rescan);

这段代码是用于在 sysfs 中为 NVMe 控制器添加一个属性以执行重新扫描操作的。让我解释一下这段代码的作用:

  1. nvme_sysfs_rescan 函数是一个用于执行重新扫描操作的回调函数。当用户在 sysfs 上写入 rescan_controller 属性时,该函数将被调用。它接收以下参数:

    • dev: 代表设备的指针,即 NVMe 控制器的设备结构体。
    • attr: 代表设备属性的指针,即要执行的属性(在此为 rescan_controller)。
    • buf: 用户传递的数据,通常是用户写入属性的值。
    • count: 传递的数据的大小。
  2. nvme_sysfs_rescan 函数中,首先获取与设备相关的 nvme_ctrl 结构体,然后调用 nvme_queue_scan 函数来重新扫描控制器的队列。这将触发 NVMe 子系统对控制器进行重新扫描,以便发现和管理新添加的设备。

  3. DEVICE_ATTR(rescan_controller, S_IWUSR, NULL, nvme_sysfs_rescan); 这一行用于定义一个名为 rescan_controller 的设备属性。它的参数分别是属性名称、权限标志、显示函数和存储函数。在此代码中,S_IWUSR 标志表示用户具有写权限。

    • NULL 表示没有显示函数,因此用户将无法读取此属性。
    • nvme_sysfs_rescan 函数是存储函数,它将在用户写入属性时被调用。

此代码段的效果是:在 sysfs 中为 NVMe 控制器创建一个名为 rescan_controller 的属性,允许用户通过写入该属性来触发对控制器的重新扫描操作。写入属性后,nvme_sysfs_rescan 函数将被调用以执行重新扫描操作,并且总是返回写入的数据大小。

dev_to_ns_head

static inline struct nvme_ns_head *dev_to_ns_head(struct device *dev)
{
	struct gendisk *disk = dev_to_disk(dev);

	if (disk->fops == &nvme_bdev_ops)
		return nvme_get_ns_from_dev(dev)->head;
	else
		return disk->private_data;
}

这段代码定义了一个名为 dev_to_ns_head 的内联函数,用于从设备指针中获取对应的 NVMe 命名空间头部结构体指针。

  1. dev_to_ns_head 函数接受一个指向 struct device 的指针作为参数。

  2. 首先,通过 dev_to_disk 宏将设备指针转换为关联的 gendisk 结构体指针,即块设备表示。

  3. 然后,检查 gendisk 的操作函数指针 fops 是否等于 nvme_bdev_ops。如果是,则说明该设备是由 NVMe 块设备驱动管理的,因此使用 nvme_get_ns_from_dev(dev)->head 获取 NVMe 命名空间头部结构体指针。

  4. 如果 fops 不等于 nvme_bdev_ops,则表示该设备不是 NVMe 块设备,而是其他类型的块设备,因此可以直接从 gendiskprivate_data 字段中获取 NVMe 命名空间头部结构体指针。

总之,dev_to_ns_head 函数根据设备类型来获取对应的 NVMe 命名空间头部结构体指针,以便进行后续操作。

wwid_show


static ssize_t wwid_show(struct device *dev, struct device_attribute *attr,
		char *buf)
{
	struct nvme_ns_head *head = dev_to_ns_head(dev);
	struct nvme_ns_ids *ids = &head->ids;
	struct nvme_subsystem *subsys = head->subsys;
	int serial_len = sizeof(subsys->serial);
	int model_len = sizeof(subsys->model);

	if (!uuid_is_null(&ids->uuid))
		return sysfs_emit(buf, "uuid.%pU\n", &ids->uuid);

	if (memchr_inv(ids->nguid, 0, sizeof(ids->nguid)))
		return sysfs_emit(buf, "eui.%16phN\n", ids->nguid);

	if (memchr_inv(ids->eui64, 0, sizeof(ids->eui64)))
		return sysfs_emit(buf, "eui.%8phN\n", ids->eui64);

	while (serial_len > 0 && (subsys->serial[serial_len - 1] == ' ' ||
				  subsys->serial[serial_len - 1] == '\0'))
		serial_len--;
	while (model_len > 0 && (subsys->model[model_len - 1] == ' ' ||
				 subsys->model[model_len - 1] == '\0'))
		model_len--;

	return sysfs_emit(buf, "nvme.%04x-%*phN-%*phN-%08x\n", subsys->vendor_id,
		serial_len, subsys->serial, model_len, subsys->model,
		head->ns_id);
}
static DEVICE_ATTR_RO(wwid);

这段代码定义了一个名为 wwid_show 的函数,用于在 sysfs 中显示命名空间的唯一标识符(wwid)。这个函数会在特定的 sysfs 属性被读取时调用。

  1. wwid_show 函数接受一个指向 struct device 的指针作为参数,以及一个指向 struct device_attribute 的指针和一个指向字符缓冲区的指针。

  2. 首先,通过 dev_to_ns_head 函数将设备指针转换为对应的 NVMe 命名空间头部结构体指针。

  3. 接着,从命名空间头部结构体中获取命名空间的标识信息,如 UUID、NGUID、EUI64、子系统的厂商ID等。

  4. 判断标识信息是否存在,如果存在则分别格式化输出到缓冲区中,并返回输出的长度。

  5. 如果标识信息不存在,会对子系统的序列号(serial)和型号(model)进行处理,将末尾的空格或空字符去除。

  6. 最后,使用 sysfs_emit 函数格式化输出 NVMe 命名空间的唯一标识符,包括子系统的厂商ID、序列号、型号和命名空间ID,输出到缓冲区中。

  7. 通过 DEVICE_ATTR_RO 宏定义了一个只读的 sysfs 设备属性,其名称为 wwid,并将 wwid_show 函数作为读取属性时的处理函数。

总之,通过这段代码,sysfs 中的 wwid 属性将根据命名空间的标识信息生成并显示唯一标识符。

nguid_show


static ssize_t nguid_show(struct device *dev, struct device_attribute *attr,
		char *buf)
{
	return sysfs_emit(buf, "%pU\n", dev_to_ns_head(dev)->ids.nguid);
}
static DEVICE_ATTR_RO(nguid);

这段代码定义了一个名为 nguid_show 的函数,用于在 sysfs 中显示命名空间的 NGUID(Namespace Globally Unique Identifier)。这个函数会在特定的 sysfs 属性被读取时调用。

  1. nguid_show 函数接受一个指向 struct device 的指针作为参数,以及一个指向 struct device_attribute 的指针和一个指向字符缓冲区的指针。

  2. 通过 dev_to_ns_head 函数将设备指针转换为对应的 NVMe 命名空间头部结构体指针。

  3. 通过命名空间头部结构体中的 ids.nguid 成员,使用 %pU 格式化字符串输出 NGUID 到缓冲区中。

  4. 使用 sysfs_emit 函数格式化输出 NGUID,并将其输出到缓冲区中。

  5. 通过 DEVICE_ATTR_RO 宏定义了一个只读的 sysfs 设备属性,其名称为 nguid,并将 nguid_show 函数作为读取属性时的处理函数。

总之,通过这段代码,sysfs 中的 nguid 属性将显示命名空间的 NGUID,这是一个全局唯一的标识符。

uuid_show

static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
		char *buf)
{
	struct nvme_ns_ids *ids = &dev_to_ns_head(dev)->ids;

	/* For backward compatibility expose the NGUID to userspace if
	 * we have no UUID set
	 */
	if (uuid_is_null(&ids->uuid)) {
		dev_warn_once(dev,
			"No UUID available providing old NGUID\n");
		return sysfs_emit(buf, "%pU\n", ids->nguid);
	}
	return sysfs_emit(buf, "%pU\n", &ids->uuid);
}
static DEVICE_ATTR_RO(uuid);

这段代码定义了一个名为 uuid_show 的函数,用于在 sysfs 中显示命名空间的 UUID(Universally Unique Identifier)。这个函数会在特定的 sysfs 属性被读取时调用。

  1. uuid_show 函数接受一个指向 struct device 的指针作为参数,以及一个指向 struct device_attribute 的指针和一个指向字符缓冲区的指针。

  2. 通过 dev_to_ns_head 函数将设备指针转换为对应的 NVMe 命名空间头部结构体指针。

  3. 通过命名空间头部结构体中的 ids 成员,使用 &ids->uuid 来获取 UUID。

  4. 检查 UUID 是否为空(null)。如果 UUID 为空,则输出对应的 NGUID(用于向后兼容),并发出一条警告。这是为了向后兼容性考虑,在没有 UUID 的情况下使用 NGUID。

  5. 如果 UUID 不为空,使用 %pU 格式化字符串输出 UUID 到缓冲区中。

  6. 使用 sysfs_emit 函数格式化输出 UUID 或 NGUID,并将其输出到缓冲区中。

  7. 通过 DEVICE_ATTR_RO 宏定义了一个只读的 sysfs 设备属性,其名称为 uuid,并将 uuid_show 函数作为读取属性时的处理函数。

总之,通过这段代码,sysfs 中的 uuid 属性将显示命名空间的 UUID。如果 UUID 为空,将显示相应的 NGUID,同时发出一条警告。这样可以在向后兼容性情况下提供相应的标识信息。

eui_show

static ssize_t eui_show(struct device *dev, struct device_attribute *attr,
		char *buf)
{
	return sysfs_emit(buf, "%8ph\n", dev_to_ns_head(dev)->ids.eui64);
}
static DEVICE_ATTR_RO(eui);

这段代码定义了一个名为 eui_show 的函数,用于在 sysfs 中显示命名空间的 EUI64(Extended Unique Identifier)。

  1. eui_show 函数接受一个指向 struct device 的指针作为参数,以及一个指向 struct device_attribute 的指针和一个指向字符缓冲区的指针。

  2. 通过 dev_to_ns_head 函数将设备指针转换为对应的 NVMe 命名空间头部结构体指针。

  3. 通过命名空间头部结构体中的 ids 成员,使用 ids->eui64 来获取 EUI64。

  4. 使用 %8ph 格式化字符串输出 EUI64 到缓冲区中,其中 %8ph 是一个格式化字符串,将 EUI64 格式化为 8 字节十六进制格式。

  5. 使用 sysfs_emit 函数格式化输出 EUI64,并将其输出到缓冲区中。

  6. 通过 DEVICE_ATTR_RO 宏定义了一个只读的 sysfs 设备属性,其名称为 eui,并将 eui_show 函数作为读取属性时的处理函数。

总之,通过这段代码,sysfs 中的 eui 属性将显示命名空间的 EUI64,以 8 字节的十六进制格式。

nsid_show


static ssize_t nsid_show(struct device *dev, struct device_attribute *attr,
		char *buf)
{
	return sysfs_emit(buf, "%d\n", dev_to_ns_head(dev)->ns_id);
}
static DEVICE_ATTR_RO(nsid);

这段代码定义了一个名为 nsid_show 的函数,用于在 sysfs 中显示命名空间的 ID(Namespace ID)。

  1. nsid_show 函数接受一个指向 struct device 的指针作为参数,以及一个指向 struct device_attribute 的指针和一个指向字符缓冲区的指针。

  2. 通过 dev_to_ns_head 函数将设备指针转换为对应的 NVMe 命名空间头部结构体指针。

  3. 通过命名空间头部结构体中的 ns_id 成员获取命名空间的 ID。

  4. 使用 %d 格式化字符串输出命名空间的 ID 到缓冲区中。

  5. 使用 sysfs_emit 函数格式化输出命名空间的 ID,并将其输出到缓冲区中。

  6. 通过 DEVICE_ATTR_RO 宏定义了一个只读的 sysfs 设备属性,其名称为 nsid,并将 nsid_show 函数作为读取属性时的处理函数。

总之,通过这段代码,sysfs 中的 nsid 属性将显示命名空间的 ID。

nvme_ns_id_attrs


static struct attribute *nvme_ns_id_attrs[] = {
	&dev_attr_wwid.attr,
	&dev_attr_uuid.attr,
	&dev_attr_nguid.attr,
	&dev_attr_eui.attr,
	&dev_attr_nsid.attr,
#ifdef CONFIG_NVME_MULTIPATH
	&dev_attr_ana_grpid.attr,
	&dev_attr_ana_state.attr,
#endif
	NULL,
};

static umode_t nvme_ns_id_attrs_are_visible(struct kobject *kobj,
		struct attribute *a, int n)
{
	struct device *dev = container_of(kobj, struct device, kobj);
	struct nvme_ns_ids *ids = &dev_to_ns_head(dev)->ids;

	if (a == &dev_attr_uuid.attr) {
		if (uuid_is_null(&ids->uuid) &&
		    !memchr_inv(ids->nguid, 0, sizeof(ids->nguid)))
			return 0;
	}
	if (a == &dev_attr_nguid.attr) {
		if (!memchr_inv(ids->nguid, 0, sizeof(ids->nguid)))
			return 0;
	}
	if (a == &dev_attr_eui.attr) {
		if (!memchr_inv(ids->eui64, 0, sizeof(ids->eui64)))
			return 0;
	}
#ifdef CONFIG_NVME_MULTIPATH
	if (a == &dev_attr_ana_grpid.attr || a == &dev_attr_ana_state.attr) {
		if (dev_to_disk(dev)->fops != &nvme_bdev_ops) /* per-path attr */
			return 0;
		if (!nvme_ctrl_use_ana(nvme_get_ns_from_dev(dev)->ctrl))
			return 0;
	}
#endif
	return a->mode;
}

static const struct attribute_group nvme_ns_id_attr_group = {
	.attrs		= nvme_ns_id_attrs,
	.is_visible	= nvme_ns_id_attrs_are_visible,
};

const struct attribute_group *nvme_ns_id_attr_groups[] = {
	&nvme_ns_id_attr_group,
	NULL,
};

这段代码定义了 NVMe 命名空间的属性组和属性可见性的相关函数。

  1. nvme_ns_id_attrs 数组定义了 NVMe 命名空间属性的指针集合,包括 WWID、UUID、NGUID、EUI 和命名空间 ID(nsid)。如果启用了 NVMe 多路径功能(CONFIG_NVME_MULTIPATH),还包括 ANA(Asymmetric Namespace Access)相关的属性。

  2. nvme_ns_id_attrs_are_visible 函数用于确定特定属性是否可见。它接受一个指向 struct kobject 结构体的指针,一个指向属性的指针,以及属性在属性组中的索引。该函数的作用是根据属性的类型和属性组的可见性规则,决定是否显示属性。

    • 对于 UUID 属性,如果 UUID 是空的且 NGUID 不为空,则不显示。
    • 对于 NGUID 属性,如果 NGUID 不为空,则不显示。
    • 对于 EUI 属性,如果 EUI 不为空,则不显示。
    • 对于 ANA 相关的属性,如果是针对每个路径的属性且控制器不使用 ANA,则不显示。
  3. nvme_ns_id_attr_group 定义了 NVMe 命名空间属性组,包括属性集合 nvme_ns_id_attrs 和属性可见性函数 nvme_ns_id_attrs_are_visible

  4. nvme_ns_id_attr_groups 数组定义了一个指向 NVMe 命名空间属性组的指针,其中包含 nvme_ns_id_attr_group。这个数组将用于为 NVMe 命名空间创建 sysfs 属性组。

总之,通过这段代码,为 NVMe 命名空间定义了一组属性,通过在 nvme_ns_id_attrs_are_visible 函数中进行可见性控制,只显示符合条件的属性,然后将这些属性集合组成一个 sysfs 属性组。

nvme_show_str_function & nvme_show_int_function


#define nvme_show_str_function(field)						\
static ssize_t  field##_show(struct device *dev,				\
			    struct device_attribute *attr, char *buf)		\
{										\
        struct nvme_ctrl *ctrl = dev_get_drvdata(dev);				\
        return sysfs_emit(buf, "%.*s\n",					\
		(int)sizeof(ctrl->subsys->field), ctrl->subsys->field);		\
}										\
static DEVICE_ATTR(field, S_IRUGO, field##_show, NULL);

nvme_show_str_function(model);
nvme_show_str_function(serial);
nvme_show_str_function(firmware_rev);

#define nvme_show_int_function(field)						\
static ssize_t  field##_show(struct device *dev,				\
			    struct device_attribute *attr, char *buf)		\
{										\
        struct nvme_ctrl *ctrl = dev_get_drvdata(dev);				\
        return sysfs_emit(buf, "%d\n", ctrl->field);				\
}										\
static DEVICE_ATTR(field, S_IRUGO, field##_show, NULL);

nvme_show_int_function(cntlid);
nvme_show_int_function(numa_node);
nvme_show_int_function(queue_count);
nvme_show_int_function(sqsize);
nvme_show_int_function(kato);

这段代码使用宏定义创建了一系列的设备属性的读取函数和对应的设备属性。

nvme_show_str_function 宏用于创建读取字符串类型属性的函数和对应的设备属性。它的参数是属性名(例如 “model”、“serial”、“firmware_rev”),根据属性名展开宏,定义了一个以属性名加上 _show 后缀的函数。这个函数接受一个指向 struct device 的指针,一个指向 struct device_attribute 的指针,以及一个字符缓冲区 buf,并使用 sysfs_emit 函数将属性值写入到缓冲区中。

nvme_show_int_function 宏用于创建读取整数类型属性的函数和对应的设备属性。与字符串类型属性类似,这个宏的参数是属性名,然后根据属性名展开宏,定义了一个以属性名加上 _show 后缀的函数。这个函数同样接受一个指向 struct device 的指针,一个指向 struct device_attribute 的指针,以及一个字符缓冲区 buf,然后使用 sysfs_emit 函数将整数属性值以格式化的方式写入缓冲区。

在每个宏的展开中,都使用了 dev_get_drvdata(dev) 获取了与设备关联的私有数据(通常是指向设备控制结构的指针)。然后使用 ctrl->subsys->fieldctrl->field 的方式获取具体的属性值。

最后,通过宏定义分别创建了各个属性的读取函数和对应的设备属性,这些设备属性可以在 sysfs 中访问,用于显示相关属性的信息。

<think>好的,用户想了解Linux中的VFIO_TYPE1_IOMMU的配置和使用方法。首先,我需要回忆VFIO的基本概念。VFIO是虚拟功能I/O,用于安全地将设备直接分配给虚拟机,比如通过KVM。而VFIO_TYPE1_IOMMU指的是使用Type1类型的IOMMU,通常与硬件IOMMU相关,如Intel VT-d或AMD-Vi。 接下来,用户可能需要配置步骤。首先,检查系统是否支持IOMMU。对于Intel CPU,需要在内核启动参数添加intel_iommu=on,AMD则是amd_iommu=on。然后确保内核配置启用了VFIO和Type1 IOMMU驱动。这通常在编译内核时设置CONFIG_VFIO和CONFIG_VFIO_IOMMU_TYPE1。 用户可能遇到设备无法绑定到vfio-pci驱动的情况。这时候需要手动绑定,比如使用driverctl工具或者直接操作sysfs。另外,参考用户提供的引用[1],需要编辑qemu.conf,添加设备到cgroup_device_acl,允许访问VFIO设备节点。例如添加“/dev/vfio/16”。 还有安全配置,比如允许不安全的中断,如引用[3]提到的修改/etc/modprobe.d/iommu_unsafe_interrupts.conf,添加options vfio_iommu_type1 allow_unsafe_interrupts=1。这对于某些NVIDIA显卡可能是必要的。 用户可能还想知道如何验证配置是否成功。可以通过dmesg查看IOMMU分组情况,或者使用virsh命令启动虚拟机并检查设备是否直通成功。 最后,生成相关问题时,需要考虑用户可能的后续疑问,比如安全风险、其他IOMMU类型、错误排查以及NVIDIA设备的特殊处理。结合提供的引用,特别是引用[2]中的NVMe配置和引用[4]的lk2nd引导程序,虽然引用[4]可能与当前问题关联不大,但其他引用中的配置示例和稳定性修复需要涵盖进去。</think>### VFIO_TYPE1_IOMMU 配置与使用详解 #### 1. **基础概念** VFIO(Virtual Function I/O)是 Linux 内核提供的设备直通框架,允许将物理设备直接分配给虚拟机(如 KVM/QEMU 虚拟机)。`VFIO_TYPE1_IOMMU` 特指使用 **Type1 类型的 IOMMU(Input-Output Memory Management Unit)**,主要用于硬件辅助的虚拟化场景[^1]。 #### 2. **启用条件** - **硬件支持**:需要 CPU 支持 Intel VT-d 或 AMD-Vi 技术。 - **内核配置**:需启用以下选项: ```bash CONFIG_VFIO=y CONFIG_VFIO_IOMMU_TYPE1=y CONFIG_VFIO_PCI=y ``` #### 3. **配置步骤** **Step 1: 开启 IOMMU** 在系统启动参数中添加以下内容(根据 CPU 类型选择): - Intel CPU: ```bash GRUB_CMDLINE_LINUX="intel_iommu=on iommu=pt" ``` - AMD CPU: ```bash GRUB_CMDLINE_LINUX="amd_iommu=on iommu=pt" ``` **Step 2: 加载 VFIO 驱动** ```bash modprobe vfio modprobe vfio-pci modprobe vfio_iommu_type1 ``` **Step 3: 绑定设备到 VFIO** 通过 `lspci` 找到目标设备(如 NVIDIA GPU),记录其 PCI ID(例如 `0000:01:00.0`),执行: ```bash echo "0000:01:00.0" > /sys/bus/pci/devices/0000:01:00.0/driver/unbind echo "vfio-pci" > /sys/bus/pci/devices/0000:01:00.0/driver_override echo "0000:01:00.0" > /sys/bus/pci/drivers/vfio-pci/bind ``` **Step 4: QEMU/KVM 配置** 在虚拟机 XML 配置中添加设备直通(参考引用[2]的 NVMe 设备配置逻辑): ```xml <hostdev mode="subsystem" type="pci"> <source> <address domain="0x0000" bus="0x01" slot="0x00" function="0x0"/> </source> </hostdev> ``` **Step 5: 安全组权限** 编辑 `/etc/libvirt/qemu.conf`,添加设备到 `cgroup_device_acl`(如引用[1]中提到的 `/dev/vfio/16`): ```bash cgroup_device_acl = [ "/dev/vfio/16", ... ] ``` #### 4. **稳定性优化** - **允许不安全中断**(如引用[3]): ```bash echo "options vfio_iommu_type1 allow_unsafe_interrupts=1" > /etc/modprobe.d/iommu_unsafe_interrupts.conf ``` - **大页内存支持**:可提升 DMA 性能。 #### 5. **验证方法** ```bash # 检查 IOMMU 分组 dmesg | grep -i iommu # 查看设备绑定状态 lspci -nnk -s 0000:01:00.0 ``` §§ 相关问题 §§ 1. VFIO 直通设备时如何解决 "Device is in use by host driver" 错误? 2. Type1 和 Type2 IOMMU 有何区别? 3. 如何在 VFIO 直通时避免虚拟机崩溃(如 NVIDIA GPU 的特殊处理)? 4. 多 GPU 直通场景下如何隔离中断?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值