Messages in this thread Patch in this message | | | Date | Mon, 13 Dec 2021 13:15:36 +0100 | From | Peter Zijlstra <> | Subject | [PATCH v2 10/9] atomic: Document the atomic_{}_overflow() functions |
| |
They're damn special, they're very likely to confuse people, write a few words in a vain attempt to twart some of that confusion.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> --- Documentation/atomic_t.txt | 36 +++++++++++++++++ include/linux/atomic/atomic-arch-fallback.h | 52 ++++++++++++++++++++++++- scripts/atomic/fallbacks/dec_and_test_overflow | 10 ++++ scripts/atomic/fallbacks/dec_overflow | 9 ++++ scripts/atomic/fallbacks/inc_overflow | 9 ++++ 5 files changed, 115 insertions(+), 1 deletion(-)
--- a/Documentation/atomic_t.txt +++ b/Documentation/atomic_t.txt @@ -45,6 +45,14 @@ The 'full' API consists of (atomic64_ an atomic_sub_and_test(), atomic_dec_and_test() +Reference count with overflow (as used by refcount_t): + + atomic_inc_overflow(), atomic_dec_overflow() + atomic_dec_and_test_overflow() + + ATOMIC_OVERFLOW_OFFSET + + Misc: atomic_inc_and_test(), atomic_add_negative() @@ -157,6 +165,34 @@ atomic variable) can be fully ordered an visible. +Overflow ops: + +The atomic_{}_overflow() ops are similar to their !_overflow() bretheren with +two notable exceptions: + + - they take a label as their final argument to to jump to when the atomic op + overflows; + + - the actual value can be offset from 0 in order to allow architectures + to play games with condition flags in order to generate better code. This + offset is ATOMIC_OVERFLOW_OFFSET. + +The canonical overflow conditions are (ATOMIC_OVERFLOW_OFFSET == 0): + + inc: zero or negative on the value pre increment + dec: zero or negative on the value post decrement + dec_and_test: negative on the value post decrement + +This gives an effective range of [0, INT_MIN] for the actual value. When an +architecture uses ATOMIC_OVERFLOW_OFFSET == 1 (x86), the effective range +becomes [-1, INT_MIN], or [0, INT_MIN+1] after correction. + +These semantics match the reference count use-case (for which they were +created). Specifically incrementing from zero is a failure because zero means +the object is freed (IOW use-after-free). Decrementing to zero is a failure +because it goes undetected (see dec_and_test) and the object would leak. + + ORDERING (go read memory-barriers.txt first) -------- --- a/include/linux/atomic/atomic-arch-fallback.h +++ b/include/linux/atomic/atomic-arch-fallback.h @@ -1251,6 +1251,14 @@ arch_atomic_dec_if_positive(atomic_t *v) #endif #ifndef arch_atomic_inc_overflow +/** + * arch_atomic_inc_overflow - increment with overflow exception + * @_v: pointer of type atomic_t + * @_label: label to goto on overflow + * + * Atomically increments @_v and goto @_label when the old + * value (+ATOMIC_OVERFLOW_OFFSET) is negative. + */ #define arch_atomic_inc_overflow(_v, _label) \ do { \ int __old = arch_atomic_fetch_inc(_v); \ @@ -1260,6 +1268,14 @@ do { \ #endif #ifndef arch_atomic_dec_overflow +/** + * arch_atomic_dec_overflow - decrement with overflow exception + * @_v: pointer of type atomic_t + * @_label: label to goto on overflow + * + * Atomically decrements @_v and goto @_label when the + * result (+ATOMIC_OVERFLOW_OFFSET) is negative. + */ #define arch_atomic_dec_overflow(_v, _label) \ do { \ int __new = arch_atomic_dec_return(_v); \ @@ -1269,6 +1285,15 @@ do { \ #endif #ifndef arch_atomic_dec_and_test_overflow +/** + * arch_atomic_dec_and_test_overflow - decrement and test if zero with overflow exception + * @_v: pointer of type atomic_t + * @_label: label to goto on overflow + * + * Atomically decrements @_v and returns true if the result + * (+ATOMIC_OVERFLOW_OFFSET) is zero or goto @_label when the + * result (+ATOMIC_OVERFLOW_OFFSET) is negative. + */ #define arch_atomic_dec_and_test_overflow(_v, _label) \ ({ \ bool __ret = false; \ @@ -2389,6 +2414,14 @@ arch_atomic64_dec_if_positive(atomic64_t #endif #ifndef arch_atomic64_inc_overflow +/** + * arch_atomic64_inc_overflow - increment with overflow exception + * @_v: pointer of type atomic64_t + * @_label: label to goto on overflow + * + * Atomically increments @_v and goto @_label when the old + * value (+ATOMIC64_OVERFLOW_OFFSET) is negative. + */ #define arch_atomic64_inc_overflow(_v, _label) \ do { \ s64 __old = arch_atomic64_fetch_inc(_v); \ @@ -2398,6 +2431,14 @@ do { \ #endif #ifndef arch_atomic64_dec_overflow +/** + * arch_atomic64_dec_overflow - decrement with overflow exception + * @_v: pointer of type atomic64_t + * @_label: label to goto on overflow + * + * Atomically decrements @_v and goto @_label when the + * result (+ATOMIC64_OVERFLOW_OFFSET) is negative. + */ #define arch_atomic64_dec_overflow(_v, _label) \ do { \ s64 __new = arch_atomic64_dec_return(_v); \ @@ -2407,6 +2448,15 @@ do { \ #endif #ifndef arch_atomic64_dec_and_test_overflow +/** + * arch_atomic64_dec_and_test_overflow - decrement and test if zero with overflow exception + * @_v: pointer of type atomic64_t + * @_label: label to goto on overflow + * + * Atomically decrements @_v and returns true if the result + * (+ATOMIC64_OVERFLOW_OFFSET) is zero or goto @_label when the + * result (+ATOMIC64_OVERFLOW_OFFSET) is negative. + */ #define arch_atomic64_dec_and_test_overflow(_v, _label) \ ({ \ bool __ret = false; \ @@ -2420,4 +2470,4 @@ do { \ #endif #endif /* _LINUX_ATOMIC_FALLBACK_H */ -// e4c677b23b3fd5e8dc4bce9d6c055103666cfc4a +// ccccab23ad71e0523949b969f68b40fe6812fc15 --- a/scripts/atomic/fallbacks/dec_and_test_overflow +++ b/scripts/atomic/fallbacks/dec_and_test_overflow @@ -1,4 +1,14 @@ +ATOMIC=`echo ${atomic} | tr '[:lower:]' '[:upper:]'` cat << EOF +/** + * arch_${atomic}_dec_and_test_overflow - decrement and test if zero with overflow exception + * @_v: pointer of type ${atomic}_t + * @_label: label to goto on overflow + * + * Atomically decrements @_v and returns true if the result + * (+${ATOMIC}_OVERFLOW_OFFSET) is zero or goto @_label when the + * result (+${ATOMIC}_OVERFLOW_OFFSET) is negative. + */ #define arch_${atomic}_dec_and_test_overflow(_v, _label) \\ ({ \\ bool __ret = false; \\ --- a/scripts/atomic/fallbacks/dec_overflow +++ b/scripts/atomic/fallbacks/dec_overflow @@ -1,4 +1,13 @@ +ATOMIC=`echo ${atomic} | tr '[:lower:]' '[:upper:]'` cat << EOF +/** + * arch_${atomic}_dec_overflow - decrement with overflow exception + * @_v: pointer of type ${atomic}_t + * @_label: label to goto on overflow + * + * Atomically decrements @_v and goto @_label when the + * result (+${ATOMIC}_OVERFLOW_OFFSET) is negative. + */ #define arch_${atomic}_dec_overflow(_v, _label) \\ do { \\ ${int} __new = arch_${atomic}_dec_return(_v); \\ --- a/scripts/atomic/fallbacks/inc_overflow +++ b/scripts/atomic/fallbacks/inc_overflow @@ -1,4 +1,13 @@ +ATOMIC=`echo ${atomic} | tr '[:lower:]' '[:upper:]'` cat << EOF +/** + * arch_${atomic}_inc_overflow - increment with overflow exception + * @_v: pointer of type ${atomic}_t + * @_label: label to goto on overflow + * + * Atomically increments @_v and goto @_label when the old + * value (+${ATOMIC}_OVERFLOW_OFFSET) is negative. + */ #define arch_${atomic}_inc_overflow(_v, _label) \\ do { \\ ${int} __old = arch_${atomic}_fetch_inc(_v); \\
| |