## Memory allocation

`kmalloc()`
`kfree()`
`malloc()`
`free()`

Default stack size is 1KB. Avoid a large local variable but use dynamic
allocation such as malloc() or kmalloc() to minimize the possiblity of stack
overflow.

## Synchronization

`lock_t` - General lock type. The value of less than 0 means locked while more
than 1 means unlocked. 락 기본형. 0 이하 값은 잠김, 1 이상의 값은 풀림이라는
것이 구현된 모든 락의 기본 전제.

`schedule()` never takes place in an interrupt context. If there are any
interrupts active or pending, `schedule()` gets its chance to run after all
that interrupts handled first.

1. If data or region is accessed by more than a task, `use mutex_lock()`
  - It guarantees you access the data exclusively. You go sleep until you get
  the key.

2. If the one you are accessing to is the resource that the system also
manipulates in an interrupt, `use spin_lock_irqsave()`.
  - spinlock never goes to sleep.

3. If you code an interrupt handler, already preemption disabled, `spin_lock()`
enough to use since `spin_lock_irqsave()` only do `preempt_disable()` before
`spin_lock()`.

Don't use `cli()` direct but `preempt_disable()`.

데드락은 아무래도 골치거리라서 체크할 방법이 필요한데, 전체락을 리스트로
일괄관리하면 어떨까? 디버깅시에 인터럽트 우선순위를 조정해서 벽돌이 된 지점에
걸린 락을 찾을 수 있도록. 아니면 디버깅용 인터럽트 하나를 최상위 우선순위로
지정하고 스택킹된 pc의 값을 출력.

(1)디폴트 우선순위를 0보다 크게 설정.
(2)디버깅용 핀을 하나 지정, 우선순위를 0으로 가장 높게 설정.
(3)핀에 신호가 걸리면 스택킹된 pc(즉 실행중이던 위치)를 출력.

### atomic data type

기본 자료형 int을 atomic 형으로 사용.

단순 대입연산(e.g. `a = value`)과 다르게 `a |= value` 와 같은 연산은 atomic으로
수행되지 않음을 주지할 필요가 있다.

### semaphore(mutex)

long term waiting.
sleeping lock.

`mutex_lock()`, `mutex_unlock()`

`semaphore_down()`, `semaphore_up()`

락을 얻지 못하면 대기큐에 들어가 `TASK_WAITING` 상태가 되는데, 락이 풀릴 때
들어간 순서대로 하나씩 깨어남. 일괄적으로 한번에 깨우기엔 오버헤드만 크고,
그렇게 해야만 하는 상황에 대해서는 아직 떠오르지 않음.

### spin lock

싱글코어가 타겟이지만 락을 쓸 때는 어쩐지 멀티코어까지 가정하게 되어서,
싱글코어로 최적화 한다면 `spin_lock()`은 공백으로, `spin_lock_irqsave()`는
`local_irq_disable()`로 변경.

멀티코어에 대해 아는바가 별로 없어서 동기화 관련 코드를 작성할 때마다 고민이
많다. 멀티코어는 잊어버리자 싶다가도, 왠지 얼렁뚱땅 허술한 인간이 되는 거
같아서..

~~short term waiting~~

`spin_lock()` - in context of interrupt. Actually it doesn't seem useful in a
single processor because being in context of interrupt proves no one has the
keys to where using that spin lock. That means whenever you use spin lock in a
task you must disable interrupts before you get the key using
`spin_lock_irqsave()`.

`spin_lock_irqsave()` - for both of interrupt handlers and user tasks.

rw lock 필요. 콘솔이나 irc 값 읽는데 스핀락은 비용이 큰 듯. 자료구조와 락 형태
개선.

### rw lock

`read_lock()`
`write_lock()`

~~`read_lock()`으로 임계영역에 진입하더라도 그 와중에 `write_lock()`이 발생하고
`read_unlock()`전에 write 연산이 종료된다면 read 쪽에서는 이를 감지하지 못함.
read 연산이 write 연산보다 짧다는 것이 보장되어야 함.~~

리드시 카운터를 up, 라이트시 down. 락이 걸려있다면 리딩은 스핀. 카운터의 상태가
unlocked(1)이 아니라면 라이팅 스핀. 스핀락과 거의 동일하지만, 리딩은
상호배타적이지 않다는 것. 슬립하지 않으므로 어디서나 사용가능.

