/*! @file
* @brief Common declarations of bdbuf tests.
*
* Copyright (C) 2010 OKTET Labs, St.-Petersburg, Russia
* Author: Oleg Kravtsov <Oleg.Kravtsov@oktetlabs.ru>
*
* 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.
*
* $Id$
*/
#ifndef BDBUF_TESTS_H
#define BDBUF_TESTS_H
#include <stdio.h>
#include <errno.h>
#include <rtems/diskdevs.h>
#include <rtems/bdbuf.h>
#ifdef __cplusplus
extern "C" {
#endif
extern void run_bdbuf_tests(void);
extern void bdbuf_test1_1_main(void);
extern void bdbuf_test1_2_main(void);
extern void bdbuf_test1_3_main(void);
extern void bdbuf_test1_4_main(void);
extern void bdbuf_test1_5_main(void);
extern void bdbuf_test2_1_main(void);
extern void bdbuf_test2_2_main(void);
extern void bdbuf_test2_3_main(void);
extern void bdbuf_test2_4_main(void);
extern void bdbuf_test2_5_main(void);
extern void bdbuf_test3_1_main(void);
extern void bdbuf_test3_2_main(void);
extern void bdbuf_test3_3_main(void);
extern void bdbuf_test4_1_main(void);
extern void bdbuf_test4_2_main(void);
extern void bdbuf_test4_3_main(void);
extern rtems_device_driver
test_disk_initialize(
rtems_device_major_number major,
rtems_device_minor_number minor,
void *arg);
#define ARRAY_NUM(a_) (sizeof(a_) / sizeof(a_[0]))
/**
* Configuration parameters of Test disk device.
*/
#define TEST_DISK_BLOCK_SIZE 512
#define TEST_DISK_BLOCK_NUM 1024
#define TEST_DISK_NAME "/dev/testdisk"
/** Type of messages between test disk driver and main test task */
enum bdbuf_test_msg_type {
/** Message sent by disk driver to main test task */
BDBUF_TEST_MSG_TYPE_DRIVER_REQ,
/**
* Message sent main test task to disk driver
* in reply to request.
*/
BDBUF_TEST_MSG_TYPE_DRIVER_REPLY,
};
/**
* Message used in communication between test disk driver
* and main test task.
* All R/W requests obtained by driver (from bdbuf library)
* are directed to main test task. Then main test task
* sends a reply after which test disk driver notifies
* bdbuf library about operation complete event.
*/
typedef struct bdbuf_test_msg {
/** Message type */
enum bdbuf_test_msg_type type;
union {
struct driver_req {
const rtems_disk_device *dd;
uint32_t req;
void *argp;
} driver_req;
struct driver_reply {
int ret_val;
int ret_errno;
rtems_status_code res_status;
int res_errno;
} driver_reply;
} val;
} bdbuf_test_msg;
typedef enum bdbuf_rest_thread_prio {
BDBUF_TEST_THREAD_PRIO_NORMAL = 3,
BDBUF_TEST_THREAD_PRIO_LOW = 7,
BDBUF_TEST_THREAD_PRIO_HIGH = 5,
} bdbuf_rest_thread_prio;
typedef struct test_ctx {
/**
* Message queue used by main task to get messages from
* disk device driver.
*/
Objects_Id test_qid;
/**
* Object ID for disk driver queue.
* Test task will send messages to this queue in reply
* to messages received on @a test_qid.
*/
Objects_Id test_drv_qid;
/** Test name */
const char *test_name;
/**
* Semaphore used for synchronization between test thread
* and main test task.
* Main test task blocks on one of these semaphores and an auxiliary thread
* releases it in order to pass control to main task again.
*/
rtems_id test_sync_main[3];
/**
* This semaphore is used for synchornization between main test task
* and the END of auxiliary threads.
*/
rtems_id test_end_main;
/**
* Semaphore used for synchronization between test thread
* and main test task.
* Thread #N blocks on this semaphore and the main task releases it
* when it wants to pass control to the auxiliary thread #N.
*/
rtems_id test_sync[3];
/** Task Id values of auxiliary threads */
Objects_Id test_task[3];
} test_ctx;
extern test_ctx g_test_ctx;
/** Device ID used for testing */
extern const rtems_disk_device *test_dd;
/**
* Create a message queue for test driver that is used for
* receiving messages from test task.
*
* @param[out] id RTEMS message queue identifier
*
* @return Status of the operation
*/
extern rtems_status_code bdbuf_test_create_drv_rx_queue(Objects_Id *id);
/**
* Obtain a message queue for test driver that is used for
* sending messages to test task.
*
* @param[out] id RTEMS message queue identifier
*
* @return Status of the operation
*/
extern rtems_status_code bdbuf_test_create_drv_tx_queue(Objects_Id *id);
/**
* Start a separate test task represented by function @p func
* and associated with task index @p idx.
*
* @param[in] idx Task index
* @param[in] func Test task function
*/
extern rtems_status_code bdbuf_test_start_thread(unsigned int idx,
rtems_task_entry func);
/**
* Finalize test run.
*/
extern rtems_status_code bdbuf_test_end(void);
/** Test result variable */
extern bool good_test_result;
#define BDBUF_TEST_BLOCK_TIMEOUT (2 * rtems_clock_get_ticks_per_second())
#define SET_THREAD_PRIORITY(t_num_, prio_) \
do { \
rtems_task_priority old_prio; \
rtems_status_code rc_; \
\
rc_ = rtems_task_set_priority( \
g_test_ctx.test_task[(t_num_) - 1], \
BDBUF_TEST_THREAD_PRIO_ ## prio_, &old_prio); \
if (rc_ != RTEMS_SUCCESSFUL) \
{ \
printk("Failed to change priority of test thread #" \
#t_num_ " in test %s\n", g_test_ctx.test_name); \
return; \
} \
} while (0)
/**
* Start thread number @p t_num_ with @p func_ function
* as an entry point of the thread.
*
* @param t_num_ thread number.
* @param func_ thread entry point.
*/
#define START_THREAD(t_num_, func_) \
do { \
if (bdbuf_test_start_thread((t_num_) - 1, func_) != \
RTEMS_SUCCESSFUL) \
{ \
return; \
} \
printk("Thread #" #t_num_ " started\n"); \
} while (0)
/**
* Wait for a message from device driver.
*
* @param[out] msg_ Pointer to a message data structure
*/
#define WAIT_DRV_MSG(msg_) \
do { \
rtems_status_code rc_; \
size_t msg_size_ = sizeof(*(msg_)); \
\
rc_ = rtems_message_queue_receive(g_test_ctx.test_qid, \
(msg_), &msg_size_, \
RTEMS_WAIT, \
RTEMS_NO_TIMEOUT); \
if (rc_ != RTEMS_SUCCESSFUL || \
msg_size_ != sizeof(*(msg_))) \
{ \
printk("Error at %s:%d\n", __FILE__, __LINE__); \
return; \
} \
if ((msg_)->type != BDBUF_TEST_MSG_TYPE_DRIVER_REQ) \
{ \
printk("Unexpected message received: %d\n", \
(msg_)->type); \
return; \
} \
} while (0)
#define WAIT_DRV_MSG_WR(msg_) \
do { \
WAIT_DRV_MSG(msg_); \
if ((msg_)->val.driver_req.req != RTEMS_BLKIO_REQUEST || \
(msg_)->val.driver_req.dd != test_dd || \
((rtems_blkdev_request *) \
((msg_)->val.driver_req.argp))->req != \
RTEMS_BLKDEV_REQ_WRITE) \
{ \
printk("Unexpected message received by disk driver: " \
"req - 0x%x (0x%x), dev - %d (%d)\n", \
(msg_)->val.driver_req.req, RTEMS_BLKIO_REQUEST, \
(msg_)->val.driver_req.dd, test_dd); \
return; \
} \
} while (0)
#define CHECK_NO_DRV_MSG() \
do { \
rtems_status_code rc_; \
bdbuf_test_msg msg_; \
size_t msg_size_ = sizeof(msg_); \
\
rc_ = rtems_message_queue_receive(g_test_ctx.test_qid, \
(&msg_), &msg_size_, \
RTEMS_WAIT, \
BDBUF_TEST_BLOCK_TIMEOUT); \
if (rc_ != RTEMS_TIMEOUT) \
{ \
printk("Error at %s:%d\n", __FILE__, __LINE__); \
return; \
} \
} while (0)
extern bdbuf_test_msg test_drv_msg;
/**
* Send a message to device driver (in order to complete
* blocked I/O operation).
*
* @param ret_val_ Return value to use when returning from
* ioctl() function.
* @param ret_errno_ errno value to set to errno variable.
* @param res_status_ In case return value from the ioctl()
* function is equal to 0, this value
* is used as status code in asynchronous
* notification.
* @param res_errno_ In case return value from the ioctl()
* function is equal to 0, this value
* is used as error value in asynchronous
* notification.
*
*/
#define SEND_DRV_MSG(ret_val_, ret_errno_, res_status_, res_errno_) \
do { \
rtems_status_code rc_; \
\
memset(&test_drv_msg, 0, sizeof(test_drv_msg)); \
test_drv_msg.type = BDBUF_TEST_MSG_TYPE_DRIVER_REPLY; \
test_drv_msg.val.driver_reply.ret_val = (ret_val_); \
test_drv_msg.val.driver_reply.ret_errno = (ret_errno_); \
test_drv_msg.val.driver_reply.res_status = (res_status_); \
test_drv_msg.val.driver_reply.res_errno = (res_errno_); \
\
rc_ = rtems_message_queue_send(g_test_ctx.test_drv_qid, \
&test_drv_msg, \
sizeof(test_drv_msg)); \
if (rc_ != RTEMS_SUCCESSFUL) \
{ \
printk("Error while sending a message to " \
"disk driver: %u\n", rc_); \
return; \
} \
} while (0)
/**
* Block main test task until a thread passes back control
* with CONTINUE_MAIN().
*
* @param t_num_ thread number from which the main thread
* is waiting for a sync.
*/
#define WAIT_THREAD_SYNC(t_num_) \
do { \
rtems_status_code rc_; \
\
rc_ = rtems_semaphore_obtain( \
g_test_ctx.test_sync_main[(t_num_) - 1], \
RTEMS_WAIT, RTEMS_NO_TIMEOUT); \
if (rc_ != RTEMS_SUCCESSFUL) \
{ \
printk("Failed to get sync with a thread: %d\n", rc_); \
return; \
} \
} while (0)
/**
* Check that a particular thread is blocked.
*
* @param t_num_ thread number that we want to check for blocking
*/
#define CHECK_THREAD_BLOCKED(t_num_) \
do { \
rtems_status_code rc_; \
\
rc_ = rtems_semaphore_obtain( \
g_test_ctx.test_sync_main[(t_num_) - 1], \
RTEMS_WAIT, BDBUF_TEST_BLOCK_TIMEOUT); \
if (rc_ != RTEMS_TIMEOUT) \
{ \
printk("Thread %d is not blocked at%s:%d\n", \
t_num_, __FILE__, __LINE__); \
return; \
} \
} while (0)
/**
* Main test task gives control to the particular thread.
*
* @param t_num_ thread number to which main test task wants
* to give control.
*/
#define CONTINUE_THREAD(t_num_) \
do { \
rtems_status_code rc_; \
\
rc_ = rtems_semaphore_release( \
g_test_ctx.test_sync[(t_num_) - 1]); \
if (rc_ != RTEMS_SUCCESSFUL) \
{ \
printk("Failed to give control to thread #" \
#t_num_ ": %d\n", rc_); \
return; \
} \
} while (0)
/**
* Passes control back to the main test task from a thread.
*
* @param t_num_ Current thread number
*/
#define CONTINUE_MAIN(t_num_) \
do { \
rtems_status_code rc_; \
\
rc_ = rtems_semaphore_release( \
g_test_ctx.test_sync_main[(t_num_) - 1]); \
if (rc_ != RTEMS_SUCCESSFUL) \
{ \
printk("Failed to give control to " \
"main task: %d", rc_); \
return; \
} \
rc_ = rtems_semaphore_obtain( \
g_test_ctx.test_sync[(t_num_) - 1], \
RTEMS_WAIT, RTEMS_NO_TIMEOUT); \
if (rc_ != RTEMS_SUCCESSFUL) \
{ \
printk("Failed to block on thread #" \
#t_num_ ": %d\n", rc_); \
return; \
} \
} while (0)
#define WAIT_MAIN_SYNC(t_num_) \
do { \
rtems_status_code rc_; \
\
rc_ = rtems_semaphore_obtain( \
g_test_ctx.test_sync[(t_num_) - 1], \
RTEMS_WAIT, RTEMS_NO_TIMEOUT); \
if (rc_ != RTEMS_SUCCESSFUL) \
{ \
printk("Failed to block on thread #" \
#t_num_ ": %d\n", rc_); \
return; \
} \
} while (0)
#define TEST_START(test_name_) \
do { \
good_test_result = true; \
g_test_ctx.test_name = test_name_; \
printk("%s - STARTED\n", \
g_test_ctx.test_name); \
} while (0)
#define TEST_CHECK_RESULT(step_) \
do { \
if (!good_test_result) \
{ \
printk("TEST FAILED (Step %s)\n", \
step_); \
rtems_task_delete(RTEMS_SELF); \
} \
else \
{ \
printk("%s: Step %s - OK\n", \
g_test_ctx.test_name, step_); \
} \
} while (0)
#define TEST_END() \
do { \
bdbuf_test_end(); \
\
if (good_test_result) \
printk("TEST PASSED\n"); \
else \
printk("TEST FAILED (END)"); \
} while (0)
#define THREAD_END() \
do { \
rtems_status_code rc_; \
\
rc_ = rtems_semaphore_release(g_test_ctx.test_end_main);\
if (rc_ != RTEMS_SUCCESSFUL) \
{ \
printk("Failed to give control to " \
"main task: %d", rc_); \
return; \
} \
rtems_task_delete(RTEMS_SELF); \
} while (0)
#define TEST_FAILED() \
do { \
good_test_result = false; \
} while (0)
#define SLEEP() \
rtems_task_wake_after(BDBUF_TEST_BLOCK_TIMEOUT)
extern rtems_status_code
bdbuf_test_start_aux_task(rtems_name name,
rtems_task_entry entry_point,
rtems_task_argument arg,
Objects_Id *id);
#ifdef __cplusplus
}
#endif
#endif /* BDBUF_TESTS_H */