# 2022q1 Homework3 (quiz4)
contributed by <`yaohwang99` >
> [quiz4](https://hackmd.io/@sysprog/linux2022-quiz4)
## Problem 1
$\lceil\log_2x\rceil$ = number of bits needed to store $x-1$, for example, $\log_28=3,\ 8-1=7=111_2$, $\lceil\log_29\rceil=4, \ 9-1=8=1000_2$
Therefore, `ceil_log2(uint32_t x)` is find last set for `x-1`.
1. Decrease x by 1.
2. Check if any of the 16 more significant bits are set, if so, increase r by 16 and shift x right 16.
3. Repeat step 2 for 8, 4, 2 bits.
```c=
int ceil_log2(uint32_t x)
{
uint32_t r, shift;
x--;
r = (x > 0xFFFF) << 4;
x >>= r;
shift = (x > 0xFF) << 3;
x >>= shift;
r |= shift;
shift = (x > 0xF) << 2;
x >>= shift;
r |= shift;
shift = (x > 0x3) << 1;
x >>= shift;
return (EXP1) + 1;
}
```
After line 14, x has 2 bit left, we also need to update `r`. With the same approach, The code after line 14 is
```c
r |= shift;
shift = (x > 0x1) << 0;
x >>= shift;
r |= shift;
return r;
```
The above code can be reduced to `return (EXP1) + 1`, where ==EXP 1 is `r | shift | x >> 1`==.
---
## Problem 2
Similar to Problem 1, the following code uses `t` as mask and check if any of the `shift` less significant bit are set. If none, increase the return value `o` by `shift`.
==EXP2: `x & t`==
==EXP3: `o |= shift`==
```c
#define BITS_PER_BYTE 8
#define BITS_PER_LONG (sizeof(unsigned long) * BITS_PER_BYTE)
#include <stddef.h>
static inline size_t ffs(unsigned long x)
{
if (x == 0)
return 0;
size_t o = 1;
unsigned long t = ~0UL;
size_t shift = BITS_PER_LONG;
shift >>= 1;
t >>= shift;
while (shift) {
if ((EXP2) == 0) {
x >>= shift;
EXP3;
}
shift >>= 1;
t >>= shift;
}
return o;
}
```
---
## Problem 3
```c=
struct foo_consumer {
int (*handler)(struct foo_consumer *self, void *);
struct foo_consumer *next;
};
struct foo {
struct foo_consumer *consumers;
unsigned long flags;
};
#include <stdbool.h>
/*
* For foo @foo, delete the consumer @fc.
* Return true if the @fc is deleted sfccessfully
* or return false.
*/
static bool consumer_del(struct foo *foo, struct foo_consumer *fc)
{
struct foo_consumer **con;
bool ret = false;
for (con = &foo->consumers; *con; EXP4) {
if (*con == fc) {
*con = EXP5;
ret = true;
break;
}
}
return ret;
}
```
At the start of `for` at line 23, `con` points to `foo->consumers`
```graphviz
digraph {
compound = true
rankdir = LR
subgraph cluster0{
label = "struct foo"
style = filled
color = gray90
node0[label = "<consumers>consumers|flag" shape = record]
}
subgraph cluster1{
label = "struct foo_consumer"
style = filled
color = gray90
node1[label = "handler|<next>next" shape = record]
}
subgraph cluster2{
label = "struct foo_consumer"
style = filled
color = gray90
node2[label = "handler|<next>next" shape = record]
}
subgraph cluster3{
label = "struct foo_consumer"
style = filled
color = gray90
node3[label = "handler|<next>next" shape = record]
}
con[shape = plaintext]
fc[shape = plaintext]
con->node0:consumers
fc->node2[lhead = cluster2]
node0:consumers->node1[lhead = cluster1]
node1:next->node2[lhead = cluster2]
node2:next->node3[lhead = cluster3]
}
```
Then `con` traverse through the list untill `*con == fc`, therefore, ==EXP4 is `con = &(*con)->next`==.
```graphviz
digraph {
compound = true
rankdir = LR
subgraph cluster0{
label = "struct foo"
style = filled
color = gray90
node0[label = "<consumers>consumers|flag" shape = record]
}
subgraph cluster1{
label = "struct foo_consumer"
style = filled
color = gray90
node1[label = "handler|<next>next" shape = record]
}
subgraph cluster2{
label = "struct foo_consumer"
style = filled
color = gray90
node2[label = "handler|<next>next" shape = record]
}
subgraph cluster3{
label = "struct foo_consumer"
style = filled
color = gray90
node3[label = "handler|<next>next" shape = record]
}
con[shape = plaintext]
fc[shape = plaintext]
con->node1:next[color = red]
fc->node2[lhead = cluster2]
node0:consumers->node1[lhead = cluster1]
node1:next->node2[lhead = cluster2]
node2:next->node3[lhead = cluster3]
}
```
Then remove `fc` from the list, ==EXP3: fc->next==
```graphviz
digraph {
compound = true
rankdir = LR
subgraph cluster0{
label = "foo"
style = filled
color = gray90
node0[label = "<consumers>consumers|flag" shape = record]
}
subgraph cluster1{
label = "foo_consumer"
style = filled
color = gray90
node1[label = "handler|<next>next" shape = record]
}
subgraph cluster2{
label = "foo_consumer"
style = filled
color = gray90
node2[label = "handler|<next>next" shape = record]
}
subgraph cluster3{
label = "foo_consumer"
style = filled
color = gray90
node3[label = "handler|<next>next" shape = record]
}
con[shape = plaintext]
fc[shape = plaintext]
con->node1:next
fc->node2[lhead = cluster2]
node0:consumers->node1[lhead = cluster1]
node1:next->node3[lhead = cluster3 color = red]
node2:next->node3[lhead = cluster3]
}
```
## Problem 4
The structure stores the `env` value. `tasks` is function designator
```c
struct task {
jmp_buf env;
struct list_head list;
};
static LIST_HEAD(tasklist);
static void (**tasks)(void *);
static int ntasks;
static jmp_buf sched;
```
```graphviz
digraph {
compound = true
rankdir = LR
subgraph cluster0{
label = "tasklist"
style = filled
color = gray90
node0[shape = record label="<next>next|<prev>prev"]
}
subgraph cluster1{
label = "task"
style = filled
color = gray90
node1[shape = record label="env|<list>list"]
}
subgraph cluster2{
label = "task"
style = filled
color = gray90
node2[shape = record label="env|<list>list"]
}
subgraph cluster3{
label = "task"
style = filled
color = gray90
node3[shape = record label="env|<list>list"]
}
node0->node1:list[dir = both]
node1:list->node2:list[dir = both]
node2:list->node3:list[dir = both]
node3:list->node0:prev[dir = both]
}
```
`task_add` inserts a new tast from tail and `task_switch` removes the first task in the list. Therefore, ==EXP6 is `list_del(&t->list)`==.
`task_join` goes through each task in the list and complete them, so ==EXP7 is `list_del(&t->list)`==.
```c
static void task_add(struct list_head *tasklist, jmp_buf env)
{
struct task *t = malloc(sizeof(*t));
memcpy(t->env, env, sizeof(jmp_buf));
INIT_LIST_HEAD(&t->list);
list_add_tail(&t->list, tasklist);
}
static void task_switch(struct list_head *tasklist)
{
jmp_buf env;
if (!list_empty(tasklist)) {
struct task *t = list_first_entry(tasklist, struct task, list);
EXP6;
memcpy(env, t->env, sizeof(jmp_buf));
free(t);
longjmp(env, 1);
}
}
static void task_join(struct list_head *tasklist)
{
jmp_buf env;
while (!list_empty(tasklist)) {
struct task *t = list_first_entry(tasklist, struct task, list);
EXP7;
memcpy(env, t->env, sizeof(jmp_buf));
free(t);
longjmp(env, 1);
}
}
```
`longjmp(sched, 1)` will jump to `setjump(sched)` in `schedule`.
```c
void schedule(void)
{
static int i;
srand(0xCAFEBABE ^ (uintptr_t) &schedule); /* Thanks to ASLR */
setjmp(sched);
while (ntasks-- > 0) {
int n = rand() % 5;
tasks[i++](&n);
printf("Never reached\n");
}
task_join(&tasklist);
}
```
For each tast, the program prints `n` then add itself to `tasklist` then do EXP8.
```c
/* A task yields control n times */
void task0(void *arg)
{
jmp_buf env;
static int n;
n = *(int *) arg;
printf("Task 0: n = %d\n", n);
if (setjmp(env) == 0) {
task_add(&tasklist, env);
EXP8;
}
for (int i = 0; i < n; i++) {
if (setjmp(env) == 0) {
task_add(&tasklist, env);
task_switch(&tasklist);
}
printf("Task 0: resume\n");
}
printf("Task 0: complete\n");
longjmp(sched, 1);
}
```
From the output, we can see that the program jumps back to `schedule()` after n is printed, so ==EXP8 and EXP9 is `longjmp(sched, 1)`==
```
Task 0: n = 3
Task 1: n = 3
Task 0: resume
Task 1: resume
Task 0: resume
Task 0: complete
Task 1: resume
Task 1: complete
```
---
## Problem 5
```c
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#define __READ_ONCE_SIZE \
({ \
switch (size) { \
case 1: \
*(uint8_t *) res = *(volatile uint8_t *) p; \
break; \
case 2: \
*(uint16_t *) res = *(volatile uint16_t *) p; \
break; \
case 4: \
*(uint32_t *) res = *(volatile uint32_t *) p; \
break; \
case 8: \
*(uint64_t *) res = *(volatile uint64_t *) p; \
break; \
default: \
memcpy((void *) res, (const void *) p, size); \
} \
})
static inline void __read_once_size(const volatile void *p, void *res, int size)
{
__READ_ONCE_SIZE;
}
```
Here, we would like to implement READ_ONCE using the above macro.
The parameter `res` corresponds to the argument `__u.__c`.
The intuitve answer is let DECL0 be `void *__c`, however, case 1 (line 8) of the above macro indicates that we need `*res` to be 1 byte.
Therefore, ==DECL0 is `char __c[1]`==, where `__c` is a pointer to 1 byte memory.
```c
#define READ_ONCE(x) \
({ \
union { \
typeof(x) __val; \
DECL0; \
} __u; \
__read_once_size(&(x), __u.__c, sizeof(x)); \
__u.__val; \
})
```