summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathan Kinder <nkinder@redhat.com>2008-10-17 22:15:26 +0000
committerNathan Kinder <nkinder@redhat.com>2008-10-17 22:15:26 +0000
commit656c5890b4bdbddb98d290c177cca4d4decaf0e4 (patch)
tree810c44d0b267e0cdf19eb2cce23ac24ed3477d0f
parent567be2b6589e5fa3e11bb103f046bec55f37dd6b (diff)
downloadds-656c5890b4bdbddb98d290c177cca4d4decaf0e4.tar.gz
ds-656c5890b4bdbddb98d290c177cca4d4decaf0e4.tar.xz
ds-656c5890b4bdbddb98d290c177cca4d4decaf0e4.zip
Related: 207457
Summary: Add 64-bit counter support (phase 1).
-rw-r--r--ldap/servers/slapd/slapi_counter.c330
-rw-r--r--ldap/servers/slapd/slapi_counter_sunos_sparcv9.il124
2 files changed, 454 insertions, 0 deletions
diff --git a/ldap/servers/slapd/slapi_counter.c b/ldap/servers/slapd/slapi_counter.c
new file mode 100644
index 00000000..1118d5b7
--- /dev/null
+++ b/ldap/servers/slapd/slapi_counter.c
@@ -0,0 +1,330 @@
+/** BEGIN COPYRIGHT BLOCK
+ * This Program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; version 2 of the License.
+ *
+ * This Program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * In addition, as a special exception, Red Hat, Inc. gives You the additional
+ * right to link the code of this Program with code not covered under the GNU
+ * General Public License ("Non-GPL Code") and to distribute linked combinations
+ * including the two, subject to the limitations in this paragraph. Non-GPL Code
+ * permitted under this exception must only link to the code of this Program
+ * through those well defined interfaces identified in the file named EXCEPTION
+ * found in the source code files (the "Approved Interfaces"). The files of
+ * Non-GPL Code may instantiate templates or use macros or inline functions from
+ * the Approved Interfaces without causing the resulting work to be covered by
+ * the GNU General Public License. Only Red Hat, Inc. may make changes or
+ * additions to the list of Approved Interfaces. You must obey the GNU General
+ * Public License in all respects for all of the Program code and other code used
+ * in conjunction with the Program except the Non-GPL Code covered by this
+ * exception. If you modify this file, you may extend this exception to your
+ * version of the file, but you are not obligated to do so. If you do not wish to
+ * provide this exception without modification, you must delete this exception
+ * statement from your version and license this file solely under the GPL without
+ * exception.
+ *
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "slap.h"
+
+#ifdef HPUX
+#ifdef ATOMIC_64BIT_OPERATIONS
+#include <machine/sys/inline.h>
+#endif
+#endif
+/*
+ * Counter Structure
+ */
+typedef struct slapi_counter {
+ PRUint64 value;
+#ifndef ATOMIC_64BIT_OPERATIONS
+ Slapi_Mutex *lock;
+#endif
+} slapi_counter;
+
+/*
+ * slapi_counter_new()
+ *
+ * Allocates and initializes a new Slapi_Counter.
+ */
+Slapi_Counter *slapi_counter_new()
+{
+ Slapi_Counter *counter = NULL;
+
+ counter = (Slapi_Counter *)slapi_ch_calloc(1, sizeof(Slapi_Counter));
+
+ if (counter != NULL) {
+ slapi_counter_init(counter);
+ }
+
+ return counter;
+}
+
+/*
+ * slapi_counter_init()
+ *
+ * Initializes a Slapi_Counter.
+ */
+void slapi_counter_init(Slapi_Counter *counter)
+{
+ if (counter != NULL) {
+#ifndef ATOMIC_64BIT_OPERATIONS
+ /* Create the lock if necessary. */
+ if (counter->lock == NULL) {
+ counter->lock = slapi_new_mutex();
+ }
+#endif
+
+ /* Set the value to 0. */
+ slapi_counter_set_value(counter, 0);
+ }
+}
+
+/*
+ * slapi_counter_destroy()
+ *
+ * Destroy's a Slapi_Counter and sets the
+ * pointer to NULL to prevent reuse.
+ */
+void slapi_counter_destroy(Slapi_Counter **counter)
+{
+ if ((counter != NULL) && (*counter != NULL)) {
+#ifndef ATOMIC_64BIT_OPERATIONS
+ slapi_destroy_mutex((*counter)->lock);
+#endif
+ slapi_ch_free((void **)counter);
+ }
+}
+
+/*
+ * slapi_counter_increment()
+ *
+ * Atomically increments a Slapi_Counter.
+ */
+PRUint64 slapi_counter_increment(Slapi_Counter *counter)
+{
+ return slapi_counter_add(counter, 1);
+}
+
+/*
+ * slapi_counter_decrement()
+ *
+ * Atomically decrements a Slapi_Counter. Note
+ * that this will not prevent you from wrapping
+ * around 0.
+ */
+PRUint64 slapi_counter_decrement(Slapi_Counter *counter)
+{
+ return slapi_counter_subtract(counter, 1);
+}
+
+/*
+ * slapi_counter_add()
+ *
+ * Atomically add a value to a Slapi_Counter.
+ */
+PRUint64 slapi_counter_add(Slapi_Counter *counter, PRUint64 addvalue)
+{
+ PRUint64 newvalue = 0;
+#ifdef HPUX
+ PRUint64 prev = 0;
+#endif
+
+ if (counter == NULL) {
+ return newvalue;
+ }
+
+#ifndef ATOMIC_64BIT_OPERATIONS
+ slapi_lock_mutex(counter->lock);
+ counter->value += addvalue;
+ newvalue = counter->value;
+ slapi_unlock_mutex(counter->lock);
+#else
+#ifdef LINUX
+ newvalue = __sync_add_and_fetch(&(counter->value), addvalue);
+#elif defined(SOLARIS)
+ newvalue = _sparcv9_AtomicAdd_il(&(counter->value), addvalue);
+#elif defined(HPUX)
+ /* fetchadd only works with values of 1, 4, 8, and 16. In addition, it requires
+ * it's argument to be an integer constant. */
+ if (addvalue == 1) {
+ newvalue = _Asm_fetchadd(_FASZ_D, _SEM_ACQ, &(counter->value), 1, _LDHINT_NONE);
+ newvalue += 1;
+ } else if (addvalue == 4) {
+ newvalue = _Asm_fetchadd(_FASZ_D, _SEM_ACQ, &(counter->value), 4, _LDHINT_NONE);
+ newvalue += 4;
+ } else if (addvalue == 8) {
+ newvalue = _Asm_fetchadd(_FASZ_D, _SEM_ACQ, &(counter->value), 8, _LDHINT_NONE);
+ newvalue += 8;
+ } else if (addvalue == 16) {
+ newvalue = _Asm_fetchadd(_FASZ_D, _SEM_ACQ, &(counter->value), 16, _LDHINT_NONE);
+ newvalue += 16;
+ } else {
+ /* For other values, we have to use cmpxchg. */
+ do {
+ prev = slapi_counter_get(counter);
+ newvalue = prev + addvalue;
+ /* Put prev in a register for cmpxchg to compare against */
+ _Asm_mov_to_ar(_AREG_CCV, prev);
+ } while (prev != _Asm_cmpxchg(_FASZ_D, _SEM_ACQ, &(counter->value), newvalue, _LDHINT_NONE));
+ }
+#endif
+#endif /* ATOMIC_64BIT_OPERATIONS */
+
+ return newvalue;
+}
+
+/*
+ * slapi_counter_subtract()
+ *
+ * Atomically subtract a value from a Slapi_Counter. Note
+ * that this will not prevent you from wrapping around 0.
+ */
+PRUint64 slapi_counter_subtract(Slapi_Counter *counter, PRUint64 subvalue)
+{
+ PRUint64 newvalue = 0;
+#ifdef HPUX
+ PRUint64 prev = 0;
+#endif
+
+ if (counter == NULL) {
+ return newvalue;
+ }
+
+#ifndef ATOMIC_64BIT_OPERATIONS
+ slapi_lock_mutex(counter->lock);
+ counter->value -= subvalue;
+ newvalue = counter->value;
+ slapi_unlock_mutex(counter->lock);
+#else
+#ifdef LINUX
+ newvalue = __sync_sub_and_fetch(&(counter->value), subvalue);
+#elif defined(SOLARIS)
+ newvalue = _sparcv9_AtomicSub_il(&(counter->value), subvalue);
+#elif defined(HPUX)
+ /* fetchadd only works with values of -1, -4, -8, and -16. In addition, it requires
+ * it's argument to be an integer constant. */
+ if (subvalue == 1) {
+ newvalue = _Asm_fetchadd(_FASZ_D, _SEM_ACQ, &(counter->value), -1, _LDHINT_NONE);
+ newvalue -= 1;
+ } else if (subvalue == 4) {
+ newvalue = _Asm_fetchadd(_FASZ_D, _SEM_ACQ, &(counter->value), -4, _LDHINT_NONE);
+ newvalue -= 4;
+ } else if (subvalue == 8) {
+ newvalue = _Asm_fetchadd(_FASZ_D, _SEM_ACQ, &(counter->value), -8, _LDHINT_NONE);
+ newvalue -= 8;
+ } else if (subvalue == 16) {
+ newvalue = _Asm_fetchadd(_FASZ_D, _SEM_ACQ, &(counter->value), -16, _LDHINT_NONE);
+ newvalue -= 16;
+ } else {
+ /* For other values, we have to use cmpxchg. */
+ do {
+ prev = slapi_counter_get(counter);
+ newvalue = prev - subvalue;
+ /* Put prev in a register for cmpxchg to compare against */
+ _Asm_mov_to_ar(_AREG_CCV, prev);
+ } while (prev != _Asm_cmpxchg(_FASZ_D, _SEM_ACQ, &(counter->value), newvalue, _LDHINT_NONE));
+ }
+#endif
+#endif /* ATOMIC_64BIT_OPERATIONS */
+
+ return newvalue;
+}
+
+/*
+ * slapi_counter_set_value()
+ *
+ * Atomically sets the value of a Slapi_Counter.
+ */
+PRUint64 slapi_counter_set_value(Slapi_Counter *counter, PRUint64 newvalue)
+{
+ PRUint64 value = 0;
+
+ if (counter == NULL) {
+ return value;
+ }
+
+#ifndef ATOMIC_64BIT_OPERATIONS
+ slapi_lock_mutex(counter->lock);
+ counter->value = newvalue;
+ slapi_unlock_mutex(counter->lock);
+ return newvalue;
+#else
+#ifdef LINUX
+ while (1) {
+ value = counter->value;
+ if (__sync_bool_compare_and_swap(&(counter->value), value, newvalue)) {
+ return newvalue;
+ }
+ }
+#elif defined(SOLARIS)
+ _sparcv9_AtomicSet_il(&(counter->value), newvalue);
+ return newvalue;
+#elif defined(HPUX)
+ do {
+ value = counter->value;
+ /* Put value in a register for cmpxchg to compare against */
+ _Asm_mov_to_ar(_AREG_CCV, value);
+ } while (value != _Asm_cmpxchg(_FASZ_D, _SEM_ACQ, &(counter->value), newvalue, _LDHINT_NONE));
+ return newvalue;
+#endif
+#endif /* ATOMIC_64BIT_OPERATIONS */
+}
+
+/*
+ * slapi_counter_get_value()
+ *
+ * Returns the value of a Slapi_Counter.
+ */
+PRUint64 slapi_counter_get_value(Slapi_Counter *counter)
+{
+ PRUint64 value = 0;
+
+ if (counter == NULL) {
+ return value;
+ }
+
+#ifndef ATOMIC_64BIT_OPERATIONS
+ slapi_lock_mutex(counter->lock);
+ value = counter->value;
+ slapi_unlock_mutex(counter->lock);
+#else
+#ifdef LINUX
+ while (1) {
+ value = counter->value;
+ if (__sync_bool_compare_and_swap(&(counter->value), value, value)) {
+ break;
+ }
+ }
+#elif defined(SOLARIS)
+ while (1) {
+ value = counter->value;
+ if (value == _sparcv9_AtomicSet_il(&(counter->value), value)) {
+ break;
+ }
+ }
+#elif defined(HPUX)
+ do {
+ value = counter->value;
+ /* Put value in a register for cmpxchg to compare against */
+ _Asm_mov_to_ar(_AREG_CCV, value);
+ } while (value != _Asm_cmpxchg(_FASZ_D, _SEM_ACQ, &(counter->value), value, _LDHINT_NONE));
+#endif
+#endif /* ATOMIC_64BIT_OPERATIONS */
+
+ return value;
+}
diff --git a/ldap/servers/slapd/slapi_counter_sunos_sparcv9.il b/ldap/servers/slapd/slapi_counter_sunos_sparcv9.il
new file mode 100644
index 00000000..ca8bdb84
--- /dev/null
+++ b/ldap/servers/slapd/slapi_counter_sunos_sparcv9.il
@@ -0,0 +1,124 @@
+// BEGIN COPYRIGHT BLOCK
+// This Program is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation; version 2 of the License.
+//
+// This Program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
+// Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// In addition, as a special exception, Red Hat, Inc. gives You the additional
+// right to link the code of this Program with code not covered under the GNU
+// General Public License ("Non-GPL Code") and to distribute linked combinations
+// including the two, subject to the limitations in this paragraph. Non-GPL Code
+// permitted under this exception must only link to the code of this Program
+// through those well defined interfaces identified in the file named EXCEPTION
+// found in the source code files (the "Approved Interfaces"). The files of
+// Non-GPL Code may instantiate templates or use macros or inline functions from
+// the Approved Interfaces without causing the resulting work to be covered by
+// the GNU General Public License. Only Red Hat, Inc. may make changes or
+// additions to the list of Approved Interfaces. You must obey the GNU General
+// Public License in all respects for all of the Program code and other code used
+// in conjunction with the Program except the Non-GPL Code covered by this
+// exception. If you modify this file, you may extend this exception to your
+// version of the file, but you are not obligated to do so. If you do not wish to
+// provide this exception without modification, you must delete this exception
+// statement from your version and license this file solely under the GPL without
+// exception.
+//
+//
+// The Original Code is the Netscape Portable Runtime (NSPR).
+//
+// The Initial Developer of the Original Code is
+// Netscape Communications Corporation.
+// Portions created by the Initial Developer are Copyright (C) 1998-2000
+// the Initial Developer. All Rights Reserved.
+//
+// The original code has been modified to support 64-bit atomic increments by
+// Red Hat, Inc. These portions are Copyright (C) 2008 Red Hat, Inc. All Rights
+// reserved.
+//
+// END COPYRIGHT BLOCK
+//
+
+// ======================================================================
+//
+// Perform the sequence a = b atomically with respect to other
+// fetch-and-stores to location a in a wait-free fashion.
+//
+// usage : newval = _sparcv9_AtomicSet(address, newval)
+//
+// -----------------------
+// Note on REGISTER USAGE:
+// as this is a LEAF procedure, a new stack frame is not created;
+// we use the caller's stack frame so what would normally be %i (input)
+// registers are actually %o (output registers). Also, we must not
+// overwrite the contents of %l (local) registers as they are not
+// assumed to be volatile during calls.
+//
+// So, the registers used are:
+// %o0 [input] - the address of the value to set
+// %o1 [input] - the new value to set for [%o0]
+// %o2 [local] - work register
+// %o3 [local] - work register
+// -----------------------
+
+.inline _sparcv9_AtomicSet_il, 0
+retryAS:
+ ld [%o0], %o2 ! set o2 to the current value
+ mov %o1, %o3 ! set up the new value
+ casx [%o0], %o2, %o3 ! atomically set if o0 hasn't changed
+ cmp %o2, %o3 ! see if we set the value
+ bne retryAS ! if not, try again
+ nop ! empty out the branch pipeline
+ retl ! return back to the caller
+ mov %o3, %o0 ! set the return code to the new value
+.end
+
+// ======================================================================
+//
+// Perform the sequence a = a + b atomically with respect to other
+// fetch-and-adds to location a in a wait-free fashion.
+//
+// usage : newval = _sparcv9_AtomicAdd(address, val)
+// return: the value after addition
+//
+
+.inline _sparcv9_AtomicAdd_il, 0
+retryAA:
+ ld [%o0], %o2 ! set o2 to the current value
+ addx %o2, %o1, %o3 ! calc the new value
+ mov %o3, %o4 ! save the return value
+ casx [%o0], %o2, %o3 ! atomically set if o0 hasn't changed
+ cmp %o2, %o3 ! see if we set the value
+ bne retryAA ! if not, try again
+ nop ! empty out the branch pipeline
+ retl ! return back to the caller
+ mov %o4, %o0 ! set the return code to the new value
+.end
+
+// ======================================================================
+//
+// Perform the sequence a = a - b atomically with respect to other
+// fetch-and-subs to location a in a wait-free fashion.
+//
+// usage : newval = _sparcv9_AtomicSub(address, val)
+// return: the value after addition
+//
+
+.inline _sparcv9_AtomicSub_il, 0
+retryAU:
+ ld [%o0], %o2 ! set o2 to the current value
+ subx %o2, %o1, %o3 ! calc the new value
+ mov %o3, %o4 ! save the return value
+ casx [%o0], %o2, %o3 ! atomically set if o0 hasn't changed
+ cmp %o2, %o3 ! see if we set the value
+ bne retryAU ! if not, try again
+ nop ! empty out the branch pipeline
+ retl ! return back to the caller
+ mov %o4, %o0 ! set the return code to the new value
+.end