xref: /illumos-gate/usr/src/cmd/dispadmin/subr.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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
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
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
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