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