callout.c revision f635d46a9872dc5a02bbbd736f2bf18685c2c221
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
/*
* Callout tables. See timeout(9F) for details.
*/
static int cpr_stop_callout;
static int callout_fanout;
static int ncallout;
{ \
}
{ \
else \
}
/*
* Allocate a callout structure. We try quite hard because we
* can't sleep, and if we can't do the allocation, we're toast.
* Failing all, we try a KM_PANIC allocation.
*/
static callout_t *
{
KM_NOSLEEP | KM_PANIC);
ncallout++;
return (cp);
}
/*
* Arrange that func(arg) be called after delta clock ticks.
*/
static timeout_id_t
{
else
/*
* Make sure the callout runs at least 1 tick in the future.
*/
if (delta <= 0)
delta = 1;
/*
* Assign an ID to this callout
*/
if (delta > CALLOUT_LONGTERM_TICKS)
else
"timeout:%K(%p) in %ld ticks, cp %p",
return ((timeout_id_t)id);
}
{
}
{
}
{
}
continue;
/*
* The callout we want to delete is currently executing.
* The DDI states that we must wait until the callout
* completes before returning, so we block on c_done until
* the callout ID changes (to zero if it's on the freelist,
* or to a new callout ID if it's in use). This implicitly
* assumes that callout structures are persistent (they are).
*/
/*
* The timeout handler called untimeout() on itself.
* Stupid, but legal. We can't wait for the timeout
* to complete without deadlocking, so we just return.
*/
"untimeout_self:ID %x", id);
return (-1);
}
"untimeout_executing:ID %lx", id);
return (-1);
}
"untimeout_bogus_id:ID %lx", id);
/*
* We didn't find the specified callout ID. This means either
* (1) the callout already fired, or (2) the caller passed us
* a bogus value. Perform a sanity check to detect case (2).
*/
return (-1);
}
/*
* Do the actual work of executing callouts. This routine is called either
* by a taskq_thread (normal case), or by softcall (realtime case).
*/
static void
{
/*
* Assuming the system time can be set forward and backward
* at any time. If it is set backward, we will measure the
* c_runtime; otherwise, we will compare c_runhrtime with
* ct_curhrtime.
*/
(xid & CALLOUT_EXECUTING))
continue;
/*
* Delete callout from hash tables, return to freelist,
* and tell anyone who cares that we're done.
* Even though we dropped and reacquired ct->ct_lock,
* it's OK to pick up where we left off because only
* newly-created timeouts can precede cp on ct_lbhash,
* and those timeouts cannot be due on this tick.
*/
}
/*
* We have completed all callouts that were scheduled to
* run at "runtime". If the global run time still matches
* our local copy, then we advance the global run time;
* otherwise, another callout thread must have already done so.
*/
}
}
/*
* Schedule any callouts that are due on or before this tick.
*/
static void
{
gethrestime(&now);
/*
* We use both the conditions cp->c_runtime == runtime and
* cp->c_runhrtime <= curhrtime to determine a timeout is
* premature or not. If the system time has been set backwards,
* then cp->c_runtime == runtime will become true first.
* Otherwise, we test cp->c_runhrtime <= curhrtime
*/
continue;
else
return;
}
ct->ct_runtime++;
}
}
/*
* Schedule callouts for all callout tables. Called by clock() on each tick.
*/
void
callout_schedule(void)
{
int f, t;
if (cpr_stop_callout)
return;
for (t = 0; t < CALLOUT_NTYPES; t++)
for (f = 0; f < callout_fanout; f++)
}
/*
* Callback handler used by CPR to stop and resume callouts.
*/
/*ARGSUSED*/
static boolean_t
{
return (B_TRUE);
}
/*
* Initialize all callout tables. Called at boot time just before clkstart().
*/
void
callout_init(void)
{
int f, t;
int table_id;
for (t = 0; t < CALLOUT_NTYPES; t++) {
for (f = 0; f < CALLOUT_FANOUT; f++) {
table_id = CALLOUT_TABLE(t, f);
if (f >= callout_fanout) {
continue;
}
/*
* We can not call gethrestime() at this moment
* since the system time has not been validated.
* So Set ct_curhrtime to zero.
*/
ct->ct_curhrtime = 0;
if (t == CALLOUT_NORMAL) {
/*
* Each callout thread consumes exactly one
* task structure while active. Therefore,
* prepopulating with 2 * CALLOUT_THREADS tasks
* ensures that there's at least one task per
* thread that's either scheduled or on the
* freelist. In turn, this guarantees that
* taskq_dispatch() will always either succeed
* (because there's a free task structure) or
* be unnecessary (because "callout_excute(ct)"
* has already scheduled).
*/
taskq_create_instance("callout_taskq", f,
}
}
}
}