apr-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Justin Erenkrantz <jerenkra...@apache.org>
Subject [PATCH] Use handcoded assembly on Linux for atomics
Date Mon, 22 Apr 2002 08:42:01 GMT
I *really* need help testing this.  Create atomic/linux/Makefile.in
atomic/linux/apr_atomic_linux.c from the fragments below and and
apply these patches.

This might get us out of the quagmire we have with the atomics on
Linux.  Basically, this is an import of the FreeBSD code for doing
atomics on x86, Alpha, and Sparc.  The other platforms should
default to the generic implementation.  I imagine if someone is
interested in the others, they can step up.  I've had my fill of
processors for one night.

This doesn't implement CAS on x86 or Alpha.  I think x86 can do
that, but I'll leave that up to someone else to figure out.  =)

I tried to keep the general idea of the code as close as I could to
the original FreeBSD code, but I did make a bunch of formatting
changes and attempted to verify the code as much as I could (well,
I didn't pay much attention to Alpha's ASM).

Since I don't have access to a Linux/Alpha or Linux/Sparc box, I
don't know what the right processor #define would be - so I took
a guess.

This passes testatomic here on my Linux/Intel box.  I really have
no idea how thorough testatomic is, but I take it as evidence that
this doesn't completely screw up.

Comments?  Some of the code is a bit rough (esp. the XSTRING
junk...).  -- justin

Index: configure.in
===================================================================
RCS file: /home/cvs/apr/configure.in,v
retrieving revision 1.433
diff -u -r1.433 configure.in
--- configure.in	22 Apr 2002 01:24:48 -0000	1.433
+++ configure.in	22 Apr 2002 08:26:02 -0000
@@ -398,6 +398,10 @@
             ;;
        esac
        ;;
+   *linux*)
+       OSDIR="linux"
+       eolstr="\\n"
+       ;;
    *)
        OSDIR="unix"
        eolstr="\\n"
Index: include/apr_atomic.h
===================================================================
RCS file: /home/cvs/apr/include/apr_atomic.h,v
retrieving revision 1.23
diff -u -r1.23 apr_atomic.h
--- include/apr_atomic.h	20 Apr 2002 22:06:38 -0000	1.23
+++ include/apr_atomic.h	22 Apr 2002 08:26:02 -0000
@@ -155,19 +155,42 @@
 #define apr_atomic_init(pool)        APR_SUCCESS
 
 #elif defined(__linux)
+/* For now, these defines are common. */
+#define apr_atomic_t apr_uint32_t
 
-#include <stdlib.h>
-#include <asm/atomic.h>
-#include <asm/system.h>
-#define apr_atomic_t atomic_t
+#ifdef __i386__
+#define apr_atomic_read(p) *p
+void apr_atomic_set(volatile apr_atomic_t *mem, apr_uint32_t val);
+void apr_atomic_add(volatile apr_atomic_t *mem, apr_uint32_t val);
+#define apr_atomic_inc(mem) apr_atomic_add(mem, 1)
+void apr_atomic_dec(volatile apr_atomic_t *mem);
+#define APR_ATOMIC_NEED_CAS_DEFAULT 1
 
-#define apr_atomic_add(mem, val)     atomic_add(val,mem)
-#define apr_atomic_dec(mem)          !atomic_dec_and_test(mem)
-#define apr_atomic_inc(mem)          atomic_inc(mem)
-#define apr_atomic_set(mem, val)     atomic_set(mem, val)
-#define apr_atomic_read(mem)         atomic_read(mem)
-#if defined(cmpxchg)
+#elif __ALPHA__
+#define apr_atomic_read(p)  *p
+void apr_atomic_set(volatile apr_atomic_t *mem, apr_uint32_t val);
+void apr_atomic_add(volatile apr_atomic_t *mem, apr_uint32_t val);
+#define apr_atomic_inc(mem) apr_atomic_add(mem, 1)
+#define apr_atomic_dec(mem) apr_atomic_subtract_alpha(mem, 1)
+void apr_atomic_subtract_alpha(volatile apr_atomic_t *mem, apr_uint32_t val);
+#define APR_ATOMIC_NEED_CAS_DEFAULT 1
+
+#elif __SPARC__
+#define apr_atomic_t apr_uint32_t
 #define apr_atomic_init(pool)        APR_SUCCESS
+apr_uint32_t apr_atomic_read(volatile apr_atomic_t *mem);
+void apr_atomic_set(volatile apr_atomic_t *mem, apr_uint32_t val);
+void apr_atomic_add(volatile apr_atomic_t *mem, apr_uint32_t val);
+#define apr_atomic_inc(mem) apr_atomic_add(mem, 1)
+void apr_atomic_dec(volatile apr_atomic_t *mem);
+apr_uint32_t apr_atomic_cas(volatile apr_uint32_t *mem, long with, long cmp);
+#else
+/* We don't know what you are. */
+#define APR_ATOMIC_NEED_DEFAULT 1
+#define APR_ATOMIC_NEED_CAS_DEFAULT 1
+#endif
+
+#if defined(cmpxchg)
 #define apr_atomic_cas(mem,with,cmp) cmpxchg(mem,cmp,with)
 #else
 #define APR_ATOMIC_NEED_CAS_DEFAULT 1

Index: atomic/linux/Makefile.in
===================================================================
srcdir = @srcdir@
VPATH = @srcdir@

TARGETS = apr_atomic_linux.lo

# bring in rules.mk for standard functionality
@INCLUDE_RULES@

DEFOSDIR=$(INCDIR)/arch/@DEFAULT_OSDIR@
INCDIR=../../include
INCLUDES=-I$(INCDIR) -I$(DEFOSDIR)

# DO NOT REMOVE
Index: atomic/linux/apr_atomic_linux.c
===================================================================
/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 * This implementation consists of segments of code from FreeBSD.
 * Please see the appropriate sections within for the specific
 * licensing terms (each platform may have different copyright holders).
 */

#include "apr.h"
#include "apr_atomic.h"
#include "apr_thread_mutex.h"

/* If there are no threads available, we don't need to use atomics.  */
#if APR_HAS_THREADS

#ifdef __i386__
/* Intel x86 architecture.
 *
 * This implementation has been borrowed from FreeBSD's
 * src/sys/i386/include/atomic.h.
 *
 *-
 * Copyright (c) 1998 Doug Rabson
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef __XSTRING
#include "apr_version.h"
#define __XSTRING(s) APR_STRINGIFY(s)
#endif
/* Note that we will *always* define MPLOCKED because we may run
 * on SMP kernels.  So, we must be very pessimistic.  We will allow
 * a sneaky person to come in and undef this variable if they know
 * what they are doing.
 */
#define MPLOCKED    lock ;

/*
 * The assembly is volatilized to demark potential before-and-after side
 * effects if an interrupt or SMP collision were to occur.
 */
#define APR_ATOMIC_ASM(NAME, TYPE, OP, CONS, V) \
void                                            \
apr_atomic_##NAME(volatile apr_atomic_t *mem, apr_u##TYPE##_t val) \
{                                               \
    __asm __volatile(__XSTRING(MPLOCKED) OP     \
             : "+m" (*mem)                      \
             : CONS (V));                       \
}

#define APR_ATOMIC_ASM1(NAME, TYPE, OP, CONS, V) \
void                                            \
apr_atomic_##NAME(volatile apr_atomic_t *mem) \
{                                               \
    __asm __volatile(__XSTRING(MPLOCKED) OP     \
             : "+m" (*mem)                      \
             : CONS (V));                       \
}

APR_ATOMIC_ASM(set,  int32, "orl %1,%0",  "ir", val)
APR_ATOMIC_ASM(add,  int32, "addl %1,%0", "ir", val)
APR_ATOMIC_ASM1(dec, int32, "subl %1,%0", "ir", 1)

#elif __ALPHA__
/* DEC Alpha architecture.
 *
 * This implementation has been borrowed from FreeBSD's
 * src/sys/alpha/include/atomic.h.
 *
 *-
 * Copyright (c) 1998 Doug Rabson
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
void apr_atomic_set(volatile apr_atomic_t *mem, apr_uint32_t val)
{
    apr_uint32_t temp;

    __asm __volatile (
        "1:\tldl_l %0, %2\n\t"      /* load old value */
        "bis %0, %3, %0\n\t"        /* calculate new value */
        "stl_c %0, %1\n\t"      /* attempt to store */
        "beq %0, 2f\n\t"        /* spin if failed */
        "mb\n\t"            /* drain to memory */
        ".section .text3,\"ax\"\n"  /* improve branch prediction */
        "2:\tbr 1b\n"           /* try again */
        ".previous\n"
        : "=&r" (temp), "=m" (*mem)
        : "m" (*mem), "r" (val)
        : "memory");
}

void apr_atomic_add(volatile apr_atomic_t *mem, apr_uint32_t val)
{
    apr_uint32_t temp;

    __asm __volatile (
        "1:\tldl_l %0, %2\n\t"      /* load old value */
        "addl %0, %3, %0\n\t"       /* calculate new value */
        "stl_c %0, %1\n\t"          /* attempt to store */
        "beq %0, 2f\n\t"            /* spin if failed */
        "mb\n\t"                    /* drain to memory */
        ".section .text3,\"ax\"\n"  /* improve branch prediction */
        "2:\tbr 1b\n"               /* try again */
        ".previous\n"
        : "=&r" (temp), "=m" (*mem)
        : "m" (*mem), "r" (val)
        : "memory");
}