### Preventing preemption

	preempt_disable()
	... here can't be interrupted ...
	preempt_enable()

`preempt_disable()` increases count by 1 while `preempt_enable()` decreases
count. When the count reaches 0, interrupts get enabled.

## waitqueue

	/* Wait for condition */
	DEFINE_WAIT_HEAD(q)
	DEFINE_WAIT(wait_task)

	while (!condition) {
		wait_in(&q, &wait_task);
	}

	/* Wake up */
	wait_up(&q, option);

	*option:
	WQ_EXCLUSIVE - 하나의 태스크만 깨움
	WQ_ALL - 대기큐의 모든 태스크 깨움
	`숫자` - 숫자만큼의 태스크 깨움

대기큐에 도착하는 순서대로 들어가서 그 순서 그대로 깨어남(FIFO). 동기화
관련부분만 테스트하고 대기큐 별도로는 테스트하지 않음.

## time

Variable `systick` can be accessed directly. `systick` is ~~not~~ counted every
HZ, ~~but hardware system clock, `get_sysclk_freq()`.~~ To calculate elapsed
time in second, ~~`systick` / `get_sysclk_freq()`~~ `systick` / `HZ`.

`get_systick64()` - to get whole 64-bit counter.

### timer

32비트 systick 값을 초과하는 시간은 설정할 수 없다. 즉 50HZ 에서 설정할 수 있는
타이머의 최대 시간은 2년 남짓이다(32bits / 60(초) / 60(분) / 60(시) / 24(일) /
365(년)).

`add_timer()`

`del_timer()`

`mod_timer()`

### timeout

systick 단위

`set_timeout()`

`is_timeout()`

### sleep & delay

`sleep(), msleep()` - absolute time based on system clock. It doesn't work in
stop mode while works in sleep mode.

`delay(), mdelay(), udelay(), ndelay()` - relative time based on clock cycle
that the task is taking, sleepless delay functions

udelay() - be aware that it can be delayed more than requested up to `HZ *
number of tasks` as context switch occurs every HZ.

## System call

`syscall(number, arg1, ...)` 형식으로 시스템 콜을 호출할 수 있다. `int`형을
반환하고, 시스템 콜 번호를 포함해 최대 4개까지 매개변수를 전달할 수 있다.

	int sys_test(int a, int b)
	{
		return a+b;
	}

위와 같은 시스템 콜을 정의했다면, `syscall.h` 파일에 해당 시스템 콜을 추가한다.

	#define SYSCALL_RESERVED 0
	#define SYSCALL_SCHEDULE 1
	#define SYSCALL_TEST     2
	#define SYSCALL_NR       3

주의할 점은 해당 시스템 콜을 추가하고 `SYSCALL_NR` 값 역시 증가시켜주어야 한다.
`SYSCALL_NR`은 등록되지 않은 시스템 콜 호출을 방지하는 데 사용된다. 유효하지
않은 시스템 콜을 호출할 경우 0번 시스템 콜 `SYSCALL_RESERVED` 가 실행된다.

