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
preen_addev(char * devnm)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
preen_subdev(char * name,struct dk_cinfo * dkiop,void * dp)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
preen_getdev(char * devnm)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
preen_releasedev(char * name)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 *
get_runnable(struct rawdev ** devlist)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
preen_addunit(void * cookie,char * dname,int (* cf)(),void * datap,uint_t unit)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
alloc_driver(char * name,int (* cf)(),void * datap)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 *
alloc_dev(int did)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
addunit(struct onedev * devp,uint_t unit)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
chooseone(int devmapsize,ulong_t * devmap,int drvmapsize,ulong_t * drvmap)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
makebusy(struct onedev * dev)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
notbusy(struct rawdev * rd)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