0

I have a USB device that presents as a mass storage device, dmesg looks like this:

[    4.416584] scsi 0:0:0:0: Direct-Access     Adap ECU Modular ECU      1.0  PQ: 0 ANSI: 2
[    4.420186] sd 0:0:0:0: [sda] 131072 512-byte logical blocks: (67.1 MB/64.0 MiB)
[    4.421063] sd 0:0:0:0: [sda] Write Protect is off
[    4.421084] sd 0:0:0:0: [sda] Mode Sense: 03 00 00 00
[    4.422053] sd 0:0:0:0: [sda] No Caching mode page found
[    4.422067] sd 0:0:0:0: [sda] Assuming drive cache: write through
[    7.446823] sd 0:0:0:0: Attached scsi generic sg0 type 0

It does not contain a filesystem, nor is it supposed to. You don't mount it, you merely open and read (in this case) /dev/sda

The issue is when I open and read the device, I will get what I want the first time around, and then each subsequent read I will get (what I assume is) cached data. I make this assumption because the IO light on the device does not flash after the subsequent reads, only on the first read and of course because my fread() contains exactly the same data as the first fread().

My code is fine because I compiled it on Windows and specified \.\X: for the file to open and it was able to poll the data just fine with fresh data each call.

So I am going to assume that Linux is caching/buffering the read.

My code looks like this:

#define STARTBYTE 272384
#define ENDBYTE 274432
#define NUM_VARS 1024

int main() {
    FILE *input = fopen("/dev/sda", "r+");
    setbuf(input, NULL);
    int exit = 0;
    while (exit < 1) {
        signed short liveBuffer[NUM_VARS];
        fseek(input, STARTBYTE, SEEK_SET);
        fread(liveBuffer, 2, NUM_VARS, input);
        fflush(input);
        // do stuff with the now filled liveBuffer data
    }
    fclose(input);
    return 0;
}

I've also tried with open() and specifying O_DIRECT, with no difference.

The aim is, I open the device (/dev/sda), seek to 272384 bytes. Read 2048 bytes. Re-seek to the same position, read 2048 bytes, etc etc.

If I fclose() the file and re-open() it, I get fresh data. Except it is SLOW. Approximately 10 samples/sec, when on Windows (without the open/close) I get around 50/sec.

One think I have noticed is that when I let my compiled code run for a few seconds and then kill it (CTRL+C), I then see the activity light on the device flash madly for about the duration I let my code run for.

Can anyone point me in the right direction?

I've been going at this for days, I think I'm about to hang myself.

  • What happens if you read instead from the associated generic SCSI device (/dev/sg0 in your dmesg, number may change)? Is it still cached? – dirkt Jun 21 '17 at 14:57
  • When I read from /dev/sg0 I don't get any data – Josh Finlay Jun 22 '17 at 2:46
1

You have complicated the matter a little by using the intervening stdio layer, and by ignoring the return codes of the functions you are calling. You can use O_DIRECT to get fresh data from the device, but you must respect the obligations that this imposes.

In particular the seek offset, buffer address, and size of i/o must all be multiples of 4096 (or some other power of 2 depending on the device).

If you modify your code to remove your current declaration of liveBuffer and include the following code at the start, you should find it works.

#define PAGE 4096
#define STARTBYTE (272384/PAGE*PAGE) // must align
#define OFFSET (272384-STARTBYTE)
#define ITEMSIZE (sizeof(*liveBuffer))
#define LIVEBUFSIZE ((OFFSET+NUM_VARS*ITEMSIZE+PAGE-1)/PAGE*PAGE)

signed short *liveBuffer;
if(posix_memalign((void**)&liveBuffer, PAGE, LIVEBUFSIZE)!=0)
   exit(5);
if (fcntl(fileno(input), F_SETFL, O_DIRECT) == -1)
   exit(6);

You must now fread() using LIVEBUFSIZE/ITEMSIZE rather than NUM_VARS. As the STARTBYTE has to be aligned, you now need to start further into your liveBuffer array at OFFSET/ITEMSIZE to find the wanted data. You should also change all the calls to check for correct return codes.

  • Would I be correct in saying that I still fseek(input, STARTBYTE, SEEK_SET); as the STARTBYTE has changed to your code. But now for read I do fread(liveBuffer, ITEMSIZE, LIVEBUFSIZE, input); ? – Josh Finlay Jun 24 '17 at 2:09
  • yes, you must seek to a multiple of page, and read a multiple of page into a memory area whose address is a multiple of page. You might find more examples if you abandon stdio and just use the basic open, lseek, read of the system. I suppose when they added O_DIRECT they should have thought of integrating it into stdio somehow so this would be done for you. – meuh Jun 24 '17 at 6:08
  • THANK YOU. This device uses a page size of 512. I modified that and its working perfectly. You my friend, are awesome. – Josh Finlay Jun 24 '17 at 10:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.