Skip to contentSkip to conversation

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

DISCUSSION Thomas Bechtold 1 participant 1 message May 10, 2012 MMC, SD and SDIO

lore
Share & tools
lore
#0 lore raw

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 ([email protected])
*
* 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);
}