LoneDuck
LoneDuck

Reputation: 1872

Write error: Invalid argument, when file is opened with O_DIRECT

it is very important to me to write to a file with the O_DIRECT flag.

This is how I open the file:

//Open the file
int fd;
if((fd = open(inFilepath, O_WRONLY | O_CREAT |O_SYNC |O_DIRECT,S_IRUSR|S_IWUSR))<0) {
    //Error handling
    return;
}

I know about O_DIRECT's alignment restrictions. This is why I initialize my buffer with calloc:

char *buff = (char *) calloc((size_t) 1,sizeof(char));

if(write(fd,buff,(size_t)1)<1) {
    //Error logging
    free(buff);
    return -1;
}

And I get the write: Invalid argument error. I even tried to use more extreme measures such as memalign and posix_memalign, but had issues with them (memalign got stuck, and posix_memalign is missing for the ARM processor).

When I comment out the O_DIRECT flag, everything works as it should (but I/O is not direct, which is what I need).

Anyone has any insight as to why this is happening? If O_DIRECT was not implemented in Android, then it should've failed at open(), not at write(); so I must be doing something wrong!

Thanks -LD

Upvotes: 7

Views: 7447

Answers (2)

LoneDuck
LoneDuck

Reputation: 1872

I solved it (with your guidance)- and wanted to post my solution in case anyone in the future has similar problems.

The trick was that with the O_DIRECT flag you need to align both the memory address and your buffer to the filesystem's block size (or at least, block size worked for me; sector didn't).

struct stat fstat;
stat(filepath, &fstat); 
int blksize = (int)fstat.st_blksize;
int align = blksize-1;

const char *buff = (char *) malloc((int)blksize+align);
buff = (char *)(((uintptr_t)buff+align)&~((uintptr_t)align));

if(write(fd,buff,(size_t)blksize)<1) { 
        //Error handling
        free((char *)buff);
        return -1;
}

I did two main things:

  1. Found the filesystem hosting my file's block size using stat() and accessing the st_blksize attribute.
  2. Allocated align more bytes than I need. I then added those extra align bytes to the pointer address so that masking off the bits to the lower block size alignment wouldn't leave me with less memory allocated than I wanted. Then of course you AND the bits with the mask (created by flipping the bits of align which is blksize-1), and voila- your buffer is blksize-aligned.

Also note that the amount you write also has to be aligned to block size (at least in my case).

-LD

Upvotes: 13

ctrl-d
ctrl-d

Reputation: 392

Calloc will not align the memory good enough in this case. Allocate more memory than you need, and round it up to the next 4k or so page. Also read the notes below in the manpage for open() with O_DIRECT.

Upvotes: 2

Related Questions