138494Sobrien/*
2310490Scy * Copyright (c) 1997-2014 Erez Zadok
338494Sobrien * Copyright (c) 1989 Jan-Simon Pendry
438494Sobrien * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
538494Sobrien * Copyright (c) 1989 The Regents of the University of California.
638494Sobrien * All rights reserved.
738494Sobrien *
838494Sobrien * This code is derived from software contributed to Berkeley by
938494Sobrien * Jan-Simon Pendry at Imperial College, London.
1038494Sobrien *
1138494Sobrien * Redistribution and use in source and binary forms, with or without
1238494Sobrien * modification, are permitted provided that the following conditions
1338494Sobrien * are met:
1438494Sobrien * 1. Redistributions of source code must retain the above copyright
1538494Sobrien *    notice, this list of conditions and the following disclaimer.
1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1738494Sobrien *    notice, this list of conditions and the following disclaimer in the
1838494Sobrien *    documentation and/or other materials provided with the distribution.
19310490Scy * 3. Neither the name of the University nor the names of its contributors
2038494Sobrien *    may be used to endorse or promote products derived from this software
2138494Sobrien *    without specific prior written permission.
2238494Sobrien *
2338494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2438494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2538494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2638494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2738494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2838494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2938494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3038494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3138494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3238494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3338494Sobrien * SUCH DAMAGE.
3438494Sobrien *
3538494Sobrien *
36174294Sobrien * File: am-utils/amd/clock.c
3738494Sobrien *
3838494Sobrien */
3938494Sobrien
4038494Sobrien/*
4138494Sobrien * Callouts.
4238494Sobrien *
4342629Sobrien * Modeled on kernel object of the same name.
4438494Sobrien * See usual references.
4538494Sobrien *
4638494Sobrien * Use of a heap-based mechanism was rejected:
4738494Sobrien * 1.  more complex implementation needed.
4838494Sobrien * 2.  not obvious that a list is too slow for Amd.
4938494Sobrien */
5038494Sobrien
5138494Sobrien#ifdef HAVE_CONFIG_H
5238494Sobrien# include <config.h>
5338494Sobrien#endif /* HAVE_CONFIG_H */
5438494Sobrien#include <am_defs.h>
5538494Sobrien#include <amd.h>
5638494Sobrien
5738494Sobrienvoid reschedule_timeouts(time_t now, time_t then);
5838494Sobrien
5938494Sobrientypedef struct callout callout;
6038494Sobrienstruct callout {
6138494Sobrien  callout *c_next;		/* List of callouts */
62174294Sobrien  callout_fun *c_fn;		/* Function to call */
63174294Sobrien  opaque_t c_arg;		/* Argument to pass to call */
6438494Sobrien  time_t c_time;		/* Time of call */
6538494Sobrien  int c_id;			/* Unique identifier */
6638494Sobrien};
6738494Sobrien
6838494Sobrienstatic callout callouts;	/* List of pending callouts */
6938494Sobrienstatic callout *free_callouts;	/* Cache of free callouts */
7038494Sobrienstatic int nfree_callouts;	/* Number on free list */
7138494Sobrienstatic int callout_id;		/* Next free callout identifier */
7238494Sobrien
7338494Sobrientime_t next_softclock;		/* Time of next call to softclock() */
7438494Sobrien
7538494Sobrien
7638494Sobrien/*
7738494Sobrien * Number of callout slots we keep on the free list
7838494Sobrien */
7938494Sobrien#define	CALLOUT_FREE_SLOP	10
8038494Sobrien
8138494Sobrien/*
8238494Sobrien * Global assumption: valid id's are non-zero.
8338494Sobrien */
84174294Sobrien#define	CID_ALLOC()		(++callout_id)
8538494Sobrien#define	CID_UNDEF		(0)
8638494Sobrien
8738494Sobrien
8838494Sobrienstatic callout *
8938494Sobrienalloc_callout(void)
9038494Sobrien{
9138494Sobrien  callout *cp = free_callouts;
9238494Sobrien
9338494Sobrien  if (cp) {
9438494Sobrien    --nfree_callouts;
9538494Sobrien    free_callouts = free_callouts->c_next;
9638494Sobrien    return cp;
9738494Sobrien  }
9838494Sobrien  return ALLOC(struct callout);
9938494Sobrien}
10038494Sobrien
10138494Sobrien
10238494Sobrienstatic void
10338494Sobrienfree_callout(callout *cp)
10438494Sobrien{
10538494Sobrien  if (nfree_callouts > CALLOUT_FREE_SLOP) {
10638494Sobrien    XFREE(cp);
10738494Sobrien  } else {
10838494Sobrien    cp->c_next = free_callouts;
10938494Sobrien    free_callouts = cp;
11038494Sobrien    nfree_callouts++;
11138494Sobrien  }
11238494Sobrien}
11338494Sobrien
11438494Sobrien
11538494Sobrien/*
11638494Sobrien * Schedule a callout.
11738494Sobrien *
118174294Sobrien * (*fn)(fn_arg) will be called at clocktime(NULL) + secs
11938494Sobrien */
12038494Sobrienint
121174294Sobrientimeout(u_int secs, callout_fun *fn, opaque_t fn_arg)
12238494Sobrien{
12338494Sobrien  callout *cp, *cp2;
124174294Sobrien  time_t t = clocktime(NULL) + secs;
12538494Sobrien
12638494Sobrien  /*
12738494Sobrien   * Allocate and fill in a new callout structure
12838494Sobrien   */
12938494Sobrien  callout *cpnew = alloc_callout();
130174294Sobrien  cpnew->c_arg = fn_arg;
13138494Sobrien  cpnew->c_fn = fn;
13238494Sobrien  cpnew->c_time = t;
133174294Sobrien  cpnew->c_id = CID_ALLOC();
13438494Sobrien
13538494Sobrien  if (t < next_softclock)
13638494Sobrien    next_softclock = t;
13738494Sobrien
13838494Sobrien  /*
13938494Sobrien   * Find the correct place in the list
14038494Sobrien   */
14138494Sobrien  for (cp = &callouts; (cp2 = cp->c_next); cp = cp2)
14238494Sobrien    if (cp2->c_time >= t)
14338494Sobrien      break;
14438494Sobrien
14538494Sobrien  /*
14638494Sobrien   * And link it in
14738494Sobrien   */
14838494Sobrien  cp->c_next = cpnew;
14938494Sobrien  cpnew->c_next = cp2;
15038494Sobrien
15138494Sobrien  /*
15238494Sobrien   * Return callout identifier
15338494Sobrien   */
15438494Sobrien  return cpnew->c_id;
15538494Sobrien}
15638494Sobrien
15738494Sobrien
15838494Sobrien/*
15938494Sobrien * De-schedule a callout
16038494Sobrien */
16138494Sobrienvoid
16238494Sobrienuntimeout(int id)
16338494Sobrien{
16438494Sobrien  callout *cp, *cp2;
16538494Sobrien  for (cp = &callouts; (cp2 = cp->c_next); cp = cp2) {
16638494Sobrien    if (cp2->c_id == id) {
16738494Sobrien      cp->c_next = cp2->c_next;
16838494Sobrien      free_callout(cp2);
16938494Sobrien      break;
17038494Sobrien    }
17138494Sobrien  }
17238494Sobrien}
17338494Sobrien
17438494Sobrien
17538494Sobrien/*
17638494Sobrien * Reschedule after clock changed
17738494Sobrien */
17838494Sobrienvoid
17938494Sobrienreschedule_timeouts(time_t now, time_t then)
18038494Sobrien{
18138494Sobrien  callout *cp;
18238494Sobrien
18338494Sobrien  for (cp = callouts.c_next; cp; cp = cp->c_next) {
18438494Sobrien    if (cp->c_time >= now && cp->c_time <= then) {
18538494Sobrien      plog(XLOG_WARNING, "job %d rescheduled to run immediately", cp->c_id);
18651292Sobrien      dlog("rescheduling job %d back %ld seconds",
18751292Sobrien	   cp->c_id, (long) (cp->c_time - now));
18838494Sobrien      next_softclock = cp->c_time = now;
18938494Sobrien    }
19038494Sobrien  }
19138494Sobrien}
19238494Sobrien
19338494Sobrien
19438494Sobrien/*
19538494Sobrien * Clock handler
19638494Sobrien */
19738494Sobrienint
19838494Sobriensoftclock(void)
19938494Sobrien{
20038494Sobrien  time_t now;
20138494Sobrien  callout *cp;
20238494Sobrien
20338494Sobrien  do {
20438494Sobrien    if (task_notify_todo)
20538494Sobrien      do_task_notify();
20638494Sobrien
207174294Sobrien    now = clocktime(NULL);
20838494Sobrien
20938494Sobrien    /*
21038494Sobrien     * While there are more callouts waiting...
21138494Sobrien     */
21238494Sobrien    while ((cp = callouts.c_next) && cp->c_time <= now) {
21338494Sobrien      /*
214174294Sobrien       * Extract first from list, save fn & fn_arg and
21538494Sobrien       * unlink callout from list and free.
21638494Sobrien       * Finally call function.
21738494Sobrien       *
21838494Sobrien       * The free is done first because
21938494Sobrien       * it is quite common that the
22038494Sobrien       * function will call timeout()
22138494Sobrien       * and try to allocate a callout
22238494Sobrien       */
223174294Sobrien      callout_fun *fn = cp->c_fn;
224174294Sobrien      opaque_t fn_arg = cp->c_arg;
22538494Sobrien
22638494Sobrien      callouts.c_next = cp->c_next;
22738494Sobrien      free_callout(cp);
228174294Sobrien      (*fn) (fn_arg);
22938494Sobrien    }
23038494Sobrien
23138494Sobrien  } while (task_notify_todo);
23238494Sobrien
23338494Sobrien  /*
23438494Sobrien   * Return number of seconds to next event,
23538494Sobrien   * or 0 if there is no event.
23638494Sobrien   */
23738494Sobrien  if ((cp = callouts.c_next))
23838494Sobrien    return cp->c_time - now;
23938494Sobrien  return 0;
24038494Sobrien}
241