# 2022q1 Homework5 (quiz6)
contrubuted by <`yaohwang99`>
> [problem link](https://hackmd.io/@sysprog/linux2022-quiz6)
## Problem 1
### Spin lock
```c
typedef struct {
volatile int lock;
unsigned int locker;
} spin_t;
```
Set `lock` and `locker` to `0`
```c
int spin_init(spin_t *l)
{
volatile int out;
volatile int *lck = &(l->lock);
asm("movl $0x0,(%1);" : "=r"(out) : "r"(lck));
l->locker = 0;
return 0;
}
```
---
If `lock` is `0`, swap the value of `lock` with the address of itself, then return.
If `lock` is not `0`, then the value is the address of itself, swap with the address of itself until the value is changed by other thread calling `spin_release` to set `lck` to `0`.
```c
int spin_acquire(spin_t *l)
{
int out;
volatile int *lck = &(l->lock);
asm("whileloop:"
"xchg %%al, (%1);"
"test %%al,%%al;"
"jne whileloop;"
: "=r"(out)
: "r"(lck));
return 0;
}
```
The instuction `xchg` swaps `%al` with `(%rax)`, which means swap the last byte of `%rax` with `*lck`. where `%rax` stores the value of `lck`.
```
spin_acquire:
pushq %rbp
movq %rsp, %rbp
movq %rdi, -24(%rbp)
movq -24(%rbp), %rax
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
whileloop:xchg %al, (%rax);test %al,%al;jne whileloop;
movl %eax, -12(%rbp)
movl $0, %eax
popq %rbp
ret
```
---
```c
static inline int spin_release(spin_t *l)
{
int out;
volatile int *lck = &(l->lock);
asm("movl $0x0,(%1);" : "=r"(out) : "r"(lck));
l->locker = 0;
return 0;
}
```
:::warning
By my understanding, the lock works by swaping tha least significant byte of it's address, which is not 0x00.
I also don't understand why `locker` exists.
:::
---
### Mutex lock
Mutex lock is almost the same as spin lock except that it uses system call and [futex](https://man7.org/linux/man-pages/man2/futex.2.html) to sleep (line 12) until `FUTEX_WAKE` (line 28).
```c=
static __attribute__((noinline)) int mutex_acquire(mutex_t *m)
{
volatile int out;
volatile int *lck = &(m->lock);
asm("mutexloop:"
"mov $1, %%eax;"
"xchg %%al, (%%rdi);"
"test %%al,%%al;"
"je end"
: "=r"(out)
: "r"(lck));
syscall(SYS_futex, m, FUTEX_WAIT, 1, NULL, NULL, 0);
asm("jmp mutexloop");
asm("end:");
return 0;
}
/**
* @brief Release the lock object atomically and wake up waiting threads
* @param lock Mutex Lock object
*/
static inline int mutex_release(mutex_t *m)
{
volatile int out;
volatile int *lck = &(m->lock);
asm("movl $0x0,(%1);" : "=r"(out) : "r"(lck));
m->locker = 0;
syscall(SYS_futex, m, FUTEX_WAKE, 1, NULL, NULL, 0);
return 0;
}
```
---
### Create thread
```c=
/**
* @brief Create a One One mapped thread
* @param t Reference to the thread
* @param routine Function associated with the thread
* @param arg Arguments to the routine
*/
int thread_create(thread_t *t, void *routine, void *arg)
{
spin_acquire(&global_lock);
static bool init_state = false;
if (!t || !routine) {
spin_release(&global_lock);
return EINVAL;
}
if (!init_state) {
init_state = true;
init();
}
node_t *node = list_insert(&tid_list, 0);
if (!node) {
printf("Thread address not found\n");
spin_release(&global_lock);
return -1;
}
funcargs_t *fa = malloc(sizeof(funcargs_t));
if (!fa) {
printf("Malloc failed\n");
spin_release(&global_lock);
return -1;
}
fa->f = routine;
fa->arg = arg;
void *thread_stack = alloc_stack(STACK_SZ, GUARD_SZ);
if (!thread_stack) {
perror("thread create");
spin_release(&global_lock);
free(fa);
return errno;
}
fa->stack = thread_stack;
thread_t tid = clone(wrap, (char *) thread_stack + STACK_SZ + GUARD_SZ,
CLONE_FLAGS, fa, &(EXP1), NULL, &(EXP2));
node->tid_copy = tid;
node->fa = fa;
if (tid == -1) {
perror("thread create");
free(thread_stack);
spin_release(&global_lock);
return errno;
}
*t = tid;
spin_release(&global_lock);
return 0;
}
```
At line17, `init()` will only be called by the first cloned thread because `static` variables will ony be initialized once.
### clone flag
```c
#define CLONE_FLAGS \
(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | \
CLONE_SYSVSEM | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID)
```
CLONE_VM: the calling process and the child process run in the same memory space.
CLONE_FS: the caller and the child process share the same filesystem information.
CLONE_FILES: the calling process and the child process share the same file descriptor table.
CLONE_SIGHAND: the calling process and the child process share the same table of signal handlers.
CLONE_THREAD: the child is placed in the same thread group as the calling process.
CLONE_SYSVSEM: the child and the calling process share a single list of System V semaphore adjustment (semadj) values (see semop(2)).
CLONE_CHILD_CLEARTID: Clear (zero) the child thread ID at the location pointed to by child_tid (clone()) or cl_args.child_tid (clone3()) in child memory when the child exits, and do a wakeup on the futex at that address. The address involved may be changed by the set_tid_address(2) system call. This is used by threading libraries.
CLONE_PARENT_SETTID: Store the child thread ID at the location pointed to by parent_tid (clone()) or cl_args.parent_tid (clone3()) in the parent's memory.
thread_create $\to$ wrap $\to$ routine
```c
thread_t tid = clone(wrap, (char *) thread_stack + STACK_SZ + GUARD_SZ,
CLONE_FLAGS, fa, &(node->tid), NULL, &(node->tid));
node->tid_copy = tid;
```
The above function will create a new thread with a new tid, where `tid`, `node->tid`, and `node->tid_copy` has the same value.
:::danger
Because the flag include `CLONE_CHILD_CLEARTID` and `CLONE_PARENT_SETTID`, the function should clear `node->tid` to 0 and set `node->tid` to `tid `and return `tid`.
:::
---
When there are multiple reader entering the critical section, `rwlock` is acquired by the first reader and released by the last reader, preventing dead lock.
```c=
static void f1(void)
{
mutex_acquire(&lock);
if (++n_readers == 1)
mutex_acquire(&rwlock);
mutex_release(&lock);
safe_printf(&print_lock, "Reader process in\n");
atomic_fetch_add(&n_readers_in, 1);
mutex_acquire(&lock);
if (--n_readers == 0)
mutex_release(&rwlock);
mutex_release(&lock);
atomic_fetch_sub(&n_readers_in, 1);
safe_printf(&print_lock, "Reader process out\n");
}
```
## Problem 2
KKKK: &prev->next
QQQQ: &map->bucket[]
ZZZZ: &prev->next