Blame source/vdo/base/atomic.h

Packit Service 310c69
/*
Packit Service 310c69
 * Copyright (c) 2020 Red Hat, Inc.
Packit Service 310c69
 *
Packit Service 310c69
 * This program is free software; you can redistribute it and/or
Packit Service 310c69
 * modify it under the terms of the GNU General Public License
Packit Service 310c69
 * as published by the Free Software Foundation; either version 2
Packit Service 310c69
 * of the License, or (at your option) any later version.
Packit Service 310c69
 * 
Packit Service 310c69
 * This program is distributed in the hope that it will be useful,
Packit Service 310c69
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 310c69
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 310c69
 * GNU General Public License for more details.
Packit Service 310c69
 * 
Packit Service 310c69
 * You should have received a copy of the GNU General Public License
Packit Service 310c69
 * along with this program; if not, write to the Free Software
Packit Service 310c69
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service 310c69
 * 02110-1301, USA. 
Packit Service 310c69
 *
Packit Service 310c69
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/atomic.h#2 $
Packit Service 310c69
 */
Packit Service 310c69
Packit Service 310c69
#ifndef ATOMIC_H
Packit Service 310c69
#define ATOMIC_H
Packit Service 310c69
Packit Service 310c69
#include "atomicDefs.h"
Packit Service 310c69
#include "compiler.h"
Packit Service 310c69
#include "typeDefs.h"
Packit Service 310c69
Packit Service 310c69
#define ATOMIC_INITIALIZER(value) { (value) }
Packit Service 310c69
Packit Service 310c69
typedef struct {
Packit Service 310c69
  atomic_t value;
Packit Service 310c69
} __attribute__((aligned(4))) Atomic32;
Packit Service 310c69
Packit Service 310c69
typedef struct {
Packit Service 310c69
  atomic64_t value;
Packit Service 310c69
} __attribute__((aligned(8))) Atomic64;
Packit Service 310c69
Packit Service 310c69
typedef struct {
Packit Service 310c69
  Atomic32 value;
Packit Service 310c69
} __attribute__((aligned(4))) AtomicBool;
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Memory load operations that precede this fence will be prevented from
Packit Service 310c69
 * changing order with any that follow this fence, by either the compiler or
Packit Service 310c69
 * the CPU. This can be used to ensure that the load operations accessing
Packit Service 310c69
 * the fields of a structure are not re-ordered so they actually take effect
