Switch lock debug
coconutnut

I’m using 2 locks: TAS & TCLock, I want to maintain a counter of how many times the locks are called, and switch lock when it hits some threshold.

Basic structure:

1
2
3
4
5
6
7
8
struct slock_struct {
// Lock implementations
atomic_t tas;
struct orig_qspinlock komb;
// Lock switching
enum LockType type;
atomic_t count;
};

1st try

Initially I tried using a counter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void slock_lock(struct slock_struct *lock)
{
while (1) {
int count = atomic_read(&lock->count);
// Stop when threshold reached (might be switching)
if (count < SWITCH_THRESHOLD) {
if (atomic_cmpxchg_acquire(&lock->count, count, count + 1) == count) {
// Underlaying lock
slock_lock_chosen(lock);
return;
}
}
}
}

void slock_unlock(struct slock_struct *lock)
{
slock_unlock_chosen(lock);
if(atomic_read(&lock->count) == SWITCH_THRESHOLD){
// Switch lock
if(lock->type == TAS){
lock->type = KOMB;
}else{
lock->type = TAS;
}

int count = atomic_cmpxchg_release(&lock->count, SWITCH_THRESHOLD, 0);
if (count != SWITCH_THRESHOLD){
print_debug("!!!!!!!!!!!!!RESET COUNT ERROR, current_count=%d", count);
}
}
}

When running rcuhashtable test, I sometimes get error with current_count==1 (or 2, 8…)

and something like:

BUG: soft Lockup CPU#0 stuck for 26s

Bug example

The above code crashes in this example (say threshold is 100):

1
2
3
4
5
6
7
8
9
10
11
12
13
T1                T2
--- ---
get count 99
get count 100
lock TAS
CS
unlock TAS
lock TAS
read count 100
change lock
reset count
---
unlock TCLock [BOOM!]

2nd try

I changed to 2 counters, one for threads entering, one for leaving

Now it’s passing the test (for a few times, no bug yet)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void slock_lock(struct slock_struct *lock)
{
while (1) {
int count = atomic_read(&lock->count_in);
if(count < SWITCH_THRESHOLD) {
if(atomic_cmpxchg_acquire(&lock->count_in, count, count+1) == count) {
// Underlaying lock
slock_lock_chosen(lock);
return;
}
}
}
}

void slock_unlock(struct slock_struct *lock)
{
slock_unlock_chosen(lock);
// Try increase counter
while(1){
int count = atomic_read(&lock->count_out);
if(atomic_cmpxchg_acquire(&lock->count_out, count, count+1) == count){
// Hit threshold
if(count == SWITCH_THRESHOLD-1) {
// Switch lock
if(lock->type == TAS){
lock->type = KOMB;
}else{
lock->type = TAS;
}

// Reset count
int count_out = atomic_cmpxchg_release(&lock->count_out, SWITCH_THRESHOLD, 0);
if (count_out != SWITCH_THRESHOLD){
print_debug("!!!!!!!!!!!!!RESET COUNT ERROR, count_in=%d", count_out);
}
int count_in = atomic_cmpxchg_release(&lock->count_in, SWITCH_THRESHOLD, 0);
if (count_in != SWITCH_THRESHOLD){
print_debug("!!!!!!!!!!!!!RESET COUNT ERROR, count_in=%d", count_in);
}
}
return;
}
}
}