| /* |
| * Atomic operations on 64-bit quantities. |
| * |
| * Copyright (C) 2017 Red Hat, Inc. |
| * |
| * Author: Paolo Bonzini <pbonzini@redhat.com> |
| * |
| * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| * See the COPYING file in the top-level directory. |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "qemu/atomic.h" |
| #include "qemu/stats64.h" |
| #include "qemu/processor.h" |
| |
| #ifndef CONFIG_ATOMIC64 |
| static inline void stat64_rdlock(Stat64 *s) |
| { |
| /* Keep out incoming writers to avoid them starving us. */ |
| atomic_add(&s->lock, 2); |
| |
| /* If there is a concurrent writer, wait for it. */ |
| while (atomic_read(&s->lock) & 1) { |
| cpu_relax(); |
| } |
| } |
| |
| static inline void stat64_rdunlock(Stat64 *s) |
| { |
| atomic_sub(&s->lock, 2); |
| } |
| |
| static inline bool stat64_wrtrylock(Stat64 *s) |
| { |
| return atomic_cmpxchg(&s->lock, 0, 1) == 0; |
| } |
| |
| static inline void stat64_wrunlock(Stat64 *s) |
| { |
| atomic_dec(&s->lock); |
| } |
| |
| uint64_t stat64_get(const Stat64 *s) |
| { |
| uint32_t high, low; |
| |
| stat64_rdlock((Stat64 *)s); |
| |
| /* 64-bit writes always take the lock, so we can read in |
| * any order. |
| */ |
| high = atomic_read(&s->high); |
| low = atomic_read(&s->low); |
| stat64_rdunlock((Stat64 *)s); |
| |
| return ((uint64_t)high << 32) | low; |
| } |
| |
| bool stat64_add32_carry(Stat64 *s, uint32_t low, uint32_t high) |
| { |
| uint32_t old; |
| |
| if (!stat64_wrtrylock(s)) { |
| cpu_relax(); |
| return false; |
| } |
| |
| /* 64-bit reads always take the lock, so they don't care about the |
| * order of our update. By updating s->low first, we can check |
| * whether we have to carry into s->high. |
| */ |
| old = atomic_fetch_add(&s->low, low); |
| high += (old + low) < old; |
| atomic_add(&s->high, high); |
| stat64_wrunlock(s); |
| return true; |
| } |
| |
| bool stat64_min_slow(Stat64 *s, uint64_t value) |
| { |
| uint32_t high, low; |
| uint64_t orig; |
| |
| if (!stat64_wrtrylock(s)) { |
| cpu_relax(); |
| return false; |
| } |
| |
| high = atomic_read(&s->high); |
| low = atomic_read(&s->low); |
| |
| orig = ((uint64_t)high << 32) | low; |
| if (value < orig) { |
| /* We have to set low before high, just like stat64_min reads |
| * high before low. The value may become higher temporarily, but |
| * stat64_get does not notice (it takes the lock) and the only ill |
| * effect on stat64_min is that the slow path may be triggered |
| * unnecessarily. |
| */ |
| atomic_set(&s->low, (uint32_t)value); |
| smp_wmb(); |
| atomic_set(&s->high, value >> 32); |
| } |
| stat64_wrunlock(s); |
| return true; |
| } |
| |
| bool stat64_max_slow(Stat64 *s, uint64_t value) |
| { |
| uint32_t high, low; |
| uint64_t orig; |
| |
| if (!stat64_wrtrylock(s)) { |
| cpu_relax(); |
| return false; |
| } |
| |
| high = atomic_read(&s->high); |
| low = atomic_read(&s->low); |
| |
| orig = ((uint64_t)high << 32) | low; |
| if (value > orig) { |
| /* We have to set low before high, just like stat64_max reads |
| * high before low. The value may become lower temporarily, but |
| * stat64_get does not notice (it takes the lock) and the only ill |
| * effect on stat64_max is that the slow path may be triggered |
| * unnecessarily. |
| */ |
| atomic_set(&s->low, (uint32_t)value); |
| smp_wmb(); |
| atomic_set(&s->high, value >> 32); |
| } |
| stat64_wrunlock(s); |
| return true; |
| } |
| #endif |