1 /*-
2 * Copyright (c) 2014-2015, Matthew Macy <mmacy@nextbsd.org>
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 are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Neither the name of Matthew Macy nor the names of its
12 * contributors may be used to endorse or promote products derived from
13 * this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND 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 COPYRIGHT OWNER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/eventhandler.h>
35 #include <sys/kernel.h>
36 #include <sys/lock.h>
37 #include <sys/mutex.h>
38 #include <sys/proc.h>
39 #include <sys/queue.h>
40 #include <sys/resource.h>
41 #include <sys/resourcevar.h>
42 #include <sys/rwlock.h>
43 #include <sys/sched.h>
44 #include <sys/sleepqueue.h>
45 #include <sys/signal.h>
46
47 #include <sys/mach/mach_types.h>
48 #include <sys/mach/mach_traps.h>
49
50 #include <sys/mach/ipc/ipc_kmsg.h>
51 #include <sys/mach/thread.h>
52 #include <sys/mach/ipc_tt.h>
53 #include <sys/mach/thread_switch.h>
54
55 #define MT_SETRUNNABLE 0x1
56
57 #ifdef notyet
58 /*
59 * Am assuming that Mach lacks the concept of uninterruptible
60 * sleep - this may need to be changed back to what is in pci_pass.c
61 */
62 static int
_intr_tdsigwakeup(struct thread * td,int intrval)63 _intr_tdsigwakeup(struct thread *td, int intrval)
64 {
65 struct proc *p = td->td_proc;
66 int rc = 0;
67
68 PROC_SLOCK(p);
69 thread_lock(td);
70 if (TD_ON_SLEEPQ(td)) {
71 /*
72 * Give low priority threads a better chance to run.
73 */
74 if (td->td_priority > PUSER)
75 sched_prio(td, PUSER);
76
77 sleepq_abort(td, intrval);
78 rc = 1;
79 }
80 PROC_SUNLOCK(p);
81 thread_unlock(td);
82 return (rc);
83 }
84 #endif
85
86
87 int
mach_thread_switch(mach_port_name_t thread_name,int option,mach_msg_timeout_t option_time)88 mach_thread_switch(mach_port_name_t thread_name, int option, mach_msg_timeout_t option_time)
89 {
90 int timeout;
91 struct mach_emuldata *med;
92 struct thread *td = curthread;
93
94 med = (struct mach_emuldata *)td->td_proc->p_emuldata;
95 timeout = option_time * hz / 1000;
96
97 /*
98 * The day we will be able to find out the struct proc from
99 * the port number, try to use preempt() to call the right thread.
100 * [- but preempt() is for _involuntary_ context switches.]
101 */
102 switch(option) {
103 case SWITCH_OPTION_NONE:
104 sched_relinquish(curthread);
105 break;
106
107 case SWITCH_OPTION_WAIT:
108 #ifdef notyet
109 med->med_thpri = 1;
110 while (med->med_thpri != 0) {
111 rw_wlock(&med->med_rightlock);
112 (void)msleep(&med->med_thpri, &med->med_rightlock, PZERO|PCATCH,
113 "thread_switch", timeout);
114 rw_wunlock(&med->med_rightlock);
115 }
116 break;
117 #endif
118 case SWITCH_OPTION_DEPRESS:
119 /* Use a callout to restore the priority after depression? */
120 td->td_priority = PRI_MAX_TIMESHARE;
121 break;
122
123 default:
124 uprintf("sys_mach_syscall_thread_switch(): unknown option %d\n", option);
125 break;
126 }
127 return (0);
128 }
129
130 void
thread_go(thread_t thread)131 thread_go(thread_t thread)
132 {
133 int needunlock = 0;
134 struct mtx *block_lock = thread->ith_block_lock_data;
135
136 MPASS(thread->ith_state != MACH_SEND_IN_PROGRESS &&
137 thread->ith_state != MACH_RCV_IN_PROGRESS &&
138 thread->ith_state != MACH_RCV_IN_PROGRESS_TIMED);
139
140 if (block_lock != NULL && !mtx_owned(block_lock)) {
141 needunlock = 1;
142 mtx_lock(block_lock);
143 }
144 wakeup(thread);
145 if (needunlock)
146 mtx_unlock(block_lock);
147 }
148
149 void
thread_block(void)150 thread_block(void)
151 {
152 thread_t thread = current_thread();
153 int rc;
154
155 MPASS(curthread == thread->ith_td);
156
157 rc = msleep(thread, thread->ith_block_lock_data, PCATCH|PSOCK, "thread_block", thread->timeout);
158 switch (rc) {
159 case EINTR:
160 case ERESTART:
161 thread->wait_result = THREAD_INTERRUPTED;
162 break;
163 case EWOULDBLOCK:
164 thread->wait_result = THREAD_TIMED_OUT;
165 break;
166 case 0:
167 thread->wait_result = THREAD_AWAKENED;
168 break;
169 default:
170 panic("unexpected return from msleep: %d\n", rc);
171 }
172 #ifdef INVARIANTS
173 if (thread->timeout == 0) {
174 if(rc == 0)
175 MPASS(thread->ith_state == MACH_MSG_SUCCESS);
176 else
177 MPASS(rc == EINTR || rc == ERESTART);
178 }
179 #endif
180 }
181
182 void
thread_will_wait_with_timeout(thread_t thread,int timeout)183 thread_will_wait_with_timeout(thread_t thread, int timeout)
184 {
185
186 thread->sleep_stamp = ticks;
187 thread->timeout = timeout;
188 }
189
190
191 void
thread_will_wait(thread_t thread)192 thread_will_wait(thread_t thread)
193 {
194
195 thread->sleep_stamp = ticks;
196 thread->timeout = 0;
197 }
198
199 static void
mach_thread_create(struct thread * td,thread_t thread)200 mach_thread_create(struct thread *td, thread_t thread)
201 {
202
203 thread->ref_count = 1;
204 ipc_thread_init(thread);
205 }
206
207
208 static uma_zone_t thread_shuttle_zone;
209
210 static int
uma_thread_init(void * _thread,int a,int b)211 uma_thread_init(void *_thread, int a, int b)
212 {
213 /* allocate thread substructures */
214 return (0);
215 }
216
217 static void
uma_thread_fini(void * _thread,int a)218 uma_thread_fini(void *_thread, int a)
219 {
220 /* deallocate thread substructures */
221 }
222
223 static void
mach_thread_init(void * arg __unused,struct thread * td)224 mach_thread_init(void *arg __unused, struct thread *td)
225 {
226 thread_t thread;
227
228 thread = uma_zalloc(thread_shuttle_zone, M_WAITOK|M_ZERO);
229 mtx_init(&thread->ith_lock_data, "mach_thread lock", NULL, MTX_DEF);
230
231 MPASS(td->td_machdata == NULL);
232 td->td_machdata = thread;
233 thread->ith_td = td;
234 ipc_thr_act_init(thread);
235 }
236
237 static void
mach_thread_fini(void * arg __unused,struct thread * td)238 mach_thread_fini(void *arg __unused, struct thread *td)
239 {
240 thread_t thread = td->td_machdata;
241
242 MPASS(thread->ith_kmsg == NULL);
243 MPASS(thread->ith_td == td);
244 ipc_thr_act_terminate(thread);
245 mtx_destroy(&thread->ith_lock_data);
246 uma_zfree(thread_shuttle_zone, thread);
247 }
248
249 static void
mach_thread_ctor(void * arg __unused,struct thread * td)250 mach_thread_ctor(void *arg __unused, struct thread *td)
251 {
252 thread_t thread = td->td_machdata;
253
254 MPASS(thread->ith_td == td);
255 mach_thread_create(td, thread);
256 thread->ith_block_lock_data = NULL;
257 }
258
259 static void
thread_sysinit(void * arg __unused)260 thread_sysinit(void *arg __unused)
261 {
262 thread_shuttle_zone = uma_zcreate("thread_shuttle_zone",
263 sizeof(struct thread_shuttle),
264 NULL, NULL, uma_thread_init,
265 uma_thread_fini, 1, 0);
266
267 EVENTHANDLER_REGISTER(thread_ctor, mach_thread_ctor, NULL, EVENTHANDLER_PRI_ANY);
268 EVENTHANDLER_REGISTER(thread_init, mach_thread_init, NULL, EVENTHANDLER_PRI_ANY);
269 EVENTHANDLER_REGISTER(thread_fini, mach_thread_fini, NULL, EVENTHANDLER_PRI_ANY);
270 }
271
272 /* before SI_SUB_INTRINSIC and after SI_SUB_EVENTHANDLER */
273 SYSINIT(mach_thread, SI_SUB_KLD, SI_ORDER_ANY, thread_sysinit, NULL);
274