|  | =================================== | 
|  | 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 :c:func:`READ_ONCE`/:c:func:`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 :c:func:`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 | 
|  | :c:func:`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 | 
|  | :c:func:`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: | 
|  |  | 
|  | * :c:func:`atomic_set` --> :c:func:`refcount_set` | 
|  | * :c:func:`atomic_read` --> :c:func:`refcount_read` | 
|  |  | 
|  | Memory ordering guarantee changes: | 
|  |  | 
|  | * none (both fully unordered) | 
|  |  | 
|  |  | 
|  | case 2) - increment-based ops that return no value | 
|  | -------------------------------------------------- | 
|  |  | 
|  | Function changes: | 
|  |  | 
|  | * :c:func:`atomic_inc` --> :c:func:`refcount_inc` | 
|  | * :c:func:`atomic_add` --> :c:func:`refcount_add` | 
|  |  | 
|  | Memory ordering guarantee changes: | 
|  |  | 
|  | * none (both fully unordered) | 
|  |  | 
|  | case 3) - decrement-based RMW ops that return no value | 
|  | ------------------------------------------------------ | 
|  |  | 
|  | Function changes: | 
|  |  | 
|  | * :c:func:`atomic_dec` --> :c:func:`refcount_dec` | 
|  |  | 
|  | Memory ordering guarantee changes: | 
|  |  | 
|  | * fully unordered --> RELEASE ordering | 
|  |  | 
|  |  | 
|  | case 4) - increment-based RMW ops that return a value | 
|  | ----------------------------------------------------- | 
|  |  | 
|  | Function changes: | 
|  |  | 
|  | * :c:func:`atomic_inc_not_zero` --> :c:func:`refcount_inc_not_zero` | 
|  | * no atomic counterpart --> :c:func:`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 5) - generic dec/sub decrement-based RMW ops that return a value | 
|  | --------------------------------------------------------------------- | 
|  |  | 
|  | Function changes: | 
|  |  | 
|  | * :c:func:`atomic_dec_and_test` --> :c:func:`refcount_dec_and_test` | 
|  | * :c:func:`atomic_sub_and_test` --> :c:func:`refcount_sub_and_test` | 
|  |  | 
|  | Memory ordering guarantees changes: | 
|  |  | 
|  | * fully ordered --> RELEASE ordering + ACQUIRE ordering on success | 
|  |  | 
|  |  | 
|  | case 6) other decrement-based RMW ops that return a value | 
|  | --------------------------------------------------------- | 
|  |  | 
|  | Function changes: | 
|  |  | 
|  | * no atomic counterpart --> :c:func:`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:: :c:func:`atomic_add_unless` only provides full order on success. | 
|  |  | 
|  |  | 
|  | case 7) - lock-based RMW | 
|  | ------------------------ | 
|  |  | 
|  | Function changes: | 
|  |  | 
|  | * :c:func:`atomic_dec_and_lock` --> :c:func:`refcount_dec_and_lock` | 
|  | * :c:func:`atomic_dec_and_mutex_lock` --> :c:func:`refcount_dec_and_mutex_lock` | 
|  |  | 
|  | Memory ordering guarantees changes: | 
|  |  | 
|  | * fully ordered --> RELEASE ordering + control dependency + hold | 
|  | :c:func:`spin_lock` on success |