From ce79aa2b73a095885ccde0d1801411bf63446422 Mon Sep 17 00:00:00 2001 From: Bahadir Balban Date: Sat, 30 May 2009 16:46:30 +0300 Subject: [PATCH] Mutex test progress - Mutex test added. Forked tasks demonstrate produce/consumer using a shared mmap'ed page. - Added l4lib assembler syscall - Added forgotten SWI to mutex control offset in syscall page. - Added mutex head initialization - Contended child successfully sleeps in a waitqueue. Issues: - Somehow the child's produced page buffer is altered at about [4020] offset. Parent fails to validate buffer therefore. - Need to add syncing to test so that parent does not unlock and lock again before child has a chance to lock buffer and produce. --- include/l4/api/mutex.h | 2 + src/api/mutex.c | 22 +++-- src/arch/arm/syscall.S | 1 + src/glue/arm/init.c | 2 + src/glue/arm/systable.c | 2 +- tasks/libl4/src/arm/syscalls.S | 12 +++ tasks/libl4/src/init.c | 2 +- tasks/libl4/src/mutex.c | 1 + tasks/test0/include/tests.h | 1 + tasks/test0/main.c | 3 + tasks/test0/src/mutextest.c | 161 +++++++++++++++++++++++++++++++++ 11 files changed, 199 insertions(+), 10 deletions(-) create mode 100644 tasks/test0/src/mutextest.c diff --git a/include/l4/api/mutex.h b/include/l4/api/mutex.h index 34e9f40..bc01100 100644 --- a/include/l4/api/mutex.h +++ b/include/l4/api/mutex.h @@ -6,6 +6,8 @@ #if defined (__KERNEL__) #define MUTEX_CONTROL_LOCK L4_MUTEX_LOCK #define MUTEX_CONTROL_UNLOCK L4_MUTEX_UNLOCK + +void init_mutex_queue_head(void); #endif #define L4_MUTEX_LOCK 0 diff --git a/src/api/mutex.c b/src/api/mutex.c index 299841b..eee33ff 100644 --- a/src/api/mutex.c +++ b/src/api/mutex.c @@ -24,23 +24,29 @@ struct mutex_queue { struct mutex_queue_head { struct list_head list; + + /* + * Lock for mutex_queue create/deletion and also list add/removal. + * Both operations are done jointly so a single lock is enough. + */ + struct mutex mutex_control_mutex; int count; } mutex_queue_head; -/* - * Lock for mutex_queue create/deletion and also list add/removal. - * Both operations are done jointly so a single lock is enough. - */ -struct mutex mutex_control_mutex; - +void init_mutex_queue_head(void) +{ + memset(&mutex_queue_head, 0, sizeof (mutex_queue_head)); + INIT_LIST_HEAD(&mutex_queue_head.list); + mutex_init(&mutex_queue_head.mutex_control_mutex); +} void mutex_queue_head_lock() { - mutex_lock(&mutex_control_mutex); + mutex_lock(&mutex_queue_head.mutex_control_mutex); } void mutex_queue_head_unlock() { - mutex_unlock(&mutex_control_mutex); + mutex_unlock(&mutex_queue_head.mutex_control_mutex); } diff --git a/src/arch/arm/syscall.S b/src/arch/arm/syscall.S index 3e0240e..9c8e6f9 100644 --- a/src/arch/arm/syscall.S +++ b/src/arch/arm/syscall.S @@ -30,5 +30,6 @@ BEGIN_PROC(arm_system_calls) swi 0x14 @ kread /* 0x28 */ swi 0x14 @ kmem_control /* 0x2C */ swi 0x14 @ time /* 0x30 */ + swi 0x14 @ mutex_control /* 0x34 */ END_PROC(arm_system_calls) diff --git a/src/glue/arm/init.c b/src/glue/arm/init.c index 009bb1e..73fa569 100644 --- a/src/glue/arm/init.c +++ b/src/glue/arm/init.c @@ -26,6 +26,7 @@ #include INC_PLAT(printascii.h) #include INC_API(syscall.h) #include INC_API(kip.h) +#include INC_API(mutex.h) unsigned int kernel_mapping_end; @@ -344,6 +345,7 @@ void init_tasks() /* Initialise the global task and address space lists */ init_ktcb_list(); init_address_space_list(); + init_mutex_queue_head(); printk("%s: Initialized. Starting %s as pager.\n", __KERNELNAME__, __PAGERNAME__); diff --git a/src/glue/arm/systable.c b/src/glue/arm/systable.c index 4743fd1..f6992f5 100644 --- a/src/glue/arm/systable.c +++ b/src/glue/arm/systable.c @@ -54,7 +54,7 @@ void syscall_init() syscall_table[sys_kread_offset >> 2] = (syscall_fn_t)sys_kread; syscall_table[sys_kmem_control_offset >> 2] = (syscall_fn_t)sys_kmem_control; syscall_table[sys_time_offset >> 2] = (syscall_fn_t)sys_time; - syscall_table[sys_mutex_control_offset >> 2] = (syscall_fn_t)sys_mutex_control; + syscall_table[sys_mutex_control_offset >> 2] = (syscall_fn_t)sys_mutex_control; add_mapping(virt_to_phys(&__syscall_page_start), ARM_SYSCALL_PAGE, PAGE_SIZE, MAP_USR_RO_FLAGS); diff --git a/tasks/libl4/src/arm/syscalls.S b/tasks/libl4/src/arm/syscalls.S index 430e484..4545f81 100644 --- a/tasks/libl4/src/arm/syscalls.S +++ b/tasks/libl4/src/arm/syscalls.S @@ -197,6 +197,18 @@ BEGIN_PROC(l4_space_control) ldmfd sp!, {pc} @ Restore original lr and return. END_PROC(l4_space_control) +/* + * Locks/unlocks a userspace mutex. + * @r0 = mutex virtual address, @r1 = mutex operation code + */ +BEGIN_PROC(l4_mutex_control) + stmfd sp!, {lr} + ldr r12, =__l4_mutex_control + mov lr, pc + ldr pc, [r12] + ldmfd sp!, {pc} @ Restore original lr and return. +END_PROC(l4_mutex_control) + /* * Sets registers of a thread and its pager. * @r0 = ptr to exregs_data structure, @r1 = tid of thread. diff --git a/tasks/libl4/src/init.c b/tasks/libl4/src/init.c index ffd93b1..8f9e6e4 100644 --- a/tasks/libl4/src/init.c +++ b/tasks/libl4/src/init.c @@ -55,6 +55,6 @@ void __l4_init(void) (__l4_exchange_registers_t)kip->exchange_registers; __l4_kmem_control = (__l4_kmem_control_t)kip->kmem_control; __l4_time = (__l4_time_t)kip->time; - __l4_mutex_control= (__l4_mutex_control_t)kip->mutex_control; + __l4_mutex_control = (__l4_mutex_control_t)kip->mutex_control; } diff --git a/tasks/libl4/src/mutex.c b/tasks/libl4/src/mutex.c index a1aabef..3f023c7 100644 --- a/tasks/libl4/src/mutex.c +++ b/tasks/libl4/src/mutex.c @@ -6,6 +6,7 @@ #include #include #include +#include /* * NOTES: diff --git a/tasks/test0/include/tests.h b/tasks/test0/include/tests.h index 44cc897..e6e313f 100644 --- a/tasks/test0/include/tests.h +++ b/tasks/test0/include/tests.h @@ -23,5 +23,6 @@ int dirtest(void); int fileio(void); int clonetest(void); int exectest(void); +int user_mutex_test(void); #endif /* __TEST0_TESTS_H__ */ diff --git a/tasks/test0/main.c b/tasks/test0/main.c index e351826..c0f039a 100644 --- a/tasks/test0/main.c +++ b/tasks/test0/main.c @@ -52,6 +52,9 @@ void main(void) ipc_full_test(); ipc_extended_test(); } + if (parent_of_all == getpid()) { + user_mutex_test(); + } exectest(); while (1) diff --git a/tasks/test0/src/mutextest.c b/tasks/test0/src/mutextest.c new file mode 100644 index 0000000..7b6546e --- /dev/null +++ b/tasks/test0/src/mutextest.c @@ -0,0 +1,161 @@ +/* + * Tests for userspace mutexes + * + * Copyright (C) 2007-2009 Bahadir Bilgehan Balban + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This structure is placed at the head of shared memory. + * Tasks lock and write to rest of the page. Indicate that + * they have run, and the later running task tests that both + * processes has run and no data corruption occured on page_rest. + */ +struct shared_page { + struct l4_mutex mutex; + int child_has_run; + int parent_has_run; + char page_rest[]; +}; + +static struct shared_page *shared_page; + +/* + * This test forks a parent task, creates a shared memory mapping, and makes two tasks + * lock and write to almost the whole page 10 times with different values, and makes + * the tasks test that the writes are all there without any wrong values. The amount of + * time spent should justify a context switch and demonstrate that lock protects the + * region. + */ +int user_mutex_test(void) +{ + pid_t child, parent; + int map_size = PAGE_SIZE; + int buf_size; + void *base; + + /* Get parent pid */ + parent = getpid(); + + + /* + * Create a shared anonymous memory region. This can be + * accessed by both parent and child after a fork. + */ + if ((int)(base = mmap(0, map_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, 0, 0)) < 0) { + printf("%s: mmap for extended ipc buffer failed: %d\n", + __FUNCTION__, (int)base); + goto out_err; + } else + printf("mmap: Anonymous shared buffer at %p\n", base); + + shared_page = base; + + /* Find the buffer size */ + buf_size = map_size - sizeof(*shared_page); + printf("Total buffer size: %d\n", buf_size); + + /* Initialize the mutex */ + l4_mutex_init(&shared_page->mutex); + + /* Initialize buffer with unique child value */ + printf("Child writing:\n"); + for (int i = 0; i < buf_size; i++) { + shared_page->page_rest[i] = 'c'; + printf("%c", shared_page->page_rest[i]); + } + printf("\n\n"); + + /* Fork the current task */ + if ((child = fork()) < 0) { + printf("%s: Fork failed with %d\n", __FUNCTION__, errno); + goto out_err; + } + + if (child) + printf("%d: Created child with pid %d\n", getpid(), child); + else + printf("Child %d running.\n", getpid()); + + /* Child locks and produces */ + if (child == 0) { + + for (int i = 0; i < 5; i++) { + /* Lock */ + printf("Child locking page.\n"); + l4_mutex_lock(&shared_page->mutex); + + printf("Child locked page.\n"); + /* Get sample value */ + char val = shared_page->page_rest[0]; + + /* Write a unique child value to whole buffer */ + for (int i = 0; i < buf_size; i++) { + /* Check sample is same in all */ + if (shared_page->page_rest[i] != val) { + printf("Sample values dont match. " + "page_rest[%d] = %c, " + "val = %c\n", i, + shared_page->page_rest[i], val); + goto out_err; + } + shared_page->page_rest[i] = 'c'; + } + printf("Child produced.\n"); + /* Unlock */ + l4_mutex_unlock(&shared_page->mutex); + printf("Child unlocked page.\n"); + } + + /* Parent locks and consumes */ + } else { + for (int i = 0; i < 5; i++) { + printf("Parent locking page.\n"); + + /* Lock the page */ + l4_mutex_lock(&shared_page->mutex); + printf("Parent locked page.\n"); + + printf("Parent reading:\n"); + + /* Test and consume the page */ + for (int i = 0; i < buf_size; i++) { + printf("%c", shared_page->page_rest[i]); + /* Test that child has produced */ + if (shared_page->page_rest[i] != 'c') { + printf("Child not produced. " + "page_rest[%d] = %c, " + "expected = 'c'\n", + i, shared_page->page_rest[i]); + goto out_err; + } + /* Consume the page */ + shared_page->page_rest[i] = 'P'; + } + printf("\n\n"); + printf("Parent consumed.\n"); + l4_mutex_unlock(&shared_page->mutex); + printf("Parent unlocked page.\n"); + } + + printf("USER MUTEX TEST: -- PASSED --\n"); + } + return 0; + +out_err: + printf("USER MUTEX TEST: -- FAILED --\n"); + return -1; +} + + + + +