1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
23 /* All Rights Reserved */
24
25
26 /*
27 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
29 */
30
31 #include <stdio.h>
32 #include <stdarg.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <sys/time.h>
37 #include <limits.h>
38
39 #include "dispadmin.h"
40
41
42 /*
43 * Utility functions for dispadmin command.
44 */
45
46
47 void
fatalerr(const char * format,...)48 fatalerr(const char *format, ...)
49 {
50 va_list ap;
51
52 (void) va_start(ap, format);
53 (void) vfprintf(stderr, format, ap);
54 va_end(ap);
55 exit(1);
56 }
57
58
59 /*
60 * hrtconvert() returns the interval specified by htp as a single
61 * value in resolution htp->hrt_res. Returns -1 on overflow.
62 */
63 long
hrtconvert(hrtimer_t * htp)64 hrtconvert(hrtimer_t *htp)
65 {
66 long sum;
67 long product;
68
69 product = htp->hrt_secs * htp->hrt_res;
70
71 if (product / htp->hrt_res == htp->hrt_secs) {
72 sum = product + htp->hrt_rem;
73 if (sum - htp->hrt_rem == product) {
74 return (sum);
75 }
76 }
77 return (-1);
78 }
79
80 /*
81 * The following routine was removed from libc (libc/port/gen/hrtnewres.c).
82 * It has also been added to priocntl, so if you fix it here, you should
83 * also probably fix it there. In the long term, this should be recoded to
84 * not be hrt'ish.
85 */
86
87 /*
88 * Convert interval expressed in htp->hrt_res to new_res.
89 *
90 * Calculate: (interval * new_res) / htp->hrt_res rounding off as
91 * specified by round.
92 *
93 * Note: All args are assumed to be positive. If
94 * the last divide results in something bigger than
95 * a long, then -1 is returned instead.
96 */
97
98 int
_hrtnewres(hrtimer_t * htp,ulong_t new_res,long round)99 _hrtnewres(hrtimer_t *htp, ulong_t new_res, long round)
100 {
101 long interval;
102 longlong_t dint;
103 longlong_t dto_res;
104 longlong_t drem;
105 longlong_t dfrom_res;
106 longlong_t prod;
107 longlong_t quot;
108 long numerator;
109 long result;
110 ulong_t modulus;
111 ulong_t twomodulus;
112 long temp;
113
114 if (htp->hrt_res == 0 || new_res == 0 ||
115 new_res > NANOSEC || htp->hrt_rem < 0)
116 return (-1);
117
118 if (htp->hrt_rem >= htp->hrt_res) {
119 htp->hrt_secs += htp->hrt_rem / htp->hrt_res;
120 htp->hrt_rem = htp->hrt_rem % htp->hrt_res;
121 }
122
123 interval = htp->hrt_rem;
124 if (interval == 0) {
125 htp->hrt_res = new_res;
126 return (0);
127 }
128
129 /*
130 * Try to do the calculations in single precision first
131 * (for speed). If they overflow, use double precision.
132 * What we want to compute is:
133 *
134 * (interval * new_res) / hrt->hrt_res
135 */
136
137 numerator = interval * new_res;
138
139 if (numerator / new_res == interval) {
140
141 /*
142 * The above multiply didn't give overflow since
143 * the division got back the original number. Go
144 * ahead and compute the result.
145 */
146
147 result = numerator / htp->hrt_res;
148
149 /*
150 * For HRT_RND, compute the value of:
151 *
152 * (interval * new_res) % htp->hrt_res
153 *
154 * If it is greater than half of the htp->hrt_res,
155 * then rounding increases the result by 1.
156 *
157 * For HRT_RNDUP, we increase the result by 1 if:
158 *
159 * result * htp->hrt_res != numerator
160 *
161 * because this tells us we truncated when calculating
162 * result above.
163 *
164 * We also check for overflow when incrementing result
165 * although this is extremely rare.
166 */
167
168 if (round == HRT_RND) {
169 modulus = numerator - result * htp->hrt_res;
170 if ((twomodulus = 2 * modulus) / 2 == modulus) {
171
172 /*
173 * No overflow (if we overflow in calculation
174 * of twomodulus we fall through and use
175 * double precision).
176 */
177 if (twomodulus >= htp->hrt_res) {
178 temp = result + 1;
179 if (temp - 1 == result)
180 result++;
181 else
182 return (-1);
183 }
184 htp->hrt_res = new_res;
185 htp->hrt_rem = result;
186 return (0);
187 }
188 } else if (round == HRT_RNDUP) {
189 if (result * htp->hrt_res != numerator) {
190 temp = result + 1;
191 if (temp - 1 == result)
192 result++;
193 else
194 return (-1);
195 }
196 htp->hrt_res = new_res;
197 htp->hrt_rem = result;
198 return (0);
199 } else { /* round == HRT_TRUNC */
200 htp->hrt_res = new_res;
201 htp->hrt_rem = result;
202 return (0);
203 }
204 }
205
206 /*
207 * We would get overflow doing the calculation is
208 * single precision so do it the slow but careful way.
209 *
210 * Compute the interval times the resolution we are
211 * going to.
212 */
213
214 dint = interval;
215 dto_res = new_res;
216 prod = dint * dto_res;
217
218 /*
219 * For HRT_RND the result will be equal to:
220 *
221 * ((interval * new_res) + htp->hrt_res / 2) / htp->hrt_res
222 *
223 * and for HRT_RNDUP we use:
224 *
225 * ((interval * new_res) + htp->hrt_res - 1) / htp->hrt_res
226 *
227 * This is a different but equivalent way of rounding.
228 */
229
230 if (round == HRT_RND) {
231 drem = htp->hrt_res / 2;
232 prod = prod + drem;
233 } else if (round == HRT_RNDUP) {
234 drem = htp->hrt_res - 1;
235 prod = prod + drem;
236 }
237
238 dfrom_res = htp->hrt_res;
239 quot = prod / dfrom_res;
240
241 /*
242 * If the quotient won't fit in a long, then we have
243 * overflow. Otherwise, return the result.
244 */
245
246 if (quot > UINT_MAX) {
247 return (-1);
248 } else {
249 htp->hrt_res = new_res;
250 htp->hrt_rem = (int)quot;
251 return (0);
252 }
253 }
254