Análisis IOVA de los veintiún DPDK de la serie DPDK

1. IOVA

IOVA, dirección virtual de IO. Parte de las funciones administradas por la capa superior EAL (Environment Abstraction Layer) en DPDK incluyen el mapeo de los registros del dispositivo de hardware en la memoria para su aplicación por parte de otros controladores. Es decir, el proceso en el modo de usuario puede usar directamente la dirección de E/S y realizar la operación de E/S. Como se mencionó anteriormente, estas direcciones se pueden dividir en direcciones físicas (PA) y direcciones virtuales IO, es decir, IOVA. La capa superior no distingue entre los dos, es decir, para la capa de aplicación, es insensible a los dos. Todos los procesos en modo usuario que ven son direcciones IOVA.
La ventaja del modo IOVA de PA es que se puede usar en aplicaciones de espacio del núcleo y para todo el hardware. Su desventaja es que será problemático si hay un requisito de permisos para operaciones de memoria. De manera similar, si hay muchos fragmentos de memoria, es posible que no sea posible asignar memoria, lo que provocará la falla de toda la inicialización de DPDK. Para resolver estos problemas, en términos generales, se usa una página más grande, como 1G, y se guía al sistema para que use una página grande al inicio. Pero una persona perspicaz puede ver de un vistazo que esta es solo una solución temporal, como dice el refrán, si un dolor de cabeza cura la cabeza, un dolor de pie cura el pie.
El modo IOVA de VA requiere un IOMMU para convertir y analizar direcciones. Es equivalente a una capa adicional de abstracción.Generalmente, aquellos que han hecho diseño entienden que la abstracción en su mayoría significa una reducción en la eficiencia (excepto la abstracción de costo cero). Su ventaja es que el procesamiento de la memoria no está completamente limitado por la memoria física real, ni requiere algunos permisos especiales. Especialmente en el entorno de la nube (IOMMU es más adecuado en el entorno virtual), la aplicación de DPDK tiene una gama más amplia de aplicaciones. Por supuesto, tiene muchas deficiencias: el hardware no necesariamente es compatible con IOMMU o la plataforma no tiene esto, el software no es compatible o IOMMU es limitado, etc.
En circunstancias normales, DPDK elige usar el modo IOVA de PA por defecto. Uno es seguro y el otro es de amplia aplicabilidad. Sin embargo, se recomienda usar el modo IOVA de VA si las condiciones lo permiten. En DPDK17.11 y versiones posteriores, puede usar el comando:
–iova-mode
para seleccionar automáticamente el modo apropiado.

2. Aplicación de IOVA

Dado que es una operación de IO, en teoría, esta función es similar a la de un controlador tradicional. En DPDK, tanto el mapeo de interrupciones como el mapeo de registros del hardware requieren la asistencia del kernel. Debe estar vinculado a PCI, como entenderá cualquiera que haya jugado con computadoras. Una característica de este PCI es que no está vinculado a un conjunto específico de dispositivos. Cualquiera que haya escrito un controlador de hardware sabe que el tipo de identificación de algunos dispositivos suele estar codificado en el controlador. Así que en teoría se puede usar con cualquier dispositivo de este tipo. Pero los desarrolladores a menudo saben qué tan profunda es el agua.
En DPDK, en espacio de usuario (UIO), por sus propias limitaciones (usa igb_uio), solo se puede usar el modo IOVA de PA, que también limita la aplicación en UIO; y recomendado en versiones superiores En el controlador kernel VFIO ( Linux3.6), está especialmente desarrollado con IOMMU, por lo que los dos modos anteriores se pueden seleccionar para procesar en VFIO (pero el problema de permisos en modo PA aún existe). Espere hasta una versión posterior del kernel (> = 4.5), después de configurar la opción enable_unsafe_noiommu_mode, puede usar VFIO sin IOMMU. Esto es aún más ventajoso.
Por supuesto, en DPDK, PMD (controlador de modo de sondeo de software) y algún software relacionado no necesitan PCI para operar el controlador. Operan el hardware a través de la infraestructura estándar del kernel, por lo que se puede ignorar el IOVA mencionado anteriormente. En otras palabras, no importa.

3. Estructura de datos y código fuente

Primero mire las definiciones de direcciones relevantes:

/** Physical address */
typedef uint64_t phys_addr_t;
#define RTE_BAD_PHYS_ADDR ((phys_addr_t)-1)

