# Atomic RMW와 spinlock: LSE/LL-SC, LOCK과 AMO

v6.18.37 / arch/arm64/include/asm/atomic.h

두 CPU가 동시에 카운터를 1씩 올릴 때 둘 다 옛 값 10을 읽고 11을 쓰면 한 번의 증가가 사라집니다. 원자적 연산은 이 중간 끼어들기를 막습니다. arm64의 LSE 또는 LL/SC, x86의 LOCK 명령, RISC-V의 AMO 또는 LR/SC는 이를 구현하지만 주변 메모리의 순서 보장은 별도 조건입니다.

## ATOMIC_FETCH_OP / ATOMIC_FETCH_OPS

```c

#define ATOMIC_FETCH_OP(name, op)					\
static __always_inline int arch_##op##name(int i, atomic_t *v)		\
{									\
	return __lse_ll_sc_body(op##name, i, v);			\
}

#define ATOMIC_FETCH_OPS(op)						\
	ATOMIC_FETCH_OP(_relaxed, op)					\
	ATOMIC_FETCH_OP(_acquire, op)					\
	ATOMIC_FETCH_OP(_release, op)					\
	ATOMIC_FETCH_OP(        , op)

```

### 34행

```c

#define ATOMIC_FETCH_OP(name, op)					\

```

연산 이름 op와 접미사 name을 받아 함수 하나를 생성하는 C 전처리 매크로입니다. 줄 끝 역슬래시는 정의가 다음 줄에도 이어짐을 뜻합니다.

### 35행

```c

static __always_inline int arch_##op##name(int i, atomic_t *v)		\

```

##로 토큰을 결합합니다. 예를 들어 op가 atomic_fetch_add이고 name이 _acquire면 arch_atomic_fetch_add_acquire가 됩니다. i는 피연산자, v는 수정할 atomic_t의 주소입니다.

### 37행

```c

	return __lse_ll_sc_body(op##name, i, v);			\

```

연산과 순서 접미사를 유지한 채 LSE 또는 LL/SC 구현으로 연결하고 그 결과를 반환합니다. fetch 연산이라면 반환값은 갱신 전 값입니다.

### 40행

```c

#define ATOMIC_FETCH_OPS(op)						\

```

이번에는 같은 연산의 메모리 순서 변형 네 개를 한꺼번에 생성할 매크로를 정의합니다.

### 41행

```c

	ATOMIC_FETCH_OP(_relaxed, op)					\

```

relaxed 변형을 만듭니다. 대상 값의 원자성은 유지하지만 주변 메모리에 추가 acquire/release 순서를 붙이지 않습니다.

### 42행

```c

	ATOMIC_FETCH_OP(_acquire, op)					\

```

acquire 변형을 만듭니다. 후속 접근과 연산 사이의 순서 보장이 필요한 소비자 쪽에 사용합니다.

### 43행

```c

	ATOMIC_FETCH_OP(_release, op)					\

```

release 변형을 만듭니다. 앞선 접근을 공개하는 생산자 쪽 순서와 연결됩니다.

### 44행

```c

	ATOMIC_FETCH_OP(        , op)

```

접미사가 없는 기본형을 만듭니다. 해당 Linux atomic API가 정한 기본 메모리 순서를 따릅니다. 이 네 줄은 네 연산을 순차 실행하는 코드가 아닙니다.