1/*
2 * Copyright (c) 2004-2006 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
4 * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5 *
6 * This software is available to you under a choice of one of two
7 * licenses.  You may choose to be licensed under the terms of the GNU
8 * General Public License (GPL) Version 2, available from the file
9 * COPYING in the main directory of this source tree, or the
10 * OpenIB.org BSD license below:
11 *
12 *     Redistribution and use in source and binary forms, with or
13 *     without modification, are permitted provided that the following
14 *     conditions are met:
15 *
16 *      - Redistributions of source code must retain the above
17 *        copyright notice, this list of conditions and the following
18 *        disclaimer.
19 *
20 *      - Redistributions in binary form must reproduce the above
21 *        copyright notice, this list of conditions and the following
22 *        disclaimer in the documentation and/or other materials
23 *        provided with the distribution.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 * SOFTWARE.
33 *
34 */
35
36/*
37 * Abstract:
38 *	Implementation of the grow pools.  The grow pools manage a pool of objects.
39 *	The pools can grow to meet demand, limited only by system memory.
40 *
41 */
42
43#if HAVE_CONFIG_H
44#  include <config.h>
45#endif				/* HAVE_CONFIG_H */
46
47#include <stdlib.h>
48#include <string.h>
49#include <complib/cl_qcomppool.h>
50#include <complib/cl_comppool.h>
51#include <complib/cl_qpool.h>
52#include <complib/cl_pool.h>
53#include <complib/cl_math.h>
54
55/*
56 * IMPLEMENTATION OF QUICK COMPOSITE POOL
57 */
58void cl_qcpool_construct(IN cl_qcpool_t * const p_pool)
59{
60	CL_ASSERT(p_pool);
61
62	memset(p_pool, 0, sizeof(cl_qcpool_t));
63
64	p_pool->state = CL_UNINITIALIZED;
65}
66
67cl_status_t
68cl_qcpool_init(IN cl_qcpool_t * const p_pool,
69	       IN const size_t min_size,
70	       IN const size_t max_size,
71	       IN const size_t grow_size,
72	       IN const size_t * const component_sizes,
73	       IN const uint32_t num_components,
74	       IN cl_pfn_qcpool_init_t pfn_initializer OPTIONAL,
75	       IN cl_pfn_qcpool_dtor_t pfn_destructor OPTIONAL,
76	       IN const void *const context)
77{
78	cl_status_t status;
79	uint32_t i;
80
81	CL_ASSERT(p_pool);
82	/* Must have a minimum of 1 component. */
83	CL_ASSERT(num_components);
84	/* A component size array is required. */
85	CL_ASSERT(component_sizes);
86	/*
87	 * If no initializer is provided, the first component must be large
88	 * enough to hold a pool item.
89	 */
90	CL_ASSERT(pfn_initializer ||
91		  (component_sizes[0] >= sizeof(cl_pool_item_t)));
92
93	cl_qcpool_construct(p_pool);
94
95	if (num_components > 1 && !pfn_initializer)
96		return (CL_INVALID_SETTING);
97
98	if (max_size && max_size < min_size)
99		return (CL_INVALID_SETTING);
100
101	/*
102	 * Allocate the array of component sizes and component pointers all
103	 * in one allocation.
104	 */
105	p_pool->component_sizes = (size_t *) malloc((sizeof(size_t) +
106						     sizeof(void *)) *
107						    num_components);
108
109	if (!p_pool->component_sizes)
110		return (CL_INSUFFICIENT_MEMORY);
111	else
112		memset(p_pool->component_sizes, 0,
113		       (sizeof(size_t) + sizeof(void *)) * num_components);
114
115	/* Calculate the pointer to the array of pointers, used for callbacks. */
116	p_pool->p_components =
117	    (void **)(p_pool->component_sizes + num_components);
118
119	/* Copy the user's sizes into our array for future use. */
120	memcpy(p_pool->component_sizes, component_sizes,
121	       sizeof(component_sizes[0]) * num_components);
122
123	/* Store the number of components per object. */
124	p_pool->num_components = num_components;
125
126	/* Round up and store the size of the components. */
127	for (i = 0; i < num_components; i++) {
128		/*
129		 * We roundup each component size so that all components
130		 * are aligned on a natural boundary.
131		 */
132		p_pool->component_sizes[i] =
133		    ROUNDUP(p_pool->component_sizes[i], sizeof(uintn_t));
134	}
135
136	p_pool->max_objects = max_size ? max_size : ~(size_t) 0;
137	p_pool->grow_size = grow_size;
138
139	/* Store callback function pointers. */
140	p_pool->pfn_init = pfn_initializer;	/* may be NULL */
141	p_pool->pfn_dtor = pfn_destructor;	/* may be NULL */
142	p_pool->context = context;
143
144	cl_qlist_init(&p_pool->alloc_list);
145
146	cl_qlist_init(&p_pool->free_list);
147
148	/*
149	 * We are now initialized.  We change the initialized flag before
150	 * growing since the grow function asserts that we are initialized.
151	 */
152	p_pool->state = CL_INITIALIZED;
153
154	/* Allocate the minimum number of objects as requested. */
155	if (!min_size)
156		return (CL_SUCCESS);
157
158	status = cl_qcpool_grow(p_pool, min_size);
159	/* Trap for error and cleanup if necessary. */
160	if (status != CL_SUCCESS)
161		cl_qcpool_destroy(p_pool);
162
163	return (status);
164}
165
166void cl_qcpool_destroy(IN cl_qcpool_t * const p_pool)
167{
168	/* CL_ASSERT that a non-NULL pointer was provided. */
169	CL_ASSERT(p_pool);
170	/* CL_ASSERT that we are in a valid state (not uninitialized memory). */
171	CL_ASSERT(cl_is_state_valid(p_pool->state));
172
173	if (p_pool->state == CL_INITIALIZED) {
174		/*
175		 * Assert if the user hasn't put everything back in the pool
176		 * before destroying it
177		 * if they haven't, then most likely they are still using memory
178		 * that will be freed, and the destructor will not be called!
179		 */
180#ifdef _DEBUG_
181		/* but we do not want "free" version to assert on this one */
182		CL_ASSERT(cl_qcpool_count(p_pool) == p_pool->num_objects);
183#endif
184		/* call the user's destructor for each object in the pool */
185		if (p_pool->pfn_dtor) {
186			while (!cl_is_qlist_empty(&p_pool->free_list)) {
187				p_pool->pfn_dtor((cl_pool_item_t *)
188						 cl_qlist_remove_head(&p_pool->
189								      free_list),
190						 (void *)p_pool->context);
191			}
192		} else {
193			cl_qlist_remove_all(&p_pool->free_list);
194		}
195
196		/* Free all allocated memory blocks. */
197		while (!cl_is_qlist_empty(&p_pool->alloc_list))
198			free(cl_qlist_remove_head(&p_pool->alloc_list));
199
200		if (p_pool->component_sizes) {
201			free(p_pool->component_sizes);
202			p_pool->component_sizes = NULL;
203		}
204	}
205
206	p_pool->state = CL_UNINITIALIZED;
207}
208
209cl_status_t cl_qcpool_grow(IN cl_qcpool_t * const p_pool, IN size_t obj_count)
210{
211	cl_status_t status = CL_SUCCESS;
212	uint8_t *p_objects;
213	cl_pool_item_t *p_pool_item;
214	uint32_t i;
215	size_t obj_size;
216
217	CL_ASSERT(p_pool);
218	CL_ASSERT(p_pool->state == CL_INITIALIZED);
219	CL_ASSERT(obj_count);
220
221	/* Validate that growth is possible. */
222	if (p_pool->num_objects == p_pool->max_objects)
223		return (CL_INSUFFICIENT_MEMORY);
224
225	/* Cap the growth to the desired maximum. */
226	if (obj_count > (p_pool->max_objects - p_pool->num_objects))
227		obj_count = p_pool->max_objects - p_pool->num_objects;
228
229	/* Calculate the size of an object. */
230	obj_size = 0;
231	for (i = 0; i < p_pool->num_components; i++)
232		obj_size += p_pool->component_sizes[i];
233
234	/* Allocate the buffer for the new objects. */
235	p_objects = (uint8_t *)
236	    malloc(sizeof(cl_list_item_t) + (obj_size * obj_count));
237
238	/* Make sure the allocation succeeded. */
239	if (!p_objects)
240		return (CL_INSUFFICIENT_MEMORY);
241	else
242		memset(p_objects, 0,
243		       sizeof(cl_list_item_t) + (obj_size * obj_count));
244
245	/* Insert the allocation in our list. */
246	cl_qlist_insert_tail(&p_pool->alloc_list, (cl_list_item_t *) p_objects);
247	p_objects += sizeof(cl_list_item_t);
248
249	/* initialize the new elements and add them to the free list */
250	while (obj_count--) {
251		/* Setup the array of components for the current object. */
252		p_pool->p_components[0] = p_objects;
253		for (i = 1; i < p_pool->num_components; i++) {
254			/* Calculate the pointer to the next component. */
255			p_pool->p_components[i] =
256			    (uint8_t *) p_pool->p_components[i - 1] +
257			    p_pool->component_sizes[i - 1];
258		}
259
260		/*
261		 * call the user's initializer
262		 * this can fail!
263		 */
264		if (p_pool->pfn_init) {
265			p_pool_item = NULL;
266			status = p_pool->pfn_init(p_pool->p_components,
267						  p_pool->num_components,
268						  (void *)p_pool->context,
269						  &p_pool_item);
270			if (status != CL_SUCCESS) {
271				/*
272				 * User initialization failed
273				 * we may have only grown the pool by some partial amount
274				 * Invoke the destructor for the object that failed
275				 * initialization.
276				 */
277				if (p_pool->pfn_dtor)
278					p_pool->pfn_dtor(p_pool_item,
279							 (void *)p_pool->
280							 context);
281
282				/* Return the user's status. */
283				return (status);
284			}
285			CL_ASSERT(p_pool_item);
286		} else {
287			/*
288			 * If no initializer is provided, assume that the pool item
289			 * is stored at the beginning of the first component.
290			 */
291			p_pool_item =
292			    (cl_pool_item_t *) p_pool->p_components[0];
293		}
294
295#ifdef _DEBUG_
296		/*
297		 * Set the pool item's pool pointer to this pool so that we can
298		 * check that items get returned to the correct pool.
299		 */
300		p_pool_item->p_pool = p_pool;
301#endif
302
303		/* Insert the new item in the free list, traping for failure. */
304		cl_qlist_insert_head(&p_pool->free_list,
305				     &p_pool_item->list_item);
306
307		p_pool->num_objects++;
308
309		/* move the pointer to the next item */
310		p_objects += obj_size;
311	}
312
313	return (status);
314}
315
316cl_pool_item_t *cl_qcpool_get(IN cl_qcpool_t * const p_pool)
317{
318	cl_list_item_t *p_list_item;
319
320	CL_ASSERT(p_pool);
321	CL_ASSERT(p_pool->state == CL_INITIALIZED);
322
323	if (cl_is_qlist_empty(&p_pool->free_list)) {
324		/*
325		 * No object is available.
326		 * Return NULL if the user does not want automatic growth.
327		 */
328		if (!p_pool->grow_size)
329			return (NULL);
330
331		/* We ran out of elements.  Get more */
332		cl_qcpool_grow(p_pool, p_pool->grow_size);
333		/*
334		 * We may not have gotten everything we wanted but we might have
335		 * gotten something.
336		 */
337		if (cl_is_qlist_empty(&p_pool->free_list))
338			return (NULL);
339	}
340
341	p_list_item = cl_qlist_remove_head(&p_pool->free_list);
342	/* OK, at this point we have an object */
343	CL_ASSERT(p_list_item != cl_qlist_end(&p_pool->free_list));
344	return ((cl_pool_item_t *) p_list_item);
345}
346
347cl_pool_item_t *cl_qcpool_get_tail(IN cl_qcpool_t * const p_pool)
348{
349	cl_list_item_t *p_list_item;
350
351	CL_ASSERT(p_pool);
352	CL_ASSERT(p_pool->state == CL_INITIALIZED);
353
354	if (cl_is_qlist_empty(&p_pool->free_list)) {
355		/*
356		 * No object is available.
357		 * Return NULL if the user does not want automatic growth.
358		 */
359		if (!p_pool->grow_size)
360			return (NULL);
361
362		/* We ran out of elements.  Get more */
363		cl_qcpool_grow(p_pool, p_pool->grow_size);
364		/*
365		 * We may not have gotten everything we wanted but we might have
366		 * gotten something.
367		 */
368		if (cl_is_qlist_empty(&p_pool->free_list))
369			return (NULL);
370	}
371
372	p_list_item = cl_qlist_remove_tail(&p_pool->free_list);
373	/* OK, at this point we have an object */
374	CL_ASSERT(p_list_item != cl_qlist_end(&p_pool->free_list));
375	return ((cl_pool_item_t *) p_list_item);
376}
377
378/*
379 * IMPLEMENTATION OF QUICK GROW POOL
380 */
381
382/*
383 * Callback to translate quick composite to quick grow pool
384 * initializer callback.
385 */
386static cl_status_t
387__cl_qpool_init_cb(IN void **const p_comp_array,
388		   IN const uint32_t num_components,
389		   IN void *const context,
390		   OUT cl_pool_item_t ** const pp_pool_item)
391{
392	cl_qpool_t *p_pool = (cl_qpool_t *) context;
393
394	CL_ASSERT(p_pool);
395	CL_ASSERT(p_pool->pfn_init);
396	CL_ASSERT(num_components == 1);
397
398	UNUSED_PARAM(num_components);
399
400	return (p_pool->pfn_init(p_comp_array[0], (void *)p_pool->context,
401				 pp_pool_item));
402}
403
404/*
405 * Callback to translate quick composite to quick grow pool
406 * destructor callback.
407 */
408static void
409__cl_qpool_dtor_cb(IN const cl_pool_item_t * const p_pool_item,
410		   IN void *const context)
411{
412	cl_qpool_t *p_pool = (cl_qpool_t *) context;
413
414	CL_ASSERT(p_pool);
415	CL_ASSERT(p_pool->pfn_dtor);
416
417	p_pool->pfn_dtor(p_pool_item, (void *)p_pool->context);
418}
419
420void cl_qpool_construct(IN cl_qpool_t * const p_pool)
421{
422	memset(p_pool, 0, sizeof(cl_qpool_t));
423
424	cl_qcpool_construct(&p_pool->qcpool);
425}
426
427cl_status_t
428cl_qpool_init(IN cl_qpool_t * const p_pool,
429	      IN const size_t min_size,
430	      IN const size_t max_size,
431	      IN const size_t grow_size,
432	      IN const size_t object_size,
433	      IN cl_pfn_qpool_init_t pfn_initializer OPTIONAL,
434	      IN cl_pfn_qpool_dtor_t pfn_destructor OPTIONAL,
435	      IN const void *const context)
436{
437	cl_status_t status;
438
439	CL_ASSERT(p_pool);
440
441	p_pool->pfn_init = pfn_initializer;	/* may be NULL */
442	p_pool->pfn_dtor = pfn_destructor;	/* may be NULL */
443	p_pool->context = context;
444
445	status = cl_qcpool_init(&p_pool->qcpool, min_size, max_size, grow_size,
446				&object_size, 1,
447				pfn_initializer ? __cl_qpool_init_cb : NULL,
448				pfn_destructor ? __cl_qpool_dtor_cb : NULL,
449				p_pool);
450
451	return (status);
452}
453
454/*
455 * IMPLEMENTATION OF COMPOSITE POOL
456 */
457
458/*
459 * Callback to translate quick composite to compsite pool
460 * initializer callback.
461 */
462static cl_status_t
463__cl_cpool_init_cb(IN void **const p_comp_array,
464		   IN const uint32_t num_components,
465		   IN void *const context,
466		   OUT cl_pool_item_t ** const pp_pool_item)
467{
468	cl_cpool_t *p_pool = (cl_cpool_t *) context;
469	cl_pool_obj_t *p_pool_obj;
470	cl_status_t status = CL_SUCCESS;
471
472	CL_ASSERT(p_pool);
473
474	/*
475	 * Set our pointer to the list item, which is stored at the beginning of
476	 * the first component.
477	 */
478	p_pool_obj = (cl_pool_obj_t *) p_comp_array[0];
479	/* Set the pool item pointer for the caller. */
480	*pp_pool_item = &p_pool_obj->pool_item;
481
482	/* Calculate the pointer to the user's first component. */
483	p_comp_array[0] = ((uint8_t *) p_comp_array[0]) + sizeof(cl_pool_obj_t);
484
485	/*
486	 * Set the object pointer in the pool object to point to the first of the
487	 * user's components.
488	 */
489	p_pool_obj->p_object = p_comp_array[0];
490
491	/* Invoke the user's constructor callback. */
492	if (p_pool->pfn_init) {
493		status = p_pool->pfn_init(p_comp_array, num_components,
494					  (void *)p_pool->context);
495	}
496
497	return (status);
498}
499
500/*
501 * Callback to translate quick composite to composite pool
502 * destructor callback.
503 */
504static void
505__cl_cpool_dtor_cb(IN const cl_pool_item_t * const p_pool_item,
506		   IN void *const context)
507{
508	cl_cpool_t *p_pool = (cl_cpool_t *) context;
509
510	CL_ASSERT(p_pool);
511	CL_ASSERT(p_pool->pfn_dtor);
512	CL_ASSERT(((cl_pool_obj_t *) p_pool_item)->p_object);
513
514	/* Invoke the user's destructor callback. */
515	p_pool->pfn_dtor((void *)((cl_pool_obj_t *) p_pool_item)->p_object,
516			 (void *)p_pool->context);
517}
518
519void cl_cpool_construct(IN cl_cpool_t * const p_pool)
520{
521	CL_ASSERT(p_pool);
522
523	memset(p_pool, 0, sizeof(cl_cpool_t));
524
525	cl_qcpool_construct(&p_pool->qcpool);
526}
527
528cl_status_t
529cl_cpool_init(IN cl_cpool_t * const p_pool,
530	      IN const size_t min_size,
531	      IN const size_t max_size,
532	      IN const size_t grow_size,
533	      IN size_t * const component_sizes,
534	      IN const uint32_t num_components,
535	      IN cl_pfn_cpool_init_t pfn_initializer OPTIONAL,
536	      IN cl_pfn_cpool_dtor_t pfn_destructor OPTIONAL,
537	      IN const void *const context)
538{
539	cl_status_t status;
540
541	CL_ASSERT(p_pool);
542	CL_ASSERT(num_components);
543	CL_ASSERT(component_sizes);
544
545	/* Add the size of the pool object to the first component. */
546	component_sizes[0] += sizeof(cl_pool_obj_t);
547
548	/* Store callback function pointers. */
549	p_pool->pfn_init = pfn_initializer;	/* may be NULL */
550	p_pool->pfn_dtor = pfn_destructor;	/* may be NULL */
551	p_pool->context = context;
552
553	status = cl_qcpool_init(&p_pool->qcpool, min_size, max_size, grow_size,
554				component_sizes, num_components,
555				__cl_cpool_init_cb,
556				pfn_destructor ? __cl_cpool_dtor_cb : NULL,
557				p_pool);
558
559	/* Restore the original value of the first component. */
560	component_sizes[0] -= sizeof(cl_pool_obj_t);
561
562	return (status);
563}
564
565/*
566 * IMPLEMENTATION OF GROW POOL
567 */
568
569/*
570 * Callback to translate quick composite to grow pool constructor callback.
571 */
572static cl_status_t
573__cl_pool_init_cb(IN void **const pp_obj,
574		  IN const uint32_t count,
575		  IN void *const context,
576		  OUT cl_pool_item_t ** const pp_pool_item)
577{
578	cl_pool_t *p_pool = (cl_pool_t *) context;
579	cl_pool_obj_t *p_pool_obj;
580	cl_status_t status = CL_SUCCESS;
581
582	CL_ASSERT(p_pool);
583	CL_ASSERT(pp_obj);
584	CL_ASSERT(count == 1);
585
586	UNUSED_PARAM(count);
587
588	/*
589	 * Set our pointer to the list item, which is stored at the beginning of
590	 * the first component.
591	 */
592	p_pool_obj = (cl_pool_obj_t *) * pp_obj;
593	*pp_pool_item = &p_pool_obj->pool_item;
594
595	/* Calculate the pointer to the user's first component. */
596	*pp_obj = ((uint8_t *) * pp_obj) + sizeof(cl_pool_obj_t);
597
598	/*
599	 * Set the object pointer in the pool item to point to the first of the
600	 * user's components.
601	 */
602	p_pool_obj->p_object = *pp_obj;
603
604	/* Invoke the user's constructor callback. */
605	if (p_pool->pfn_init)
606		status = p_pool->pfn_init(*pp_obj, (void *)p_pool->context);
607
608	return (status);
609}
610
611/*
612 * Callback to translate quick composite to grow pool destructor callback.
613 */
614static void
615__cl_pool_dtor_cb(IN const cl_pool_item_t * const p_pool_item,
616		  IN void *const context)
617{
618	cl_pool_t *p_pool = (cl_pool_t *) context;
619
620	CL_ASSERT(p_pool);
621	CL_ASSERT(p_pool->pfn_dtor);
622	CL_ASSERT(((cl_pool_obj_t *) p_pool_item)->p_object);
623
624	/* Invoke the user's destructor callback. */
625	p_pool->pfn_dtor((void *)((cl_pool_obj_t *) p_pool_item)->p_object,
626			 (void *)p_pool->context);
627}
628
629void cl_pool_construct(IN cl_pool_t * const p_pool)
630{
631	CL_ASSERT(p_pool);
632
633	memset(p_pool, 0, sizeof(cl_pool_t));
634
635	cl_qcpool_construct(&p_pool->qcpool);
636}
637
638cl_status_t
639cl_pool_init(IN cl_pool_t * const p_pool,
640	     IN const size_t min_size,
641	     IN const size_t max_size,
642	     IN const size_t grow_size,
643	     IN const size_t object_size,
644	     IN cl_pfn_pool_init_t pfn_initializer OPTIONAL,
645	     IN cl_pfn_pool_dtor_t pfn_destructor OPTIONAL,
646	     IN const void *const context)
647{
648	cl_status_t status;
649	size_t total_size;
650
651	CL_ASSERT(p_pool);
652
653	/* Add the size of the list item to the first component. */
654	total_size = object_size + sizeof(cl_pool_obj_t);
655
656	/* Store callback function pointers. */
657	p_pool->pfn_init = pfn_initializer;	/* may be NULL */
658	p_pool->pfn_dtor = pfn_destructor;	/* may be NULL */
659	p_pool->context = context;
660
661	/*
662	 * We need an initializer in all cases for quick composite pool, since
663	 * the user pointer must be manipulated to hide the prefixed cl_pool_obj_t.
664	 */
665	status = cl_qcpool_init(&p_pool->qcpool, min_size, max_size, grow_size,
666				&total_size, 1, __cl_pool_init_cb,
667				pfn_destructor ? __cl_pool_dtor_cb : NULL,
668				p_pool);
669
670	return (status);
671}
672