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