1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004-2007 Voltaire, Inc. All rights reserved.
3219820Sjeff * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
4219820Sjeff * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5219820Sjeff *
6219820Sjeff * This software is available to you under a choice of one of two
7219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
8219820Sjeff * General Public License (GPL) Version 2, available from the file
9219820Sjeff * COPYING in the main directory of this source tree, or the
10219820Sjeff * OpenIB.org BSD license below:
11219820Sjeff *
12219820Sjeff *     Redistribution and use in source and binary forms, with or
13219820Sjeff *     without modification, are permitted provided that the following
14219820Sjeff *     conditions are met:
15219820Sjeff *
16219820Sjeff *      - Redistributions of source code must retain the above
17219820Sjeff *        copyright notice, this list of conditions and the following
18219820Sjeff *        disclaimer.
19219820Sjeff *
20219820Sjeff *      - Redistributions in binary form must reproduce the above
21219820Sjeff *        copyright notice, this list of conditions and the following
22219820Sjeff *        disclaimer in the documentation and/or other materials
23219820Sjeff *        provided with the distribution.
24219820Sjeff *
25219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32219820Sjeff * SOFTWARE.
33219820Sjeff *
34219820Sjeff */
35219820Sjeff
36219820Sjeff#if HAVE_CONFIG_H
37219820Sjeff#  include <config.h>
38219820Sjeff#endif				/* HAVE_CONFIG_H */
39219820Sjeff
40219820Sjeff#include <math.h>
41219820Sjeff#include <stdlib.h>
42219820Sjeff#include <complib/cl_event_wheel.h>
43219820Sjeff#include <complib/cl_debug.h>
44219820Sjeff
45219820Sjeff#define CL_DBG(fmt, arg...)
46219820Sjeff
47219820Sjeffstatic cl_status_t
48219820Sjeff__event_will_age_before(IN const cl_list_item_t * const p_list_item,
49219820Sjeff			IN void *context)
50219820Sjeff{
51219820Sjeff	uint64_t aging_time = *((uint64_t *) context);
52219820Sjeff	cl_event_wheel_reg_info_t *p_event;
53219820Sjeff
54219820Sjeff	p_event =
55219820Sjeff	    PARENT_STRUCT(p_list_item, cl_event_wheel_reg_info_t, list_item);
56219820Sjeff
57219820Sjeff	if (p_event->aging_time < aging_time)
58219820Sjeff		return CL_SUCCESS;
59219820Sjeff	else
60219820Sjeff		return CL_NOT_FOUND;
61219820Sjeff}
62219820Sjeff
63219820Sjeffstatic void __cl_event_wheel_callback(IN void *context)
64219820Sjeff{
65219820Sjeff	cl_event_wheel_t *p_event_wheel = (cl_event_wheel_t *) context;
66219820Sjeff	cl_list_item_t *p_list_item, *p_prev_event_list_item;
67219820Sjeff	cl_list_item_t *p_list_next_item;
68219820Sjeff	cl_event_wheel_reg_info_t *p_event;
69219820Sjeff	uint64_t current_time;
70219820Sjeff	uint64_t next_aging_time;
71219820Sjeff	uint32_t new_timeout;
72219820Sjeff	cl_status_t cl_status;
73219820Sjeff
74219820Sjeff	/* might be during closing ...  */
75219820Sjeff	if (p_event_wheel->closing)
76219820Sjeff		return;
77219820Sjeff
78219820Sjeff	current_time = cl_get_time_stamp();
79219820Sjeff
80219820Sjeff	if (NULL != p_event_wheel->p_external_lock)
81219820Sjeff
82219820Sjeff		/* Take care of the order of acquiring locks to avoid the deadlock!
83219820Sjeff		 * The external lock goes first.
84219820Sjeff		 */
85219820Sjeff		cl_spinlock_acquire(p_event_wheel->p_external_lock);
86219820Sjeff
87219820Sjeff	cl_spinlock_acquire(&p_event_wheel->lock);
88219820Sjeff
89219820Sjeff	p_list_item = cl_qlist_head(&p_event_wheel->events_wheel);
90219820Sjeff	if (p_list_item == cl_qlist_end(&p_event_wheel->events_wheel))
91219820Sjeff		/* the list is empty - nothing to do */
92219820Sjeff		goto Exit;
93219820Sjeff
94219820Sjeff	/* we found such an item.  get the p_event */
95219820Sjeff	p_event =
96219820Sjeff	    PARENT_STRUCT(p_list_item, cl_event_wheel_reg_info_t, list_item);
97219820Sjeff
98219820Sjeff	while (p_event->aging_time <= current_time) {
99219820Sjeff		/* this object has aged - invoke it's callback */
100219820Sjeff		if (p_event->pfn_aged_callback)
101219820Sjeff			next_aging_time =
102219820Sjeff			    p_event->pfn_aged_callback(p_event->key,
103219820Sjeff						       p_event->num_regs,
104219820Sjeff						       p_event->context);
105219820Sjeff		else
106219820Sjeff			next_aging_time = 0;
107219820Sjeff
108219820Sjeff		/* point to the next object in the wheel */
109219820Sjeff		p_list_next_item = cl_qlist_next(p_list_item);
110219820Sjeff
111219820Sjeff		/* We need to retire the event if the next aging time passed */
112219820Sjeff		if (next_aging_time < current_time) {
113219820Sjeff			/* remove it from the map */
114219820Sjeff			cl_qmap_remove_item(&p_event_wheel->events_map,
115219820Sjeff					    &(p_event->map_item));
116219820Sjeff
117219820Sjeff			/* pop p_event from the wheel */
118219820Sjeff			cl_qlist_remove_head(&p_event_wheel->events_wheel);
119219820Sjeff
120219820Sjeff			/* delete the event info object - allocated by cl_event_wheel_reg */
121219820Sjeff			free(p_event);
122219820Sjeff		} else {
123219820Sjeff			/* update the required aging time */
124219820Sjeff			p_event->aging_time = next_aging_time;
125219820Sjeff			p_event->num_regs++;
126219820Sjeff
127219820Sjeff			/* do not remove from the map  - but remove from the list head and
128219820Sjeff			   place in the correct position */
129219820Sjeff
130219820Sjeff			/* pop p_event from the wheel */
131219820Sjeff			cl_qlist_remove_head(&p_event_wheel->events_wheel);
132219820Sjeff
133219820Sjeff			/* find the event that ages just before */
134219820Sjeff			p_prev_event_list_item =
135219820Sjeff			    cl_qlist_find_from_tail(&p_event_wheel->
136219820Sjeff						    events_wheel,
137219820Sjeff						    __event_will_age_before,
138219820Sjeff						    &p_event->aging_time);
139219820Sjeff
140219820Sjeff			/* insert just after */
141219820Sjeff			cl_qlist_insert_next(&p_event_wheel->events_wheel,
142219820Sjeff					     p_prev_event_list_item,
143219820Sjeff					     &p_event->list_item);
144219820Sjeff
145219820Sjeff			/* as we have modified the list - restart from first item: */
146219820Sjeff			p_list_next_item =
147219820Sjeff			    cl_qlist_head(&p_event_wheel->events_wheel);
148219820Sjeff		}
149219820Sjeff
150219820Sjeff		/* advance to next event */
151219820Sjeff		p_list_item = p_list_next_item;
152219820Sjeff		if (p_list_item == cl_qlist_end(&p_event_wheel->events_wheel))
153219820Sjeff			/* the list is empty - nothing to do */
154219820Sjeff			break;
155219820Sjeff
156219820Sjeff		/* get the p_event */
157219820Sjeff		p_event =
158219820Sjeff		    PARENT_STRUCT(p_list_item, cl_event_wheel_reg_info_t,
159219820Sjeff				  list_item);
160219820Sjeff	}
161219820Sjeff
162219820Sjeff	/* We need to restart the timer only if the list is not empty now */
163219820Sjeff	if (p_list_item != cl_qlist_end(&p_event_wheel->events_wheel)) {
164219820Sjeff		/* get the p_event */
165219820Sjeff		p_event =
166219820Sjeff		    PARENT_STRUCT(p_list_item, cl_event_wheel_reg_info_t,
167219820Sjeff				  list_item);
168219820Sjeff
169219820Sjeff		/* start the timer to the timeout [msec] */
170219820Sjeff		new_timeout =
171219820Sjeff		    (uint32_t) (((p_event->aging_time - current_time) / 1000) +
172219820Sjeff				0.5);
173219820Sjeff		CL_DBG("__cl_event_wheel_callback: Restart timer in: "
174219820Sjeff		       "%u [msec]\n", new_timeout);
175219820Sjeff		cl_status = cl_timer_start(&p_event_wheel->timer, new_timeout);
176219820Sjeff		if (cl_status != CL_SUCCESS) {
177219820Sjeff			CL_DBG("__cl_event_wheel_callback : ERR 6100: "
178219820Sjeff			       "Failed to start timer\n");
179219820Sjeff		}
180219820Sjeff	}
181219820Sjeff
182219820Sjeff	/* release the lock */
183219820SjeffExit:
184219820Sjeff	cl_spinlock_release(&p_event_wheel->lock);
185219820Sjeff	if (NULL != p_event_wheel->p_external_lock)
186219820Sjeff		cl_spinlock_release(p_event_wheel->p_external_lock);
187219820Sjeff}
188219820Sjeff
189219820Sjeff/*
190219820Sjeff * Construct and Initialize
191219820Sjeff */
192219820Sjeffvoid cl_event_wheel_construct(IN cl_event_wheel_t * const p_event_wheel)
193219820Sjeff{
194219820Sjeff	cl_spinlock_construct(&(p_event_wheel->lock));
195219820Sjeff	cl_timer_construct(&(p_event_wheel->timer));
196219820Sjeff}
197219820Sjeff
198219820Sjeffcl_status_t
199219820Sjeffcl_event_wheel_init(IN cl_event_wheel_t * const p_event_wheel)
200219820Sjeff{
201219820Sjeff	cl_status_t cl_status = CL_SUCCESS;
202219820Sjeff
203219820Sjeff	/* initialize */
204219820Sjeff	p_event_wheel->p_external_lock = NULL;
205219820Sjeff	p_event_wheel->closing = FALSE;
206219820Sjeff	cl_status = cl_spinlock_init(&(p_event_wheel->lock));
207219820Sjeff	if (cl_status != CL_SUCCESS)
208219820Sjeff		return cl_status;
209219820Sjeff	cl_qlist_init(&p_event_wheel->events_wheel);
210219820Sjeff	cl_qmap_init(&p_event_wheel->events_map);
211219820Sjeff
212219820Sjeff	/* init the timer with timeout */
213219820Sjeff	cl_status = cl_timer_init(&p_event_wheel->timer, __cl_event_wheel_callback, p_event_wheel);	/* cb context */
214219820Sjeff
215219820Sjeff	return cl_status;
216219820Sjeff}
217219820Sjeff
218219820Sjeffcl_status_t
219219820Sjeffcl_event_wheel_init_ex(IN cl_event_wheel_t * const p_event_wheel,
220219820Sjeff		       IN cl_spinlock_t * p_external_lock)
221219820Sjeff{
222219820Sjeff	cl_status_t cl_status;
223219820Sjeff
224219820Sjeff	cl_status = cl_event_wheel_init(p_event_wheel);
225219820Sjeff	if (CL_SUCCESS != cl_status)
226219820Sjeff		return cl_status;
227219820Sjeff
228219820Sjeff	p_event_wheel->p_external_lock = p_external_lock;
229219820Sjeff	return cl_status;
230219820Sjeff}
231219820Sjeff
232219820Sjeffvoid cl_event_wheel_dump(IN cl_event_wheel_t * const p_event_wheel)
233219820Sjeff{
234219820Sjeff	cl_list_item_t *p_list_item;
235219820Sjeff	cl_event_wheel_reg_info_t *p_event;
236219820Sjeff
237219820Sjeff	p_list_item = cl_qlist_head(&p_event_wheel->events_wheel);
238219820Sjeff
239219820Sjeff	while (p_list_item != cl_qlist_end(&p_event_wheel->events_wheel)) {
240219820Sjeff		p_event =
241219820Sjeff		    PARENT_STRUCT(p_list_item, cl_event_wheel_reg_info_t,
242219820Sjeff				  list_item);
243219820Sjeff		CL_DBG("cl_event_wheel_dump: Found event key:<0x%"
244219820Sjeff		       PRIx64 ">, aging time:%" PRIu64 "\n",
245219820Sjeff		       p_event->key, p_event->aging_time);
246219820Sjeff		p_list_item = cl_qlist_next(p_list_item);
247219820Sjeff	}
248219820Sjeff}
249219820Sjeff
250219820Sjeffvoid cl_event_wheel_destroy(IN cl_event_wheel_t * const p_event_wheel)
251219820Sjeff{
252219820Sjeff	cl_list_item_t *p_list_item;
253219820Sjeff	cl_map_item_t *p_map_item;
254219820Sjeff	cl_event_wheel_reg_info_t *p_event;
255219820Sjeff
256219820Sjeff	/* we need to get a lock */
257219820Sjeff	cl_spinlock_acquire(&p_event_wheel->lock);
258219820Sjeff
259219820Sjeff	cl_event_wheel_dump(p_event_wheel);
260219820Sjeff
261219820Sjeff	/* go over all the items in the list and remove them */
262219820Sjeff	p_list_item = cl_qlist_remove_head(&p_event_wheel->events_wheel);
263219820Sjeff	while (p_list_item != cl_qlist_end(&p_event_wheel->events_wheel)) {
264219820Sjeff		p_event =
265219820Sjeff		    PARENT_STRUCT(p_list_item, cl_event_wheel_reg_info_t,
266219820Sjeff				  list_item);
267219820Sjeff
268219820Sjeff		CL_DBG("cl_event_wheel_destroy: Found outstanding event"
269219820Sjeff		       " key:<0x%" PRIx64 ">\n", p_event->key);
270219820Sjeff
271219820Sjeff		/* remove it from the map */
272219820Sjeff		p_map_item = &(p_event->map_item);
273219820Sjeff		cl_qmap_remove_item(&p_event_wheel->events_map, p_map_item);
274219820Sjeff		free(p_event);	/* allocated by cl_event_wheel_reg */
275219820Sjeff		p_list_item =
276219820Sjeff		    cl_qlist_remove_head(&p_event_wheel->events_wheel);
277219820Sjeff	}
278219820Sjeff
279219820Sjeff	/* destroy the timer */
280219820Sjeff	cl_timer_destroy(&p_event_wheel->timer);
281219820Sjeff
282219820Sjeff	/* destroy the lock (this should be done without releasing - we don't want
283219820Sjeff	   any other run to grab the lock at this point. */
284219820Sjeff	cl_spinlock_release(&p_event_wheel->lock);
285219820Sjeff	cl_spinlock_destroy(&(p_event_wheel->lock));
286219820Sjeff}
287219820Sjeff
288219820Sjeffcl_status_t
289219820Sjeffcl_event_wheel_reg(IN cl_event_wheel_t * const p_event_wheel,
290219820Sjeff		   IN const uint64_t key,
291219820Sjeff		   IN const uint64_t aging_time_usec,
292219820Sjeff		   IN cl_pfn_event_aged_cb_t pfn_callback,
293219820Sjeff		   IN void *const context)
294219820Sjeff{
295219820Sjeff	cl_event_wheel_reg_info_t *p_event;
296219820Sjeff	uint64_t timeout;
297219820Sjeff	uint32_t to;
298219820Sjeff	cl_status_t cl_status = CL_SUCCESS;
299219820Sjeff	cl_list_item_t *prev_event_list_item;
300219820Sjeff	cl_map_item_t *p_map_item;
301219820Sjeff
302219820Sjeff	/* Get the lock on the manager */
303219820Sjeff	cl_spinlock_acquire(&(p_event_wheel->lock));
304219820Sjeff
305219820Sjeff	cl_event_wheel_dump(p_event_wheel);
306219820Sjeff
307219820Sjeff	/* Make sure such a key does not exists */
308219820Sjeff	p_map_item = cl_qmap_get(&p_event_wheel->events_map, key);
309219820Sjeff	if (p_map_item != cl_qmap_end(&p_event_wheel->events_map)) {
310219820Sjeff		CL_DBG("cl_event_wheel_reg: Already exists key:0x%"
311219820Sjeff		       PRIx64 "\n", key);
312219820Sjeff
313219820Sjeff		/* already there - remove it from the list as it is getting a new time */
314219820Sjeff		p_event =
315219820Sjeff		    PARENT_STRUCT(p_map_item, cl_event_wheel_reg_info_t,
316219820Sjeff				  map_item);
317219820Sjeff
318219820Sjeff		/* remove the item from the qlist */
319219820Sjeff		cl_qlist_remove_item(&p_event_wheel->events_wheel,
320219820Sjeff				     &p_event->list_item);
321219820Sjeff		/* and the qmap */
322219820Sjeff		cl_qmap_remove_item(&p_event_wheel->events_map,
323219820Sjeff				    &p_event->map_item);
324219820Sjeff	} else {
325219820Sjeff		/* make a new one */
326219820Sjeff		p_event = (cl_event_wheel_reg_info_t *)
327219820Sjeff		    malloc(sizeof(cl_event_wheel_reg_info_t));
328219820Sjeff		p_event->num_regs = 0;
329219820Sjeff	}
330219820Sjeff
331219820Sjeff	p_event->key = key;
332219820Sjeff	p_event->aging_time = aging_time_usec;
333219820Sjeff	p_event->pfn_aged_callback = pfn_callback;
334219820Sjeff	p_event->context = context;
335219820Sjeff	p_event->num_regs++;
336219820Sjeff
337219820Sjeff	CL_DBG("cl_event_wheel_reg: Registering event key:0x%" PRIx64
338219820Sjeff	       " aging in %u [msec]\n", p_event->key,
339219820Sjeff	       (uint32_t) ((p_event->aging_time -
340219820Sjeff			    cl_get_time_stamp()) / 1000));
341219820Sjeff
342219820Sjeff	/* If the list is empty - need to start the timer */
343219820Sjeff	if (cl_is_qlist_empty(&p_event_wheel->events_wheel)) {
344219820Sjeff		/* Edward Bortnikov 03/29/2003
345219820Sjeff		 * ++TBD Consider moving the timer manipulation behind the list manipulation.
346219820Sjeff		 */
347219820Sjeff
348219820Sjeff		/* calculate the new timeout */
349219820Sjeff		timeout =
350219820Sjeff		    (p_event->aging_time - cl_get_time_stamp() + 500) / 1000;
351219820Sjeff
352219820Sjeff		/* stop the timer if it is running */
353219820Sjeff
354219820Sjeff		/* Edward Bortnikov 03/29/2003
355219820Sjeff		 * Don't call cl_timer_stop() because it spins forever.
356219820Sjeff		 * cl_timer_start() will invoke cl_timer_stop() by itself.
357219820Sjeff		 *
358219820Sjeff		 * The problematic scenario is when __cl_event_wheel_callback()
359219820Sjeff		 * is in race condition with this code. It sets timer.in_timer_cb
360219820Sjeff		 * to TRUE and then blocks on p_event_wheel->lock. Following this,
361219820Sjeff		 * the call to cl_timer_stop() hangs. Following this, the whole system
362219820Sjeff		 * enters into a deadlock.
363219820Sjeff		 *
364219820Sjeff		 * cl_timer_stop(&p_event_wheel->timer);
365219820Sjeff		 */
366219820Sjeff
367219820Sjeff		/* The timeout for the cl_timer_start should be given as uint32_t.
368219820Sjeff		   if there is an overflow - warn about it. */
369219820Sjeff		to = (uint32_t) timeout;
370219820Sjeff		if (timeout > (uint32_t) timeout) {
371219820Sjeff			to = 0xffffffff;	/* max 32 bit timer */
372219820Sjeff			CL_DBG("cl_event_wheel_reg: timeout requested is "
373219820Sjeff			       "too large. Using timeout: %u\n", to);
374219820Sjeff		}
375219820Sjeff
376219820Sjeff		/* start the timer to the timeout [msec] */
377219820Sjeff		cl_status = cl_timer_start(&p_event_wheel->timer, to);
378219820Sjeff		if (cl_status != CL_SUCCESS) {
379219820Sjeff			CL_DBG("cl_event_wheel_reg : ERR 6103: "
380219820Sjeff			       "Failed to start timer\n");
381219820Sjeff			goto Exit;
382219820Sjeff		}
383219820Sjeff	}
384219820Sjeff
385219820Sjeff	/* insert the object to the qlist and the qmap */
386219820Sjeff
387219820Sjeff	/* BUT WE MUST INSERT IT IN A SORTED MANNER */
388219820Sjeff	prev_event_list_item =
389219820Sjeff	    cl_qlist_find_from_tail(&p_event_wheel->events_wheel,
390219820Sjeff				    __event_will_age_before,
391219820Sjeff				    &p_event->aging_time);
392219820Sjeff
393219820Sjeff	cl_qlist_insert_next(&p_event_wheel->events_wheel,
394219820Sjeff			     prev_event_list_item, &p_event->list_item);
395219820Sjeff
396219820Sjeff	cl_qmap_insert(&p_event_wheel->events_map, key, &(p_event->map_item));
397219820Sjeff
398219820SjeffExit:
399219820Sjeff	cl_spinlock_release(&p_event_wheel->lock);
400219820Sjeff
401219820Sjeff	return cl_status;
402219820Sjeff}
403219820Sjeff
404219820Sjeffvoid
405219820Sjeffcl_event_wheel_unreg(IN cl_event_wheel_t * const p_event_wheel, IN uint64_t key)
406219820Sjeff{
407219820Sjeff	cl_event_wheel_reg_info_t *p_event;
408219820Sjeff	cl_map_item_t *p_map_item;
409219820Sjeff
410219820Sjeff	CL_DBG("cl_event_wheel_unreg: " "Removing key:0x%" PRIx64 "\n", key);
411219820Sjeff
412219820Sjeff	cl_spinlock_acquire(&p_event_wheel->lock);
413219820Sjeff	p_map_item = cl_qmap_get(&p_event_wheel->events_map, key);
414219820Sjeff	if (p_map_item != cl_qmap_end(&p_event_wheel->events_map)) {
415219820Sjeff		/* we found such an item. */
416219820Sjeff		p_event =
417219820Sjeff		    PARENT_STRUCT(p_map_item, cl_event_wheel_reg_info_t,
418219820Sjeff				  map_item);
419219820Sjeff
420219820Sjeff		/* remove the item from the qlist */
421219820Sjeff		cl_qlist_remove_item(&p_event_wheel->events_wheel,
422219820Sjeff				     &(p_event->list_item));
423219820Sjeff		/* remove the item from the qmap */
424219820Sjeff		cl_qmap_remove_item(&p_event_wheel->events_map,
425219820Sjeff				    &(p_event->map_item));
426219820Sjeff
427219820Sjeff		CL_DBG("cl_event_wheel_unreg: Removed key:0x%" PRIx64 "\n",
428219820Sjeff		       key);
429219820Sjeff
430219820Sjeff		/* free the item */
431219820Sjeff		free(p_event);
432219820Sjeff	} else {
433219820Sjeff		CL_DBG("cl_event_wheel_unreg: did not find key:0x%" PRIx64
434219820Sjeff		       "\n", key);
435219820Sjeff	}
436219820Sjeff
437219820Sjeff	cl_spinlock_release(&p_event_wheel->lock);
438219820Sjeff}
439219820Sjeff
440219820Sjeffuint32_t
441219820Sjeffcl_event_wheel_num_regs(IN cl_event_wheel_t * const p_event_wheel,
442219820Sjeff			IN uint64_t key)
443219820Sjeff{
444219820Sjeff
445219820Sjeff	cl_event_wheel_reg_info_t *p_event;
446219820Sjeff	cl_map_item_t *p_map_item;
447219820Sjeff	uint32_t num_regs = 0;
448219820Sjeff
449219820Sjeff	/* try to find the key in the map */
450219820Sjeff	CL_DBG("cl_event_wheel_num_regs: Looking for key:0x%"
451219820Sjeff	       PRIx64 "\n", key);
452219820Sjeff
453219820Sjeff	cl_spinlock_acquire(&p_event_wheel->lock);
454219820Sjeff	p_map_item = cl_qmap_get(&p_event_wheel->events_map, key);
455219820Sjeff	if (p_map_item != cl_qmap_end(&p_event_wheel->events_map)) {
456219820Sjeff		/* ok so we can simply return it's num_regs */
457219820Sjeff		p_event =
458219820Sjeff		    PARENT_STRUCT(p_map_item, cl_event_wheel_reg_info_t,
459219820Sjeff				  map_item);
460219820Sjeff		num_regs = p_event->num_regs;
461219820Sjeff	}
462219820Sjeff
463219820Sjeff	cl_spinlock_release(&p_event_wheel->lock);
464219820Sjeff	return (num_regs);
465219820Sjeff}
466219820Sjeff
467219820Sjeff#ifdef __CL_EVENT_WHEEL_TEST__
468219820Sjeff
469219820Sjeff/* Dump out the complete state of the event wheel */
470219820Sjeffvoid __cl_event_wheel_dump(IN cl_event_wheel_t * const p_event_wheel)
471219820Sjeff{
472219820Sjeff	cl_list_item_t *p_list_item;
473219820Sjeff	cl_map_item_t *p_map_item;
474219820Sjeff	cl_event_wheel_reg_info_t *p_event;
475219820Sjeff
476219820Sjeff	printf("************** Event Wheel Dump ***********************\n");
477219820Sjeff	printf("Event Wheel List has %u items:\n",
478219820Sjeff	       cl_qlist_count(&p_event_wheel->events_wheel));
479219820Sjeff
480219820Sjeff	p_list_item = cl_qlist_head(&p_event_wheel->events_wheel);
481219820Sjeff	while (p_list_item != cl_qlist_end(&p_event_wheel->events_wheel)) {
482219820Sjeff		p_event =
483219820Sjeff		    PARENT_STRUCT(p_list_item, cl_event_wheel_reg_info_t,
484219820Sjeff				  list_item);
485219820Sjeff		printf("Event key:0x%" PRIx64 " Context:%s NumRegs:%u\n",
486219820Sjeff		       p_event->key, (char *)p_event->context,
487219820Sjeff		       p_event->num_regs);
488219820Sjeff
489219820Sjeff		/* next */
490219820Sjeff		p_list_item = cl_qlist_next(p_list_item);
491219820Sjeff	}
492219820Sjeff
493219820Sjeff	printf("Event Map has %u items:\n",
494219820Sjeff	       cl_qmap_count(&p_event_wheel->events_map));
495219820Sjeff
496219820Sjeff	p_map_item = cl_qmap_head(&p_event_wheel->events_map);
497219820Sjeff	while (p_map_item != cl_qmap_end(&p_event_wheel->events_map)) {
498219820Sjeff		p_event =
499219820Sjeff		    PARENT_STRUCT(p_map_item, cl_event_wheel_reg_info_t,
500219820Sjeff				  map_item);
501219820Sjeff		printf("Event key:0x%" PRIx64 " Context:%s NumRegs:%u\n",
502219820Sjeff		       p_event->key, (char *)p_event->context,
503219820Sjeff		       p_event->num_regs);
504219820Sjeff
505219820Sjeff		/* next */
506219820Sjeff		p_map_item = cl_qmap_next(p_map_item);
507219820Sjeff	}
508219820Sjeff
509219820Sjeff}
510219820Sjeff
511219820Sjeff/* The callback for aging event */
512219820Sjeff/* We assume we pass a text context */
513219820Sjeffvoid __test_event_aging(uint64_t key, void *context)
514219820Sjeff{
515219820Sjeff	printf("*****************************************************\n");
516219820Sjeff	printf("Aged key: 0x%" PRIx64 " Context:%s\n", key, (char *)context);
517219820Sjeff}
518219820Sjeff
519219820Sjeffint main()
520219820Sjeff{
521219820Sjeff	cl_event_wheel_t event_wheel;
522219820Sjeff	/*  uint64_t key; */
523219820Sjeff
524219820Sjeff	/* construct */
525219820Sjeff	cl_event_wheel_construct(&event_wheel);
526219820Sjeff
527219820Sjeff	/* init */
528219820Sjeff	cl_event_wheel_init(&event_wheel);
529219820Sjeff
530219820Sjeff	/* Start Playing */
531219820Sjeff	cl_event_wheel_reg(&event_wheel, 1,	/*  key */
532219820Sjeff			   cl_get_time_stamp() + 3000000,	/*  3 sec lifetime */
533219820Sjeff			   __test_event_aging,	/*  cb */
534219820Sjeff			   "The first Aging Event");
535219820Sjeff
536219820Sjeff	cl_event_wheel_reg(&event_wheel, 2,	/*  key */
537219820Sjeff			   cl_get_time_stamp() + 3000000,	/*  3 sec lifetime */
538219820Sjeff			   __test_event_aging,	/*  cb */
539219820Sjeff			   "The Second Aging Event");
540219820Sjeff
541219820Sjeff	cl_event_wheel_reg(&event_wheel, 3,	/*  key */
542219820Sjeff			   cl_get_time_stamp() + 3500000,	/*  3 sec lifetime */
543219820Sjeff			   __test_event_aging,	/*  cb */
544219820Sjeff			   "The Third Aging Event");
545219820Sjeff
546219820Sjeff	__cl_event_wheel_dump(&event_wheel);
547219820Sjeff
548219820Sjeff	sleep(2);
549219820Sjeff	cl_event_wheel_reg(&event_wheel, 2,	/*  key */
550219820Sjeff			   cl_get_time_stamp() + 8000000,	/*  3 sec lifetime */
551219820Sjeff			   __test_event_aging,	/*  cb */
552219820Sjeff			   "The Second Aging Event Moved");
553219820Sjeff
554219820Sjeff	__cl_event_wheel_dump(&event_wheel);
555219820Sjeff
556219820Sjeff	sleep(1);
557219820Sjeff	/* remove the third event */
558219820Sjeff	cl_event_wheel_unreg(&event_wheel, 3);	/*  key */
559219820Sjeff
560219820Sjeff	/* get the number of registrations for the keys */
561219820Sjeff	printf("Event 1 Registered: %u\n",
562219820Sjeff	       cl_event_wheel_num_regs(&event_wheel, 1));
563219820Sjeff	printf("Event 2 Registered: %u\n",
564219820Sjeff	       cl_event_wheel_num_regs(&event_wheel, 2));
565219820Sjeff
566219820Sjeff	sleep(5);
567219820Sjeff	/* destroy */
568219820Sjeff	cl_event_wheel_destroy(&event_wheel);
569219820Sjeff
570219820Sjeff	return (0);
571219820Sjeff}
572219820Sjeff
573219820Sjeff#endif				/* __CL_EVENT_WHEEL_TEST__ */
574