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 #pragma ident "%Z%%M% %I% %E% SMI" 23 /* 24 * Copyright (c) 1992,1996, by Sun Microsystems, Inc. 25 * All rights reserved. 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 static 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 static 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 static 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 preen_addev(char *devnm) 105 { 106 struct rawdev *rdp; 107 int fd; 108 struct dk_cinfo dki; 109 extern char *strdup(); 110 111 if ((fd = open64(devnm, O_RDONLY)) == -1) { 112 perror(devnm); 113 return (-1); 114 } 115 if (ioctl(fd, DKIOCINFO, &dki) == -1) { 116 perror("DKIOCINFO"); 117 fprintf(stderr, "device: `%s'\n", devnm); 118 (void) close(fd); 119 return (-1); 120 } 121 (void) close(fd); 122 if ((rdp = (struct rawdev *)malloc(sizeof (struct rawdev))) == NULL) { 123 (void) fprintf(stderr, "out of memory in preenlib\n"); 124 return (-1); 125 } 126 if ((rdp->devname = strdup(devnm)) == NULL) { 127 (void) fprintf(stderr, "out of memory in preenlib\n"); 128 return (-1); 129 } 130 rdp->alldevs = NULL; 131 rdp->nxtrd = NULL; 132 133 if (preen_subdev(devnm, &dki, (void *)rdp)) { 134 preen_addunit(rdp, dki.dki_dname, NULL, NULL, dki.dki_unit); 135 } 136 137 rdp->nxtrd = unchecked; 138 unchecked = rdp; 139 return (0); 140 } 141 142 preen_subdev(char *name, struct dk_cinfo *dkiop, void *dp) 143 { 144 char modname[255]; 145 void *dlhandle; 146 int (*fptr)(); 147 148 (void) sprintf(modname, "%s/%s%s.so.%d", 149 OBJECT_DIRECTORY, OBJECT_PREFIX, dkiop->dki_dname, OBJECT_VERSION); 150 dlhandle = dlopen(modname, RTLD_LAZY); 151 if (dlhandle == NULL) { 152 if (debug) 153 (void) fprintf(stderr, "preen_subdev: %s\n", dlerror()); 154 return (1); 155 } 156 fptr = (int (*)())dlsym(dlhandle, BUILD_NAME); 157 if (fptr == NULL) { 158 if (debug) 159 (void) fprintf(stderr, "preen_subdev: %s\n", dlerror()); 160 return (1); 161 } 162 (*fptr)(name, dkiop, dp); 163 return (0); 164 } 165 166 /* 167 * select a device from the "unchecked" list, and add it to the 168 * active list. 169 */ 170 preen_getdev(char *devnm) 171 { 172 struct rawdev *rdp; 173 register struct onedev *dp; 174 175 if (unchecked == NULL) 176 return (0); 177 178 rdp = get_runnable(&unchecked); 179 180 if (rdp) { 181 for (dp = rdp->alldevs; dp; dp = dp->nxtdev) { 182 makebusy(dp); 183 } 184 rdp->nxtrd = active; 185 active = rdp; 186 (void) strcpy(devnm, rdp->devname); 187 return (1); 188 } else { 189 return (2); 190 } 191 } 192 193 preen_releasedev(char *name) 194 { 195 register struct rawdev *dp, *ldp; 196 197 for (ldp = NULL, dp = active; dp != NULL; ldp = dp, dp = dp->nxtrd) { 198 if (strcmp(dp->devname, name) == 0) 199 break; 200 } 201 202 if (dp == NULL) 203 return (-1); 204 if (ldp != NULL) { 205 ldp->nxtrd = dp->nxtrd; 206 } else { 207 active = dp->nxtrd; 208 } 209 210 notbusy(dp); 211 /* 212 * free(dp->devname); 213 * free(dp); 214 */ 215 return (0); 216 } 217 218 static 219 struct rawdev * 220 get_runnable(struct rawdev **devlist) 221 { 222 register struct rawdev *last, *rdp; 223 register struct onedev *devp; 224 register struct driver *drvp; 225 int rc = 1; 226 227 for (last = NULL, rdp = *devlist; rdp; last = rdp, rdp = rdp->nxtrd) { 228 for (devp = rdp->alldevs; devp != NULL; devp = devp->nxtdev) { 229 drvp = &dlist[devp->drvid]; 230 rc = (*drvp->choosefunc)(devp->mapsize, devp->unitmap, 231 drvp->mapsize, drvp->busymap); 232 if (rc != 0) 233 break; 234 } 235 if (rc == 0) 236 break; 237 } 238 239 /* 240 * remove from list... 241 */ 242 if (rdp) { 243 if (last) { 244 last->nxtrd = rdp->nxtrd; 245 } else { 246 *devlist = rdp->nxtrd; 247 } 248 } 249 250 return (rdp); 251 } 252 253 /* 254 * add the given driver/unit reference to the `rawdev' structure identified 255 * by `cookie' 256 * If a new `driver' structure needs to be created, associate the given 257 * choosing function and driver private data with it. 258 */ 259 void 260 preen_addunit( 261 void *cookie, 262 char *dname, /* driver name */ 263 int (*cf)(), /* candidate choosing function */ 264 void *datap, /* driver private data */ 265 uint_t unit) /* unit number */ 266 { 267 register int drvid; 268 register struct driver *dp; 269 register struct onedev *devp; 270 struct rawdev *rdp = (struct rawdev *)cookie; 271 272 /* 273 * locate the driver struct 274 */ 275 dp = NULL; 276 for (drvid = 0; drvid < ndrivers; drvid++) { 277 if (strcmp(dlist[drvid].name, dname) == 0) { 278 dp = &dlist[drvid]; 279 break; 280 } 281 } 282 283 if (dp == NULL) { 284 /* 285 * driver struct doesn't exist yet -- create one 286 */ 287 if (cf == NULL) 288 cf = chooseone; 289 drvid = alloc_driver(dname, cf, datap); 290 dp = &dlist[drvid]; 291 } 292 293 for (devp = rdp->alldevs; devp != NULL; devp = devp->nxtdev) { 294 /* 295 * see if this device already references the given driver 296 */ 297 if (devp->drvid == drvid) 298 break; 299 } 300 301 if (devp == NULL) { 302 /* 303 * allocate a new `struct onedev' and chain it in 304 * rdp->alldevs... 305 */ 306 devp = alloc_dev(drvid); 307 devp->nxtdev = rdp->alldevs; 308 rdp->alldevs = devp; 309 } 310 311 /* 312 * add `unit' to the unitmap in devp 313 */ 314 addunit(devp, unit); 315 } 316 317 static 318 int 319 alloc_driver(char *name, int (*cf)(), void *datap) 320 { 321 register struct driver *dp; 322 extern char *strdup(); 323 324 if (ndrivers == ndalloc) { 325 dlist = ndalloc ? 326 (struct driver *) 327 realloc(dlist, sizeof (struct driver) * DRIVER_ALLOC) : 328 (struct driver *) 329 malloc(sizeof (struct driver) * DRIVER_ALLOC); 330 if (dlist == NULL) { 331 (void) fprintf(stderr, "out of memory in preenlib\n"); 332 exit(1); 333 } 334 ndalloc += DRIVER_ALLOC; 335 } 336 337 dp = &dlist[ndrivers]; 338 dp->name = strdup(name); 339 if (dp->name == NULL) { 340 (void) fprintf(stderr, "out of memory in preenlib\n"); 341 exit(1); 342 } 343 dp->choosefunc = cf; 344 dp->data = datap; 345 dp->mapsize = 0; 346 dp->busymap = NULL; 347 return (ndrivers++); 348 } 349 350 static 351 struct onedev * 352 alloc_dev(int did) 353 { 354 register struct onedev *devp; 355 356 devp = (struct onedev *)malloc(sizeof (struct onedev)); 357 if (devp == NULL) { 358 (void) fprintf(stderr, "out of memory in preenlib\n"); 359 exit(1); 360 } 361 devp->drvid = did; 362 devp->mapsize = 0; 363 devp->unitmap = NULL; 364 devp->nxtdev = NULL; 365 return (devp); 366 } 367 368 static 369 void 370 addunit(struct onedev *devp, uint_t unit) 371 { 372 uint_t newsize; 373 374 newsize = howmany(unit+1, WORDSIZE); 375 if (devp->mapsize < newsize) { 376 devp->unitmap = devp->mapsize ? 377 (uint_t *)realloc(devp->unitmap, 378 newsize * sizeof (uint_t)) : 379 (uint_t *)malloc(newsize * sizeof (uint_t)); 380 if (devp->unitmap == NULL) { 381 (void) fprintf(stderr, "out of memory in preenlib\n"); 382 exit(1); 383 } 384 (void) memset((char *)&devp->unitmap[devp->mapsize], 0, 385 (uint_t)((newsize - devp->mapsize) * sizeof (uint_t))); 386 devp->mapsize = newsize; 387 } 388 devp->unitmap[unit / WORDSIZE] |= (1 << (unit % WORDSIZE)); 389 } 390 391 static 392 chooseone(int devmapsize, ulong_t *devmap, int drvmapsize, ulong_t *drvmap) 393 { 394 register int i; 395 396 for (i = 0; i < min(devmapsize, drvmapsize); i++) { 397 if (devmap[i] & drvmap[i]) 398 return (1); 399 } 400 return (0); 401 } 402 403 /* 404 * mark the given driver/unit pair as busy. This is called from 405 * preen_getdev. 406 */ 407 static 408 void 409 makebusy(struct onedev *dev) 410 { 411 struct driver *drvp = &dlist[dev->drvid]; 412 int newsize = dev->mapsize; 413 register int i; 414 415 if (drvp->mapsize < newsize) { 416 drvp->busymap = drvp->mapsize ? 417 (uint_t *)realloc(drvp->busymap, 418 newsize * sizeof (uint_t)) : 419 (uint_t *)malloc(newsize * sizeof (uint_t)); 420 if (drvp->busymap == NULL) { 421 (void) fprintf(stderr, "out of memory in preenlib\n"); 422 exit(1); 423 } 424 (void) memset((char *)&drvp->busymap[drvp->mapsize], 0, 425 (uint_t)((newsize - drvp->mapsize) * sizeof (uint_t))); 426 drvp->mapsize = newsize; 427 } 428 429 for (i = 0; i < newsize; i++) 430 drvp->busymap[i] |= dev->unitmap[i]; 431 } 432 433 /* 434 * make each device in the given `rawdev' un-busy. 435 * Called from preen_releasedev 436 */ 437 static 438 void 439 notbusy(struct rawdev *rd) 440 { 441 struct onedev *devp; 442 struct driver *drvp; 443 register int i; 444 445 for (devp = rd->alldevs; devp; devp = devp->nxtdev) { 446 drvp = &dlist[devp->drvid]; 447 for (i = 0; i < devp->mapsize; i++) 448 drvp->busymap[i] &= ~(devp->unitmap[i]); 449 } 450 } 451