reliable write to micro sd-card with O_SYNC and O_DIRECT not possible

From: Thomas Bechtold
Date: Thu May 10 2012 - 10:01:00 EST


Hi,

i tried to write some pages (each 512 byte) to a industrial micro
sd-card (from delkin devices, 2 GB) with O_SYNC and O_DIRECT but the
write process is not reliable when i remove the card.
i do the following:

1) open the device (with O_DIRECT and O_SYNC)
2) write the page to the card
3) close the device
4) open the device
5) read the page
6) compare the read page with the written page
7) if both pages are equal, the page-write was successful. otherwise, an
error code is returned.

The strange thing is, that my write-process returns successfully
(written and read page are equal)but when i later read the card, some
pages are empty.

I can reproduce this behavior with the attached testprogram when i do
the following:

1) start the program with ./mmc-test write //and fix the device path
MEMORY_DEVICE_PATH before you start the program
2) remove and attach the memory card 10x
3) start the program with ./mmc-test read

now there are some pages which should be successfully written, but in
fact are unavailable.

i tested this with a usb-mmc-card adapter on i386 (debian 3.2 backports
kernel)and with the atmel-mci driver on arm at91 platform (3.2.16
upstream kernel).

TIA,

Tom
/**
* Test page write on linux (thomasbechtold@xxxxxxxxxxx)
*
* Compile with: gcc -o mmc-test mmc-write.c
*
* Usage: ./mmc-test write //write to the given device some pages
* Usage: ./mmc-test read //read the pages from the device
*
*
* show the written pages with hexdump: for X in {0..800}; do hexdump -C /dev/sdb -n 5 -s "$X"b; done
*/

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


#define PAGE_SIZE 512
#define MESSAGE_LENGTH 100

/* CHANGE THIS TO YOUR MMC DEVICE PATH */
#define MEMORY_DEVICE_PATH "/dev/sdb"


/* print the given message to stdout but if the same message was already printed, just print a '*' */
void myprint (char *message)
{
static char message_cache[MESSAGE_LENGTH];
if (memcmp (message, &message_cache, MESSAGE_LENGTH) == 0)
{
// same message again
printf ("*");
}
else
{
// new message
printf ("\n%s ", message);
memcpy (&message_cache, message , MESSAGE_LENGTH);
}
}

void write_page (unsigned long id)
{
//the page we want to write
char page[PAGE_SIZE] = { 0 };
page[0] = 'P';
memcpy (&page[1], &id, sizeof (unsigned long));

//file descriptor to write and read the page
int memory_fd = -1;

//loop as long as the page-write (and read) was unsucessful
while (1)
{
//buffer for a console message
char message[100] = { 0 };

close (memory_fd);
memory_fd = -1;

errno = 0;
memory_fd = open (MEMORY_DEVICE_PATH, O_RDWR, O_SYNC | O_DIRECT);
if (memory_fd >= 0)
{
//seek to correct position (to write the page)
off_t lseek_write_res = -1;
errno = 0;
lseek_write_res = lseek (memory_fd, id * PAGE_SIZE, SEEK_SET);
if (lseek_write_res == -1 || lseek_write_res != id * PAGE_SIZE)
{
sprintf (message, "%lu: seek failed: %s\n", id, strerror (errno));
myprint (message);
continue;
}

//write page
errno = 0;
ssize_t write_res = 0;
write_res = write (memory_fd, &page, PAGE_SIZE);
if (write_res == -1 || write_res != PAGE_SIZE)
{
sprintf (message, "%lu: write failed: %s\n", id, strerror (errno));
myprint (message);
continue;
}

//close and reopen the device to read and check the page
close (memory_fd);
memory_fd = open (MEMORY_DEVICE_PATH, O_RDONLY);

//seek to correct position (to read the written page)
errno = 0;
off_t lseek_read_res = -1;
lseek_read_res = lseek (memory_fd, id * PAGE_SIZE, SEEK_SET);
if (lseek_read_res == -1 || lseek_read_res != id * PAGE_SIZE)
{
sprintf (message, "%lu: seek failed: %s\n", id, strerror (errno));
myprint (message);
continue;
}

//read the written page
char page_read[PAGE_SIZE] = { 0 };
errno = 0;
ssize_t read_res = 0;
read_res = read (memory_fd, &page_read, PAGE_SIZE);
if (read_res == -1 || read_res != PAGE_SIZE)
{
sprintf (message, "%lu: read failed: %s\n", id, strerror (errno));
myprint (message);
continue;
}

//compare the written page with the read page
if (memcmp (&page, &page_read, sizeof (PAGE_SIZE)) != 0)
{
sprintf (message, "%lu: compare failed.\n", id);
myprint (message);
continue;
}

//wrote successfully the page
break;
}
else
{
sprintf (message, "%lu: open failed: %s\n", id, strerror (errno));
myprint (message);
}

}
close (memory_fd);
printf ("\n%lu: sucessfully written", id);
}

void read_page (unsigned long id)
{
char page[PAGE_SIZE] = { 0 };
page[0] = 'P';
memcpy (&page[1], &id, sizeof (unsigned long));
int memory_fd = -1;
// open device
while (1)
{
errno = 0;
close (memory_fd);
memory_fd = -1;
//memory_fd = open (MEMORY_DEVICE_PATH, O_DIRECT | O_SYNC, O_RDWR);
memory_fd = open (MEMORY_DEVICE_PATH, O_RDWR);
if (memory_fd >= 0)
{
//seek to correct position (to read the written page)
errno = 0;
off_t lseek_read_res = -1;
lseek_read_res = lseek (memory_fd, id * PAGE_SIZE, SEEK_SET);
if (lseek_read_res == -1 || lseek_read_res != id * PAGE_SIZE)
{
printf ("%lu: seek failed: %s\n", id, strerror (errno));
continue;
}

//read the written page
char page_read[PAGE_SIZE] = { 0 };
errno = 0;
ssize_t read_res = 0;
read_res = read (memory_fd, &page_read, PAGE_SIZE);
if (read_res == -1 || read_res != PAGE_SIZE)
{
printf ("%lu: read failed: %s\n", id, strerror (errno));
continue;
}

//compare page
if (memcmp (&page, &page_read, sizeof (PAGE_SIZE)) != 0)
{
printf ("%lu: compare failed.\n", id);
close (memory_fd);
exit (-1);
}
//read successfully the page
break;
}
else
{
printf ("%lu: open failed: %s\n", id, strerror (errno));
}

}
close (memory_fd);
}


int main (int argc, char *argv[])
{
if (argc != 2)
{
printf ("please tell mode: 'read' or 'write'\n");
exit (-1);
}
//function to use for the loop
void (*mode)(unsigned long) = NULL;
if (strcmp (argv[1], "read") == 0)
{
mode = &read_page;
}
else if (strcmp (argv[1], "write") == 0)
{
mode = &write_page;
}
else
{
printf ("unknown mode '%s'\n", argv[1]);
exit (-1);
}


unsigned long x;
for (x = 0; ; x++ )
{

mode (x);
}
printf ("\n");
exit(EXIT_SUCCESS);
}