LINUX.ORG.RU

Драйвер с поддержкой DMA и непонятное поведение dma_sync_sg_for_cpu

 , ,


0

2

Пишу драйвер с поддержкой DMA. Система Ubuntu 15.04 x64, ядро 3.19.0. Столкнулся с тем, что если использую dma_sync_sg_for_cpu с числом элементов в списке > 1, то данные (32 битный счетчик) приходят испорченными в некоторых областях буфера. В большинстве случаев эти области начинаются с нового элемента sg списка и выглядит это так:

//так должно быть
01 00 ...
....
10 00 ...
....
20 00 ...
...
30 00 ...

//такие данные приходят
01 00 ...
....
20 00 ...
....
30 00 ...
...
00 00 ...

Вот работающий код

int i, refcnt, mapcnt;
size_t DmaBufSize;
u_int8_t *DmaBufBaseCreate;
dma_addr_t DmaBufBaseLACreate;
struct scatterlist *sgl;
struct device *device;

refcnt = 1;

sgl = (struct scatterlist *)kmalloc(refcnt * sizeof(struct scatterlist), GFP_KERNEL);

DmaBufBaseCreate = kmalloc(DmaBufSize + PAGE_SIZE, GFP_KERNEL | GFP_DMA32);

sg_init_table(sgl, refcnt);		

sg_set_buf(sgl, DmaBufBaseCreate, DmaBufSize + PAGE_SIZE);

mapcnt = dma_map_sg(device, sgl, refcnt, DMA_BIDIRECTIONAL);

DmaBufBaseLACreate = sg_dma_address(sgl); // по этому адресу устройство осуществляет запись данных

/*
по завершении записи в буфер
перед копированием в user space буфер вызывается
*/

dma_sync_sg_for_cpu(device, sgl, refcnt, DMA_BIDIRECTIONAL);

Код, получающий «битые» данные

int i, refcnt, mapcnt;
size_t DmaBufSize, size, blocksize;
u_int8_t *buf;
u_int8_t *DmaBufBaseCreate;
dma_addr_t DmaBufBaseLACreate;
struct scatterlist *sgl;
struct scatterlist *sg;
struct device *device;

blocksize = 64*1024;
refcnt = ((DmaBufSize + PAGE_SIZE) % blocksize == 0)?
(DmaBufSize + PAGE_SIZE) / blocksize:
((DmaBufSize + PAGE_SIZE) / blocksize + 1);

sgl = (struct scatterlist *)kmalloc(refcnt * sizeof(struct scatterlist), GFP_KERNEL);

DmaBufBaseCreate = kmalloc(DmaBufSize + PAGE_SIZE, GFP_KERNEL | GFP_DMA32);

size = DmaBufSize + PAGE_SIZE;
buf = DmaBufBaseCreate;
sg_init_table(sgl, refcnt);		
for_each_sg(sgl, sg, refcnt, i)
{
	if (size < blocksize) blocksize = size;
	
	sg_set_buf(sg, buf, blocksize);
	
	buf += blocksize;
	size -= blocksize;
}

mapcnt = dma_map_sg(device, sgl, refcnt, DMA_BIDIRECTIONAL);

DmaBufBaseLACreate = sg_dma_address(sgl); // по этому адресу устройство осуществляет запись данных

/*
по завершении записи в буфер
перед копированием в user space буфер вызывается
*/

dma_sync_sg_for_cpu(device, sgl, refcnt, DMA_BIDIRECTIONAL);

В чем может быть причина?



Последнее исправление: Klymedy (всего исправлений: 1)

А кто сказал, что во втором случае sg_dma_address(sgl) будет адресом с которого устройство может непрерывно записать что-то длинной > blocksize?

anonymous
()
Ответ на: комментарий от anonymous

kmalloc выделяет физ. непрерывный блок памяти. Я проверял адреса всех sg элементов списка - адреса идут подряд.

cppdev
() автор топика

Решение найдено. Во все функции, принимающие в качестве параметра struct device, необходимо передавать поле dev из struct pci_dev.

cppdev
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.