Управление кешем dma в Arm Linux
Управление кешем dma в Arm Linux

автор

Фу Ханьцзе hankf@amd.com

Обзор

Две недели назад кто-то спросил об операциях кэширования и согласованности DMA под DMA. Я уже видел этот код по частям. Временно ищу, но пока не нашел.

За последние два дня я разобрался с процессом вызова и обнаружил использование dma-coherent. В документации Linux не объясняется подробно использование dma-coherent. Согласно коду, если в дереве устройств dma присутствует dma-coherent, Linux будет думать, что оборудование будет поддерживать согласованность кэша и не будет выполнять операции кэширования во время работы dma.

Использование dma_map_single/dma_unmap_single

Драйверы устройств обычно вызывают dma_map_single()/dma_unmap_single() для обработки кеша. При вызове функции dma_map_single необходимо указать направление DMA, DMA_TO_DEVICE или DMA_FROM_DEVICE. Linux аннулирует или очистит кеш в зависимости от значения направления.

В функции macb_tx_map() драйверов\net\ethernet\cadence\macb_main.c вызовите dma_map_single() для обновления кэша, а macb_tx_unmap() из macb_tx_interrupt() затем вызовет dma_unmap_single().

Код упрощается следующим образом:

Язык кода:javascript
копировать
macb_tx_map( )
{
.......
	mapping = dma_map_single(&bp->pdev->dev,
			 skb->data + offset,
			 size, DMA_TO_DEVICE);
	.......		
}
			 
macb_tx_unmap( )
{
	.......				
	 
	dma_unmap_single(&bp->pdev->dev, tx_skb->mapping,
			 tx_skb->size, DMA_TO_DEVICE);
	.......		
}
	 
					 					
gem_rx( )
{
.......
	dma_unmap_single(&bp->pdev->dev, addr,
		bp->rx_buffer_size, DMA_FROM_DEVICE);
	.......		
}					
					
gem_rx_refill()
{
.......
	/* now fill corresponding descriptor entry */
	paddr = dma_map_single(&bp->pdev->dev, skb->data,
					bp->rx_buffer_size,
					DMA_FROM_DEVICE);
	.......		
}

Определение dma_map_single/dma_unmap_single

И dma_map_single(), и dma_unmap_single() определены в include\linux\dma-mapping.h. Если нет особых обстоятельств, будут вызваны dma_direct_map_page() и dma_direct_unmap_page(). К особым случаям для Arm64 относятся виртуальные машины iommu и Xen. Виртуальные машины iommu и Xen должны предоставлять dma_map_ops, поэтому используйте функции map и unmap. dma_map_ops iommu — это iommu_dma_ops, определенный в драйверах\iommu\Dma-iommu.c. dma_map_ops iommu — это xen_swiotlb_dma_ops, определенный в драйверах/xen/swiotlb-xen.c.

Язык кода:javascript
копировать
#define dma_map_single(d, a, s, r) dma_map_single_attrs(d, a, s, r, 0)
#define dma_unmap_single(d, a, s, r) dma_unmap_single_attrs(d, a, s, r, 0)

static inline dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,
		size_t size, enum dma_data_direction dir, unsigned long attrs)
{
	debug_dma_map_single(dev, ptr, size);
	return dma_map_page_attrs(dev, virt_to_page(ptr), offset_in_page(ptr),
			size, dir, attrs);
}

static inline void dma_unmap_single_attrs(struct device *dev, dma_addr_t addr,
		size_t size, enum dma_data_direction dir, unsigned long attrs)
{
	return dma_unmap_page_attrs(dev, addr, size, dir, attrs);
}


static inline dma_addr_t dma_map_page_attrs(struct device *dev,
		struct page *page, size_t offset, size_t size,
		enum dma_data_direction dir, unsigned long attrs)
{
	const struct dma_map_ops *ops = get_dma_ops(dev);
	dma_addr_t addr;

	BUG_ON(!valid_dma_direction(dir));
	if (dma_is_direct(ops))
		addr = dma_direct_map_page(dev, page, offset, size, dir, attrs);
	else
		addr = ops->map_page(dev, page, offset, size, dir, attrs);
	debug_dma_map_page(dev, page, offset, size, dir, addr);

