xref: /freebsd-src/lib/libthr/thread/thr_pshared.c (revision a2f733abcff64628b7771a47089628b7327a88bd)
11bdbd705SKonstantin Belousov /*-
21bdbd705SKonstantin Belousov  * Copyright (c) 2015 The FreeBSD Foundation
31bdbd705SKonstantin Belousov  *
41bdbd705SKonstantin Belousov  * This software was developed by Konstantin Belousov
51bdbd705SKonstantin Belousov  * under sponsorship from the FreeBSD Foundation.
61bdbd705SKonstantin Belousov  *
71bdbd705SKonstantin Belousov  * Redistribution and use in source and binary forms, with or without
81bdbd705SKonstantin Belousov  * modification, are permitted provided that the following conditions
91bdbd705SKonstantin Belousov  * are met:
101bdbd705SKonstantin Belousov  * 1. Redistributions of source code must retain the above copyright
111bdbd705SKonstantin Belousov  *    notice, this list of conditions and the following disclaimer.
121bdbd705SKonstantin Belousov  * 2. Redistributions in binary form must reproduce the above copyright
131bdbd705SKonstantin Belousov  *    notice, this list of conditions and the following disclaimer in the
141bdbd705SKonstantin Belousov  *    documentation and/or other materials provided with the distribution.
151bdbd705SKonstantin Belousov  *
161bdbd705SKonstantin Belousov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
171bdbd705SKonstantin Belousov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
181bdbd705SKonstantin Belousov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
191bdbd705SKonstantin Belousov  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
201bdbd705SKonstantin Belousov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
211bdbd705SKonstantin Belousov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
221bdbd705SKonstantin Belousov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
231bdbd705SKonstantin Belousov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
241bdbd705SKonstantin Belousov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
251bdbd705SKonstantin Belousov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
261bdbd705SKonstantin Belousov  * SUCH DAMAGE.
271bdbd705SKonstantin Belousov  */
281bdbd705SKonstantin Belousov 
291bdbd705SKonstantin Belousov #include <sys/types.h>
301bdbd705SKonstantin Belousov #include <sys/mman.h>
311bdbd705SKonstantin Belousov #include <sys/queue.h>
321bdbd705SKonstantin Belousov #include "namespace.h"
331bdbd705SKonstantin Belousov #include <stdlib.h>
341bdbd705SKonstantin Belousov #include "un-namespace.h"
351bdbd705SKonstantin Belousov 
361bdbd705SKonstantin Belousov #include "thr_private.h"
371bdbd705SKonstantin Belousov 
381bdbd705SKonstantin Belousov struct psh {
391bdbd705SKonstantin Belousov 	LIST_ENTRY(psh) link;
401bdbd705SKonstantin Belousov 	void *key;
411bdbd705SKonstantin Belousov 	void *val;
421bdbd705SKonstantin Belousov };
431bdbd705SKonstantin Belousov 
441bdbd705SKonstantin Belousov LIST_HEAD(pshared_hash_head, psh);
451bdbd705SKonstantin Belousov #define	HASH_SIZE	128
461bdbd705SKonstantin Belousov static struct pshared_hash_head pshared_hash[HASH_SIZE];
471bdbd705SKonstantin Belousov #define	PSHARED_KEY_HASH(key)	(((unsigned long)(key) >> 8) % HASH_SIZE)
481bdbd705SKonstantin Belousov /* XXXKIB: lock could be split to per-hash chain, if appears contested */
491bdbd705SKonstantin Belousov static struct urwlock pshared_lock = DEFAULT_URWLOCK;
50c7904405SAndrew Turner static int page_size;
511bdbd705SKonstantin Belousov 
521bdbd705SKonstantin Belousov void
__thr_pshared_init(void)531bdbd705SKonstantin Belousov __thr_pshared_init(void)
541bdbd705SKonstantin Belousov {
551bdbd705SKonstantin Belousov 	int i;
561bdbd705SKonstantin Belousov 
57c7904405SAndrew Turner 	page_size = getpagesize();
58c7904405SAndrew Turner 	THR_ASSERT(page_size >= THR_PAGE_SIZE_MIN,
59c7904405SAndrew Turner 	    "THR_PAGE_SIZE_MIN is too large");
60c7904405SAndrew Turner 
611bdbd705SKonstantin Belousov 	_thr_urwlock_init(&pshared_lock);
621bdbd705SKonstantin Belousov 	for (i = 0; i < HASH_SIZE; i++)
631bdbd705SKonstantin Belousov 		LIST_INIT(&pshared_hash[i]);
641bdbd705SKonstantin Belousov }
651bdbd705SKonstantin Belousov 
661bdbd705SKonstantin Belousov static void
pshared_rlock(struct pthread * curthread)671bdbd705SKonstantin Belousov pshared_rlock(struct pthread *curthread)
681bdbd705SKonstantin Belousov {
691bdbd705SKonstantin Belousov 
701bdbd705SKonstantin Belousov 	curthread->locklevel++;
711bdbd705SKonstantin Belousov 	_thr_rwl_rdlock(&pshared_lock);
721bdbd705SKonstantin Belousov }
731bdbd705SKonstantin Belousov 
741bdbd705SKonstantin Belousov static void
pshared_wlock(struct pthread * curthread)751bdbd705SKonstantin Belousov pshared_wlock(struct pthread *curthread)
761bdbd705SKonstantin Belousov {
771bdbd705SKonstantin Belousov 
781bdbd705SKonstantin Belousov 	curthread->locklevel++;
791bdbd705SKonstantin Belousov 	_thr_rwl_wrlock(&pshared_lock);
801bdbd705SKonstantin Belousov }
811bdbd705SKonstantin Belousov 
821bdbd705SKonstantin Belousov static void
pshared_unlock(struct pthread * curthread)831bdbd705SKonstantin Belousov pshared_unlock(struct pthread *curthread)
841bdbd705SKonstantin Belousov {
851bdbd705SKonstantin Belousov 
861bdbd705SKonstantin Belousov 	_thr_rwl_unlock(&pshared_lock);
871bdbd705SKonstantin Belousov 	curthread->locklevel--;
881bdbd705SKonstantin Belousov 	_thr_ast(curthread);
891bdbd705SKonstantin Belousov }
901bdbd705SKonstantin Belousov 
9106409412SKonstantin Belousov /*
9206409412SKonstantin Belousov  * Among all processes sharing a lock only one executes
9306409412SKonstantin Belousov  * pthread_lock_destroy().  Other processes still have the hash and
9406409412SKonstantin Belousov  * mapped off-page.
9506409412SKonstantin Belousov  *
9606409412SKonstantin Belousov  * Mitigate the problem by checking the liveness of all hashed keys
9706409412SKonstantin Belousov  * periodically.  Right now this is executed on each
9806409412SKonstantin Belousov  * pthread_lock_destroy(), but may be done less often if found to be
9906409412SKonstantin Belousov  * too time-consuming.
10006409412SKonstantin Belousov  */
1011bdbd705SKonstantin Belousov static void
pshared_gc(struct pthread * curthread)1021bdbd705SKonstantin Belousov pshared_gc(struct pthread *curthread)
1031bdbd705SKonstantin Belousov {
1041bdbd705SKonstantin Belousov 	struct pshared_hash_head *hd;
1051bdbd705SKonstantin Belousov 	struct psh *h, *h1;
1061bdbd705SKonstantin Belousov 	int error, i;
1071bdbd705SKonstantin Belousov 
1081bdbd705SKonstantin Belousov 	pshared_wlock(curthread);
1091bdbd705SKonstantin Belousov 	for (i = 0; i < HASH_SIZE; i++) {
1101bdbd705SKonstantin Belousov 		hd = &pshared_hash[i];
1111bdbd705SKonstantin Belousov 		LIST_FOREACH_SAFE(h, hd, link, h1) {
1121bdbd705SKonstantin Belousov 			error = _umtx_op(NULL, UMTX_OP_SHM, UMTX_SHM_ALIVE,
1131bdbd705SKonstantin Belousov 			    h->val, NULL);
1141bdbd705SKonstantin Belousov 			if (error == 0)
1151bdbd705SKonstantin Belousov 				continue;
1161bdbd705SKonstantin Belousov 			LIST_REMOVE(h, link);
117c7904405SAndrew Turner 			munmap(h->val, page_size);
1181bdbd705SKonstantin Belousov 			free(h);
1191bdbd705SKonstantin Belousov 		}
1201bdbd705SKonstantin Belousov 	}
1211bdbd705SKonstantin Belousov 	pshared_unlock(curthread);
1221bdbd705SKonstantin Belousov }
1231bdbd705SKonstantin Belousov 
1241bdbd705SKonstantin Belousov static void *
pshared_lookup(void * key)1251bdbd705SKonstantin Belousov pshared_lookup(void *key)
1261bdbd705SKonstantin Belousov {
1271bdbd705SKonstantin Belousov 	struct pshared_hash_head *hd;
1281bdbd705SKonstantin Belousov 	struct psh *h;
1291bdbd705SKonstantin Belousov 
1301bdbd705SKonstantin Belousov 	hd = &pshared_hash[PSHARED_KEY_HASH(key)];
1311bdbd705SKonstantin Belousov 	LIST_FOREACH(h, hd, link) {
1321bdbd705SKonstantin Belousov 		if (h->key == key)
1331bdbd705SKonstantin Belousov 			return (h->val);
1341bdbd705SKonstantin Belousov 	}
1351bdbd705SKonstantin Belousov 	return (NULL);
1361bdbd705SKonstantin Belousov }
1371bdbd705SKonstantin Belousov 
1381bdbd705SKonstantin Belousov static int
pshared_insert(void * key,void ** val)1391bdbd705SKonstantin Belousov pshared_insert(void *key, void **val)
1401bdbd705SKonstantin Belousov {
1411bdbd705SKonstantin Belousov 	struct pshared_hash_head *hd;
1421bdbd705SKonstantin Belousov 	struct psh *h;
1431bdbd705SKonstantin Belousov 
1441bdbd705SKonstantin Belousov 	hd = &pshared_hash[PSHARED_KEY_HASH(key)];
1451bdbd705SKonstantin Belousov 	LIST_FOREACH(h, hd, link) {
14606409412SKonstantin Belousov 		/*
14706409412SKonstantin Belousov 		 * When the key already exists in the hash, we should
14806409412SKonstantin Belousov 		 * return either the new (just mapped) or old (hashed)
14906409412SKonstantin Belousov 		 * val, and the other val should be unmapped to avoid
15006409412SKonstantin Belousov 		 * address space leak.
15106409412SKonstantin Belousov 		 *
15206409412SKonstantin Belousov 		 * If two threads perform lock of the same object
15306409412SKonstantin Belousov 		 * which is not yet stored in the pshared_hash, then
15406409412SKonstantin Belousov 		 * the val already inserted by the first thread should
15506409412SKonstantin Belousov 		 * be returned, and the second val freed (order is by
15606409412SKonstantin Belousov 		 * the pshared_lock()).  Otherwise, if we unmap the
15706409412SKonstantin Belousov 		 * value obtained from the hash, the first thread
15806409412SKonstantin Belousov 		 * might operate on an unmapped off-page object.
15906409412SKonstantin Belousov 		 *
16006409412SKonstantin Belousov 		 * There is still an issue: if hashed key was unmapped
16106409412SKonstantin Belousov 		 * and then other page is mapped at the same key
16206409412SKonstantin Belousov 		 * address, the hash would return the old val.  I
16306409412SKonstantin Belousov 		 * decided to handle the race of simultaneous hash
16406409412SKonstantin Belousov 		 * insertion, leaving the unlikely remap problem
16506409412SKonstantin Belousov 		 * unaddressed.
16606409412SKonstantin Belousov 		 */
1671bdbd705SKonstantin Belousov 		if (h->key == key) {
1681bdbd705SKonstantin Belousov 			if (h->val != *val) {
169c7904405SAndrew Turner 				munmap(*val, page_size);
1701bdbd705SKonstantin Belousov 				*val = h->val;
1711bdbd705SKonstantin Belousov 			}
1721bdbd705SKonstantin Belousov 			return (1);
1731bdbd705SKonstantin Belousov 		}
1741bdbd705SKonstantin Belousov 	}
1751bdbd705SKonstantin Belousov 
1761bdbd705SKonstantin Belousov 	h = malloc(sizeof(*h));
1771bdbd705SKonstantin Belousov 	if (h == NULL)
1781bdbd705SKonstantin Belousov 		return (0);
1791bdbd705SKonstantin Belousov 	h->key = key;
1801bdbd705SKonstantin Belousov 	h->val = *val;
1811bdbd705SKonstantin Belousov 	LIST_INSERT_HEAD(hd, h, link);
1821bdbd705SKonstantin Belousov 	return (1);
1831bdbd705SKonstantin Belousov }
1841bdbd705SKonstantin Belousov 
1851bdbd705SKonstantin Belousov static void *
pshared_remove(void * key)1861bdbd705SKonstantin Belousov pshared_remove(void *key)
1871bdbd705SKonstantin Belousov {
1881bdbd705SKonstantin Belousov 	struct pshared_hash_head *hd;
1891bdbd705SKonstantin Belousov 	struct psh *h;
1901bdbd705SKonstantin Belousov 	void *val;
1911bdbd705SKonstantin Belousov 
1921bdbd705SKonstantin Belousov 	hd = &pshared_hash[PSHARED_KEY_HASH(key)];
1931bdbd705SKonstantin Belousov 	LIST_FOREACH(h, hd, link) {
1941bdbd705SKonstantin Belousov 		if (h->key == key) {
1951bdbd705SKonstantin Belousov 			LIST_REMOVE(h, link);
1961bdbd705SKonstantin Belousov 			val = h->val;
1971bdbd705SKonstantin Belousov 			free(h);
1981bdbd705SKonstantin Belousov 			return (val);
1991bdbd705SKonstantin Belousov 		}
2001bdbd705SKonstantin Belousov 	}
2011bdbd705SKonstantin Belousov 	return (NULL);
2021bdbd705SKonstantin Belousov }
2031bdbd705SKonstantin Belousov 
2041bdbd705SKonstantin Belousov static void
pshared_clean(void * key,void * val)2051bdbd705SKonstantin Belousov pshared_clean(void *key, void *val)
2061bdbd705SKonstantin Belousov {
2071bdbd705SKonstantin Belousov 
2081bdbd705SKonstantin Belousov 	if (val != NULL)
209c7904405SAndrew Turner 		munmap(val, page_size);
2101bdbd705SKonstantin Belousov 	_umtx_op(NULL, UMTX_OP_SHM, UMTX_SHM_DESTROY, key, NULL);
2111bdbd705SKonstantin Belousov }
2121bdbd705SKonstantin Belousov 
2133cf37d12SKonstantin Belousov static void
pshared_destroy(struct pthread * curthread,void * key)2143cf37d12SKonstantin Belousov pshared_destroy(struct pthread *curthread, void *key)
2153cf37d12SKonstantin Belousov {
2163cf37d12SKonstantin Belousov 	void *val;
2173cf37d12SKonstantin Belousov 
2183cf37d12SKonstantin Belousov 	pshared_wlock(curthread);
2193cf37d12SKonstantin Belousov 	val = pshared_remove(key);
2203cf37d12SKonstantin Belousov 	pshared_unlock(curthread);
2213cf37d12SKonstantin Belousov 	pshared_clean(key, val);
2223cf37d12SKonstantin Belousov }
2233cf37d12SKonstantin Belousov 
2241bdbd705SKonstantin Belousov void *
__thr_pshared_offpage(void * key,int doalloc)2251bdbd705SKonstantin Belousov __thr_pshared_offpage(void *key, int doalloc)
2261bdbd705SKonstantin Belousov {
2271bdbd705SKonstantin Belousov 	struct pthread *curthread;
2281bdbd705SKonstantin Belousov 	void *res;
2291bdbd705SKonstantin Belousov 	int fd, ins_done;
2301bdbd705SKonstantin Belousov 
2311bdbd705SKonstantin Belousov 	curthread = _get_curthread();
232*25c862aeSKonstantin Belousov 	if (doalloc) {
233*25c862aeSKonstantin Belousov 		pshared_destroy(curthread, key);
234*25c862aeSKonstantin Belousov 		res = NULL;
235*25c862aeSKonstantin Belousov 	} else {
2361bdbd705SKonstantin Belousov 		pshared_rlock(curthread);
2371bdbd705SKonstantin Belousov 		res = pshared_lookup(key);
2381bdbd705SKonstantin Belousov 		pshared_unlock(curthread);
2391bdbd705SKonstantin Belousov 		if (res != NULL)
2401bdbd705SKonstantin Belousov 			return (res);
241*25c862aeSKonstantin Belousov 	}
2421bdbd705SKonstantin Belousov 	fd = _umtx_op(NULL, UMTX_OP_SHM, doalloc ? UMTX_SHM_CREAT :
2431bdbd705SKonstantin Belousov 	    UMTX_SHM_LOOKUP, key, NULL);
2441bdbd705SKonstantin Belousov 	if (fd == -1)
2451bdbd705SKonstantin Belousov 		return (NULL);
246c7904405SAndrew Turner 	res = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
2471bdbd705SKonstantin Belousov 	close(fd);
2481bdbd705SKonstantin Belousov 	if (res == MAP_FAILED)
2491bdbd705SKonstantin Belousov 		return (NULL);
2501bdbd705SKonstantin Belousov 	pshared_wlock(curthread);
2511bdbd705SKonstantin Belousov 	ins_done = pshared_insert(key, &res);
2521bdbd705SKonstantin Belousov 	pshared_unlock(curthread);
2531bdbd705SKonstantin Belousov 	if (!ins_done) {
2541bdbd705SKonstantin Belousov 		pshared_clean(key, res);
2551bdbd705SKonstantin Belousov 		res = NULL;
2561bdbd705SKonstantin Belousov 	}
2571bdbd705SKonstantin Belousov 	return (res);
2581bdbd705SKonstantin Belousov }
2591bdbd705SKonstantin Belousov 
2601bdbd705SKonstantin Belousov void
__thr_pshared_destroy(void * key)2611bdbd705SKonstantin Belousov __thr_pshared_destroy(void *key)
2621bdbd705SKonstantin Belousov {
2631bdbd705SKonstantin Belousov 	struct pthread *curthread;
2641bdbd705SKonstantin Belousov 
2651bdbd705SKonstantin Belousov 	curthread = _get_curthread();
2663cf37d12SKonstantin Belousov 	pshared_destroy(curthread, key);
2671bdbd705SKonstantin Belousov 	pshared_gc(curthread);
2681bdbd705SKonstantin Belousov }
26953fd961fSKonstantin Belousov 
27053fd961fSKonstantin Belousov void
__thr_pshared_atfork_pre(void)27153fd961fSKonstantin Belousov __thr_pshared_atfork_pre(void)
27253fd961fSKonstantin Belousov {
27353fd961fSKonstantin Belousov 
27453fd961fSKonstantin Belousov 	_thr_rwl_rdlock(&pshared_lock);
27553fd961fSKonstantin Belousov }
27653fd961fSKonstantin Belousov 
27753fd961fSKonstantin Belousov void
__thr_pshared_atfork_post(void)27853fd961fSKonstantin Belousov __thr_pshared_atfork_post(void)
27953fd961fSKonstantin Belousov {
28053fd961fSKonstantin Belousov 
28153fd961fSKonstantin Belousov 	_thr_rwl_unlock(&pshared_lock);
28253fd961fSKonstantin Belousov }
283