从引导时保留的内存写入时,内核AIO(libaio)写入请求失败

时间:2015-04-17 18:40:56

标签: c++ linux asynchronous io aio

我是linux aio(libaio)的新手,我希望有更多经验的人可以帮助我。

我有一个从PCI设备到系统RAM执行高速DMA的系统。然后,我想直接从DMA地址空间使用libaio将数据写入磁盘。目前,用于DMA的内存通过使用" memmap"来保留。内核启动命令。

在我的应用程序中,内存使用mmap映射到虚拟用户空间地址指针,我相信我应该能够将其作为缓冲区传递给io_prep_pwrite()调用。但是,当我这样做时,写入似乎并不成功。当使用io_getevents获取请求时," res"字段有错误代码-22,打印为"错误地址"。

但是,如果我从先前映射的内存位置执行memcpy到我的应用程序分配的新缓冲区,然后在调用io_prep_pwrite时使用指向此本地缓冲区的指针,请求就可以了,数据写入磁盘。问题是,执行memcpy会为我需要将数据流式传输到磁盘的速度创造一个瓶颈,而我无法跟上DMA速率。

我不明白为什么我必须先复制内存才能使写请求生效。我创建了一个最小的例子来说明下面的问题。 bufBaseLoc是映射地址,localBuffer是数据复制到的地址。我不想要执行以下行:

memcpy(localBuffer, bufBaseLoc, WRITE_SIZE);

...或者拥有localBuffer。在"准备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 个答案:

答案 0 :(得分:0)

可能是您的原始缓冲区未对齐。

libAIO写入磁盘的任何缓冲区都需要对齐(我认为约为4K)。分配临时缓冲区时,使用posix_memalign,它将正确对齐分配的缓冲区,这意味着写入将成功。如果您原始的保留缓冲区未对齐,则可能会导致您出现问题。

如果您可以尝试使用对齐的分配来分配初始缓冲区,则可以解决此问题。