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