diff options
author | Matt Raybuck <mraybuc@us.ibm.com> | 2018-10-24 07:54:39 -0500 |
---|---|---|
committer | Daniel M. Crowell <dcrowell@us.ibm.com> | 2018-10-29 10:43:57 -0500 |
commit | 0c5edba986eb543bc94ecb3514c40ca151dafc61 (patch) | |
tree | 157c85bcbd0d613615a8933cc1f0bb836d4381da /src | |
parent | 13d6fcf76a5fcab45d092fe39a807fbd70f4aff9 (diff) | |
download | talos-hostboot-0c5edba986eb543bc94ecb3514c40ca151dafc61.tar.gz talos-hostboot-0c5edba986eb543bc94ecb3514c40ca151dafc61.zip |
Added support for recursive mutexes
There was only support for non-recursive mutexes. This commit adds
support for recursive mutexes and a new API for using them.
Change-Id: I664c181af1633b05b8b2da6b1ff21b93a37cec28
RTC: 196793
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/67938
Reviewed-by: Nicholas E. Bofferding <bofferdn@us.ibm.com>
Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com>
Reviewed-by: Ilya Smirnov <ismirno@us.ibm.com>
Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com>
Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com>
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/include/sys/sync.h | 64 | ||||
-rw-r--r-- | src/lib/sync.C | 128 | ||||
-rwxr-xr-x | src/usr/targeting/common/xmltohb/xmltohb.pl | 16 | ||||
-rw-r--r-- | src/usr/testcore/lib/makefile | 4 | ||||
-rw-r--r-- | src/usr/testcore/lib/synctest.H | 78 |
5 files changed, 262 insertions, 28 deletions
diff --git a/src/include/sys/sync.h b/src/include/sys/sync.h index d56f8795b..2772c8807 100644 --- a/src/include/sys/sync.h +++ b/src/include/sys/sync.h @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2014 */ +/* Contributors Listed Below - COPYRIGHT 2011,2018 */ /* [+] Google Inc. */ /* [+] International Business Machines Corp. */ /* */ @@ -27,6 +27,7 @@ #define __SYS_SYNC_H #include <stdint.h> +#include <sys/task.h> /** * Mutex object type @@ -34,6 +35,12 @@ struct _futex_imp_t { uint64_t iv_val; + uint64_t iv_ownerLockCount; + tid_t iv_owner; + bool iv_recursive; + + _futex_imp_t () : iv_val(0), iv_ownerLockCount(0), + iv_owner(0), iv_recursive(false) {} }; typedef _futex_imp_t mutex_t; @@ -51,7 +58,7 @@ struct _barrier_imp_t typedef _barrier_imp_t barrier_t; -#define MUTEX_INITIALIZER {0} +#define MUTEX_INITIALIZER mutex_t() /** * Conditional variable types @@ -103,25 +110,47 @@ void barrier_wait (barrier_t * i_barrier); /** * @fn mutex_init - * @brief Initialize a mutex object + * @brief Initialize a non-recursive mutex object * @param[out] o_mutex the mutex * @pre an uninitialized mutex object - * @post a valid mutex object + * @post a valid non-recursive mutex object */ void mutex_init(mutex_t * o_mutex); /** + * @fn recursive_mutex_init + * @brief Initialize a recursive mutex object + * @param[out] o_mutex the mutex + * @pre an uninitialized mutex object + * @post a valid recursive mutex object + */ +void recursive_mutex_init(mutex_t * o_mutex); + + +/** * @fn mutex_destroy - * @brief Destroy / uninitialize a mutex object. - * @param[in] i_mutex - the mutex + * @brief Destroy / uninitialize a non-recursive mutex object. Code will assert + * if a recursive mutex is passed in. + * @param[in] i_mutex - the non-recursive mutex * @note This does not free the memory associated with the object if the mutex * was allocated off the heap. */ void mutex_destroy(mutex_t * i_mutex); /** + * @fn recursive_mutex_destroy + * @brief Destroy / uninitialize a recursive mutex object. Code will assert if a + * non-recursive mutex is passed in. + * @param[in] i_mutex - the mutex + * @note This does not free the memory associated with the object if the mutex + * was allocated off the heap. + */ +void recursive_mutex_destroy(mutex_t * i_mutex); + +/** * @fn mutex_lock - * @brief Obtain a lock on a mutex + * @brief Obtain a lock on a mutex. If a recursive mutex is passed as the + * parameter the code will assert. * @param[in] i_mutex - The mutex * @post returns when this task has the lock */ @@ -129,13 +158,32 @@ void mutex_lock(mutex_t * i_mutex); /** * @fn mutex_unlock - * @brief Release a lock on a mutex + * @brief Release a lock on a mutex. If a recursive mutex is passed as the + * parameter the code will assert. * @param[in] i_mutex - the mutex * @post mutex lock released */ void mutex_unlock(mutex_t * i_mutex); /** + * @fn recursive_mutex_lock + * @brief Obtain a lock on a recursive mutex. If a non-recursive mutex is passed + * as the parameter the code will assert. + * @param[in] i_mutex - The mutex + * @post returns when this task has the lock + */ +void recursive_mutex_lock(mutex_t * i_mutex); + +/** + * @fn recursive_mutex_unlock + * @brief Release a lock on a recursive mutex. If a non-recursive mutex is + * passed as the parameter the code will assert. + * @param[in] i_mutex - A recursive mutex + * @post mutex lock released or mutex iv_ownerLockCount decremented. + */ +void recursive_mutex_unlock(mutex_t * i_mutex); + +/** * @fn sync_cond_init * @brief Initialize a condtional variable * @param i_cond, The conditional variable diff --git a/src/lib/sync.C b/src/lib/sync.C index 37367b219..c318856a1 100644 --- a/src/lib/sync.C +++ b/src/lib/sync.C @@ -5,7 +5,9 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* COPYRIGHT International Business Machines Corp. 2011,2014 */ +/* Contributors Listed Below - COPYRIGHT 2011,2018 */ +/* [+] International Business Machines Corp. */ +/* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ @@ -116,9 +118,32 @@ void barrier_wait (barrier_t * i_barrier) //----------------------------------------------------------------------------- -void mutex_init(mutex_t * o_mutex) +/** + * @fn mutex_init + * @brief Initialize a mutex object. + * @param[out] o_mutex the mutex + * @param[in] i_recursive indicate whether a mutex is recursive or not. + * @pre an uninitialized mutex object + * @post a valid mutex object + */ +void mutex_init(mutex_t * o_mutex, bool i_recursive) { o_mutex->iv_val = 0; + o_mutex->iv_owner = 0; + o_mutex->iv_ownerLockCount = 0; + o_mutex->iv_recursive = i_recursive; + return; +} + +void mutex_init(mutex_t * o_mutex) +{ + mutex_init(o_mutex, false); + return; +} + +void recursive_mutex_init(mutex_t * o_mutex) +{ + mutex_init(o_mutex, true); return; } @@ -126,7 +151,21 @@ void mutex_init(mutex_t * o_mutex) void mutex_destroy(mutex_t * i_mutex) { + crit_assert(!i_mutex->iv_recursive); + i_mutex->iv_val = ~0; + i_mutex->iv_owner = 0; + i_mutex->iv_ownerLockCount = 0; + return; +} + +void recursive_mutex_destroy(mutex_t * i_mutex) +{ + crit_assert(i_mutex->iv_recursive); + + i_mutex->iv_val = ~0; + i_mutex->iv_owner = 0; + i_mutex->iv_ownerLockCount = 0; return; } @@ -141,19 +180,21 @@ void mutex_lock(mutex_t * i_mutex) // leaving (isync). Both __sync_val_compare_and_swap and // __sync_lock_test_and_set have an implied isync. - uint64_t l_count = __sync_val_compare_and_swap(&(i_mutex->iv_val),0,1); + crit_assert(!i_mutex->iv_recursive); - if(unlikely(l_count != 0)) + uint64_t l_lockStatus = __sync_val_compare_and_swap(&(i_mutex->iv_val),0,1); + + if(unlikely(l_lockStatus != 0)) { - if (likely(l_count != 2)) + if(likely(l_lockStatus != 2)) { - l_count = __sync_lock_test_and_set(&(i_mutex->iv_val), 2); + l_lockStatus = __sync_lock_test_and_set(&(i_mutex->iv_val), 2); } - while( l_count != 0 ) + while(l_lockStatus != 0) { - futex_wait( &(i_mutex->iv_val), 2); - l_count = __sync_lock_test_and_set(&(i_mutex->iv_val),2); + futex_wait(&(i_mutex->iv_val), 2); + l_lockStatus = __sync_lock_test_and_set(&(i_mutex->iv_val), 2); // if more than one task gets out - one continues while // the rest get blocked again. } @@ -174,8 +215,11 @@ void mutex_unlock(mutex_t * i_mutex) // and futex_wake pair will appear globally ordered due to the // context synchronizing nature of the 'sc' instruction. - uint64_t l_count = __sync_fetch_and_sub(&(i_mutex->iv_val),1); - if(unlikely(2 <= l_count)) + crit_assert(!i_mutex->iv_recursive); + + uint64_t l_lockStatus = __sync_fetch_and_sub(&(i_mutex->iv_val), 1); + + if(unlikely(2 <= l_lockStatus)) { i_mutex->iv_val = 0; futex_wake(&(i_mutex->iv_val), 1); // wake one task @@ -184,6 +228,68 @@ void mutex_unlock(mutex_t * i_mutex) return; } +void recursive_mutex_lock(mutex_t * i_mutex) +{ + crit_assert(i_mutex->iv_recursive); + + uint64_t l_lockStatus = __sync_val_compare_and_swap(&(i_mutex->iv_val),0,1); + do + { + if(unlikely(l_lockStatus != 0)) + { + if (task_gettid() == i_mutex->iv_owner) + { + ++i_mutex->iv_ownerLockCount; + break; + } + + if (likely(l_lockStatus != 2)) + { + l_lockStatus = __sync_lock_test_and_set(&(i_mutex->iv_val), 2); + } + + while( l_lockStatus != 0 ) + { + futex_wait( &(i_mutex->iv_val), 2); + l_lockStatus = __sync_lock_test_and_set(&(i_mutex->iv_val),2); + // if more than one task gets out - one continues while + // the rest get blocked again. + } + } + + i_mutex->iv_owner = task_gettid(); + ++i_mutex->iv_ownerLockCount; + + } while(0); + + return; +} + +void recursive_mutex_unlock(mutex_t * i_mutex) +{ + crit_assert(i_mutex->iv_recursive); + + uint64_t l_lockStatus = 0; + if (i_mutex->iv_ownerLockCount <= 1) + { + i_mutex->iv_ownerLockCount = 0; + i_mutex->iv_owner = 0; + l_lockStatus = __sync_fetch_and_sub(&(i_mutex->iv_val),1); + + if(unlikely(2 <= l_lockStatus)) + { + i_mutex->iv_val = 0; + futex_wake(&(i_mutex->iv_val), 1); // wake one task + } + } + else + { + --i_mutex->iv_ownerLockCount; + } + + return; +} + void sync_cond_init(sync_cond_t * i_cond) { i_cond->mutex = NULL; diff --git a/src/usr/targeting/common/xmltohb/xmltohb.pl b/src/usr/targeting/common/xmltohb/xmltohb.pl index c3f8c76f0..e92bf598e 100755 --- a/src/usr/targeting/common/xmltohb/xmltohb.pl +++ b/src/usr/targeting/common/xmltohb/xmltohb.pl @@ -4450,6 +4450,17 @@ sub unhexify { } ################################################################################ +# Pack mutex +################################################################################ + +sub packMutex { + my $length = 24; + my $binaryData .= pack ("C".$length); + + return $binaryData; +} + +################################################################################ # Pack 8 byte value into a buffer using configured endianness ################################################################################ @@ -4868,7 +4879,7 @@ sub simpleTypeProperties { $typesHoH{"uint32_t"} = { supportsArray => 1, canBeHex => 1, complexTypeSupport => 1, typeName => "uint32_t" , bytes => 4, bits => 32, default => \&defaultZero , alignment => 1, specialPolicies =>\&null, packfmt =>\&pack4byte}; $typesHoH{"uint64_t"} = { supportsArray => 1, canBeHex => 1, complexTypeSupport => 1, typeName => "uint64_t" , bytes => 8, bits => 64, default => \&defaultZero , alignment => 1, specialPolicies =>\&null, packfmt =>\&pack8byte}; $typesHoH{"enumeration"} = { supportsArray => 1, canBeHex => 1, complexTypeSupport => 0, typeName => "XMLTOHB_USE_PARENT_ATTR_ID" , bytes => 0, bits => 0 , default => \&defaultEnum , alignment => 1, specialPolicies =>\&null, packfmt => "packEnumeration"}; - $typesHoH{"hbmutex"} = { supportsArray => 1, canBeHex => 1, complexTypeSupport => 0, typeName => "mutex_t*" , bytes => 8, bits => 64, default => \&defaultZero , alignment => 8, specialPolicies =>\&enforceHbMutex, packfmt =>\&pack8byte}; + $typesHoH{"hbmutex"} = { supportsArray => 1, canBeHex => 1, complexTypeSupport => 0, typeName => "mutex_t*" , bytes => 24, bits => 192, default => \&defaultZero , alignment => 8, specialPolicies =>\&enforceHbMutex, packfmt =>\&packMutex}; $typesHoH{"Target_t"} = { supportsArray => 0, canBeHex => 1, complexTypeSupport => 0, typeName => "TARGETING::Target*" , bytes => 8, bits => 64, default => \&defaultZero , alignment => 8, specialPolicies =>\&null, packfmt =>\&pack8byte}; $typesHoH{"fspmutex"} = { supportsArray => 1, canBeHex => 1, complexTypeSupport => 0, typeName => "util::Mutex*" , bytes => 8, bits => 64, default => \&defaultZero , alignment => 8, specialPolicies =>\&enforceFspMutex, packfmt =>\&pack8byte}; @@ -6367,7 +6378,8 @@ sub generateTargetingImage { #skip the present target next; } -# print Dumper($allAttributes); + + #print Dumper($allAttributes); # Update hash with any per-instance overrides, but only if that # attribute has already been defined diff --git a/src/usr/testcore/lib/makefile b/src/usr/testcore/lib/makefile index a688b2a8f..ce80dc0df 100644 --- a/src/usr/testcore/lib/makefile +++ b/src/usr/testcore/lib/makefile @@ -5,7 +5,7 @@ # # OpenPOWER HostBoot Project # -# Contributors Listed Below - COPYRIGHT 2011,2016 +# Contributors Listed Below - COPYRIGHT 2011,2018 # [+] International Business Machines Corp. # # @@ -28,7 +28,7 @@ MODULE = testsyslib #@TODO-RTC:151185-Turn enable all test cases #TESTS = *.H TESTS = stltest.H - +TESTS += synctest.H SUBDIRS += runtime.d diff --git a/src/usr/testcore/lib/synctest.H b/src/usr/testcore/lib/synctest.H index c75e97db9..2662f020d 100644 --- a/src/usr/testcore/lib/synctest.H +++ b/src/usr/testcore/lib/synctest.H @@ -5,7 +5,9 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* COPYRIGHT International Business Machines Corp. 2011,2014 */ +/* Contributors Listed Below - COPYRIGHT 2011,2018 */ +/* [+] International Business Machines Corp. */ +/* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ @@ -41,6 +43,49 @@ class SyncTest: public CxxTest::TestSuite { public: + void testRecursiveMutex(void) + { + int l_status = TASK_STATUS_EXITED_CLEAN; + // Note: These test cases are considered to have passed if no + // deadlocks occur. + // Case 1: Single thread locks recursive mutex three times in a row. + { + recursive_mutex_init(&mutex); + + l_status = TASK_STATUS_EXITED_CLEAN; + tid_t l_task1 = task_create(entry_func, this); + + if ((l_task1 != task_wait_tid(l_task1, &l_status, nullptr)) || + (l_status == TASK_STATUS_EXITED_CLEAN)) + { + TS_INFO("Thread ended."); + } + } + + // Case 2: One thread grabs recursive mutex and another attempts to + // grab it (hangs) and then the first thread grabs it again + // without issue. + { + recursive_mutex_init(&mutex); + + l_status = TASK_STATUS_EXITED_CLEAN; + tid_t l_task1 = task_create(entry_func, this); + tid_t l_task2 = task_create(entry_func, this); + + if ((l_task2 != task_wait_tid(l_task2, &l_status, nullptr)) || + (l_status == TASK_STATUS_EXITED_CLEAN)) + { + TS_INFO("Thread ended."); + } + + if ((l_task1 != task_wait_tid(l_task1, &l_status, nullptr)) || + (l_status == TASK_STATUS_EXITED_CLEAN)) + { + TS_INFO("Thread ended."); + } + + } + } void testMutex() { @@ -142,10 +187,6 @@ class SyncTest: public CxxTest::TestSuite // test is success if it completes w/o hang } - - - - private: enum @@ -162,6 +203,33 @@ class SyncTest: public CxxTest::TestSuite size_t counter; + static void* entry_func(void* i_p) + { + SyncTest* my = (SyncTest*) i_p; + mutex_t* myMutex = &(my->mutex); + + // Call the recursive function. + recursive_func(myMutex, 2); + + return nullptr; + } + + static void recursive_func(mutex_t* mutex, int val) + { + TS_INFO("Attempting to enter Critical Section."); + recursive_mutex_lock(mutex); + + TS_INFO("Accessing Critical Section."); + nanosleep(0, TEN_CTX_SWITCHES_NS); + + if (val > 0) + { + recursive_func(mutex, val-1); + } + + TS_INFO("Exiting Critical Section."); + recursive_mutex_unlock(mutex); + } static void* func1(void * i_p) { |