1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
25  * Copyright 2023 RackTop Systems, Inc.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/types.h>
30 #include <sys/tzfile.h>
31 #include <sys/atomic.h>
32 #include <sys/disp.h>
33 #include <sys/kidmap.h>
34 #include <sys/time.h>
35 #include <sys/spl.h>
36 #include <sys/random.h>
37 #include <smbsrv/smb_kproto.h>
38 #include <smbsrv/smb_fsops.h>
39 #include <smbsrv/smbinfo.h>
40 #include <smbsrv/smb_xdr.h>
41 #include <smbsrv/smb_vops.h>
42 #include <smbsrv/smb_idmap.h>
43 
44 #include <sys/sid.h>
45 #include <sys/priv_names.h>
46 
47 #ifdef	_FAKE_KERNEL
48 #define	THR_TO_DID(t)	((kt_did_t)(uintptr_t)t)
49 #else
50 #define	THR_TO_DID(t)	(t->t_did)
51 #endif
52 
53 static boolean_t smb_thread_continue_timedwait_locked(smb_thread_t *, int);
54 
55 /*
56  * smb_thread_entry_point
57  *
58  * Common entry point for all the threads created through smb_thread_start.
59  * The state of the thread is set to "running" at the beginning and moved to
60  * "exiting" just before calling thread_exit(). The condition variable is
61  *  also signaled.
62  */
63 static void
smb_thread_entry_point(smb_thread_t * thread)64 smb_thread_entry_point(
65     smb_thread_t	*thread)
66 {
67 	ASSERT(thread->sth_magic == SMB_THREAD_MAGIC);
68 	mutex_enter(&thread->sth_mtx);
69 	ASSERT(thread->sth_state == SMB_THREAD_STATE_STARTING);
70 
71 	if (!thread->sth_kill) {
72 		thread->sth_state = SMB_THREAD_STATE_RUNNING;
73 		cv_signal(&thread->sth_cv);
74 		mutex_exit(&thread->sth_mtx);
75 
76 		/* Run the real thread entry point. */
77 		thread->sth_ep(thread, thread->sth_ep_arg);
78 
79 		mutex_enter(&thread->sth_mtx);
80 	}
81 	/*
82 	 * It's tempting to clear sth_did here too, but don't.
83 	 * That's needed in thread_join().
84 	 */
85 	thread->sth_th = NULL;
86 	thread->sth_state = SMB_THREAD_STATE_EXITING;
87 	cv_broadcast(&thread->sth_cv);
88 	mutex_exit(&thread->sth_mtx);
89 #ifdef	_KERNEL
90 	if (curthread->t_lwp != NULL) {
91 		mutex_enter(&curproc->p_lock);
92 		lwp_exit(); /* noreturn */
93 	}
94 #endif	/* _KERNEL */
95 	thread_exit();
96 }
97 
98 /*
99  * smb_thread_init
100  */
101 void
smb_thread_init(smb_thread_t * thread,char * name,smb_thread_ep_t ep,void * ep_arg,pri_t pri,smb_server_t * sv)102 smb_thread_init(
103     smb_thread_t	*thread,
104     char		*name,
105     smb_thread_ep_t	ep,
106     void		*ep_arg,
107     pri_t		pri,
108     smb_server_t	*sv)
109 {
110 	ASSERT(thread->sth_magic != SMB_THREAD_MAGIC);
111 
112 	bzero(thread, sizeof (*thread));
113 
114 	(void) strlcpy(thread->sth_name, name, sizeof (thread->sth_name));
115 	thread->sth_server = sv;
116 	thread->sth_ep = ep;
117 	thread->sth_ep_arg = ep_arg;
118 	thread->sth_state = SMB_THREAD_STATE_EXITED;
119 	thread->sth_pri = pri;
120 	mutex_init(&thread->sth_mtx, NULL, MUTEX_DEFAULT, NULL);
121 	cv_init(&thread->sth_cv, NULL, CV_DEFAULT, NULL);
122 	thread->sth_magic = SMB_THREAD_MAGIC;
123 }
124 
125 /*
126  * smb_thread_destroy
127  */
128 void
smb_thread_destroy(smb_thread_t * thread)129 smb_thread_destroy(
130     smb_thread_t	*thread)
131 {
132 	ASSERT(thread->sth_magic == SMB_THREAD_MAGIC);
133 	ASSERT(thread->sth_state == SMB_THREAD_STATE_EXITED);
134 	thread->sth_magic = 0;
135 	mutex_destroy(&thread->sth_mtx);
136 	cv_destroy(&thread->sth_cv);
137 }
138 
139 /*
140  * smb_thread_start
141  *
142  * This function starts a thread with the parameters provided. It waits until
143  * the state of the thread has been moved to running.
144  */
145 /*ARGSUSED*/
146 int
smb_thread_start(smb_thread_t * sth)147 smb_thread_start(
148     smb_thread_t	*sth)
149 {
150 	kthread_t	*t;
151 	struct proc	*procp;
152 	smb_server_t	*sv = sth->sth_server;
153 	int		rc;
154 
155 	ASSERT(sth->sth_magic == SMB_THREAD_MAGIC);
156 
157 	procp = (sv->sv_proc_p != NULL) ?
158 	    sv->sv_proc_p : curzone->zone_zsched;
159 
160 	mutex_enter(&sth->sth_mtx);
161 	if (sth->sth_state != SMB_THREAD_STATE_EXITED) {
162 		mutex_exit(&sth->sth_mtx);
163 		return (-1);
164 	}
165 	sth->sth_state = SMB_THREAD_STATE_STARTING;
166 	mutex_exit(&sth->sth_mtx);
167 
168 #ifdef	_KERNEL
169 	if (sth->sth_pri < MINCLSYSPRI) {
170 		t = lwp_kernel_create(procp, smb_thread_entry_point, sth,
171 		    TS_RUN, sth->sth_pri);
172 	} else
173 #endif	/* _KERNEL */
174 	{
175 		t = thread_create(NULL, 0, smb_thread_entry_point, sth,
176 		    0, procp, TS_RUN, sth->sth_pri);
177 	}
178 	ASSERT(t != NULL);
179 
180 	mutex_enter(&sth->sth_mtx);
181 	sth->sth_th = t;
182 	sth->sth_did = THR_TO_DID(t);
183 
184 	/* rendez-vouz with new thread */
185 	while (sth->sth_state == SMB_THREAD_STATE_STARTING)
186 		cv_wait(&sth->sth_cv, &sth->sth_mtx);
187 	if (sth->sth_state == SMB_THREAD_STATE_RUNNING)
188 		rc = 0;
189 	else
190 		rc = -1;
191 	mutex_exit(&sth->sth_mtx);
192 
193 	return (rc);
194 }
195 
196 /*
197  * smb_thread_stop
198  *
199  * This function signals a thread to kill itself and waits until the "exiting"
200  * state has been reached.
201  */
202 void
smb_thread_stop(smb_thread_t * thread)203 smb_thread_stop(smb_thread_t *thread)
204 {
205 	ASSERT(thread->sth_magic == SMB_THREAD_MAGIC);
206 
207 	mutex_enter(&thread->sth_mtx);
208 	switch (thread->sth_state) {
209 	case SMB_THREAD_STATE_RUNNING:
210 	case SMB_THREAD_STATE_STARTING:
211 		if (!thread->sth_kill) {
212 			thread->sth_kill = B_TRUE;
213 			cv_broadcast(&thread->sth_cv);
214 			while (thread->sth_state != SMB_THREAD_STATE_EXITING)
215 				cv_wait(&thread->sth_cv, &thread->sth_mtx);
216 			mutex_exit(&thread->sth_mtx);
217 			thread_join(thread->sth_did);
218 			mutex_enter(&thread->sth_mtx);
219 			thread->sth_state = SMB_THREAD_STATE_EXITED;
220 			thread->sth_did = 0;
221 			thread->sth_kill = B_FALSE;
222 			cv_broadcast(&thread->sth_cv);
223 			break;
224 		}
225 		/* FALLTHROUGH */
226 
227 	case SMB_THREAD_STATE_EXITING:
228 		if (thread->sth_kill) {
229 			while (thread->sth_state != SMB_THREAD_STATE_EXITED)
230 				cv_wait(&thread->sth_cv, &thread->sth_mtx);
231 		} else {
232 			thread->sth_state = SMB_THREAD_STATE_EXITED;
233 			thread->sth_did = 0;
234 		}
235 		break;
236 
237 	case SMB_THREAD_STATE_EXITED:
238 		break;
239 
240 	default:
241 		ASSERT(0);
242 		break;
243 	}
244 	mutex_exit(&thread->sth_mtx);
245 }
246 
247 /*
248  * smb_thread_signal
249  *
250  * This function signals a thread.
251  */
252 void
smb_thread_signal(smb_thread_t * thread)253 smb_thread_signal(smb_thread_t *thread)
254 {
255 	ASSERT(thread->sth_magic == SMB_THREAD_MAGIC);
256 
257 	mutex_enter(&thread->sth_mtx);
258 	switch (thread->sth_state) {
259 	case SMB_THREAD_STATE_RUNNING:
260 		cv_signal(&thread->sth_cv);
261 		break;
262 
263 	default:
264 		break;
265 	}
266 	mutex_exit(&thread->sth_mtx);
267 }
268 
269 boolean_t
smb_thread_continue(smb_thread_t * thread)270 smb_thread_continue(smb_thread_t *thread)
271 {
272 	boolean_t result;
273 
274 	ASSERT(thread->sth_magic == SMB_THREAD_MAGIC);
275 
276 	mutex_enter(&thread->sth_mtx);
277 	result = smb_thread_continue_timedwait_locked(thread, 0);
278 	mutex_exit(&thread->sth_mtx);
279 
280 	return (result);
281 }
282 
283 boolean_t
smb_thread_continue_nowait(smb_thread_t * thread)284 smb_thread_continue_nowait(smb_thread_t *thread)
285 {
286 	boolean_t result;
287 
288 	ASSERT(thread->sth_magic == SMB_THREAD_MAGIC);
289 
290 	mutex_enter(&thread->sth_mtx);
291 	/*
292 	 * Setting ticks=-1 requests a non-blocking check.  We will
293 	 * still block if the thread is in "suspend" state.
294 	 */
295 	result = smb_thread_continue_timedwait_locked(thread, -1);
296 	mutex_exit(&thread->sth_mtx);
297 
298 	return (result);
299 }
300 
301 boolean_t
smb_thread_continue_timedwait(smb_thread_t * thread,int seconds)302 smb_thread_continue_timedwait(smb_thread_t *thread, int seconds)
303 {
304 	boolean_t result;
305 
306 	ASSERT(thread->sth_magic == SMB_THREAD_MAGIC);
307 
308 	mutex_enter(&thread->sth_mtx);
309 	result = smb_thread_continue_timedwait_locked(thread,
310 	    SEC_TO_TICK(seconds));
311 	mutex_exit(&thread->sth_mtx);
312 
313 	return (result);
314 }
315 
316 /*
317  * smb_thread_continue_timedwait_locked
318  *
319  * Internal only.  Ticks==-1 means don't block, Ticks == 0 means wait
320  * indefinitely
321  */
322 static boolean_t
smb_thread_continue_timedwait_locked(smb_thread_t * thread,int ticks)323 smb_thread_continue_timedwait_locked(smb_thread_t *thread, int ticks)
324 {
325 	boolean_t	result;
326 
327 	/* -1 means don't block */
328 	if (ticks != -1 && !thread->sth_kill) {
329 		if (ticks == 0) {
330 			cv_wait(&thread->sth_cv, &thread->sth_mtx);
331 		} else {
332 			(void) cv_reltimedwait(&thread->sth_cv,
333 			    &thread->sth_mtx, (clock_t)ticks, TR_CLOCK_TICK);
334 		}
335 	}
336 	result = (thread->sth_kill == 0);
337 
338 	return (result);
339 }
340