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 * Abstraction of Timer create, destroy functions.
39 *
40 */
41
42#if HAVE_CONFIG_H
43#  include <config.h>
44#endif				/* HAVE_CONFIG_H */
45
46#include <stdlib.h>
47#include <string.h>
48#include <complib/cl_timer.h>
49#include <sys/time.h>
50#include <sys/errno.h>
51#include <stdio.h>
52
53/* Timer provider (emulates timers in user mode). */
54typedef struct _cl_timer_prov {
55	pthread_t thread;
56	pthread_mutex_t mutex;
57	pthread_cond_t cond;
58	cl_qlist_t queue;
59
60	boolean_t exit;
61
62} cl_timer_prov_t;
63
64/* Global timer provider. */
65static cl_timer_prov_t *gp_timer_prov = NULL;
66
67static void *__cl_timer_prov_cb(IN void *const context);
68
69/*
70 * Creates the process global timer provider.  Must be called by the shared
71 * object framework to solve all serialization issues.
72 */
73cl_status_t __cl_timer_prov_create(void)
74{
75	CL_ASSERT(gp_timer_prov == NULL);
76
77	gp_timer_prov = malloc(sizeof(cl_timer_prov_t));
78	if (!gp_timer_prov)
79		return (CL_INSUFFICIENT_MEMORY);
80	else
81		memset(gp_timer_prov, 0, sizeof(cl_timer_prov_t));
82
83	cl_qlist_init(&gp_timer_prov->queue);
84
85	pthread_mutex_init(&gp_timer_prov->mutex, NULL);
86	pthread_cond_init(&gp_timer_prov->cond, NULL);
87
88	if (pthread_create(&gp_timer_prov->thread, NULL,
89			   __cl_timer_prov_cb, NULL)) {
90		__cl_timer_prov_destroy();
91		return (CL_ERROR);
92	}
93
94	return (CL_SUCCESS);
95}
96
97void __cl_timer_prov_destroy(void)
98{
99	pthread_t tid;
100
101	if (!gp_timer_prov)
102		return;
103
104	tid = gp_timer_prov->thread;
105	pthread_mutex_lock(&gp_timer_prov->mutex);
106	gp_timer_prov->exit = TRUE;
107	pthread_cond_broadcast(&gp_timer_prov->cond);
108	pthread_mutex_unlock(&gp_timer_prov->mutex);
109	pthread_join(tid, NULL);
110
111	/* Destroy the mutex and condition variable. */
112	pthread_mutex_destroy(&gp_timer_prov->mutex);
113	pthread_cond_destroy(&gp_timer_prov->cond);
114
115	/* Free the memory and reset the global pointer. */
116	free(gp_timer_prov);
117	gp_timer_prov = NULL;
118}
119
120/*
121 * This is the internal work function executed by the timer's thread.
122 */
123static void *__cl_timer_prov_cb(IN void *const context)
124{
125	int ret;
126	cl_timer_t *p_timer;
127
128	pthread_mutex_lock(&gp_timer_prov->mutex);
129	while (!gp_timer_prov->exit) {
130		if (cl_is_qlist_empty(&gp_timer_prov->queue)) {
131			/* Wait until we exit or a timer is queued. */
132			/* cond wait does:
133			 * pthread_cond_wait atomically unlocks the mutex (as per
134			 * pthread_unlock_mutex) and waits for the condition variable
135			 * cond to be signaled. The thread execution is suspended and
136			 * does not consume any CPU time until the condition variable is
137			 * signaled. The mutex must be locked by the calling thread on
138			 * entrance to pthread_cond_wait. Before RETURNING TO THE
139			 * CALLING THREAD, PTHREAD_COND_WAIT RE-ACQUIRES MUTEX (as per
140			 * pthread_lock_mutex).
141			 */
142			ret = pthread_cond_wait(&gp_timer_prov->cond,
143						&gp_timer_prov->mutex);
144		} else {
145			/*
146			 * The timer elements are on the queue in expiration order.
147			 * Get the first in the list to determine how long to wait.
148			 */
149
150			p_timer =
151			    (cl_timer_t *) cl_qlist_head(&gp_timer_prov->queue);
152			ret =
153			    pthread_cond_timedwait(&gp_timer_prov->cond,
154						   &gp_timer_prov->mutex,
155						   &p_timer->timeout);
156
157			/*
158			   Sleep again on every event other than timeout and invalid
159			   Note: EINVAL means that we got behind. This can occur when
160			   we are very busy...
161			 */
162			if (ret != ETIMEDOUT && ret != EINVAL)
163				continue;
164
165			/*
166			 * The timer expired.  Check the state in case it was cancelled
167			 * after it expired but before we got a chance to invoke the
168			 * callback.
169			 */
170			if (p_timer->timer_state != CL_TIMER_QUEUED)
171				continue;
172
173			/*
174			 * Mark the timer as running to synchronize with its
175			 * cancelation since we can't hold the mutex during the
176			 * callback.
177			 */
178			p_timer->timer_state = CL_TIMER_RUNNING;
179
180			/* Remove the item from the timer queue. */
181			cl_qlist_remove_item(&gp_timer_prov->queue,
182					     &p_timer->list_item);
183			pthread_mutex_unlock(&gp_timer_prov->mutex);
184			/* Invoke the callback. */
185			p_timer->pfn_callback((void *)p_timer->context);
186
187			/* Acquire the mutex again. */
188			pthread_mutex_lock(&gp_timer_prov->mutex);
189			/*
190			 * Only set the state to idle if the timer has not been accessed
191			 * from the callback
192			 */
193			if (p_timer->timer_state == CL_TIMER_RUNNING)
194				p_timer->timer_state = CL_TIMER_IDLE;
195
196			/*
197			 * Signal any thread trying to manipulate the timer
198			 * that expired.
199			 */
200			pthread_cond_signal(&p_timer->cond);
201		}
202	}
203	gp_timer_prov->thread = 0;
204	pthread_mutex_unlock(&gp_timer_prov->mutex);
205	pthread_exit(NULL);
206}
207
208/* Timer implementation. */
209void cl_timer_construct(IN cl_timer_t * const p_timer)
210{
211	memset(p_timer, 0, sizeof(cl_timer_t));
212	p_timer->state = CL_UNINITIALIZED;
213}
214
215cl_status_t
216cl_timer_init(IN cl_timer_t * const p_timer,
217	      IN cl_pfn_timer_callback_t pfn_callback,
218	      IN const void *const context)
219{
220	CL_ASSERT(p_timer);
221	CL_ASSERT(pfn_callback);
222
223	cl_timer_construct(p_timer);
224
225	if (!gp_timer_prov)
226		return (CL_ERROR);
227
228	/* Store timer parameters. */
229	p_timer->pfn_callback = pfn_callback;
230	p_timer->context = context;
231
232	/* Mark the timer as idle. */
233	p_timer->timer_state = CL_TIMER_IDLE;
234
235	/* Create the condition variable that is used when cancelling a timer. */
236	pthread_cond_init(&p_timer->cond, NULL);
237
238	p_timer->state = CL_INITIALIZED;
239
240	return (CL_SUCCESS);
241}
242
243void cl_timer_destroy(IN cl_timer_t * const p_timer)
244{
245	CL_ASSERT(p_timer);
246	CL_ASSERT(cl_is_state_valid(p_timer->state));
247
248	if (p_timer->state == CL_INITIALIZED)
249		cl_timer_stop(p_timer);
250
251	p_timer->state = CL_UNINITIALIZED;
252
253	/* is it possible we have some threads waiting on the cond now? */
254	pthread_cond_broadcast(&p_timer->cond);
255	pthread_cond_destroy(&p_timer->cond);
256
257}
258
259/*
260 * Return TRUE if timeout value 1 is earlier than timeout value 2.
261 */
262static __inline boolean_t
263__cl_timer_is_earlier(IN struct timespec *p_timeout1,
264		      IN struct timespec *p_timeout2)
265{
266	return ((p_timeout1->tv_sec < p_timeout2->tv_sec) ||
267		((p_timeout1->tv_sec == p_timeout2->tv_sec) &&
268		 (p_timeout1->tv_nsec < p_timeout2->tv_nsec)));
269}
270
271/*
272 * Search for a timer with an earlier timeout than the one provided by
273 * the context.  Both the list item and the context are pointers to
274 * a cl_timer_t structure with valid timeouts.
275 */
276static cl_status_t
277__cl_timer_find(IN const cl_list_item_t * const p_list_item,
278		IN void *const context)
279{
280	cl_timer_t *p_in_list;
281	cl_timer_t *p_new;
282
283	CL_ASSERT(p_list_item);
284	CL_ASSERT(context);
285
286	p_in_list = (cl_timer_t *) p_list_item;
287	p_new = (cl_timer_t *) context;
288
289	CL_ASSERT(p_in_list->state == CL_INITIALIZED);
290	CL_ASSERT(p_new->state == CL_INITIALIZED);
291
292	CL_ASSERT(p_in_list->timer_state == CL_TIMER_QUEUED);
293
294	if (__cl_timer_is_earlier(&p_in_list->timeout, &p_new->timeout))
295		return (CL_SUCCESS);
296
297	return (CL_NOT_FOUND);
298}
299
300cl_status_t
301cl_timer_start(IN cl_timer_t * const p_timer, IN const uint32_t time_ms)
302{
303	struct timeval curtime;
304	cl_list_item_t *p_list_item;
305	uint32_t delta_time = time_ms;
306
307	CL_ASSERT(p_timer);
308	CL_ASSERT(p_timer->state == CL_INITIALIZED);
309
310	pthread_mutex_lock(&gp_timer_prov->mutex);
311	/* Signal the timer provider thread to wake up. */
312	pthread_cond_signal(&gp_timer_prov->cond);
313
314	/* Remove the timer from the queue if currently queued. */
315	if (p_timer->timer_state == CL_TIMER_QUEUED)
316		cl_qlist_remove_item(&gp_timer_prov->queue,
317				     &p_timer->list_item);
318
319	/* Get the current time */
320#ifndef timerclear
321#define	timerclear(tvp)		(tvp)->tv_sec = (time_t)0, (tvp)->tv_usec = 0L
322#endif
323	timerclear(&curtime);
324	gettimeofday(&curtime, NULL);
325
326	/* do not do 0 wait ! */
327	/* if (delta_time < 1000.0) {delta_time = 1000;} */
328
329	/* Calculate the timeout. */
330	p_timer->timeout.tv_sec = curtime.tv_sec + (delta_time / 1000);
331	p_timer->timeout.tv_nsec =
332	    (curtime.tv_usec + ((delta_time % 1000) * 1000)) * 1000;
333
334	/* Add the timer to the queue. */
335	if (cl_is_qlist_empty(&gp_timer_prov->queue)) {
336		/* The timer list is empty.  Add to the head. */
337		cl_qlist_insert_head(&gp_timer_prov->queue,
338				     &p_timer->list_item);
339	} else {
340		/* Find the correct insertion place in the list for the timer. */
341		p_list_item = cl_qlist_find_from_tail(&gp_timer_prov->queue,
342						      __cl_timer_find, p_timer);
343
344		/* Insert the timer. */
345		cl_qlist_insert_next(&gp_timer_prov->queue, p_list_item,
346				     &p_timer->list_item);
347	}
348	/* Set the state. */
349	p_timer->timer_state = CL_TIMER_QUEUED;
350	pthread_mutex_unlock(&gp_timer_prov->mutex);
351
352	return (CL_SUCCESS);
353}
354
355void cl_timer_stop(IN cl_timer_t * const p_timer)
356{
357	CL_ASSERT(p_timer);
358	CL_ASSERT(p_timer->state == CL_INITIALIZED);
359
360	pthread_mutex_lock(&gp_timer_prov->mutex);
361	switch (p_timer->timer_state) {
362	case CL_TIMER_RUNNING:
363		/* Wait for the callback to complete. */
364		pthread_cond_wait(&p_timer->cond, &gp_timer_prov->mutex);
365		/* Timer could have been queued while we were waiting. */
366		if (p_timer->timer_state != CL_TIMER_QUEUED)
367			break;
368
369	case CL_TIMER_QUEUED:
370		/* Change the state of the timer. */
371		p_timer->timer_state = CL_TIMER_IDLE;
372		/* Remove the timer from the queue. */
373		cl_qlist_remove_item(&gp_timer_prov->queue,
374				     &p_timer->list_item);
375		/*
376		 * Signal the timer provider thread to move onto the
377		 * next timer in the queue.
378		 */
379		pthread_cond_signal(&gp_timer_prov->cond);
380		break;
381
382	case CL_TIMER_IDLE:
383		break;
384	}
385	pthread_mutex_unlock(&gp_timer_prov->mutex);
386}
387
388cl_status_t
389cl_timer_trim(IN cl_timer_t * const p_timer, IN const uint32_t time_ms)
390{
391	struct timeval curtime;
392	struct timespec newtime;
393	cl_status_t status;
394
395	CL_ASSERT(p_timer);
396	CL_ASSERT(p_timer->state == CL_INITIALIZED);
397
398	pthread_mutex_lock(&gp_timer_prov->mutex);
399
400	/* Get the current time */
401	timerclear(&curtime);
402	gettimeofday(&curtime, NULL);
403
404	/* Calculate the timeout. */
405	newtime.tv_sec = curtime.tv_sec + (time_ms / 1000);
406	newtime.tv_nsec = (curtime.tv_usec + ((time_ms % 1000) * 1000)) * 1000;
407
408	if (p_timer->timer_state == CL_TIMER_QUEUED) {
409		/* If the old time is earlier, do not trim it.  Just return. */
410		if (__cl_timer_is_earlier(&p_timer->timeout, &newtime)) {
411			pthread_mutex_unlock(&gp_timer_prov->mutex);
412			return (CL_SUCCESS);
413		}
414	}
415
416	/* Reset the timer to the new timeout value. */
417
418	pthread_mutex_unlock(&gp_timer_prov->mutex);
419	status = cl_timer_start(p_timer, time_ms);
420
421	return (status);
422}
423
424uint64_t cl_get_time_stamp(void)
425{
426	uint64_t tstamp;
427	struct timeval tv;
428
429	timerclear(&tv);
430	gettimeofday(&tv, NULL);
431
432	/* Convert the time of day into a microsecond timestamp. */
433	tstamp = ((uint64_t) tv.tv_sec * 1000000) + (uint64_t) tv.tv_usec;
434
435	return (tstamp);
436}
437
438uint32_t cl_get_time_stamp_sec(void)
439{
440	struct timeval tv;
441
442	timerclear(&tv);
443	gettimeofday(&tv, NULL);
444
445	return (tv.tv_sec);
446}
447