xref: /netbsd-src/usr.bin/config/pack.c (revision 181254a7b1bdde6873432bffef2d2decc4b5c22f)
1 /*	$NetBSD: pack.c,v 1.10 2015/09/12 19:11:13 joerg Exp $	*/
2 
3 /*
4  * Copyright (c) 1992, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This software was developed by the Computer Systems Engineering group
8  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9  * contributed to Berkeley.
10  *
11  * All advertising materials mentioning features or use of this software
12  * must display the following acknowledgement:
13  *	This product includes software developed by the University of
14  *	California, Lawrence Berkeley Laboratories.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  * 3. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  *	from: @(#)pack.c	8.1 (Berkeley) 6/6/93
41  */
42 
43 #if HAVE_NBTOOL_CONFIG_H
44 #include "nbtool_config.h"
45 #endif
46 
47 #include <sys/cdefs.h>
48 __RCSID("$NetBSD: pack.c,v 1.10 2015/09/12 19:11:13 joerg Exp $");
49 
50 #include <sys/param.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <util.h>
54 #include "defs.h"
55 
56 /*
57  * Packing.  We have three separate kinds of packing here.
58  *
59  * First, we pack device instances which have identical parent specs.
60  *
61  * Second, we pack locators.  Given something like
62  *
63  *	hp0 at mba0 drive 0
64  *	hp* at mba* drive ?
65  *	ht0 at mba0 drive 0
66  *	tu0 at ht0 slave 0
67  *	ht* at mba* drive ?
68  *	tu* at ht* slave ?
69  *
70  * (where the default drive and slave numbers are -1), we have three
71  * locators whose value is 0 and three whose value is -1.  Rather than
72  * emitting six integers, we emit just two.
73  *
74  * When packing locators, we would like to find sequences such as
75  *	{1 2 3} {2 3 4} {3} {4 5}
76  * and turn this into the flat sequence {1 2 3 4 5}, with each subsequence
77  * given by the appropriate offset (here 0, 1, 2, and 3 respectively).
78  * Non-overlapping packing is much easier, and so we use that here
79  * and miss out on the chance to squeeze the locator sequence optimally.
80  * (So it goes.)
81  */
82 
83 typedef int (*vec_cmp_func)(const void *, int, int);
84 
85 #define	TAILHSIZE	128
86 #define	PVHASH(i)	((i) & (TAILHSIZE - 1))
87 #define	LOCHASH(l)	(((long)(l) >> 2) & (TAILHSIZE - 1))
88 struct tails {
89 	struct	tails *t_next;
90 	int	t_ends_at;
91 };
92 
93 static struct tails *tails[TAILHSIZE];
94 static int locspace;
95 
96 static void packdevi(void);
97 static void packlocs(void);
98 
99 static int sameas(struct devi *, struct devi *);
100 static int findvec(const void *, int, int, vec_cmp_func, int);
101 static int samelocs(const void *, int, int);
102 static int addlocs(const char **, int);
103 static int loclencmp(const void *, const void *);
104 static void resettails(void);
105 
106 void
107 pack(void)
108 {
109 	struct pspec *p;
110 	struct devi *i;
111 
112 	/* Pack instances and make parent vectors. */
113 	packdevi();
114 
115 	/*
116 	 * Now that we know what we have, find upper limits on space
117 	 * needed for the loc[] table.  The loc table size is bounded by
118 	 * what we would get if no packing occurred.
119 	 */
120 	locspace = 0;
121 	TAILQ_FOREACH(i, &alldevi, i_next) {
122 		if (i->i_active != DEVI_ACTIVE || i->i_collapsed)
123 			continue;
124 		if ((p = i->i_pspec) == NULL)
125 			continue;
126 		locspace += p->p_iattr->a_loclen;
127 	}
128 
129 	/* Allocate and pack loc[]. */
130 	locators.vec = ecalloc((size_t)locspace, sizeof(*locators.vec));
131 	locators.used = 0;
132 	packlocs();
133 }
134 
135 /*
136  * Pack device instances together wherever possible.
137  */
138 static void
139 packdevi(void)
140 {
141 	struct devi *firststar, *i, **ip, *l, *p;
142 	struct devbase *d;
143 	u_short j, m, n;
144 
145 	/*
146 	 * Sort all the cloning units to after the non-cloning units,
147 	 * preserving order of cloning and non-cloning units with
148 	 * respect to other units of the same type.
149 	 *
150 	 * Algorithm: Walk down the list until the first cloning unit is
151 	 * seen for the second time (or until the end of the list, if there
152 	 * are no cloning units on the list), moving starred units to the
153 	 * end of the list.
154 	 */
155 	TAILQ_FOREACH(d, &allbases, d_next) {
156 		ip = &d->d_ihead;
157 		firststar = NULL;
158 
159 		for (i = *ip; i != firststar; i = *ip) {
160 			if (i->i_unit != STAR) {
161 				/* try i->i_bsame next */
162 				ip = &i->i_bsame;
163 			} else {
164 				if (firststar == NULL)
165 					firststar = i;
166 
167 				*d->d_ipp = i;
168 				d->d_ipp = &i->i_bsame;
169 
170 				*ip = i->i_bsame;
171 				i->i_bsame = NULL;
172 
173 				/* leave ip alone; try (old) i->i_bsame next */
174 			}
175 		}
176 	}
177 
178 	packed = ecalloc((size_t)ndevi + 1, sizeof *packed);
179 	n = 0;
180 	TAILQ_FOREACH(d, &allbases, d_next) {
181 		/*
182 		 * For each instance of each device, add or collapse
183 		 * all its aliases.
184 		 *
185 		 * Pseudo-devices have a non-empty d_ihead for convenience.
186 		 * Ignore them.
187 		 */
188 		if (d->d_ispseudo)
189 			continue;
190 		for (i = d->d_ihead; i != NULL; i = i->i_bsame) {
191 			m = n;
192 			for (l = i; l != NULL; l = l->i_alias) {
193 				if (l->i_active != DEVI_ACTIVE
194 				    || i->i_pseudoroot)
195 					continue;
196 				l->i_locoff = -1;
197 				/* try to find an equivalent for l */
198 				for (j = m; j < n; j++) {
199 					p = packed[j];
200 					if (sameas(l, p)) {
201 						l->i_collapsed = 1;
202 						l->i_cfindex = p->i_cfindex;
203 						goto nextalias;
204 					}
205 				}
206 				/* could not find a suitable alias */
207 				l->i_collapsed = 0;
208 				l->i_cfindex = n;
209 				packed[n++] = l;
210  nextalias:;
211 			}
212 		}
213 	}
214 	npacked = n;
215 	packed[n] = NULL;
216 }
217 
218 /*
219  * Return true if two aliases are "the same".  In this case, they need
220  * to have the same parent spec, have the same config flags, and have
221  * the same locators.
222  */
223 static int
224 sameas(struct devi *i1, struct devi *i2)
225 {
226 	const char **p1, **p2;
227 
228 	if (i1->i_pspec != i2->i_pspec)
229 		return (0);
230 	if (i1->i_cfflags != i2->i_cfflags)
231 		return (0);
232 	for (p1 = i1->i_locs, p2 = i2->i_locs; *p1 == *p2; p2++)
233 		if (*p1++ == 0)
234 			return (1);
235 	return (0);
236 }
237 
238 static void
239 packlocs(void)
240 {
241 	struct pspec *ps;
242 	struct devi **p, *i;
243 	int l,o;
244 	extern int Pflag;
245 
246 	qsort(packed, npacked, sizeof *packed, loclencmp);
247 	for (p = packed; (i = *p) != NULL; p++) {
248 		if ((ps = i->i_pspec) != NULL &&
249 		    (l = ps->p_iattr->a_loclen) > 0) {
250 			if (Pflag) {
251 				o = findvec(i->i_locs,
252 				    LOCHASH(i->i_locs[l - 1]), l,
253 				    samelocs, locators.used);
254 				i->i_locoff = o < 0 ?
255 				    addlocs(i->i_locs, l) : o;
256 			} else
257 				i->i_locoff = addlocs(i->i_locs, l);
258 		} else
259 			i->i_locoff = -1;
260 	}
261 	resettails();
262 }
263 
264 /*
265  * Return the index at which the given vector already exists, or -1
266  * if it is not anywhere in the current set.  If we return -1, we assume
267  * our caller will add it at the end of the current set, and we make
268  * sure that next time, we will find it there.
269  */
270 static int
271 findvec(const void *ptr, int hash, int len, vec_cmp_func cmp, int nextplace)
272 {
273 	struct tails *t, **hp;
274 	int off;
275 
276 	hp = &tails[hash];
277 	for (t = *hp; t != NULL; t = t->t_next) {
278 		off = t->t_ends_at - len;
279 		if (off >= 0 && (*cmp)(ptr, off, len))
280 			return (off);
281 	}
282 	t = ecalloc(1, sizeof(*t));
283 	t->t_next = *hp;
284 	*hp = t;
285 	t->t_ends_at = nextplace + len;
286 	return (-1);
287 }
288 
289 /*
290  * Comparison function for locators.
291  */
292 static int
293 samelocs(const void *ptr, int off, int len)
294 {
295 	const char * const *p, * const *q;
296 
297 	for (p = &locators.vec[off], q = (const char * const *)ptr; --len >= 0;)
298 		if (*p++ != *q++)
299 			return (0);	/* different */
300 	return (1);			/* same */
301 }
302 
303 /*
304  * Add the given locators at the end of the global loc[] table.
305  */
306 static int
307 addlocs(const char **locs, int len)
308 {
309 	const char **p;
310 	int ret;
311 
312 	ret = locators.used;
313 	if ((locators.used = ret + len) > locspace)
314 		panic("addlocs: overrun");
315 	for (p = &locators.vec[ret]; --len >= 0;)
316 		*p++ = *locs++;
317 	return (ret);
318 }
319 
320 /*
321  * Comparison function for qsort-by-locator-length, longest first.
322  * We rashly assume that subtraction of these lengths does not overflow.
323  */
324 static int
325 loclencmp(const void *a, const void *b)
326 {
327 	const struct pspec *p1, *p2;
328 	int l1, l2;
329 
330 	p1 = (*(const struct devi * const *)a)->i_pspec;
331 	l1 = p1 != NULL ? p1->p_iattr->a_loclen : 0;
332 
333 	p2 = (*(const struct devi * const *)b)->i_pspec;
334 	l2 = p2 != NULL ? p2->p_iattr->a_loclen : 0;
335 
336 	return (l2 - l1);
337 }
338 
339 static void
340 resettails(void)
341 {
342 	struct tails **p, *t, *next;
343 	int i;
344 
345 	for (p = tails, i = TAILHSIZE; --i >= 0; p++) {
346 		for (t = *p; t != NULL; t = next) {
347 			next = t->t_next;
348 			free(t);
349 		}
350 		*p = NULL;
351 	}
352 }
353