1/*
2 * Copyright (c) 2001 Alexander Kabaev
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 *    in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $FreeBSD$
29 */
30#include <sys/cdefs.h>
31#include <stdlib.h>
32
33#include "rtld_lock.h"
34#include "thr_private.h"
35
36static int	_thr_rtld_clr_flag(int);
37static void	*_thr_rtld_lock_create(void);
38static void	_thr_rtld_lock_destroy(void *);
39static void	_thr_rtld_lock_release(void *);
40static void	_thr_rtld_rlock_acquire(void *);
41static int	_thr_rtld_set_flag(int);
42static void	_thr_rtld_wlock_acquire(void *);
43
44#ifdef NOTYET
45static void *
46_thr_rtld_lock_create(void)
47{
48	pthread_rwlock_t prwlock;
49	if (_pthread_rwlock_init(&prwlock, NULL))
50		return (NULL);
51	return (prwlock);
52}
53
54static void
55_thr_rtld_lock_destroy(void *lock)
56{
57	pthread_rwlock_t prwlock;
58
59	prwlock = (pthread_rwlock_t)lock;
60	if (prwlock != NULL)
61		_pthread_rwlock_destroy(&prwlock);
62}
63
64static void
65_thr_rtld_rlock_acquire(void *lock)
66{
67	pthread_rwlock_t prwlock;
68
69	prwlock = (pthread_rwlock_t)lock;
70	_thr_rwlock_rdlock(&prwlock);
71}
72
73static void
74_thr_rtld_wlock_acquire(void *lock)
75{
76	pthread_rwlock_t prwlock;
77
78	prwlock = (pthread_rwlock_t)lock;
79	_thr_rwlock_wrlock(&prwlock);
80}
81
82static void
83_thr_rtld_lock_release(void *lock)
84{
85	pthread_rwlock_t prwlock;
86
87	prwlock = (pthread_rwlock_t)lock;
88	_thr_rwlock_unlock(&prwlock);
89}
90
91
92static int
93_thr_rtld_set_flag(int mask)
94{
95	struct pthread *curthread;
96	int bits;
97
98	curthread = _get_curthread();
99	if (curthread != NULL) {
100		bits = curthread->rtld_bits;
101		curthread->rtld_bits |= mask;
102	} else {
103		bits = 0;
104		PANIC("No current thread in rtld call");
105	}
106
107	return (bits);
108}
109
110static int
111_thr_rtld_clr_flag(int mask)
112{
113	struct pthread *curthread;
114	int bits;
115
116	curthread = _get_curthread();
117	if (curthread != NULL) {
118		bits = curthread->rtld_bits;
119		curthread->rtld_bits &= ~mask;
120	} else {
121		bits = 0;
122		PANIC("No current thread in rtld call");
123	}
124	return (bits);
125}
126
127void
128_thr_rtld_init(void)
129{
130	struct RtldLockInfo li;
131
132	li.lock_create  = _thr_rtld_lock_create;
133	li.lock_destroy = _thr_rtld_lock_destroy;
134	li.rlock_acquire = _thr_rtld_rlock_acquire;
135	li.wlock_acquire = _thr_rtld_wlock_acquire;
136	li.lock_release  = _thr_rtld_lock_release;
137	li.thread_set_flag = _thr_rtld_set_flag;
138	li.thread_clr_flag = _thr_rtld_clr_flag;
139	li.at_fork = NULL;
140	_rtld_thread_init(&li);
141}
142
143void
144_thr_rtld_fini(void)
145{
146	_rtld_thread_init(NULL);
147}
148#endif
149
150struct rtld_kse_lock {
151	struct lock	lck;
152	struct kse	*owner;
153	kse_critical_t	crit;
154	int		count;
155	int		write;
156};
157
158static void *
159_thr_rtld_lock_create(void)
160{
161	struct rtld_kse_lock *l;
162
163	if ((l = malloc(sizeof(struct rtld_kse_lock))) != NULL) {
164		_lock_init(&l->lck, LCK_ADAPTIVE, _kse_lock_wait,
165		    _kse_lock_wakeup, calloc);
166		l->owner = NULL;
167		l->count = 0;
168		l->write = 0;
169	}
170	return (l);
171}
172
173static void
174_thr_rtld_lock_destroy(void *lock __unused)
175{
176	/* XXX We really can not free memory after a fork() */
177#if 0
178	struct rtld_kse_lock *l;
179
180	l = (struct rtld_kse_lock *)lock;
181	_lock_destroy(&l->lck);
182	free(l);
183#endif
184	return;
185}
186
187static void
188_thr_rtld_rlock_acquire(void *lock)
189{
190	struct rtld_kse_lock *l;
191	kse_critical_t crit;
192	struct kse *curkse;
193
194	l  = (struct rtld_kse_lock *)lock;
195	crit = _kse_critical_enter();
196	curkse = _get_curkse();
197	if (l->owner == curkse) {
198		l->count++;
199		_kse_critical_leave(crit);	/* probably not necessary */
200	} else {
201		KSE_LOCK_ACQUIRE(curkse, &l->lck);
202		l->crit = crit;
203		l->owner = curkse;
204		l->count = 1;
205		l->write = 0;
206	}
207}
208
209static void
210_thr_rtld_wlock_acquire(void *lock)
211{
212	struct rtld_kse_lock *l;
213	kse_critical_t crit;
214	struct kse *curkse;
215
216	l = (struct rtld_kse_lock *)lock;
217	crit = _kse_critical_enter();
218	curkse = _get_curkse();
219	if (l->owner == curkse) {
220		_kse_critical_leave(crit);
221		PANIC("Recursive write lock attempt on rtld lock");
222	} else {
223		KSE_LOCK_ACQUIRE(curkse, &l->lck);
224		l->crit = crit;
225		l->owner = curkse;
226		l->count = 1;
227		l->write = 1;
228	}
229}
230
231static void
232_thr_rtld_lock_release(void *lock)
233{
234	struct rtld_kse_lock *l;
235	kse_critical_t crit;
236	struct kse *curkse;
237
238	l = (struct rtld_kse_lock *)lock;
239	crit = _kse_critical_enter();
240	curkse = _get_curkse();
241	if (l->owner != curkse) {
242		/*
243		 * We might want to forcibly unlock the rtld lock
244		 * and/or disable threaded mode so there is better
245		 * chance that the panic will work.  Otherwise,
246		 * we could end up trying to take the rtld lock
247		 * again.
248		 */
249		_kse_critical_leave(crit);
250		PANIC("Attempt to unlock rtld lock when not owner.");
251	} else {
252		l->count--;
253		if (l->count == 0) {
254			/*
255			 * If there ever is a count associated with
256			 * _kse_critical_leave(), we'll need to add
257			 * another call to it here with the crit
258			 * value from above.
259			 */
260			crit  = l->crit;
261			l->owner = NULL;
262			l->write = 0;
263			KSE_LOCK_RELEASE(curkse, &l->lck);
264		}
265		_kse_critical_leave(crit);
266	}
267}
268
269
270static int
271_thr_rtld_set_flag(int mask __unused)
272{
273	return (0);
274}
275
276static int
277_thr_rtld_clr_flag(int mask __unused)
278{
279	return (0);
280}
281
282void
283_thr_rtld_init(void)
284{
285	struct RtldLockInfo li;
286
287	li.lock_create  = _thr_rtld_lock_create;
288	li.lock_destroy = _thr_rtld_lock_destroy;
289	li.rlock_acquire = _thr_rtld_rlock_acquire;
290	li.wlock_acquire = _thr_rtld_wlock_acquire;
291	li.lock_release  = _thr_rtld_lock_release;
292	li.thread_set_flag = _thr_rtld_set_flag;
293	li.thread_clr_flag = _thr_rtld_clr_flag;
294	li.at_fork = NULL;
295	_rtld_thread_init(&li);
296}
297
298void
299_thr_rtld_fini(void)
300{
301	_rtld_thread_init(NULL);
302}
303