Files
rtems/testsuites/libtests/block06/init.c
Sebastian Huber ae88aa7927 sapi: Use one SMP lock for all chains
This partially reverts commit 1215fd4d94.

In order to support profiling of SMP locks and provide a future
compatible SMP locks API it is necessary to add an SMP lock destroy
function.  Since the commit above adds an SMP lock to each chain control
we would have to add a rtems_chain_destroy() function as well.  This
complicates the chain usage dramatically.  Thus revert the patch above.
A global SMP lock for all chains is used to implement the protected
chain operations.

Advantages:

* The SAPI chain API is now identical on SMP and non-SMP
  configurations.

* The size of the chain control is reduced and is then equal to the
  Score chains.

* The protected chain operations work correctly on SMP.

Disadvantage:

* Applications using many different chains and the protected operations
  may notice lock contention.

The chain control size drop is a huge benefit (SAPI chain controls are
66% larger than the Score chain controls).  The only disadvantage is not
really a problem since these applications can use specific interrupt
locks and unprotected chain operations to avoid this issue.
2014-03-11 10:58:09 +01:00

1864 lines
42 KiB
C

/*
* Copyright 2008 Chris Johns (chrisj@rtems.org)
*
* 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.
*/
/**
* BD Buffer test.
*
* Please add more tests
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <inttypes.h>
#include <rtems.h>
#include <rtems/chain.h>
#include <rtems/error.h>
#include <rtems/bdbuf.h>
#include <bsp.h>
/* forward declarations to avoid warnings */
static rtems_task Init(rtems_task_argument argument);
#define BDBUF_DISKS 2
#define BDBUF_SIZE 1024
#if 0
const rtems_bdbuf_config rtems_bdbuf_configuration =
{
5, /* max_read_ahead_blocks */
5, /* max_write_blocks */
15, /* swapout_priority */
250, /* swapout_period */
1000, /* swap_block_hold */
0, /* swapout_workers */
15, /* swapout_worker_priority */
1024 * 1024, /* size */
512, /* buffer_min */
4096 /* buffer_max */
};
#endif
/**
* Let the IO system allocation the next available major number.
*/
#define RTEMS_DRIVER_AUTO_MAJOR (0)
/**
* The bdbuf disk driver base name.
*/
#define BDBUF_DISK_DEVICE_BASE_NAME "/dev/bddisk"
/**
* The actions the disk driver handles.
*/
typedef enum bdbuf_disk_action
{
BDBUF_DISK_NOOP,
BDBUF_DISK_WAIT,
BDBUF_DISK_SLEEP,
BDBUF_DISK_BLOCKS_INORDER
} bdbuf_disk_action;
/**
* The BDBUF Disk driver.
*/
typedef struct bdbuf_disk
{
const char* name;
rtems_id lock;
uint32_t block_size;
uint32_t block_count;
bdbuf_disk_action driver_action;
const char* watcher_name;
rtems_id watcher;
int watch_count;
const char* waiting_name;
rtems_id waiting;
uint32_t driver_sleep;
} bdbuf_disk;
/*
* A disk drive for each pool.
*/
static bdbuf_disk bdbuf_disks[BDBUF_DISKS];
/**
* Task control.
*/
typedef struct bdbuf_task_control
{
bool die;
const char* name;
rtems_id task;
rtems_id master;
int test;
rtems_device_major_number major;
rtems_device_minor_number minor;
bool passed;
rtems_disk_device *dd;
} bdbuf_task_control;
#define BDBUF_TEST_TASKS (3)
#define BDBUF_TEST_STACK_SIZE (2 * RTEMS_MINIMUM_STACK_SIZE)
/**
* Seconds as milli-seconds.
*/
#define BDBUF_SECONDS(msec) ((msec) * 1000UL)
/**
* Print a message to output and flush it.
*
* @param format The format string. See printf for details.
* @param ... The arguments for the format text.
* @return int The number of bytes written to the output.
*/
static int
bdbuf_test_printf (const char *format, ...)
{
int ret = 0;
va_list args;
va_start (args, format);
ret = vfprintf (stdout, format, args);
fflush (stdout);
return ret;
}
/**
* Print the status code description and return true if true.
*
* @param sc The RTEMS status code.
* @retval true The status code is successful.
* @retval false The status code is not successful.
*/
static bool
bdbuf_test_print_sc (rtems_status_code sc, bool newline)
{
if (newline)
fprintf (stdout, "%s\n", rtems_status_text (sc));
else
fprintf (stdout, "%s", rtems_status_text (sc));
return sc == RTEMS_SUCCESSFUL;
}
/**
* BDBuf disk device driver lock.
*/
static bool
bdbuf_disk_lock (bdbuf_disk* bdd)
{
rtems_status_code sc;
sc = rtems_semaphore_obtain (bdd->lock, RTEMS_WAIT, 0);
if (sc != RTEMS_SUCCESSFUL)
{
bdbuf_test_printf ("disk ioctl: lock failed: ");
bdbuf_test_print_sc (sc, true);
return false;
}
return true;
}
/**
* BDBuf disk device driver unlock.
*/
static bool
bdbuf_disk_unlock (bdbuf_disk* bdd)
{
rtems_status_code sc;
sc = rtems_semaphore_release (bdd->lock);
if (sc != RTEMS_SUCCESSFUL)
{
bdbuf_test_printf ("disk ioctl: unlock failed: ");
bdbuf_test_print_sc (sc, true);
return false;
}
return true;
}
/**
* BDBUf wait for the wait event.
*/
static rtems_status_code
bdbuf_wait (const char* who, unsigned long timeout)
{
rtems_status_code sc;
rtems_event_set out;
sc = rtems_event_receive (RTEMS_EVENT_0,
RTEMS_WAIT | RTEMS_EVENT_ANY,
RTEMS_MICROSECONDS_TO_TICKS (timeout * 1000),
&out);
if (sc != RTEMS_SUCCESSFUL)
{
bdbuf_test_printf ("%s: wait: receive failed: ", who);
bdbuf_test_print_sc (sc, true);
}
else if ((out & RTEMS_EVENT_0) == 0)
{
bdbuf_test_printf ("%s: wait: received wrong event: %08x", who, out);
}
return sc;
}
/**
* BDBUf send wait event.
*/
static bool
bdbuf_send_wait_event (const char* task, const char* msg, rtems_id id)
{
bdbuf_test_printf ("%s: %s: %08x: ", task, msg, id);
return bdbuf_test_print_sc (rtems_event_send (id, RTEMS_EVENT_0), true);
}
/**
* BDBUf wait for the wait event.
*/
static rtems_status_code
bdbuf_watch (unsigned long timeout)
{
rtems_status_code sc;
rtems_event_set out;
sc = rtems_event_receive (RTEMS_EVENT_1,
RTEMS_WAIT | RTEMS_EVENT_ANY,
RTEMS_MICROSECONDS_TO_TICKS (timeout * 1000),
&out);
if (sc != RTEMS_SUCCESSFUL)
{
bdbuf_test_printf ("watch: receive failed: ");
bdbuf_test_print_sc (sc, true);
}
else if ((out & RTEMS_EVENT_1) == 0)
{
bdbuf_test_printf ("watch: received wrong event: %08x", out);
}
return sc;
}
/**
* BDBUf send wait event.
*/
static bool
bdbuf_send_watch_event (const char* task, const char* msg, rtems_id id)
{
bdbuf_test_printf ("%s: %s: %08x: ", task, msg, id);
return bdbuf_test_print_sc (rtems_event_send (id, RTEMS_EVENT_1), true);
}
/**
* Set up a disk driver watch.
*/
static void
bdbuf_set_disk_driver_watch (bdbuf_task_control* tc, int count)
{
/*
* Set up a disk watch and wait for the write to happen.
*/
bdbuf_disk_lock (&bdbuf_disks[tc->minor]);
bdbuf_disks[tc->minor].watcher_name = tc->name;
bdbuf_disks[tc->minor].watcher = tc->task;
bdbuf_disks[tc->minor].watch_count = count;
bdbuf_disk_unlock (&bdbuf_disks[tc->minor]);
}
/**
* Clear the disk driver watch.
*/
static void
bdbuf_clear_disk_driver_watch (bdbuf_task_control* tc)
{
/*
* Set up a disk watch and wait for the write to happen.
*/
bdbuf_disk_lock (&bdbuf_disks[tc->minor]);
bdbuf_disks[tc->minor].watcher_name = 0;
bdbuf_disks[tc->minor].watcher = 0;
bdbuf_disks[tc->minor].watch_count = 0;
bdbuf_disk_unlock (&bdbuf_disks[tc->minor]);
}
/**
* Wait for the disk driver watch.
*/
static bool
bdbuf_disk_driver_watch_wait (bdbuf_task_control* tc, unsigned long msecs)
{
bool passed = true;
rtems_status_code sc = bdbuf_watch (msecs);
if (sc != RTEMS_SUCCESSFUL)
{
bdbuf_test_printf ("%s: driver watch: driver wait: ", tc->name);
passed = bdbuf_test_print_sc (sc, true);
}
bdbuf_clear_disk_driver_watch (tc);
return passed;
}
/**
* Set the disk driver action.
*/
static void
bdbuf_set_disk_driver_action (bdbuf_task_control* tc, bdbuf_disk_action action)
{
/*
* Set up a disk action.
*/
bdbuf_disk_lock (&bdbuf_disks[tc->minor]);
bdbuf_disks[tc->minor].driver_action = action;
bdbuf_disk_unlock (&bdbuf_disks[tc->minor]);
}
/**
* BDBUF Sleep.
*/
static bool
bdbuf_sleep (unsigned long msecs)
{
rtems_status_code sc;
sc = rtems_task_wake_after (RTEMS_MICROSECONDS_TO_TICKS (msecs * 1000));
if (sc != RTEMS_SUCCESSFUL)
{
bdbuf_test_printf ("sleep wake after failed: ");
bdbuf_test_print_sc (sc, true);
return false;
}
return true;
}
/**
* Initialise a task control.
*/
static void
bdbuf_task_control_init (int task,
bdbuf_task_control* tc,
rtems_id master,
rtems_device_major_number major,
rtems_disk_device *dd)
{
char name[6];
sprintf (name, "bdt%d", task);
tc->die = false;
tc->name = strdup (name); /* leaks */
tc->task = 0;
tc->master = master;
tc->test = 0;
tc->major = major;
tc->minor = 0;
tc->passed = false;
tc->dd = dd;
}
static bool
bdbuf_disk_ioctl_watcher (bdbuf_disk* bdd, int update)
{
/*
* Always wake the watcher.
*/
if (bdd->watcher)
{
if (bdd->watch_count)
{
if (update > bdd->watch_count)
bdd->watch_count -= update;
else
bdd->watch_count = 0;
}
if (bdd->watch_count == 0)
{
bdbuf_send_watch_event (bdd->watcher_name,
"disk ioctl: wake watcher",
bdd->watcher);
bdd->watcher = 0;
}
}
return true;
}
static bool
bdbuf_disk_ioctl_process (bdbuf_disk* bdd, rtems_blkdev_request* req)
{
bool result = true;
int b;
/*
* Perform the requested action.
*/
switch (bdd->driver_action)
{
case BDBUF_DISK_NOOP:
break;
case BDBUF_DISK_WAIT:
if (bdd->waiting)
bdbuf_test_printf ("disk ioctl: bad waiter: %s:%08x\n",
bdd->waiting_name, bdd->waiting);
bdd->waiting_name = "bdd";
bdd->waiting = rtems_task_self ();
if (bdbuf_disk_unlock (bdd))
result = bdbuf_wait (bdd->name, 0) == RTEMS_SUCCESSFUL;
else
result = false;
/*
* Ignore any error if one happens.
*/
bdbuf_disk_lock (bdd);
break;
case BDBUF_DISK_SLEEP:
bdbuf_test_printf ("disk ioctl: sleeping: %d msecs\n",
bdd->driver_sleep);
result = bdbuf_sleep (bdd->driver_sleep);
break;
case BDBUF_DISK_BLOCKS_INORDER:
bdbuf_test_printf ("disk ioctl: multi-block order check: count = %d\n",
req->bufnum);
for (b = 0; b < (req->bufnum - 1); b++)
if (req->bufs[b].block >= req->bufs[b + 1].block)
bdbuf_test_printf ("disk ioctl: out of order: index:%d (%d >= %d\n",
b, req->bufs[b].block, req->bufs[b + 1].block);
break;
default:
bdbuf_test_printf ("disk ioctl: invalid action: %d\n",
bdd->driver_action);
result = false;
break;
}
return result;
}
static bool
bdbuf_disk_ioctl_leave (bdbuf_disk* bdd, int buffer_count)
{
/*
* Handle the watcher.
*/
bdbuf_disk_ioctl_watcher (bdd, buffer_count);
return true;
}
/**
* BDBuf disk IOCTL handler.
*
* @param dd Disk device.
* @param req IOCTL request code.
* @param argp IOCTL argument.
* @retval The IOCTL return value
*/
static int
bdbuf_disk_ioctl (rtems_disk_device *dd, uint32_t req, void* argp)
{
rtems_blkdev_request *r = argp;
bdbuf_disk *bdd = rtems_disk_get_driver_data (dd);
errno = 0;
if (!bdbuf_disk_lock (bdd))
{
errno = EIO;
}
else
{
switch (req)
{
case RTEMS_BLKIO_REQUEST:
switch (r->req)
{
case RTEMS_BLKDEV_REQ_READ:
if (!bdbuf_disk_ioctl_process (bdd, r))
rtems_blkdev_request_done(r, RTEMS_IO_ERROR);
else
{
rtems_blkdev_sg_buffer* sg = r->bufs;
uint32_t block = RTEMS_BLKDEV_START_BLOCK (r);
uint32_t b;
int32_t remains;
remains = r->bufnum * bdd->block_size;
for (b = 0; b < r->bufnum; b++, block++, sg++)
{
uint32_t length = sg->length;
if (sg->length != bdd->block_size)
if (length > bdd->block_size)
length = bdd->block_size;
memset (sg->buffer, block, length);
remains -= length;
}
rtems_blkdev_request_done (r, RTEMS_SUCCESSFUL);
}
bdbuf_disk_ioctl_leave (bdd, r->bufnum);
break;
case RTEMS_BLKDEV_REQ_WRITE:
if (!bdbuf_disk_ioctl_process (bdd, r))
rtems_blkdev_request_done(r, RTEMS_IO_ERROR);
else
rtems_blkdev_request_done(r, RTEMS_SUCCESSFUL);
bdbuf_disk_ioctl_leave (bdd, r->bufnum);
break;
default:
errno = EINVAL;
break;
}
break;
default:
errno = EINVAL;
break;
}
if (!bdbuf_disk_unlock (bdd))
errno = EIO;
}
return errno == 0 ? 0 : -1;
}
/**
* BDBuf disk device driver initialization.
*
* @param major Disk major device number.
* @param minor Minor device number, not applicable.
* @param arg Initialization argument, not applicable.
*/
static rtems_device_driver
bdbuf_disk_initialize (rtems_device_major_number major,
rtems_device_minor_number minor,
void* arg)
{
rtems_status_code sc;
bdbuf_test_printf ("disk io init: ");
sc = rtems_disk_io_initialize ();
if (!bdbuf_test_print_sc (sc, true))
return sc;
for (minor = 0; minor < BDBUF_DISKS; minor++)
{
char name[sizeof (BDBUF_DISK_DEVICE_BASE_NAME) + 10];
bdbuf_disk* bdd = &bdbuf_disks[minor];
rtems_status_code sc;
snprintf (name, sizeof (name),
BDBUF_DISK_DEVICE_BASE_NAME "%" PRIu32, minor);
bdd->name = strdup (name);
bdbuf_test_printf ("disk init: %s\n", bdd->name);
bdbuf_test_printf ("disk lock: ");
sc = rtems_semaphore_create (rtems_build_name ('B', 'D', 'D', 'K'), 1,
RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE |
RTEMS_INHERIT_PRIORITY, 0, &bdd->lock);
if (!bdbuf_test_print_sc (sc, true))
return RTEMS_IO_ERROR;
bdd->block_size = 512 * (minor + 1);
bdd->block_count = BDBUF_SIZE * (minor + 1);
sc = rtems_disk_create_phys(rtems_filesystem_make_dev_t (major, minor),
bdd->block_size, bdd->block_count,
bdbuf_disk_ioctl, bdd, name);
if (sc != RTEMS_SUCCESSFUL)
{
bdbuf_test_printf ("disk init: create phys failed: ");
bdbuf_test_print_sc (sc, true);
return sc;
}
}
return RTEMS_SUCCESSFUL;
}
/**
* Create the RAM Disk Driver entry.
*/
static rtems_driver_address_table bdbuf_disk_io_ops = {
initialization_entry: bdbuf_disk_initialize,
open_entry: rtems_blkdev_generic_open,
close_entry: rtems_blkdev_generic_close,
read_entry: rtems_blkdev_generic_read,
write_entry: rtems_blkdev_generic_write,
control_entry: rtems_blkdev_generic_ioctl
};
/**
* Set up the disk.
*/
static bool
bdbuf_tests_setup_disk (rtems_device_major_number *major,
rtems_disk_device **dd_ptr)
{
rtems_status_code sc;
bool ok;
/*
* Register the disk driver.
*/
bdbuf_test_printf ("register disk driver\n");
sc = rtems_io_register_driver (RTEMS_DRIVER_AUTO_MAJOR,
&bdbuf_disk_io_ops,
major);
ok = sc == RTEMS_SUCCESSFUL;
if (ok) {
*dd_ptr = rtems_disk_obtain (rtems_filesystem_make_dev_t (*major, 0));
ok = *dd_ptr != NULL;
}
return ok;
}
static bool
bdbuf_tests_create_task (bdbuf_task_control* tc,
rtems_task_priority priority,
rtems_task_entry entry_point)
{
rtems_status_code sc;
bdbuf_test_printf ("creating task: %s: priority: %d: ",
tc->name, priority);
sc = rtems_task_create (rtems_build_name (tc->name[0], tc->name[1],
tc->name[2], tc->name[3]),
priority,
BDBUF_TEST_STACK_SIZE,
RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR,
&tc->task);
if (!bdbuf_test_print_sc (sc, true))
return false;
bdbuf_test_printf ("starting task: %s: ", tc->name);
sc = rtems_task_start (tc->task, entry_point, (rtems_task_argument) tc);
return bdbuf_test_print_sc (sc, true);
}
/**
* Get the block 0 buffer twice. The first time it is requested it
* will be taken from the empty list and returned to the LRU list.
* The second time it will be removed from the LRU list.
*/
static void
bdbuf_tests_task_0_test_1 (bdbuf_task_control* tc)
{
rtems_status_code sc;
bool passed;
int i;
rtems_bdbuf_buffer* bd;
/*
* Set task control's passed to false to handle a timeout.
*/
tc->passed = false;
passed = true;
for (i = 0; (i < 2) && passed; i++)
{
bdbuf_test_printf ("%s: rtems_bdbuf_get[0]: ", tc->name);
sc = rtems_bdbuf_get (tc->dd, 0, &bd);
if (!bdbuf_test_print_sc (sc, true))
{
passed = false;
break;
}
bdbuf_test_printf ("%s: rtems_bdbuf_release[0]: ", tc->name);
sc = rtems_bdbuf_release (bd);
if (!bdbuf_test_print_sc (sc, true))
{
passed = false;
break;
}
}
tc->passed = passed;
tc->test = 0;
}
/**
* Get the blocks 0 -> 4 and hold them. Wake the master to tell it was have the
* buffers then wait for the master to tell us to release a single buffer.
* Task 1 will be block waiting for each buffer. It is a higher priority.
*/
static void
bdbuf_tests_task_0_test_2 (bdbuf_task_control* tc)
{
rtems_status_code sc;
bool passed;
int i;
rtems_bdbuf_buffer* bd;
rtems_chain_control buffers;
/*
* Set task control's passed to false to handle a timeout.
*/
tc->passed = false;
passed = true;
/*
* Get the blocks 0 -> 4 and hold them.
*/
rtems_chain_initialize_empty (&buffers);
for (i = 0; (i < 5) && passed; i++)
{
bdbuf_test_printf ("%s: rtems_bdbuf_get[%d]: ", tc->name, i);
sc = rtems_bdbuf_get (tc->dd, i, &bd);
if (!bdbuf_test_print_sc (sc, true))
passed = false;
rtems_chain_append (&buffers, &bd->link);
}
/*
* Wake the master to tell it we have the buffers.
*/
bdbuf_send_wait_event (tc->name, "wake master", tc->master);
if (passed)
{
/*
* For each buffer we hold wait until the master wakes us
* and then return it. Task 2 will block waiting for this
* buffer. It is a higher priority task.
*/
for (i = 0; (i < 5) && passed; i++)
{
sc = bdbuf_wait (tc->name, BDBUF_SECONDS (5));
if (sc != RTEMS_SUCCESSFUL)
{
bdbuf_test_printf ("%s: wait failed: ", tc->name);
bdbuf_test_print_sc (sc, true);
passed = false;
break;
}
else
{
bdbuf_test_printf ("%s: rtems_bdbuf_release[%d]: unblocks task 1\n",
tc->name, i);
bd = (rtems_bdbuf_buffer*) rtems_chain_get (&buffers);
sc = rtems_bdbuf_release (bd);
bdbuf_test_printf ("%s: rtems_bdbuf_release[%d]: ", tc->name, i);
if (!bdbuf_test_print_sc (sc, true))
{
passed = false;
break;
}
}
}
}
tc->passed = passed;
tc->test = 0;
}
/**
* Read the block 5 from the disk modify it then release it modified.
*/
static void
bdbuf_tests_task_0_test_3 (bdbuf_task_control* tc)
{
rtems_status_code sc;
bool passed;
rtems_bdbuf_buffer* bd;
/*
* Set task control's passed to false to handle a timeout.
*/
tc->passed = false;
passed = true;
bdbuf_disk_lock (&bdbuf_disks[tc->minor]);
bdbuf_disks[tc->minor].driver_action = BDBUF_DISK_NOOP;
bdbuf_disk_unlock (&bdbuf_disks[tc->minor]);
/*
* Read the buffer and then release it.
*/
bdbuf_test_printf ("%s: rtems_bdbuf_read[5]: ", tc->name);
sc = rtems_bdbuf_read (tc->dd, 5, &bd);
if ((passed = bdbuf_test_print_sc (sc, true)))
{
bdbuf_test_printf ("%s: rtems_bdbuf_release_modified[5]: ", tc->name);
sc = rtems_bdbuf_release_modified (bd);
passed = bdbuf_test_print_sc (sc, true);
}
/*
* Read the buffer again and then just release. The buffer should
* be maintained as modified.
*/
bdbuf_test_printf ("%s: rtems_bdbuf_read[5]: ", tc->name);
sc = rtems_bdbuf_read (tc->dd, 5, &bd);
if ((passed = bdbuf_test_print_sc (sc, true)))
{
bdbuf_test_printf ("%s: rtems_bdbuf_release[5]: ", tc->name);
sc = rtems_bdbuf_release (bd);
passed = bdbuf_test_print_sc (sc, true);
}
/*
* Set up a disk watch and wait for the write to happen.
*/
bdbuf_set_disk_driver_watch (tc, 1);
passed = bdbuf_disk_driver_watch_wait (tc, BDBUF_SECONDS (5));
tc->passed = passed;
tc->test = 0;
}
static size_t
bdbuf_test_buffer_count (void)
{
return rtems_bdbuf_configuration.size / rtems_bdbuf_configuration.buffer_min;
}
/**
* Get all the blocks in the pool and hold them. Wake the master to tell it was
* have the buffers then wait for the master to tell us to release them.
*/
static void
bdbuf_tests_task_0_test_4 (bdbuf_task_control* tc)
{
rtems_status_code sc;
bool passed;
size_t i;
rtems_bdbuf_buffer* bd;
rtems_chain_control buffers;
size_t num = bdbuf_test_buffer_count ();
/*
* Set task control's passed to false to handle a timeout.
*/
tc->passed = false;
passed = true;
/*
* Clear any disk settings.
*/
bdbuf_clear_disk_driver_watch (tc);
bdbuf_set_disk_driver_action (tc, BDBUF_DISK_NOOP);
/*
* Get the blocks 0 -> 4 and hold them.
*/
rtems_chain_initialize_empty (&buffers);
for (i = 0; (i < num) && passed; i++)
{
bdbuf_test_printf ("%s: rtems_bdbuf_read[%d]: ", tc->name, i);
sc = rtems_bdbuf_read (tc->dd, i, &bd);
if (!bdbuf_test_print_sc (sc, true))
passed = false;
rtems_chain_append (&buffers, &bd->link);
}
/*
* Wake the master to tell it we have the buffers.
*/
bdbuf_send_wait_event (tc->name, "wake master", tc->master);
if (passed)
{
bdbuf_sleep (250);
bdbuf_set_disk_driver_watch (tc, num / 2);
/*
* Release half the buffers, wait 500msecs then release the
* remainder. This tests the swap out timer on each buffer.
*/
bdbuf_test_printf ("%s: rtems_bdbuf_release_modified[0]: unblocks task 1\n",
tc->name);
bd = (rtems_bdbuf_buffer*) rtems_chain_get (&buffers);
sc = rtems_bdbuf_release_modified (bd);
bdbuf_test_printf ("%s: rtems_bdbuf_release_modified[0]: ", tc->name);
passed = bdbuf_test_print_sc (sc, true);
if (passed)
{
for (i = 1; (i < (num / 2)) && passed; i++)
{
bdbuf_test_printf ("%s: rtems_bdbuf_release_modified[%d]: " \
"unblocks task 1\n", tc->name, i);
bd = (rtems_bdbuf_buffer*) rtems_chain_get (&buffers);
sc = rtems_bdbuf_release_modified (bd);
bdbuf_test_printf ("%s: rtems_bdbuf_release_modified[%d]: ",
tc->name, i);
passed = bdbuf_test_print_sc (sc, true);
if (!passed)
break;
}
if (passed)
{
passed = bdbuf_disk_driver_watch_wait (tc, BDBUF_SECONDS (5));
if (passed)
{
bdbuf_sleep (500);
bdbuf_set_disk_driver_watch (tc, num / 2);
for (i = 0; (i < (num / 2)) && passed; i++)
{
bdbuf_test_printf ("%s: rtems_bdbuf_release_modified[%d]: ",
tc->name, i + (num / 2));
bd = (rtems_bdbuf_buffer*) rtems_chain_get (&buffers);
passed = bdbuf_test_print_sc (rtems_bdbuf_release_modified (bd),
true);
if (!passed)
break;
}
passed = bdbuf_disk_driver_watch_wait (tc, BDBUF_SECONDS (5));
if (passed)
{
if (!rtems_chain_is_empty (&buffers))
{
passed = false;
bdbuf_test_printf ("%s: buffer chain not empty\n", tc->name);
}
}
}
}
}
}
tc->passed = passed;
tc->test = 0;
}
static void
bdbuf_tests_task_0_test_5 (bdbuf_task_control* tc)
{
bdbuf_tests_task_0_test_4 (tc);
}
static void
bdbuf_tests_task_0_test_6 (bdbuf_task_control* tc)
{
rtems_status_code sc;
bool passed;
int i;
rtems_bdbuf_buffer* bd;
rtems_chain_control buffers;
/*
* Set task control's passed to false to handle a timeout.
*/
tc->passed = false;
passed = true;
/*
* Clear any disk settings.
*/
bdbuf_clear_disk_driver_watch (tc);
bdbuf_set_disk_driver_action (tc, BDBUF_DISK_NOOP);
/*
* Get the blocks 0 -> 4 and hold them.
*/
rtems_chain_initialize_empty (&buffers);
for (i = 0; (i < 5) && passed; i++)
{
bdbuf_test_printf ("%s: rtems_bdbuf_read[%d]: ", tc->name, i);
sc = rtems_bdbuf_get (tc->dd, i, &bd);
if (!bdbuf_test_print_sc (sc, true))
passed = false;
rtems_chain_append (&buffers, &bd->link);
}
for (i = 0; (i < 4) && passed; i++)
{
bdbuf_test_printf ("%s: rtems_bdbuf_release_modified[%d]: ",
tc->name, i);
bd = (rtems_bdbuf_buffer*) rtems_chain_get (&buffers);
passed = bdbuf_test_print_sc (rtems_bdbuf_release_modified (bd),
true);
}
if (passed)
{
bdbuf_test_printf ("%s: rtems_bdbuf_sync[%d]: ", tc->name, i);
bd = (rtems_bdbuf_buffer*) rtems_chain_get (&buffers);
passed = bdbuf_test_print_sc (rtems_bdbuf_sync (bd), true);
}
tc->passed = passed;
tc->test = 0;
}
static void
bdbuf_tests_task_0_test_7 (bdbuf_task_control* tc)
{
rtems_status_code sc;
bool passed;
int i;
rtems_bdbuf_buffer* bd;
rtems_chain_control buffers;
/*
* Set task control's passed to false to handle a timeout.
*/
tc->passed = false;
passed = true;
/*
* Clear any disk settings.
*/
bdbuf_clear_disk_driver_watch (tc);
bdbuf_set_disk_driver_action (tc, BDBUF_DISK_NOOP);
/*
* Get the blocks 0 -> 4 and hold them.
*/
rtems_chain_initialize_empty (&buffers);
for (i = 0; (i < 5) && passed; i++)
{
bdbuf_test_printf ("%s: rtems_bdbuf_read[%d]: ", tc->name, i);
sc = rtems_bdbuf_get (tc->dd, i, &bd);
if (!bdbuf_test_print_sc (sc, true))
passed = false;
rtems_chain_append (&buffers, &bd->link);
}
for (i = 0; (i < 5) && passed; i++)
{
bdbuf_test_printf ("%s: rtems_bdbuf_release_modified[%d]: ",
tc->name, i);
bd = (rtems_bdbuf_buffer*) rtems_chain_get (&buffers);
passed = bdbuf_test_print_sc (rtems_bdbuf_release_modified (bd),
true);
}
if (passed)
{
bdbuf_test_printf ("%s: rtems_bdbuf_syncdev[%d:%d]: ",
tc->name, i,
tc->major,
tc->minor);
passed = bdbuf_test_print_sc (rtems_bdbuf_syncdev (tc->dd), true);
}
tc->passed = passed;
tc->test = 0;
}
static void
bdbuf_tests_task_0_test_8 (bdbuf_task_control* tc)
{
rtems_status_code sc;
bool passed;
int i;
rtems_bdbuf_buffer* bd;
rtems_chain_control buffers;
rtems_chain_node* node;
rtems_chain_node* pnode;
/*
* Set task control's passed to false to handle a timeout.
*/
tc->passed = false;
passed = true;
/*
* Clear any disk settings.
*/
bdbuf_clear_disk_driver_watch (tc);
bdbuf_set_disk_driver_action (tc, BDBUF_DISK_NOOP);
/*
* Get the blocks 0 -> 4 and hold them.
*/
rtems_chain_initialize_empty (&buffers);
for (i = 0; (i < 5) && passed; i++)
{
bdbuf_test_printf ("%s: rtems_bdbuf_read[%d]: ", tc->name, i);
sc = rtems_bdbuf_get (tc->dd, i, &bd);
if (!bdbuf_test_print_sc (sc, true))
passed = false;
rtems_chain_append (&buffers, &bd->link);
}
node = rtems_chain_tail (&buffers);
node = node->previous;
bd = (rtems_bdbuf_buffer*) node;
pnode = node->previous;
rtems_chain_extract (node);
node = pnode;
bdbuf_test_printf ("%s: rtems_bdbuf_release_modified[4]: ", tc->name);
passed = bdbuf_test_print_sc (rtems_bdbuf_release_modified (bd), true);
bd = (rtems_bdbuf_buffer*) node;
pnode = node->previous;
rtems_chain_extract (node);
node = pnode;
bdbuf_test_printf ("%s: rtems_bdbuf_release_modified[3]: ", tc->name);
passed = bdbuf_test_print_sc (rtems_bdbuf_release_modified (bd), true);
for (i = 0; (i < 3) && passed; i++)
{
bdbuf_test_printf ("%s: rtems_bdbuf_release_modified[%d]: ",
tc->name, i);
bd = (rtems_bdbuf_buffer*) rtems_chain_get (&buffers);
passed = bdbuf_test_print_sc (rtems_bdbuf_release_modified (bd),
true);
}
if (passed)
{
/*
* Check the block order.
*/
bdbuf_set_disk_driver_action (tc, BDBUF_DISK_BLOCKS_INORDER);
bdbuf_test_printf ("%s: rtems_bdbuf_syncdev[%d:%d]: checking order\n",
tc->name, i,
tc->major,
tc->minor);
sc = rtems_bdbuf_syncdev (tc->dd);
bdbuf_test_printf ("%s: rtems_bdbuf_syncdev[%d:%d]: ",
tc->name, i,
tc->major,
tc->minor);
passed = bdbuf_test_print_sc (sc, true);
}
tc->passed = passed;
tc->test = 0;
}
static void
bdbuf_tests_task_0 (rtems_task_argument arg)
{
bdbuf_task_control* tc = (bdbuf_task_control*) arg;
while (!tc->die)
{
switch (tc->test)
{
case 0:
/*
* Wait for the next test.
*/
bdbuf_wait (tc->name, 0);
break;
case 1:
bdbuf_tests_task_0_test_1 (tc);
break;
case 2:
bdbuf_tests_task_0_test_2 (tc);
break;
case 3:
bdbuf_tests_task_0_test_3 (tc);
break;
case 4:
bdbuf_tests_task_0_test_4 (tc);
break;
case 5:
bdbuf_tests_task_0_test_5 (tc);
break;
case 6:
bdbuf_tests_task_0_test_6 (tc);
break;
case 7:
bdbuf_tests_task_0_test_7 (tc);
break;
case 8:
bdbuf_tests_task_0_test_8 (tc);
break;
default:
/*
* Invalid test for this task. An error.
*/
bdbuf_test_printf ("%s: invalid test: %d\n", tc->name, tc->test);
tc->passed = false;
tc->test = 0;
break;
}
}
bdbuf_test_printf ("%s: delete task\n", tc->name);
rtems_task_delete (RTEMS_SELF);
}
/**
* Get the blocks 0 -> 4 and release them. Task 0 should be holding
* each one.
*/
static void
bdbuf_tests_ranged_get_release (bdbuf_task_control* tc,
bool wake_master,
int lower,
int upper)
{
rtems_status_code sc;
bool passed;
int i;
rtems_bdbuf_buffer* bd;
/*
* Set task control's passed to false to handle a timeout.
*/
tc->passed = false;
passed = true;
for (i = lower; (i < upper) && passed; i++)
{
bdbuf_test_printf ("%s: rtems_bdbuf_get[%d]: blocking ...\n", tc->name, i);
sc = rtems_bdbuf_get (tc->dd, i, &bd);
bdbuf_test_printf ("%s: rtems_bdbuf_get[%d]: ", tc->name, i);
if (!bdbuf_test_print_sc (sc, true))
{
passed = false;
break;
}
bdbuf_test_printf ("%s: rtems_bdbuf_release[%d]: ", tc->name, i);
sc = rtems_bdbuf_release (bd);
if (!bdbuf_test_print_sc (sc, true))
{
passed = false;
break;
}
/*
* Wake the master to tell it we have finished.
*/
if (wake_master)
bdbuf_send_wait_event (tc->name, "wake master", tc->master);
}
tc->passed = passed;
tc->test = 0;
}
static void
bdbuf_tests_task_1 (rtems_task_argument arg)
{
bdbuf_task_control* tc = (bdbuf_task_control*) arg;
while (!tc->die)
{
switch (tc->test)
{
case 0:
/*
* Wait for the next test.
*/
bdbuf_wait (tc->name, 0);
break;
case 2:
bdbuf_tests_ranged_get_release (tc, false, 0, 5);
break;
case 4:
bdbuf_tests_ranged_get_release (tc, false, 0, 9);
break;
case 5:
bdbuf_tests_ranged_get_release (tc, false, 20, 25);
break;
default:
/*
* Invalid test for this task. An error.
*/
bdbuf_test_printf ("%s: invalid test: %d\n", tc->name, tc->test);
tc->passed = false;
tc->test = 0;
break;
}
}
bdbuf_test_printf ("%s: delete task\n", tc->name);
rtems_task_delete (RTEMS_SELF);
}
/**
* Get the blocks 0 -> 4 and release them. Task 0 should be holding
* each one.
*/
static void
bdbuf_tests_task_2_test_2 (bdbuf_task_control* tc)
{
/*
* Use task 1's test 2. They are the same.
*/
bdbuf_tests_ranged_get_release (tc, true, 0, 5);
}
static void
bdbuf_tests_task_2 (rtems_task_argument arg)
{
bdbuf_task_control* tc = (bdbuf_task_control*) arg;
while (!tc->die)
{
switch (tc->test)
{
case 0:
/*
* Wait for the next test.
*/
bdbuf_wait (tc->name, 0);
break;
case 2:
bdbuf_tests_task_2_test_2 (tc);
break;
default:
/*
* Invalid test for this task. An error.
*/
bdbuf_test_printf ("%s: invalid test: %d\n", tc->name, tc->test);
tc->passed = false;
tc->test = 0;
break;
}
}
bdbuf_test_printf ("%s: delete task\n", tc->name);
rtems_task_delete (RTEMS_SELF);
}
/**
* Table of task entry points.
*/
static rtems_task_entry bdbuf_test_tasks[] =
{
bdbuf_tests_task_0,
bdbuf_tests_task_1,
bdbuf_tests_task_2
};
#define BDBUF_TESTS_PRI_HIGH (30)
/**
* Wait for the all tests to finish. This is signalled by the test
* number becoming 0.
*/
static bool
bdbuf_tests_finished (bdbuf_task_control* tasks)
{
uint32_t time = 0;
bool finished = false;
while (time < (10 * 4))
{
int t;
finished = true;
for (t = 0; t < BDBUF_TEST_TASKS; t++)
if (tasks[t].test)
{
finished = false;
break;
}
if (finished)
break;
bdbuf_sleep (250);
time++;
}
if (!finished)
bdbuf_test_printf ("master: test timed out\n");
else
{
int t;
for (t = 0; t < BDBUF_TEST_TASKS; t++)
if (!tasks[0].passed)
{
finished = false;
break;
}
}
return finished;
}
/**
* Test 1.
*
* # Get task 0 to get buffer 0 from the pool then release it twice.
*/
static bool
bdbuf_test_1 (bdbuf_task_control* tasks)
{
tasks[0].test = 1;
/*
* Use pool 0.
*/
tasks[0].minor = 0;
bdbuf_send_wait_event ("master", "wake task 0", tasks[0].task);
return bdbuf_tests_finished (tasks);
}
/**
* Test 2.
*
* # Get task 0 to get buffers 0 -> 4 from the pool hold them. Then get
* task 1 and task 2 to get them with blocking. The 2 tasks tests the
* priority blocking on the buffer.
*/
static bool
bdbuf_test_2 (bdbuf_task_control* tasks)
{
int i;
tasks[0].test = 2;
tasks[1].test = 2;
tasks[2].test = 2;
/*
* Use pool 0.
*/
tasks[0].minor = 0;
tasks[1].minor = 0;
tasks[2].minor = 0;
/*
* Wake task 0 and wait for it to have all the buffers.
*/
bdbuf_send_wait_event ("master", "wake task 0", tasks[0].task);
if (bdbuf_wait ("master", BDBUF_SECONDS (5)) != RTEMS_SUCCESSFUL)
return false;
/*
* Wake task 1.
*/
bdbuf_send_wait_event ("master", "wake task 1", tasks[1].task);
/*
* Wake task 2.
*/
bdbuf_send_wait_event ("master", "wake task 2", tasks[2].task);
for (i = 0; i < 5; i++)
{
/*
* Wake task 0 and watch task 2 then task 1 get the released buffer.
*/
bdbuf_send_wait_event ("master", "wake task 0", tasks[0].task);
/*
* Wait until task 2 has the buffer.
*/
if (bdbuf_wait ("master", BDBUF_SECONDS (5)) != RTEMS_SUCCESSFUL)
return false;
}
/*
* Wait for the tests to finish.
*/
return bdbuf_tests_finished (tasks);
}
/**
* Test 3.
*
* # Read a block from disk into the buffer, modify the block and release
* it modified. Use a block great then 4 because 0 -> 4 are in the cache.
*/
static bool
bdbuf_test_3 (bdbuf_task_control* tasks)
{
tasks[0].test = 3;
/*
* Use pool 0.
*/
tasks[0].minor = 0;
/*
* Wake task 0.
*/
bdbuf_send_wait_event ("master", "wake task 0", tasks[0].task);
return bdbuf_tests_finished (tasks);
}
/**
* Test 4.
*
* # Read every buffer in the pool and hold. Then get task 1 to ask for another
* buffer that is being accessed. It will block waiting for it to appear.
*/
static bool
bdbuf_test_4 (bdbuf_task_control* tasks)
{
tasks[0].test = 4;
tasks[1].test = 4;
/*
* Use pool 0.
*/
tasks[0].minor = 0;
tasks[1].minor = 0;
/*
* Wake task 0.
*/
bdbuf_send_wait_event ("master", "wake task 0", tasks[0].task);
/*
* Wait for the buffers in the pool to be taken.
*/
if (bdbuf_wait ("master", BDBUF_SECONDS (5)) != RTEMS_SUCCESSFUL)
return false;
bdbuf_sleep (100);
/*
* Wake task 1 to read another one and have to block.
*/
bdbuf_send_wait_event ("master", "wake task 1", tasks[1].task);
bdbuf_sleep (100);
/*
* Wake task 0 to release it buffers.
*/
bdbuf_send_wait_event ("master", "wake task 0", tasks[0].task);
return bdbuf_tests_finished (tasks);
}
/**
* Test 5.
*
* # Read every buffer in the pool and hold. Then get task 1 to ask for a new
* buffer. It will block waiting for one to appear.
*/
static bool
bdbuf_test_5 (bdbuf_task_control* tasks)
{
tasks[0].test = 5;
tasks[1].test = 5;
/*
* Use pool 0.
*/
tasks[0].minor = 0;
tasks[1].minor = 0;
/*
* Wake task 0.
*/
bdbuf_send_wait_event ("master", "wake task 0", tasks[0].task);
/*
* Wait for the buffers in the pool to be taken.
*/
if (bdbuf_wait ("master", BDBUF_SECONDS (5)) != RTEMS_SUCCESSFUL)
return false;
bdbuf_sleep (100);
/*
* Wake task 1 to read another one and have to block.
*/
bdbuf_send_wait_event ("master", "wake task 1", tasks[1].task);
bdbuf_sleep (100);
/*
* Wake task 0 to release it buffers.
*/
bdbuf_send_wait_event ("master", "wake task 0", tasks[0].task);
return bdbuf_tests_finished (tasks);
}
/**
* Test 6.
*
* # Get 5 buffers, release modify 4 then sync the last.
*/
static bool
bdbuf_test_6 (bdbuf_task_control* tasks)
{
tasks[0].test = 6;
/*
* Use pool 0.
*/
tasks[0].minor = 0;
/*
* Wake task 0.
*/
bdbuf_send_wait_event ("master", "wake task 0", tasks[0].task);
return bdbuf_tests_finished (tasks);
}
/**
* Test 7.
*
* # Get 5 buffers, release modify them all then sync the device.
*/
static bool
bdbuf_test_7 (bdbuf_task_control* tasks)
{
tasks[0].test = 7;
/*
* Use pool 0.
*/
tasks[0].minor = 0;
/*
* Wake task 0.
*/
bdbuf_send_wait_event ("master", "wake task 0", tasks[0].task);
return bdbuf_tests_finished (tasks);
}
/**
* Test 8.
*
* # Get 5 buffers, release modify the last 2 then the reset from 0.
*/
static bool
bdbuf_test_8 (bdbuf_task_control* tasks)
{
tasks[0].test = 8;
/*
* Use pool 0.
*/
tasks[0].minor = 0;
/*
* Wake task 0.
*/
bdbuf_send_wait_event ("master", "wake task 0", tasks[0].task);
return bdbuf_tests_finished (tasks);
}
/**
* A test.
*/
typedef bool (*bdbuf_test) (bdbuf_task_control* tasks);
/**
* A test name and function.
*/
typedef struct bdbuf_test_ident
{
const char* label;
bdbuf_test test;
} bdbuf_test_ident;
/**
* Table of tests.
*/
static bdbuf_test_ident bdbuf_tests[] =
{
{
"Task 0 get buffer 0 from pool 0",
bdbuf_test_1
},
{
"Task 0 get buffer 0 -> 4 from pool 0, task 2 and 1 block getting",
bdbuf_test_2
},
{
"Task 0 read buffer 5, modify and release modified",
bdbuf_test_3
},
{
"Task 0 read all buffers, task 1 blocks waiting for acessed buffer",
bdbuf_test_4
},
{
"Task 0 read all buffers, task 1 blocks waiting for new buffer",
bdbuf_test_5
},
{
"Task 0 release modified 4 buffers then syncs a 5th buffer",
bdbuf_test_6
},
{
"Task 0 release modified 5 buffers then sync the device",
bdbuf_test_7
},
{
"Task 0 releases modified 5 buffers is out or order sequence and the" \
" driver checks the buffers are in order",
bdbuf_test_8
}
};
#define BDBUF_TEST_NUM (sizeof (bdbuf_tests) / sizeof (bdbuf_test_ident))
/**
* Test the BD Buffering code.
*/
static void
bdbuf_tester (void)
{
rtems_device_major_number major;
bdbuf_task_control tasks[BDBUF_TEST_TASKS];
rtems_task_priority old_priority;
int t;
bool passed = true;
rtems_disk_device *dd;
/*
* Change priority to a lower one.
*/
bdbuf_test_printf ("lower priority to %d: ", BDBUF_TESTS_PRI_HIGH + 1);
bdbuf_test_print_sc (rtems_task_set_priority (RTEMS_SELF,
BDBUF_TESTS_PRI_HIGH + 1,
&old_priority),
true);
/*
* This sets up the buffer pools.
*/
if (!bdbuf_tests_setup_disk (&major, &dd))
{
bdbuf_test_printf ("disk set up failed\n");
return;
}
/*
* Make sure the swapout task has run. The user could block
* the swapout task from running until later. This is not
* tested.
*/
bdbuf_sleep (100);
/*
* Start the test tasks used to test the threading parts
* of the bdbuf code.
*/
for (t = 0; t < BDBUF_TEST_TASKS; t++)
{
bdbuf_task_control_init (t, &tasks[t],
rtems_task_self (),
major,
dd);
if (!bdbuf_tests_create_task (&tasks[t],
BDBUF_TESTS_PRI_HIGH - t,
bdbuf_test_tasks[t]))
return;
}
/*
* Let the test tasks run if they have not already done so.
*/
bdbuf_sleep (100);
/*
* Perform each test.
*/
for (t = 0; (t < BDBUF_TEST_NUM) && passed; t++)
{
bdbuf_test_printf ("test %d: %s\n", t + 1, bdbuf_tests[t].label);
passed = bdbuf_tests[t].test (tasks);
bdbuf_test_printf ("test %d: %s\n", t + 1, passed ? "passed" : "failed");
}
}
static rtems_task Init(rtems_task_argument argument)
{
printf ("\n\n*** TEST BLOCK 6 ***\n");
bdbuf_tester ();
printf ("*** END OF TEST BLOCK 6 ***\n");
exit (0);
}
#define CONFIGURE_INIT
#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_BDBUF_TASK_STACK_SIZE BDBUF_TEST_STACK_SIZE
#define CONFIGURE_MAXIMUM_TASKS (1 + BDBUF_TEST_TASKS)
#define CONFIGURE_MAXIMUM_DRIVERS 3
#define CONFIGURE_MAXIMUM_SEMAPHORES 2
#define CONFIGURE_EXTRA_TASK_STACKS \
(BDBUF_TEST_TASKS * BDBUF_TEST_STACK_SIZE)
#define CONFIGURE_INIT_TASK_STACK_SIZE BDBUF_TEST_STACK_SIZE
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#include <rtems/confdefs.h>