Packit Service 310c69
 * before a pointer to the structure is resolved.
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE void loadFence(void)
Packit Service 310c69
{
Packit Service 310c69
  smp_rmb();
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Memory store operations that precede this fence will be prevented from
Packit Service 310c69
 * changing order with any that follow this fence, by either the compiler or
Packit Service 310c69
 * the CPU. This can be used to ensure that the store operations initializing
Packit Service 310c69
 * the fields of a structure are not re-ordered so they actually take effect
Packit Service 310c69
 * after a pointer to the structure is published.
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE void storeFence(void)
Packit Service 310c69
{
Packit Service 310c69
  smp_wmb();
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Generate a full memory fence for the compiler and CPU. Load and store
Packit Service 310c69
 * operations issued before the fence will not be re-ordered with operations
Packit Service 310c69
 * issued after the fence.
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE void memoryFence(void)
Packit Service 310c69
{
Packit Service 310c69
  smp_mb();
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Access the value of a 32-bit atomic variable, ensuring that the load is not
Packit Service 310c69
 * re-ordered by the compiler or CPU with any subsequent load operations.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom  a pointer to the atomic variable to access
Packit Service 310c69
 *
Packit Service 310c69
 * @return the value that was in the atom at the moment it was accessed
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE uint32_t atomicLoad32(const Atomic32 *atom)
Packit Service 310c69
{
Packit Service 310c69
  uint32_t value = atomic_read(&atom->value);
Packit Service 310c69
  loadFence();
Packit Service 310c69
  return value;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Access the value of a 64-bit atomic variable, ensuring that the memory load
Packit Service 310c69
 * is not re-ordered by the compiler or CPU with any subsequent load
Packit Service 310c69
 * operations.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom  a pointer to the atomic variable to access
Packit Service 310c69
 *
Packit Service 310c69
 * @return the value that was in the atom at the moment it was accessed
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE uint64_t atomicLoad64(const Atomic64 *atom)
Packit Service 310c69
{
Packit Service 310c69
  uint64_t value = atomic64_read(&atom->value);
Packit Service 310c69
  loadFence();
Packit Service 310c69
  return value;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Access the value of a boolean atomic variable, ensuring that the load is not
Packit Service 310c69
 * re-ordered by the compiler or CPU with any subsequent load operations.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom  a pointer to the atomic variable to access
Packit Service 310c69
 *
Packit Service 310c69
 * @return the value that was in the atom at the moment it was accessed
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE bool atomicLoadBool(const AtomicBool *atom)
Packit Service 310c69
{
Packit Service 310c69
  return (atomicLoad32(&atom->value) > 0);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Set the value of a 32-bit atomic variable, ensuring that the memory store
Packit Service 310c69
 * operation is not re-ordered by the compiler or CPU with any preceding store
Packit Service 310c69
 * operations.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom      a pointer to the atomic variable to modify
Packit Service 310c69
 * @param newValue  the value to assign to the atomic variable
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE void atomicStore32(Atomic32 *atom, uint32_t newValue)
Packit Service 310c69
{
Packit Service 310c69
  storeFence();
Packit Service 310c69
  atomic_set(&atom->value, newValue);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Set the value of a 64-bit atomic variable, ensuring that the memory store
Packit Service 310c69
 * operation is not re-ordered by the compiler or CPU with any preceding store
Packit Service 310c69
 * operations.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom      a pointer to the atomic variable to modify
Packit Service 310c69
 * @param newValue  the value to assign to the atomic variable
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE void atomicStore64(Atomic64 *atom, uint64_t newValue)
Packit Service 310c69
{
Packit Service 310c69
  storeFence();
Packit Service 310c69
  atomic64_set(&atom->value, newValue);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Set the value of a boolean atomic variable, ensuring that the memory store
Packit Service 310c69
 * operation is not re-ordered by the compiler or CPU with any preceding store
Packit Service 310c69
 * operations.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom      a pointer to the atomic variable to modify
Packit Service 310c69
 * @param newValue  the value to assign to the atomic variable
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE void atomicStoreBool(AtomicBool *atom, bool newValue)
Packit Service 310c69
{
Packit Service 310c69
  atomicStore32(&atom->value, (newValue ? 1 : 0));
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Add a 32-bit signed delta to a 32-bit atomic variable.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom   a pointer to the atomic variable
Packit Service 310c69
 * @param delta  the value to be added (or subtracted) from the variable
Packit Service 310c69
 *
Packit Service 310c69
 * @return       the new value of the atom after the add operation
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE uint32_t atomicAdd32(Atomic32 *atom, int32_t delta)
Packit Service 310c69
{
Packit Service 310c69
  return atomic_add_return(delta, &atom->value);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Add a 64-bit signed delta to a 64-bit atomic variable.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom   a pointer to the atomic variable
Packit Service 310c69
 * @param delta  the value to be added (or subtracted) from the variable
Packit Service 310c69
 *
Packit Service 310c69
 * @return       the new value of the atom after the add operation
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE uint64_t atomicAdd64(Atomic64 *atom, int64_t delta)
Packit Service 310c69
{
Packit Service 310c69
  return atomic64_add_return(delta, &atom->value);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Atomic 32-bit compare-and-swap. If the atom is identical to a required
Packit Service 310c69
 * value, atomically replace it with the new value and return true, otherwise
Packit Service 310c69
 * do nothing and return false.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom           a pointer to the atomic variable
Packit Service 310c69
 * @param requiredValue  the value that must be present to perform the swap
Packit Service 310c69
 * @param newValue       the value to be swapped for the required value
Packit Service 310c69
 *
Packit Service 310c69
 * @return               true if the atom was changed, false otherwise
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE bool compareAndSwap32(Atomic32 *atom,
Packit Service 310c69
                                    uint32_t  requiredValue,
Packit Service 310c69
                                    uint32_t  newValue)
Packit Service 310c69
{
Packit Service 310c69
  /*
Packit Service 310c69
   * Our initial implementation, for x86, effectively got a full
Packit Service 310c69
   * memory barrier because of how "lock cmpxchg" operates. The
Packit Service 310c69
   * atomic_cmpxchg interface provides for a full barrier *if* the
Packit Service 310c69
   * exchange is done, but not necessarily if it is not.
Packit Service 310c69
   *
Packit Service 310c69
   * Do we need the full barrier always? We need to investigate that,
Packit Service 310c69
   * as part of (eventually) converting to using that API directly.
Packit Service 310c69
   * For now, play it safe, and ensure the same behavior on other
Packit Service 310c69
   * architectures too.
Packit Service 310c69
   */
Packit Service 310c69
#ifndef __x86_64__
Packit Service 310c69
  smp_mb();
Packit Service 310c69
#endif
Packit Service 310c69
  int oldValue = atomic_cmpxchg(&atom->value, requiredValue, newValue);
Packit Service 310c69
#ifndef __x86_64__
Packit Service 310c69
  smp_mb();
Packit Service 310c69
#endif
Packit Service 310c69
  return requiredValue == (uint32_t) oldValue;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Atomic 64-bit compare-and-swap. If the atom is identical to a required
Packit Service 310c69
 * value, atomically replace it with the new value and return true, otherwise
Packit Service 310c69
 * do nothing and return false.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom           a pointer to the atomic variable
Packit Service 310c69
 * @param requiredValue  the value that must be present to perform the swap
Packit Service 310c69
 * @param newValue       the value to be swapped for the required value
Packit Service 310c69
 *
Packit Service 310c69
 * @return               true if the atom was changed, false otherwise
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE bool compareAndSwap64(Atomic64 *atom,
Packit Service 310c69
                                    uint64_t  requiredValue,
Packit Service 310c69
                                    uint64_t  newValue)
Packit Service 310c69
{
Packit Service 310c69
#ifndef __x86_64__
Packit Service 310c69
  smp_mb();
Packit Service 310c69
#endif
Packit Service 310c69
  long oldValue = atomic64_cmpxchg(&atom->value, requiredValue, newValue);
Packit Service 310c69
#ifndef __x86_64__
Packit Service 310c69
  smp_mb();
Packit Service 310c69
#endif
Packit Service 310c69
  return requiredValue == (uint64_t) oldValue;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Atomic boolean compare-and-swap. If the atom is identical to a required
Packit Service 310c69
 * value, atomically replace it with the new value and return true, otherwise
Packit Service 310c69
 * do nothing and return false.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom           a pointer to the atomic variable
Packit Service 310c69
 * @param requiredValue  the value that must be present to perform the swap
Packit Service 310c69
 * @param newValue       the value to be swapped for the required value
Packit Service 310c69
 *
Packit Service 310c69
 * @return               true if the atom was changed, false otherwise
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE bool compareAndSwapBool(AtomicBool *atom,
Packit Service 310c69
                                      bool        requiredValue,
Packit Service 310c69
                                      bool        newValue)
Packit Service 310c69
{
Packit Service 310c69
  return compareAndSwap32(&atom->value, (requiredValue ? 1 : 0),
Packit Service 310c69
                          (newValue ? 1 : 0));
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Access the value of a 32-bit atomic variable using relaxed memory order,
Packit Service 310c69
 * without any compiler or CPU fences.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom  a pointer to the atomic variable to access
Packit Service 310c69
 *
Packit Service 310c69
 * @return the value that was in the atom at the moment it was accessed
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE uint32_t relaxedLoad32(const Atomic32 *atom)
Packit Service 310c69
{
Packit Service 310c69
  return atomic_read(&atom->value);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Access the value of a 64-bit atomic variable using relaxed memory order,
Packit Service 310c69
 * without any compiler or CPU fences.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom  a pointer to the atomic variable to access
Packit Service 310c69
 *
Packit Service 310c69
 * @return the value that was in the atom at the moment it was accessed
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE uint64_t relaxedLoad64(const Atomic64 *atom)
Packit Service 310c69
{
Packit Service 310c69
  return atomic64_read(&atom->value);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Access the value of a boolean atomic variable using relaxed memory order,
Packit Service 310c69
 * without any compiler or CPU fences.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom  a pointer to the atomic variable to access
Packit Service 310c69
 *
Packit Service 310c69
 * @return the value that was in the atom at the moment it was accessed
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE bool relaxedLoadBool(const AtomicBool *atom)
Packit Service 310c69
{
Packit Service 310c69
  return (relaxedLoad32(&atom->value) > 0);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Set the value of a 32-bit atomic variable using relaxed memory order,
Packit Service 310c69
 * without any compiler or CPU fences.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom      a pointer to the atomic variable to modify
Packit Service 310c69
 * @param newValue  the value to assign to the atomic variable
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE void relaxedStore32(Atomic32 *atom, uint32_t newValue)
Packit Service 310c69
{
Packit Service 310c69
  atomic_set(&atom->value, newValue);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Set the value of a 64-bit atomic variable using relaxed memory order,
Packit Service 310c69
 * without any compiler or CPU fences.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom      a pointer to the atomic variable to modify
Packit Service 310c69
 * @param newValue  the value to assign to the atomic variable
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE void relaxedStore64(Atomic64 *atom, uint64_t newValue)
Packit Service 310c69
{
Packit Service 310c69
  atomic64_set(&atom->value, newValue);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Set the value of a boolean atomic variable using relaxed memory order,
Packit Service 310c69
 * without any compiler or CPU fences.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom      a pointer to the atomic variable to modify
Packit Service 310c69
 * @param newValue  the value to assign to the atomic variable
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE void relaxedStoreBool(AtomicBool *atom, bool newValue)
Packit Service 310c69
{
Packit Service 310c69
  relaxedStore32(&atom->value, (newValue ? 1 : 0));
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Non-atomically add a 32-bit signed delta to a 32-bit atomic variable,
Packit Service 310c69
 * without any compiler or CPU fences.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom   a pointer to the atomic variable
Packit Service 310c69
 * @param delta  the value to be added (or subtracted) from the variable
Packit Service 310c69
 *
Packit Service 310c69
 * @return       the new value of the atom after the add operation
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE uint32_t relaxedAdd32(Atomic32 *atom, int32_t delta)
Packit Service 310c69
{
Packit Service 310c69
  uint32_t newValue = (relaxedLoad32(atom) + delta);
Packit Service 310c69
  relaxedStore32(atom, newValue);
Packit Service 310c69
  return newValue;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Non-atomically add a 64-bit signed delta to a 64-bit atomic variable,
Packit Service 310c69
 * without any compiler or CPU fences.
Packit Service 310c69
 *
Packit Service 310c69
 * @param atom   a pointer to the atomic variable
Packit Service 310c69
 * @param delta  the value to be added (or subtracted) from the variable
Packit Service 310c69
 *
Packit Service 310c69
 * @return       the new value of the atom after the add operation
Packit Service 310c69
 **/
Packit Service 310c69
static INLINE uint64_t relaxedAdd64(Atomic64 *atom, int64_t delta)
Packit Service 310c69
{
Packit Service 310c69
  uint64_t newValue = (relaxedLoad64(atom) + delta);
Packit Service 310c69
  relaxedStore64(atom, newValue);
Packit Service 310c69
  return newValue;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
#endif /* ATOMIC_H */