From 7fab7fc0cf00718dd917bc53efea75a702663498 Mon Sep 17 00:00:00 2001 From: Ralf Kirchner Date: Wed, 5 Dec 2012 10:54:17 +0100 Subject: libblock: Add sparse disk --- cpukit/Makefile.am | 1 + cpukit/libblock/Makefile.am | 1 + cpukit/libblock/include/rtems/sparse-disk.h | 137 +++++++ cpukit/libblock/src/sparse-disk.c | 387 +++++++++++++++++++ cpukit/preinstall.am | 4 + testsuites/libtests/Makefile.am | 1 + testsuites/libtests/configure.ac | 1 + testsuites/libtests/sparsedisk01/Makefile.am | 19 + testsuites/libtests/sparsedisk01/init.c | 443 ++++++++++++++++++++++ testsuites/libtests/sparsedisk01/sparsedisk01.doc | 12 + testsuites/libtests/sparsedisk01/sparsedisk01.scn | 2 + 11 files changed, 1008 insertions(+) create mode 100644 cpukit/libblock/include/rtems/sparse-disk.h create mode 100644 cpukit/libblock/src/sparse-disk.c create mode 100644 testsuites/libtests/sparsedisk01/Makefile.am create mode 100644 testsuites/libtests/sparsedisk01/init.c create mode 100644 testsuites/libtests/sparsedisk01/sparsedisk01.doc create mode 100644 testsuites/libtests/sparsedisk01/sparsedisk01.scn diff --git a/cpukit/Makefile.am b/cpukit/Makefile.am index b5569fa280..5b11e7e6f6 100644 --- a/cpukit/Makefile.am +++ b/cpukit/Makefile.am @@ -124,6 +124,7 @@ include_rtems_HEADERS += libblock/include/rtems/flashdisk.h include_rtems_HEADERS += libblock/include/rtems/ramdisk.h include_rtems_HEADERS += libblock/include/rtems/nvdisk.h include_rtems_HEADERS += libblock/include/rtems/nvdisk-sram.h +include_rtems_HEADERS += libblock/include/rtems/sparse-disk.h include_rtems_HEADERS += libblock/include/rtems/ide_part_table.h include_rtems_HEADERS += libblock/include/rtems/bdpart.h include_rtems_HEADERS += libblock/include/rtems/media.h diff --git a/cpukit/libblock/Makefile.am b/cpukit/libblock/Makefile.am index d07eb5e177..cafd1fcba0 100644 --- a/cpukit/libblock/Makefile.am +++ b/cpukit/libblock/Makefile.am @@ -31,6 +31,7 @@ libblock_a_SOURCES = src/bdbuf.c \ src/media-server.c \ src/media-desc.c \ src/media-dev-ident.c \ + src/sparse-disk.c \ include/rtems/bdbuf.h include/rtems/blkdev.h \ include/rtems/diskdevs.h include/rtems/flashdisk.h \ include/rtems/ramdisk.h include/rtems/nvdisk.h include/rtems/nvdisk-sram.h \ diff --git a/cpukit/libblock/include/rtems/sparse-disk.h b/cpukit/libblock/include/rtems/sparse-disk.h new file mode 100644 index 0000000000..3dc806b407 --- /dev/null +++ b/cpukit/libblock/include/rtems/sparse-disk.h @@ -0,0 +1,137 @@ +/** + * @file + * + * @ingroup rtems_sparse_disk + * + * @brief Sparse disk block device API. + */ + +/* + * Copyright (c) 2012 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#ifndef SPARSE_DISK_H +#define SPARSE_DISK_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @defgroup rtems_sparse_disk Sparse Disk Device + * + * @ingroup rtems_blkdev + * + * @{ + */ + +typedef struct { + rtems_blkdev_bnum block; + void *data; +} rtems_sparse_disk_key; + +typedef struct rtems_sparse_disk rtems_sparse_disk; + +typedef void (*rtems_sparse_disk_delete_handler)(rtems_sparse_disk *sparse_disk); + +struct rtems_sparse_disk { + rtems_id mutex; + rtems_blkdev_bnum blocks_with_buffer; + size_t used_count; + uint32_t media_block_size; + rtems_sparse_disk_delete_handler delete_handler; + uint8_t fill_pattern; + rtems_sparse_disk_key *key_table; +}; + +/** + * @brief Creates and registers a sparse disk. + * + * @param[in] device_file_name The device file name path. + * @param[in] media_block_size The media block size in bytes. + * @param[in] blocks_with_buffer Blocks of the device with a buffer. Other + * blocks can store only fill pattern value bytes. + * @param[in] block_count The media block count of the device. It is the sum + * of blocks with buffer and blocks that contain only fill pattern value bytes. + * @param[in] fill_pattern The fill pattern specifies the byte value of blocks + * without a buffer. It is also the initial value for blocks with a buffer. + * + * @retval RTEMS_SUCCESSFUL Successful operation. + * @retval RTEMS_INVALID_NUMBER Media block size or media block count is not + * positive. The blocks with buffer count is greater than the media block count. + * @retval RTEMS_NO_MEMORY Not enough memory. + * @retval RTEMS_TOO_MANY Cannot create semaphore. + * @retval RTEMS_UNSATISFIED Cannot create generic device node. + * + * @see rtems_sparse_disk_register(). + */ +rtems_status_code rtems_sparse_disk_create_and_register( + const char *device_file_name, + uint32_t media_block_size, + rtems_blkdev_bnum blocks_with_buffer, + rtems_blkdev_bnum media_block_count, + uint8_t fill_pattern +); + +/** + * @brief Frees a sparse disk. + * + * Calls free() on the sparse disk pointer. + */ +void rtems_sparse_disk_free( rtems_sparse_disk *sparse_disk ); + +/** + * @brief Initializes and registers a sparse disk. + * + * This will create one semaphore for mutual exclusion. + * + * @param[in] device_file_name The device file name path. + * @param[in, out] sparse_disk The sparse disk. + * @param[in] media_block_size The media block size in bytes. + * @param[in] blocks_with_buffer Blocks of the device with a buffer. Other + * blocks can store only fill pattern value bytes. + * @param[in] block_count The media block count of the device. It is the sum + * of blocks with buffer and blocks that contain only fill pattern value bytes. + * @param[in] fill_pattern The fill pattern specifies the byte value of blocks + * without a buffer. It is also the initial value for blocks with a buffer. + * @param[in] sparse_disk_delete The sparse disk delete handler. + * + * @retval RTEMS_SUCCESSFUL Successful operation. + * @retval RTEMS_INVALID_NUMBER Media block size or media block count is not + * positive. The blocks with buffer count is greater than the media block count. + * @retval RTEMS_INVALID_ADDRESS Invalid sparse disk address. + * @retval RTEMS_TOO_MANY Cannot create semaphore. + * @retval RTEMS_UNSATISFIED Cannot create generic device node. + */ +rtems_status_code rtems_sparse_disk_register( + const char *device_file_name, + rtems_sparse_disk *sparse_disk, + uint32_t media_block_size, + rtems_blkdev_bnum blocks_with_buffer, + rtems_blkdev_bnum media_block_count, + uint8_t fill_pattern, + rtems_sparse_disk_delete_handler sparse_disk_delete +); + +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SPARSE_DISK_H */ diff --git a/cpukit/libblock/src/sparse-disk.c b/cpukit/libblock/src/sparse-disk.c new file mode 100644 index 0000000000..987ab6d806 --- /dev/null +++ b/cpukit/libblock/src/sparse-disk.c @@ -0,0 +1,387 @@ +/** + * @file + * + * @ingroup rtems_sparse_disk + * + * @brief Sparse disk block device implementation. + */ + +/* + * Copyright (c) 2012 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#include +#include +#include + +#include +#include +#include + +#include "rtems/sparse-disk.h" + +/* + * Allocate RAM for sparse disk + */ +static rtems_sparse_disk *sparse_disk_allocate( + const uint32_t media_block_size, + const rtems_blkdev_bnum blocks_with_buffer ) +{ + size_t const key_table_size = blocks_with_buffer + * sizeof( rtems_sparse_disk_key ); + size_t const data_size = blocks_with_buffer * media_block_size; + size_t const alloc_size = sizeof( rtems_sparse_disk ) + + key_table_size + data_size; + + rtems_sparse_disk *const sd = (rtems_sparse_disk *) malloc( + alloc_size ); + + return sd; +} + +/* + * Initialize sparse disk data + */ +static rtems_status_code sparse_disk_initialize( rtems_sparse_disk *sd, + const uint32_t media_block_size, + const rtems_blkdev_bnum blocks_with_buffer, + const rtems_sparse_disk_delete_handler sparse_disk_delete, + const uint8_t fill_pattern ) +{ + rtems_status_code sc; + rtems_blkdev_bnum i; + + if ( NULL == sd ) + return RTEMS_INVALID_ADDRESS; + + uint8_t *data = (uint8_t *) sd; + size_t const key_table_size = blocks_with_buffer + * sizeof( rtems_sparse_disk_key ); + size_t const data_size = blocks_with_buffer * media_block_size; + + memset( data, 0, sizeof( rtems_sparse_disk ) + key_table_size ); + + sd->fill_pattern = fill_pattern; + memset( (uint8_t *) ( data + sizeof( rtems_sparse_disk ) + key_table_size ), + sd->fill_pattern, + data_size ); + + sd->delete_handler = sparse_disk_delete; + + sc = rtems_semaphore_create( + rtems_build_name( 'S', 'P', 'A', 'R' ), + 1, + RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY, + 0, + &sd->mutex + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + + data += sizeof( rtems_sparse_disk ); + + sd->blocks_with_buffer = blocks_with_buffer; + sd->key_table = (rtems_sparse_disk_key *) data; + + data += key_table_size; + + for ( i = 0; i < blocks_with_buffer; ++i, data += media_block_size ) { + sd->key_table[i].data = data; + } + + sd->media_block_size = media_block_size; + return RTEMS_SUCCESSFUL; +} + +/* + * Block comparison + */ +static int sparse_disk_compare( const void *aa, const void *bb ) +{ + const rtems_sparse_disk_key *a = aa; + const rtems_sparse_disk_key *b = bb; + + if ( a->block < b->block ) { + return -1; + } else if ( a->block == b->block ) { + return 0; + } else { + return 1; + } +} + +static rtems_sparse_disk_key *sparse_disk_get_new_block( + rtems_sparse_disk *sparse_disk, + const rtems_blkdev_bnum block ) +{ + rtems_sparse_disk_key *key; + + if ( sparse_disk->used_count < sparse_disk->blocks_with_buffer ) { + key = &sparse_disk->key_table[sparse_disk->used_count]; + key->block = block; + ++sparse_disk->used_count; + qsort( sparse_disk->key_table, sparse_disk->used_count, + sizeof( rtems_sparse_disk_key ), sparse_disk_compare ); + } else + return NULL; + + return key; +} + +static int sparse_disk_read_block( + const rtems_sparse_disk *sparse_disk, + const rtems_blkdev_bnum block, + uint8_t *buffer, + const size_t buffer_size ) +{ + rtems_sparse_disk_key *key; + rtems_sparse_disk_key block_key = { + .block = block, + .data = NULL + }; + size_t bytes_to_copy = sparse_disk->media_block_size; + + if ( buffer_size < bytes_to_copy ) + bytes_to_copy = buffer_size; + + key = bsearch( + &block_key, + sparse_disk->key_table, + sparse_disk->used_count, + sizeof( rtems_sparse_disk_key ), + sparse_disk_compare + ); + + if ( NULL != key ) + memcpy( buffer, key->data, bytes_to_copy ); + else + memset( buffer, sparse_disk->fill_pattern, buffer_size ); + + return bytes_to_copy; +} + +static int sparse_disk_write_block( + rtems_sparse_disk *sparse_disk, + const rtems_blkdev_bnum block, + const uint8_t *buffer, + const size_t buffer_size ) +{ + unsigned int i; + bool block_needs_writing = false; + rtems_sparse_disk_key *key; + rtems_sparse_disk_key block_key = { + .block = block, + .data = NULL + }; + size_t bytes_to_copy = sparse_disk->media_block_size; + + if ( buffer_size < bytes_to_copy ) + bytes_to_copy = buffer_size; + + /* we only need to write the block if it is different from the fill pattern. + * If the read method does not find a block it will deliver the fill pattern anyway. + */ + + key = bsearch( + &block_key, + sparse_disk->key_table, + sparse_disk->used_count, + sizeof( rtems_sparse_disk_key ), + sparse_disk_compare + ); + + if ( NULL == key ) { + for ( i = 0; ( !block_needs_writing ) && ( i < bytes_to_copy ); ++i ) { + if ( buffer[i] != sparse_disk->fill_pattern ) + block_needs_writing = true; + } + + if ( block_needs_writing ) { + key = sparse_disk_get_new_block( sparse_disk, block ); + } + } + + if ( NULL != key ) + memcpy( key->data, buffer, bytes_to_copy ); + else if ( block_needs_writing ) + return -1; + + return bytes_to_copy; +} + +/* + * Read/write handling + */ +static int sparse_disk_read_write( + rtems_sparse_disk *sparse_disk, + rtems_blkdev_request *req, + const bool read ) +{ + int rv = 0; + uint32_t req_buffer; + rtems_blkdev_sg_buffer *scatter_gather; + rtems_blkdev_bnum block; + uint8_t *buff; + size_t buff_size; + unsigned int bytes_handled; + + rtems_semaphore_obtain( sparse_disk->mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT ); + + for ( req_buffer = 0; + ( 0 <= rv ) && ( req_buffer < req->bufnum ); + ++req_buffer ) { + scatter_gather = &req->bufs[req_buffer]; + + bytes_handled = 0; + buff = (uint8_t *) scatter_gather->buffer; + block = scatter_gather->block; + buff_size = scatter_gather->length; + + while ( ( 0 <= rv ) && ( 0 < buff_size ) ) { + if ( read ) + rv = sparse_disk_read_block( sparse_disk, + block, + &buff[bytes_handled], + buff_size ); + else + rv = sparse_disk_write_block( sparse_disk, + block, + &buff[bytes_handled], + buff_size ); + + ++block; + bytes_handled += rv; + buff_size -= rv; + } + } + + rtems_semaphore_release( sparse_disk->mutex ); + + if ( 0 > rv ) + rtems_blkdev_request_done( req, RTEMS_IO_ERROR ); + else + rtems_blkdev_request_done( req, RTEMS_SUCCESSFUL ); + + return 0; +} + +/* + * ioctl handler to be passed to the block device handler + */ +static int sparse_disk_ioctl( rtems_disk_device *dd, uint32_t req, void *argp ) +{ + rtems_status_code sc; + rtems_sparse_disk *sd = rtems_disk_get_driver_data( dd ); + + if ( RTEMS_BLKIO_REQUEST == req ) { + rtems_blkdev_request *r = argp; + + switch ( r->req ) { + case RTEMS_BLKDEV_REQ_READ: + case RTEMS_BLKDEV_REQ_WRITE: + return sparse_disk_read_write( sd, r, r->req == RTEMS_BLKDEV_REQ_READ ); + default: + break; + } + } else if ( RTEMS_BLKIO_DELETED == req ) { + sc = rtems_semaphore_delete( sd->mutex ); + + if ( RTEMS_SUCCESSFUL != sc ) + rtems_fatal_error_occurred( 0xdeadbeef ); + + sd->mutex = RTEMS_ID_NONE; + + if ( NULL != sd->delete_handler ) + ( *sd->delete_handler )( sd ); + + return 0; + } else { + return rtems_blkdev_ioctl( dd, req, argp ); + } + + errno = EINVAL; + return -1; +} + +void rtems_sparse_disk_free( rtems_sparse_disk *sd ) +{ + free( sd ); +} + +rtems_status_code rtems_sparse_disk_create_and_register( + const char *device_file_name, + uint32_t media_block_size, + rtems_blkdev_bnum blocks_with_buffer, + rtems_blkdev_bnum media_block_count, + uint8_t fill_pattern ) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_sparse_disk *sparse_disk = sparse_disk_allocate( + media_block_size, + blocks_with_buffer + ); + + if ( sparse_disk != NULL ) { + sc = rtems_sparse_disk_register( + device_file_name, + sparse_disk, + media_block_size, + blocks_with_buffer, + media_block_count, + fill_pattern, + rtems_sparse_disk_free + ); + } else { + sc = RTEMS_NO_MEMORY; + } + + return sc; +} + +rtems_status_code rtems_sparse_disk_register( + const char *device_file_name, + rtems_sparse_disk *sparse_disk, + uint32_t media_block_size, + rtems_blkdev_bnum blocks_with_buffer, + rtems_blkdev_bnum media_block_count, + uint8_t fill_pattern, + rtems_sparse_disk_delete_handler sparse_disk_delete ) +{ + rtems_status_code sc; + + if ( blocks_with_buffer <= media_block_count ) { + sc = sparse_disk_initialize( + sparse_disk, + media_block_size, + blocks_with_buffer, + sparse_disk_delete, + fill_pattern + ); + + if ( RTEMS_SUCCESSFUL == sc ) { + sc = rtems_blkdev_create( + device_file_name, + media_block_size, + media_block_count, + sparse_disk_ioctl, + sparse_disk + ); + } + } else { + sc = RTEMS_INVALID_NUMBER; + } + + return sc; +} diff --git a/cpukit/preinstall.am b/cpukit/preinstall.am index f6d24bb78f..26b90ecc75 100644 --- a/cpukit/preinstall.am +++ b/cpukit/preinstall.am @@ -272,6 +272,10 @@ $(PROJECT_INCLUDE)/rtems/nvdisk-sram.h: libblock/include/rtems/nvdisk-sram.h $(P $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/nvdisk-sram.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/nvdisk-sram.h +$(PROJECT_INCLUDE)/rtems/sparse-disk.h: libblock/include/rtems/sparse-disk.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/sparse-disk.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/sparse-disk.h + $(PROJECT_INCLUDE)/rtems/ide_part_table.h: libblock/include/rtems/ide_part_table.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/ide_part_table.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/ide_part_table.h diff --git a/testsuites/libtests/Makefile.am b/testsuites/libtests/Makefile.am index afd8605250..119774277b 100644 --- a/testsuites/libtests/Makefile.am +++ b/testsuites/libtests/Makefile.am @@ -1,6 +1,7 @@ ACLOCAL_AMFLAGS = -I ../aclocal SUBDIRS = POSIX +SUBDIRS += sparsedisk01 SUBDIRS += block16 SUBDIRS += block15 SUBDIRS += block14 diff --git a/testsuites/libtests/configure.ac b/testsuites/libtests/configure.ac index 83e22ed647..b8be927f47 100644 --- a/testsuites/libtests/configure.ac +++ b/testsuites/libtests/configure.ac @@ -43,6 +43,7 @@ AM_CONDITIONAL(HAS_POSIX,test x"${rtems_cv_RTEMS_POSIX_API}" = x"yes") # Explicitly list all Makefiles here AC_CONFIG_FILES([Makefile +sparsedisk01/Makefile block16/Makefile mghttpd01/Makefile block15/Makefile diff --git a/testsuites/libtests/sparsedisk01/Makefile.am b/testsuites/libtests/sparsedisk01/Makefile.am new file mode 100644 index 0000000000..3a836c84a7 --- /dev/null +++ b/testsuites/libtests/sparsedisk01/Makefile.am @@ -0,0 +1,19 @@ +rtems_tests_PROGRAMS = sparsedisk01 +sparsedisk01_SOURCES = init.c + +dist_rtems_tests_DATA = sparsedisk01.scn sparsedisk01.doc + +include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg +include $(top_srcdir)/../automake/compile.am +include $(top_srcdir)/../automake/leaf.am + +AM_CPPFLAGS += -I$(top_srcdir)/../support/include + +LINK_OBJS = $(sparsedisk01_OBJECTS) +LINK_LIBS = $(sparsedisk01_LDLIBS) + +sparsedisk01$(EXEEXT): $(sparsedisk01_OBJECTS) $(sparsedisk01_DEPENDENCIES) + @rm -f sparsedisk01$(EXEEXT) + $(make-exe) + +include $(top_srcdir)/../automake/local.am diff --git a/testsuites/libtests/sparsedisk01/init.c b/testsuites/libtests/sparsedisk01/init.c new file mode 100644 index 0000000000..fa386674aa --- /dev/null +++ b/testsuites/libtests/sparsedisk01/init.c @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2012 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include "rtems/sparse-disk.h" + +#include "tmacros.h" + +/* Number of bytes for test pattern within a sparse disk container */ +#define STATIC_PATTERN_SIZE 4096 + +/* Block size used for the sparse disk in a sparse disk container */ +#define STATIC_BLOCK_SIZE 4096 + +/* Number of block allocated for the sparse disk in a sparse disk container */ +#define STATIC_ALLOCATED_BLOCK_COUNT 1 + +/* Blocks simulated by the sparse disk in a disk container */ +#define STATIC_SIMULATED_BLOCK_COUNT 4096 + +/* + * Container which cotains a sparse disk + memory for key table and data as would get + * allocated by rtems_sparse_disk_create() + memory for a memory test pattern + * By using this container white box testing of a sparse disk becomes possible + */ +typedef struct { + rtems_sparse_disk sparse_disk; + rtems_sparse_disk_key keytable[STATIC_ALLOCATED_BLOCK_COUNT]; + uint8_t data[STATIC_BLOCK_SIZE * STATIC_ALLOCATED_BLOCK_COUNT]; + uint8_t pattern[STATIC_PATTERN_SIZE]; +} sparse_disk_container; + +/* + * Black box test the disk parameters of a sparse disk + */ +static void test_disk_params( + const int file_descriptor, + const uint32_t block_size, + const uint32_t media_block_size, + const rtems_blkdev_bnum block_number ) +{ + int rv; + uint32_t value = 0; + rtems_disk_device *fd_dd = NULL; + rtems_blkdev_bnum block_count = 0; + + + rv = rtems_disk_fd_get_media_block_size( file_descriptor, &value ); + rtems_test_assert( 0 == rv ); + rtems_test_assert( media_block_size == value ); + + value = 0; + rv = rtems_disk_fd_get_block_size( file_descriptor, &value ); + rtems_test_assert( 0 == rv ); + rtems_test_assert( block_size == value ); + + block_count = 0; + rv = rtems_disk_fd_get_block_count( file_descriptor, &block_count ); + rtems_test_assert( 0 == rv ); + rtems_test_assert( block_number == block_count ); + + rv = rtems_disk_fd_get_disk_device( file_descriptor, &fd_dd ); + rtems_test_assert( 0 == rv ); + rtems_test_assert( NULL != fd_dd ); +} + +/* + * Verify that writing to a sparse disk delivers expected results + */ +static void test_writing( + const int file_descriptor, + const uint32_t block_size, + const rtems_blkdev_bnum blocks_allocated ) +{ + int rv; + rtems_blkdev_bnum block_count = 0; + unsigned int byte_count; + off_t file_pos; + uint8_t buff[block_size]; + + + /* Write a pattern to all allocated blocks */ + for ( block_count = 0; block_count < blocks_allocated; block_count++ ) { + file_pos = (off_t) block_count * block_size; + rv = lseek( file_descriptor, file_pos, SEEK_SET ); + rtems_test_assert( file_pos == rv ); + + rv = read( file_descriptor, buff, block_size ); + rtems_test_assert( block_size == rv ); + + for ( byte_count = 0; + byte_count < ( block_size / sizeof( byte_count ) ); + byte_count++ ) { + memcpy( buff + ( byte_count * sizeof( byte_count ) ), &byte_count, + sizeof( byte_count ) ); + } + + rv = lseek( file_descriptor, file_pos, SEEK_SET ); + rtems_test_assert( file_pos == rv ); + + rv = write( file_descriptor, buff, block_size ); + rtems_test_assert( block_size == rv ); + } +} + +/* + * Verify that black box reading for a sparse disk delivers expected results + */ +static void test_reading( + const int file_descriptor, + const uint32_t block_size, + const rtems_blkdev_bnum blocks_allocated, + const uint8_t fill_pattern ) +{ + int rv; + rtems_blkdev_bnum block_count = 0; + unsigned int byte_count; + off_t file_pos; + uint8_t buff[block_size]; + uint32_t value = 0; + + + rv = fsync( file_descriptor ); + rtems_test_assert( 0 == rv ); + + /* Read back the patterns */ + for ( block_count = 0; block_count < blocks_allocated; block_count++ ) { + file_pos = (off_t) block_count * block_size; + value = lseek( file_descriptor, file_pos, SEEK_SET ); + rtems_test_assert( file_pos == value ); + + rv = read( file_descriptor, &buff, block_size ); + rtems_test_assert( block_size <= rv ); + + for ( byte_count = 0; + byte_count < ( block_size / sizeof( byte_count ) ); + byte_count++ ) { + rv = memcmp( buff + ( byte_count * sizeof( byte_count ) ), + &byte_count, + sizeof( byte_count ) ); + rtems_test_assert( 0 == rv ); + } + } + + /* Try to read from unallocated block */ + file_pos = (off_t) block_count * block_size; + rv = lseek( file_descriptor, file_pos, SEEK_SET ); + rtems_test_assert( file_pos == rv ); + + rv = read( file_descriptor, buff, block_size ); + rtems_test_assert( block_size == rv ); + + for ( byte_count = 0; byte_count < block_size; ++byte_count ) + rtems_test_assert( fill_pattern == buff[byte_count] ); +} + +/* + * Do black box io testing on a sparse disk + */ +static void test_device_io( const char *device_name, + const uint32_t block_size, + const uint32_t media_block_size, + const rtems_blkdev_bnum block_number, + const rtems_blkdev_bnum blocks_allocated, + const uint8_t fill_pattern ) +{ + int rv; + int file_descriptor; + + + file_descriptor = open( device_name, O_RDWR ); + rtems_test_assert( 0 <= file_descriptor ); + + test_disk_params( + file_descriptor, + block_size, + media_block_size, + block_number + ); + + test_writing( + file_descriptor, + block_size, + blocks_allocated + ); + + test_reading( + file_descriptor, + block_size, + blocks_allocated, + fill_pattern + ); + + rv = close( file_descriptor ); + rtems_test_assert( 0 == rv ); +} + +/* + * In white box testing verify the key table of the sparse disk is correct + */ +static void test_static_key_table( + const sparse_disk_container *disk_container, + const rtems_blkdev_bnum blocks_allocated, + const uint32_t block_size ) +{ + unsigned int i; + + + for ( i = 0; i < blocks_allocated; ++i ) { + rtems_test_assert( i == disk_container->keytable[i].block ); + rtems_test_assert( + &disk_container->data[i * block_size] + == disk_container->keytable[i].data ); + } +} + +/* + * Verify the test pattern used in white box testing is as expected + */ +static void test_static_pattern( + const unsigned int pattern_size, + const uint8_t *pattern ) +{ + unsigned int i; + + + for ( i = 0; i < pattern_size; ++i ) + rtems_test_assert( ( (uint8_t) ( pattern_size - 1 - i ) ) == pattern[i] ); +} + +/* + * Read write testing with a statically allocated disk. Thus white box testing can be done + */ +static void test_with_whitebox( const char *device_name ) +{ + rtems_status_code sc; + int rv; + unsigned int i; + sparse_disk_container disk_container; + int file_descriptor; + rtems_blkdev_bnum block_count = 0; + unsigned int byte_count; + uint8_t fill_pattern = 0; + + + memset( disk_container.data, 0, sizeof( disk_container.data ) ); + memset( disk_container.keytable, 0, sizeof( disk_container.keytable ) ); + + for ( i = 0; i < STATIC_PATTERN_SIZE; ++i ) + disk_container.pattern[i] = (uint8_t) ( STATIC_PATTERN_SIZE - 1 - i ); + + sc = rtems_sparse_disk_register( + "/dev/sda1", + &disk_container.sparse_disk, + STATIC_BLOCK_SIZE, + STATIC_ALLOCATED_BLOCK_COUNT, + STATIC_SIMULATED_BLOCK_COUNT, + fill_pattern, + NULL + ); + rtems_test_assert( RTEMS_SUCCESSFUL == sc ); + + test_static_key_table( + &disk_container, + STATIC_ALLOCATED_BLOCK_COUNT, + STATIC_BLOCK_SIZE + ); + + for ( i = 0; i < ( STATIC_BLOCK_SIZE * STATIC_ALLOCATED_BLOCK_COUNT ); ++i ) + rtems_test_assert( 0 == disk_container.data[i] ); + + test_static_pattern( + STATIC_PATTERN_SIZE, + &disk_container.pattern[0] + ); + + file_descriptor = open( device_name, O_RDWR ); + rtems_test_assert( 0 <= file_descriptor ); + + test_disk_params( + file_descriptor, + STATIC_BLOCK_SIZE, + STATIC_BLOCK_SIZE, + STATIC_SIMULATED_BLOCK_COUNT + ); + + test_writing( + file_descriptor, + STATIC_BLOCK_SIZE, + STATIC_ALLOCATED_BLOCK_COUNT + ); + + test_reading( + file_descriptor, + STATIC_BLOCK_SIZE, + STATIC_ALLOCATED_BLOCK_COUNT, + fill_pattern + ); + + rv = close( file_descriptor ); + rtems_test_assert( 0 == rv ); + + test_static_key_table( + &disk_container, + STATIC_ALLOCATED_BLOCK_COUNT, + STATIC_BLOCK_SIZE + ); + + for ( block_count = 0; + block_count < STATIC_ALLOCATED_BLOCK_COUNT; + block_count++ ) { + for ( byte_count = 0; + byte_count < ( STATIC_BLOCK_SIZE / sizeof( byte_count ) ); + byte_count++ ) { + rv = memcmp( &disk_container.data[byte_count * sizeof( byte_count )], + &byte_count, + sizeof( byte_count ) ); + rtems_test_assert( 0 == rv ); + } + } + + test_static_pattern( + STATIC_PATTERN_SIZE, + &disk_container.pattern[0] + ); +} + +/* + * The test sequence + */ +static +void test( void ) +{ + rtems_status_code sc; + int rv; + char device_name[] = "/dev/sda1"; + uint32_t block_size; + rtems_blkdev_bnum block_number; + rtems_blkdev_bnum blocks_allocated; + int file_descriptor; + uint8_t fill_pattern = 0; + + + sc = rtems_disk_io_initialize(); + rtems_test_assert( sc == RTEMS_SUCCESSFUL ); + + block_size = 512; + block_number = 4 * 2 * 1024; + blocks_allocated = 8; + sc = rtems_sparse_disk_create_and_register( + "/dev/sda1", + block_size, + blocks_allocated, + block_number, + fill_pattern + ); + rtems_test_assert( RTEMS_SUCCESSFUL == sc ); + + /* Test reading and writing with sector size 512 and 8 such sectors + * allocated. Block size will default to 512 */ + test_device_io( + device_name, + block_size, + block_size, + block_number, + blocks_allocated, + fill_pattern + ); + + file_descriptor = open( device_name, O_RDWR ); + rtems_test_assert( 0 <= file_descriptor ); + + rv = rtems_disk_fd_set_block_size( file_descriptor, + blocks_allocated * block_size ); + rtems_test_assert( 0 == rv ); + + rv = close( file_descriptor ); + rtems_test_assert( 0 == rv ); + + /* Block size was increased to 4k. Thus all to allocated disk space + * corresponds to one block. Repeat the read write tests */ + test_device_io( + device_name, + block_size * blocks_allocated, + block_size, + block_number, + 1, + fill_pattern + ); + + rv = unlink( device_name ); + rtems_test_assert( 0 == rv ); + + /* Do testing with a statically allocated disk. This permits white box + * testing */ + test_with_whitebox( device_name ); +} + +static void Init( rtems_task_argument arg ) +{ + (void) arg; + puts( "\n\n*** TEST SPARSEDISK 1 ***" ); + + test(); + + puts( "*** END OF TEST SPARSEDISK 1 ***" ); + + rtems_test_exit( 0 ); +} + +#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER +#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER +#define CONFIGURE_APPLICATION_NEEDS_LIBBLOCK + +#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM +#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 4 + +#define CONFIGURE_MAXIMUM_TASKS 1 +#define CONFIGURE_MAXIMUM_SEMAPHORES 1 + +#define CONFIGURE_INIT_TASK_STACK_SIZE ( 16 * 1024 ) + +#define CONFIGURE_RTEMS_INIT_TASKS_TABLE + +#define CONFIGURE_INIT + +#include \ No newline at end of file diff --git a/testsuites/libtests/sparsedisk01/sparsedisk01.doc b/testsuites/libtests/sparsedisk01/sparsedisk01.doc new file mode 100644 index 0000000000..948c61cecc --- /dev/null +++ b/testsuites/libtests/sparsedisk01/sparsedisk01.doc @@ -0,0 +1,12 @@ +This file describes the directives and concepts tested by this test set. + +test set name: sparsedisk01 + +directives: + + - rtems_sparse_disk_create() + - rtems_sparse_disk_register() + +concepts: + + - Ensures that the sparse disk works. diff --git a/testsuites/libtests/sparsedisk01/sparsedisk01.scn b/testsuites/libtests/sparsedisk01/sparsedisk01.scn new file mode 100644 index 0000000000..2420aea516 --- /dev/null +++ b/testsuites/libtests/sparsedisk01/sparsedisk01.scn @@ -0,0 +1,2 @@ +*** TEST SPARSEDISK 1 *** +*** END OF TEST SPARSEDISK 1 *** -- cgit v1.2.3