Linux try_cmpxchg神秘的内联汇编

时间:2017-08-06 16:38:29

标签: linux assembly locking

我需要一些帮助来理解Linux的`try_cmpxchg语义和实现。在内核源代码中,它实现为:

#define __raw_try_cmpxchg(_ptr, _pold, _new, size, lock)        \
({                                                              \
    bool success;                                               \
    __typeof__(_ptr) _old = (_pold);                            \
    __typeof__(*(_ptr)) __old = *_old;                          \
    __typeof__(*(_ptr)) __new = (_new);                         \
    switch (size) {                                             \
    case __X86_CASE_B:                                          \
    {                                                           \
        volatile u8 *__ptr = (volatile u8 *)(_ptr);             \
        asm volatile(lock "cmpxchgb %[new], %[ptr]"             \
                 CC_SET(z)                                      \
                 : CC_OUT(z) (success),                         \
                   [ptr] "+m" (*__ptr),                         \
                   [old] "+a" (__old)                           \
                 : [new] "q" (__new)                            \
                 : "memory");                                   \
        break;                                                  \
    }                                                           \
    case __X86_CASE_W:                                          \
    {                                                           \
        volatile u16 *__ptr = (volatile u16 *)(_ptr);           \
        asm volatile(lock "cmpxchgw %[new], %[ptr]"             \
                 CC_SET(z)                                      \
                 : CC_OUT(z) (success),                         \
                   [ptr] "+m" (*__ptr),                         \
                   [old] "+a" (__old)                           \
                 : [new] "r" (__new)                            \
                 : "memory");                                   \
        break;                                                  \
    }                                                           \
    case __X86_CASE_L:                                          \
    {                                                           \
        volatile u32 *__ptr = (volatile u32 *)(_ptr);           \
        asm volatile(lock "cmpxchgl %[new], %[ptr]"             \
                 CC_SET(z)                                      \
                 : CC_OUT(z) (success),                         \
                   [ptr] "+m" (*__ptr),                         \
                   [old] "+a" (__old)                           \
                 : [new] "r" (__new)                            \
                 : "memory");                                   \
        break;                                                  \
    }                                                           \
    case __X86_CASE_Q:                                          \
    {                                                           \
        volatile u64 *__ptr = (volatile u64 *)(_ptr);           \
        asm volatile(lock "cmpxchgq %[new], %[ptr]"             \
                 CC_SET(z)                                      \
                 : CC_OUT(z) (success),                         \
                   [ptr] "+m" (*__ptr),                         \
                   [old] "+a" (__old)                           \
                 : [new] "r" (__new)                            \
                 : "memory");                                   \
        break;                                                  \
    }                                                           \
    default:                                                    \
        __cmpxchg_wrong_size();                                 \
    }                                                           \
    if (unlikely(!success))                                     \
        *_old = __old;                                          \
    likely(success);                                            \
})

#define __try_cmpxchg(ptr, pold, new, size)     \
    __raw_try_cmpxchg((ptr), (pold), (new), (size), LOCK_PREFIX)

#define try_cmpxchg(ptr, pold, new)                 \
    __try_cmpxchg((ptr), (pold), (new), sizeof(*(ptr)))

我很好奇那些CC_SETCC_OUT意味着什么。它们被定义为:

/*
 * Macros to generate condition code outputs from inline assembly,
 * The output operand must be type "bool".
 */
#ifdef __GCC_ASM_FLAG_OUTPUTS__
# define CC_SET(c) "\n\t/* output condition code " #c "*/\n"
# define CC_OUT(c) "=@cc" #c
#else
# define CC_SET(c) "\n\tset" #c " %[_cc_" #c "]\n"
# define CC_OUT(c) [_cc_ ## c] "=qm"
#endif

另外,如果你能解释try_cmpxchg的确切语义(不太明白原子cmpxchg怎么会失败......),那就太棒了。)

1 个答案:

答案 0 :(得分:3)

较新版本的gcc(我相信版本6)支持特定的标志输出。如果可用,宏将使用此支持,否则通过执行setCC指令和临时输出回退到旧方法。

关于cmpxchg如何"失败":它进行比较,因此如果比较失败则失败,在这种情况下目标不变并且当前值从内存中获取。有关详细信息,请参阅指令集参考。