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