1321936Shselasky/*
2321936Shselasky * Copyright (c) 2004-2006 Voltaire, Inc. All rights reserved.
3321936Shselasky * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
4321936Shselasky * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5321936Shselasky *
6321936Shselasky * This software is available to you under a choice of one of two
7321936Shselasky * licenses.  You may choose to be licensed under the terms of the GNU
8321936Shselasky * General Public License (GPL) Version 2, available from the file
9321936Shselasky * COPYING in the main directory of this source tree, or the
10321936Shselasky * OpenIB.org BSD license below:
11321936Shselasky *
12321936Shselasky *     Redistribution and use in source and binary forms, with or
13321936Shselasky *     without modification, are permitted provided that the following
14321936Shselasky *     conditions are met:
15321936Shselasky *
16321936Shselasky *      - Redistributions of source code must retain the above
17321936Shselasky *        copyright notice, this list of conditions and the following
18321936Shselasky *        disclaimer.
19321936Shselasky *
20321936Shselasky *      - Redistributions in binary form must reproduce the above
21321936Shselasky *        copyright notice, this list of conditions and the following
22321936Shselasky *        disclaimer in the documentation and/or other materials
23321936Shselasky *        provided with the distribution.
24321936Shselasky *
25321936Shselasky * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26321936Shselasky * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27321936Shselasky * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28321936Shselasky * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29321936Shselasky * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30321936Shselasky * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31321936Shselasky * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32321936Shselasky * SOFTWARE.
33321936Shselasky *
34321936Shselasky */
35321936Shselasky
36321936Shselasky/*
37321936Shselasky * Abstract:
38321936Shselasky * Abstraction of Timer create, destroy functions.
39321936Shselasky *
40321936Shselasky */
41321936Shselasky
42321936Shselasky#if HAVE_CONFIG_H
43321936Shselasky#  include <config.h>
44321936Shselasky#endif				/* HAVE_CONFIG_H */
45321936Shselasky
46321936Shselasky#include <stdlib.h>
47321936Shselasky#include <string.h>
48321936Shselasky#include <complib/cl_timer.h>
49321936Shselasky#include <sys/time.h>
50321936Shselasky#include <sys/errno.h>
51321936Shselasky#include <stdio.h>
52321936Shselasky
53321936Shselasky/* Timer provider (emulates timers in user mode). */
54321936Shselaskytypedef struct _cl_timer_prov {
55321936Shselasky	pthread_t thread;
56321936Shselasky	pthread_mutex_t mutex;
57321936Shselasky	pthread_cond_t cond;
58321936Shselasky	cl_qlist_t queue;
59321936Shselasky
60321936Shselasky	boolean_t exit;
61321936Shselasky
62321936Shselasky} cl_timer_prov_t;
63321936Shselasky
64321936Shselasky/* Global timer provider. */
65321936Shselaskystatic cl_timer_prov_t *gp_timer_prov = NULL;
66321936Shselasky
67321936Shselaskystatic void *__cl_timer_prov_cb(IN void *const context);
68321936Shselasky
69321936Shselasky/*
70321936Shselasky * Creates the process global timer provider.  Must be called by the shared
71321936Shselasky * object framework to solve all serialization issues.
72321936Shselasky */
73321936Shselaskycl_status_t __cl_timer_prov_create(void)
74321936Shselasky{
75321936Shselasky	CL_ASSERT(gp_timer_prov == NULL);
76321936Shselasky
77321936Shselasky	gp_timer_prov = malloc(sizeof(cl_timer_prov_t));
78321936Shselasky	if (!gp_timer_prov)
79321936Shselasky		return (CL_INSUFFICIENT_MEMORY);
80321936Shselasky	else
81321936Shselasky		memset(gp_timer_prov, 0, sizeof(cl_timer_prov_t));
82321936Shselasky
83321936Shselasky	cl_qlist_init(&gp_timer_prov->queue);
84321936Shselasky
85321936Shselasky	pthread_mutex_init(&gp_timer_prov->mutex, NULL);
86321936Shselasky	pthread_cond_init(&gp_timer_prov->cond, NULL);
87321936Shselasky
88321936Shselasky	if (pthread_create(&gp_timer_prov->thread, NULL,
89321936Shselasky			   __cl_timer_prov_cb, NULL)) {
90321936Shselasky		__cl_timer_prov_destroy();
91321936Shselasky		return (CL_ERROR);
92321936Shselasky	}
93321936Shselasky
94321936Shselasky	return (CL_SUCCESS);
95321936Shselasky}
96321936Shselasky
97321936Shselaskyvoid __cl_timer_prov_destroy(void)
98321936Shselasky{
99321936Shselasky	pthread_t tid;
100321936Shselasky
101321936Shselasky	if (!gp_timer_prov)
102321936Shselasky		return;
103321936Shselasky
104321936Shselasky	tid = gp_timer_prov->thread;
105321936Shselasky	pthread_mutex_lock(&gp_timer_prov->mutex);
106321936Shselasky	gp_timer_prov->exit = TRUE;
107321936Shselasky	pthread_cond_broadcast(&gp_timer_prov->cond);
108321936Shselasky	pthread_mutex_unlock(&gp_timer_prov->mutex);
109321936Shselasky	pthread_join(tid, NULL);
110321936Shselasky
111321936Shselasky	/* Destroy the mutex and condition variable. */
112321936Shselasky	pthread_mutex_destroy(&gp_timer_prov->mutex);
113321936Shselasky	pthread_cond_destroy(&gp_timer_prov->cond);
114321936Shselasky
115321936Shselasky	/* Free the memory and reset the global pointer. */
116321936Shselasky	free(gp_timer_prov);
117321936Shselasky	gp_timer_prov = NULL;
118321936Shselasky}
119321936Shselasky
120321936Shselasky/*
121321936Shselasky * This is the internal work function executed by the timer's thread.
122321936Shselasky */
123321936Shselaskystatic void *__cl_timer_prov_cb(IN void *const context)
124321936Shselasky{
125321936Shselasky	int ret;
126321936Shselasky	cl_timer_t *p_timer;
127321936Shselasky
128321936Shselasky	pthread_mutex_lock(&gp_timer_prov->mutex);
129321936Shselasky	while (!gp_timer_prov->exit) {
130321936Shselasky		if (cl_is_qlist_empty(&gp_timer_prov->queue)) {
131321936Shselasky			/* Wait until we exit or a timer is queued. */
132321936Shselasky			/* cond wait does:
133321936Shselasky			 * pthread_cond_wait atomically unlocks the mutex (as per
134321936Shselasky			 * pthread_unlock_mutex) and waits for the condition variable
135321936Shselasky			 * cond to be signaled. The thread execution is suspended and
136321936Shselasky			 * does not consume any CPU time until the condition variable is
137321936Shselasky			 * signaled. The mutex must be locked by the calling thread on
138321936Shselasky			 * entrance to pthread_cond_wait. Before RETURNING TO THE
139321936Shselasky			 * CALLING THREAD, PTHREAD_COND_WAIT RE-ACQUIRES MUTEX (as per
140321936Shselasky			 * pthread_lock_mutex).
141321936Shselasky			 */
142321936Shselasky			ret = pthread_cond_wait(&gp_timer_prov->cond,
143321936Shselasky						&gp_timer_prov->mutex);
144321936Shselasky		} else {
145321936Shselasky			/*
146321936Shselasky			 * The timer elements are on the queue in expiration order.
147321936Shselasky			 * Get the first in the list to determine how long to wait.
148321936Shselasky			 */
149321936Shselasky
150321936Shselasky			p_timer =
151321936Shselasky			    (cl_timer_t *) cl_qlist_head(&gp_timer_prov->queue);
152321936Shselasky			ret =
153321936Shselasky			    pthread_cond_timedwait(&gp_timer_prov->cond,
154321936Shselasky						   &gp_timer_prov->mutex,
155321936Shselasky						   &p_timer->timeout);
156321936Shselasky
157321936Shselasky			/*
158321936Shselasky			   Sleep again on every event other than timeout and invalid
159321936Shselasky			   Note: EINVAL means that we got behind. This can occur when
160321936Shselasky			   we are very busy...
161321936Shselasky			 */
162321936Shselasky			if (ret != ETIMEDOUT && ret != EINVAL)
163321936Shselasky				continue;
164321936Shselasky
165321936Shselasky			/*
166321936Shselasky			 * The timer expired.  Check the state in case it was cancelled
167321936Shselasky			 * after it expired but before we got a chance to invoke the
168321936Shselasky			 * callback.
169321936Shselasky			 */
170321936Shselasky			if (p_timer->timer_state != CL_TIMER_QUEUED)
171321936Shselasky				continue;
172321936Shselasky
173321936Shselasky			/*
174321936Shselasky			 * Mark the timer as running to synchronize with its
175321936Shselasky			 * cancelation since we can't hold the mutex during the
176321936Shselasky			 * callback.
177321936Shselasky			 */
178321936Shselasky			p_timer->timer_state = CL_TIMER_RUNNING;
179321936Shselasky
180321936Shselasky			/* Remove the item from the timer queue. */
181321936Shselasky			cl_qlist_remove_item(&gp_timer_prov->queue,
182321936Shselasky					     &p_timer->list_item);
183321936Shselasky			pthread_mutex_unlock(&gp_timer_prov->mutex);
184321936Shselasky			/* Invoke the callback. */
185321936Shselasky			p_timer->pfn_callback((void *)p_timer->context);
186321936Shselasky
187321936Shselasky			/* Acquire the mutex again. */
188321936Shselasky			pthread_mutex_lock(&gp_timer_prov->mutex);
189321936Shselasky			/*
190321936Shselasky			 * Only set the state to idle if the timer has not been accessed
191321936Shselasky			 * from the callback
192321936Shselasky			 */
193321936Shselasky			if (p_timer->timer_state == CL_TIMER_RUNNING)
194321936Shselasky				p_timer->timer_state = CL_TIMER_IDLE;
195321936Shselasky
196321936Shselasky			/*
197321936Shselasky			 * Signal any thread trying to manipulate the timer
198321936Shselasky			 * that expired.
199321936Shselasky			 */
200321936Shselasky			pthread_cond_signal(&p_timer->cond);
201321936Shselasky		}
202321936Shselasky	}
203321936Shselasky	gp_timer_prov->thread = 0;
204321936Shselasky	pthread_mutex_unlock(&gp_timer_prov->mutex);
205321936Shselasky	pthread_exit(NULL);
206321936Shselasky}
207321936Shselasky
208321936Shselasky/* Timer implementation. */
209321936Shselaskyvoid cl_timer_construct(IN cl_timer_t * const p_timer)
210321936Shselasky{
211321936Shselasky	memset(p_timer, 0, sizeof(cl_timer_t));
212321936Shselasky	p_timer->state = CL_UNINITIALIZED;
213321936Shselasky}
214321936Shselasky
215321936Shselaskycl_status_t cl_timer_init(IN cl_timer_t * const p_timer,
216321936Shselasky			  IN cl_pfn_timer_callback_t pfn_callback,
217321936Shselasky			  IN const void *const context)
218321936Shselasky{
219321936Shselasky	CL_ASSERT(p_timer);
220321936Shselasky	CL_ASSERT(pfn_callback);
221321936Shselasky
222321936Shselasky	cl_timer_construct(p_timer);
223321936Shselasky
224321936Shselasky	if (!gp_timer_prov)
225321936Shselasky		return (CL_ERROR);
226321936Shselasky
227321936Shselasky	/* Store timer parameters. */
228321936Shselasky	p_timer->pfn_callback = pfn_callback;
229321936Shselasky	p_timer->context = context;
230321936Shselasky
231321936Shselasky	/* Mark the timer as idle. */
232321936Shselasky	p_timer->timer_state = CL_TIMER_IDLE;
233321936Shselasky
234321936Shselasky	/* Create the condition variable that is used when cancelling a timer. */
235321936Shselasky	pthread_cond_init(&p_timer->cond, NULL);
236321936Shselasky
237321936Shselasky	p_timer->state = CL_INITIALIZED;
238321936Shselasky
239321936Shselasky	return (CL_SUCCESS);
240321936Shselasky}
241321936Shselasky
242321936Shselaskyvoid cl_timer_destroy(IN cl_timer_t * const p_timer)
243321936Shselasky{
244321936Shselasky	CL_ASSERT(p_timer);
245321936Shselasky	CL_ASSERT(cl_is_state_valid(p_timer->state));
246321936Shselasky
247321936Shselasky	if (p_timer->state == CL_INITIALIZED)
248321936Shselasky		cl_timer_stop(p_timer);
249321936Shselasky
250321936Shselasky	p_timer->state = CL_UNINITIALIZED;
251321936Shselasky
252321936Shselasky	/* is it possible we have some threads waiting on the cond now? */
253321936Shselasky	pthread_cond_broadcast(&p_timer->cond);
254321936Shselasky	pthread_cond_destroy(&p_timer->cond);
255321936Shselasky
256321936Shselasky}
257321936Shselasky
258321936Shselasky/*
259321936Shselasky * Return TRUE if timeout value 1 is earlier than timeout value 2.
260321936Shselasky */
261321936Shselaskystatic __inline boolean_t __cl_timer_is_earlier(IN struct timespec *p_timeout1,
262321936Shselasky						IN struct timespec *p_timeout2)
263321936Shselasky{
264321936Shselasky	return ((p_timeout1->tv_sec < p_timeout2->tv_sec) ||
265321936Shselasky		((p_timeout1->tv_sec == p_timeout2->tv_sec) &&
266321936Shselasky		 (p_timeout1->tv_nsec < p_timeout2->tv_nsec)));
267321936Shselasky}
268321936Shselasky
269321936Shselasky/*
270321936Shselasky * Search for a timer with an earlier timeout than the one provided by
271321936Shselasky * the context.  Both the list item and the context are pointers to
272321936Shselasky * a cl_timer_t structure with valid timeouts.
273321936Shselasky */
274321936Shselaskystatic cl_status_t __cl_timer_find(IN const cl_list_item_t * const p_list_item,
275321936Shselasky				   IN void *const context)
276321936Shselasky{
277321936Shselasky	cl_timer_t *p_in_list;
278321936Shselasky	cl_timer_t *p_new;
279321936Shselasky
280321936Shselasky	CL_ASSERT(p_list_item);
281321936Shselasky	CL_ASSERT(context);
282321936Shselasky
283321936Shselasky	p_in_list = (cl_timer_t *) p_list_item;
284321936Shselasky	p_new = (cl_timer_t *) context;
285321936Shselasky
286321936Shselasky	CL_ASSERT(p_in_list->state == CL_INITIALIZED);
287321936Shselasky	CL_ASSERT(p_new->state == CL_INITIALIZED);
288321936Shselasky
289321936Shselasky	CL_ASSERT(p_in_list->timer_state == CL_TIMER_QUEUED);
290321936Shselasky
291321936Shselasky	if (__cl_timer_is_earlier(&p_in_list->timeout, &p_new->timeout))
292321936Shselasky		return (CL_SUCCESS);
293321936Shselasky
294321936Shselasky	return (CL_NOT_FOUND);
295321936Shselasky}
296321936Shselasky
297321936Shselasky/*
298321936Shselasky * Calculate 'struct timespec' value that is the
299321936Shselasky * current time plus the 'time_ms' milliseconds.
300321936Shselasky */
301321936Shselaskystatic __inline void __cl_timer_calculate(IN const uint32_t time_ms,
302321936Shselasky					  OUT struct timespec * const p_timer)
303321936Shselasky{
304321936Shselasky	struct timeval curtime, deltatime, endtime;
305321936Shselasky
306321936Shselasky	gettimeofday(&curtime, NULL);
307321936Shselasky
308321936Shselasky	deltatime.tv_sec = time_ms / 1000;
309321936Shselasky	deltatime.tv_usec = (time_ms % 1000) * 1000;
310321936Shselasky	timeradd(&curtime, &deltatime, &endtime);
311321936Shselasky	p_timer->tv_sec = endtime.tv_sec;
312321936Shselasky	p_timer->tv_nsec = endtime.tv_usec * 1000;
313321936Shselasky}
314321936Shselasky
315321936Shselaskycl_status_t cl_timer_start(IN cl_timer_t * const p_timer,
316321936Shselasky			   IN const uint32_t time_ms)
317321936Shselasky{
318321936Shselasky	cl_list_item_t *p_list_item;
319321936Shselasky
320321936Shselasky	CL_ASSERT(p_timer);
321321936Shselasky	CL_ASSERT(p_timer->state == CL_INITIALIZED);
322321936Shselasky
323321936Shselasky	pthread_mutex_lock(&gp_timer_prov->mutex);
324321936Shselasky	/* Signal the timer provider thread to wake up. */
325321936Shselasky	pthread_cond_signal(&gp_timer_prov->cond);
326321936Shselasky
327321936Shselasky	/* Remove the timer from the queue if currently queued. */
328321936Shselasky	if (p_timer->timer_state == CL_TIMER_QUEUED)
329321936Shselasky		cl_qlist_remove_item(&gp_timer_prov->queue,
330321936Shselasky				     &p_timer->list_item);
331321936Shselasky
332321936Shselasky	__cl_timer_calculate(time_ms, &p_timer->timeout);
333321936Shselasky
334321936Shselasky	/* Add the timer to the queue. */
335321936Shselasky	if (cl_is_qlist_empty(&gp_timer_prov->queue)) {
336321936Shselasky		/* The timer list is empty.  Add to the head. */
337321936Shselasky		cl_qlist_insert_head(&gp_timer_prov->queue,
338321936Shselasky				     &p_timer->list_item);
339321936Shselasky	} else {
340321936Shselasky		/* Find the correct insertion place in the list for the timer. */
341321936Shselasky		p_list_item = cl_qlist_find_from_tail(&gp_timer_prov->queue,
342321936Shselasky						      __cl_timer_find, p_timer);
343321936Shselasky
344321936Shselasky		/* Insert the timer. */
345321936Shselasky		cl_qlist_insert_next(&gp_timer_prov->queue, p_list_item,
346321936Shselasky				     &p_timer->list_item);
347321936Shselasky	}
348321936Shselasky	/* Set the state. */
349321936Shselasky	p_timer->timer_state = CL_TIMER_QUEUED;
350321936Shselasky	pthread_mutex_unlock(&gp_timer_prov->mutex);
351321936Shselasky
352321936Shselasky	return (CL_SUCCESS);
353321936Shselasky}
354321936Shselasky
355321936Shselaskyvoid cl_timer_stop(IN cl_timer_t * const p_timer)
356321936Shselasky{
357321936Shselasky	CL_ASSERT(p_timer);
358321936Shselasky	CL_ASSERT(p_timer->state == CL_INITIALIZED);
359321936Shselasky
360321936Shselasky	pthread_mutex_lock(&gp_timer_prov->mutex);
361321936Shselasky	switch (p_timer->timer_state) {
362321936Shselasky	case CL_TIMER_RUNNING:
363321936Shselasky		/* Wait for the callback to complete. */
364321936Shselasky		pthread_cond_wait(&p_timer->cond, &gp_timer_prov->mutex);
365321936Shselasky		/* Timer could have been queued while we were waiting. */
366321936Shselasky		if (p_timer->timer_state != CL_TIMER_QUEUED)
367321936Shselasky			break;
368321936Shselasky
369321936Shselasky	case CL_TIMER_QUEUED:
370321936Shselasky		/* Change the state of the timer. */
371321936Shselasky		p_timer->timer_state = CL_TIMER_IDLE;
372321936Shselasky		/* Remove the timer from the queue. */
373321936Shselasky		cl_qlist_remove_item(&gp_timer_prov->queue,
374321936Shselasky				     &p_timer->list_item);
375321936Shselasky		/*
376321936Shselasky		 * Signal the timer provider thread to move onto the
377321936Shselasky		 * next timer in the queue.
378321936Shselasky		 */
379321936Shselasky		pthread_cond_signal(&gp_timer_prov->cond);
380321936Shselasky		break;
381321936Shselasky
382321936Shselasky	case CL_TIMER_IDLE:
383321936Shselasky		break;
384321936Shselasky	}
385321936Shselasky	pthread_mutex_unlock(&gp_timer_prov->mutex);
386321936Shselasky}
387321936Shselasky
388321936Shselaskycl_status_t cl_timer_trim(IN cl_timer_t * const p_timer,
389321936Shselasky			  IN const uint32_t time_ms)
390321936Shselasky{
391321936Shselasky	struct timespec newtime;
392321936Shselasky	cl_status_t status;
393321936Shselasky
394321936Shselasky	CL_ASSERT(p_timer);
395321936Shselasky	CL_ASSERT(p_timer->state == CL_INITIALIZED);
396321936Shselasky
397321936Shselasky	pthread_mutex_lock(&gp_timer_prov->mutex);
398321936Shselasky
399321936Shselasky	__cl_timer_calculate(time_ms, &newtime);
400321936Shselasky
401321936Shselasky	if (p_timer->timer_state == CL_TIMER_QUEUED) {
402321936Shselasky		/* If the old time is earlier, do not trim it.  Just return. */
403321936Shselasky		if (__cl_timer_is_earlier(&p_timer->timeout, &newtime)) {
404321936Shselasky			pthread_mutex_unlock(&gp_timer_prov->mutex);
405321936Shselasky			return (CL_SUCCESS);
406321936Shselasky		}
407321936Shselasky	}
408321936Shselasky
409321936Shselasky	/* Reset the timer to the new timeout value. */
410321936Shselasky
411321936Shselasky	pthread_mutex_unlock(&gp_timer_prov->mutex);
412321936Shselasky	status = cl_timer_start(p_timer, time_ms);
413321936Shselasky
414321936Shselasky	return (status);
415321936Shselasky}
416321936Shselasky
417321936Shselaskyuint64_t cl_get_time_stamp(void)
418321936Shselasky{
419321936Shselasky	uint64_t tstamp;
420321936Shselasky	struct timeval tv;
421321936Shselasky
422321936Shselasky	gettimeofday(&tv, NULL);
423321936Shselasky
424321936Shselasky	/* Convert the time of day into a microsecond timestamp. */
425321936Shselasky	tstamp = ((uint64_t) tv.tv_sec * 1000000) + (uint64_t) tv.tv_usec;
426321936Shselasky
427321936Shselasky	return (tstamp);
428321936Shselasky}
429321936Shselasky
430321936Shselaskyuint32_t cl_get_time_stamp_sec(void)
431321936Shselasky{
432321936Shselasky	struct timeval tv;
433321936Shselasky
434321936Shselasky	gettimeofday(&tv, NULL);
435321936Shselasky
436321936Shselasky	return (tv.tv_sec);
437321936Shselasky}
438