Remove DEC Alpha and PC98 support.
[dragonfly.git] / libexec / rtld-elf / alpha / lockdflt.c
1 /*-
2  * Copyright 1999, 2000 John D. Polstra.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  * $FreeBSD: src/libexec/rtld-elf/alpha/lockdflt.c,v 1.5.2.3 2002/07/11 23:52:27 jdp Exp $
26  * $DragonFly: src/libexec/rtld-elf/alpha/Attic/lockdflt.c,v 1.2 2003/06/17 04:27:08 dillon Exp $
27  */
28
29 /*
30  * Thread locking implementation for the dynamic linker.
31  *
32  * We use the "simple, non-scalable reader-preference lock" from:
33  *
34  *   J. M. Mellor-Crummey and M. L. Scott. "Scalable Reader-Writer
35  *   Synchronization for Shared-Memory Multiprocessors." 3rd ACM Symp. on
36  *   Principles and Practice of Parallel Programming, April 1991.
37  *
38  * In this algorithm the lock is a single word.  Its low-order bit is
39  * set when a writer holds the lock.  The remaining high-order bits
40  * contain a count of readers desiring the lock.  The algorithm requires
41  * atomic "compare_and_store" and "add" operations, which we implement
42  * using assembly language sequences in "rtld_start.S".
43  */
44
45 #include <signal.h>
46 #include <stdlib.h>
47 #include <time.h>
48
49 #include "debug.h"
50 #include "rtld.h"
51
52 /*
53  * This value of CACHE_LINE_SIZE is conservative.  The actual size
54  * is 32 on the  21064, 21064A, 21066, 21066A, and 21164.  It is 64
55  * on the 21264.  Compaq recommends sequestering each lock in its own
56  * 128-byte block to allow for future implementations with larger
57  * cache lines.
58  */
59 #define CACHE_LINE_SIZE         128
60
61 #define WAFLAG          0x1     /* A writer holds the lock */
62 #define RC_INCR         0x2     /* Adjusts count of readers desiring lock */
63
64 typedef struct Struct_Lock {
65         volatile int lock;
66         void *base;
67 } Lock;
68
69 static sigset_t fullsigmask, oldsigmask;
70
71 static void *
72 lock_create(void *context)
73 {
74     void *base;
75     char *p;
76     uintptr_t r;
77     Lock *l;
78
79     /*
80      * Arrange for the lock to occupy its own cache line.  First, we
81      * optimistically allocate just a cache line, hoping that malloc
82      * will give us a well-aligned block of memory.  If that doesn't
83      * work, we allocate a larger block and take a well-aligned cache
84      * line from it.
85      */
86     base = xmalloc(CACHE_LINE_SIZE);
87     p = (char *)base;
88     if ((uintptr_t)p % CACHE_LINE_SIZE != 0) {
89         free(base);
90         base = xmalloc(2 * CACHE_LINE_SIZE);
91         p = (char *)base;
92         if ((r = (uintptr_t)p % CACHE_LINE_SIZE) != 0)
93             p += CACHE_LINE_SIZE - r;
94     }
95     l = (Lock *)p;
96     l->base = base;
97     l->lock = 0;
98     return l;
99 }
100
101 static void
102 lock_destroy(void *lock)
103 {
104     Lock *l = (Lock *)lock;
105
106     free(l->base);
107 }
108
109 static void
110 rlock_acquire(void *lock)
111 {
112     Lock *l = (Lock *)lock;
113
114     atomic_add_int(&l->lock, RC_INCR);
115     while (l->lock & WAFLAG)
116             ;   /* Spin */
117 }
118
119 static void
120 wlock_acquire(void *lock)
121 {
122     Lock *l = (Lock *)lock;
123     sigset_t tmp_oldsigmask;
124
125     for ( ; ; ) {
126         sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask);
127         if (cmp0_and_store_int(&l->lock, WAFLAG) == 0)
128             break;
129         sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL);
130     }
131     oldsigmask = tmp_oldsigmask;
132 }
133
134 static void
135 rlock_release(void *lock)
136 {
137     Lock *l = (Lock *)lock;
138
139     atomic_add_int(&l->lock, -RC_INCR);
140 }
141
142 static void
143 wlock_release(void *lock)
144 {
145     Lock *l = (Lock *)lock;
146
147     atomic_add_int(&l->lock, -WAFLAG);
148     sigprocmask(SIG_SETMASK, &oldsigmask, NULL);
149 }
150
151 void
152 lockdflt_init(LockInfo *li)
153 {
154     li->context = NULL;
155     li->lock_create = lock_create;
156     li->rlock_acquire = rlock_acquire;
157     li->wlock_acquire = wlock_acquire;
158     li->rlock_release = rlock_release;
159     li->wlock_release = wlock_release;
160     li->lock_destroy = lock_destroy;
161     li->context_destroy = NULL;
162     /*
163      * Construct a mask to block all signals except traps which might
164      * conceivably be generated within the dynamic linker itself.
165      */
166     sigfillset(&fullsigmask);
167     sigdelset(&fullsigmask, SIGILL);
168     sigdelset(&fullsigmask, SIGTRAP);
169     sigdelset(&fullsigmask, SIGABRT);
170     sigdelset(&fullsigmask, SIGEMT);
171     sigdelset(&fullsigmask, SIGFPE);
172     sigdelset(&fullsigmask, SIGBUS);
173     sigdelset(&fullsigmask, SIGSEGV);
174     sigdelset(&fullsigmask, SIGSYS);
175 }