linux代码之atomic

/*
 * 2020/12/4    15:07    qing
 *
 * 原子操作需要靠硬件实现
 */

/*
 * atomic
 */
Linux内核中提供了各种各样的原子操作函数。除了最原始的读取和设置之外,还包括各种运算,以及位操作等等。而且有的原子操作还要返回操作过后变量的值,
有的要返回操作之前变量的值,如果再牵涉到内存屏障的问题,将这些因素组合起来,有非常多的原子操作函数。

/*
 * Bus Lock(锁总线)
 */

    CPU执行原子指令时,给总线上锁,这样在释放前,可以防止其它CPU的内存操作。

/*
 * Cache Lock
 */

    除了和IO紧密相关的(如MMIO),大部分的内存都是可以被cache的,由前面介绍的cache一致性原理,我们知道由cacheline处于Exclusive或Modified时,
    该变量只有当前CPU缓存了数据,因此当进行原子操作时,发出Read Invalidate消息,使其它CPU上的缓存无效,cacheline变成Exclusive状态然后将该

    cacheline上锁,接着就可以取数据,修改并写入cacheline,如果这时有其它CPU也进行原子操作,发出read invalidate消息,但由于当前CPU的
    cacheline是locked状态,因此暂时不会回复消息,这样其它CPU就一直在等待,直到当前CPU完成,使cacheline变为unlocked状态。

/*
 * 基本型
 */

    基本型包括非RMW(Read Modifiy Write)的读写操作,以及RMW的算术和位操作等。

    非RMW的操作很简单,只有两个,即用来读取的atomic_read()和用来写入的atomic_set()。
    一般对单独变量的读取或写入操作本身都是原子的,如果代码中只对单个变量进行读写操作,而从来没对它使用RMW操作,那一般说明你用错了,
    没必要使用内核提供的原子操作。

    算术操作:包括加、减、递增和递减,命名形式是atomic_{add,sub,inc,dec}();
    Bit位操作:包括与、或、异或和与非,命名形式是atomic_{and,or,xor,andnot}();
    交换操作:包括交换(atomic_xchg())、比较交换(atomic_cmpxchg())和添加了返回成功与否的比较交换(atomic_try_cmpxchg())。

    除了atomic_t类型外,Linux内核还支持对64位和长整形的atomic64_t和atomic_long_t类型,它们也都有对应的基本原子操作函数,
    只不过函数前缀名分别是atomic64_和atomic_long_

static inline void atomic_add(int i, atomic_t *v)
{
    atomic_add_return(i, v);
}

#ifndef atomic_add_return
#define  atomic_add_return(...)                        \
    __atomic_op_fence(atomic_add_return, __VA_ARGS__)
#endif

