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 #pragma ident "%Z%%M% %I% %E% SMI"
32
33 #include <stdio.h>
34 #include <stdarg.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <sys/types.h>
38 #include <sys/time.h>
39 #include <limits.h>
40
41 #include "dispadmin.h"
42
43
44 /*
45 * Utility functions for dispadmin command.
46 */
47
48
49 void
fatalerr(const char * format,...)50 fatalerr(const char *format, ...)
51 {
52 va_list ap;
53
54 (void) va_start(ap, format);
55 (void) vfprintf(stderr, format, ap);
56 va_end(ap);
57 exit(1);
58 }
59
60
61 /*
62 * hrtconvert() returns the interval specified by htp as a single
63 * value in resolution htp->hrt_res. Returns -1 on overflow.
64 */
65 long
hrtconvert(hrtimer_t * htp)66 hrtconvert(hrtimer_t *htp)
67 {
68 long sum;
69 long product;
70
71 product = htp->hrt_secs * htp->hrt_res;
72
73 if (product / htp->hrt_res == htp->hrt_secs) {
74 sum = product + htp->hrt_rem;
75 if (sum - htp->hrt_rem == product) {
76 return (sum);
77 }
78 }
79 return (-1);
80 }
81
82 /*
83 * The following routine was removed from libc (libc/port/gen/hrtnewres.c).
84 * It has also been added to priocntl, so if you fix it here, you should
85 * also probably fix it there. In the long term, this should be recoded to
86 * not be hrt'ish.
87 */
88
89 /*
90 * Convert interval expressed in htp->hrt_res to new_res.
91 *
92 * Calculate: (interval * new_res) / htp->hrt_res rounding off as
93 * specified by round.
94 *
95 * Note: All args are assumed to be positive. If
96 * the last divide results in something bigger than
97 * a long, then -1 is returned instead.
98 */
99
100 int
_hrtnewres(hrtimer_t * htp,ulong_t new_res,long round)101 _hrtnewres(hrtimer_t *htp, ulong_t new_res, long round)
102 {
103 long interval;
104 longlong_t dint;
105 longlong_t dto_res;
106 longlong_t drem;
107 longlong_t dfrom_res;
108 longlong_t prod;
109 longlong_t quot;
110 long numerator;
111 long result;
112 ulong_t modulus;
113 ulong_t twomodulus;
114 long temp;
115
116 if (htp->hrt_res == 0 || new_res == 0 ||
117 new_res > NANOSEC || htp->hrt_rem < 0)
118 return (-1);
119
120 if (htp->hrt_rem >= htp->hrt_res) {
121 htp->hrt_secs += htp->hrt_rem / htp->hrt_res;
122 htp->hrt_rem = htp->hrt_rem % htp->hrt_res;
123 }
124
125 interval = htp->hrt_rem;
126 if (interval == 0) {
127 htp->hrt_res = new_res;
128 return (0);
129 }
130
131 /*
132 * Try to do the calculations in single precision first
133 * (for speed). If they overflow, use double precision.
134 * What we want to compute is:
135 *
136 * (interval * new_res) / hrt->hrt_res
137 */
138
139 numerator = interval * new_res;
140
141 if (numerator / new_res == interval) {
142
143 /*
144 * The above multiply didn't give overflow since
145 * the division got back the original number. Go
146 * ahead and compute the result.
147 */
148
149 result = numerator / htp->hrt_res;
150
151 /*
152 * For HRT_RND, compute the value of:
153 *
154 * (interval * new_res) % htp->hrt_res
155 *
156 * If it is greater than half of the htp->hrt_res,
157 * then rounding increases the result by 1.
158 *
159 * For HRT_RNDUP, we increase the result by 1 if:
160 *
161 * result * htp->hrt_res != numerator
162 *
163 * because this tells us we truncated when calculating
164 * result above.
165 *
166 * We also check for overflow when incrementing result
167 * although this is extremely rare.
168 */
169
170 if (round == HRT_RND) {
171 modulus = numerator - result * htp->hrt_res;
172 if ((twomodulus = 2 * modulus) / 2 == modulus) {
173
174 /*
175 * No overflow (if we overflow in calculation
176 * of twomodulus we fall through and use
177 * double precision).
178 */
179 if (twomodulus >= htp->hrt_res) {
180 temp = result + 1;
181 if (temp - 1 == result)
182 result++;
183 else
184 return (-1);
185 }
186 htp->hrt_res = new_res;
187 htp->hrt_rem = result;
188 return (0);
189 }
190 } else if (round == HRT_RNDUP) {
191 if (result * htp->hrt_res != numerator) {
192 temp = result + 1;
193 if (temp - 1 == result)
194 result++;
195 else
196 return (-1);
197 }
198 htp->hrt_res = new_res;
199 htp->hrt_rem = result;
200 return (0);
201 } else { /* round == HRT_TRUNC */
202 htp->hrt_res = new_res;
203 htp->hrt_rem = result;
204 return (0);
205 }
206 }
207
208 /*
209 * We would get overflow doing the calculation is
210 * single precision so do it the slow but careful way.
211 *
212 * Compute the interval times the resolution we are
213 * going to.
214 */
215
216 dint = interval;
217 dto_res = new_res;
218 prod = dint * dto_res;
219
220 /*
221 * For HRT_RND the result will be equal to:
222 *
223 * ((interval * new_res) + htp->hrt_res / 2) / htp->hrt_res
224 *
225 * and for HRT_RNDUP we use:
226 *
227 * ((interval * new_res) + htp->hrt_res - 1) / htp->hrt_res
228 *
229 * This is a different but equivalent way of rounding.
230 */
231
232 if (round == HRT_RND) {
233 drem = htp->hrt_res / 2;
234 prod = prod + drem;
235 } else if (round == HRT_RNDUP) {
236 drem = htp->hrt_res - 1;
237 prod = prod + drem;
238 }
239
240 dfrom_res = htp->hrt_res;
241 quot = prod / dfrom_res;
242
243 /*
244 * If the quotient won't fit in a long, then we have
245 * overflow. Otherwise, return the result.
246 */
247
248 if (quot > UINT_MAX) {
249 return (-1);
250 } else {
251 htp->hrt_res = new_res;
252 htp->hrt_rem = (int)quot;
253 return (0);
254 }
255 }
256