Logo Search packages:      
Sourcecode: fcoe-utils version File versions  Download package

sa_timer.c

/*
 * Copyright(c) 2009 Intel Corporation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Maintained at www.Open-FCoE.org
 */

#include "fcoemon_utils.h"
#include "net_types.h"
#include "fc_types.h"

#define SA_TIMER_HZ     (1000 * 1000 * 1000ULL) /* nanoseconds per second */
#define SA_TIMER_FUZZ   (500 * 1000ULL)   /* 500 microseconds is close enough */

static struct sa_timer *sa_timer_head;    /* queue of scheduled events */
static u_int64_t sa_timer_nsec;           /* nanoseconds since start */

/*
 * Initialize a timer structure.  Set handler.
 */
void
sa_timer_init(struct sa_timer *tm, void (*handler)(void *), void *arg)
{
      ASSERT(handler != NULL);
      memset(tm, 0, sizeof(*tm));
      tm->tm_handler = handler;
      tm->tm_handler_arg = arg;
}

/*
 * Allocate a timer structure.  Set handler.
 */
struct sa_timer *
sa_timer_alloc(void (*handler)(void *arg), void *arg)
{
      struct sa_timer *tm;

      tm = malloc(sizeof(*tm));
      if (tm)
            sa_timer_init(tm, handler, arg);
      return tm;
}

u_int64_t
sa_timer_get(void)
{
      u_int64_t nsec;
#ifndef _POSIX_TIMERS
      struct timeval tv;

      gettimeofday(&tv, NULL);      /* XXX want monotonic time, not TOD */
      nsec = tv.tv_sec * SA_TIMER_HZ + tv.tv_usec * 1000;
#else /* _POSIX_TIMERS */
      struct timespec ts;
      int rc;

      rc = clock_gettime(CLOCK_MONOTONIC, &ts);
      ASSERT_NOTIMPL(rc == 0);
      nsec = ts.tv_sec * SA_TIMER_HZ + ts.tv_nsec;
#endif /* _POSIX_TIMERS */

#if 0 /* XXX */
      ASSERT(nsec >= sa_timer_nsec);      /* really must be monotonic */
#else
      if (nsec < sa_timer_nsec)
            sa_log("sa_timer_get: negative time lapse "
                  "old %qud new %qud diff %qd nsec\n",
                  (long long unsigned int) sa_timer_nsec,
                  (long long unsigned int) nsec,
                  (long long int) (nsec - sa_timer_nsec));
#endif
      sa_timer_nsec = nsec;
      return nsec;
}

/*
 * Get monotonic time since some arbitrary time in the past.
 * If _POSIX_MONOTONIC_CLOCK isn't available, we'll use time of day.
 */
u_int
sa_timer_get_secs(void)
{
      u_int sec;

#ifndef _POSIX_TIMERS
      struct timeval tv;

      gettimeofday(&tv, NULL); /* XXX want monotonic time, not TOD */
      sec = tv.tv_sec;
#else /* _POSIX_TIMERS */
      struct timespec ts;
      int rc;

      rc = clock_gettime(CLOCK_MONOTONIC, &ts);
      ASSERT_NOTIMPL(rc == 0);
      sec = ts.tv_sec;
#endif /* _POSIX_TIMERS */
      return sec;
}

/*
 * Set timer to fire.   Delta is in microseconds from now.
 */
void
sa_timer_set(struct sa_timer *tm, u_long delta_usec)
{
      struct sa_timer *cur;
      struct sa_timer **prev;

      ASSERT(delta_usec != 0);
      ASSERT(tm->tm_handler != NULL);
      sa_timer_cancel(tm);
      ASSERT(sa_timer_active(tm) == 0);
      tm->tm_nsec =
          sa_timer_get() + delta_usec * SA_TIMER_HZ / SA_TIMER_UNITS;
      ASSERT(tm->tm_nsec != 0);

      /*
       * Insert timer into sorted linked list.
       * Find insertion point, before cur.
       */
      for (prev = &sa_timer_head;
           (cur = *prev) != NULL && cur->tm_nsec <= tm->tm_nsec;
           prev = &cur->tm_next)
            ;
      *prev = tm;
      tm->tm_next = cur;
}

/*
 * Cancel timer if it is active.
 */
void
sa_timer_cancel(struct sa_timer *tm)
{
      struct sa_timer *cur;
      struct sa_timer **prev;

      if (sa_timer_active(tm)) {
            for (prev = &sa_timer_head; (cur = *prev) != NULL;
                 prev = &cur->tm_next)
                  if (cur == tm) {
                        tm->tm_nsec = 0;
                        *prev = tm->tm_next;
                        break;
                  }
            ASSERT(cur == tm);
      }
}

/*
 * Free (and cancel) timer.
 */
void
sa_timer_free(struct sa_timer *tm)
{
      if (sa_timer_active(tm))
            sa_timer_cancel(tm);
      free(tm);
}

/*
 * Handle timer checks.  Called from select loop or other periodic function.
 *
 * The struct timeval is set before returning to the maximum amount of time
 * that should elapse before the next call.
 *
 * Returns 1 if any timer functions were called, 0 otherwise.
 */
int
sa_timer_check(struct timeval *tv)
{
      u_int64_t now = 0;
      u_int64_t next_due = 0;
      struct sa_timer *tm;
      int ret = 0;

      /*
       * Remember, the list may change during the handler.
       */
      for (;;) {
            now = sa_timer_get();
            tm = sa_timer_head;
            if (tm == NULL) {
                  next_due = now;
                  break;
            }

            next_due = tm->tm_nsec;
            if (next_due > now + SA_TIMER_FUZZ)
                  break;

            /*
             * Remove this element from the list.
             */
            sa_timer_head = tm->tm_next;
            tm->tm_next = NULL;

            /*
             * Mark cancelled and call handler.
             */
            tm->tm_nsec = 0;
            ASSERT(tm->tm_handler != NULL);
            (*tm->tm_handler)(tm->tm_handler_arg);
            ret = 1;
      }

      ASSERT(next_due >= now);
      next_due -= now;
      tv->tv_sec = (time_t) (next_due / SA_TIMER_HZ);
      tv->tv_usec = (long) (next_due % SA_TIMER_HZ) / 1000;

      return ret;
}

Generated by  Doxygen 1.6.0   Back to index