#ifndef __atomic_op_fence
#define __atomic_op_fence(op, args...)                    \
({                                    \
    typeof(op##_relaxed(args)) __ret;                \
    smp_mb__before_atomic();                    \
    __ret = op##_relaxed(args);                    \
    smp_mb__after_atomic();                        \
    __ret;                                \
})
#endif


/*
 * 返回修改过后的值
 */

    如果一个原子操作函数要返回修改过后的原子变量的值,那么该函数名会含有“_return”字符串,并且是在表示具体操作字符串的后面。
    
    例如,atomic_add_return、atomic_dec_return_relaxed等。


/*
 * 要返回修改之前的值
 */

    如果一个原子操作函数要返回修改之前的原子变量的值,那么该函数名会含有“_fetch”字符串,并且是在表示具体操作字符串的前面。
    
    例如,atomic_fetch_and、atomic_fetch_or_acquire等。


/*
 * 有Acquire和Release单向屏障语义
 */

    如果一个原子操作函数还需要包括Acquire或者Release单向屏障语义,那么该函数名会有“_acquire”或者“_release”后缀。
    
    例如,atomic_xchg_acquire、atomic_cmpxchg_release等。


    相反的,如果一个原子操作函数名有“_relaxed”后缀,表示这个函数没有被任何内存屏障保护,可以被任意重排序。
    
    例如,atomic_add_return_relaxed、atomic_xchg_relaxed等。


    只有当一个原子操作函数要返回值的时候才有可能添加_acquire、_release和_relaxed后缀。


/*
 * 原子操作的重排序规则
 */

    Linux内核的原子操作只保证对单一变量的某个操作是原子的,多个CPU同时操作时,不会出现中间的错误状态。但是,对这个原子操作本身,
    并不一定保证其执行的顺序,在SMP系统下,有可能会出现重排序的问题。
    
    因此,前面也提到过,有些原子操作函数自己就带了一定的内存屏障的语义。具体有没有带,带了多少,可以通过函数名看出来,具体规则如下:

    非RMW的原子操作可以被任意重排序;

    RMW的原子操作,如果没有返回值可以被任意重排序;
    RMW的原子操作,如果有返回值,并且没有_relaxed、_acquire和_release后缀,是有内存屏障保护的,可以保证不被重排序;
    RMW的原子操作,如果有返回值,且有_relaxed后缀,没有任何内存屏障保护,可以被任意重排序;
    RMW的原子操作,如果有返回值,且有_acquire后缀,表示读(RMW中的R)操作是有Acquire单向屏障保护的;
    RMW的原子操作,如果有返回值,且有_release后缀,表示写(RMW中的W)操作是有Release单向屏障保护的;
    RMW的原子操作,如果有条件判断,那么条件是否的那部分会被任意重排序。

    对于那些不提供内存屏障语义的原子操作来说,为了保证其本身不被重排序,还需要显式的在其前面或后面使用内存屏障。
    Linux内核提供了两个函数,分别是smp_mb__before_atomic()用于原子操作函数的前面,和smp_mb__after_atomic()用于原子操作函数的后面。

/*
 * ARMv8架构下原子操作的实现
 */

#define ATOMIC_INIT(i)    { (i) }

 /* atomic_read  */

#define atomic_read(v)            READ_ONCE((v)->counter)

#define __READ_ONCE(x, check)                        \
({                                    \
    union { typeof(x) __val; char __c[1]; } __u;            \
    if (check)                            \
        __read_once_size(&(x), __u.__c, sizeof(x));        \
    else                                \
        __read_once_size_nocheck(&(x), __u.__c, sizeof(x));    \
    __u.__val;                            \
})
#define READ_ONCE(x) __READ_ONCE(x, 1)

static __always_inline
void __read_once_size(const volatile void *p, void *res, int size)
{
    __READ_ONCE_SIZE;
}

#define __READ_ONCE_SIZE                        \
({                                    \
    switch (size) {                            \
    case 1: *(__u8 *)res = *(volatile __u8 *)p; break;        \
    case 2: *(__u16 *)res = *(volatile __u16 *)p; break;        \
    case 4: *(__u32 *)res = *(volatile __u32 *)p; break;        \
    case 8: *(__u64 *)res = *(volatile __u64 *)p; break;        \
    default:                            \
        barrier();                        \
        __builtin_memcpy((void *)res, (const void *)p, size);    \
        barrier();                        \
    }                                \
})

/* atomic_set */
#define atomic_set(v, i)        WRITE_ONCE(((v)->counter), (i))

#define WRITE_ONCE(x, val) \
({                            \
    union { typeof(x) __val; char __c[1]; } __u =    \
        { .__val = (__force typeof(x)) (val) }; \
    __write_once_size(&(x), __u.__c, sizeof(x));    \
    __u.__val;                    \
})

static __always_inline void __write_once_size(volatile void *p, void *res, int size)
{
    switch (size) {
    case 1: *(volatile __u8 *)p = *(__u8 *)res; break;
    case 2: *(volatile __u16 *)p = *(__u16 *)res; break;
    case 4: *(volatile __u32 *)p = *(__u32 *)res; break;
    case 8: *(volatile __u64 *)p = *(__u64 *)res; break;
    default:
        barrier();
        __builtin_memcpy((void *)p, (const void *)res, size);
        barrier();
    }
}

/*
 * 废弃的函数,学习一下C语法 及 指令
 */

     static inline int
    __ll_sc_atomic_add_return_acquire(int i, atomic_t *v)
    {
        unsigned long tmp;
        int result;
     
        asm volatile(
    "       // 将v->counter预取到CPU缓存\n"                                   \
    "    prfm    pstl1strm, %2\n"                    \
    "       // 将v->counter独占的读入result中\n"                              \
    "1:    ldaxr    %w0, %2\n"                            \
    "       // result = result + i\n"                                       \
    "    add    %w0, %w0, %w3\n"                                \
    "       // 将result的值独占的存入v->counter中\n"                           \
    "       // 如果存入时独占标记被清除则将tmp置1\n"                             \
    "    stxr    %w1, %w0, %2\n"                            \
    "       // 如果tmp被置1则从头再次执行一遍\n"                                \
    "    cbnz    %w1, 1b\n"                        \
            )                                \
        : "=&r" (result), "=&r" (tmp), "+Q" (v->counter)        \
        : __stringify(I) "r" (i)                        \
        : "memory");                                    \
                                        \
        return result;                            \
    }

    static inline int                            \
    __ll_sc_atomic_fetch_or_release(int i, atomic_t *v)            \
    {                                    \
        unsigned long tmp;                        \
        int val, result;                        \
                                        \
        asm volatile(                                            \
    "       // 将v->counter预取到CPU缓存\n"                                   \
    "    prfm    pstl1strm, %3\n"                    \
    "       // 将v->counter独占的读入result中\n"                              \
    "1:    ldxr    %w0, %3\n"                            \
    "       // val = result | i\n"                                          \
    "    orr    %w1, %w0, %w4\n"                        \
    "       // 将val的值独占的存入v->counter中\n"                              \
    "       // 如果存入时独占标记被清除则将tmp置1\n"                             \
    "    stlxr    %w2, %w1, %3\n"                            \
    "       // 如果tmp被置1则从头再次执行一遍\n"                                \
    "    cbnz    %w2, 1b\n"                        \
        : "=&r" (result), "=&r" (val), "=&r" (tmp), "+Q" (v->counter)    \
        : __stringify(K) "r" (i)                        \
        : "memory");                            \
                                        \
        return result;                            \
    }

