요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
===================================
refcount_t API compared to atomic_t
===================================
.. contents:: :local:
Introduction
============
The goal of refcount_t API is to provide a minimal API for implementing
an object's reference counters. While a generic architecture-independent
implementation from lib/refcount.c uses atomic operations underneath,
there are a number of differences between some of the ``refcount_*()`` and
``atomic_*()`` functions with regards to the memory ordering guarantees.
This document outlines the differences and provides respective examples
in order to help maintainers validate their code against the change in
these memory ordering guarantees.
The terms used through this document try to follow the formal LKMM defined in
tools/memory-model/Documentation/explanation.txt.
memory-barriers.txt and atomic_t.txt provide more background to the
memory ordering in general and for atomic operations specifically.
Relevant types of memory ordering
=================================
.. note:: The following section only covers some of the memory
ordering types that are relevant for the atomics and reference
counters and used through this document. For a much broader picture
please consult memory-barriers.txt document.
In the absence of any memory ordering guarantees (i.e. fully unordered)
atomics & refcounters only provide atomicity and
program order (po) relation (on the same CPU). It guarantees that
each ``atomic_*()`` and ``refcount_*()`` operation is atomic and instructions
are executed in program order on a single CPU.
This is implemented using READ_ONCE()/WRITE_ONCE() and
compare-and-swap primitives.
A strong (full) memory ordering guarantees that all prior loads and
stores (all po-earlier instructions) on the same CPU are completed
before any po-later instruction is executed on the same CPU.
It also guarantees that all po-earlier stores on the same CPU
and all propagated stores from other CPUs must propagate to all
other CPUs before any po-later instruction is executed on the original
CPU (A-cumulative property). This is implemented using smp_mb().
A RELEASE memory ordering guarantees that all prior loads and
stores (all po-earlier instructions) on the same CPU are completed
before the operation. It also guarantees that all po-earlier
stores on the same CPU and all propagated stores from other CPUs
must propagate to all other CPUs before the release operation
(A-cumulative property). This is implemented using
smp_store_release().
An ACQUIRE memory ordering guarantees that all post loads and
stores (all po-later instructions) on the same CPU are
completed after the acquire operation. It also guarantees that all
po-later stores on the same CPU must propagate to all other CPUs
after the acquire operation executes. This is implemented using
smp_acquire__after_ctrl_dep().
A control dependency (on success) for refcounters guarantees that
if a reference for an object was successfully obtained (reference
counter increment or addition happened, function returned true),
then further stores are ordered against this operation.
Control dependency on stores are not implemented using any explicit
barriers, but rely on CPU not to speculate on stores. This is only
a single CPU relation and provides no guarantees for other CPUs.
Comparison of functions
=======================
case 1) - non-"Read/Modify/Write" (RMW) ops
-------------------------------------------
Function changes:
* atomic_set() --> refcount_set()
* atomic_read() --> refcount_read()
Memory ordering guarantee changes:
* none (both fully unordered)
case 2) - non-"Read/Modify/Write" (RMW) ops with release ordering
-----------------------------------------------------------------
Function changes:
* atomic_set_release() --> refcount_set_release()
Memory ordering guarantee changes:
* none (both provide RELEASE ordering)
case 3) - increment-based ops that return no value
--------------------------------------------------
Function changes:
* atomic_inc() --> refcount_inc()
* atomic_add() --> refcount_add()
Memory ordering guarantee changes:
* none (both fully unordered)
case 4) - decrement-based RMW ops that return no value
------------------------------------------------------
Function changes:
* atomic_dec() --> refcount_dec()
Memory ordering guarantee changes:
* fully unordered --> RELEASE ordering
case 5) - increment-based RMW ops that return a value
-----------------------------------------------------
Function changes:
* atomic_inc_not_zero() --> refcount_inc_not_zero()
* no atomic counterpart --> refcount_add_not_zero()
Memory ordering guarantees changes:
* fully ordered --> control dependency on success for stores
.. note:: We really assume here that necessary ordering is provided as a
result of obtaining pointer to the object!
case 6) - increment-based RMW ops with acquire ordering that return a value
---------------------------------------------------------------------------
Function changes:
* atomic_inc_not_zero() --> refcount_inc_not_zero_acquire()
* no atomic counterpart --> refcount_add_not_zero_acquire()
Memory ordering guarantees changes:
* fully ordered --> ACQUIRE ordering on success
case 7) - generic dec/sub decrement-based RMW ops that return a value
---------------------------------------------------------------------
Function changes:
* atomic_dec_and_test() --> refcount_dec_and_test()
* atomic_sub_and_test() --> refcount_sub_and_test()
Memory ordering guarantees changes:
* fully ordered --> RELEASE ordering + ACQUIRE ordering on success
case 8) other decrement-based RMW ops that return a value
---------------------------------------------------------
Function changes:
* no atomic counterpart --> refcount_dec_if_one()
* ``atomic_add_unless(&var, -1, 1)`` --> ``refcount_dec_not_one(&var)``
Memory ordering guarantees changes:
* fully ordered --> RELEASE ordering + control dependency
.. note:: atomic_add_unless() only provides full order on success.
case 9) - lock-based RMW
------------------------
Function changes:
* atomic_dec_and_lock() --> refcount_dec_and_lock()
* atomic_dec_and_mutex_lock() --> refcount_dec_and_mutex_lock()
Memory ordering guarantees changes:
* fully ordered --> RELEASE ordering + control dependency + hold
spin_lock() on success
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
refcount_t와 atomic_t 비교 개요
1-24`refcount_t` API와 `atomic_t` 비교
이 페이지는 local contents 목차를 제공합니다.
소개
`refcount_t` API의 목표는 object의 reference counter를 구현하는 최소 API를 제공하는 것입니다. `lib/refcount.c`의 generic architecture-independent 구현은 내부적으로 atomic operation을 사용하지만, 일부 `refcount_*()`와 `atomic_*()` function은 memory ordering 보장에서 여러 차이가 있습니다.
이 문서는 이러한 차이와 해당 예제를 정리하여 maintainer가 memory ordering 보장 변경에 맞춰 code를 검증할 수 있도록 돕습니다.
문서의 용어는 `tools/memory-model/Documentation/explanation.txt`에 정의된 formal LKMM을 따르려고 합니다.
일반적인 memory ordering 배경은 `memory-barriers.txt`, atomic operation에 특화된 배경은 `atomic_t.txt`를 참고하십시오.
관련 memory ordering 유형
25-71관련 memory ordering 유형
이 절은 atomic과 reference counter에 관련되어 문서에서 사용하는 일부 memory ordering 유형만 다룹니다. 더 넓은 내용은 `memory-barriers.txt`를 참고하십시오.
Memory ordering 보장이 전혀 없는 fully unordered 상태에서 atomic과 refcounter는 atomicity와 같은 CPU 안의 program order(po) relation만 제공합니다. 각 `atomic_*()`와 `refcount_*()` operation이 atomic이며 instruction이 한 CPU에서 program order로 실행됨을 보장합니다. 이는 `READ_ONCE()/WRITE_ONCE()`와 compare-and-swap primitive로 구현합니다.
Strong 또는 full memory ordering은 같은 CPU의 모든 이전 load와 store, 즉 모든 po-earlier instruction이 완료된 뒤에야 po-later instruction을 실행하도록 보장합니다.
또한 같은 CPU의 모든 po-earlier store와 다른 CPU에서 전파된 모든 store가 원래 CPU의 po-later instruction 실행 전에 다른 모든 CPU로 전파되어야 함을 보장합니다. 이것이 A-cumulative property이며 `smp_mb()`로 구현합니다.
RELEASE memory ordering은 같은 CPU의 모든 이전 load와 store가 release operation보다 먼저 완료됨을 보장합니다. 같은 CPU의 po-earlier store와 다른 CPU에서 전파된 store도 release operation 전에 다른 모든 CPU로 전파되어야 합니다. 이 A-cumulative property는 `smp_store_release()`로 구현합니다.
ACQUIRE memory ordering은 같은 CPU의 모든 이후 load와 store, 즉 po-later instruction이 acquire operation 뒤에 완료됨을 보장합니다. 같은 CPU의 모든 po-later store도 acquire operation 실행 뒤에 다른 모든 CPU로 전파되어야 합니다. 이는 `smp_acquire__after_ctrl_dep()`로 구현합니다.
Refcounter의 성공 시 control dependency는 object reference를 성공적으로 얻은 경우, 즉 reference counter increment 또는 addition이 발생하고 function이 true를 반환한 경우 이후 store가 이 operation에 대해 ordering됨을 보장합니다.
Store에 대한 control dependency는 명시적인 barrier로 구현하지 않고 CPU가 store를 speculate하지 않는다는 성질에 의존합니다. 이는 한 CPU 안의 relation일 뿐이며 다른 CPU에는 아무 보장도 제공하지 않습니다.
Case 1과 2: non-RMW operation
72-100Function 비교
Case 1: non-Read/Modify/Write(RMW) operation
Function 변경:
- `atomic_set()` -> `refcount_set()`
- `atomic_read()` -> `refcount_read()`
Memory ordering 보장 변경:
- 변경 없음. 둘 다 fully unordered입니다.
Case 2: release ordering을 사용하는 non-Read/Modify/Write(RMW) operation
Function 변경:
- `atomic_set_release()` -> `refcount_set_release()`
Memory ordering 보장 변경:
- 변경 없음. 둘 다 RELEASE ordering을 제공합니다.
Case 3과 4: 반환값 없는 increment와 decrement
101-124Case 3: 값을 반환하지 않는 increment 기반 operation
Function 변경:
- `atomic_inc()` -> `refcount_inc()`
- `atomic_add()` -> `refcount_add()`
Memory ordering 보장 변경:
- 변경 없음. 둘 다 fully unordered입니다.
Case 4: 값을 반환하지 않는 decrement 기반 RMW operation
Function 변경:
- `atomic_dec()` -> `refcount_dec()`
Memory ordering 보장 변경:
- Fully unordered에서 RELEASE ordering으로 변경됩니다.
Case 5: 값을 반환하는 increment operation
125-140Case 5: 값을 반환하는 increment 기반 RMW operation
Function 변경:
- `atomic_inc_not_zero()` -> `refcount_inc_not_zero()`
- 대응하는 atomic function 없음 -> `refcount_add_not_zero()`
Memory ordering 보장 변경:
- Fully ordered에서 store에 대한 성공 시 control dependency로 변경됩니다.
여기서는 object pointer를 얻은 결과로 필요한 ordering이 제공된다고 전제합니다.
Case 6: acquire increment operation
141-153Case 6: acquire ordering을 사용하고 값을 반환하는 increment 기반 RMW operation
Function 변경:
- `atomic_inc_not_zero()` -> `refcount_inc_not_zero_acquire()`
- 대응하는 atomic function 없음 -> `refcount_add_not_zero_acquire()`
Memory ordering 보장 변경:
- Fully ordered에서 성공 시 ACQUIRE ordering으로 변경됩니다.
Case 7: generic decrement operation
154-166Case 7: 값을 반환하는 generic dec/sub decrement 기반 RMW operation
Function 변경:
- `atomic_dec_and_test()` -> `refcount_dec_and_test()`
- `atomic_sub_and_test()` -> `refcount_sub_and_test()`
Memory ordering 보장 변경:
- Fully ordered에서 RELEASE ordering과 성공 시 ACQUIRE ordering의 조합으로 변경됩니다.
Case 8: 기타 decrement operation
167-181Case 8: 값을 반환하는 기타 decrement 기반 RMW operation
Function 변경:
- 대응하는 atomic function 없음 -> `refcount_dec_if_one()`
- `atomic_add_unless(&var, -1, 1)` -> `refcount_dec_not_one(&var)`
Memory ordering 보장 변경:
- Fully ordered에서 RELEASE ordering과 control dependency의 조합으로 변경됩니다.
`atomic_add_unless()`는 성공한 경우에만 full ordering을 제공합니다.
Case 9: lock 기반 RMW
182-193Case 9: lock 기반 RMW
Function 변경:
- `atomic_dec_and_lock()` -> `refcount_dec_and_lock()`
- `atomic_dec_and_mutex_lock()` -> `refcount_dec_and_mutex_lock()`
Memory ordering 보장 변경:
- Fully ordered에서 RELEASE ordering, control dependency, 성공 시 `spin_lock()` 보유의 조합으로 변경됩니다.
요약과 해설
refcount-vs-atomic.rst:1-193`refcount_t`는 object lifetime 관리에 필요한 최소 operation과 overflow 방어를 제공하지만, 대응하는 `atomic_t` function과 memory ordering이 항상 같지는 않습니다.
단순 set/read와 일부 increment는 unordered 상태를 유지하는 반면 decrement는 RELEASE ordering을 제공하고, 성공 여부를 반환하는 operation은 ACQUIRE 또는 control dependency를 추가할 수 있습니다.
변환할 때는 function 이름만 바꾸지 말고 기존 code가 full barrier에 의존했는지 확인해야 합니다. 특히 성공 경로의 pointer 접근, 마지막 reference 해제, lock 획득 경로에서 요구하는 ordering을 LKMM 기준으로 검토해야 합니다.