summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/sys/callo.h
blob: b48d41e3b95db5f61b76170bc169474aa71c8e5e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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
 * or http://www.opensolaris.org/os/licensing.
 * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/


/*
 * Copyright (c) 1997-1998 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#ifndef _SYS_CALLO_H
#define	_SYS_CALLO_H

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <sys/t_lock.h>
#include <sys/taskq.h>

#ifdef	__cplusplus
extern "C" {
#endif

typedef long	callout_id_t;		/* internal form of timeout_id_t */

/*
 * The callout mechanism provides general-purpose event scheduling:
 * an arbitrary function is called in a specified amount of time.
 */
typedef struct callout {
	struct callout	*c_idnext;	/* next in ID hash, or on freelist */
	struct callout	*c_idprev;	/* prev in ID hash */
	struct callout	*c_lbnext;	/* next in lbolt hash */
	struct callout	*c_lbprev;	/* prev in lbolt hash */
	callout_id_t	c_xid;		/* extended callout ID; see below */
	clock_t		c_runtime;	/* absolute run time */
	void		(*c_func)(void *); /* function to call */
	void		*c_arg;		/* argument to function */
	kthread_id_t	c_executor;	/* thread executing callout */
	kcondvar_t	c_done;		/* signal callout completion */
} callout_t;

/*
 * The extended callout ID consists of the callout ID (as returned by
 * timeout()) plus a bit indicating whether the callout is executing.
 *
 * The callout ID uniquely identifies a callout.  It contains a table ID,
 * indicating which callout table the callout belongs to, a bit indicating
 * whether this is a short-term or long-term callout, and a running counter.
 * The highest bit of the running counter is always set; this ensures that
 * the callout ID is always non-zero, thus eliminating the need for an
 * explicit wrap-around test during ID generation.
 *
 * The long-term bit exists to address the problem of callout ID collision.
 * This is an issue because the system typically generates a large number of
 * timeout() requests, which means that callout IDs eventually get recycled.
 * Most timeouts are very short-lived, so that ID recycling isn't a problem;
 * but there are a handful of timeouts which are sufficiently long-lived to
 * see their own IDs reused.  We use the long-term bit to partition the
 * ID namespace into pieces; the short-term space gets all the heavy traffic
 * and can wrap frequently (i.e., on the order of a day) with no ill effects;
 * the long-term space gets very little traffic and thus never wraps.
 */
#define	CALLOUT_EXECUTING	(1UL << (8 * sizeof (long) - 1))
#define	CALLOUT_LONGTERM	(1UL << (8 * sizeof (long) - 2))
#define	CALLOUT_COUNTER_HIGH	(1UL << (8 * sizeof (long) - 3))
#define	CALLOUT_FANOUT_BITS	3
#define	CALLOUT_TYPE_BITS	1
#define	CALLOUT_NTYPES		(1 << CALLOUT_TYPE_BITS)
#define	CALLOUT_FANOUT		(1 << CALLOUT_FANOUT_BITS)
#define	CALLOUT_FANOUT_MASK	(CALLOUT_FANOUT - 1)
#define	CALLOUT_COUNTER_SHIFT	(CALLOUT_TYPE_BITS + CALLOUT_FANOUT_BITS)
#define	CALLOUT_COUNTER_LOW	(1 << CALLOUT_COUNTER_SHIFT)
#define	CALLOUT_TABLES		CALLOUT_COUNTER_LOW
#define	CALLOUT_TABLE_MASK	(CALLOUT_TABLES - 1)
#define	CALLOUT_TABLE(t, f)	\
	(((t) << CALLOUT_FANOUT_BITS) + ((f) & CALLOUT_FANOUT_MASK))

/*
 * We assume that during any period of CALLOUT_LONGTERM_TICKS ticks, at most
 * (CALLOUT_COUNTER_HIGH / CALLOUT_COUNTER_LOW) callouts will be generated.
 */
#define	CALLOUT_LONGTERM_TICKS	0x4000
#define	CALLOUT_BUCKETS		512		/* MUST be a power of 2 */
#define	CALLOUT_BUCKET_MASK	(CALLOUT_BUCKETS - 1)
#define	CALLOUT_HASH(x)		((x) & CALLOUT_BUCKET_MASK)
#define	CALLOUT_IDHASH(x)	CALLOUT_HASH((x) >> CALLOUT_COUNTER_SHIFT)
#define	CALLOUT_LBHASH(x)	CALLOUT_HASH(x)

#define	CALLOUT_THREADS		2		/* keep it simple for now */

#define	CALLOUT_REALTIME	0		/* realtime callout type */
#define	CALLOUT_NORMAL		1		/* normal callout type */

/*
 * All of the state information associated with a callout table.
 * The fields are ordered with cache performance in mind.
 */
typedef struct callout_table {
	kmutex_t	ct_lock;	/* protects all callout state */
	callout_t	*ct_freelist;	/* free callout structures */
	clock_t		ct_curtime;	/* current time; tracks lbolt */
	clock_t		ct_runtime;	/* the callouts we're running now */
	taskq_t		*ct_taskq;	/* taskq to execute normal callouts */
	callout_id_t	ct_short_id;	/* most recently issued short-term ID */
	callout_id_t	ct_long_id;	/* most recently issued long-term ID */
	callout_t 	*ct_idhash[CALLOUT_BUCKETS];	/* ID hash chains */
	callout_t 	*ct_lbhash[CALLOUT_BUCKETS];	/* lbolt hash chains */
} callout_table_t;

#ifdef	_KERNEL
extern	void		callout_init(void);
extern	void		callout_schedule(void);
#endif

#ifdef	__cplusplus
}
#endif

#endif	/* _SYS_CALLO_H */