root/arch/s390/include/asm/percpu.h
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ARCH_S390_PERCPU__
#define __ARCH_S390_PERCPU__

#include <linux/preempt.h>
#include <asm/cmpxchg.h>
#include <asm/march.h>

/*
 * s390 uses its own implementation for per cpu data, the offset of
 * the cpu local data area is cached in the cpu's lowcore memory.
 */
#define __my_cpu_offset get_lowcore()->percpu_offset

#define arch_raw_cpu_ptr(_ptr)                                          \
({                                                                      \
        unsigned long lc_percpu, tcp_ptr__;                             \
                                                                        \
        tcp_ptr__ = (__force unsigned long)(_ptr);                      \
        lc_percpu = offsetof(struct lowcore, percpu_offset);            \
        asm_inline volatile(                                            \
        ALTERNATIVE("ag         %[__ptr__],%[offzero](%%r0)\n",         \
                    "ag         %[__ptr__],%[offalt](%%r0)\n",          \
                    ALT_FEATURE(MFEATURE_LOWCORE))                      \
        : [__ptr__] "+d" (tcp_ptr__)                                    \
        : [offzero] "i" (lc_percpu),                                    \
          [offalt] "i" (lc_percpu + LOWCORE_ALT_ADDRESS),               \
          "m" (((struct lowcore *)0)->percpu_offset)                    \
        : "cc");                                                        \
        (TYPEOF_UNQUAL(*(_ptr)) __force __kernel *)tcp_ptr__;           \
})

/*
 * We use a compare-and-swap loop since that uses less cpu cycles than
 * disabling and enabling interrupts like the generic variant would do.
 */
#define arch_this_cpu_to_op_simple(pcp, val, op)                        \
({                                                                      \
        typedef typeof(pcp) pcp_op_T__;                                 \
        pcp_op_T__ old__, new__, prev__;                                \
        pcp_op_T__ *ptr__;                                              \
        preempt_disable_notrace();                                      \
        ptr__ = raw_cpu_ptr(&(pcp));                                    \
        prev__ = READ_ONCE(*ptr__);                                     \
        do {                                                            \
                old__ = prev__;                                         \
                new__ = old__ op (val);                                 \
                prev__ = cmpxchg(ptr__, old__, new__);                  \
        } while (prev__ != old__);                                      \
        preempt_enable_notrace();                                       \
        new__;                                                          \
})

#define this_cpu_add_1(pcp, val)        arch_this_cpu_to_op_simple(pcp, val, +)
#define this_cpu_add_2(pcp, val)        arch_this_cpu_to_op_simple(pcp, val, +)
#define this_cpu_add_return_1(pcp, val) arch_this_cpu_to_op_simple(pcp, val, +)
#define this_cpu_add_return_2(pcp, val) arch_this_cpu_to_op_simple(pcp, val, +)
#define this_cpu_and_1(pcp, val)        arch_this_cpu_to_op_simple(pcp, val, &)
#define this_cpu_and_2(pcp, val)        arch_this_cpu_to_op_simple(pcp, val, &)
#define this_cpu_or_1(pcp, val)         arch_this_cpu_to_op_simple(pcp, val, |)
#define this_cpu_or_2(pcp, val)         arch_this_cpu_to_op_simple(pcp, val, |)

/*
 * Macros to be used for percpu code section based on atomic instructions.
 *
 * Avoid the need to use preempt_disable() / preempt_disable() pairs and the
 * conditional preempt_schedule_notrace() function calls which come with
 * this. The idea is that this_cpu operations based on atomic instructions are
 * guarded with mviy instructions:
 *
 * - The first mviy instruction writes the register number, which contains the
 *   percpu address variable to lowcore. This also indicates that a percpu
 *   code section is executed.
 *
 * - The first mviy instruction following the mviy instruction must be the ag
 *   instruction which adds the percpu offset to the percpu address register.
 *
 * - Afterwards the atomic percpu operation follows.
 *
 * - Then a second mviy instruction writes a zero to lowcore, which indicates
 *   the end of the percpu code section.
 *
 * - In case of an interrupt/exception/nmi the register number which was
 *   written to lowcore is copied to the exception frame (pt_regs), and a zero
 *   is written to lowcore.
 *
 * - On return to the previous context it is checked if a percpu code section
 *   was executed (saved register number not zero), and if the process was
 *   migrated to a different cpu. If the percpu offset was already added to
 *   the percpu address register (instruction address does _not_ point to the
 *   ag instruction) the content of the percpu address register is adjusted so
 *   it points to percpu variable of the new cpu.
 *
 * Inline assemblies making use of this typically have a code sequence like:
 *
 *   MVIY_PERCPU(...) <- start of percpu code section
 *   AG_ALT(...)      <- add percpu offset; must be the second instruction
 *   atomic_op        <- atomic op
 *   MVIY_ALT(...)    <- end of percpu code section
 */

#define MVIY_PERCPU(disp, dispalt, reg)                                         \
        ".macro GEN_MVIY disp reg\n"                                            \
        ".irp   rs,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15\n"                     \
        "       .ifc \\reg,%%r\\rs\n"                                           \
        "       mviy    \\disp(%%r0),\\rs\n"                                    \
        "       .endif\n"                                                       \
        ".endr\n"                                                               \
        ".endm\n"                                                               \
        ALTERNATIVE("GEN_MVIY " __stringify(disp)    " " __stringify(reg) "\n", \
                    "GEN_MVIY " __stringify(dispalt) " " __stringify(reg) "\n", \
                    ALT_FEATURE(MFEATURE_LOWCORE))                              \
        ".purgem GEN_MVIY\n"

#define MVIY_ALT(disp, dispalt)                                                 \
        ALTERNATIVE("   mviy    " disp    "(%%r0),0\n",                         \
                    "   mviy    " dispalt "(%%r0),0\n",                         \
                    ALT_FEATURE(MFEATURE_LOWCORE))

#define AG_ALT(disp, dispalt, reg)                                              \
        ALTERNATIVE("   ag      " reg ", " disp    "(%%r0)\n",                  \
                    "   ag      " reg ", " dispalt "(%%r0)\n",                  \
                    ALT_FEATURE(MFEATURE_LOWCORE))

#ifndef MARCH_HAS_Z196_FEATURES

#define this_cpu_add_4(pcp, val)        arch_this_cpu_to_op_simple(pcp, val, +)
#define this_cpu_add_8(pcp, val)        arch_this_cpu_to_op_simple(pcp, val, +)
#define this_cpu_add_return_4(pcp, val) arch_this_cpu_to_op_simple(pcp, val, +)
#define this_cpu_add_return_8(pcp, val) arch_this_cpu_to_op_simple(pcp, val, +)
#define this_cpu_and_4(pcp, val)        arch_this_cpu_to_op_simple(pcp, val, &)
#define this_cpu_and_8(pcp, val)        arch_this_cpu_to_op_simple(pcp, val, &)
#define this_cpu_or_4(pcp, val)         arch_this_cpu_to_op_simple(pcp, val, |)
#define this_cpu_or_8(pcp, val)         arch_this_cpu_to_op_simple(pcp, val, |)

#else /* MARCH_HAS_Z196_FEATURES */

#define arch_this_cpu_add(pcp, val, op1, op2, szcast)                           \
do {                                                                            \
        unsigned long lc_pcpr, lc_pcpo;                                         \
        typedef typeof(pcp) pcp_op_T__;                                         \
        pcp_op_T__ val__ = (val);                                               \
        pcp_op_T__ old__, *ptr__;                                               \
                                                                                \
        lc_pcpr = offsetof(struct lowcore, percpu_register);                    \
        lc_pcpo = offsetof(struct lowcore, percpu_offset);                      \
        ptr__ = PERCPU_PTR(&(pcp));                                             \
        if (__builtin_constant_p(val__) &&                                      \
            ((szcast)val__ > -129) && ((szcast)val__ < 128)) {                  \
                asm volatile(                                                   \
                        MVIY_PERCPU("%[disppcpr]", "%[dispaltpcpr]", "%[ptr__]")\
                        AG_ALT("%[disppcpo]", "%[dispaltpcpo]", "%[ptr__]")     \
                        op2 "   0(%[ptr__]),%[val__]\n"                         \
                        MVIY_ALT("%[disppcpr]", "%[dispaltpcpr]")               \
                        : [ptr__] "+&a" (ptr__), "+m" (*ptr__),                 \
                          "=m" (((struct lowcore *)0)->percpu_register)         \
                        : [val__] "i" ((szcast)val__),                          \
                          [disppcpr] "i" (lc_pcpr),                             \
                          [disppcpo] "i" (lc_pcpo),                             \
                          [dispaltpcpr] "i" (lc_pcpr + LOWCORE_ALT_ADDRESS),    \
                          [dispaltpcpo] "i" (lc_pcpo + LOWCORE_ALT_ADDRESS),    \
                          "m" (((struct lowcore *)0)->percpu_offset)            \
                        : "cc");                                                \
        } else {                                                                \
                asm volatile(                                                   \
                        MVIY_PERCPU("%[disppcpr]", "%[dispaltpcpr]", "%[ptr__]")\
                        AG_ALT("%[disppcpo]", "%[dispaltpcpo]", "%[ptr__]")     \
                        op1 "   %[old__],%[val__],0(%[ptr__])\n"                \
                        MVIY_ALT("%[disppcpr]", "%[dispaltpcpr]")               \
                        : [old__] "=&d" (old__),                                \
                          [ptr__] "+&a" (ptr__),  "+m" (*ptr__),                \
                          "=m" (((struct lowcore *)0)->percpu_register)         \
                        : [val__] "d" (val__),                                  \
                          [disppcpr] "i" (lc_pcpr),                             \
                          [disppcpo] "i" (lc_pcpo),                             \
                          [dispaltpcpr] "i" (lc_pcpr + LOWCORE_ALT_ADDRESS),    \
                          [dispaltpcpo] "i" (lc_pcpo + LOWCORE_ALT_ADDRESS),    \
                          "m" (((struct lowcore *)0)->percpu_offset)            \
                        : "cc");                                                \
        }                                                                       \
} while (0)

#define this_cpu_add_4(pcp, val) arch_this_cpu_add(pcp, val, "laa", "asi", int)
#define this_cpu_add_8(pcp, val) arch_this_cpu_add(pcp, val, "laag", "agsi", long)

#define arch_this_cpu_add_return(pcp, val, op)                          \
({                                                                      \
        unsigned long lc_pcpr, lc_pcpo;                                 \
        typedef typeof(pcp) pcp_op_T__;                                 \
        pcp_op_T__ val__ = (val);                                       \
        pcp_op_T__ old__, *ptr__;                                       \
                                                                        \
        lc_pcpr = offsetof(struct lowcore, percpu_register);            \
        lc_pcpo = offsetof(struct lowcore, percpu_offset);              \
        ptr__ = PERCPU_PTR(&(pcp));                                     \
        asm_inline volatile(                                            \
                MVIY_PERCPU("%[disppcpr]", "%[dispaltpcpr]", "%[ptr__]")\
                AG_ALT("%[disppcpo]", "%[dispaltpcpo]", "%[ptr__]")     \
                op "    %[old__],%[val__],0(%[ptr__])\n"                \
                MVIY_ALT("%[disppcpr]", "%[dispaltpcpr]")               \
                : [old__] "=&d" (old__),                                \
                  [ptr__] "+&a" (ptr__), "+m" (*ptr__),                 \
                  "=m" (((struct lowcore *)0)->percpu_register)         \
                : [val__] "d" (val__),                                  \
                  [disppcpr] "i" (lc_pcpr),                             \
                  [disppcpo] "i" (lc_pcpo),                             \
                  [dispaltpcpr] "i" (lc_pcpr + LOWCORE_ALT_ADDRESS),    \
                  [dispaltpcpo] "i" (lc_pcpo + LOWCORE_ALT_ADDRESS),    \
                  "m" (((struct lowcore *)0)->percpu_offset)            \
                : "cc");                                                \
        old__ + val__;                                                  \
})

#define this_cpu_add_return_4(pcp, val) arch_this_cpu_add_return(pcp, val, "laa")
#define this_cpu_add_return_8(pcp, val) arch_this_cpu_add_return(pcp, val, "laag")

#define arch_this_cpu_to_op(pcp, val, op)                               \
do {                                                                    \
        unsigned long lc_pcpr, lc_pcpo;                                 \
        typedef typeof(pcp) pcp_op_T__;                                 \
        pcp_op_T__ val__ = (val);                                       \
        pcp_op_T__ old__, *ptr__;                                       \
                                                                        \
        lc_pcpr = offsetof(struct lowcore, percpu_register);            \
        lc_pcpo = offsetof(struct lowcore, percpu_offset);              \
        ptr__ = PERCPU_PTR(&(pcp));                                     \
        asm_inline volatile(                                            \
                MVIY_PERCPU("%[disppcpr]", "%[dispaltpcpr]", "%[ptr__]")\
                AG_ALT("%[disppcpo]", "%[dispaltpcpo]", "%[ptr__]")     \
                op "    %[old__],%[val__],0(%[ptr__])\n"                \
                MVIY_ALT("%[disppcpr]", "%[dispaltpcpr]")               \
                : [old__] "=&d" (old__),                                \
                  [ptr__] "+&a" (ptr__), "+m" (*ptr__),                 \
                  "=m" (((struct lowcore *)0)->percpu_register)         \
                : [val__] "d" (val__),                                  \
                  [disppcpr] "i" (lc_pcpr),                             \
                  [disppcpo] "i" (lc_pcpo),                             \
                  [dispaltpcpr] "i" (lc_pcpr + LOWCORE_ALT_ADDRESS),    \
                  [dispaltpcpo] "i" (lc_pcpo + LOWCORE_ALT_ADDRESS),    \
                  "m" (((struct lowcore *)0)->percpu_offset)            \
                : "cc");                                                \
} while (0)

#define this_cpu_and_4(pcp, val)        arch_this_cpu_to_op(pcp, val, "lan")
#define this_cpu_and_8(pcp, val)        arch_this_cpu_to_op(pcp, val, "lang")
#define this_cpu_or_4(pcp, val)         arch_this_cpu_to_op(pcp, val, "lao")
#define this_cpu_or_8(pcp, val)         arch_this_cpu_to_op(pcp, val, "laog")

#endif /* MARCH_HAS_Z196_FEATURES */

#define arch_this_cpu_read(pcp, op)                                     \
({                                                                      \
        unsigned long lc_pcpr, lc_pcpo, res__;                          \
        typedef typeof(pcp) pcp_op_T__;                                 \
        pcp_op_T__ *ptr__;                                              \
                                                                        \
        lc_pcpr = offsetof(struct lowcore, percpu_register);            \
        lc_pcpo = offsetof(struct lowcore, percpu_offset);              \
        ptr__ = PERCPU_PTR(&(pcp));                                     \
        asm_inline volatile(                                            \
                MVIY_PERCPU("%[disppcpr]", "%[dispaltpcpr]", "%[ptr__]")\
                AG_ALT("%[disppcpo]", "%[dispaltpcpo]", "%[ptr__]")     \
                op "    %[res__],0(%[ptr__])\n"                         \
                MVIY_ALT("%[disppcpr]", "%[dispaltpcpr]")               \
                : [res__] "=&d" (res__), [ptr__] "+&a" (ptr__),         \
                  "=m" (((struct lowcore *)0)->percpu_register)         \
                : [disppcpr] "i" (lc_pcpr),                             \
                  [disppcpo] "i" (lc_pcpo),                             \
                  [dispaltpcpr] "i" (lc_pcpr + LOWCORE_ALT_ADDRESS),    \
                  [dispaltpcpo] "i" (lc_pcpo + LOWCORE_ALT_ADDRESS),    \
                  "m" (*ptr__),                                         \
                  "m" (((struct lowcore *)0)->percpu_offset)            \
                : "cc");                                                \
        (pcp_op_T__)res__;                                              \
})

#define this_cpu_read_1(pcp) arch_this_cpu_read(pcp, "llgc")
#define this_cpu_read_2(pcp) arch_this_cpu_read(pcp, "llgh")
#define this_cpu_read_4(pcp) arch_this_cpu_read(pcp, "llgf")
#define this_cpu_read_8(pcp) arch_this_cpu_read(pcp, "lg")

#define arch_this_cpu_write(pcp, val, op)                               \
do {                                                                    \
        unsigned long lc_pcpr, lc_pcpo;                                 \
        typedef typeof(pcp) pcp_op_T__;                                 \
        pcp_op_T__ *ptr__, val__ = (val);                               \
                                                                        \
        lc_pcpr = offsetof(struct lowcore, percpu_register);            \
        lc_pcpo = offsetof(struct lowcore, percpu_offset);              \
        ptr__ = PERCPU_PTR(&(pcp));                                     \
        asm_inline volatile(                                            \
                MVIY_PERCPU("%[disppcpr]", "%[dispaltpcpr]", "%[ptr__]")\
                AG_ALT("%[disppcpo]", "%[dispaltpcpo]", "%[ptr__]")     \
                op "    %[val__],0(%[ptr__])\n"                         \
                MVIY_ALT("%[disppcpr]", "%[dispaltpcpr]")               \
                : [ptr__] "+&a" (ptr__), "=m" (*ptr__),                 \
                  "=m" (((struct lowcore *)0)->percpu_register)         \
                : [val__] "d" (val__),                                  \
                  [disppcpr] "i" (lc_pcpr),                             \
                  [disppcpo] "i" (lc_pcpo),                             \
                  [dispaltpcpr] "i" (lc_pcpr + LOWCORE_ALT_ADDRESS),    \
                  [dispaltpcpo] "i" (lc_pcpo + LOWCORE_ALT_ADDRESS),    \
                  "m" (((struct lowcore *)0)->percpu_offset)            \
                : "cc");                                                \
} while (0)

#define this_cpu_write_1(pcp, val) arch_this_cpu_write(pcp, val, "stc")
#define this_cpu_write_2(pcp, val) arch_this_cpu_write(pcp, val, "sth")
#define this_cpu_write_4(pcp, val) arch_this_cpu_write(pcp, val, "st")
#define this_cpu_write_8(pcp, val) arch_this_cpu_write(pcp, val, "stg")

#define arch_this_cpu_cmpxchg(pcp, oval, nval)                          \
({                                                                      \
        typedef typeof(pcp) pcp_op_T__;                                 \
        pcp_op_T__ ret__;                                               \
        pcp_op_T__ *ptr__;                                              \
        preempt_disable_notrace();                                      \
        ptr__ = raw_cpu_ptr(&(pcp));                                    \
        ret__ = cmpxchg(ptr__, oval, nval);                             \
        preempt_enable_notrace();                                       \
        ret__;                                                          \
})

#define this_cpu_cmpxchg_1(pcp, oval, nval) arch_this_cpu_cmpxchg(pcp, oval, nval)
#define this_cpu_cmpxchg_2(pcp, oval, nval) arch_this_cpu_cmpxchg(pcp, oval, nval)
#define this_cpu_cmpxchg_4(pcp, oval, nval) arch_this_cpu_cmpxchg(pcp, oval, nval)
#define this_cpu_cmpxchg_8(pcp, oval, nval) arch_this_cpu_cmpxchg(pcp, oval, nval)

#define this_cpu_cmpxchg64(pcp, o, n)   this_cpu_cmpxchg_8(pcp, o, n)

#define this_cpu_cmpxchg128(pcp, oval, nval)                            \
({                                                                      \
        typedef typeof(pcp) pcp_op_T__;                                 \
        u128 old__, new__, ret__;                                       \
        pcp_op_T__ *ptr__;                                              \
        old__ = oval;                                                   \
        new__ = nval;                                                   \
        preempt_disable_notrace();                                      \
        ptr__ = raw_cpu_ptr(&(pcp));                                    \
        ret__ = cmpxchg128((void *)ptr__, old__, new__);                \
        preempt_enable_notrace();                                       \
        ret__;                                                          \
})

#define arch_this_cpu_xchg(pcp, nval)                                   \
({                                                                      \
        typeof(pcp) *ptr__;                                             \
        typeof(pcp) ret__;                                              \
        preempt_disable_notrace();                                      \
        ptr__ = raw_cpu_ptr(&(pcp));                                    \
        ret__ = xchg(ptr__, nval);                                      \
        preempt_enable_notrace();                                       \
        ret__;                                                          \
})

#define this_cpu_xchg_1(pcp, nval) arch_this_cpu_xchg(pcp, nval)
#define this_cpu_xchg_2(pcp, nval) arch_this_cpu_xchg(pcp, nval)
#define this_cpu_xchg_4(pcp, nval) arch_this_cpu_xchg(pcp, nval)
#define this_cpu_xchg_8(pcp, nval) arch_this_cpu_xchg(pcp, nval)

#include <asm-generic/percpu.h>

#endif /* __ARCH_S390_PERCPU__ */