xref: /illumos-gate/usr/src/cmd/ndmpd/tlm/tlm_buffers.c (revision 2654012f)
1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * BSD 3 Clause License
8  *
9  * Copyright (c) 2007, The Storage Networking Industry Association.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 	- Redistributions of source code must retain the above copyright
15  *	  notice, this list of conditions and the following disclaimer.
16  *
17  * 	- Redistributions in binary form must reproduce the above copyright
18  *	  notice, this list of conditions and the following disclaimer in
19  *	  the documentation and/or other materials provided with the
20  *	  distribution.
21  *
22  *	- Neither the name of The Storage Networking Industry Association (SNIA)
23  *	  nor the names of its contributors may be used to endorse or promote
24  *	  products derived from this software without specific prior written
25  *	  permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
31  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <sys/errno.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <pthread.h>
45 #include <synch.h>
46 #include <tlm_buffers.h>
47 #include <tlm.h>
48 #include "tlm_proto.h"
49 
50 
51 /*
52  * tlm_allocate_buffers
53  *
54  * build a set of buffers
55  */
56 tlm_buffers_t *
tlm_allocate_buffers(boolean_t write,long xfer_size)57 tlm_allocate_buffers(boolean_t write, long xfer_size)
58 {
59 	tlm_buffers_t *buffers = ndmp_malloc(sizeof (tlm_buffers_t));
60 	int	buf;
61 
62 	if (buffers == 0)
63 		return (0);
64 
65 	for (buf = 0; buf < TLM_TAPE_BUFFERS; buf++) {
66 		buffers->tbs_buffer[buf].tb_buffer_data =
67 		    ndmp_malloc(xfer_size);
68 		if (buffers->tbs_buffer[buf].tb_buffer_data == 0) {
69 			int	i;
70 
71 			/* Memory allocation failed. Give everything back */
72 			for (i = 0; i < buf; i++)
73 				free(buffers->tbs_buffer[i].tb_buffer_data);
74 
75 			free(buffers);
76 			return (0);
77 		} else {
78 			buffers->tbs_buffer[buf].tb_buffer_size = (write)
79 			    ? xfer_size : 0;
80 			buffers->tbs_buffer[buf].tb_full = FALSE;
81 			buffers->tbs_buffer[buf].tb_eof = FALSE;
82 			buffers->tbs_buffer[buf].tb_eot = FALSE;
83 			buffers->tbs_buffer[buf].tb_errno = 0;
84 			buffers->tbs_buffer[buf].tb_buffer_spot = 0;
85 		}
86 
87 	}
88 
89 	(void) mutex_init(&buffers->tbs_mtx, 0, NULL);
90 	(void) cond_init(&buffers->tbs_in_cv, 0, NULL);
91 	(void) cond_init(&buffers->tbs_out_cv, 0, NULL);
92 
93 	buffers->tbs_data_transfer_size = xfer_size;
94 	buffers->tbs_ref = 1;
95 	return (buffers);
96 }
97 
98 /*
99  * tlm_release_buffers
100  *
101  * give all memory back to the OS
102  */
103 void
tlm_release_buffers(tlm_buffers_t * buffers)104 tlm_release_buffers(tlm_buffers_t *buffers)
105 {
106 	int i;
107 
108 	if (buffers != NULL) {
109 		tlm_buffer_release_in_buf(buffers);
110 		tlm_buffer_release_out_buf(buffers);
111 
112 		(void) mutex_lock(&buffers->tbs_mtx);
113 
114 		if (--buffers->tbs_ref <= 0) {
115 			for (i = 0; i < TLM_TAPE_BUFFERS; i++)
116 				free(buffers->tbs_buffer[i].tb_buffer_data);
117 
118 		}
119 
120 		(void) cond_destroy(&buffers->tbs_in_cv);
121 		(void) cond_destroy(&buffers->tbs_out_cv);
122 		(void) mutex_unlock(&buffers->tbs_mtx);
123 		(void) mutex_destroy(&buffers->tbs_mtx);
124 		free(buffers);
125 	}
126 }
127 
128 /*
129  * tlm_buffer_mark_empty
130  *
131  * Mark a buffer empty and clear its flags. No lock is take here:
132  * the buffer should be marked empty before it is released for use
133  * by another thread.
134  */
135 void
tlm_buffer_mark_empty(tlm_buffer_t * buf)136 tlm_buffer_mark_empty(tlm_buffer_t *buf)
137 {
138 	if (buf == NULL)
139 		return;
140 
141 	buf->tb_full = buf->tb_eof = buf->tb_eot = FALSE;
142 	buf->tb_errno = 0;
143 }
144 
145 
146 /*
147  * tlm_buffer_advance_in_idx
148  *
149  * Advance the input index of the buffers(round-robin) and return pointer
150  * to the next buffer in the buffer pool.
151  */
152 tlm_buffer_t *
tlm_buffer_advance_in_idx(tlm_buffers_t * bufs)153 tlm_buffer_advance_in_idx(tlm_buffers_t *bufs)
154 {
155 	if (bufs == NULL)
156 		return (NULL);
157 
158 	(void) mutex_lock(&bufs->tbs_mtx);
159 	if (++bufs->tbs_buffer_in >= TLM_TAPE_BUFFERS)
160 		bufs->tbs_buffer_in = 0;
161 
162 	(void) mutex_unlock(&bufs->tbs_mtx);
163 	return (&bufs->tbs_buffer[bufs->tbs_buffer_in]);
164 }
165 
166 
167 /*
168  * tlm_buffer_advance_out_idx
169  *
170  * Advance the output index of the buffers(round-robin) and return pointer
171  * to the next buffer in the buffer pool.
172  */
173 tlm_buffer_t *
tlm_buffer_advance_out_idx(tlm_buffers_t * bufs)174 tlm_buffer_advance_out_idx(tlm_buffers_t *bufs)
175 {
176 	if (bufs == NULL)
177 		return (NULL);
178 
179 	(void) mutex_lock(&bufs->tbs_mtx);
180 	if (++bufs->tbs_buffer_out >= TLM_TAPE_BUFFERS)
181 		bufs->tbs_buffer_out = 0;
182 
183 	(void) mutex_unlock(&bufs->tbs_mtx);
184 	return (&bufs->tbs_buffer[bufs->tbs_buffer_out]);
185 }
186 
187 
188 /*
189  * tlm_buffer_in_buf
190  *
191  * Return pointer to the next buffer in the buffer pool.
192  */
193 tlm_buffer_t *
tlm_buffer_in_buf(tlm_buffers_t * bufs,int * idx)194 tlm_buffer_in_buf(tlm_buffers_t *bufs, int *idx)
195 {
196 	tlm_buffer_t *ret;
197 
198 	if (bufs == NULL)
199 		return (NULL);
200 
201 	(void) mutex_lock(&bufs->tbs_mtx);
202 	ret = &bufs->tbs_buffer[bufs->tbs_buffer_in];
203 	if (idx)
204 		*idx = bufs->tbs_buffer_in;
205 	(void) mutex_unlock(&bufs->tbs_mtx);
206 	return (ret);
207 }
208 
209 
210 /*
211  * tlm_buffer_out_buf
212  *
213  * Return pointer to the next buffer in the buffer pool.
214  */
215 tlm_buffer_t *
tlm_buffer_out_buf(tlm_buffers_t * bufs,int * idx)216 tlm_buffer_out_buf(tlm_buffers_t *bufs, int *idx)
217 {
218 	tlm_buffer_t *ret;
219 
220 	if (bufs == NULL)
221 		return (NULL);
222 
223 	(void) mutex_lock(&bufs->tbs_mtx);
224 	ret = &bufs->tbs_buffer[bufs->tbs_buffer_out];
225 	if (idx)
226 		*idx = bufs->tbs_buffer_out;
227 	(void) mutex_unlock(&bufs->tbs_mtx);
228 	return (ret);
229 }
230 
231 
232 /*
233  * tlm_buffer_release_in_buf
234  *
235  * Another buffer is filled. Wake up the consumer if it's waiting for it.
236  */
237 void
tlm_buffer_release_in_buf(tlm_buffers_t * bufs)238 tlm_buffer_release_in_buf(tlm_buffers_t *bufs)
239 {
240 	(void) mutex_lock(&bufs->tbs_mtx);
241 	bufs->tbs_flags |= TLM_BUF_IN_READY;
242 	(void) cond_signal(&bufs->tbs_in_cv);
243 	(void) mutex_unlock(&bufs->tbs_mtx);
244 }
245 
246 
247 /*
248  * tlm_buffer_release_out_buf
249  *
250  * A buffer is used. Wake up the producer to re-fill a buffer if it's waiting
251  * for the buffer to be used.
252  */
253 void
tlm_buffer_release_out_buf(tlm_buffers_t * bufs)254 tlm_buffer_release_out_buf(tlm_buffers_t *bufs)
255 {
256 	(void) mutex_lock(&bufs->tbs_mtx);
257 	bufs->tbs_flags |= TLM_BUF_OUT_READY;
258 	(void) cond_signal(&bufs->tbs_out_cv);
259 	(void) mutex_unlock(&bufs->tbs_mtx);
260 }
261 
262 /*
263  * tlm_buffer_in_buf_wait
264  *
265  * Wait for the input buffer to get available.
266  */
267 void
tlm_buffer_in_buf_wait(tlm_buffers_t * bufs)268 tlm_buffer_in_buf_wait(tlm_buffers_t *bufs)
269 
270 {
271 	(void) mutex_lock(&bufs->tbs_mtx);
272 
273 	while ((bufs->tbs_flags & TLM_BUF_IN_READY) == 0)
274 		(void) cond_wait(&bufs->tbs_in_cv, &bufs->tbs_mtx);
275 
276 	bufs->tbs_flags &= ~TLM_BUF_IN_READY;
277 
278 	(void) mutex_unlock(&bufs->tbs_mtx);
279 }
280 
281 /*
282  * tlm_buffer_setup_timer
283  *
284  * Set up the time out value.
285  */
286 static inline void
tlm_buffer_setup_timer(timestruc_t * timo,unsigned milli_timo)287 tlm_buffer_setup_timer(timestruc_t *timo, unsigned milli_timo)
288 {
289 	if (milli_timo == 0)
290 		milli_timo = 1;
291 
292 	if (milli_timo / 1000)
293 		timo->tv_sec = (milli_timo / 1000);
294 	else
295 		timo->tv_sec = 0;
296 	timo->tv_nsec = (milli_timo % 1000) * 1000000L;
297 }
298 
299 
300 /*
301  * tlm_buffer_in_buf_timed_wait
302  *
303  * Wait for the input buffer to get ready with a time out.
304  */
305 void
tlm_buffer_in_buf_timed_wait(tlm_buffers_t * bufs,unsigned int milli_timo)306 tlm_buffer_in_buf_timed_wait(tlm_buffers_t *bufs, unsigned int milli_timo)
307 
308 {
309 	timestruc_t timo;
310 
311 	tlm_buffer_setup_timer(&timo, milli_timo);
312 
313 	(void) mutex_lock(&bufs->tbs_mtx);
314 
315 	(void) cond_reltimedwait(&bufs->tbs_in_cv, &bufs->tbs_mtx, &timo);
316 
317 	/*
318 	 * TLM_BUF_IN_READY doesn't matter for timedwait but clear
319 	 * it here so that cond_wait doesn't get the wrong result.
320 	 */
321 	bufs->tbs_flags &= ~TLM_BUF_IN_READY;
322 
323 	(void) mutex_unlock(&bufs->tbs_mtx);
324 }
325 
326 
327 /*
328  * tlm_buffer_out_buf_timed_wait
329  *
330  * Wait for the output buffer to get ready with a time out.
331  */
332 void
tlm_buffer_out_buf_timed_wait(tlm_buffers_t * bufs,unsigned int milli_timo)333 tlm_buffer_out_buf_timed_wait(tlm_buffers_t *bufs, unsigned int milli_timo)
334 {
335 	timestruc_t timo;
336 
337 	tlm_buffer_setup_timer(&timo, milli_timo);
338 
339 	(void) mutex_lock(&bufs->tbs_mtx);
340 
341 	(void) cond_reltimedwait(&bufs->tbs_out_cv, &bufs->tbs_mtx, &timo);
342 
343 	/*
344 	 * TLM_BUF_OUT_READY doesn't matter for timedwait but clear
345 	 * it here so that cond_wait doesn't get the wrong result.
346 	 */
347 	bufs->tbs_flags &= ~TLM_BUF_OUT_READY;
348 
349 	(void) mutex_unlock(&bufs->tbs_mtx);
350 }
351 
352 
353 /*
354  * tlm_cmd_wait
355  *
356  * TLM command synchronization typically use by command
357  * parent threads to wait for launched threads to initialize.
358  */
359 void
tlm_cmd_wait(tlm_cmd_t * cmd,uint32_t event_type)360 tlm_cmd_wait(tlm_cmd_t *cmd, uint32_t event_type)
361 {
362 	(void) mutex_lock(&cmd->tc_mtx);
363 
364 	while ((cmd->tc_flags & event_type) == 0)
365 		(void) cond_wait(&cmd->tc_cv, &cmd->tc_mtx);
366 
367 	cmd->tc_flags &= ~event_type;
368 	(void) mutex_unlock(&cmd->tc_mtx);
369 }
370 
371 
372 /*
373  * tlm_cmd_signal
374  *
375  * TLM command synchronization typically use by launched threads
376  * to unleash the parent thread.
377  */
378 void
tlm_cmd_signal(tlm_cmd_t * cmd,uint32_t event_type)379 tlm_cmd_signal(tlm_cmd_t *cmd, uint32_t event_type)
380 {
381 	(void) mutex_lock(&cmd->tc_mtx);
382 
383 	cmd->tc_flags |= event_type;
384 	(void) cond_signal(&cmd->tc_cv);
385 
386 	(void) mutex_unlock(&cmd->tc_mtx);
387 }
388