/* arch_cmpxchg */
#define __cmpxchg_wrapper(sfx, ptr, o, n)                \
({                                    \
    __typeof__(*(ptr)) __ret;                    \
    __ret = (__typeof__(*(ptr)))                    \
        __cmpxchg##sfx((ptr), (unsigned long)(o),        \
                (unsigned long)(n), sizeof(*(ptr)));    \
    __ret;                                \
})

#define arch_cmpxchg(...)        __cmpxchg_wrapper( _mb, __VA_ARGS__)

arch_cmpxchg最终被宏定义成了__cmpxchg_wrapper,而它又调用了__cmpxchg_mb函数:

#define __CMPXCHG_GEN(sfx)                        \
static __always_inline unsigned long __cmpxchg##sfx(volatile void *ptr,    \
                       unsigned long old,        \
                       unsigned long new,        \
                       int size)            \
{                                    \
    switch (size) {                            \
    case 1:                                \
        return __cmpxchg_case##sfx##_8(ptr, old, new);        \
    case 2:                                \
        return __cmpxchg_case##sfx##_16(ptr, old, new);        \
    case 4:                                \
        return __cmpxchg_case##sfx##_32(ptr, old, new);        \
    case 8:                                \
        return __cmpxchg_case##sfx##_64(ptr, old, new);        \
    default:                            \
        BUILD_BUG();                        \
    }                                \
                                    \
    unreachable();                            \
}
 
__CMPXCHG_GEN()
__CMPXCHG_GEN(_acq)
__CMPXCHG_GEN(_rel)
__CMPXCHG_GEN(_mb)
 
#undef __CMPXCHG_GEN

猜你喜欢

转载自blog.csdn.net/xiaozhiwise/article/details/111215857