xref: /illumos-gate/usr/src/cmd/fs.d/preenlib.c (revision ddb365bfc9e868ad24ccdcb0dc91af18b10df082)
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 
23 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 /*
29  * common routines for parallelization (used by both fsck and quotacheck)
30  */
31 #include <stdio.h>
32 #include <fcntl.h>
33 #include <dlfcn.h>
34 #include <macros.h>
35 #include <sys/types.h>
36 #include <sys/wait.h>
37 #include <sys/mntent.h>
38 #include <sys/dkio.h>
39 
40 /*
41  * data structures for parallelization
42  */
43 struct driver {
44 	char 	*name;			/* driver name (from DKIOCINFO) */
45 	uint_t	mapsize;		/* size of `busymap' */
46 	uint_t	*busymap;		/* bitmask of active units */
47 	int	(*choosefunc)();	/* driver specific chooser */
48 	void	*data;			/* driver private data */
49 };
50 
51 struct onedev {
52 	int	drvid;			/* index in driver array */
53 	uint_t	mapsize;		/* size of `unitmap' */
54 	uint_t	*unitmap;		/* unit #'s (from DKIOCINFO) */
55 	struct onedev *nxtdev;
56 };
57 
58 struct rawdev {
59 	char	*devname;		/* name passed to preen_addev */
60 	struct	onedev *alldevs;	/* info about each component device */
61 	struct rawdev *nxtrd;		/* next entry in list */
62 };
63 
64 static int debug = 0;
65 
66 /*
67  * defines used in building shared object names
68  */
69 
70 /* the directory where we find shared objects */
71 #define	OBJECT_DIRECTORY	"/usr/lib/drv"
72 
73 /* a shared object name is OBJECT_PREFIX || driver_name */
74 #define	OBJECT_PREFIX		"preen_"
75 
76 /* the version of the driver interface we support */
77 #define	OBJECT_VERSION		1
78 
79 /* the "build" entry point for a driver specific object is named this */
80 #define	BUILD_ENTRY		preen_build_devs
81 #define	BUILD_NAME		"preen_build_devs"
82 
83 #define	DRIVER_ALLOC	10
84 static int ndrivers, ndalloc;
85 static struct driver *dlist;
86 
87 static struct rawdev *unchecked, *active, *get_runnable();
88 static struct onedev *alloc_dev();
89 static int chooseone();
90 
91 #define	WORDSIZE	(NBBY * sizeof (uint_t))
92 
93 void 	preen_addunit(void *, char *, int (*)(), void *, uint_t);
94 int 	preen_subdev(char *, struct dk_cinfo *, void *);
95 
96 static int 	alloc_driver(char *, int (*)(), void *);
97 static void 	addunit(struct onedev *, uint_t);
98 static void	makebusy(struct onedev *);
99 static void	notbusy(struct rawdev *);
100 
101 /*
102  * add the given device to the list of devices to be checked
103  */
104 int
105 preen_addev(char *devnm)
106 {
107 	struct rawdev *rdp;
108 	int fd;
109 	struct dk_cinfo dki;
110 	extern char *strdup();
111 
112 	if ((fd = open64(devnm, O_RDONLY)) == -1) {
113 		perror(devnm);
114 		return (-1);
115 	}
116 	if (ioctl(fd, DKIOCINFO, &dki) == -1) {
117 		perror("DKIOCINFO");
118 		fprintf(stderr, "device: `%s'\n", devnm);
119 		(void) close(fd);
120 		return (-1);
121 	}
122 	(void) close(fd);
123 	if ((rdp = (struct rawdev *)malloc(sizeof (struct rawdev))) == NULL) {
124 		(void) fprintf(stderr, "out of memory in preenlib\n");
125 		return (-1);
126 	}
127 	if ((rdp->devname = strdup(devnm)) == NULL) {
128 		(void) fprintf(stderr, "out of memory in preenlib\n");
129 		return (-1);
130 	}
131 	rdp->alldevs = NULL;
132 	rdp->nxtrd = NULL;
133 
134 	if (preen_subdev(devnm, &dki, (void *)rdp)) {
135 		preen_addunit(rdp, dki.dki_dname, NULL, NULL, dki.dki_unit);
136 	}
137 
138 	rdp->nxtrd = unchecked;
139 	unchecked = rdp;
140 	return (0);
141 }
142 
143 int
144 preen_subdev(char *name, struct dk_cinfo *dkiop, void *dp)
145 {
146 	char modname[255];
147 	void *dlhandle;
148 	int (*fptr)();
149 
150 	(void) sprintf(modname, "%s/%s%s.so.%d",
151 	    OBJECT_DIRECTORY, OBJECT_PREFIX, dkiop->dki_dname, OBJECT_VERSION);
152 	dlhandle = dlopen(modname, RTLD_LAZY);
153 	if (dlhandle == NULL) {
154 		if (debug)
155 			(void) fprintf(stderr, "preen_subdev: %s\n", dlerror());
156 		return (1);
157 	}
158 	fptr = (int (*)())dlsym(dlhandle, BUILD_NAME);
159 	if (fptr == NULL) {
160 		if (debug)
161 			(void) fprintf(stderr, "preen_subdev: %s\n", dlerror());
162 		return (1);
163 	}
164 	(*fptr)(name, dkiop, dp);
165 	return (0);
166 }
167 
168 /*
169  * select a device from the "unchecked" list, and add it to the
170  * active list.
171  */
172 int
173 preen_getdev(char *devnm)
174 {
175 	struct rawdev *rdp;
176 	struct onedev *dp;
177 
178 	if (unchecked == NULL)
179 		return (0);
180 
181 	rdp = get_runnable(&unchecked);
182 
183 	if (rdp) {
184 		for (dp = rdp->alldevs; dp; dp = dp->nxtdev) {
185 			makebusy(dp);
186 		}
187 		rdp->nxtrd = active;
188 		active = rdp;
189 		(void) strcpy(devnm, rdp->devname);
190 		return (1);
191 	} else {
192 		return (2);
193 	}
194 }
195 
196 int
197 preen_releasedev(char *name)
198 {
199 	struct rawdev *dp, *ldp;
200 
201 	for (ldp = NULL, dp = active; dp != NULL; ldp = dp, dp = dp->nxtrd) {
202 		if (strcmp(dp->devname, name) == 0)
203 			break;
204 	}
205 
206 	if (dp == NULL)
207 		return (-1);
208 	if (ldp != NULL) {
209 		ldp->nxtrd = dp->nxtrd;
210 	} else {
211 		active = dp->nxtrd;
212 	}
213 
214 	notbusy(dp);
215 	/*
216 	 * free(dp->devname);
217 	 * free(dp);
218 	 */
219 	return (0);
220 }
221 
222 static
223 struct rawdev *
224 get_runnable(struct rawdev **devlist)
225 {
226 	struct rawdev *last, *rdp;
227 	struct onedev *devp;
228 	struct driver *drvp;
229 	int rc = 1;
230 
231 	for (last = NULL, rdp = *devlist; rdp; last = rdp, rdp = rdp->nxtrd) {
232 		for (devp = rdp->alldevs; devp != NULL; devp = devp->nxtdev) {
233 			drvp = &dlist[devp->drvid];
234 			rc = (*drvp->choosefunc)(devp->mapsize, devp->unitmap,
235 			    drvp->mapsize, drvp->busymap);
236 			if (rc != 0)
237 				break;
238 		}
239 		if (rc == 0)
240 			break;
241 	}
242 
243 	/*
244 	 * remove from list...
245 	 */
246 	if (rdp) {
247 		if (last) {
248 			last->nxtrd = rdp->nxtrd;
249 		} else {
250 			*devlist = rdp->nxtrd;
251 		}
252 	}
253 
254 	return (rdp);
255 }
256 
257 /*
258  * add the given driver/unit reference to the `rawdev' structure identified
259  * by `cookie'
260  * If a new `driver' structure needs to be created, associate the given
261  * choosing function and driver private data with it.
262  */
263 void
264 preen_addunit(
265 	void    *cookie,
266 	char	*dname,		/* driver name */
267 	int	(*cf)(),	/* candidate choosing function */
268 	void	*datap,		/* driver private data */
269 	uint_t	unit)		/* unit number */
270 {
271 	int drvid;
272 	struct driver *dp;
273 	struct onedev *devp;
274 	struct rawdev *rdp = (struct rawdev *)cookie;
275 
276 	/*
277 	 * locate the driver struct
278 	 */
279 	dp = NULL;
280 	for (drvid = 0; drvid < ndrivers; drvid++) {
281 		if (strcmp(dlist[drvid].name, dname) == 0) {
282 			dp = &dlist[drvid];
283 			break;
284 		}
285 	}
286 
287 	if (dp == NULL) {
288 		/*
289 		 * driver struct doesn't exist yet -- create one
290 		 */
291 		if (cf == NULL)
292 			cf = chooseone;
293 		drvid = alloc_driver(dname, cf, datap);
294 		dp = &dlist[drvid];
295 	}
296 
297 	for (devp = rdp->alldevs; devp != NULL; devp = devp->nxtdev) {
298 		/*
299 		 * see if this device already references the given driver
300 		 */
301 		if (devp->drvid == drvid)
302 			break;
303 	}
304 
305 	if (devp == NULL) {
306 		/*
307 		 * allocate a new `struct onedev' and chain it in
308 		 * rdp->alldevs...
309 		 */
310 		devp = alloc_dev(drvid);
311 		devp->nxtdev = rdp->alldevs;
312 		rdp->alldevs = devp;
313 	}
314 
315 	/*
316 	 * add `unit' to the unitmap in devp
317 	 */
318 	addunit(devp, unit);
319 }
320 
321 static
322 int
323 alloc_driver(char *name, int (*cf)(), void *datap)
324 {
325 	struct driver *dp;
326 	extern char *strdup();
327 
328 	if (ndrivers == ndalloc) {
329 		dlist = ndalloc ?
330 		    (struct driver *)
331 		    realloc(dlist, sizeof (struct driver) * DRIVER_ALLOC) :
332 		    (struct driver *)
333 		    malloc(sizeof (struct driver) * DRIVER_ALLOC);
334 		if (dlist == NULL) {
335 			(void) fprintf(stderr, "out of memory in preenlib\n");
336 			exit(1);
337 		}
338 		ndalloc += DRIVER_ALLOC;
339 	}
340 
341 	dp = &dlist[ndrivers];
342 	dp->name = strdup(name);
343 	if (dp->name == NULL) {
344 		(void) fprintf(stderr, "out of memory in preenlib\n");
345 		exit(1);
346 	}
347 	dp->choosefunc = cf;
348 	dp->data = datap;
349 	dp->mapsize = 0;
350 	dp->busymap = NULL;
351 	return (ndrivers++);
352 }
353 
354 static
355 struct onedev *
356 alloc_dev(int did)
357 {
358 	struct onedev *devp;
359 
360 	devp = (struct onedev *)malloc(sizeof (struct onedev));
361 	if (devp == NULL) {
362 		(void) fprintf(stderr, "out of memory in preenlib\n");
363 		exit(1);
364 	}
365 	devp->drvid = did;
366 	devp->mapsize = 0;
367 	devp->unitmap = NULL;
368 	devp->nxtdev = NULL;
369 	return (devp);
370 }
371 
372 static
373 void
374 addunit(struct onedev *devp, uint_t unit)
375 {
376 	uint_t newsize;
377 
378 	newsize = howmany(unit+1, WORDSIZE);
379 	if (devp->mapsize < newsize) {
380 		devp->unitmap = devp->mapsize ?
381 		    (uint_t *)realloc(devp->unitmap,
382 		    newsize * sizeof (uint_t)) :
383 		    (uint_t *)malloc(newsize * sizeof (uint_t));
384 		if (devp->unitmap == NULL) {
385 			(void) fprintf(stderr, "out of memory in preenlib\n");
386 			exit(1);
387 		}
388 		(void) memset((char *)&devp->unitmap[devp->mapsize], 0,
389 		    (uint_t)((newsize - devp->mapsize) * sizeof (uint_t)));
390 		devp->mapsize = newsize;
391 	}
392 	devp->unitmap[unit / WORDSIZE] |= (1 << (unit % WORDSIZE));
393 }
394 
395 static int
396 chooseone(int devmapsize, ulong_t *devmap, int drvmapsize, ulong_t *drvmap)
397 {
398 	int i;
399 
400 	for (i = 0; i < min(devmapsize, drvmapsize); i++) {
401 		if (devmap[i] & drvmap[i])
402 			return (1);
403 	}
404 	return (0);
405 }
406 
407 /*
408  * mark the given driver/unit pair as busy.  This is called from
409  * preen_getdev.
410  */
411 static
412 void
413 makebusy(struct onedev *dev)
414 {
415 	struct driver *drvp = &dlist[dev->drvid];
416 	int newsize = dev->mapsize;
417 	int i;
418 
419 	if (drvp->mapsize < newsize) {
420 		drvp->busymap = drvp->mapsize ?
421 		    (uint_t *)realloc(drvp->busymap,
422 		    newsize * sizeof (uint_t)) :
423 		    (uint_t *)malloc(newsize * sizeof (uint_t));
424 		if (drvp->busymap == NULL) {
425 			(void) fprintf(stderr, "out of memory in preenlib\n");
426 			exit(1);
427 		}
428 		(void) memset((char *)&drvp->busymap[drvp->mapsize], 0,
429 		    (uint_t)((newsize - drvp->mapsize) * sizeof (uint_t)));
430 		drvp->mapsize = newsize;
431 	}
432 
433 	for (i = 0; i < newsize; i++)
434 		drvp->busymap[i] |= dev->unitmap[i];
435 }
436 
437 /*
438  * make each device in the given `rawdev' un-busy.
439  * Called from preen_releasedev
440  */
441 static
442 void
443 notbusy(struct rawdev *rd)
444 {
445 	struct onedev *devp;
446 	struct driver *drvp;
447 	int i;
448 
449 	for (devp = rd->alldevs; devp; devp = devp->nxtdev) {
450 		drvp = &dlist[devp->drvid];
451 		for (i = 0; i < devp->mapsize; i++)
452 			drvp->busymap[i] &= ~(devp->unitmap[i]);
453 	}
454 }
455