/* Even though we do not directly use it, we must implement this here
 * so that we can implment apr_atomic_dec. */
void apr_atomic_subtract_alpha(volatile apr_atomic_t *mem, apr_uint32_t val)
{
    apr_uint32_t temp;

    __asm __volatile (
        "1:\tldl_l %0, %2\n\t"      /* load old value */
        "subl %0, %3, %0\n\t"       /* calculate new value */
        "stl_c %0, %1\n\t"          /* attempt to store */
        "beq %0, 2f\n\t"            /* spin if failed */
        "mb\n\t"                    /* drain to memory */
        ".section .text3,\"ax\"\n"  /* improve branch prediction */
        "2:\tbr 1b\n"               /* try again */
        ".previous\n"
        : "=&r" (temp), "=m" (*mem)
        : "m" (*mem), "r" (val)
        : "memory");
}

#elif __SPARC__
/* Sun SPARC 64-bit architecture.
 *
 * This implementation has been borrowed from FreeBSD's
 * src/sys/sparc64/include/atomic.h.
 *
 *-
 * Copyright (c) 1998 Doug Rabson
 * Copyright (c) 2001 Jake Burkholder.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Various simple arithmetic on memory which is atomic in the presence
 * of interrupts and multiple processors.  See atomic(9) for details.
 * Note that efficient hardware support exists only for the 32 and 64
 * bit variants; the 8 and 16 bit versions are not provided and should
 * not be used in MI code.
 *
 * This implementation takes advantage of the fact that the sparc64
 * cas instruction is both a load and a store.  The loop is often coded
 * as follows:
 *
 *  do {
 *      expect = *p;
 *      new = expect + 1;
 *  } while (cas(p, expect, new) != expect);
 *
 * which performs an unnnecessary load on each iteration that the cas
 * operation fails.  Modified as follows:
 *
 *  expect = *p;
 *  for (;;) {
 *      new = expect + 1;
 *      result = cas(p, expect, new);
 *      if (result == expect)
 *          break;
 *      expect = result;
 *  }
 *
 * the return value of cas is used to avoid the extra reload.  At the
 * time of writing, with gcc version 2.95.3, the branch for the if
 * statement is predicted incorrectly as not taken, rather than taken.
 * It is expected that the branch prediction hints available in gcc 3.0,
 * __builtin_expect, will allow better code to be generated.
 *
 */

/* We are userspace, not kernel space, so we must define this.  If you
 * are cute, you can find out the right value for kernel assemblers.  */
#define __ASI_ATOMIC    ASI_P

#define itype(sz)   apr_uint ## sz ## _t

/* If we were to use 64-bits, we would use casxa */
#define atomic_cas(p, e, s)     casa(p, e, s, __ASI_ATOMIC)

#define atomic_op(p, op, v, sz) do {                    \
    itype(sz) e, r, s;                                  \
    for (e = *(volatile itype(sz) *)p;; e = r) {        \
        s = e op v;                                     \
        r = atomic_cas(p, e, s);                        \
        if (r == e)                                     \
            break;                                      \
    }                                                   \
} while (0)

#define atomic_func(name, p, op, v, sz)                 \
void apr_atomic_##name##(volatile apr_atomic_t *mem, apr_uint32_t val) { \
    atomic_op(p, op, v, sz);                            \
}

#define apr_atomic_set(mem, val) atomic_func(set, mem, |, val, 32)
#define apr_atomic_add(mem, val) atomic_op(add, mem, +, val, 32)
#define apr_atomic_dec(mem) atomic_op(dec, mem, -, 1, 32)

apr_uint32_t apr_atomic_read(volatile apr_atomic_t *mem)
{
    return ((apr_uint32_t)atomic_cas(mem, 0, 0, 32));
}

apr_uint32_t apr_atomic_cas(volatile apr_uint32_t *mem, long with, long cmp) {
    /* FIXME: Based on analysis of SPARC assembly code for Solaris in APR,
     * I think the parameters are reversed in APR's apr_atomic_cas call.
     */
    return ((apr_uint32_t)atomic_cas(mem, cmp, with, cmp, 32));
}
#else
#warning Your platform is not supported with processor-level atomics.  Switching to generic
fallback implementation.
#include "../unix/apr_atomic.c"
#endif /* Processor selection */

/* Not all implementations support CAS, so on those, we must include the
 * standard.
 */
#ifdef APR_ATOMIC_NEED_CAS_DEFAULT
#include "../unix/apr_atomic.c"
#endif
#endif /* APR_HAS_THREADS */

Mime
View raw message