/**
 * IO virtual address type.
 * When the physical addressing mode (IOVA as PA) is in use,
 * the translation from an IO virtual address (IOVA) to a physical address
 * is a direct mapping, i.e. the same value.
 * Otherwise, in virtual mode (IOVA as VA), an IOMMU may do the translation.
 */
typedef uint64_t rte_iova_t;
#define RTE_BAD_IOVA ((rte_iova_t)-1)

En el análisis anterior, incluida la estructura de datos analizada más adelante, se puede ver la aplicación de estos dos tipos de datos. Echemos un vistazo al código de conversión relevante:

//\dpdk-stable-19.11.14\lib\librte_eal\linux\eal\eal_memory.c
rte_iova_t
rte_mem_virt2iova(const void *virtaddr)
{
	if (rte_eal_iova_mode() == RTE_IOVA_VA)
		return (uintptr_t)virtaddr;
	return rte_mem_virt2phy(virtaddr);
}

Mira VFIO de nuevo:

struct user_mem_map {
	uint64_t addr;
	uint64_t iova;
	uint64_t len;
};
//\lib\librte_eal\linux\eal\eal_vfio.h
struct vfio_iommu_type {
	int type_id;
	const char *name;
	bool partial_unmap;
	vfio_dma_user_func_t dma_user_map_func;
	vfio_dma_func_t dma_map_func;
};

Mira el juicio de modo de nuevo:

/* IOMMU types we support */
static const struct vfio_iommu_type iommu_types[] = {
	/* x86 IOMMU, otherwise known as type 1 */
	{
		.type_id = RTE_VFIO_TYPE1,
		.name = "Type 1",
		.partial_unmap = false,
		.dma_map_func = &vfio_type1_dma_map,
		.dma_user_map_func = &vfio_type1_dma_mem_map
	},
	/* ppc64 IOMMU, otherwise known as spapr */
	{
		.type_id = RTE_VFIO_SPAPR,
		.name = "sPAPR",
		.partial_unmap = true,
		.dma_map_func = &vfio_spapr_dma_map,
		.dma_user_map_func = &vfio_spapr_dma_mem_map
	},
	/* IOMMU-less mode */
	{
		.type_id = RTE_VFIO_NOIOMMU,
		.name = "No-IOMMU",
		.partial_unmap = true,
		.dma_map_func = &vfio_noiommu_dma_map,
		.dma_user_map_func = &vfio_noiommu_dma_mem_map
	},
};
//4\lib\librte_eal\common\eal_common_bus.c
/*
 * Get iommu class of devices on the bus.
 */
enum rte_iova_mode
rte_bus_get_iommu_class(void)
{
	enum rte_iova_mode mode = RTE_IOVA_DC;
	bool buses_want_va = false;
	bool buses_want_pa = false;
	struct rte_bus * bus;

	TAILQ_FOREACH(bus, &rte_bus_list, next) {
		enum rte_iova_mode bus_iova_mode;

		if (bus->get_iommu_class == NULL)
			continue;

		bus_iova_mode = bus->get_iommu_class();
		RTE_LOG(DEBUG, EAL, "Bus %s wants IOVA as '%s'\n",
			bus->name,
			bus_iova_mode == RTE_IOVA_DC ? "DC" :
			(bus_iova_mode == RTE_IOVA_PA ? "PA" : "VA"));
		if (bus_iova_mode == RTE_IOVA_PA)
			buses_want_pa = true;
		else if (bus_iova_mode == RTE_IOVA_VA)
			buses_want_va = true;
	}
	if (buses_want_va && !buses_want_pa) {
		mode = RTE_IOVA_VA;
	} else if (buses_want_pa && !buses_want_va) {
		mode = RTE_IOVA_PA;
	} else {
		mode = RTE_IOVA_DC;
		if (buses_want_va) {
			RTE_LOG(WARNING, EAL, "Some buses want 'VA' but forcing 'DC' because other buses want 'PA'.\n");
			RTE_LOG(WARNING, EAL, "Depending on the final decision by the EAL, not all buses may be able to initialize.\n");
		}
	}

	return mode;
}

Mire el soporte para IOMMU en el kernel:

//\drivers\bus\pci\linux\pci.c
#if defined(RTE_ARCH_X86)
bool
pci_device_iommu_support_va(const struct rte_pci_device *dev)
{
#define VTD_CAP_MGAW_SHIFT	16
#define VTD_CAP_MGAW_MASK	(0x3fULL << VTD_CAP_MGAW_SHIFT)
	const struct rte_pci_addr *addr = &dev->addr;
	char filename[PATH_MAX];
	FILE *fp;
	uint64_t mgaw, vtd_cap_reg = 0;

	snprintf(filename, sizeof(filename),
		 "%s/" PCI_PRI_FMT "/iommu/intel-iommu/cap",
		 rte_pci_get_sysfs_path(), addr->domain, addr->bus, addr->devid,
		 addr->function);

	fp = fopen(filename, "r");
	if (fp == NULL) {
		/* We don't have an Intel IOMMU, assume VA supported */
		if (errno == ENOENT)
			return true;

		RTE_LOG(ERR, EAL, "%s(): can't open %s: %s\n",
			__func__, filename, strerror(errno));
		return false;
	}

	/* We have an Intel IOMMU */
	if (fscanf(fp, "%" PRIx64, &vtd_cap_reg) != 1) {
		RTE_LOG(ERR, EAL, "%s(): can't read %s\n", __func__, filename);
		fclose(fp);
		return false;
	}

	fclose(fp);

	mgaw = ((vtd_cap_reg & VTD_CAP_MGAW_MASK) >> VTD_CAP_MGAW_SHIFT) + 1;

	/*
	 * Assuming there is no limitation by now. We can not know at this point
	 * because the memory has not been initialized yet. Setting the dma mask
	 * will force a check once memory initialization is done. We can not do
	 * a fallback to IOVA PA now, but if the dma check fails, the error
	 * message should advice for using '--iova-mode pa' if IOVA VA is the
	 * current mode.
	 */
	rte_mem_set_dma_mask(mgaw);
	return true;
}
#elif defined(RTE_ARCH_PPC_64)
bool
pci_device_iommu_support_va(__rte_unused const struct rte_pci_device *dev)
{
	return false;
}
#else
bool
pci_device_iommu_support_va(__rte_unused const struct rte_pci_device *dev)
{
	return true;
}
#endif

enum rte_iova_mode
pci_device_iova_mode(const struct rte_pci_driver *pdrv,
		     const struct rte_pci_device *pdev)
{
	enum rte_iova_mode iova_mode = RTE_IOVA_DC;

	switch (pdev->kdrv) {
	case RTE_KDRV_VFIO: {
#ifdef VFIO_PRESENT
		static int is_vfio_noiommu_enabled = -1;

		if (is_vfio_noiommu_enabled == -1) {
			if (rte_vfio_noiommu_is_enabled() == 1)
				is_vfio_noiommu_enabled = 1;
			else
				is_vfio_noiommu_enabled = 0;
		}
		if (is_vfio_noiommu_enabled != 0)
			iova_mode = RTE_IOVA_PA;
		else if ((pdrv->drv_flags & RTE_PCI_DRV_NEED_IOVA_AS_VA) != 0)
			iova_mode = RTE_IOVA_VA;
#endif
		break;
	}

	case RTE_KDRV_IGB_UIO:
	case RTE_KDRV_UIO_GENERIC:
		iova_mode = RTE_IOVA_PA;
		break;

	default:
		if ((pdrv->drv_flags & RTE_PCI_DRV_NEED_IOVA_AS_VA) != 0)
			iova_mode = RTE_IOVA_VA;
		break;
	}
	return iova_mode;
}

De hecho, cosas como IOVA e IOMMU son un método de control de direcciones, y IOMMU puede servir mejor a IOVA. De todos modos, a los ojos de las aplicaciones de nivel superior, no existe lo físico y lo virtual. Solo se preocupa por la dirección de la operación. En cuanto a cómo lidiar con la dirección al final, no es motivo de preocupación. a otros. Al igual que almacenar dinero en el banco, simplemente envía el dinero y no le importa lo que haga el banco con el dinero.

4. Análisis

Espero que los programadores nacionales se desarrollen hasta el fondo cuando tengan tiempo, esta es la dirección del futuro. La aplicación de la capa superior está en auge desde hace diez años, y se encuentra que básicamente ha llegado a una encrucijada. ¿Ir a la izquierda, ir a la derecha o continuar? Los benévolos ven benevolencia. Pero no importa cómo vaya, se separará del soporte que no está en el fondo. Sin cimientos, no importa cuán bueno sea el edificio, no podrá resistir el viento y la lluvia.

Supongo que te gusta

Origin blog.csdn.net/fpcc/article/details/131348677
Recomendado
Clasificación