Sort by bltincmd and remove lines that have been commented out for years.
[dragonfly.git] / libexec / rtld-elf / amd64 / 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/i386/lockdflt.c,v 1.5.2.4 2002/07/11 23:52:32 jdp Exp $
26  * $DragonFly: src/libexec/rtld-elf/amd64/lockdflt.c,v 1.1 2006/07/27 00:40:35 corecode Exp $
27  */
28
29 /*
30  * Thread locking implementation for the dynamic linker.
31  *
32  *   J. M. Mellor-Crummey and M. L. Scott. "Scalable Reader-Writer
33  *   Synchronization for Shared-Memory Multiprocessors." 3rd ACM Symp. on
34  *   Principles and Practice of Parallel Programming, April 1991.
35  *
36  * In this algorithm the lock is a single word.  Its low-order bit is
37  * set when a writer holds the lock.  The remaining high-order bits
38  * contain a count of readers desiring the lock.  The algorithm requires
39  * atomic "compare_and_store" and "add" operations.
40  */
41
42 #include <setjmp.h>
43 #include <signal.h>
44 #include <stdlib.h>
45 #include <time.h>
46
47 #include "debug.h"
48 #include "rtld.h"
49
50 #define WAFLAG          0x1     /* A writer holds the lock */
51 #define RC_INCR         0x2     /* Adjusts count of readers desiring lock */
52
53 typedef struct Struct_Lock {
54         volatile int lock;
55         void *base;
56 } Lock;
57
58 static sigset_t fullsigmask, oldsigmask;
59
60 static inline int
61 cmpxchgl(int old, int new, volatile int *m)
62 {
63         int result;
64
65         __asm __volatile ("lock; cmpxchgl %2, %0"
66             : "+m"(*m), "=a"(result)
67             : "r"(new), "1"(old)
68             : "cc");
69
70         return result;
71 }
72
73 static void *
74 lock_create(void *context)
75 {
76     void *base;
77     char *p;
78     uintptr_t r;
79     Lock *l;
80
81     /*
82      * Arrange for the lock to occupy its own cache line.  First, we
83      * optimistically allocate just a cache line, hoping that malloc
84      * will give us a well-aligned block of memory.  If that doesn't
85      * work, we allocate a larger block and take a well-aligned cache
86      * line from it.
87      */
88     base = xmalloc(CACHE_LINE_SIZE);
89     p = (char *)base;
90     if ((uintptr_t)p % CACHE_LINE_SIZE != 0) {
91         free(base);
92         base = xmalloc(2 * CACHE_LINE_SIZE);
93         p = (char *)base;
94         if ((r = (uintptr_t)p % CACHE_LINE_SIZE) != 0)
95             p += CACHE_LINE_SIZE - r;
96     }
97     l = (Lock *)p;
98     l->base = base;
99     l->lock = 0;
100     return l;
101 }
102
103 static void
104 lock_destroy(void *lock)
105 {
106     Lock *l = (Lock *)lock;
107
108     free(l->base);
109 }
110
111 /*
112  * Better reader/writer locks for the 80486 and later CPUs.
113  */
114 static void
115 rlock_acquire(void *lock)
116 {
117     Lock *l = (Lock *)lock;
118
119     atomic_add_int(&l->lock, RC_INCR);
120     while (l->lock & WAFLAG)
121             ;   /* Spin */
122 }
123
124 static void
125 wlock_acquire(void *lock)
126 {
127     Lock *l = (Lock *)lock;
128     sigset_t tmp_oldsigmask;
129
130     for ( ; ; ) {
131         sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask);
132         if (cmpxchgl(0, WAFLAG, &l->lock) == 0)
133             break;
134         sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL);
135     }
136     oldsigmask = tmp_oldsigmask;
137 }
138
139 static void
140 rlock_release(void *lock)
141 {
142     Lock *l = (Lock *)lock;
143
144     atomic_add_int(&l->lock, -RC_INCR);
145 }
146
147 static void
148 wlock_release(void *lock)
149 {
150     Lock *l = (Lock *)lock;
151
152     atomic_add_int(&l->lock, -WAFLAG);
153     sigprocmask(SIG_SETMASK, &oldsigmask, NULL);
154 }
155
156 void
157 lockdflt_init(LockInfo *li)
158 {
159     li->context = NULL;
160     li->context_destroy = NULL;
161     li->lock_create = lock_create;
162     li->lock_destroy = lock_destroy;
163     li->rlock_acquire = rlock_acquire;
164     li->wlock_acquire = wlock_acquire;
165     li->rlock_release = rlock_release;
166     li->wlock_release = wlock_release;
167     /*
168      * Construct a mask to block all signals except traps which might
169      * conceivably be generated within the dynamic linker itself.
170      */
171     sigfillset(&fullsigmask);
172     sigdelset(&fullsigmask, SIGILL);
173     sigdelset(&fullsigmask, SIGTRAP);
174     sigdelset(&fullsigmask, SIGABRT);
175     sigdelset(&fullsigmask, SIGEMT);
176     sigdelset(&fullsigmask, SIGFPE);
177     sigdelset(&fullsigmask, SIGBUS);
178     sigdelset(&fullsigmask, SIGSEGV);
179     sigdelset(&fullsigmask, SIGSYS);
180 }