bripappas
bripappas

Reputation: 86

Kernel AIO (libaio) write request fails when writing from memory reserved at boot time

I am pretty new to linux aio (libaio) and am hoping someone here with more experience can help me out.

I have a system that performs high-speed DMA from a PCI device to the system RAM. I then want to write the data to disk using libaio directly from the DMA address space. Currently the memory used for DMA is reserved on boot through the use of the "memmap" kernel boot command.

In my application, the memory is mapped with mmap to a virtual userspace address pointer which I believe I should be able to pass as my buffer to the io_prep_pwrite() call. However, when I do this, the write does not seem to succeed. When the request is reaped with io_getevents the "res" field has error code -22 which prints as "Bad Address".

However, if I do a memcpy from the previously mapped memory location to a new buffer allocated by my application and then use the pointer to this local buffer in the call to io_prep_pwrite the request works just fine and the data is written to disk. The problem is that performing the memcpy creates a bottle-neck for the speed at which I need to stream data to disk and I am unable to keep up with the DMA rate.

I do not understand why I have to copy the memory first for the write request to work. I created a minimal example to illustrate the issue below. The bufBaseLoc is the mapped address and the localBuffer is the address the data is copied to. I do not want to have to perform the following line:

memcpy(localBuffer, bufBaseLoc, WRITE_SIZE);

...or have the localBuffer at all. In the "Prepare IOCB" section I want to use:

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

...but it does not work. But the local buffer, which is just a copy, does work.

Does anyone have any insight into why? Any help would be greatly appreciated.

Thanks,

#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);
  }

}

Upvotes: 1

Views: 1430

Answers (1)

Jack Turpitt
Jack Turpitt

Reputation: 204

It could be that your original buffer is not aligned.

Any buffer that libAIO writes to disk needs to be aligned(to around 4K I think). When you allocate your temporary buffer you use posix_memalign, which will allocate it correctly aligned, meaning the write will succeed. If your original reserved buffer is not aligned it could be causing you issues.

If you can try to allocate that initial buffer with an aligned alloc it could solve the issue.

Upvotes: 1

Related Questions