1/*
2 * Copyright (c) 2003 Daniel M. Eischen <deischen@freebsd.org>
3 * Copyright (c) 2003 Sergey Osokin <osa@freebsd.org.ru>.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Neither the name of the author nor the names of any co-contributors
12 *    may be used to endorse or promote products derived from this software
13 *    without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD$
28 */
29
30#include "namespace.h"
31#include <errno.h>
32#include <pthread.h>
33#include <sys/types.h>
34#include <sys/sysctl.h>
35#include "un-namespace.h"
36
37#include "thr_private.h"
38
39/*#define DEBUG_CONCURRENCY */
40#ifdef DEBUG_CONCURRENCY
41#define DBG_MSG		stdout_debug
42#else
43#define	DBG_MSG(x...)
44#endif
45
46static int level = 0;
47
48__weak_reference(_pthread_getconcurrency, pthread_getconcurrency);
49__weak_reference(_pthread_setconcurrency, pthread_setconcurrency);
50
51int
52_pthread_getconcurrency(void)
53{
54	return (level);
55}
56
57int
58_pthread_setconcurrency(int new_level)
59{
60	int ret;
61
62	if (new_level < 0)
63		ret = EINVAL;
64	else if (new_level == level)
65		ret = 0;
66	else if (new_level == 0) {
67		level = 0;
68		ret = 0;
69	} else if ((_kse_isthreaded() == 0) && (_kse_setthreaded(1) != 0)) {
70		DBG_MSG("Can't enable threading.\n");
71		ret = EAGAIN;
72	} else {
73		ret = _thr_setconcurrency(new_level);
74		if (ret == 0)
75			level = new_level;
76	}
77	return (ret);
78}
79
80int
81_thr_setconcurrency(int new_level)
82{
83	struct pthread *curthread;
84	struct kse *newkse, *kse;
85	kse_critical_t crit;
86	int kse_count;
87	int i;
88	int ret;
89
90	/*
91	 * Turn on threaded mode, if failed, it is unnecessary to
92	 * do further work.
93	 */
94	if (_kse_isthreaded() == 0 && _kse_setthreaded(1))
95		return (EAGAIN);
96
97	ret = 0;
98	curthread = _get_curthread();
99	/* Race condition, but so what. */
100	kse_count = _kse_initial->k_kseg->kg_ksecount;
101	if (new_level > kse_count) {
102		for (i = kse_count; i < new_level; i++) {
103			newkse = _kse_alloc(curthread, 0);
104			if (newkse == NULL) {
105				DBG_MSG("Can't alloc new KSE.\n");
106				ret = EAGAIN;
107				break;
108			}
109			newkse->k_kseg = _kse_initial->k_kseg;
110			newkse->k_schedq = _kse_initial->k_schedq;
111			newkse->k_curthread = NULL;
112			crit = _kse_critical_enter();
113			KSE_SCHED_LOCK(curthread->kse, newkse->k_kseg);
114			TAILQ_INSERT_TAIL(&newkse->k_kseg->kg_kseq,
115			    newkse, k_kgqe);
116			newkse->k_kseg->kg_ksecount++;
117			newkse->k_flags |= KF_STARTED;
118			KSE_SCHED_UNLOCK(curthread->kse, newkse->k_kseg);
119			if (kse_create(&newkse->k_kcb->kcb_kmbx, 0) != 0) {
120				KSE_SCHED_LOCK(curthread->kse, newkse->k_kseg);
121				TAILQ_REMOVE(&newkse->k_kseg->kg_kseq,
122				    newkse, k_kgqe);
123				newkse->k_kseg->kg_ksecount--;
124				KSE_SCHED_UNLOCK(curthread->kse,
125				    newkse->k_kseg);
126				_kse_critical_leave(crit);
127				_kse_free(curthread, newkse);
128				DBG_MSG("kse_create syscall failed.\n");
129				ret = EAGAIN;
130				break;
131			} else {
132				_kse_critical_leave(crit);
133			}
134		}
135	} else if (new_level < kse_count) {
136		kse_count = 0;
137		crit = _kse_critical_enter();
138		KSE_SCHED_LOCK(curthread->kse, _kse_initial->k_kseg);
139		/* Count the number of active KSEs */
140		TAILQ_FOREACH(kse, &_kse_initial->k_kseg->kg_kseq, k_kgqe) {
141			if ((kse->k_flags & KF_TERMINATED) == 0)
142				kse_count++;
143		}
144		/* Reduce the number of active KSEs appropriately. */
145		kse = TAILQ_FIRST(&_kse_initial->k_kseg->kg_kseq);
146		while ((kse != NULL) && (kse_count > new_level)) {
147			if ((kse != _kse_initial) &&
148			    ((kse->k_flags & KF_TERMINATED) == 0)) {
149				kse->k_flags |= KF_TERMINATED;
150				kse_count--;
151				/* Wakup the KSE in case it is idle. */
152				kse_wakeup(&kse->k_kcb->kcb_kmbx);
153			}
154			kse = TAILQ_NEXT(kse, k_kgqe);
155		}
156		KSE_SCHED_UNLOCK(curthread->kse, _kse_initial->k_kseg);
157		_kse_critical_leave(crit);
158	}
159	return (ret);
160}
161
162int
163_thr_setmaxconcurrency(void)
164{
165	int vcpu;
166	size_t len;
167	int ret;
168
169	len = sizeof(vcpu);
170	ret = sysctlbyname("kern.threads.virtual_cpu", &vcpu, &len, NULL, 0);
171	if (ret == 0 && vcpu > 0)
172		ret = _thr_setconcurrency(vcpu);
173	return (ret);
174}
175
176