	return addr;
}

static inline void dma_unmap_page_attrs(struct device *dev, dma_addr_t addr,
		size_t size, enum dma_data_direction dir, unsigned long attrs)
{
	const struct dma_map_ops *ops = get_dma_ops(dev);

	BUG_ON(!valid_dma_direction(dir));
	if (dma_is_direct(ops))
		dma_direct_unmap_page(dev, addr, size, dir, attrs);
	else if (ops->unmap_page)
		ops->unmap_page(dev, addr, size, dir, attrs);
	debug_dma_unmap_page(dev, addr, size, dir);
}

Определение dma_direct_map_page/dma_direct_unmap_page

dma_direct_map_page(), dma_direct_unmap_page() определены в kernel\dma\direct.c.

Язык кода:javascript
копировать
dma_addr_t dma_direct_map_page(struct device *dev, struct page *page,
		unsigned long offset, size_t size, enum dma_data_direction dir,
		unsigned long attrs)
{
	phys_addr_t phys = page_to_phys(page) + offset;
	dma_addr_t dma_addr = phys_to_dma(dev, phys);

	if (unlikely(!dma_direct_possible(dev, dma_addr, size)) &&
	    !swiotlb_map(dev, &phys, &dma_addr, size, dir, attrs)) {
		report_addr(dev, dma_addr, size);
		return DMA_MAPPING_ERROR;
	}

	if (!dev_is_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
		arch_sync_dma_for_device(dev, phys, size, dir);
	return dma_addr;
}
EXPORT_SYMBOL(dma_direct_map_page);


void dma_direct_unmap_page(struct device *dev, dma_addr_t addr,
		size_t size, enum dma_data_direction dir, unsigned long attrs)
{
	phys_addr_t phys = dma_to_phys(dev, addr);

	if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
		dma_direct_sync_single_for_cpu(dev, addr, size, dir);

	if (unlikely(is_swiotlb_buffer(phys)))
		swiotlb_tbl_unmap_single(dev, phys, size, size, dir, attrs);
}
EXPORT_SYMBOL(dma_direct_unmap_page);


void dma_direct_sync_single_for_cpu(struct device *dev,
        dma_addr_t addr, size_t size, enum dma_data_direction dir)
{
    phys_addr_t paddr = dma_to_phys(dev, addr);
 
    if (!dev_is_dma_coherent(dev)) {
        arch_sync_dma_for_cpu(dev, paddr, size, dir);
        arch_sync_dma_for_cpu_all(dev);
    }
 
    if (unlikely(is_swiotlb_buffer(paddr)))
        swiotlb_tbl_sync_single(dev, paddr, size, dir, SYNC_FOR_CPU);
}

Прослеживая весь путь, dma_map_single() в конечном итоге вызовет Arch_sync_dma_for_device(), dma_unmap_single() в конечном итоге вызовет Arch_sync_dma_for_cpu() и Arch_sync_dma_for_cpu_all(). А Arch_sync_dma_for_cpu_all() — пустая функция для Arm64.

Arch_sync_dma_for_device/arch_sync_dma_for_cpuопределение

Arch_sync_dma_for_device/arch_sync_dma_for_cpu определяется в файле Arch\arm64\mm\dma-mapping.c.

Язык кода:javascript
копировать
void arch_sync_dma_for_device(struct device *dev, phys_addr_t paddr,
		size_t size, enum dma_data_direction dir)
{
	__dma_map_area(phys_to_virt(paddr), size, dir);
}


void arch_sync_dma_for_cpu(struct device *dev, phys_addr_t paddr,
		size_t size, enum dma_data_direction dir)
{
	__dma_unmap_area(phys_to_virt(paddr), size, dir);
}

Определение __dma_map_area/__dma_unmap_area

Определение __dma_map_area/__dma_unmap_area находится в файле Arch\arm64\mm\cache.S.

Все реализации сборки также находятся в файлеarch\arm64\mm\cache.S.

Язык кода:javascript
копировать
/*
 *	__dma_map_area(start, size, dir)
 *	- start	- kernel virtual start address
 *	- size	- size of region
 *	- dir	- DMA direction
 */
ENTRY(__dma_map_area)
	cmp	w2, #DMA_FROM_DEVICE
	b.eq	__dma_inv_area
	b	__dma_clean_area
ENDPIPROC(__dma_map_area)

/*
 *	__dma_unmap_area(start, size, dir)
 *	- start	- kernel virtual start address
 *	- size	- size of region
 *	- dir	- DMA direction
 */
ENTRY(__dma_unmap_area)
	cmp	w2, #DMA_TO_DEVICE
	b.ne	__dma_inv_area
	ret
ENDPIPROC(__dma_unmap_area)

Видно, что __dma_map_area, вызываемая функцией серии карт, если направление DMA_FROM_DEVICE, выполните __dma_inv_area, в противном случае выполните __dma_clean_area; __dma_unmap_area, вызываемая функциями unmap series, если направление не DMA_TO_DEVICE, выполните __dma_inv_area, в противном случае выполните __dma_clean_area;

Краткое изложение следующее:

Operation

map

unmap

DMA_FROM_DEVICE

__dma_inv_area

__dma_inv_area

DMA_TO_DEVICE

__dma_clean_area

__dma_clean_area

__dma_inv_area завершает операцию аннулирования и удаляет данные кэша. Его аннотация:

Язык кода:javascript
копировать
Ensure that any D-cache lines for the interval [kaddr, kaddr+size)
 * 	are invalidated. Any partial lines at the ends of the interval are
 *	also cleaned to PoC to prevent data loss。

Что касается Invalidate, в руководстве ARM «Справочное руководство по архитектуре Arm для архитектуры A-профиля» объясняется следующее:

Язык кода:javascript
копировать
Invalidate  A cache invalidate instruction ensures that updates made visible by observers that access memory 
at the point to which the invalidate is defined, are made visible to an observer that controls the cache. 
This might result in the loss of updates to the locations affected by the invalidate instruction that 
have been written by observers that access the cache, if those updates have not been cleaned from 
the cache since they were made.
If the address of an entry on which the invalidate instruction operates is Normal, Non-cacheable or 
any type of Device memory then an invalidate instruction also ensures that this address is not 
present in the cache.

__dma_clean_area завершает операцию очистки и обновляет полученные данные в DDR. Его аннотация:

Язык кода:javascript
копировать
 Ensure that any D-cache lines for the interval [kaddr, kaddr+size)
 * 	are cleaned to the PoC.

Что касается Clean, в руководстве ARM объясняется следующее:

Язык кода:javascript
копировать
Clean  A cache clean instruction ensures that updates made by an observer that controls the cache are made 
visible to other observers that can access memory at the point to which the instruction is performed. 
Once the Clean has completed, the new memory values are guaranteed to be visible to the point to 
which the instruction is performed, for example to the Point of Unification.
The cleaning of a cache entry from a cache can overwrite memory that has been written by another 
observer only if the entry contains a location that has been written to by an observer in the 
shareability domain of that memory location.

Перед отправкой Ethernet выполняется сопоставление DMA_TO_DEVICE; после отправки выполняется отметка DMA_TO_DEVICE; оба выполняют __dma_clean_area для обновления данных из кэша в DDR;

Перед приемом Ethernet выполняется карта DMA_FROM_DEVICE, выполняется __dma_inv_area, и данные кэша удаляются; после приема выполняется unmap DMA_FROM_DEVICE, выполняется __dma_clean_area, и данные обновляются из кэша в DDR.

Обновление таблицы:

Operation

map,for_device

unmap,for_cpu

DMA_FROM_DEVICE

Перед получением __dma_inv_area

После получения __dma_inv_area

DMA_TO_DEVICE

Перед отправкой __dma_clean_area

После отправки __dma_clean_area

Я так и не понял, почему в драйвере Linux есть две операции с кэшем, и название немного сбивает с толку: сначала сопоставить, а потом отключить. В Standalone драйвере вам нужно только очистить кэш перед отправкой и аннулировать кэш после получения, нет необходимости оперировать кэшем после отправки и перед получением;

Немного понятно, что операция приема выполняется дважды. Возможно, другие модули вызывают повторную загрузку старых данных в кэш. Трудно понять, что отправка и выполнение двух операций с кэшем. Возможно, здесь также были изменены настройки таблицы MMU.

Раньше я также имел дело с проблемой. Предиктивное выполнение Arm приведет к чтению DDR, который не используется программным обеспечением. Чтобы предотвратить эту ситуацию, запись соответствующего адреса должна быть установлена ​​в таблице mmu как полностью недействительная. Возможно, Linux раньше сталкивался с некоторыми проблемами и перешел на эту операцию.

dma-coherent

Атрибут «dma-coherent» можно настроить в дереве устройств DMA.

of_dma_is_coherent() в файле driver\of\address.c считывает атрибут «dma-coherent».

Язык кода:javascript
копировать
bool of_dma_is_coherent(struct device_node *np)
{
    struct device_node *node = of_node_get(np);
 
    while (node) {
        if (of_property_read_bool(node, "dma-coherent")) {
            of_node_put(node);
            return true;
        }
        node = of_get_next_parent(node);
    }
    of_node_put(node);
    return false;
}

драйверы\of\Device.cintermediateof_dma_configure( ) вызовы of_dma_is_coherent( ) считывает атрибут «dma-coherent», а затем вызывает Arch_setup_dma_ops( ),Сохранить в переменной“dev->dma_coherent”середина。

Язык кода:javascript
копировать
/**
 * of_dma_configure - Setup DMA configuration
 * @dev:	Device to apply DMA configuration
 * @np:		Pointer to OF node having DMA configuration
 * @force_dma:  Whether device is to be set up by of_dma_configure() even if
 *		DMA capability is not explicitly described by firmware.
 *
 * Try to get devices's DMA configuration from DT and update it
 * accordingly.
 *
 * If platform code needs to use its own special DMA configuration, it
 * can use a platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE events
 * to fix up DMA configuration.
 */
int of_dma_configure(struct device *dev, struct device_node *np, bool force_dma)
{
    ........

	coherent = of_dma_is_coherent(np);
	dev_dbg(dev, "device is%sdma coherent\n",
		coherent ? " " : " not ");

	iommu = of_iommu_configure(dev, np);
	if (IS_ERR(iommu) && PTR_ERR(iommu) == -EPROBE_DEFER)
		return -EPROBE_DEFER;

	dev_dbg(dev, "device is%sbehind an iommu\n",
		iommu ? " " : " not ");

	arch_setup_dma_ops(dev, dma_addr, size, iommu, coherent);

	return 0;
}

Arch\arm64\mm\Dma-mapping.cсерединаизarch_setup_dma_ops( ),поставить настройки Сохранить в переменной“dev->dma_coherent”середина。

Язык кода:javascript
копировать
void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
			const struct iommu_ops *iommu, bool coherent)
{
	int cls = cache_line_size_of_cpu();

	WARN_TAINT(!coherent && cls > ARCH_DMA_MINALIGN,
		   TAINT_CPU_OUT_OF_SPEC,
		   "%s %s: ARCH_DMA_MINALIGN smaller than CTR_EL0.CWG (%d < %d)",
		   dev_driver_string(dev), dev_name(dev),
		   ARCH_DMA_MINALIGN, cls);

	dev->dma_coherent = coherent;
	if (iommu)
		iommu_setup_dma_ops(dev, dma_base, size);

#ifdef CONFIG_XEN
	if (xen_initial_domain())
		dev->dma_ops = &xen_swiotlb_dma_ops;
#endif

Определение и использование dev_is_dma_coherent

в dma_direct_map_page и,Вызовите dev_is_dma_coherent().,Проверьте указанные выше переменныеdev->dma_coherent,Проверьте, поддерживается ли синхронизация аппаратного кэша. в случае,Операция кэширования не будет выполнена.

Определение dev_is_dma_coherent находится в файле include\linux\dma-noncoherent.h.

Язык кода:javascript
копировать
static inline bool dev_is_dma_coherent(struct device *dev)
{
    return dev->dma_coherent;
}
boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose