Re: [PATCH V2 7/7] MIPS: Loongson: Introduce and use LOONGSON_LLSC_WAR

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Should all CPU with
     defined(CONFIG_WEAK_REORDERING_BEYOND_LLSC) && defined(CONFIG_SMP)
do the same thing?

On Mon, Nov 14, 2016 at 11:12 AM, Huacai Chen <chenhc@xxxxxxxxxx> wrote:
> On the Loongson-2G/2H/3A/3B there is a hardware flaw that ll/sc and
> lld/scd is very weak ordering. We should add sync instructions before
> each ll/lld and after the last sc/scd to workaround. Otherwise, this
> flaw will cause deadlock occationally (e.g. when doing heavy load test
> with LTP).
>
> Signed-off-by: Huacai Chen <chenhc@xxxxxxxxxx>
> ---
>  arch/mips/include/asm/atomic.h                 | 107 ++++++++++
>  arch/mips/include/asm/bitops.h                 | 273 +++++++++++++++++++------
>  arch/mips/include/asm/cmpxchg.h                |  54 +++++
>  arch/mips/include/asm/edac.h                   |  33 ++-
>  arch/mips/include/asm/futex.h                  |  62 ++++++
>  arch/mips/include/asm/local.h                  |  34 +++
>  arch/mips/include/asm/mach-cavium-octeon/war.h |   1 +
>  arch/mips/include/asm/mach-generic/war.h       |   1 +
>  arch/mips/include/asm/mach-ip22/war.h          |   1 +
>  arch/mips/include/asm/mach-ip27/war.h          |   1 +
>  arch/mips/include/asm/mach-ip28/war.h          |   1 +
>  arch/mips/include/asm/mach-ip32/war.h          |   1 +
>  arch/mips/include/asm/mach-loongson64/war.h    |  26 +++
>  arch/mips/include/asm/mach-malta/war.h         |   1 +
>  arch/mips/include/asm/mach-pmcs-msp71xx/war.h  |   1 +
>  arch/mips/include/asm/mach-rc32434/war.h       |   1 +
>  arch/mips/include/asm/mach-rm/war.h            |   1 +
>  arch/mips/include/asm/mach-sibyte/war.h        |   1 +
>  arch/mips/include/asm/mach-tx49xx/war.h        |   1 +
>  arch/mips/include/asm/pgtable.h                |  19 ++
>  arch/mips/include/asm/spinlock.h               | 142 +++++++++++++
>  arch/mips/include/asm/war.h                    |   8 +
>  arch/mips/kernel/syscall.c                     |  34 +++
>  arch/mips/mm/tlbex.c                           |  17 ++
>  24 files changed, 754 insertions(+), 67 deletions(-)
>  create mode 100644 arch/mips/include/asm/mach-loongson64/war.h
>
> diff --git a/arch/mips/include/asm/atomic.h b/arch/mips/include/asm/atomic.h
> index 0ab176b..e0002c58 100644
> --- a/arch/mips/include/asm/atomic.h
> +++ b/arch/mips/include/asm/atomic.h
> @@ -56,6 +56,22 @@ static __inline__ void atomic_##op(int i, atomic_t * v)                            \
>                 "       .set    mips0                                   \n"   \
>                 : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (v->counter)          \
>                 : "Ir" (i));                                                  \
> +       } else if (kernel_uses_llsc && LOONGSON_LLSC_WAR) {                   \
> +               int temp;                                                     \
> +                                                                             \
> +               do {                                                          \
> +                       __asm__ __volatile__(                                 \
> +                       "       .set    "MIPS_ISA_LEVEL"                \n"   \
> +                       __WEAK_LLSC_MB                                        \
> +                       "       ll      %0, %1          # atomic_" #op "\n"   \
> +                       "       " #asm_op " %0, %2                      \n"   \
> +                       "       sc      %0, %1                          \n"   \
> +                       "       .set    mips0                           \n"   \
> +                       : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (v->counter)      \
> +                       : "Ir" (i));                                          \
> +               } while (unlikely(!temp));                                    \
> +                                                                             \
> +               smp_llsc_mb();                                                \
>         } else if (kernel_uses_llsc) {                                        \
>                 int temp;                                                     \
>                                                                               \
> @@ -97,6 +113,23 @@ static __inline__ int atomic_##op##_return_relaxed(int i, atomic_t * v)           \
>                 : "=&r" (result), "=&r" (temp),                               \
>                   "+" GCC_OFF_SMALL_ASM() (v->counter)                        \
>                 : "Ir" (i));                                                  \
> +       } else if (kernel_uses_llsc && LOONGSON_LLSC_WAR) {                   \
> +               int temp;                                                     \
> +                                                                             \
> +               do {                                                          \
> +                       __asm__ __volatile__(                                 \
> +                       "       .set    "MIPS_ISA_LEVEL"                \n"   \
> +                       __WEAK_LLSC_MB                                        \
> +                       "       ll      %1, %2  # atomic_" #op "_return \n"   \
> +                       "       " #asm_op " %0, %1, %3                  \n"   \
> +                       "       sc      %0, %2                          \n"   \
> +                       "       .set    mips0                           \n"   \
> +                       : "=&r" (result), "=&r" (temp),                       \
> +                         "+" GCC_OFF_SMALL_ASM() (v->counter)                \
> +                       : "Ir" (i));                                          \
> +               } while (unlikely(!result));                                  \
> +                                                                             \
> +               result = temp; result c_op i;                                 \
>         } else if (kernel_uses_llsc) {                                        \
>                 int temp;                                                     \
>                                                                               \
> @@ -237,6 +270,26 @@ static __inline__ int atomic_sub_if_positive(int i, atomic_t * v)
>                   "+" GCC_OFF_SMALL_ASM() (v->counter)
>                 : "Ir" (i), GCC_OFF_SMALL_ASM() (v->counter)
>                 : "memory");
> +       } else if (kernel_uses_llsc && LOONGSON_LLSC_WAR) {
> +               int temp;
> +
> +               __asm__ __volatile__(
> +               "       .set    "MIPS_ISA_LEVEL"                        \n"
> +               "1:                             # atomic_sub_if_positive\n"
> +               __WEAK_LLSC_MB
> +               "       ll      %1, %2                                  \n"
> +               "       subu    %0, %1, %3                              \n"
> +               "       bltz    %0, 1f                                  \n"
> +               "       sc      %0, %2                                  \n"
> +               "       .set    noreorder                               \n"
> +               "       beqz    %0, 1b                                  \n"
> +               "        subu   %0, %1, %3                              \n"
> +               "       .set    reorder                                 \n"
> +               "1:                                                     \n"
> +               "       .set    mips0                                   \n"
> +               : "=&r" (result), "=&r" (temp),
> +                 "+" GCC_OFF_SMALL_ASM() (v->counter)
> +               : "Ir" (i));
>         } else if (kernel_uses_llsc) {
>                 int temp;
>
> @@ -398,6 +451,22 @@ static __inline__ void atomic64_##op(long i, atomic64_t * v)                     \
>                 "       .set    mips0                                   \n"   \
>                 : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (v->counter)          \
>                 : "Ir" (i));                                                  \
> +       } else if (kernel_uses_llsc && LOONGSON_LLSC_WAR) {                   \
> +               long temp;                                                    \
> +                                                                             \
> +               do {                                                          \
> +                       __asm__ __volatile__(                                 \
> +                       "       .set    "MIPS_ISA_LEVEL"                \n"   \
> +                       __WEAK_LLSC_MB                                        \
> +                       "       lld     %0, %1          # atomic64_" #op "\n" \
> +                       "       " #asm_op " %0, %2                      \n"   \
> +                       "       scd     %0, %1                          \n"   \
> +                       "       .set    mips0                           \n"   \
> +                       : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (v->counter)      \
> +                       : "Ir" (i));                                          \
> +               } while (unlikely(!temp));                                    \
> +                                                                             \
> +               smp_llsc_mb();                                                \
>         } else if (kernel_uses_llsc) {                                        \
>                 long temp;                                                    \
>                                                                               \
> @@ -439,6 +508,24 @@ static __inline__ long atomic64_##op##_return_relaxed(long i, atomic64_t * v) \
>                 : "=&r" (result), "=&r" (temp),                               \
>                   "+" GCC_OFF_SMALL_ASM() (v->counter)                        \
>                 : "Ir" (i));                                                  \
> +       } else if (kernel_uses_llsc && LOONGSON_LLSC_WAR) {                   \
> +               long temp;                                                    \
> +                                                                             \
> +               do {                                                          \
> +                       __asm__ __volatile__(                                 \
> +                       "       .set    "MIPS_ISA_LEVEL"                \n"   \
> +                       __WEAK_LLSC_MB                                        \
> +                       "       lld     %1, %2  # atomic64_" #op "_return\n"  \
> +                       "       " #asm_op " %0, %1, %3                  \n"   \
> +                       "       scd     %0, %2                          \n"   \
> +                       "       .set    mips0                           \n"   \
> +                       : "=&r" (result), "=&r" (temp),                       \
> +                         "=" GCC_OFF_SMALL_ASM() (v->counter)                \
> +                       : "Ir" (i), GCC_OFF_SMALL_ASM() (v->counter)          \
> +                       : "memory");                                          \
> +               } while (unlikely(!result));                                  \
> +                                                                             \
> +               result = temp; result c_op i;                                 \
>         } else if (kernel_uses_llsc) {                                        \
>                 long temp;                                                    \
>                                                                               \
> @@ -582,6 +669,26 @@ static __inline__ long atomic64_sub_if_positive(long i, atomic64_t * v)
>                   "=" GCC_OFF_SMALL_ASM() (v->counter)
>                 : "Ir" (i), GCC_OFF_SMALL_ASM() (v->counter)
>                 : "memory");
> +       } else if (kernel_uses_llsc && LOONGSON_LLSC_WAR) {
> +               long temp;
> +
> +               __asm__ __volatile__(
> +               "       .set    "MIPS_ISA_LEVEL"                        \n"
> +               "1:                             # atomic64_sub_if_positive\n"
> +               __WEAK_LLSC_MB
> +               "       lld     %1, %2                                  \n"
> +               "       dsubu   %0, %1, %3                              \n"
> +               "       bltz    %0, 1f                                  \n"
> +               "       scd     %0, %2                                  \n"
> +               "       .set    noreorder                               \n"
> +               "       beqz    %0, 1b                                  \n"
> +               "        dsubu  %0, %1, %3                              \n"
> +               "       .set    reorder                                 \n"
> +               "1:                                                     \n"
> +               "       .set    mips0                                   \n"
> +               : "=&r" (result), "=&r" (temp),
> +                 "+" GCC_OFF_SMALL_ASM() (v->counter)
> +               : "Ir" (i));
>         } else if (kernel_uses_llsc) {
>                 long temp;
>
> diff --git a/arch/mips/include/asm/bitops.h b/arch/mips/include/asm/bitops.h
> index fa57cef..6bef54a 100644
> --- a/arch/mips/include/asm/bitops.h
> +++ b/arch/mips/include/asm/bitops.h
> @@ -68,26 +68,54 @@ static inline void set_bit(unsigned long nr, volatile unsigned long *addr)
>                 : "ir" (1UL << bit), GCC_OFF_SMALL_ASM() (*m));
>  #if defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6)
>         } else if (kernel_uses_llsc && __builtin_constant_p(bit)) {
> -               do {
> -                       __asm__ __volatile__(
> -                       "       " __LL "%0, %1          # set_bit       \n"
> -                       "       " __INS "%0, %3, %2, 1                  \n"
> -                       "       " __SC "%0, %1                          \n"
> -                       : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m)
> -                       : "ir" (bit), "r" (~0));
> -               } while (unlikely(!temp));
> +               if (LOONGSON_LLSC_WAR) {
> +                       do {
> +                               __asm__ __volatile__(
> +                               __WEAK_LLSC_MB
> +                               "       " __LL "%0, %1          # set_bit       \n"
> +                               "       " __INS "%0, %3, %2, 1                  \n"
> +                               "       " __SC "%0, %1                          \n"
> +                               : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m)
> +                               : "ir" (bit), "r" (~0));
> +                       } while (unlikely(!temp));
> +                       smp_llsc_mb();
> +               } else {
> +                       do {
> +                               __asm__ __volatile__(
> +                               "       " __LL "%0, %1          # set_bit       \n"
> +                               "       " __INS "%0, %3, %2, 1                  \n"
> +                               "       " __SC "%0, %1                          \n"
> +                               : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m)
> +                               : "ir" (bit), "r" (~0));
> +                       } while (unlikely(!temp));
> +               }
>  #endif /* CONFIG_CPU_MIPSR2 || CONFIG_CPU_MIPSR6 */
>         } else if (kernel_uses_llsc) {
> -               do {
> -                       __asm__ __volatile__(
> -                       "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> -                       "       " __LL "%0, %1          # set_bit       \n"
> -                       "       or      %0, %2                          \n"
> -                       "       " __SC  "%0, %1                         \n"
> -                       "       .set    mips0                           \n"
> -                       : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m)
> -                       : "ir" (1UL << bit));
> -               } while (unlikely(!temp));
> +               if (LOONGSON_LLSC_WAR) {
> +                       do {
> +                               __asm__ __volatile__(
> +                               "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> +                               __WEAK_LLSC_MB
> +                               "       " __LL "%0, %1          # set_bit       \n"
> +                               "       or      %0, %2                          \n"
> +                               "       " __SC  "%0, %1                         \n"
> +                               "       .set    mips0                           \n"
> +                               : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m)
> +                               : "ir" (1UL << bit));
> +                       } while (unlikely(!temp));
> +                       smp_llsc_mb();
> +               } else {
> +                       do {
> +                               __asm__ __volatile__(
> +                               "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> +                               "       " __LL "%0, %1          # set_bit       \n"
> +                               "       or      %0, %2                          \n"
> +                               "       " __SC  "%0, %1                         \n"
> +                               "       .set    mips0                           \n"
> +                               : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m)
> +                               : "ir" (1UL << bit));
> +                       } while (unlikely(!temp));
> +               }
>         } else
>                 __mips_set_bit(nr, addr);
>  }
> @@ -120,26 +148,54 @@ static inline void clear_bit(unsigned long nr, volatile unsigned long *addr)
>                 : "ir" (~(1UL << bit)));
>  #if defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6)
>         } else if (kernel_uses_llsc && __builtin_constant_p(bit)) {
> -               do {
> -                       __asm__ __volatile__(
> -                       "       " __LL "%0, %1          # clear_bit     \n"
> -                       "       " __INS "%0, $0, %2, 1                  \n"
> -                       "       " __SC "%0, %1                          \n"
> -                       : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m)
> -                       : "ir" (bit));
> -               } while (unlikely(!temp));
> +               if (LOONGSON_LLSC_WAR) {
> +                       do {
> +                               __asm__ __volatile__(
> +                               __WEAK_LLSC_MB
> +                               "       " __LL "%0, %1          # clear_bit     \n"
> +                               "       " __INS "%0, $0, %2, 1                  \n"
> +                               "       " __SC "%0, %1                          \n"
> +                               : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m)
> +                               : "ir" (bit));
> +                       } while (unlikely(!temp));
> +                       smp_llsc_mb();
> +               } else {
> +                       do {
> +                               __asm__ __volatile__(
> +                               "       " __LL "%0, %1          # clear_bit     \n"
> +                               "       " __INS "%0, $0, %2, 1                  \n"
> +                               "       " __SC "%0, %1                          \n"
> +                               : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m)
> +                               : "ir" (bit));
> +                       } while (unlikely(!temp));
> +               }
>  #endif /* CONFIG_CPU_MIPSR2 || CONFIG_CPU_MIPSR6 */
>         } else if (kernel_uses_llsc) {
> -               do {
> -                       __asm__ __volatile__(
> -                       "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> -                       "       " __LL "%0, %1          # clear_bit     \n"
> -                       "       and     %0, %2                          \n"
> -                       "       " __SC "%0, %1                          \n"
> -                       "       .set    mips0                           \n"
> -                       : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m)
> -                       : "ir" (~(1UL << bit)));
> -               } while (unlikely(!temp));
> +               if (LOONGSON_LLSC_WAR) {
> +                       do {
> +                               __asm__ __volatile__(
> +                               "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> +                               __WEAK_LLSC_MB
> +                               "       " __LL "%0, %1          # clear_bit     \n"
> +                               "       and     %0, %2                          \n"
> +                               "       " __SC "%0, %1                          \n"
> +                               "       .set    mips0                           \n"
> +                               : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m)
> +                               : "ir" (~(1UL << bit)));
> +                       } while (unlikely(!temp));
> +                       smp_llsc_mb();
> +               } else {
> +                       do {
> +                               __asm__ __volatile__(
> +                               "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> +                               "       " __LL "%0, %1          # clear_bit     \n"
> +                               "       and     %0, %2                          \n"
> +                               "       " __SC "%0, %1                          \n"
> +                               "       .set    mips0                           \n"
> +                               : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m)
> +                               : "ir" (~(1UL << bit)));
> +                       } while (unlikely(!temp));
> +               }
>         } else
>                 __mips_clear_bit(nr, addr);
>  }
> @@ -184,6 +240,23 @@ static inline void change_bit(unsigned long nr, volatile unsigned long *addr)
>                 "       .set    mips0                           \n"
>                 : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m)
>                 : "ir" (1UL << bit));
> +       } else if (kernel_uses_llsc && LOONGSON_LLSC_WAR) {
> +               unsigned long *m = ((unsigned long *) addr) + (nr >> SZLONG_LOG);
> +               unsigned long temp;
> +
> +               do {
> +                       __asm__ __volatile__(
> +                       "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> +                       __WEAK_LLSC_MB
> +                       "       " __LL "%0, %1          # change_bit    \n"
> +                       "       xor     %0, %2                          \n"
> +                       "       " __SC  "%0, %1                         \n"
> +                       "       .set    mips0                           \n"
> +                       : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m)
> +                       : "ir" (1UL << bit));
> +               } while (unlikely(!temp));
> +
> +               smp_llsc_mb();
>         } else if (kernel_uses_llsc) {
>                 unsigned long *m = ((unsigned long *) addr) + (nr >> SZLONG_LOG);
>                 unsigned long temp;
> @@ -233,6 +306,24 @@ static inline int test_and_set_bit(unsigned long nr,
>                 : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m), "=&r" (res)
>                 : "r" (1UL << bit)
>                 : "memory");
> +       } else if (kernel_uses_llsc && LOONGSON_LLSC_WAR) {
> +               unsigned long *m = ((unsigned long *) addr) + (nr >> SZLONG_LOG);
> +               unsigned long temp;
> +
> +               do {
> +                       __asm__ __volatile__(
> +                       "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> +                       __WEAK_LLSC_MB
> +                       "       " __LL "%0, %1  # test_and_set_bit      \n"
> +                       "       or      %2, %0, %3                      \n"
> +                       "       " __SC  "%2, %1                         \n"
> +                       "       .set    mips0                           \n"
> +                       : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m), "=&r" (res)
> +                       : "r" (1UL << bit)
> +                       : "memory");
> +               } while (unlikely(!res));
> +
> +               res = temp & (1UL << bit);
>         } else if (kernel_uses_llsc) {
>                 unsigned long *m = ((unsigned long *) addr) + (nr >> SZLONG_LOG);
>                 unsigned long temp;
> @@ -287,6 +378,24 @@ static inline int test_and_set_bit_lock(unsigned long nr,
>                 : "=&r" (temp), "+m" (*m), "=&r" (res)
>                 : "r" (1UL << bit)
>                 : "memory");
> +       } else if (kernel_uses_llsc && LOONGSON_LLSC_WAR) {
> +               unsigned long *m = ((unsigned long *) addr) + (nr >> SZLONG_LOG);
> +               unsigned long temp;
> +
> +               do {
> +                       __asm__ __volatile__(
> +                       "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> +                       __WEAK_LLSC_MB
> +                       "       " __LL "%0, %1  # test_and_set_bit      \n"
> +                       "       or      %2, %0, %3                      \n"
> +                       "       " __SC  "%2, %1                         \n"
> +                       "       .set    mips0                           \n"
> +                       : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m), "=&r" (res)
> +                       : "r" (1UL << bit)
> +                       : "memory");
> +               } while (unlikely(!res));
> +
> +               res = temp & (1UL << bit);
>         } else if (kernel_uses_llsc) {
>                 unsigned long *m = ((unsigned long *) addr) + (nr >> SZLONG_LOG);
>                 unsigned long temp;
> @@ -348,33 +457,63 @@ static inline int test_and_clear_bit(unsigned long nr,
>                 unsigned long *m = ((unsigned long *) addr) + (nr >> SZLONG_LOG);
>                 unsigned long temp;
>
> -               do {
> -                       __asm__ __volatile__(
> -                       "       " __LL  "%0, %1 # test_and_clear_bit    \n"
> -                       "       " __EXT "%2, %0, %3, 1                  \n"
> -                       "       " __INS "%0, $0, %3, 1                  \n"
> -                       "       " __SC  "%0, %1                         \n"
> -                       : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m), "=&r" (res)
> -                       : "ir" (bit)
> -                       : "memory");
> -               } while (unlikely(!temp));
> +               if (LOONGSON_LLSC_WAR) {
> +                       do {
> +                               __asm__ __volatile__(
> +                               __WEAK_LLSC_MB
> +                               "       " __LL  "%0, %1 # test_and_clear_bit    \n"
> +                               "       " __EXT "%2, %0, %3, 1                  \n"
> +                               "       " __INS "%0, $0, %3, 1                  \n"
> +                               "       " __SC  "%0, %1                         \n"
> +                               : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m), "=&r" (res)
> +                               : "ir" (bit)
> +                               : "memory");
> +                       } while (unlikely(!temp));
> +               } else {
> +                       do {
> +                               __asm__ __volatile__(
> +                               "       " __LL  "%0, %1 # test_and_clear_bit    \n"
> +                               "       " __EXT "%2, %0, %3, 1                  \n"
> +                               "       " __INS "%0, $0, %3, 1                  \n"
> +                               "       " __SC  "%0, %1                         \n"
> +                               : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m), "=&r" (res)
> +                               : "ir" (bit)
> +                               : "memory");
> +                       } while (unlikely(!temp));
> +               }
>  #endif
>         } else if (kernel_uses_llsc) {
>                 unsigned long *m = ((unsigned long *) addr) + (nr >> SZLONG_LOG);
>                 unsigned long temp;
>
> -               do {
> -                       __asm__ __volatile__(
> -                       "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> -                       "       " __LL  "%0, %1 # test_and_clear_bit    \n"
> -                       "       or      %2, %0, %3                      \n"
> -                       "       xor     %2, %3                          \n"
> -                       "       " __SC  "%2, %1                         \n"
> -                       "       .set    mips0                           \n"
> -                       : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m), "=&r" (res)
> -                       : "r" (1UL << bit)
> -                       : "memory");
> -               } while (unlikely(!res));
> +               if (LOONGSON_LLSC_WAR) {
> +                       do {
> +                               __asm__ __volatile__(
> +                               "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> +                               __WEAK_LLSC_MB
> +                               "       " __LL  "%0, %1 # test_and_clear_bit    \n"
> +                               "       or      %2, %0, %3                      \n"
> +                               "       xor     %2, %3                          \n"
> +                               "       " __SC  "%2, %1                         \n"
> +                               "       .set    mips0                           \n"
> +                               : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m), "=&r" (res)
> +                               : "r" (1UL << bit)
> +                               : "memory");
> +                       } while (unlikely(!res));
> +               } else {
> +                       do {
> +                               __asm__ __volatile__(
> +                               "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> +                               "       " __LL  "%0, %1 # test_and_clear_bit    \n"
> +                               "       or      %2, %0, %3                      \n"
> +                               "       xor     %2, %3                          \n"
> +                               "       " __SC  "%2, %1                         \n"
> +                               "       .set    mips0                           \n"
> +                               : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m), "=&r" (res)
> +                               : "r" (1UL << bit)
> +                               : "memory");
> +                       } while (unlikely(!res));
> +               }
>
>                 res = temp & (1UL << bit);
>         } else
> @@ -416,6 +555,24 @@ static inline int test_and_change_bit(unsigned long nr,
>                 : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m), "=&r" (res)
>                 : "r" (1UL << bit)
>                 : "memory");
> +       } else if (kernel_uses_llsc && LOONGSON_LLSC_WAR) {
> +               unsigned long *m = ((unsigned long *) addr) + (nr >> SZLONG_LOG);
> +               unsigned long temp;
> +
> +               do {
> +                       __asm__ __volatile__(
> +                       "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> +                       __WEAK_LLSC_MB
> +                       "       " __LL  "%0, %1 # test_and_change_bit   \n"
> +                       "       xor     %2, %0, %3                      \n"
> +                       "       " __SC  "\t%2, %1                       \n"
> +                       "       .set    mips0                           \n"
> +                       : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (*m), "=&r" (res)
> +                       : "r" (1UL << bit)
> +                       : "memory");
> +               } while (unlikely(!res));
> +
> +               res = temp & (1UL << bit);
>         } else if (kernel_uses_llsc) {
>                 unsigned long *m = ((unsigned long *) addr) + (nr >> SZLONG_LOG);
>                 unsigned long temp;
> diff --git a/arch/mips/include/asm/cmpxchg.h b/arch/mips/include/asm/cmpxchg.h
> index b71ab4a..5bfd70d 100644
> --- a/arch/mips/include/asm/cmpxchg.h
> +++ b/arch/mips/include/asm/cmpxchg.h
> @@ -34,6 +34,24 @@ static inline unsigned long __xchg_u32(volatile int * m, unsigned int val)
>                 : "=&r" (retval), "=" GCC_OFF_SMALL_ASM() (*m), "=&r" (dummy)
>                 : GCC_OFF_SMALL_ASM() (*m), "Jr" (val)
>                 : "memory");
> +       } else if (kernel_uses_llsc && LOONGSON_LLSC_WAR) {
> +               unsigned long dummy;
> +
> +               do {
> +                       __asm__ __volatile__(
> +                       "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> +                       __WEAK_LLSC_MB
> +                       "       ll      %0, %3          # xchg_u32      \n"
> +                       "       .set    mips0                           \n"
> +                       "       move    %2, %z4                         \n"
> +                       "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> +                       "       sc      %2, %1                          \n"
> +                       "       .set    mips0                           \n"
> +                       : "=&r" (retval), "=" GCC_OFF_SMALL_ASM() (*m),
> +                         "=&r" (dummy)
> +                       : GCC_OFF_SMALL_ASM() (*m), "Jr" (val)
> +                       : "memory");
> +               } while (unlikely(!dummy));
>         } else if (kernel_uses_llsc) {
>                 unsigned long dummy;
>
> @@ -85,6 +103,22 @@ static inline __u64 __xchg_u64(volatile __u64 * m, __u64 val)
>                 : "=&r" (retval), "=" GCC_OFF_SMALL_ASM() (*m), "=&r" (dummy)
>                 : GCC_OFF_SMALL_ASM() (*m), "Jr" (val)
>                 : "memory");
> +       } else if (kernel_uses_llsc && LOONGSON_LLSC_WAR) {
> +               unsigned long dummy;
> +
> +               do {
> +                       __asm__ __volatile__(
> +                       "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> +                       __WEAK_LLSC_MB
> +                       "       lld     %0, %3          # xchg_u64      \n"
> +                       "       move    %2, %z4                         \n"
> +                       "       scd     %2, %1                          \n"
> +                       "       .set    mips0                           \n"
> +                       : "=&r" (retval), "=" GCC_OFF_SMALL_ASM() (*m),
> +                         "=&r" (dummy)
> +                       : GCC_OFF_SMALL_ASM() (*m), "Jr" (val)
> +                       : "memory");
> +               } while (unlikely(!dummy));
>         } else if (kernel_uses_llsc) {
>                 unsigned long dummy;
>
> @@ -159,6 +193,26 @@ static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int siz
>                 : "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m)           \
>                 : GCC_OFF_SMALL_ASM() (*m), "Jr" (old), "Jr" (new)              \
>                 : "memory");                                            \
> +       } else if (kernel_uses_llsc && LOONGSON_LLSC_WAR) {             \
> +               __asm__ __volatile__(                                   \
> +               "       .set    push                            \n"     \
> +               "       .set    noat                            \n"     \
> +               "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"     \
> +               "1:                             # __cmpxchg_asm \n"     \
> +               __WEAK_LLSC_MB                                          \
> +               "       " ld "  %0, %2                          \n"     \
> +               "       bne     %0, %z3, 2f                     \n"     \
> +               "       .set    mips0                           \n"     \
> +               "       move    $1, %z4                         \n"     \
> +               "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"     \
> +               "       " st "  $1, %1                          \n"     \
> +               "       beqz    $1, 1b                          \n"     \
> +               "       .set    pop                             \n"     \
> +               "2:                                             \n"     \
> +               __WEAK_LLSC_MB                                          \
> +               : "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m)           \
> +               : GCC_OFF_SMALL_ASM() (*m), "Jr" (old), "Jr" (new)              \
> +               : "memory");                                            \
>         } else if (kernel_uses_llsc) {                                  \
>                 __asm__ __volatile__(                                   \
>                 "       .set    push                            \n"     \
> diff --git a/arch/mips/include/asm/edac.h b/arch/mips/include/asm/edac.h
> index 980b165..a864aa9 100644
> --- a/arch/mips/include/asm/edac.h
> +++ b/arch/mips/include/asm/edac.h
> @@ -19,15 +19,30 @@ static inline void edac_atomic_scrub(void *va, u32 size)
>                  * Intel: asm("lock; addl $0, %0"::"m"(*virt_addr));
>                  */
>
> -               __asm__ __volatile__ (
> -               "       .set    mips2                                   \n"
> -               "1:     ll      %0, %1          # edac_atomic_scrub     \n"
> -               "       addu    %0, $0                                  \n"
> -               "       sc      %0, %1                                  \n"
> -               "       beqz    %0, 1b                                  \n"
> -               "       .set    mips0                                   \n"
> -               : "=&r" (temp), "=" GCC_OFF_SMALL_ASM() (*virt_addr)
> -               : GCC_OFF_SMALL_ASM() (*virt_addr));
> +               if (LOONGSON_LLSC_WAR) {
> +                       __asm__ __volatile__ (
> +                       "       .set    mips2                                   \n"
> +                       "1:                             # edac_atomic_scrub     \n"
> +                       __WEAK_LLSC_MB
> +                       "       ll      %0, %1                                  \n"
> +                       "       addu    %0, $0                                  \n"
> +                       "       sc      %0, %1                                  \n"
> +                       "       beqz    %0, 1b                                  \n"
> +                       "       .set    mips0                                   \n"
> +                       : "=&r" (temp), "=" GCC_OFF_SMALL_ASM() (*virt_addr)
> +                       : GCC_OFF_SMALL_ASM() (*virt_addr));
> +                       smp_llsc_mb();
> +               } else {
> +                       __asm__ __volatile__ (
> +                       "       .set    mips2                                   \n"
> +                       "1:     ll      %0, %1          # edac_atomic_scrub     \n"
> +                       "       addu    %0, $0                                  \n"
> +                       "       sc      %0, %1                                  \n"
> +                       "       beqz    %0, 1b                                  \n"
> +                       "       .set    mips0                                   \n"
> +                       : "=&r" (temp), "=" GCC_OFF_SMALL_ASM() (*virt_addr)
> +                       : GCC_OFF_SMALL_ASM() (*virt_addr));
> +               }
>
>                 virt_addr++;
>         }
> diff --git a/arch/mips/include/asm/futex.h b/arch/mips/include/asm/futex.h
> index 1de190b..3e2741f 100644
> --- a/arch/mips/include/asm/futex.h
> +++ b/arch/mips/include/asm/futex.h
> @@ -49,6 +49,37 @@
>                 : "0" (0), GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oparg),  \
>                   "i" (-EFAULT)                                         \
>                 : "memory");                                            \
> +       } else if (cpu_has_llsc && LOONGSON_LLSC_WAR) {                                 \
> +               __asm__ __volatile__(                                   \
> +               "       .set    push                            \n"     \
> +               "       .set    noat                            \n"     \
> +               "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"     \
> +               "1:                              # __futex_atomic_op\n" \
> +               __WEAK_LLSC_MB                                          \
> +               "       "user_ll("%1", "%4")"                   \n"     \
> +               "       .set    mips0                           \n"     \
> +               "       " insn  "                               \n"     \
> +               "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"     \
> +               "2:     "user_sc("$1", "%2")"                   \n"     \
> +               "       beqz    $1, 1b                          \n"     \
> +               __WEAK_LLSC_MB                                          \
> +               "3:                                             \n"     \
> +               "       .insn                                   \n"     \
> +               "       .set    pop                             \n"     \
> +               "       .set    mips0                           \n"     \
> +               "       .section .fixup,\"ax\"                  \n"     \
> +               "4:     li      %0, %6                          \n"     \
> +               "       j       3b                              \n"     \
> +               "       .previous                               \n"     \
> +               "       .section __ex_table,\"a\"               \n"     \
> +               "       "__UA_ADDR "\t(1b + 4), 4b              \n"     \
> +               "       "__UA_ADDR "\t(2b + 0), 4b              \n"     \
> +               "       .previous                               \n"     \
> +               : "=r" (ret), "=&r" (oldval),                           \
> +                 "=" GCC_OFF_SMALL_ASM() (*uaddr)                              \
> +               : "0" (0), GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oparg),  \
> +                 "i" (-EFAULT)                                         \
> +               : "memory");                                            \
>         } else if (cpu_has_llsc) {                                      \
>                 __asm__ __volatile__(                                   \
>                 "       .set    push                            \n"     \
> @@ -178,6 +209,37 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
>                 : GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oldval), "Jr" (newval),
>                   "i" (-EFAULT)
>                 : "memory");
> +       } else if (cpu_has_llsc && LOONGSON_LLSC_WAR) {
> +               __asm__ __volatile__(
> +               "# futex_atomic_cmpxchg_inatomic                        \n"
> +               "       .set    push                                    \n"
> +               "       .set    noat                                    \n"
> +               "       .set    "MIPS_ISA_ARCH_LEVEL"                   \n"
> +               "1:                                                     \n"
> +               __WEAK_LLSC_MB
> +               "       "user_ll("%1", "%3")"                           \n"
> +               "       bne     %1, %z4, 3f                             \n"
> +               "       .set    mips0                                   \n"
> +               "       move    $1, %z5                                 \n"
> +               "       .set    "MIPS_ISA_ARCH_LEVEL"                   \n"
> +               "2:     "user_sc("$1", "%2")"                           \n"
> +               "       beqz    $1, 1b                                  \n"
> +               __WEAK_LLSC_MB
> +               "3:                                                     \n"
> +               "       .insn                                           \n"
> +               "       .set    pop                                     \n"
> +               "       .section .fixup,\"ax\"                          \n"
> +               "4:     li      %0, %6                                  \n"
> +               "       j       3b                                      \n"
> +               "       .previous                                       \n"
> +               "       .section __ex_table,\"a\"                       \n"
> +               "       "__UA_ADDR "\t(1b + 4), 4b                      \n"
> +               "       "__UA_ADDR "\t(2b + 0), 4b                      \n"
> +               "       .previous                                       \n"
> +               : "+r" (ret), "=&r" (val), "=" GCC_OFF_SMALL_ASM() (*uaddr)
> +               : GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oldval), "Jr" (newval),
> +                 "i" (-EFAULT)
> +               : "memory");
>         } else if (cpu_has_llsc) {
>                 __asm__ __volatile__(
>                 "# futex_atomic_cmpxchg_inatomic                        \n"
> diff --git a/arch/mips/include/asm/local.h b/arch/mips/include/asm/local.h
> index 8feaed6..a6e9d06 100644
> --- a/arch/mips/include/asm/local.h
> +++ b/arch/mips/include/asm/local.h
> @@ -44,6 +44,23 @@ static __inline__ long local_add_return(long i, local_t * l)
>                 : "=&r" (result), "=&r" (temp), "=m" (l->a.counter)
>                 : "Ir" (i), "m" (l->a.counter)
>                 : "memory");
> +       } else if (kernel_uses_llsc && LOONGSON_LLSC_WAR) {
> +               unsigned long temp;
> +
> +               __asm__ __volatile__(
> +               "       .set    "MIPS_ISA_ARCH_LEVEL"                   \n"
> +               "1:                                                     \n"
> +                       __WEAK_LLSC_MB
> +                       __LL    "%1, %2         # local_add_return      \n"
> +               "       addu    %0, %1, %3                              \n"
> +                       __SC    "%0, %2                                 \n"
> +               "       beqz    %0, 1b                                  \n"
> +               "       addu    %0, %1, %3                              \n"
> +               "       .set    mips0                                   \n"
> +               : "=&r" (result), "=&r" (temp), "=m" (l->a.counter)
> +               : "Ir" (i), "m" (l->a.counter)
> +               : "memory");
> +               smp_llsc_mb();
>         } else if (kernel_uses_llsc) {
>                 unsigned long temp;
>
> @@ -89,6 +106,23 @@ static __inline__ long local_sub_return(long i, local_t * l)
>                 : "=&r" (result), "=&r" (temp), "=m" (l->a.counter)
>                 : "Ir" (i), "m" (l->a.counter)
>                 : "memory");
> +       } else if (kernel_uses_llsc && LOONGSON_LLSC_WAR) {
> +               unsigned long temp;
> +
> +               __asm__ __volatile__(
> +               "       .set    "MIPS_ISA_ARCH_LEVEL"                   \n"
> +               "1:                                                     \n"
> +                       __WEAK_LLSC_MB
> +                       __LL    "%1, %2         # local_sub_return      \n"
> +               "       subu    %0, %1, %3                              \n"
> +                       __SC    "%0, %2                                 \n"
> +               "       beqz    %0, 1b                                  \n"
> +               "       subu    %0, %1, %3                              \n"
> +               "       .set    mips0                                   \n"
> +               : "=&r" (result), "=&r" (temp), "=m" (l->a.counter)
> +               : "Ir" (i), "m" (l->a.counter)
> +               : "memory");
> +               smp_llsc_mb();
>         } else if (kernel_uses_llsc) {
>                 unsigned long temp;
>
> diff --git a/arch/mips/include/asm/mach-cavium-octeon/war.h b/arch/mips/include/asm/mach-cavium-octeon/war.h
> index 35c80be..1c43fb2 100644
> --- a/arch/mips/include/asm/mach-cavium-octeon/war.h
> +++ b/arch/mips/include/asm/mach-cavium-octeon/war.h
> @@ -20,6 +20,7 @@
>  #define TX49XX_ICACHE_INDEX_INV_WAR    0
>  #define ICACHE_REFILLS_WORKAROUND_WAR  0
>  #define R10000_LLSC_WAR                        0
> +#define LOONGSON_LLSC_WAR              0
>  #define MIPS34K_MISSED_ITLB_WAR                0
>
>  #define CAVIUM_OCTEON_DCACHE_PREFETCH_WAR      \
> diff --git a/arch/mips/include/asm/mach-generic/war.h b/arch/mips/include/asm/mach-generic/war.h
> index a1bc2e7..2dd9bf5 100644
> --- a/arch/mips/include/asm/mach-generic/war.h
> +++ b/arch/mips/include/asm/mach-generic/war.h
> @@ -19,6 +19,7 @@
>  #define TX49XX_ICACHE_INDEX_INV_WAR    0
>  #define ICACHE_REFILLS_WORKAROUND_WAR  0
>  #define R10000_LLSC_WAR                        0
> +#define LOONGSON_LLSC_WAR              0
>  #define MIPS34K_MISSED_ITLB_WAR                0
>
>  #endif /* __ASM_MACH_GENERIC_WAR_H */
> diff --git a/arch/mips/include/asm/mach-ip22/war.h b/arch/mips/include/asm/mach-ip22/war.h
> index fba6405..66ddafa 100644
> --- a/arch/mips/include/asm/mach-ip22/war.h
> +++ b/arch/mips/include/asm/mach-ip22/war.h
> @@ -23,6 +23,7 @@
>  #define TX49XX_ICACHE_INDEX_INV_WAR    0
>  #define ICACHE_REFILLS_WORKAROUND_WAR  0
>  #define R10000_LLSC_WAR                        0
> +#define LOONGSON_LLSC_WAR              0
>  #define MIPS34K_MISSED_ITLB_WAR                0
>
>  #endif /* __ASM_MIPS_MACH_IP22_WAR_H */
> diff --git a/arch/mips/include/asm/mach-ip27/war.h b/arch/mips/include/asm/mach-ip27/war.h
> index 4ee0e4b..63ee1e5 100644
> --- a/arch/mips/include/asm/mach-ip27/war.h
> +++ b/arch/mips/include/asm/mach-ip27/war.h
> @@ -19,6 +19,7 @@
>  #define TX49XX_ICACHE_INDEX_INV_WAR    0
>  #define ICACHE_REFILLS_WORKAROUND_WAR  0
>  #define R10000_LLSC_WAR                        1
> +#define LOONGSON_LLSC_WAR              0
>  #define MIPS34K_MISSED_ITLB_WAR                0
>
>  #endif /* __ASM_MIPS_MACH_IP27_WAR_H */
> diff --git a/arch/mips/include/asm/mach-ip28/war.h b/arch/mips/include/asm/mach-ip28/war.h
> index 4821c7b..e455320 100644
> --- a/arch/mips/include/asm/mach-ip28/war.h
> +++ b/arch/mips/include/asm/mach-ip28/war.h
> @@ -19,6 +19,7 @@
>  #define TX49XX_ICACHE_INDEX_INV_WAR    0
>  #define ICACHE_REFILLS_WORKAROUND_WAR  0
>  #define R10000_LLSC_WAR                        1
> +#define LOONGSON_LLSC_WAR              0
>  #define MIPS34K_MISSED_ITLB_WAR                0
>
>  #endif /* __ASM_MIPS_MACH_IP28_WAR_H */
> diff --git a/arch/mips/include/asm/mach-ip32/war.h b/arch/mips/include/asm/mach-ip32/war.h
> index 9807ecd..2bd4caf 100644
> --- a/arch/mips/include/asm/mach-ip32/war.h
> +++ b/arch/mips/include/asm/mach-ip32/war.h
> @@ -19,6 +19,7 @@
>  #define TX49XX_ICACHE_INDEX_INV_WAR    0
>  #define ICACHE_REFILLS_WORKAROUND_WAR  1
>  #define R10000_LLSC_WAR                        0
> +#define LOONGSON_LLSC_WAR              0
>  #define MIPS34K_MISSED_ITLB_WAR                0
>
>  #endif /* __ASM_MIPS_MACH_IP32_WAR_H */
> diff --git a/arch/mips/include/asm/mach-loongson64/war.h b/arch/mips/include/asm/mach-loongson64/war.h
> new file mode 100644
> index 0000000..c5f9aaa
> --- /dev/null
> +++ b/arch/mips/include/asm/mach-loongson64/war.h
> @@ -0,0 +1,26 @@
> +/*
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file "COPYING" in the main directory of this archive
> + * for more details.
> + *
> + * Copyright (C) 2002, 2004, 2007 by Ralf Baechle <ralf@xxxxxxxxxxxxxx>
> + * Copyright (C) 2015, 2016 by Huacai Chen <chenhc@xxxxxxxxxx>
> + */
> +#ifndef __ASM_MIPS_MACH_LOONGSON64_WAR_H
> +#define __ASM_MIPS_MACH_LOONGSON64_WAR_H
> +
> +#define R4600_V1_INDEX_ICACHEOP_WAR    0
> +#define R4600_V1_HIT_CACHEOP_WAR       0
> +#define R4600_V2_HIT_CACHEOP_WAR       0
> +#define R5432_CP0_INTERRUPT_WAR                0
> +#define BCM1250_M3_WAR                 0
> +#define SIBYTE_1956_WAR                        0
> +#define MIPS4K_ICACHE_REFILL_WAR       0
> +#define MIPS_CACHE_SYNC_WAR            0
> +#define TX49XX_ICACHE_INDEX_INV_WAR    0
> +#define ICACHE_REFILLS_WORKAROUND_WAR  0
> +#define R10000_LLSC_WAR                        0
> +#define LOONGSON_LLSC_WAR              IS_ENABLED(CONFIG_CPU_LOONGSON3)
> +#define MIPS34K_MISSED_ITLB_WAR                0
> +
> +#endif /* __ASM_MIPS_MACH_LOONGSON64_WAR_H */
> diff --git a/arch/mips/include/asm/mach-malta/war.h b/arch/mips/include/asm/mach-malta/war.h
> index d068fc4..c380825 100644
> --- a/arch/mips/include/asm/mach-malta/war.h
> +++ b/arch/mips/include/asm/mach-malta/war.h
> @@ -19,6 +19,7 @@
>  #define TX49XX_ICACHE_INDEX_INV_WAR    0
>  #define ICACHE_REFILLS_WORKAROUND_WAR  1
>  #define R10000_LLSC_WAR                        0
> +#define LOONGSON_LLSC_WAR              0
>  #define MIPS34K_MISSED_ITLB_WAR                0
>
>  #endif /* __ASM_MIPS_MACH_MIPS_WAR_H */
> diff --git a/arch/mips/include/asm/mach-pmcs-msp71xx/war.h b/arch/mips/include/asm/mach-pmcs-msp71xx/war.h
> index a60bf9d..8c5f396 100644
> --- a/arch/mips/include/asm/mach-pmcs-msp71xx/war.h
> +++ b/arch/mips/include/asm/mach-pmcs-msp71xx/war.h
> @@ -19,6 +19,7 @@
>  #define TX49XX_ICACHE_INDEX_INV_WAR    0
>  #define ICACHE_REFILLS_WORKAROUND_WAR  0
>  #define R10000_LLSC_WAR                        0
> +#define LOONGSON_LLSC_WAR              0
>  #if defined(CONFIG_PMC_MSP7120_EVAL) || defined(CONFIG_PMC_MSP7120_GW) || \
>         defined(CONFIG_PMC_MSP7120_FPGA)
>  #define MIPS34K_MISSED_ITLB_WAR                1
> diff --git a/arch/mips/include/asm/mach-rc32434/war.h b/arch/mips/include/asm/mach-rc32434/war.h
> index 1bfd489a..72d2926 100644
> --- a/arch/mips/include/asm/mach-rc32434/war.h
> +++ b/arch/mips/include/asm/mach-rc32434/war.h
> @@ -19,6 +19,7 @@
>  #define TX49XX_ICACHE_INDEX_INV_WAR    0
>  #define ICACHE_REFILLS_WORKAROUND_WAR  0
>  #define R10000_LLSC_WAR                        0
> +#define LOONGSON_LLSC_WAR              0
>  #define MIPS34K_MISSED_ITLB_WAR                0
>
>  #endif /* __ASM_MIPS_MACH_MIPS_WAR_H */
> diff --git a/arch/mips/include/asm/mach-rm/war.h b/arch/mips/include/asm/mach-rm/war.h
> index a3dde98..5683389 100644
> --- a/arch/mips/include/asm/mach-rm/war.h
> +++ b/arch/mips/include/asm/mach-rm/war.h
> @@ -23,6 +23,7 @@
>  #define TX49XX_ICACHE_INDEX_INV_WAR    0
>  #define ICACHE_REFILLS_WORKAROUND_WAR  0
>  #define R10000_LLSC_WAR                        0
> +#define LOONGSON_LLSC_WAR              0
>  #define MIPS34K_MISSED_ITLB_WAR                0
>
>  #endif /* __ASM_MIPS_MACH_RM_WAR_H */
> diff --git a/arch/mips/include/asm/mach-sibyte/war.h b/arch/mips/include/asm/mach-sibyte/war.h
> index 520f8fc..b9d7bcb 100644
> --- a/arch/mips/include/asm/mach-sibyte/war.h
> +++ b/arch/mips/include/asm/mach-sibyte/war.h
> @@ -34,6 +34,7 @@ extern int sb1250_m3_workaround_needed(void);
>  #define TX49XX_ICACHE_INDEX_INV_WAR    0
>  #define ICACHE_REFILLS_WORKAROUND_WAR  0
>  #define R10000_LLSC_WAR                        0
> +#define LOONGSON_LLSC_WAR              0
>  #define MIPS34K_MISSED_ITLB_WAR                0
>
>  #endif /* __ASM_MIPS_MACH_SIBYTE_WAR_H */
> diff --git a/arch/mips/include/asm/mach-tx49xx/war.h b/arch/mips/include/asm/mach-tx49xx/war.h
> index a8e2c58..fd44710 100644
> --- a/arch/mips/include/asm/mach-tx49xx/war.h
> +++ b/arch/mips/include/asm/mach-tx49xx/war.h
> @@ -19,6 +19,7 @@
>  #define TX49XX_ICACHE_INDEX_INV_WAR    1
>  #define ICACHE_REFILLS_WORKAROUND_WAR  0
>  #define R10000_LLSC_WAR                        0
> +#define LOONGSON_LLSC_WAR              0
>  #define MIPS34K_MISSED_ITLB_WAR                0
>
>  #endif /* __ASM_MIPS_MACH_TX49XX_WAR_H */
> diff --git a/arch/mips/include/asm/pgtable.h b/arch/mips/include/asm/pgtable.h
> index 9e9e944..d534185 100644
> --- a/arch/mips/include/asm/pgtable.h
> +++ b/arch/mips/include/asm/pgtable.h
> @@ -228,6 +228,25 @@ static inline void set_pte(pte_t *ptep, pte_t pteval)
>                         "       .set    mips0                           \n"
>                         : [buddy] "+m" (buddy->pte), [tmp] "=&r" (tmp)
>                         : [global] "r" (page_global));
> +               } else if (kernel_uses_llsc && LOONGSON_LLSC_WAR) {
> +                       __asm__ __volatile__ (
> +                       "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> +                       "       .set    push                            \n"
> +                       "       .set    noreorder                       \n"
> +                       "1:                                             \n"
> +                               __WEAK_LLSC_MB
> +                               __LL    "%[tmp], %[buddy]               \n"
> +                       "       bnez    %[tmp], 2f                      \n"
> +                       "        or     %[tmp], %[tmp], %[global]       \n"
> +                               __SC    "%[tmp], %[buddy]               \n"
> +                       "       beqz    %[tmp], 1b                      \n"
> +                       "       nop                                     \n"
> +                       "2:                                             \n"
> +                       "       .set    pop                             \n"
> +                       "       .set    mips0                           \n"
> +                       : [buddy] "+m" (buddy->pte), [tmp] "=&r" (tmp)
> +                       : [global] "r" (page_global));
> +                       smp_llsc_mb();
>                 } else if (kernel_uses_llsc) {
>                         __asm__ __volatile__ (
>                         "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
> diff --git a/arch/mips/include/asm/spinlock.h b/arch/mips/include/asm/spinlock.h
> index f485afe..618e5df 100644
> --- a/arch/mips/include/asm/spinlock.h
> +++ b/arch/mips/include/asm/spinlock.h
> @@ -114,6 +114,41 @@ static inline void arch_spin_lock(arch_spinlock_t *lock)
>                   [ticket] "=&r" (tmp),
>                   [my_ticket] "=&r" (my_ticket)
>                 : [inc] "r" (inc));
> +       } else if (LOONGSON_LLSC_WAR) {
> +               __asm__ __volatile__ (
> +               "       .set push               # arch_spin_lock        \n"
> +               "       .set noreorder                                  \n"
> +               "                                                       \n"
> +               "1:                                                     \n"
> +               __WEAK_LLSC_MB
> +               "       ll      %[ticket], %[ticket_ptr]                \n"
> +               "       addu    %[my_ticket], %[ticket], %[inc]         \n"
> +               "       sc      %[my_ticket], %[ticket_ptr]             \n"
> +               "       beqz    %[my_ticket], 1b                        \n"
> +               "        srl    %[my_ticket], %[ticket], 16             \n"
> +               "       andi    %[ticket], %[ticket], 0xffff            \n"
> +               "       bne     %[ticket], %[my_ticket], 4f             \n"
> +               "        subu   %[ticket], %[my_ticket], %[ticket]      \n"
> +               "2:                                                     \n"
> +               "       .subsection 2                                   \n"
> +               "4:     andi    %[ticket], %[ticket], 0xffff            \n"
> +               "       sll     %[ticket], 5                            \n"
> +               "                                                       \n"
> +               "6:     bnez    %[ticket], 6b                           \n"
> +               "        subu   %[ticket], 1                            \n"
> +               "                                                       \n"
> +               "       lhu     %[ticket], %[serving_now_ptr]           \n"
> +               "       beq     %[ticket], %[my_ticket], 2b             \n"
> +               "        subu   %[ticket], %[my_ticket], %[ticket]      \n"
> +               "       b       4b                                      \n"
> +               "        subu   %[ticket], %[ticket], 1                 \n"
> +               "       .previous                                       \n"
> +               "       .set pop                                        \n"
> +               : [ticket_ptr] "+" GCC_OFF_SMALL_ASM() (lock->lock),
> +                 [serving_now_ptr] "+m" (lock->h.serving_now),
> +                 [ticket] "=&r" (tmp),
> +                 [my_ticket] "=&r" (my_ticket)
> +               : [inc] "r" (inc));
>         } else {
>                 __asm__ __volatile__ (
>                 "       .set push               # arch_spin_lock        \n"
> @@ -189,6 +224,32 @@ static inline unsigned int arch_spin_trylock(arch_spinlock_t *lock)
>                   [my_ticket] "=&r" (tmp2),
>                   [now_serving] "=&r" (tmp3)
>                 : [inc] "r" (inc));
> +       } if (LOONGSON_LLSC_WAR) {
> +               __asm__ __volatile__ (
> +               "       .set push               # arch_spin_trylock     \n"
> +               "       .set noreorder                                  \n"
> +               "                                                       \n"
> +               "1:                                                     \n"
> +               __WEAK_LLSC_MB
> +               "       ll      %[ticket], %[ticket_ptr]                \n"
> +               "       srl     %[my_ticket], %[ticket], 16             \n"
> +               "       andi    %[now_serving], %[ticket], 0xffff       \n"
> +               "       bne     %[my_ticket], %[now_serving], 3f        \n"
> +               "        addu   %[ticket], %[ticket], %[inc]            \n"
> +               "       sc      %[ticket], %[ticket_ptr]                \n"
> +               "       beqz    %[ticket], 1b                           \n"
> +               "        li     %[ticket], 1                            \n"
> +               "2:                                                     \n"
> +               "       .subsection 2                                   \n"
> +               "3:     b       2b                                      \n"
> +               "        li     %[ticket], 0                            \n"
> +               "       .previous                                       \n"
> +               "       .set pop                                        \n"
> +               : [ticket_ptr] "+" GCC_OFF_SMALL_ASM() (lock->lock),
> +                 [ticket] "=&r" (tmp),
> +                 [my_ticket] "=&r" (tmp2),
> +                 [now_serving] "=&r" (tmp3)
> +               : [inc] "r" (inc));
>         } else {
>                 __asm__ __volatile__ (
>                 "       .set push               # arch_spin_trylock     \n"
> @@ -258,6 +319,19 @@ static inline void arch_read_lock(arch_rwlock_t *rw)
>                 : "=" GCC_OFF_SMALL_ASM() (rw->lock), "=&r" (tmp)
>                 : GCC_OFF_SMALL_ASM() (rw->lock)
>                 : "memory");
> +       } else if (LOONGSON_LLSC_WAR) {
> +               do {
> +                       __asm__ __volatile__(
> +                       "1:                     # arch_read_lock        \n"
> +                       __WEAK_LLSC_MB
> +                       "       ll      %1, %2                          \n"
> +                       "       bltz    %1, 1b                          \n"
> +                       "        addu   %1, 1                           \n"
> +                       "2:     sc      %1, %0                          \n"
> +                       : "=" GCC_OFF_SMALL_ASM() (rw->lock), "=&r" (tmp)
> +                       : GCC_OFF_SMALL_ASM() (rw->lock)
> +                       : "memory");
> +               } while (unlikely(!tmp));
>         } else {
>                 do {
>                         __asm__ __volatile__(
> @@ -289,6 +363,20 @@ static inline void arch_read_unlock(arch_rwlock_t *rw)
>                 : "=" GCC_OFF_SMALL_ASM() (rw->lock), "=&r" (tmp)
>                 : GCC_OFF_SMALL_ASM() (rw->lock)
>                 : "memory");
> +       } else if (LOONGSON_LLSC_WAR) {
> +               do {
> +                       __asm__ __volatile__(
> +                       "1:                     # arch_read_unlock      \n"
> +                       __WEAK_LLSC_MB
> +                       "       ll      %1, %2                          \n"
> +                       "       addiu   %1, -1                          \n"
> +                       "       sc      %1, %0                          \n"
> +                       : "=" GCC_OFF_SMALL_ASM() (rw->lock), "=&r" (tmp)
> +                       : GCC_OFF_SMALL_ASM() (rw->lock)
> +                       : "memory");
> +               } while (unlikely(!tmp));
> +
> +               smp_llsc_mb();
>         } else {
>                 do {
>                         __asm__ __volatile__(
> @@ -319,6 +407,19 @@ static inline void arch_write_lock(arch_rwlock_t *rw)
>                 : "=" GCC_OFF_SMALL_ASM() (rw->lock), "=&r" (tmp)
>                 : GCC_OFF_SMALL_ASM() (rw->lock)
>                 : "memory");
> +       } else if (LOONGSON_LLSC_WAR) {
> +               do {
> +                       __asm__ __volatile__(
> +                       "1:                     # arch_write_lock       \n"
> +                       __WEAK_LLSC_MB
> +                       "       ll      %1, %2                          \n"
> +                       "       bnez    %1, 1b                          \n"
> +                       "        lui    %1, 0x8000                      \n"
> +                       "2:     sc      %1, %0                          \n"
> +                       : "=" GCC_OFF_SMALL_ASM() (rw->lock), "=&r" (tmp)
> +                       : GCC_OFF_SMALL_ASM() (rw->lock)
> +                       : "memory");
> +               } while (unlikely(!tmp));
>         } else {
>                 do {
>                         __asm__ __volatile__(
> @@ -345,6 +446,8 @@ static inline void arch_write_unlock(arch_rwlock_t *rw)
>         : "=m" (rw->lock)
>         : "m" (rw->lock)
>         : "memory");
> +
> +       nudge_writes();
>  }
>
>  static inline int arch_read_trylock(arch_rwlock_t *rw)
> @@ -369,6 +472,27 @@ static inline int arch_read_trylock(arch_rwlock_t *rw)
>                 : "=" GCC_OFF_SMALL_ASM() (rw->lock), "=&r" (tmp), "=&r" (ret)
>                 : GCC_OFF_SMALL_ASM() (rw->lock)
>                 : "memory");
> +       } else if (LOONGSON_LLSC_WAR) {
> +               __asm__ __volatile__(
> +               "       .set    noreorder       # arch_read_trylock     \n"
> +               "       li      %2, 0                                   \n"
> +               "1:                                                     \n"
> +               __WEAK_LLSC_MB
> +               "       ll      %1, %3                                  \n"
> +               "       bltz    %1, 2f                                  \n"
> +               "        addu   %1, 1                                   \n"
> +               "       sc      %1, %0                                  \n"
> +               "       beqz    %1, 1b                                  \n"
> +               "        nop                                            \n"
> +               "       .set    reorder                                 \n"
> +               __WEAK_LLSC_MB
> +               "       li      %2, 1                                   \n"
> +               "2:                                                     \n"
> +               : "=" GCC_OFF_SMALL_ASM() (rw->lock), "=&r" (tmp), "=&r" (ret)
> +               : GCC_OFF_SMALL_ASM() (rw->lock)
> +               : "memory");
> +
> +               smp_llsc_mb();
>         } else {
>                 __asm__ __volatile__(
>                 "       .set    noreorder       # arch_read_trylock     \n"
> @@ -413,6 +537,24 @@ static inline int arch_write_trylock(arch_rwlock_t *rw)
>                 : "=" GCC_OFF_SMALL_ASM() (rw->lock), "=&r" (tmp), "=&r" (ret)
>                 : GCC_OFF_SMALL_ASM() (rw->lock)
>                 : "memory");
> +       } else if (LOONGSON_LLSC_WAR) {
> +               do {
> +                       __asm__ __volatile__(
> +                       __WEAK_LLSC_MB
> +                       "       ll      %1, %3  # arch_write_trylock    \n"
> +                       "       li      %2, 0                           \n"
> +                       "       bnez    %1, 2f                          \n"
> +                       "       lui     %1, 0x8000                      \n"
> +                       "       sc      %1, %0                          \n"
> +                       "       li      %2, 1                           \n"
> +                       "2:                                             \n"
> +                       : "=" GCC_OFF_SMALL_ASM() (rw->lock), "=&r" (tmp),
> +                         "=&r" (ret)
> +                       : GCC_OFF_SMALL_ASM() (rw->lock)
> +                       : "memory");
> +               } while (unlikely(!tmp));
> +
> +               smp_llsc_mb();
>         } else {
>                 do {
>                         __asm__ __volatile__(
> diff --git a/arch/mips/include/asm/war.h b/arch/mips/include/asm/war.h
> index 9344e24..2fe696a 100644
> --- a/arch/mips/include/asm/war.h
> +++ b/arch/mips/include/asm/war.h
> @@ -227,6 +227,14 @@
>  #endif
>
>  /*
> + * On the Loongson-2G/2H/3A/3B there is a bug that ll / sc and lld / scd is
> + * very weak ordering.
> + */
> +#ifndef LOONGSON_LLSC_WAR
> +#error Check setting of LOONGSON_LLSC_WAR for your platform
> +#endif
> +
> +/*
>   * 34K core erratum: "Problems Executing the TLBR Instruction"
>   */
>  #ifndef MIPS34K_MISSED_ITLB_WAR
> diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c
> index 53a7ef9..6c09736 100644
> --- a/arch/mips/kernel/syscall.c
> +++ b/arch/mips/kernel/syscall.c
> @@ -134,6 +134,40 @@ static inline int mips_atomic_set(unsigned long addr, unsigned long new)
>                   [new] "r" (new),
>                   [efault] "i" (-EFAULT)
>                 : "memory");
> +       } else if (cpu_has_llsc && LOONGSON_LLSC_WAR) {
> +               __asm__ __volatile__ (
> +               "       .set    "MIPS_ISA_ARCH_LEVEL"                   \n"
> +               "       li      %[err], 0                               \n"
> +               "1:                                                     \n"
> +               __WEAK_LLSC_MB
> +               "       ll      %[old], (%[addr])                       \n"
> +               "       move    %[tmp], %[new]                          \n"
> +               "2:     sc      %[tmp], (%[addr])                       \n"
> +               "       bnez    %[tmp], 4f                              \n"
> +               "3:                                                     \n"
> +               "       .insn                                           \n"
> +               "       .subsection 2                                   \n"
> +               "4:     b       1b                                      \n"
> +               "       .previous                                       \n"
> +               "                                                       \n"
> +               "       .section .fixup,\"ax\"                          \n"
> +               "5:     li      %[err], %[efault]                       \n"
> +               "       j       3b                                      \n"
> +               "       .previous                                       \n"
> +               "       .section __ex_table,\"a\"                       \n"
> +               "       "STR(PTR)"      (1b + 4), 5b                    \n"
> +               "       "STR(PTR)"      (2b + 0), 5b                    \n"
> +               "       .previous                                       \n"
> +               "       .set    mips0                                   \n"
> +               : [old] "=&r" (old),
> +                 [err] "=&r" (err),
> +                 [tmp] "=&r" (tmp)
> +               : [addr] "r" (addr),
> +                 [new] "r" (new),
> +                 [efault] "i" (-EFAULT)
> +               : "memory");
> +
> +               smp_llsc_mb();
>         } else if (cpu_has_llsc) {
>                 __asm__ __volatile__ (
>                 "       .set    "MIPS_ISA_ARCH_LEVEL"                   \n"
> diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
> index 55ce396..04d3b2d 100644
> --- a/arch/mips/mm/tlbex.c
> +++ b/arch/mips/mm/tlbex.c
> @@ -90,6 +90,11 @@ static inline int __maybe_unused r10000_llsc_war(void)
>         return R10000_LLSC_WAR;
>  }
>
> +static inline int __maybe_unused loongson_llsc_war(void)
> +{
> +       return LOONGSON_LLSC_WAR;
> +}
> +
>  static int use_bbit_insns(void)
>  {
>         switch (current_cpu_type()) {
> @@ -914,6 +919,8 @@ build_get_pgd_vmalloc64(u32 **p, struct uasm_label **l, struct uasm_reloc **r,
>                  * to mimic that here by taking a load/istream page
>                  * fault.
>                  */
> +               if (loongson_llsc_war())
> +                       uasm_i_sync(p, 0);
>                 UASM_i_LA(p, ptr, (unsigned long)tlb_do_page_fault_0);
>                 uasm_i_jr(p, ptr);
>
> @@ -1522,6 +1529,8 @@ static void build_loongson3_tlb_refill_handler(void)
>
>         if (check_for_high_segbits) {
>                 uasm_l_large_segbits_fault(&l, p);
> +               if (loongson_llsc_war())
> +                       uasm_i_sync(&p, 0);
>                 UASM_i_LA(&p, K1, (unsigned long)tlb_do_page_fault_0);
>                 uasm_i_jr(&p, K1);
>                 uasm_i_nop(&p);
> @@ -1620,6 +1629,8 @@ static void
>  iPTE_LW(u32 **p, unsigned int pte, unsigned int ptr)
>  {
>  #ifdef CONFIG_SMP
> +       if (loongson_llsc_war())
> +               uasm_i_sync(p, 0);
>  # ifdef CONFIG_PHYS_ADDR_T_64BIT
>         if (cpu_has_64bits)
>                 uasm_i_lld(p, pte, 0, ptr);
> @@ -2201,6 +2212,8 @@ static void build_r4000_tlb_load_handler(void)
>  #endif
>
>         uasm_l_nopage_tlbl(&l, p);
> +       if (loongson_llsc_war())
> +               uasm_i_sync(&p, 0);
>         build_restore_work_registers(&p);
>  #ifdef CONFIG_CPU_MICROMIPS
>         if ((unsigned long)tlb_do_page_fault_0 & 1) {
> @@ -2256,6 +2269,8 @@ static void build_r4000_tlb_store_handler(void)
>  #endif
>
>         uasm_l_nopage_tlbs(&l, p);
> +       if (loongson_llsc_war())
> +               uasm_i_sync(&p, 0);
>         build_restore_work_registers(&p);
>  #ifdef CONFIG_CPU_MICROMIPS
>         if ((unsigned long)tlb_do_page_fault_1 & 1) {
> @@ -2312,6 +2327,8 @@ static void build_r4000_tlb_modify_handler(void)
>  #endif
>
>         uasm_l_nopage_tlbm(&l, p);
> +       if (loongson_llsc_war())
> +               uasm_i_sync(&p, 0);
>         build_restore_work_registers(&p);
>  #ifdef CONFIG_CPU_MICROMIPS
>         if ((unsigned long)tlb_do_page_fault_1 & 1) {
> --
> 2.7.0
>
>
>
>



-- 
YunQiang Su





[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux