Home | History | Annotate | Download | only in fs.d
      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 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