그리고 추가한 번호 순서에 알맞은 테이블 위치에(`kernel/syscall.c`) 해당 함수를
등록한다.

	void *syscall_table[] = {
		sys_reserved,		/* 0 */
	        sys_schedule,
	        sys_test,

시스템 콜은 보통 관련소스(파일 시스템이라면 fs.c) 하단에 정의되어 있다.

## Device Driver

`devtab` 해시 테이블에 모든 디바이스가 등록된다.

`getdev()` - 디바이스 자료구조 구하기

`dev_get_newid()` - 새로운 디바이스 아이디 얻기

`register_dev(id, ops, name)` - 디바이스 등록

`/dev/` 아래 `name` 노드 생성. `name`이 NULL일 경우 노드가 이미 생성된 것으로
가정하고 디바이스만 등록. minor 번호가 `name` 접미사로 붙음. e.g. name1

`MODULE_INIT(func)` - 디바이스 초기화 함수 등록

e.g.

	static int your_open(struct file *file)
	{
		...
	}

	static size_t your_read(struct file *file, void *buf, size_t size)
	{
		...
	}

	static struct file_operations your_ops = {
		.open  = your_open,
		.read  = your_read,
		.write = NULL,
		.close = NULL,
	};

	static your_init()
	{
		dev_t id = mkdev(0, 0);

		register_dev(id, &your_ops, "your_dev");
		...
	}

	MODULE_INIT(console_init);

`mkdev()` 리턴값이  0이면 오류. 디바이스를 등록할 가용공간이 없음.

첫번째 인자 major 값이 0이면 자동할당. 이미 할당된 major 번호를 공유하는 공통
드라이버를 구현할 경우 이미 생성된 노드에서 major 번호를 구할 것. major 번호는
순차적으로 증가한 값을 갖는다. 즉, 지정한 major 번호가 등록되지 않은 번호라면
요청한 번호는 적절한 값으로 변경될 수 있다. 반면 지정한 minor 번호가 이미
존재할 경우에는 자동으로 비어있는 번호를 찾아 등록된다. 대부분의 경우,
`mkdev(0, 0)`으로 충분.

major 16 비트, minor 16 비트. 각 65535개씩 등록 가능(0번째 항목은 에러체크로
제외).

~~id는 사실 전달할 필요가 없다. 인자를 하나씩 제거할 수 있지만, 디바이스 상위
시스템 추상화를 어떻게 할지 감을 못잡았으므로 일단 그대로 두는 걸로.~~

`MODULE_INIT`은 부트 타임 등록만 된다. 런타임 등록도 고려해볼 것.

~~통합 버퍼 관리자 고려해볼 것. 디바이스 드라이버에서 사용하는 버퍼 같은 경우
직접 kmalloc 사용하기보단 상위 관리자를 이용.~~

## GPIO

`gpio_init()` - 인터럽트 벡터 번호 리턴. 비인터럽트일 경우 마이너스 값 리턴.
flags 설명(gpio.h)

각 포트와 핀은 0~n으로 지시. 포트당 8핀일 경우 PORTA와 PORTB의 각 마지막 핀
번호는 7과 13으로 지시.

`ret_from_gpio_int()` - 인터럽트 서비스 루틴 구현시 이 함수로 리턴해야 함

## Softirq

`request_softirq()`
`raise_softirq()`

할당받은 softirq 번호가 `SOFTIRQ_MAX` 보다 크거나 같다면 오류. softirq pool이
이미 꽉 찬 상태. 등록시 반드시 체크필요.

## miscellaneous

`itoa()` 함수원형: `char *itoa(int v, char *buf, unsigned int base, size_t
maxlen)`. 오버플로어를 방지하기 위해 마지막 maxlen 인자가 추가됨. 변환된
문자열의 시작주소가 반환됨. 인자로 넘긴 buf변수를 변환된 문자열로 사용하면
안됨. 반드시 리턴값을 문자열로 사용해야 함.

## clone

`fork()`

MMU 가상화 장치의 혜택을 받지 못하므로 새로 생성된 태스크의 스택은 해당 메모리
주소 공간으로 재조정되어야 한다. 원본으로부터 복제된 초기의 스택은 원본의
주소공간을 가리키고 있기 때문에 페이지 폴트가 일어난다. 따라서 원본의
주소공간과 스택 포인터 간의 관계(옵셋)을 고려하여 복제된 스택의 주소공간이
원본의 주소공간(원본 스택주소내의 주소일 경우)을 가리키는 경우 해당 주소를
재조정해야 한다. 하나의 태스크 주소공간에 다른 태스크의 주소공간이 어드레싱될
경우는 생길 수 없다. 따라서 세련된 방식은 아닐지라도 유효한 방법이 될 수 있다.

## scheduler

`schedule()` - general use in user context, either a user task or a kernel task
`resched()` - is used when scheduling needed in handler context, raising interrupt pending bit or setting `resched` flag
`run_scheduler(true/false) - enable or disable scheduler

## fifo

`type_size` up to WORD_SIZE possible.

`fifo_get(struct fifo, int type_size)`

`fifo_put(struct fifo, int value, type_size)`

## misc

`is_interrupt_disabled()` - if true, disabled or enabled
`get_current_rank()` - Either TF_PRIVILEGED or TF_USER
`which_context()` - tells you which context you are in:
                    0 = Thread mode
		    1 = Reserved
		    2 = NMI
		    3 = HardFault
		    4 = MemManage
		    5 = BusFault
		    6 = UsageFault
		    7-10 = Reserved
		    11 = SVCall
		    12 = Reserved for Debug
		    13 = Reserved
		    14 = PendSV
		    15 = SysTick
		    16 = IRQ0
