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