]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/kern_callout.c
xnu-1699.22.73.tar.gz
[apple/xnu.git] / bsd / kern / kern_callout.c
CommitLineData
b0d623f7
A
1/*
2 * Copyright (c) 2004-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29/*
30 * Kernel callout related functions, including moving average calculation
31 * to permit the kernel to know about insufficiently responsive user space
32 * processes.
33 */
34
35#include <string.h> /* memove, memset */
36#include <stdint.h> /* uint64_t */
37#include <sys/kern_callout.h>
38
39/*
40 * kco_ma_init
41 *
42 * Initialize a moving average structure for use
43 *
44 * Parameters: map Pointer to the moving average state
45 * threshold Threshold % at which to trigger (>100)
46 * kind Kind of trigger(s) to set
47 *
48 * Returns: (void)
49 *
50 * Notes: The number of samples in a simple moving average is not
51 * controllable; this might be a future direction.
52 *
53 * The simple and weighted thresholds are not separately
54 * controllable; this might be a future direction, but
55 * will likely be unnecessary due to one type being in use
56 * at a time in the most likely scenarios.
57 */
58void
59kco_ma_init(struct kco_moving_average *map, int32_t threshold, int kind)
60{
61 memset(map, 0, sizeof(*map));
62
63 /* per algorithm init required */
64 map->ma_flags |= KCO_MA_F_NEEDS_INIT;
65
66 /* set algorithm selector flags */
67 map->ma_flags |= kind;
68
69 /* set thresholds */
70 map->ma_sma_threshold = threshold;
71 map->ma_wma_threshold = threshold;
72}
73
74
75/*
76 * kco_ma_info
77 *
78 * Report on the current moving average information; this is typically only
79 * called after a trigger event.
80 *
81 * Parameters: map Pointer to the moving average state
82 * kind Kind of trigger to report on
83 * averagep Pointer to area to receive current
84 * old_averagep Pointer to area to receive previous
85 * thresholdp Pointer to area to receive threshold
86 *
87 * Returns: 0 Information not available
88 * 1 Information retrieved
89 *
90 * Notes: You can only retrieve one kind of average information at a
91 * time; if you are collecting multiple types, then you must
92 * call this function one time for each type you are interested
93 * in obtaining.
94 */
95int
96kco_ma_info(struct kco_moving_average *map, int kind, uint64_t *averagep, uint64_t *old_averagep, int32_t *thresholdp, int *countp)
97{
98 uint64_t average;
99 uint64_t old_average;
100 int32_t threshold;
101 int count;
102
103 /* Not collecting this type of data or no data yet*/
104 if (!(map->ma_flags & kind) || (map->ma_flags & KCO_MA_F_NEEDS_INIT))
105 return(0);
106
107 switch(kind) {
108 case KCO_MA_F_SMA:
109 average = map->ma_sma;
110 old_average = map->ma_old_sma;
111 threshold = map->ma_sma_threshold;
112 count = map->ma_sma_trigger_count;
113 break;
114
115 case KCO_MA_F_WMA:
116 average = map->ma_wma;
117 old_average = map->ma_old_wma;
118 threshold = map->ma_wma_threshold;
119 count = map->ma_wma_trigger_count;
120 break;
121
122 default:
123 /*
124 * Asking for data we don't have or more than one kind of
125 * data at the same time.
126 */
127 return(0);
128 }
129
130 if (averagep != NULL)
131 *averagep = average;
132 if (old_averagep != NULL)
133 *old_averagep = old_average;
134 if (thresholdp != NULL)
135 *thresholdp = threshold;
136 if (countp != NULL)
137 *countp = count;
138
139 return(1);
140}
141
142
143/*
144 * kco_ma_addsample
145 *
146 * Accumulate a sample into a moving average
147 *
148 * Parameters: map Pointer to the moving average state
149 * sample_time latency delta time
150 *
151 * Returns: 0 Nothing triggered
152 * !0 Bitmap of KCO_MA_F_* flags for the
153 * algorithms which triggered
154 *
155 * Notes: Add a delta time sample to the moving average; this function
156 * will return bits for each algorithm which went over its
157 * trigger threshold as a result of receiving the sample.
158 * Callers can then log/complain/panic over the unresponsive
159 * process to which they are calling out.
160 */
161int
162kco_ma_addsample(struct kco_moving_average *map, uint64_t sample_time)
163{
164 int triggered = 0;
165 int do_init = (map->ma_flags & KCO_MA_F_NEEDS_INIT);
166
167 /*
168 * SIMPLE MOVING AVERAGE
169 *
170 * Compute simple moving average over MA_SMA_SAMPLES; incremental is
171 * cheaper than re-sum.
172 */
173 if (map->ma_flags & KCO_MA_F_SMA) {
174 map->ma_old_sma = map->ma_sma;
175
176 map->ma_sma = ((map->ma_sma * MA_SMA_SAMPLES) - map->ma_sma_samples[0] + sample_time) / MA_SMA_SAMPLES;
177 memmove(&map->ma_sma_samples[1], &map->ma_sma_samples[0], sizeof(map->ma_sma_samples[0]) *(MA_SMA_SAMPLES - 1));
178 map->ma_sma_samples[0] = sample_time;
179 /*
180 * Check if percentage change exceeds the allowed trigger
181 * threshold; this will only happen if the sample time
182 * increases more than an acceptable amount; decreases will
183 * not cause a trigger (but will decrease the overall average,
184 * which can cause a trigger the next time).
185 *
186 * Note: We don't start triggering on the simple moving
187 * average until after we have enough samples for
188 * the delta to be statistically valid; this is
189 * defined to be MA_SMA_SAMPLES.
190 */
191 if (map->ma_sma_samples[MA_SMA_SAMPLES-1] && ((int)((map->ma_sma * 100) / map->ma_old_sma)) > map->ma_sma_threshold) {
192 triggered |= KCO_MA_F_SMA;
193 map->ma_sma_trigger_count++;
194 }
195 }
196
197 /*
198 * WEIGHTED MOVING AVERAGE
199 *
200 * Compute the weighted moving average. Do this by averaging over
201 * two values, one with a lesser weighting than the other; the lesser
202 * weighted value is the persistent historical value, whose sample
203 * weight decreases over time, the older the samples get. Be careful
204 * here to permit strict integer artimatic.
205 */
206 if (map->ma_flags & KCO_MA_F_WMA) {
207 map->ma_old_wma = map->ma_wma;
208
209 /* Prime the pump, if necessary */
210 if (do_init)
211 map->ma_old_wma = sample_time;
212
213 map->ma_wma = ((((map->ma_wma * 90) + sample_time * ((100*2) - 90))/100) / 2);
214
215 /*
216 * Check if percentage change exceeds the allowed trigger
217 * threshold; this will only happen if the sample time
218 * increases more than an acceptable amount; decreases will
219 * not cause a trigger (but will decrease the overall average,
220 * which can cause a trigger the next time).
221 */
222 if (((int)(((map->ma_wma * 100) / map->ma_old_wma))) > map->ma_wma_threshold) {
223 triggered |= KCO_MA_F_WMA;
224 map->ma_wma_trigger_count++;
225 }
226 }
227
228 if (do_init)
229 map->ma_flags &= ~KCO_MA_F_NEEDS_INIT;
230
231 return (triggered);
232}