Back to home page

OSCL-LXR

 
 

    


0001 
0002 .. _local_ops:
0003 
0004 =================================================
0005 Semantics and Behavior of Local Atomic Operations
0006 =================================================
0007 
0008 :Author: Mathieu Desnoyers
0009 
0010 
0011 This document explains the purpose of the local atomic operations, how
0012 to implement them for any given architecture and shows how they can be used
0013 properly. It also stresses on the precautions that must be taken when reading
0014 those local variables across CPUs when the order of memory writes matters.
0015 
0016 .. note::
0017 
0018     Note that ``local_t`` based operations are not recommended for general
0019     kernel use. Please use the ``this_cpu`` operations instead unless there is
0020     really a special purpose. Most uses of ``local_t`` in the kernel have been
0021     replaced by ``this_cpu`` operations. ``this_cpu`` operations combine the
0022     relocation with the ``local_t`` like semantics in a single instruction and
0023     yield more compact and faster executing code.
0024 
0025 
0026 Purpose of local atomic operations
0027 ==================================
0028 
0029 Local atomic operations are meant to provide fast and highly reentrant per CPU
0030 counters. They minimize the performance cost of standard atomic operations by
0031 removing the LOCK prefix and memory barriers normally required to synchronize
0032 across CPUs.
0033 
0034 Having fast per CPU atomic counters is interesting in many cases: it does not
0035 require disabling interrupts to protect from interrupt handlers and it permits
0036 coherent counters in NMI handlers. It is especially useful for tracing purposes
0037 and for various performance monitoring counters.
0038 
0039 Local atomic operations only guarantee variable modification atomicity wrt the
0040 CPU which owns the data. Therefore, care must taken to make sure that only one
0041 CPU writes to the ``local_t`` data. This is done by using per cpu data and
0042 making sure that we modify it from within a preemption safe context. It is
0043 however permitted to read ``local_t`` data from any CPU: it will then appear to
0044 be written out of order wrt other memory writes by the owner CPU.
0045 
0046 
0047 Implementation for a given architecture
0048 =======================================
0049 
0050 It can be done by slightly modifying the standard atomic operations: only
0051 their UP variant must be kept. It typically means removing LOCK prefix (on
0052 i386 and x86_64) and any SMP synchronization barrier. If the architecture does
0053 not have a different behavior between SMP and UP, including
0054 ``asm-generic/local.h`` in your architecture's ``local.h`` is sufficient.
0055 
0056 The ``local_t`` type is defined as an opaque ``signed long`` by embedding an
0057 ``atomic_long_t`` inside a structure. This is made so a cast from this type to
0058 a ``long`` fails. The definition looks like::
0059 
0060     typedef struct { atomic_long_t a; } local_t;
0061 
0062 
0063 Rules to follow when using local atomic operations
0064 ==================================================
0065 
0066 * Variables touched by local ops must be per cpu variables.
0067 * *Only* the CPU owner of these variables must write to them.
0068 * This CPU can use local ops from any context (process, irq, softirq, nmi, ...)
0069   to update its ``local_t`` variables.
0070 * Preemption (or interrupts) must be disabled when using local ops in
0071   process context to make sure the process won't be migrated to a
0072   different CPU between getting the per-cpu variable and doing the
0073   actual local op.
0074 * When using local ops in interrupt context, no special care must be
0075   taken on a mainline kernel, since they will run on the local CPU with
0076   preemption already disabled. I suggest, however, to explicitly
0077   disable preemption anyway to make sure it will still work correctly on
0078   -rt kernels.
0079 * Reading the local cpu variable will provide the current copy of the
0080   variable.
0081 * Reads of these variables can be done from any CPU, because updates to
0082   "``long``", aligned, variables are always atomic. Since no memory
0083   synchronization is done by the writer CPU, an outdated copy of the
0084   variable can be read when reading some *other* cpu's variables.
0085 
0086 
0087 How to use local atomic operations
0088 ==================================
0089 
0090 ::
0091 
0092     #include <linux/percpu.h>
0093     #include <asm/local.h>
0094 
0095     static DEFINE_PER_CPU(local_t, counters) = LOCAL_INIT(0);
0096 
0097 
0098 Counting
0099 ========
0100 
0101 Counting is done on all the bits of a signed long.
0102 
0103 In preemptible context, use ``get_cpu_var()`` and ``put_cpu_var()`` around
0104 local atomic operations: it makes sure that preemption is disabled around write
0105 access to the per cpu variable. For instance::
0106 
0107     local_inc(&get_cpu_var(counters));
0108     put_cpu_var(counters);
0109 
0110 If you are already in a preemption-safe context, you can use
0111 ``this_cpu_ptr()`` instead::
0112 
0113     local_inc(this_cpu_ptr(&counters));
0114 
0115 
0116 
0117 Reading the counters
0118 ====================
0119 
0120 Those local counters can be read from foreign CPUs to sum the count. Note that
0121 the data seen by local_read across CPUs must be considered to be out of order
0122 relatively to other memory writes happening on the CPU that owns the data::
0123 
0124     long sum = 0;
0125     for_each_online_cpu(cpu)
0126             sum += local_read(&per_cpu(counters, cpu));
0127 
0128 If you want to use a remote local_read to synchronize access to a resource
0129 between CPUs, explicit ``smp_wmb()`` and ``smp_rmb()`` memory barriers must be used
0130 respectively on the writer and the reader CPUs. It would be the case if you use
0131 the ``local_t`` variable as a counter of bytes written in a buffer: there should
0132 be a ``smp_wmb()`` between the buffer write and the counter increment and also a
0133 ``smp_rmb()`` between the counter read and the buffer read.
0134 
0135 
0136 Here is a sample module which implements a basic per cpu counter using
0137 ``local.h``::
0138 
0139     /* test-local.c
0140      *
0141      * Sample module for local.h usage.
0142      */
0143 
0144 
0145     #include <asm/local.h>
0146     #include <linux/module.h>
0147     #include <linux/timer.h>
0148 
0149     static DEFINE_PER_CPU(local_t, counters) = LOCAL_INIT(0);
0150 
0151     static struct timer_list test_timer;
0152 
0153     /* IPI called on each CPU. */
0154     static void test_each(void *info)
0155     {
0156             /* Increment the counter from a non preemptible context */
0157             printk("Increment on cpu %d\n", smp_processor_id());
0158             local_inc(this_cpu_ptr(&counters));
0159 
0160             /* This is what incrementing the variable would look like within a
0161              * preemptible context (it disables preemption) :
0162              *
0163              * local_inc(&get_cpu_var(counters));
0164              * put_cpu_var(counters);
0165              */
0166     }
0167 
0168     static void do_test_timer(unsigned long data)
0169     {
0170             int cpu;
0171 
0172             /* Increment the counters */
0173             on_each_cpu(test_each, NULL, 1);
0174             /* Read all the counters */
0175             printk("Counters read from CPU %d\n", smp_processor_id());
0176             for_each_online_cpu(cpu) {
0177                     printk("Read : CPU %d, count %ld\n", cpu,
0178                             local_read(&per_cpu(counters, cpu)));
0179             }
0180             mod_timer(&test_timer, jiffies + 1000);
0181     }
0182 
0183     static int __init test_init(void)
0184     {
0185             /* initialize the timer that will increment the counter */
0186             timer_setup(&test_timer, do_test_timer, 0);
0187             mod_timer(&test_timer, jiffies + 1);
0188 
0189             return 0;
0190     }
0191 
0192     static void __exit test_exit(void)
0193     {
0194             del_timer_sync(&test_timer);
0195     }
0196 
0197     module_init(test_init);
0198     module_exit(test_exit);
0199 
0200     MODULE_LICENSE("GPL");
0201     MODULE_AUTHOR("Mathieu Desnoyers");
0202     MODULE_DESCRIPTION("Local Atomic Ops");