Semantics and Behavior of Atomic and Bitmask Operations David S. Miller This document is intended to serve as a guide to Linux port maintainers on how to implement atomic counter, bitops, and spinlock interfaces properly. The atomic_t type should be defined as a signed integer and the atomic_long_t type as a signed long integer. Also, they should be made opaque such that any kind of cast to a normal C integer type will fail. Something like the following should suffice: typedef struct { int counter; } atomic_t; typedef struct { long counter; } atomic_long_t; Historically, counter has been declared volatile. This is now discouraged. See Documentation/volatile-considered-harmful.txt for the complete rationale. local_t is very similar to atomic_t. If the counter is per CPU and only updated by one CPU, local_t is probably more appropriate. Please see Documentation/local_ops.txt for the semantics of local_t. The first operations to implement for atomic_t's are the initializers and plain reads. #define ATOMIC_INIT(i) { (i) } #define atomic_set(v, i) ((v)->counter = (i)) The first macro is used in definitions, such as: static atomic_t my_counter = ATOMIC_INIT(1); The initializer is atomic in that the return values of the atomic operations are guaranteed to be correct reflecting the initialized value if the initializer is used before runtime. If the initializer is used at runtime, a proper implicit or explicit read memory barrier is needed before reading the value with atomic_read from another thread. As with all of the atomic_ interfaces, replace the leading "atomic_" with "atomic_long_" to operate on atomic_long_t. The second interface can be used at runtime, as in: struct foo { atomic_t counter; }; ... struct foo *k; k = kmalloc(sizeof(*k), GFP_KERNEL); if (!k) return -ENOMEM; atomic_set(&k->counter, 0); The setting is atomic in that the return values of the atomic operations by all threads are guaranteed to be correct reflecting either the value that has been set with this operation or set with another operation. A proper implicit or explicit memory barrier is needed before the value set with the operation is guaranteed to be readable with atomic_read from another thread. Next, we have: #define atomic_read(v) ((v)->counter) which simply reads the counter value currently visible to the calling thread. The read is atomic in that the return value is guaranteed to be one of the values initialized or modified with the interface operations if a proper implicit or explicit memory barrier is used after possible runtime initialization by any other thread and the value is modified only with the interface operations. atomic_read does not guarantee that the runtime initialization by any other thread is visible yet, so the user of the interface must take care of that with a proper implicit or explicit memory barrier. *** WARNING: atomic_read() and atomic_set() DO NOT IMPLY BARRIERS! *** Some architectures may choose to use the volatile keyword, barriers, or inline assembly to guarantee some degree of immediacy for atomic_read() and atomic_set(). This is not uniformly guaranteed, and may change in the future, so all users of atomic_t should treat atomic_read() and atomic_set() as simple C statements that may be reordered or optimized away entirely by the compiler or processor, and explicitly invoke the appropriate compiler and/or memory barrier for each use case. Failure to do so will result in code that may suddenly break when used with different architectures or compiler optimizations, or even changes in unrelated code which changes how the compiler optimizes the section accessing atomic_t variables. *** YOU HAVE BEEN WARNED! *** Properly aligned pointers, longs, ints, and chars (and unsigned equivalents) may be atomically loaded from and stored to in the same sense as described for atomic_read() and atomic_set(). The ACCESS_ONCE() macro should be used to prevent the compiler from using optimizations that might otherwise optimize accesses out of existence on the one hand, or that might create unsolicited accesses on the other. For example consider the following code: while (a > 0) do_something(); If the compiler can prove that do_something() does not store to the variable a, then the compiler is within its rights transforming this to the following: tmp = a; if (a > 0) for (;;) do_something(); If you don't want the compiler to do this (and you probably don't), then you should use something like the following: while (ACCESS_ONCE(a) < 0) do_something(); Alternatively, you could place a barrier() call in the loop. For another example, consider the following code: tmp_a = a; do_something_with(tmp_a); do_something_else_with(tmp_a); If the compiler can prove that do_something_with() does not store to the variable a, then the compiler is within its rights to manufacture an additional load as follows: tmp_a = a; do_something_with(tmp_a); tmp_a = a; do_something_else_with(tmp_a); This could fatally confuse your code if it expected the same value to be passed to do_something_with() and do_something_else_with(). The compiler would be likely to manufacture this additional load if do_something_with() was an inline function that made very heavy use of registers: reloading from variable a could save a flush to the stack and later reload. To prevent the compiler from attacking your code in this manner, write the following: tmp_a = ACCESS_ONCE(a); do_something_with(tmp_a); do_something_else_with(tmp_a); For a final example, consider the following code, assuming that the variable a is set at boot time before the second CPU is brought online and never changed
# Copyright (c) 2016-2017 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
access-list1:
acl:
access-list-entries:
- ace:
ace-oper-data:
match-counter: 0
actions: drop,count
matches:
destination-ipv4-network: 152.16.40.20/24
destination-port-range:
lower-port: 0
upper-port: 65535
source-ipv4-network: 0.0.0.0/0
source-port-range:
lower-port: 0
upper-port: 65535
rule-name: rule1588
- ace:
ace-oper-data:
match-counter: 0
actions: drop,count
matches:
destination-ipv4-network: 0.0.0.0/0
destination-port-range:
lower-port: 0
upper-port: 65535
source-ipv4-network: 152.16.100.20/24
source-port-range:
lower-port: 0
upper-port: 65535
rule-name: rule1589
acl-name: sample-ipv4-acl
acl-type: ipv4-acl