Сбой запроса записи ядра AIO (libaio) при записи из памяти, зарезервированной во время загрузки

Я довольно новичок в Linux AIO (Libaio), и я надеюсь, что кто-то здесь с большим опытом может помочь мне.

У меня есть система, которая выполняет высокоскоростной DMA от устройства PCI к системной памяти. Затем я хочу записать данные на диск с помощью libaio непосредственно из адресного пространства DMA. В настоящее время память, используемая для DMA, зарезервирована при загрузке с помощью команды загрузки ядра «memmap».

В моем приложении память отображается с помощью mmap на указатель адреса виртуального пространства пользователя, который, как я считаю, должен быть в качестве буфера для передачи в вызов io_prep_pwrite (). Однако, когда я делаю это, запись не кажется успешной. Когда запрос обрабатывается с помощью io_getevents, поле «res» имеет код ошибки -22, который печатается как «Bad Address».

Однако, если я выполняю memcpy из ранее отображенной области памяти в новый буфер, выделенный моим приложением, а затем использую указатель на этот локальный буфер в вызове io_prep_pwrite, запрос работает нормально, и данные записываются на диск. Проблема состоит в том, что выполнение memcpy создает узкое место для скорости, с которой мне нужно передавать данные на диск, и я не могу идти в ногу со скоростью DMA.

Я не понимаю, почему я должен сначала скопировать память, чтобы запрос на запись работал. Я создал минимальный пример, чтобы проиллюстрировать проблему ниже. BufBaseLoc — это сопоставленный адрес, а localBuffer — это адрес, на который копируются данные. Я не хочу выполнять следующую строку:

memcpy(localBuffer, bufBaseLoc, WRITE_SIZE);

…или иметь локальный буфер вообще. В разделе «Подготовка IOCB» я хочу использовать:

io_prep_pwrite(iocb, fid, bufBaseLoc, WRITE_SIZE, 0);

…Но это не работает. Но локальный буфер, который является просто копией, работает.

У кого-нибудь есть понимание, почему? Любая помощь будет принята с благодарностью.

Спасибо,

#include <cstdio>
#include <string>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <libaio.h>

#define WRITE_SIZE 0x80000              //4MB buffer
#define DMA_BASE_ADDRESS 0x780000000    //Physical address of memory reserved at boot

//Reaping callback
void writeCallback(io_context_t ctx, struct iocb *iocb, long res, long res2)
{
//Res is number of bytes written by the request.  It should match the requested IO size.  If negative it is an error code
if(res != (long)iocb->u.c.nbytes)
{
fprintf(stderr, "WRITE_ERROR: %s\n", strerror(-res));
}
else
{
fprintf(stderr, "Success\n");
}
}

int main()
{
//Initialize Kernel AIO
io_context_t ctx = 0;
io_queue_init(256, &ctx);

//Open /dev/mem and map physical address to userspace
int fdmem = open("/dev/mem", O_RDWR);
void *bufBaseLoc = mmap(NULL, WRITE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fdmem, DMA_BASE_ADDRESS);

//Set memory here for test (normally done be DMA)
memset(bufBaseLoc, 1, WRITE_SIZE);

//Initialize Local Memory Buffer (DON’T WANT TO HAVE TO DO THIS)
uint8_t* localBuffer;
posix_memalign((void**)&localBuffer, 4096, WRITE_SIZE);
memset(localBuffer, 1, WRITE_SIZE);

//Open/Allocate file on disk
std::string filename = "tmpData.dat";
int fid = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_DIRECT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
posix_fallocate(fid, 0, WRITE_SIZE);

//Copy from DMA buffer to local process buffer (THIS IS THE COPY I WANT TO AVOID)
memcpy(localBuffer, bufBaseLoc, WRITE_SIZE);

//Prepare IOCB
struct iocb *iocbs[1];
struct iocb *iocb = (struct iocb*) malloc(sizeof (struct iocb));
io_prep_pwrite(iocb, fid, localBuffer, WRITE_SIZE, 0);      //<--THIS WORKS (but is not what I want to do)
//io_prep_pwrite(iocb, fid, bufBaseLoc, WRITE_SIZE, 0);  //<--THIS DOES NOT WORK (but is what I want to do)
io_set_callback(iocb, writeCallback);
iocbs[0] = iocb;

//Send Request
int res = io_submit(ctx, 1, iocbs);
if (res !=1)
{
fprintf(stderr, "IO_SUBMIT_ERROR: %s\n", strerror(-res));
}

//Reap Request
struct io_event events[1];
size_t ret = io_getevents(ctx, 1, 1, events, 0);
if (ret==1)
{
io_callback_t cb=(io_callback_t)events[0].data;
struct iocb *iocb_e = events[0].obj;
cb(ctx, iocb_e, events[0].res, events[0].res2);
}

}

1

Решение

Возможно, ваш исходный буфер не выровнен.

Любой буфер, который libAIO записывает на диск, должен быть выровнен (я думаю, около 4K). Когда вы выделяете временный буфер, вы используете posix_memalign, который выделит его правильно выровненным, что означает, что запись будет успешной. Если исходный зарезервированный буфер не выровнен, это может вызвать проблемы.

Если вы можете попытаться выделить этот начальный буфер с выравниванием, это может решить проблему.

0

Другие решения