2

Board Introduction:

I am working on a board that has ST40 chip on it basically used for capturing the DVB stream and displaying it on the TV. The board is running on Linux OS.

Problem Description:

I am trying to read data from a large file(approximately 2 GB) on USB using O_DIRECT flag. Here is the relevant code snippet:

char subblk[BLKSIZE];
open (filename2,O_CREAT|O_WRONLY|O_DIRECT,S_IRWXU|S_IRWXG|S_IRWXO);
read (fp,subblk,BLKSIZE);

It says read failed with error number 22 - "EINVAL 22 /* Invalid argument"

To clarify whether this a programming issue or some architecture dependent problem, I ran the same code on my Desktop system, it worked perfectly fine and I was able to print the characters what I just read. What is the reason it is failing on my ST40 board?

Sandeep
  • 18,356
  • 16
  • 68
  • 108
  • Was the test on the desktop also reading from (the very same) external USB storage device? – unwind May 09 '12 at 09:15
  • My official system is Windows. There is a remote Linux server on which we are given logins. So i cannot connect a USB device to that system. Hence i copied the same file into my local working directory and tested the code. It was working fine.. – Sandeep May 09 '12 at 09:34

3 Answers3

10

You should align buffer by filesystem block boundary. To achieve this you should not use buffer on the stack (like in your example), nor calling malloc(BLKSIZE), but you should use memalign(). So your code will be:

/* make sure BLKSIZE is defined as 512 */
char *subblk = memalign(BLKSIZE, BLKSIZE);
open (filename2,O_CREAT|O_WRONLY|O_DIRECT,S_IRWXU|S_IRWXG|S_IRWXO);
read (fp,subblk,BLKSIZE);

Why your code is working on the desktop is probably accidental alignment or different filesystem type (w/o alignment requirement).

bstpierre
  • 30,042
  • 15
  • 70
  • 103
catpnosis
  • 546
  • 4
  • 13
2

To quote from the manual page:

The O_DIRECT flag may impose alignment restrictions on the length and address of userspace buffers and the file offset of I/Os. In Linux alignment restrictions vary by file system and kernel version and might be absent entirely. However there is currently no file system-independent interface for an application to discover these restrictions for a given file or file system. Some file systems provide their own interfaces for doing so, for example the XFS_IOC_DIOINFO operation in xfsctl(3).

Under Linux 2.4, transfer sizes, and the alignment of the user buffer and the file offset must all be multiples of the logical block size of the file system. Under Linux 2.6, alignment to 512-byte boundaries suffices.

Is your subblk block well-aligned? Do both systems have the same file system and/or kernel version?

torek
  • 448,244
  • 59
  • 642
  • 775
  • The kernel versions are different. What does Allignment mean? Is it that the total block size must be multiples of 512? – Sandeep May 09 '12 at 09:48
  • 1
    Alignment refers both to the virtual address of the buffer, and the block size. In particular some file systems require that the buffer address be congruent to zero mod K where K is some magic constant that depends on kernel and/or file system versions. It can be 512, 1024, 2048, or 4096 on some common systems. Often, using `malloc` to obtain the buffer memory, and making the size a multiple of the underlying file system block size, will suffice; but as the manual page notes, the restrictions are not always easily found. – torek May 09 '12 at 09:53
  • Thanks... In my case the block size i am requesting is multiple of 512.. Will try using Malloc. – Sandeep May 09 '12 at 10:44
  • @happy2Help: see http://stackoverflow.com/questions/6563120/what-does-posix-memalign-memalign-do for an alternative to malloc for this – Mat May 09 '12 at 11:19
  • @torekThanks for the help. DMA module is not enabled in the kernel, which is why read is failing. But i think this condition should ideally be handled by linux kernel in the open system call itself. It should check whether DMA is enable or not(when given O_DIRECT) and throw a error in case it is not enabled – Sandeep May 09 '12 at 11:25
1

O_DIRECT flag uses DMA internally and in my kernel, DMA is not enabled. This is the basic reason why it was working on my Desktop pc but not functional on the Board.. They had different kernels in them one in which DMA was enabled and other in which DMA was not enabled..

Sandeep
  • 18,356
  • 16
  • 68
  • 108