1/*
2 * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2002-2005,2009 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 quick list, and list.
39 *
40 */
41
42#if HAVE_CONFIG_H
43#  include <config.h>
44#endif				/* HAVE_CONFIG_H */
45
46#include <complib/cl_qlist.h>
47#include <complib/cl_list.h>
48
49#define FREE_ITEM_GROW_SIZE		10
50
51/******************************************************************************
52 IMPLEMENTATION OF QUICK LIST
53******************************************************************************/
54void cl_qlist_insert_array_head(IN cl_qlist_t * const p_list,
55				IN cl_list_item_t * const p_array,
56				IN uint32_t item_count,
57				IN const uint32_t item_size)
58{
59	cl_list_item_t *p_item;
60
61	CL_ASSERT(p_list);
62	CL_ASSERT(p_list->state == CL_INITIALIZED);
63	CL_ASSERT(p_array);
64	CL_ASSERT(item_size >= sizeof(cl_list_item_t));
65	CL_ASSERT(item_count);
66
67	/*
68	 * To add items from the array to the list in the same order as
69	 * the elements appear in the array, we add them starting with
70	 * the last one first.  Locate the last item.
71	 */
72	p_item = (cl_list_item_t *) ((uint8_t *) p_array +
73				     (item_size * (item_count - 1)));
74
75	/* Continue to add all items to the list. */
76	while (item_count--) {
77		cl_qlist_insert_head(p_list, p_item);
78
79		/* Get the next object to add to the list. */
80		p_item = (cl_list_item_t *) ((uint8_t *) p_item - item_size);
81	}
82}
83
84void cl_qlist_insert_array_tail(IN cl_qlist_t * const p_list,
85				IN cl_list_item_t * const p_array,
86				IN uint32_t item_count,
87				IN const uint32_t item_size)
88{
89	cl_list_item_t *p_item;
90
91	CL_ASSERT(p_list);
92	CL_ASSERT(p_list->state == CL_INITIALIZED);
93	CL_ASSERT(p_array);
94	CL_ASSERT(item_size >= sizeof(cl_list_item_t));
95	CL_ASSERT(item_count);
96
97	/* Set the first item to add to the list. */
98	p_item = p_array;
99
100	/* Continue to add all items to the list. */
101	while (item_count--) {
102		cl_qlist_insert_tail(p_list, p_item);
103
104		/* Get the next object to add to the list. */
105		p_item = (cl_list_item_t *) ((uint8_t *) p_item + item_size);
106	}
107}
108
109void cl_qlist_insert_list_head(IN cl_qlist_t * const p_dest_list,
110			       IN cl_qlist_t * const p_src_list)
111{
112#if defined( _DEBUG_ )
113	cl_list_item_t *p_item;
114#endif
115
116	CL_ASSERT(p_dest_list);
117	CL_ASSERT(p_src_list);
118	CL_ASSERT(p_dest_list->state == CL_INITIALIZED);
119	CL_ASSERT(p_src_list->state == CL_INITIALIZED);
120
121	/*
122	 * Is the src list empty?
123	 * We must have this check here for code below to work.
124	 */
125	if (cl_is_qlist_empty(p_src_list))
126		return;
127
128#if defined( _DEBUG_ )
129	/* Check that all items in the source list belong there. */
130	p_item = cl_qlist_head(p_src_list);
131	while (p_item != cl_qlist_end(p_src_list)) {
132		/* All list items in the source list must point to it. */
133		CL_ASSERT(p_item->p_list == p_src_list);
134		/* Point them all to the destination list. */
135		p_item->p_list = p_dest_list;
136		p_item = cl_qlist_next(p_item);
137	}
138#endif
139
140	/* Chain the destination list to the tail of the source list. */
141	cl_qlist_tail(p_src_list)->p_next = cl_qlist_head(p_dest_list);
142	cl_qlist_head(p_dest_list)->p_prev = cl_qlist_tail(p_src_list);
143
144	/*
145	 * Update the head of the destination list to the head of
146	 * the source list.
147	 */
148	p_dest_list->end.p_next = cl_qlist_head(p_src_list);
149	cl_qlist_head(p_src_list)->p_prev = &p_dest_list->end;
150
151	/*
152	 * Update the count of the destination to reflect the source items having
153	 * been added.
154	 */
155	p_dest_list->count += p_src_list->count;
156
157	/* Update source list to reflect being empty. */
158	__cl_qlist_reset(p_src_list);
159}
160
161void cl_qlist_insert_list_tail(IN cl_qlist_t * const p_dest_list,
162			       IN cl_qlist_t * const p_src_list)
163{
164#if defined( _DEBUG_ )
165	cl_list_item_t *p_item;
166#endif
167
168	CL_ASSERT(p_dest_list);
169	CL_ASSERT(p_src_list);
170	CL_ASSERT(p_dest_list->state == CL_INITIALIZED);
171	CL_ASSERT(p_src_list->state == CL_INITIALIZED);
172
173	/*
174	 * Is the src list empty?
175	 * We must have this check here for code below to work.
176	 */
177	if (cl_is_qlist_empty(p_src_list))
178		return;
179
180#if defined( _DEBUG_ )
181	/* Check that all items in the source list belong there. */
182	p_item = cl_qlist_head(p_src_list);
183	while (p_item != cl_qlist_end(p_src_list)) {
184		/* All list items in the source list must point to it. */
185		CL_ASSERT(p_item->p_list == p_src_list);
186		/* Point them all to the destination list. */
187		p_item->p_list = p_dest_list;
188		p_item = cl_qlist_next(p_item);
189	}
190#endif
191
192	/* Chain the source list to the tail of the destination list. */
193	cl_qlist_tail(p_dest_list)->p_next = cl_qlist_head(p_src_list);
194	cl_qlist_head(p_src_list)->p_prev = cl_qlist_tail(p_dest_list);
195
196	/*
197	 * Update the tail of the destination list to the tail of
198	 * the source list.
199	 */
200	p_dest_list->end.p_prev = cl_qlist_tail(p_src_list);
201	cl_qlist_tail(p_src_list)->p_next = &p_dest_list->end;
202
203	/*
204	 * Update the count of the destination to reflect the source items having
205	 * been added.
206	 */
207	p_dest_list->count += p_src_list->count;
208
209	/* Update source list to reflect being empty. */
210	__cl_qlist_reset(p_src_list);
211}
212
213boolean_t cl_is_item_in_qlist(IN const cl_qlist_t * const p_list,
214			      IN const cl_list_item_t * const p_list_item)
215{
216	const cl_list_item_t *p_temp;
217
218	CL_ASSERT(p_list);
219	CL_ASSERT(p_list_item);
220	CL_ASSERT(p_list->state == CL_INITIALIZED);
221
222	/* Traverse looking for a match */
223	p_temp = cl_qlist_head(p_list);
224	while (p_temp != cl_qlist_end(p_list)) {
225		if (p_temp == p_list_item) {
226			CL_ASSERT(p_list_item->p_list == p_list);
227			return (TRUE);
228		}
229
230		p_temp = cl_qlist_next(p_temp);
231	}
232
233	return (FALSE);
234}
235
236cl_list_item_t *cl_qlist_find_next(IN const cl_qlist_t * const p_list,
237				   IN const cl_list_item_t * const p_list_item,
238				   IN cl_pfn_qlist_find_t pfn_func,
239				   IN const void *const context)
240{
241	cl_list_item_t *p_found_item;
242
243	CL_ASSERT(p_list);
244	CL_ASSERT(p_list->state == CL_INITIALIZED);
245	CL_ASSERT(p_list_item);
246	CL_ASSERT(p_list_item->p_list == p_list);
247	CL_ASSERT(pfn_func);
248
249	p_found_item = cl_qlist_next(p_list_item);
250
251	/* The user provided a compare function */
252	while (p_found_item != cl_qlist_end(p_list)) {
253		CL_ASSERT(p_found_item->p_list == p_list);
254
255		if (pfn_func(p_found_item, (void *)context) == CL_SUCCESS)
256			break;
257
258		p_found_item = cl_qlist_next(p_found_item);
259	}
260
261	/* No match */
262	return (p_found_item);
263}
264
265cl_list_item_t *cl_qlist_find_prev(IN const cl_qlist_t * const p_list,
266				   IN const cl_list_item_t * const p_list_item,
267				   IN cl_pfn_qlist_find_t pfn_func,
268				   IN const void *const context)
269{
270	cl_list_item_t *p_found_item;
271
272	CL_ASSERT(p_list);
273	CL_ASSERT(p_list->state == CL_INITIALIZED);
274	CL_ASSERT(p_list_item);
275	CL_ASSERT(p_list_item->p_list == p_list);
276	CL_ASSERT(pfn_func);
277
278	p_found_item = cl_qlist_prev(p_list_item);
279
280	/* The user provided a compare function */
281	while (p_found_item != cl_qlist_end(p_list)) {
282		CL_ASSERT(p_found_item->p_list == p_list);
283
284		if (pfn_func(p_found_item, (void *)context) == CL_SUCCESS)
285			break;
286
287		p_found_item = cl_qlist_prev(p_found_item);
288	}
289
290	/* No match */
291	return (p_found_item);
292}
293
294void cl_qlist_apply_func(IN const cl_qlist_t * const p_list,
295			 IN cl_pfn_qlist_apply_t pfn_func,
296			 IN const void *const context)
297{
298	cl_list_item_t *p_list_item;
299
300	/* Note that context can have any arbitrary value. */
301	CL_ASSERT(p_list);
302	CL_ASSERT(p_list->state == CL_INITIALIZED);
303	CL_ASSERT(pfn_func);
304
305	p_list_item = cl_qlist_head(p_list);
306	while (p_list_item != cl_qlist_end(p_list)) {
307		pfn_func(p_list_item, (void *)context);
308		p_list_item = cl_qlist_next(p_list_item);
309	}
310}
311
312void cl_qlist_move_items(IN cl_qlist_t * const p_src_list,
313			 IN cl_qlist_t * const p_dest_list,
314			 IN cl_pfn_qlist_find_t pfn_func,
315			 IN const void *const context)
316{
317	cl_list_item_t *p_current_item, *p_next;
318
319	CL_ASSERT(p_src_list);
320	CL_ASSERT(p_dest_list);
321	CL_ASSERT(p_src_list->state == CL_INITIALIZED);
322	CL_ASSERT(p_dest_list->state == CL_INITIALIZED);
323	CL_ASSERT(pfn_func);
324
325	p_current_item = cl_qlist_head(p_src_list);
326
327	while (p_current_item != cl_qlist_end(p_src_list)) {
328		/* Before we do anything, get a pointer to the next item. */
329		p_next = cl_qlist_next(p_current_item);
330
331		if (pfn_func(p_current_item, (void *)context) == CL_SUCCESS) {
332			/* Move the item from one list to the other. */
333			cl_qlist_remove_item(p_src_list, p_current_item);
334			cl_qlist_insert_tail(p_dest_list, p_current_item);
335		}
336		p_current_item = p_next;
337	}
338}
339
340/******************************************************************************
341 IMPLEMENTATION OF LIST
342******************************************************************************/
343void cl_list_construct(IN cl_list_t * const p_list)
344{
345	CL_ASSERT(p_list);
346
347	cl_qpool_construct(&p_list->list_item_pool);
348}
349
350cl_status_t cl_list_init(IN cl_list_t * const p_list, IN const size_t min_items)
351{
352	uint32_t grow_size;
353
354	CL_ASSERT(p_list);
355	cl_qlist_init(&p_list->list);
356
357	/*
358	 * We will grow by min_items/8 items at a time, with a minimum of
359	 * FREE_ITEM_GROW_SIZE.
360	 */
361	grow_size = (uint32_t) min_items >> 3;
362	if (grow_size < FREE_ITEM_GROW_SIZE)
363		grow_size = FREE_ITEM_GROW_SIZE;
364
365	/* Initialize the pool of list items. */
366	return (cl_qpool_init(&p_list->list_item_pool, min_items, 0, grow_size,
367			      sizeof(cl_pool_obj_t), NULL, NULL, NULL));
368}
369
370void cl_list_destroy(IN cl_list_t * const p_list)
371{
372	CL_ASSERT(p_list);
373
374	cl_qpool_destroy(&p_list->list_item_pool);
375}
376
377static cl_status_t cl_list_find_cb(IN const cl_list_item_t * const p_list_item,
378				   IN void *const context)
379{
380	CL_ASSERT(p_list_item);
381
382	if (cl_list_obj(p_list_item) == context)
383		return (CL_SUCCESS);
384
385	return (CL_NOT_FOUND);
386}
387
388cl_status_t cl_list_remove_object(IN cl_list_t * const p_list,
389				  IN const void *const p_object)
390{
391	cl_list_item_t *p_list_item;
392
393	CL_ASSERT(p_list);
394	CL_ASSERT(cl_is_qpool_inited(&p_list->list_item_pool));
395
396	/* find the item in question */
397	p_list_item =
398	    cl_qlist_find_from_head(&p_list->list, cl_list_find_cb, p_object);
399	if (p_list_item != cl_qlist_end(&p_list->list)) {
400		/* remove this item */
401		cl_qlist_remove_item(&p_list->list, p_list_item);
402		cl_qpool_put(&p_list->list_item_pool,
403			     (cl_pool_item_t *) p_list_item);
404		return (CL_SUCCESS);
405	}
406	return (CL_NOT_FOUND);
407}
408
409boolean_t cl_is_object_in_list(IN const cl_list_t * const p_list,
410			       IN const void *const p_object)
411{
412	CL_ASSERT(p_list);
413	CL_ASSERT(cl_is_qpool_inited(&p_list->list_item_pool));
414
415	return (cl_qlist_find_from_head
416		(&p_list->list, cl_list_find_cb, p_object)
417		!= cl_qlist_end(&p_list->list));
418}
419
420cl_status_t cl_list_insert_array_head(IN cl_list_t * const p_list,
421				      IN const void *const p_array,
422				      IN uint32_t item_count,
423				      IN const uint32_t item_size)
424{
425	cl_status_t status;
426	void *p_object;
427	uint32_t items_remain = item_count;
428
429	CL_ASSERT(p_list);
430	CL_ASSERT(cl_is_qpool_inited(&p_list->list_item_pool));
431	CL_ASSERT(p_array);
432	CL_ASSERT(item_size);
433	CL_ASSERT(item_count);
434
435	/*
436	 * To add items from the array to the list in the same order as
437	 * the elements appear in the array, we add them starting with
438	 * the last one first.  Locate the last item.
439	 */
440	p_object = ((uint8_t *) p_array + (item_size * (item_count - 1)));
441
442	/* Continue to add all items to the list. */
443	while (items_remain--) {
444		status = cl_list_insert_head(p_list, p_object);
445		if (status != CL_SUCCESS) {
446			/* Remove all items that have been inserted. */
447			while (items_remain++ < (item_count - 1))
448				cl_list_remove_head(p_list);
449			return (status);
450		}
451
452		/* Get the next object to add to the list. */
453		p_object = ((uint8_t *) p_object - item_size);
454	}
455
456	return (CL_SUCCESS);
457}
458
459cl_status_t cl_list_insert_array_tail(IN cl_list_t * const p_list,
460				      IN const void *const p_array,
461				      IN uint32_t item_count,
462				      IN const uint32_t item_size)
463{
464	cl_status_t status;
465	void *p_object;
466	uint32_t items_remain = item_count;
467
468	CL_ASSERT(p_list);
469	CL_ASSERT(cl_is_qpool_inited(&p_list->list_item_pool));
470	CL_ASSERT(p_array);
471	CL_ASSERT(item_size);
472	CL_ASSERT(item_count);
473
474	/* Set the first item to add to the list. */
475	p_object = (void *)p_array;
476
477	/* Continue to add all items to the list. */
478	while (items_remain--) {
479		status = cl_list_insert_tail(p_list, p_object);
480		if (status != CL_SUCCESS) {
481			/* Remove all items that have been inserted. */
482			while (items_remain++ < (item_count - 1))
483				cl_list_remove_tail(p_list);
484			return (status);
485		}
486
487		/* Get the next object to add to the list. */
488		p_object = ((uint8_t *) p_object + item_size);
489	}
490
491	return (CL_SUCCESS);
492}
493
494cl_list_iterator_t cl_list_find_from_head(IN const cl_list_t * const p_list,
495					  IN cl_pfn_list_find_t pfn_func,
496					  IN const void *const context)
497{
498	cl_status_t status;
499	cl_list_iterator_t itor;
500
501	/* Note that context can have any arbitrary value. */
502	CL_ASSERT(p_list);
503	CL_ASSERT(cl_is_qpool_inited(&p_list->list_item_pool));
504	CL_ASSERT(pfn_func);
505
506	itor = cl_list_head(p_list);
507
508	while (itor != cl_list_end(p_list)) {
509		status = pfn_func(cl_list_obj(itor), (void *)context);
510		if (status == CL_SUCCESS)
511			break;
512
513		itor = cl_list_next(itor);
514	}
515
516	/* no match */
517	return (itor);
518}
519
520cl_list_iterator_t cl_list_find_from_tail(IN const cl_list_t * const p_list,
521					  IN cl_pfn_list_find_t pfn_func,
522					  IN const void *const context)
523{
524	cl_status_t status;
525	cl_list_iterator_t itor;
526
527	/* Note that context can have any arbitrary value. */
528	CL_ASSERT(p_list);
529	CL_ASSERT(cl_is_qpool_inited(&p_list->list_item_pool));
530	CL_ASSERT(pfn_func);
531
532	itor = cl_list_tail(p_list);
533
534	while (itor != cl_list_end(p_list)) {
535		status = pfn_func(cl_list_obj(itor), (void *)context);
536		if (status == CL_SUCCESS)
537			break;
538
539		itor = cl_list_prev(itor);
540	}
541
542	/* no match */
543	return (itor);
544}
545
546void cl_list_apply_func(IN const cl_list_t * const p_list,
547			IN cl_pfn_list_apply_t pfn_func,
548			IN const void *const context)
549{
550	cl_list_iterator_t itor;
551
552	/* Note that context can have any arbitrary value. */
553	CL_ASSERT(p_list);
554	CL_ASSERT(cl_is_qpool_inited(&p_list->list_item_pool));
555	CL_ASSERT(pfn_func);
556
557	itor = cl_list_head(p_list);
558
559	while (itor != cl_list_end(p_list)) {
560		pfn_func(cl_list_obj(itor), (void *)context);
561
562		itor = cl_list_next(itor);
563	}
564}
565