summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/lib/sync.C83
-rw-r--r--src/runtime/rt_sync.C8
-rw-r--r--src/usr/targeting/common/targetservice.C15
-rwxr-xr-xsrc/usr/targeting/common/xmltohb/attribute_types_hb.xml12
-rw-r--r--src/usr/targeting/common/xmltohb/target_types_hb.xml3
-rwxr-xr-xsrc/usr/targeting/common/xmltohb/xmltohb.pl89
-rwxr-xr-xsrc/usr/targeting/targetservicestart.C47
-rw-r--r--src/usr/targeting/test/testtargeting.H176
-rw-r--r--src/usr/testcore/lib/makefile2
-rw-r--r--src/usr/testcore/lib/synctest.H15
10 files changed, 394 insertions, 56 deletions
diff --git a/src/lib/sync.C b/src/lib/sync.C
index c318856a1..210790074 100644
--- a/src/lib/sync.C
+++ b/src/lib/sync.C
@@ -31,6 +31,10 @@
using namespace Systemcalls;
+// The size of mutexes are hardcoded in xmltohb.pl script and must be that
+// size.
+const int MUTEX_SIZE = 24;
+
//-----------------------------------------------------------------------------
int futex_wait(uint64_t * i_addr, uint64_t i_val)
@@ -181,6 +185,16 @@ void mutex_lock(mutex_t * i_mutex)
// __sync_lock_test_and_set have an implied isync.
crit_assert(!i_mutex->iv_recursive);
+ static_assert(sizeof(mutex_t) == MUTEX_SIZE,
+ "mutex_t must be size of 24 bytes");
+
+ // The mutex is only allowed to take on three values: 0, 1, or 2.
+ // 0: Lock is not held.
+ // 1: Lock is held by a task with no contention.
+ // 2: Lock is held by a task and there is contention.
+ // Any other values indicates that outside influences have messed with the
+ // mutex and we shouldn't continue.
+ crit_assert(i_mutex->iv_val <= 2);
uint64_t l_lockStatus = __sync_val_compare_and_swap(&(i_mutex->iv_val),0,1);
@@ -216,11 +230,22 @@ void mutex_unlock(mutex_t * i_mutex)
// context synchronizing nature of the 'sc' instruction.
crit_assert(!i_mutex->iv_recursive);
+ static_assert(sizeof(mutex_t) == MUTEX_SIZE,
+ "mutex_t must be size of 24 bytes");
+
+ // The mutex is only allowed to take on three values: 0, 1, or 2.
+ // 0: Lock is not held.
+ // 1: Lock is held by a task with no contention.
+ // 2: Lock is held by a task and there is contention.
+ // Any other values indicates that outside influences have messed with the
+ // mutex and we shouldn't continue.
+ crit_assert(i_mutex->iv_val <= 2);
uint64_t l_lockStatus = __sync_fetch_and_sub(&(i_mutex->iv_val), 1);
- if(unlikely(2 <= l_lockStatus))
+ if(unlikely(l_lockStatus == 2))
{
+ // Fully release the lock and let another task grab it.
i_mutex->iv_val = 0;
futex_wake(&(i_mutex->iv_val), 1); // wake one task
}
@@ -231,23 +256,57 @@ void mutex_unlock(mutex_t * i_mutex)
void recursive_mutex_lock(mutex_t * i_mutex)
{
crit_assert(i_mutex->iv_recursive);
-
+ static_assert(sizeof(mutex_t) == MUTEX_SIZE,
+ "mutex_t must be size of 24 bytes");
+
+ // The mutex is only allowed to take on three values: 0, 1, or 2.
+ // 0: Lock is not held.
+ // 1: Lock is held by a task with no contention.
+ // 2: Lock is held by a task and there is contention.
+ // Any other values indicates that outside influences have messed with the
+ // mutex and we shouldn't continue.
+ crit_assert(i_mutex->iv_val <= 2);
+
+ // Check the contents of the mutex's iv_val and if it's equal to 0, then
+ // assign it the value of 1. l_lockStatus is initialized with the value of
+ // iv_val prior to the assignment.
uint64_t l_lockStatus = __sync_val_compare_and_swap(&(i_mutex->iv_val),0,1);
+
do
{
if(unlikely(l_lockStatus != 0))
{
+ // There may be contention for the lock. Since this is a recursive
+ // mutex we first need to check if the owner has called for the lock
+ // again.
if (task_gettid() == i_mutex->iv_owner)
{
+ // The owner called for the lock. There isn't contention at this
+ // point and so subsequent calls to this function will still
+ // initialize l_lockStatus to 1.
+ //
+ // Increment the owner's lock count to keep track of how much
+ // unwinding will be necessary but the lock can be safely
+ // released.
++i_mutex->iv_ownerLockCount;
+
+ // Return now so that the owner doesn't set the lock to be
+ // contended and enter the waiting queue, thus causing a
+ // deadlock.
break;
}
+ // By reaching this point there is certainly contention for the
+ // lock. Ensure that the lock status reflects this if it doesn't
+ // already.
if (likely(l_lockStatus != 2))
{
+ // mutex's iv_val will be set to 2 and the previous value will
+ // be assigned to l_lockStatus.
l_lockStatus = __sync_lock_test_and_set(&(i_mutex->iv_val), 2);
}
+ // Send caller to the wait queue.
while( l_lockStatus != 0 )
{
futex_wait( &(i_mutex->iv_val), 2);
@@ -257,6 +316,7 @@ void recursive_mutex_lock(mutex_t * i_mutex)
}
}
+ // Set the new owner of the lock and increment the lock count.
i_mutex->iv_owner = task_gettid();
++i_mutex->iv_ownerLockCount;
@@ -268,22 +328,39 @@ void recursive_mutex_lock(mutex_t * i_mutex)
void recursive_mutex_unlock(mutex_t * i_mutex)
{
crit_assert(i_mutex->iv_recursive);
+ static_assert(sizeof(mutex_t) == MUTEX_SIZE,
+ "mutex_t must be size of 24 bytes");
+
+ // The mutex is only allowed to take on three values: 0, 1, or 2.
+ // 0: Lock is not held.
+ // 1: Lock is held by a task with no contention.
+ // 2: Lock is held by a task and there is contention.
+ // Any other values indicates that outside influences have messed with the
+ // mutex and we shouldn't continue.
+ crit_assert(i_mutex->iv_val <= 2);
uint64_t l_lockStatus = 0;
+
if (i_mutex->iv_ownerLockCount <= 1)
{
+ // Owner is finished with the lock. So reset owner variables of mutex.
i_mutex->iv_ownerLockCount = 0;
i_mutex->iv_owner = 0;
+
+ // Decrements iv_val by 1 and assigns the value prior to the operation
+ // to l_lockStatus.
l_lockStatus = __sync_fetch_and_sub(&(i_mutex->iv_val),1);
- if(unlikely(2 <= l_lockStatus))
+ if(unlikely(l_lockStatus == 2))
{
+ // Fully release the lock to allow the next task to grab it.
i_mutex->iv_val = 0;
futex_wake(&(i_mutex->iv_val), 1); // wake one task
}
}
else
{
+ // Unwind the recursive lock one step.
--i_mutex->iv_ownerLockCount;
}
diff --git a/src/runtime/rt_sync.C b/src/runtime/rt_sync.C
index 86d1b426b..0b7cc027e 100644
--- a/src/runtime/rt_sync.C
+++ b/src/runtime/rt_sync.C
@@ -5,7 +5,9 @@
/* */
/* OpenPOWER HostBoot Project */
/* */
-/* COPYRIGHT International Business Machines Corp. 2013,2014 */
+/* Contributors Listed Below - COPYRIGHT 2013,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. */
@@ -22,6 +24,10 @@
/* IBM_PROLOG_END_TAG */
#include <sys/sync.h>
+void recursive_mutex_init(mutex_t*) {};
+void recursive_mutex_destroy(mutex_t*) {};
+void recursive_mutex_lock(mutex_t*) {};
+void recursive_mutex_unlock(mutex_t*) {};
void mutex_init(mutex_t*) {};
void mutex_destroy(mutex_t*) {};
void mutex_lock(mutex_t*) {};
diff --git a/src/usr/targeting/common/targetservice.C b/src/usr/targeting/common/targetservice.C
index c7f002f87..e3c3dde92 100644
--- a/src/usr/targeting/common/targetservice.C
+++ b/src/usr/targeting/common/targetservice.C
@@ -1793,14 +1793,23 @@ uint32_t TargetService::resetMutexAttributes(const Target* i_pTarget)
for ( uint32_t l_attrIndex = 0; l_attrIndex < l_attrCount; l_attrIndex++)
{
const ATTRIBUTE_ID l_attrId = l_pAttrIds[l_attrIndex];
- for( const auto mutexId : hbMutexAttrIds)
+ for( const auto mutex : hbMutexAttrIds)
{
- if(l_attrId == mutexId)
+ if(l_attrId == mutex.id)
{
mutex_t* l_mutex;
if(i_pTarget->_tryGetHbMutexAttr(l_attrId, l_mutex))
{
- mutex_init(l_mutex);
+#ifdef __HOSTBOOT_MODULE
+ if (mutex.isRecursive)
+ {
+ recursive_mutex_init(l_mutex);
+ }
+ else
+#endif
+ {
+ mutex_init(l_mutex);
+ }
l_numberMutexAttrsReset++;
}
else
diff --git a/src/usr/targeting/common/xmltohb/attribute_types_hb.xml b/src/usr/targeting/common/xmltohb/attribute_types_hb.xml
index 5f88bbce1..1c574ebaa 100755
--- a/src/usr/targeting/common/xmltohb/attribute_types_hb.xml
+++ b/src/usr/targeting/common/xmltohb/attribute_types_hb.xml
@@ -330,6 +330,18 @@
</attribute>
<attribute>
+ <id>HB_RECURSIVE_MUTEX_TEST_LOCK</id>
+ <description>Host boot recursive mutex for testing</description>
+ <simpleType>
+ <hbrecursivemutex/>
+ </simpleType>
+ <persistency>volatile-zeroed</persistency>
+ <readable/>
+ <writeable/>
+ <hbOnly/>
+ </attribute>
+
+ <attribute>
<id>HB_RSV_MEM_NEXT_SECTION</id>
<description>
The next HB reserved memory section available to assign
diff --git a/src/usr/targeting/common/xmltohb/target_types_hb.xml b/src/usr/targeting/common/xmltohb/target_types_hb.xml
index 7ac29598f..eaf0ca0b6 100644
--- a/src/usr/targeting/common/xmltohb/target_types_hb.xml
+++ b/src/usr/targeting/common/xmltohb/target_types_hb.xml
@@ -260,6 +260,9 @@
<id>HB_MUTEX_TEST_LOCK</id>
</attribute>
<attribute>
+ <id>HB_RECURSIVE_MUTEX_TEST_LOCK</id>
+ </attribute>
+ <attribute>
<id>HB_RSV_MEM_NEXT_SECTION</id>
</attribute>
<attribute>
diff --git a/src/usr/targeting/common/xmltohb/xmltohb.pl b/src/usr/targeting/common/xmltohb/xmltohb.pl
index e92bf598e..29727c403 100755
--- a/src/usr/targeting/common/xmltohb/xmltohb.pl
+++ b/src/usr/targeting/common/xmltohb/xmltohb.pl
@@ -2330,7 +2330,7 @@ namespace TARGETING
* Array defining all attribute ids found that are of type hbMutex.
* This file is autogenerated and should not be altered.
*/
-const uint32_t hbMutexAttrIds[] = {
+const struct {uint32_t id; bool isRecursive;} hbMutexAttrIds[] = {
VERBATIM
my @mutexAttrIds;
@@ -2340,22 +2340,30 @@ VERBATIM
#check if hbmutex tag is present
#check that attr is readable/writeable
if( (exists $attribute->{simpleType})
- && (exists $attribute->{simpleType}->{hbmutex})
+ && (exists $attribute->{simpleType}->{hbmutex}
+ || exists $attribute->{simpleType}->{hbrecursivemutex})
&& (exists $attribute->{readable})
&& (exists $attribute->{writeable}))
{
- push @mutexAttrIds, $attribute->{id};
+ my $recursiveType = "false";
+ if (exists $attribute->{simpleType}->{hbrecursivemutex})
+ {
+ $recursiveType = "true";
+ }
+
+ push @mutexAttrIds, ([ $attribute->{id}, $recursiveType ]);
}
}
# variables that can be used for writing the enums to the file
my $attrId;
my $hexVal;
+ my $recursiveVal;
# Format below intentionally > 80 chars for clarity
format ATTRMUTEXFORMAT =
- @>>>>>>>>>>
- $hexVal .","
+ @>>>>>>>>>>>>>>>>>>>>
+ "{ ". $hexVal .", ". $recursiveVal ." },"
.
select($outFile);
$~ = 'ATTRMUTEXFORMAT';
@@ -2368,7 +2376,9 @@ VERBATIM
$attrId = $enumerator->{name};
foreach my $mutexAttrId (@mutexAttrIds)
{
- if( $mutexAttrId eq $attrId )
+ $recursiveVal = $mutexAttrId->[1];
+
+ if( $mutexAttrId->[0] eq $attrId )
{
write;
last;
@@ -2494,7 +2504,8 @@ sub writeTraitFileTraits {
# Mark the attribute as being a host boot mutex or non-host boot mutex
if( (exists $attribute->{simpleType})
- && (exists $attribute->{simpleType}->{hbmutex}) )
+ && (exists $attribute->{simpleType}->{hbmutex}
+ || exists $attribute->{simpleType}->{hbrecursivemutex}) )
{
$traits .= " hbMutex,";
}
@@ -2751,6 +2762,7 @@ sub writeAttrErrlCFile {
!(exists $attribute->{writeable}) || # read-only attributes
(exists $attribute->{simpleType} && (
(exists $attribute->{simpleType}->{hbmutex}) ||
+ (exists $attribute->{simpleType}->{hbrecrusivemutex}) ||
(exists $attribute->{simpleType}->{fspmutex}))) # mutex attributes
) {
print $outFile " case (ATTR_",$attribute->{id},"): { break; }\n";
@@ -2923,6 +2935,7 @@ sub writeAttrErrlCFile {
!(exists $attribute->{writeable}) || # read-only attributes
(exists $attribute->{simpleType} && (
(exists $attribute->{simpleType}->{hbmutex}) ||
+ (exists $attribute->{simpleType}->{hbrecursivemutex}) ||
(exists $attribute->{simpleType}->{fspmutex}))) # mutex attributes
) {
next;
@@ -3040,6 +3053,7 @@ sub writeAttrErrlHFile {
!(exists $attribute->{writeable}) || # read-only attributes
(exists $attribute->{simpleType} && (
(exists $attribute->{simpleType}->{hbmutex}) ||
+ (exists $attribute->{simpleType}->{hbrecursivemutex}) ||
(exists $attribute->{simpleType}->{fspmutex}))) # mutex attributes
) {
print $outFile " //not readable\n";
@@ -3069,7 +3083,7 @@ sub writeAttrErrlHFile {
}
}
# makes no sense to dump mutex attributes, so skipping
- elsif(exists $attribute->{simpleType} && (exists $attribute->{simpleType}->{hbmutex}) ) {
+ elsif(exists $attribute->{simpleType} && (exists $attribute->{simpleType}->{hbmutex}) || (exists $attribute->{simpleType}->{hbrecursivemutex}) ) {
print $outFile " //Mutex attributes - skipping\n";
}
# makes no sense to dump fsp mutex attributes, so skipping
@@ -4880,6 +4894,7 @@ sub simpleTypeProperties {
$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 => 24, bits => 192, default => \&defaultZero , alignment => 8, specialPolicies =>\&enforceHbMutex, packfmt =>\&packMutex};
+ $typesHoH{"hbrecursivemutex"} = { 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};
@@ -5038,33 +5053,45 @@ sub mergeComplexAttributeFields {
# the two fields.
my $mergedFields = dclone $currentAttrFields;
- # Iterate over the fields of $newField and look for their corresponding id
- # in $currentAttrFields. All the fields in $newField should exist in
- # $currentAttrFields, if not, then there is a problem
- foreach my $newField (@{$newAttrFields->{default}->{field}})
+ # If the merged field's (an alias for the current attribute fields)
+ # hash is empty, then just assign it the new attribute field's value -
+ # no merge necessary
+ if ($mergedFields->{default} == 0)
{
- my $foundField = 0;
-
- # Iterate over $mergedFields (really $currentAttrFields) looking
- # for the $newField of $newAttrFields
- foreach my $currentField (@{$mergedFields->{default}->{field}})
+ $mergedFields->{default} = $newAttrFields->{default};
+ }
+ # Only proceed if both attribute pairs have values to merge
+ elsif ($newAttrFields->{default} != 0)
+ {
+ # Iterate over the fields of $newField and look for their corresponding
+ # id in $currentAttrFields. All the fields in $newField should exist in
+ # $currentAttrFields, if not, then there is a problem
+ foreach my $newField (@{$newAttrFields->{default}->{field}})
{
- # Found the field in question
- if ($currentField->{id} eq $newField->{id})
+ my $foundField = 0;
+
+ # Iterate over $mergedFields (really $currentAttrFields) looking
+ # for the $newField of $newAttrFields
+ foreach my $currentField (@{$mergedFields->{default}->{field}})
{
- # Merge in the new value from $newField
- $currentField->{value} = $newField->{value};
- $foundField = 1;
- last;
- }
- } # end foreach my $currentField ...
+ # Found the field in question
+ if ($currentField->{id} eq $newField->{id})
+ {
+ # Merge in the new value from $newField
+ $currentField->{value} = $newField->{value};
+ $foundField = 1;
+ last;
+ }
+ } # end foreach my $currentField ...
- # A field was not found ... halt execution
- if ($foundField == 0)
- {
- croak("Field $newField is not supported.")
- }
- } # end foreach my $newField ...
+ # A field was not found ... halt execution
+ if ($foundField == 0)
+ {
+ croak("Field $newField is not supported.")
+ }
+ } # end foreach my $newField ...
+ }
+ # else new attribute fields is empty - nothing to merge
return $mergedFields;
}
diff --git a/src/usr/targeting/targetservicestart.C b/src/usr/targeting/targetservicestart.C
index 20dce1e48..795bd4072 100755
--- a/src/usr/targeting/targetservicestart.C
+++ b/src/usr/targeting/targetservicestart.C
@@ -221,29 +221,40 @@ static void initTargeting(errlHndl_t& io_pError)
}
}
- initializeAttributes(l_targetService, l_isMpipl, l_isIstepMode, l_scratch);
- //Ensure all mutex attributes are reset on MPIPL
+ initializeAttributes(l_targetService, l_isMpipl, l_isIstepMode,
+ l_scratch);
+
+
+ uint32_t l_peerTargetsAdjusted = 0;
+ uint32_t l_numberMutexAttrsReset = 0;
+
if(l_isMpipl)
{
- // updatePeerTargets will write to read-only attribute pages
- // to get around vmm fails we need to allow writes to readonly
- // memory for the duration of this loop (unlocking is done above)
- uint32_t l_peerTargetsAdjusted = 0;
- uint32_t l_numberMutexAttrsReset = 0;
+
+
for( auto targ_iter = l_targetService.begin();
targ_iter != l_targetService.end();
targ_iter++)
{
const Target* l_pTarget = *targ_iter;
- // Check if there any mutex attributes we need to reset on this target
- l_numberMutexAttrsReset += l_targetService.resetMutexAttributes(l_pTarget);
+
+ // Ensure all mutex attributes are setup correctly for MPIPL
+ // Check if there any mutex attributes
+ // we need to reset on this target
+ l_numberMutexAttrsReset +=
+ l_targetService.resetMutexAttributes(l_pTarget);
// Update any peer target addresses if necessary
+ // updatePeerTargets will write to read-only attribute pages.
+ // To get around vmm fails we need to allow writes to readonly
+ // memory for the duration of this loop (unlocking is done
+ // above).
if(l_targetService.updatePeerTarget(l_pTarget))
{
l_peerTargetsAdjusted++;
}
}
+
// Now that the loop is complete we can re-apply
// the read only permissions to the read only attr pages
l_targetService.modifyReadOnlyPagePermissions(false);
@@ -252,6 +263,24 @@ static void initTargeting(errlHndl_t& io_pError)
TARG_INF("Number of mutex attributes reset: %d",
l_numberMutexAttrsReset);
}
+ else
+ {
+
+ for( auto targ_iter = l_targetService.begin();
+ targ_iter != l_targetService.end();
+ targ_iter++)
+ {
+ const Target* l_pTarget = *targ_iter;
+
+ // Ensure all mutex attributes are setup correctly for IPL
+ // Recursive Mutexes need to be setup since they are defaulted
+ // to all zero.
+ // Check if there any mutex attributes
+ // we need to reset on this target
+ l_targetService.resetMutexAttributes(l_pTarget);
+ }
+ }
+
checkProcessorTargeting(l_targetService);
diff --git a/src/usr/targeting/test/testtargeting.H b/src/usr/targeting/test/testtargeting.H
index c87d599e9..16d2fc99d 100644
--- a/src/usr/targeting/test/testtargeting.H
+++ b/src/usr/targeting/test/testtargeting.H
@@ -90,6 +90,63 @@ void* funcTestMutex(void* i_pData)
return NULL;
}
+/**
+ * @brief Function which attempts to recursively access a critical section with
+ * a recursive mutex.
+ *
+ * @param[in] i_pData Pointer to mutex pointer/value pointer structure
+ *
+ * @return nullptr
+ */
+void* funcTestRecursiveMutex(void* i_pData)
+{
+ MutexTestData_t* l_pData = static_cast<MutexTestData_t*>(i_pData);
+
+ // The parent thread holds the lock initially. So the child thread will be
+ // blocked when it attempts to access the critical section.
+ TS_INFO("Child Thread: Attempting to access the Critical Section");
+ recursive_mutex_lock(l_pData->pMutex);
+
+ TS_INFO("Child Thread: Entering the Critical Section");
+ if (*(l_pData->pVar) > 0)
+ {
+ *(l_pData->pVar) = 0;
+ funcTestRecursiveMutex(l_pData);
+ }
+
+ TS_INFO("Child Thread: Leaving the Critical Section");
+ recursive_mutex_unlock(l_pData->pMutex);
+ return nullptr;
+}
+
+/**
+ * @brief Function that serves as an entry point to the recursive function
+ * it calls. This is so that the barrier_wait calls work without the
+ * recursion causing a deadlock.
+ *
+ * @param[in] i_pData Pointer to mutex pointer/value pointer structure
+ *
+ * @return nullptr
+ */
+void* funcTestRecursiveMutexEntry(void* i_pData)
+{
+ MutexTestData_t* l_pData = static_cast<MutexTestData_t*>(i_pData);
+
+ // Signal to the parent thread that the child thread is about to access the
+ // lock which is held by it. That way the main thread can verify that
+ // this thread hasn't started recursion yet.
+ barrier_wait(l_pData->pBarrier);
+
+ funcTestRecursiveMutex(l_pData);
+
+ // Wake the parent thread so that it can verify that this thread accessed
+ // the protected value which proves that the child thread got
+ // the lock recursively without a deadlock occuring.
+ barrier_wait(l_pData->pBarrier);
+
+ return nullptr;
+}
+
class TargetingTestSuite : public CxxTest::TestSuite
{
public:
@@ -97,6 +154,122 @@ class TargetingTestSuite : public CxxTest::TestSuite
/**
* @brief Test Hostboot specific mutex attribute support
*/
+ void testHbRecursiveMutexAttr()
+ {
+ TS_TRACE(ENTER_MRK "testHbRecursiveMutexAttr" );
+
+ using namespace TARGETING;
+
+ do {
+
+ // Get a reference to the target service
+ TargetService& l_targetService = targetService();
+
+ // Get the system target containing the test mutex
+ TARGETING::Target* l_pTarget = nullptr;
+ (void) l_targetService.getTopLevelTarget(l_pTarget);
+ if (l_pTarget == nullptr)
+ {
+ TS_FAIL("Top level target handle is NULL");
+ break;
+ }
+
+ // Get the mutex attribute (actually a mutex_t* which points to
+ // a mutex)
+ HB_RECURSIVE_MUTEX_TEST_LOCK_ATTR l_pLock =
+ l_pTarget->getHbMutexAttr
+ <TARGETING::ATTR_HB_RECURSIVE_MUTEX_TEST_LOCK>();
+
+ // Verify the recursive mutex was initialized correctly
+ mutex_t* l_recursiveMutex = reinterpret_cast<mutex_t*>(l_pLock);
+
+ if ((l_recursiveMutex->iv_val != 0)
+ || (l_recursiveMutex->iv_ownerLockCount != 0)
+ || (l_recursiveMutex->iv_owner != 0)
+ || (l_recursiveMutex->iv_recursive != true))
+ {
+ TS_FAIL("Mutex attribute must be initialized as recursive.");
+ break;
+ }
+
+ // Try to get the attribute, and ensure it's the same
+ HB_RECURSIVE_MUTEX_TEST_LOCK_ATTR l_pLockTry = nullptr;
+ if(l_pTarget->tryGetHbMutexAttr
+ <TARGETING::ATTR_HB_RECURSIVE_MUTEX_TEST_LOCK>(l_pLockTry))
+ {
+ if(l_pLockTry != l_pLock)
+ {
+ TS_FAIL("Mutex attributes should match, but dont. "
+ "l_pLockTry = %p, l_pLock = %p",l_pLockTry,
+ l_pLock);
+ break;
+ }
+ }
+ else
+ {
+ TS_FAIL("Mutex attribute tryGet failed, even though it exists");
+ break;
+ }
+
+ // Create a structure holding pointers to
+ // the mutex and a protected value
+ volatile uint32_t l_var = 1;
+ TS_INFO("Parent Thread: Acquiring recursive lock");
+ (void)recursive_mutex_lock(l_pLock);
+ barrier_t l_barrier;
+ (void)barrier_init(&l_barrier, 2);
+ MutexTestData_t l_mutexTestData = { l_pLock, &l_barrier, &l_var };
+
+ // Spawn off a function which tries to write the protected value to
+ // something unexpected. If the mutex is working, the for loop will
+ // always poll the expected value.
+ TS_INFO("Parent Thread: Creating Child thread");
+ task_create(funcTestRecursiveMutexEntry,
+ static_cast<void*>(&l_mutexTestData));
+
+ // Guarantee the child process runs and blocks on the mutex prior to
+ // modifying the protected value. isync to ensure the processor doesn't
+ // speculatively perform the comparison prior to the sleep completing
+ TS_INFO("Parent Thread: Waiting for Child to attempt to grab the lock");
+ barrier_wait(&l_barrier);
+ nanosleep(0,TEN_CTX_SWITCHES_NS); isync();
+
+ if(l_var != 1)
+ {
+ TS_FAIL("Protected value must be 1, was %d instead",l_var);
+ break;
+ }
+
+ // Now unlock the mutex, allowing the other thread to overwrite the
+ // protected value; which should happen within 100,000 reads of the
+ // var. This will confirm the other thread was actively trying to
+ // write the controlled value
+ TS_INFO(
+ "Parent Thread: Releasing lock to Child to test recursive mutex.");
+ (void)recursive_mutex_unlock(l_pLock);
+
+ // Guarantee the child process acquires the mutex and modifies the
+ // protected value.
+ TS_INFO("Parent Thread: Waiting for Child to modify the value");
+ barrier_wait(&l_barrier);
+
+ if(l_var != 0)
+ {
+ TS_FAIL("Protected value must now be 0, was %d instead",l_var);
+ break;
+ }
+
+ TS_INFO("Parent Thread: Child completed successfully.");
+ barrier_destroy(&l_barrier);
+
+ } while(0);
+
+ TS_TRACE(EXIT_MRK "testHbRecursiveMutexAttr");
+ }
+
+ /**
+ * @brief Test Hostboot specific mutex attribute support
+ */
void testHbMutexAttr()
{
TS_TRACE(ENTER_MRK "testHbMutexAttr" );
@@ -149,7 +322,8 @@ class TargetingTestSuite : public CxxTest::TestSuite
break;
}
- // Create a structue holding pointers to the mutex and a protected value
+ // Create a structure holding pointers to
+ // the mutex and a protected value
volatile uint32_t l_var = 0;
(void)mutex_lock(l_pLock);
barrier_t l_barrier;
diff --git a/src/usr/testcore/lib/makefile b/src/usr/testcore/lib/makefile
index 9bf180042..ce80dc0df 100644
--- a/src/usr/testcore/lib/makefile
+++ b/src/usr/testcore/lib/makefile
@@ -28,7 +28,7 @@ MODULE = testsyslib
#@TODO-RTC:151185-Turn enable all test cases
#TESTS = *.H
TESTS = stltest.H
-#TESTS += synctest.H
+TESTS += synctest.H
SUBDIRS += runtime.d
diff --git a/src/usr/testcore/lib/synctest.H b/src/usr/testcore/lib/synctest.H
index 2662f020d..ea71dac53 100644
--- a/src/usr/testcore/lib/synctest.H
+++ b/src/usr/testcore/lib/synctest.H
@@ -50,10 +50,11 @@ class SyncTest: public CxxTest::TestSuite
// deadlocks occur.
// Case 1: Single thread locks recursive mutex three times in a row.
{
- recursive_mutex_init(&mutex);
+ mutex_t recursive_mutex;
+ recursive_mutex_init(&recursive_mutex);
l_status = TASK_STATUS_EXITED_CLEAN;
- tid_t l_task1 = task_create(entry_func, this);
+ tid_t l_task1 = task_create(entry_func, &recursive_mutex);
if ((l_task1 != task_wait_tid(l_task1, &l_status, nullptr)) ||
(l_status == TASK_STATUS_EXITED_CLEAN))
@@ -66,11 +67,12 @@ class SyncTest: public CxxTest::TestSuite
// grab it (hangs) and then the first thread grabs it again
// without issue.
{
- recursive_mutex_init(&mutex);
+ mutex_t recursive_mutex;
+ recursive_mutex_init(&recursive_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);
+ tid_t l_task1 = task_create(entry_func, &recursive_mutex);
+ tid_t l_task2 = task_create(entry_func, &recursive_mutex);
if ((l_task2 != task_wait_tid(l_task2, &l_status, nullptr)) ||
(l_status == TASK_STATUS_EXITED_CLEAN))
@@ -205,8 +207,7 @@ class SyncTest: public CxxTest::TestSuite
static void* entry_func(void* i_p)
{
- SyncTest* my = (SyncTest*) i_p;
- mutex_t* myMutex = &(my->mutex);
+ mutex_t* myMutex = (mutex_t*) i_p;
// Call the recursive function.
recursive_func(myMutex, 2);
OpenPOWER on IntegriCloud