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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include "iscsi_thread.h"
27 
28 static	void	iscsi_threads_entry(void *arg);
29 
30 /*
31  * iscsi_thread_create - Creates the needed resources to handle a thread
32  */
33 iscsi_thread_t *
iscsi_thread_create(dev_info_t * dip,char * name,iscsi_thread_ep_t entry_point,void * arg)34 iscsi_thread_create(dev_info_t *dip, char *name,
35     iscsi_thread_ep_t entry_point, void *arg)
36 {
37 	iscsi_thread_t		*thread;
38 
39 	thread = kmem_zalloc(sizeof (iscsi_thread_t), KM_SLEEP);
40 
41 	if (thread != NULL) {
42 
43 		thread->tq = ddi_taskq_create(dip, name, 1,
44 		    TASKQ_DEFAULTPRI, 0);
45 
46 		if (thread->tq != NULL) {
47 			thread->signature	= SIG_ISCSI_THREAD;
48 			thread->dip		= dip;
49 			thread->entry_point	= entry_point;
50 			thread->arg		= arg;
51 			thread->state		= ISCSI_THREAD_STATE_STOPPED;
52 			thread->sign.bitmap	= 0;
53 			mutex_init(&thread->mgnt.mtx, NULL, MUTEX_DRIVER, NULL);
54 			mutex_init(&thread->sign.mtx, NULL, MUTEX_DRIVER, NULL);
55 			cv_init(&thread->sign.cdv, NULL, CV_DRIVER, NULL);
56 		} else {
57 			kmem_free(thread, sizeof (iscsi_thread_t));
58 			thread = NULL;
59 		}
60 	}
61 
62 	return (thread);
63 }
64 
65 /*
66  * iscsi_thread_destroy - Releases the needed resources to handle a thread
67  */
68 void
iscsi_thread_destroy(iscsi_thread_t * thread)69 iscsi_thread_destroy(
70 	iscsi_thread_t		*thread
71 )
72 {
73 	ASSERT(thread != NULL);
74 	ASSERT(thread->signature == SIG_ISCSI_THREAD);
75 
76 	mutex_enter(&thread->mgnt.mtx);
77 
78 	switch (thread->state) {
79 
80 	case ISCSI_THREAD_STATE_STARTED:
81 
82 		/* A kill signal is sent first. */
83 		thread->state = ISCSI_THREAD_STATE_DESTROYING;
84 		mutex_enter(&thread->sign.mtx);
85 		if (!(thread->sign.bitmap & ISCSI_THREAD_SIGNAL_KILL)) {
86 			thread->sign.bitmap |= ISCSI_THREAD_SIGNAL_KILL;
87 			cv_signal(&thread->sign.cdv);
88 		}
89 		mutex_exit(&thread->sign.mtx);
90 		ddi_taskq_wait(thread->tq);
91 		break;
92 
93 	case ISCSI_THREAD_STATE_STOPPED:
94 
95 		/* Switch the state and wait for the thread to exit. */
96 		thread->state = ISCSI_THREAD_STATE_DESTROYING;
97 		break;
98 
99 	default:
100 		ASSERT(0);
101 		break;
102 	}
103 
104 	mutex_exit(&thread->mgnt.mtx);
105 	ddi_taskq_destroy(thread->tq);
106 	cv_destroy(&thread->sign.cdv);
107 	mutex_destroy(&thread->sign.mtx);
108 	mutex_destroy(&thread->mgnt.mtx);
109 	thread->signature = (uint32_t)~SIG_ISCSI_THREAD;
110 	kmem_free(thread, sizeof (iscsi_thread_t));
111 }
112 
113 /*
114  * iscsi_thread_start - Starts the thread given as an entry parameter
115  */
116 boolean_t
iscsi_thread_start(iscsi_thread_t * thread)117 iscsi_thread_start(
118 	iscsi_thread_t		*thread
119 )
120 {
121 	boolean_t		ret = B_FALSE;
122 
123 	ASSERT(thread != NULL);
124 	ASSERT(thread->signature == SIG_ISCSI_THREAD);
125 
126 	mutex_enter(&thread->mgnt.mtx);
127 
128 	switch (thread->state) {
129 
130 	case ISCSI_THREAD_STATE_STARTED:
131 
132 		mutex_enter(&thread->sign.mtx);
133 
134 		thread->state = ISCSI_THREAD_STATE_STOPPING;
135 
136 		if (!(thread->sign.bitmap & ISCSI_THREAD_SIGNAL_KILL)) {
137 			thread->sign.bitmap |= ISCSI_THREAD_SIGNAL_KILL;
138 			cv_signal(&thread->sign.cdv);
139 		}
140 		mutex_exit(&thread->sign.mtx);
141 		ddi_taskq_wait(thread->tq);
142 		thread->state = ISCSI_THREAD_STATE_STOPPED;
143 		/* FALLTHRU */
144 
145 	case ISCSI_THREAD_STATE_STOPPED:
146 
147 		thread->sign.bitmap = 0;
148 		thread->state	    = ISCSI_THREAD_STATE_STARTING;
149 
150 		if (ddi_taskq_dispatch(thread->tq, iscsi_threads_entry,
151 		    thread, DDI_SLEEP) == DDI_SUCCESS) {
152 			/*
153 			 * The dispatch succeeded.
154 			 */
155 			thread->state = ISCSI_THREAD_STATE_STARTED;
156 			ret = B_TRUE;
157 		}
158 		break;
159 
160 	default:
161 		ASSERT(0);
162 		break;
163 	}
164 	mutex_exit(&thread->mgnt.mtx);
165 	return (ret);
166 }
167 
168 /*
169  * iscsi_thread_stop -
170  */
171 boolean_t
iscsi_thread_stop(iscsi_thread_t * thread)172 iscsi_thread_stop(
173 	iscsi_thread_t		*thread
174 )
175 {
176 	boolean_t		ret = B_FALSE;
177 
178 	ASSERT(thread != NULL);
179 	ASSERT(thread->signature == SIG_ISCSI_THREAD);
180 
181 	mutex_enter(&thread->mgnt.mtx);
182 
183 	switch (thread->state) {
184 
185 	case ISCSI_THREAD_STATE_STARTED:
186 
187 		mutex_enter(&thread->sign.mtx);
188 
189 		thread->state = ISCSI_THREAD_STATE_STOPPING;
190 
191 		if (!(thread->sign.bitmap & ISCSI_THREAD_SIGNAL_KILL)) {
192 			thread->sign.bitmap |= ISCSI_THREAD_SIGNAL_KILL;
193 			cv_signal(&thread->sign.cdv);
194 		}
195 		mutex_exit(&thread->sign.mtx);
196 		ddi_taskq_wait(thread->tq);
197 		thread->state = ISCSI_THREAD_STATE_STOPPED;
198 		ret = B_TRUE;
199 		break;
200 
201 	case ISCSI_THREAD_STATE_STOPPED:
202 		ret = B_TRUE;
203 		break;
204 
205 	default:
206 		ASSERT(0);
207 		break;
208 	}
209 	mutex_exit(&thread->mgnt.mtx);
210 	return (ret);
211 }
212 
213 /*
214  * iscsi_thread_send_kill -
215  */
216 void
iscsi_thread_send_kill(iscsi_thread_t * thread)217 iscsi_thread_send_kill(
218 	iscsi_thread_t		*thread
219 )
220 {
221 	ASSERT(thread != NULL);
222 	ASSERT(thread->signature == SIG_ISCSI_THREAD);
223 
224 	mutex_enter(&thread->mgnt.mtx);
225 
226 	switch (thread->state) {
227 
228 	case ISCSI_THREAD_STATE_STARTED:
229 
230 		mutex_enter(&thread->sign.mtx);
231 		if (!(thread->sign.bitmap & ISCSI_THREAD_SIGNAL_KILL)) {
232 			thread->sign.bitmap |= ISCSI_THREAD_SIGNAL_KILL;
233 			cv_signal(&thread->sign.cdv);
234 		}
235 		mutex_exit(&thread->sign.mtx);
236 		break;
237 
238 	default:
239 		ASSERT(0);
240 		break;
241 	}
242 	mutex_exit(&thread->mgnt.mtx);
243 }
244 
245 /*
246  * iscsi_thread_send_wakeup -
247  */
248 boolean_t
iscsi_thread_send_wakeup(iscsi_thread_t * thread)249 iscsi_thread_send_wakeup(
250 	iscsi_thread_t		*thread
251 )
252 {
253 	boolean_t	ret = B_FALSE;
254 
255 	ASSERT(thread != NULL);
256 	ASSERT(thread->signature == SIG_ISCSI_THREAD);
257 
258 	mutex_enter(&thread->mgnt.mtx);
259 
260 	switch (thread->state) {
261 
262 	case ISCSI_THREAD_STATE_STARTED:
263 
264 		mutex_enter(&thread->sign.mtx);
265 		if (!(thread->sign.bitmap & ISCSI_THREAD_SIGNAL_WAKEUP)) {
266 			thread->sign.bitmap |= ISCSI_THREAD_SIGNAL_WAKEUP;
267 			cv_signal(&thread->sign.cdv);
268 		}
269 		mutex_exit(&thread->sign.mtx);
270 		ret = B_TRUE;
271 		break;
272 
273 	default:
274 		break;
275 	}
276 	mutex_exit(&thread->mgnt.mtx);
277 	return (ret);
278 }
279 
280 /*
281  * iscsi_thread_check_signals -
282  */
283 uint32_t
iscsi_thread_check_signals(iscsi_thread_t * thread)284 iscsi_thread_check_signals(
285 	iscsi_thread_t		*thread
286 )
287 {
288 	uint32_t		bitmap;
289 
290 	ASSERT(thread != NULL);
291 	ASSERT(thread->signature == SIG_ISCSI_THREAD);
292 
293 	/* Acquire the mutex before anychecking. */
294 	mutex_enter(&thread->sign.mtx);
295 	bitmap = thread->sign.bitmap;
296 	mutex_exit(&thread->sign.mtx);
297 	return (bitmap);
298 }
299 /*
300  * iscsi_thread_wait -
301  */
302 int
iscsi_thread_wait(iscsi_thread_t * thread,clock_t timeout)303 iscsi_thread_wait(
304 	iscsi_thread_t		*thread,
305 	clock_t			timeout
306 )
307 {
308 	int			rtn = 1;
309 
310 	ASSERT(thread != NULL);
311 	ASSERT(thread->signature == SIG_ISCSI_THREAD);
312 
313 	/* Acquire the mutex before anychecking. */
314 	mutex_enter(&thread->sign.mtx);
315 
316 	/* Check the signals. */
317 	if (thread->sign.bitmap & ISCSI_THREAD_SIGNAL_KILL) {
318 		goto signal_kill;
319 	} else if (thread->sign.bitmap & ISCSI_THREAD_SIGNAL_WAKEUP) {
320 		goto signal_wakeup;
321 	} else if (timeout == 0) {
322 		goto iscsi_thread_sleep_exit;
323 	}
324 
325 	if (timeout == -1) {
326 		cv_wait(&thread->sign.cdv, &thread->sign.mtx);
327 	} else {
328 		rtn = cv_reltimedwait(&thread->sign.cdv, &thread->sign.mtx,
329 		    timeout, TR_CLOCK_TICK);
330 	}
331 
332 	/* Check the signals. */
333 	if (thread->sign.bitmap & ISCSI_THREAD_SIGNAL_KILL) {
334 		goto signal_kill;
335 	} else if (thread->sign.bitmap & ISCSI_THREAD_SIGNAL_WAKEUP) {
336 		goto signal_wakeup;
337 	}
338 
339 iscsi_thread_sleep_exit:
340 	mutex_exit(&thread->sign.mtx);
341 	return (rtn);
342 
343 signal_kill:
344 	mutex_exit(&thread->sign.mtx);
345 	return (0);
346 
347 signal_wakeup:
348 	thread->sign.bitmap &= ~ISCSI_THREAD_SIGNAL_WAKEUP;
349 	mutex_exit(&thread->sign.mtx);
350 	return (1);
351 }
352 
353 /*
354  * iscsi_threads_entry - Common entry point for all threads
355  */
356 static
357 void
iscsi_threads_entry(void * arg)358 iscsi_threads_entry(
359 	void			*arg
360 )
361 {
362 	iscsi_thread_t		*thread;
363 
364 	thread = (iscsi_thread_t *)arg;
365 
366 	ASSERT(thread != NULL);
367 	ASSERT(thread->signature == SIG_ISCSI_THREAD);
368 
369 	(thread->entry_point)(thread, thread->arg);
370 }
371