Subject: Overhaul device parent matching in config(8)/kernel
To: None <tech-kern@netbsd.org>
From: Jason R Thorpe <thorpej@wasabisystems.com>
List: tech-kern
Date: 09/25/2002 09:49:42
--ZGiS0Q5IWpPtfppv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Folks...

I have overhauled how device parent matching is done in the kernel in
order to better support:

	1. Drivers are loadable modules (no, this is no where near
	   complete yet).

	2. Some forthcoming changes to support attaching real devices
	   to pseudo-devices.

For those of you fortunate enough to not know how this currently works
in the kernel, here is a lesson:

	* For each entry in the cfdata[] table, config(8) emits a
	  "parent vector", which is an array of indices back into
	  the cfdata[] table.  These reference cfdata[] entries for
	  potential parents of the device.

	* When the kernel attaches a device, it scans the cfdata[]
	  table looking for un-attached entries who's parent vector
	  array references the device's cfdata[] entry, and when it
	  finds one, applies the match info in the child's cfdata[]
	  entry, attempting to attach the device.

This is sub-optimal for several reasons:

	* In the presence of multiple cfdata[] tables (i.e. which
	  would be the case with loadable driver modules), the parent
	  vector can't reference a parent from another cfdata[] table.

	* For devices which are spec'd to attach to an interface attribute
	  (e.g. "scsibus* at scsi?"), config(8) represents this internally
	  by expanding the vector to all device's which carry that interface
	  attribute.  I.e. if you say "acphy* at mii?", conifig(8) actually
	  generates a parent vector that looks like this:

		acphy* at url*|aue*|xi*|ste*|bge*|wm*|stge*|gsip*| \
			  sip*|vr*|tl*|pcn*|sf*|tlp*|tlp*|rtk*|ne0|\
			  ne1|ne*|ne*|ne*|ne*|epic*|sm0|sm*|sm*|fxp*|\
			  ex*|ep*|ep*|ep*|ep*|ep*|ep* 

	  This obviously doesn't work if you later load a "foo" device
	  which carries the "mii" interface attribute, and want to attach
	  an acphy to it ... you'd need to create another cfdata[] entry
	  for acphy with the correct parent vector.

What I have done to address this is:

	* Add a "const char **cd_attrs" member to the cfdriver structure.
	  If non-NULL, it is an array of interface attributes carried by
	  a device, e.g.:

static const char *ex_attrs[] = { "mii", NULL };
struct cfdriver ex_cd = {
	NULL, "ex", DV_IFNET, 0, ex_attrs
};

	  For devices which do not have interface attributes, you
	  have this instead:

struct cfdriver lc_cd = {
	NULL, "lc", DV_IFNET, 0, NULL
};

	  By recording the fact that something has an interface attribute,
	  this allows you to attach to that attribute at run-time, rather
	  than relying on a pre-expanded list of things that have the
	  attribute.

	* Remove the parent vector array, in favor of a new "parent spec",
	  which looks like this:

struct cfparent {
	const char *cfp_iattr;		/* interface attribute */
	const char *cfp_parent;		/* optional specific parent */
	int cfp_unit;			/* optional specific unit
					   (-1 to wildcard) */
};

	  Since all devices attach to an interface attribute, the
	  parent spec records which one.  Additionally, if a device
	  instance wants to attach to a specific parent, that is also
	  recorded.  So, the new parent spec can preceisely describe
	  the following three cases:

		scsibus* at scsi?
			-> iattr = "scsi", parent = NULL, unit = -1

		scsibus* at ahc?
			-> iattr = "scsi", parent = "ahc", unit = -1

		scsibus* at ahc5
			-> iattr = "scsi", parent = "ahc", unit = 5

	* The kernel now uses the cd_attrs array and the parent specs
	  to identify potential children of a device (i.e. the device
	  matches the child's parent spec).  This works in the presence
	  of multiple cfdata tables, because no references back into
	  any cfdata table are made; everything is done with string
	  comparisons.

This exercise has also allowed me to garbage-collect vast amounts of
confusing code from config(8) :-)

Attached are the diffs that implement this.  They're actually not
that large, and some of the changes were merely to make the
__BROKEN_CONFIG_UNIT_USAGE stuff a little more readable.

-- 
        -- Jason R. Thorpe <thorpej@wasabisystems.com>

--ZGiS0Q5IWpPtfppv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=config-diffs

Index: sys/kern/subr_autoconf.c
===================================================================
RCS file: /cvsroot/syssrc/sys/kern/subr_autoconf.c,v
retrieving revision 1.65
diff -c -r1.65 subr_autoconf.c
*** sys/kern/subr_autoconf.c	2002/09/23 23:16:06	1.65
--- sys/kern/subr_autoconf.c	2002/09/25 15:13:29
***************
*** 222,227 ****
--- 222,278 ----
  }
  
  /*
+  * Determine if `parent' is a potential parent for a device spec based
+  * on `cfp'.
+  */
+ static int
+ cfparent_match(struct device *parent, const struct cfparent *cfp)
+ {
+ 	struct cfdriver *pcd = parent->dv_cfdata->cf_driver;
+ 	const char **cpp, *cp;
+ 
+ 	/*
+ 	 * First, ensure this parent has the correct interface
+ 	 * attribute.
+ 	 */
+ 	if (pcd->cd_attrs == NULL)
+ 		return (0);	/* no interface attributes -> no children */
+ 	for (cpp = pcd->cd_attrs; (cp = *cpp) != NULL; cpp++) {
+ 		if (cp[0] == cfp->cfp_iattr[0] &&
+ 		    strcmp(cp, cfp->cfp_iattr) == 0) {
+ 			/* Match. */
+ 			break;
+ 		}
+ 	}
+ 	if (cp == NULL)
+ 		return (0);	/* doesn't carry the req'd attribute */
+ 
+ 	/*
+ 	 * If no specific parent device instance was specified (i.e.
+ 	 * we're attaching to the attribute only), we're done!
+ 	 */
+ 	if (cfp->cfp_parent == NULL)
+ 		return (1);
+ 
+ 	/*
+ 	 * Check the parent device's name.
+ 	 */
+ 	if (pcd->cd_name[0] != cfp->cfp_parent[0] ||
+ 	    strcmp(pcd->cd_name, cfp->cfp_parent) != 0)
+ 		return (0);	/* not the same parent */
+ 
+ 	/*
+ 	 * Make sure the unit number matches.
+ 	 */
+ 	if (cfp->cfp_unit == -1 ||	/* wildcard */
+ 	    cfp->cfp_unit == parent->dv_unit)
+ 		return (1);
+ 
+ 	/* Unit numbers don't match. */
+ 	return (0);
+ }
+ 
+ /*
   * Iterate over all potential children of some device, calling the given
   * function (default being the child's match function) for each one.
   * Nonzero returns are matches; the highest value returned is considered
***************
*** 237,243 ****
  {
  	struct cftable *ct;
  	struct cfdata *cf;
- 	short *p;
  	struct matchinfo m;
  
  	m.fn = fn;
--- 288,293 ----
***************
*** 258,266 ****
  			if (cf->cf_fstate == FSTATE_DNOTFOUND ||
  			    cf->cf_fstate == FSTATE_DSTAR)
  				continue;
! 			for (p = cf->cf_parents; *p >= 0; p++)
! 				if (parent->dv_cfdata == &(ct->ct_cfdata)[*p])
! 					mapply(&m, cf);
  		}
  	}
  	return (m.match);
--- 308,315 ----
  			if (cf->cf_fstate == FSTATE_DNOTFOUND ||
  			    cf->cf_fstate == FSTATE_DSTAR)
  				continue;
! 			if (cfparent_match(parent, cf->cf_pspec))
! 				mapply(&m, cf);
  		}
  	}
  	return (m.match);
***************
*** 404,409 ****
--- 453,459 ----
  	ca = cf->cf_attach;
  	if (ca->ca_devsize < sizeof(struct device))
  		panic("config_attach");
+ 
  #ifndef __BROKEN_CONFIG_UNIT_USAGE
  	if (cf->cf_fstate == FSTATE_STAR) {
  		for (myunit = cf->cf_unit; myunit < cd->cd_ndevs; myunit++)
***************
*** 415,429 ****
  		 */
  	} else {
  		myunit = cf->cf_unit;
! #else /* __BROKEN_CONFIG_UNIT_USAGE */
  	myunit = cf->cf_unit;
  	if (cf->cf_fstate == FSTATE_STAR)
  		cf->cf_unit++;
  	else {
- #endif /* __BROKEN_CONFIG_UNIT_USAGE */
  		KASSERT(cf->cf_fstate == FSTATE_NOTFOUND);
  		cf->cf_fstate = FSTATE_FOUND;
  	}
  
  	/* compute length of name and decimal expansion of unit number */
  	lname = strlen(cd->cd_name);
--- 465,482 ----
  		 */
  	} else {
  		myunit = cf->cf_unit;
! 		KASSERT(cf->cf_fstate == FSTATE_NOTFOUND);
! 		cf->cf_fstate = FSTATE_FOUND;
! 	}
! #else
  	myunit = cf->cf_unit;
  	if (cf->cf_fstate == FSTATE_STAR)
  		cf->cf_unit++;
  	else {
  		KASSERT(cf->cf_fstate == FSTATE_NOTFOUND);
  		cf->cf_fstate = FSTATE_FOUND;
  	}
+ #endif /* ! __BROKEN_CONFIG_UNIT_USAGE */
  
  	/* compute length of name and decimal expansion of unit number */
  	lname = strlen(cd->cd_name);
***************
*** 465,473 ****
  	 * Before attaching, clobber any unfound devices that are
  	 * otherwise identical.
  	 */
- #ifdef __BROKEN_CONFIG_UNIT_USAGE
- 	/* bump the unit number on all starred cfdata for this device. */
- #endif /* __BROKEN_CONFIG_UNIT_USAGE */
  	TAILQ_FOREACH(ct, &allcftables, ct_list) {
  		for (cf = ct->ct_cfdata; cf->cf_driver; cf++) {
  			if (cf->cf_driver == cd &&
--- 518,523 ----
***************
*** 475,480 ****
--- 525,534 ----
  				if (cf->cf_fstate == FSTATE_NOTFOUND)
  					cf->cf_fstate = FSTATE_FOUND;
  #ifdef __BROKEN_CONFIG_UNIT_USAGE
+ 				/*
+ 				 * Bump the unit number on all starred cfdata
+ 				 * entries for this device.
+ 				 */
  				if (cf->cf_fstate == FSTATE_STAR)
  					cf->cf_unit++;
  #endif /* __BROKEN_CONFIG_UNIT_USAGE */
***************
*** 570,581 ****
  	/*
  	 * Mark cfdata to show that the unit can be reused, if possible.
  	 */
- #ifdef __BROKEN_CONFIG_UNIT_USAGE
- 	/*
- 	 * Note that we can only re-use a starred unit number if the unit
- 	 * being detached had the last assigned unit number.
- 	 */
- #endif /* __BROKEN_CONFIG_UNIT_USAGE */
  	TAILQ_FOREACH(ct, &allcftables, ct_list) {
  		for (cf = ct->ct_cfdata; cf->cf_driver; cf++) {
  			if (cf->cf_driver == cd) {
--- 624,629 ----
***************
*** 583,588 ****
--- 631,641 ----
  				    cf->cf_unit == dev->dv_unit)
  					cf->cf_fstate = FSTATE_NOTFOUND;
  #ifdef __BROKEN_CONFIG_UNIT_USAGE
+ 				/*
+ 				 * Note that we can only re-use a starred
+ 				 * unit number if the unit being detached
+ 				 * had the last assigned unit number.
+ 				 */
  				if (cf->cf_fstate == FSTATE_STAR &&
  				    cf->cf_unit == dev->dv_unit + 1)
  					cf->cf_unit--;
Index: sys/sys/device.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/device.h,v
retrieving revision 1.50
diff -c -r1.50 device.h
*** sys/sys/device.h	2002/09/23 23:16:07	1.50
--- sys/sys/device.h	2002/09/25 15:13:30
***************
*** 162,167 ****
--- 162,180 ----
      }
  
  /*
+  * Description of a configuration parent.  Each device attachment attaches
+  * to an "interface attribute", which is given in this structure.  The parent
+  * *must* carry this attribute.  Optionally, an individual device instance
+  * may also specify a specific parent device instance.
+  */
+ struct cfparent {
+ 	const char *cfp_iattr;		/* interface attribute */
+ 	const char *cfp_parent;		/* optional specific parent */
+ 	int cfp_unit;			/* optional specific unit
+ 					   (-1 to wildcard) */
+ };
+ 
+ /*
   * Configuration data (i.e., data placed in ioconf.c).
   */
  struct cfdata {
***************
*** 171,177 ****
  	short	cf_fstate;		/* finding state (below) */
  	int	*cf_loc;		/* locators (machine dependent) */
  	int	cf_flags;		/* flags from config */
! 	short	*cf_parents;		/* potential parents */
  	const char **cf_locnames;	/* locator names (machine dependent) */
  };
  #define FSTATE_NOTFOUND		0	/* has not been found */
--- 184,190 ----
  	short	cf_fstate;		/* finding state (below) */
  	int	*cf_loc;		/* locators (machine dependent) */
  	int	cf_flags;		/* flags from config */
! 	const struct cfparent *cf_pspec;/* parent specification */
  	const char **cf_locnames;	/* locator names (machine dependent) */
  };
  #define FSTATE_NOTFOUND		0	/* has not been found */
***************
*** 224,229 ****
--- 237,243 ----
  	const char *cd_name;		/* device name */
  	enum	devclass cd_class;	/* device classification */
  	int	cd_ndevs;		/* size of cd_devs array */
+ 	const char **cd_attrs;		/* attributes for this device */
  };
  
  /*
Index: usr.sbin/config/defs.h
===================================================================
RCS file: /cvsroot/syssrc/usr.sbin/config/defs.h,v
retrieving revision 1.6
diff -c -r1.6 defs.h
*** usr.sbin/config/defs.h	2002/09/11 06:20:09	1.6
--- usr.sbin/config/defs.h	2002/09/25 15:13:31
***************
*** 156,161 ****
--- 156,175 ----
  };
  
  /*
+  * Parent specification.  Multiple device instances may share a
+  * given parent spec.  Parent specs are emitted only if there are
+  * device instances which actually reference it.
+  */
+ struct pspec {
+ 	TAILQ_ENTRY(pspec) p_list;	/* link on parent spec list */
+ 	struct	attr *p_iattr;		/* interface attribute of parent */
+ 	struct	devbase *p_atdev;	/* optional parent device base */
+ 	int	p_atunit;		/* optional parent device unit */
+ 	struct	nvlist *p_devs;		/* children using it */
+ 	int	p_inst;			/* parent spec instance */
+ };
+ 
+ /*
   * The "base" part (struct devbase) of a device ("uba", "sd"; but not
   * "uba2" or "sd0").  It may be found "at" one or more attributes,
   * including "at root" (this is represented by a NULL attribute), as
***************
*** 225,246 ****
  	struct	devi *i_asame;	/* list on same base attachment */
  	struct	devi *i_alias;	/* other aliases of this instance */
  	const char *i_at;	/* where this is "at" (NULL if at root) */
! 	struct	attr *i_atattr;	/* attr that allowed attach */
! 	struct	devbase *i_atdev;/* if "at <devname><unit>", else NULL */
  	struct	deva *i_atdeva;
! 	const char **i_locs;	/* locators (as given by i_atattr) */
! 	int	i_atunit;	/* unit from "at" */
  	int	i_cfflags;	/* flags from config line */
  	int	i_lineno;	/* line # in config, for later errors */
  
  	/* created during packing or ioconf.c generation */
- /* 		i_loclen	   via i_atattr->a_loclen */
  	short	i_collapsed;	/* set => this alias no longer needed */
  	short	i_cfindex;	/* our index in cfdata */
- 	short	i_pvlen;	/* number of parents */
- 	short	i_pvoff;	/* offset in parents.vec */
  	short	i_locoff;	/* offset in locators.vec */
- 	struct	devi **i_parents;/* the parents themselves */
  
  };
  /* special units */
--- 239,254 ----
  	struct	devi *i_asame;	/* list on same base attachment */
  	struct	devi *i_alias;	/* other aliases of this instance */
  	const char *i_at;	/* where this is "at" (NULL if at root) */
! 	struct	pspec *i_pspec;	/* parent spec (NULL if at root) */
  	struct	deva *i_atdeva;
! 	const char **i_locs;	/* locators (as given by pspec's iattr) */
  	int	i_cfflags;	/* flags from config line */
  	int	i_lineno;	/* line # in config, for later errors */
  
  	/* created during packing or ioconf.c generation */
  	short	i_collapsed;	/* set => this alias no longer needed */
  	short	i_cfindex;	/* our index in cfdata */
  	short	i_locoff;	/* offset in locators.vec */
  
  };
  /* special units */
***************
*** 375,381 ****
--- 383,391 ----
  TAILQ_HEAD(, devi)	alldevi,	/* list of all instances */
  			allpseudo;	/* list of all pseudo-devices */
  TAILQ_HEAD(, devm)	alldevms;	/* list of all device-majors */
+ TAILQ_HEAD(, pspec)	allpspecs;	/* list of all parent specs */
  int	ndevi;				/* number of devi's (before packing) */
+ int	npspecs;			/* number of parent specs */
  int	maxbdevm;			/* max number of block major */
  int	maxcdevm;			/* max number of character major */
  int	do_devsw;			/* 0 if pre-devsw config */
***************
*** 390,399 ****
  struct	devi **packed;		/* arrayified table for packed devi's */
  int	npacked;		/* size of packed table, <= ndevi */
  
- struct {			/* pv[] table for config */
- 	short	*vec;
- 	int	used;
- } parents;
  struct {			/* loc[] table for config */
  	const char **vec;
  	int	used;
--- 400,405 ----
Index: usr.sbin/config/main.c
===================================================================
RCS file: /cvsroot/syssrc/usr.sbin/config/main.c,v
retrieving revision 1.70
diff -c -r1.70 main.c
*** usr.sbin/config/main.c	2002/09/11 06:20:09	1.70
--- usr.sbin/config/main.c	2002/09/25 15:13:32
***************
*** 864,871 ****
  static int
  hasparent(struct devi *i)
  {
  	struct nvlist *nv;
- 	int atunit = i->i_atunit;
  
  	/*
  	 * We determine whether or not a device has a parent in in one
--- 864,871 ----
  static int
  hasparent(struct devi *i)
  {
+ 	struct pspec *p;
  	struct nvlist *nv;
  
  	/*
  	 * We determine whether or not a device has a parent in in one
***************
*** 879,896 ****
  	 *	    may be able to attach the device.
  	 */
  
  	/*
  	 * Case (1): A parent was named.  Either it's configured, or not.
  	 */
! 	if (i->i_atdev != NULL)
! 		return (devbase_has_instances(i->i_atdev, atunit));
  
  	/*
  	 * Case (2): No parent was named.  Look for devs that provide the attr.
  	 */
! 	if (i->i_atattr != NULL)
! 		for (nv = i->i_atattr->a_refs; nv != NULL; nv = nv->nv_next)
! 			if (devbase_has_instances(nv->nv_ptr, atunit))
  				return (1);
  	return (0);
  }
--- 879,900 ----
  	 *	    may be able to attach the device.
  	 */
  
+ 	/* No pspec, no parent (root node). */
+ 	if ((p = i->i_pspec) == NULL)
+ 		return (0);
+ 
  	/*
  	 * Case (1): A parent was named.  Either it's configured, or not.
  	 */
! 	if (p->p_atdev != NULL)
! 		return (devbase_has_instances(p->p_atdev, p->p_atunit));
  
  	/*
  	 * Case (2): No parent was named.  Look for devs that provide the attr.
  	 */
! 	if (p->p_iattr != NULL)
! 		for (nv = p->p_iattr->a_refs; nv != NULL; nv = nv->nv_next)
! 			if (devbase_has_instances(nv->nv_ptr, p->p_atunit))
  				return (1);
  	return (0);
  }
***************
*** 944,961 ****
  int
  crosscheck(void)
  {
  	struct devi *i;
  	struct config *cf;
  	int errs;
  
  	errs = 0;
  	TAILQ_FOREACH(i, &alldevi, i_next) {
! 		if (i->i_at == NULL || hasparent(i))
  			continue;
  		xerror(conffile, i->i_lineno,
  		    "%s at %s is orphaned", i->i_name, i->i_at);
  		(void)fprintf(stderr, " (%s %s declared)\n",
! 		    i->i_atunit == WILD ? "nothing matching" : "no",
  		    i->i_at);
  		errs++;
  	}
--- 948,966 ----
  int
  crosscheck(void)
  {
+ 	struct pspec *p;
  	struct devi *i;
  	struct config *cf;
  	int errs;
  
  	errs = 0;
  	TAILQ_FOREACH(i, &alldevi, i_next) {
! 		if ((p = i->i_pspec) == NULL || hasparent(i))
  			continue;
  		xerror(conffile, i->i_lineno,
  		    "%s at %s is orphaned", i->i_name, i->i_at);
  		(void)fprintf(stderr, " (%s %s declared)\n",
! 		    p->p_atunit == WILD ? "nothing matching" : "no",
  		    i->i_at);
  		errs++;
  	}
Index: usr.sbin/config/mkioconf.c
===================================================================
RCS file: /cvsroot/syssrc/usr.sbin/config/mkioconf.c,v
retrieving revision 1.61
diff -c -r1.61 mkioconf.c
*** usr.sbin/config/mkioconf.c	2002/09/11 06:20:09	1.61
--- usr.sbin/config/mkioconf.c	2002/09/25 15:13:32
***************
*** 62,68 ****
  static int emithdr(FILE *);
  static int emitloc(FILE *);
  static int emitpseudo(FILE *);
! static int emitpv(FILE *);
  static int emitroots(FILE *);
  static int emitvfslist(FILE *);
  static int emitname2blk(FILE *);
--- 62,68 ----
  static int emithdr(FILE *);
  static int emitloc(FILE *);
  static int emitpseudo(FILE *);
! static int emitparents(FILE *);
  static int emitroots(FILE *);
  static int emitvfslist(FILE *);
  static int emitname2blk(FILE *);
***************
*** 91,98 ****
  	}
  	v = emithdr(fp);
  	if (v != 0 || emitcfdrivers(fp) || emitexterns(fp) || emitloc(fp) ||
! 	    emitpv(fp) || emitcfdata(fp) || emitroots(fp) || emitpseudo(fp) ||
! 	    emitvfslist(fp) || (do_devsw ? 0 : emitname2blk(fp))) {
  		if (v >= 0)
  			(void)fprintf(stderr,
  			    "config: error writing ioconf.c: %s\n",
--- 91,99 ----
  	}
  	v = emithdr(fp);
  	if (v != 0 || emitcfdrivers(fp) || emitexterns(fp) || emitloc(fp) ||
! 	    emitparents(fp) || emitcfdata(fp) || emitroots(fp) ||
! 	    emitpseudo(fp) || emitvfslist(fp) ||
! 	    (do_devsw ? 0 : emitname2blk(fp))) {
  		if (v >= 0)
  			(void)fprintf(stderr,
  			    "config: error writing ioconf.c: %s\n",
***************
*** 168,187 ****
  emitcfdrivers(FILE *fp)
  {
  	struct devbase *d;
  
  	NEWLINE;
  	TAILQ_FOREACH(d, &allbases, d_next) {
  		if (!devbase_has_instances(d, WILD))
  			continue;
  		if (fprintf(fp, "struct cfdriver %s_cd = {\n",
  			    d->d_name) < 0)
  			return (1);
! 		if (fprintf(fp, "\tNULL, \"%s\", %s\n",
  			    d->d_name, d->d_classattr != NULL ?
  			    d->d_classattr->a_devclass : "DV_DULL") < 0)
  			return (1);
! 		if (fprintf(fp, "};\n\n") < 0)
  			return (1);
  	}
  	return (0);
  }
--- 169,210 ----
  emitcfdrivers(FILE *fp)
  {
  	struct devbase *d;
+ 	struct nvlist *nv;
+ 	struct attr *a;
+ 	int has_iattrs;
  
  	NEWLINE;
  	TAILQ_FOREACH(d, &allbases, d_next) {
  		if (!devbase_has_instances(d, WILD))
  			continue;
+ 		has_iattrs = 0;
+ 		for (nv = d->d_attrs; nv != NULL; nv = nv->nv_next) {
+ 			a = nv->nv_ptr;
+ 			if (a->a_iattr == 0)
+ 				continue;
+ 			if (has_iattrs == 0 &&
+ 			    fprintf(fp, "static const char *%s_attrs[] = { ",
+ 			    	    d->d_name) < 0)
+ 				return (1);
+ 			has_iattrs = 1;
+ 			if (fprintf(fp, "\"%s\", ", a->a_name) < 0)
+ 				return (1);
+ 		}
+ 		if (has_iattrs && fprintf(fp, "NULL };\n") < 0)
+ 			return (1);
  		if (fprintf(fp, "struct cfdriver %s_cd = {\n",
  			    d->d_name) < 0)
  			return (1);
! 		if (fprintf(fp, "\tNULL, \"%s\", %s, 0, ",
  			    d->d_name, d->d_classattr != NULL ?
  			    d->d_classattr->a_devclass : "DV_DULL") < 0)
+ 			return (1);
+ 		if (has_iattrs && fprintf(fp, "%s_attrs", d->d_name) < 0)
  			return (1);
! 		else if (has_iattrs == 0 && fprintf(fp, "NULL") < 0)
  			return (1);
+ 		if (fprintf(fp, "\n};\n\n") < 0)
+ 			return (1);
  	}
  	return (0);
  }
***************
*** 261,283 ****
  }
  
  /*
!  * Emit global parents-vector.
   */
  static int
! emitpv(FILE *fp)
  {
! 	int i;
  
! 	if (parents.used == 0)
! 		return (0);
! 
! 	if (fprintf(fp, "\n/* parent vectors */\n\
! static short pv[%d] = {", parents.used) < 0)
! 		return (1);
! 	for (i = 0; i < parents.used; i++)
! 		if (fprintf(fp, "%s%d,", SEP(i, 16), parents.vec[i]) < 0)
  			return (1);
! 	return (fprintf(fp, "\n};\n") < 0);
  }
  
  /*
--- 284,317 ----
  }
  
  /*
!  * Emit static parent data.
   */
  static int
! emitparents(FILE *fp)
  {
! 	struct pspec *p;
  
! 	NEWLINE;
! 	TAILQ_FOREACH(p, &allpspecs, p_list) {
! 		if (p->p_devs == NULL)
! 			continue;
! 		if (fprintf(fp,
! "static const struct cfparent pspec%d = {\n", p->p_inst) < 0)
  			return (1);
! 		if (fprintf(fp, "\t\"%s\", ", p->p_iattr->a_name) < 0)
! 			return (1);
! 		if (p->p_atdev != NULL) {
! 			if (fprintf(fp, "\"%s\", %d", p->p_atdev->d_name,
! 				    p->p_atunit == WILD ? -1
! 				    			: p->p_atunit) < 0)
! 				return (1);
! 		} else if (fprintf(fp, "NULL, 0") < 0)
! 			return (1);
! 		if (fprintf(fp, "\n};\n") < 0)
! 			return (1);
! 	}
! 
! 	return (0);
  }
  
  /*
***************
*** 286,292 ****
  static int
  emitcfdata(FILE *fp)
  {
! 	struct devi **p, *i, **par;
  	int unit, v;
  	const char *state, *basename, *attachment;
  	struct nvlist *nv;
--- 320,327 ----
  static int
  emitcfdata(FILE *fp)
  {
! 	struct devi **p, *i;
! 	struct pspec *ps;
  	int unit, v;
  	const char *state, *basename, *attachment;
  	struct nvlist *nv;
***************
*** 300,332 ****
  #define STAR FSTATE_STAR\n\
  \n\
  struct cfdata cfdata[] = {\n\
!     /* attachment       driver        unit state loc   flags parents\n\
         locnames */\n") < 0)
  		return (1);
  	for (p = packed; (i = *p) != NULL; p++) {
  		/* the description */
  		if (fprintf(fp, "/*%3d: %s at ", i->i_cfindex, i->i_name) < 0)
  			return (1);
! 		par = i->i_parents;
! 		for (v = 0; v < i->i_pvlen; v++)
! 			if (fprintf(fp, "%s%s", v == 0 ? "" : "|",
! 			    i->i_parents[v]->i_name) < 0)
! 				return (1);
! 		if (v == 0 && fputs("root", fp) < 0)
! 			return (1);
! 		a = i->i_atattr;
! 		for (nv = a->a_locs, v = 0; nv != NULL; nv = nv->nv_next, v++) {
! 			if (ARRNAME(nv->nv_name, lastname)) {
! 				if (fprintf(fp, " %s %s",
! 				    nv->nv_name, i->i_locs[v]) < 0)
  					return (1);
  			} else {
! 				if (fprintf(fp, " %s %s",
! 					    nv->nv_name, i->i_locs[v]) < 0)
  					return (1);
- 				lastname = nv->nv_name;
  			}
! 		}
  		if (fputs(" */\n", fp) < 0)
  			return (-1);
  
--- 335,379 ----
  #define STAR FSTATE_STAR\n\
  \n\
  struct cfdata cfdata[] = {\n\
!     /* attachment       driver        unit state loc   flags pspec\n\
         locnames */\n") < 0)
  		return (1);
  	for (p = packed; (i = *p) != NULL; p++) {
  		/* the description */
  		if (fprintf(fp, "/*%3d: %s at ", i->i_cfindex, i->i_name) < 0)
  			return (1);
! 		if ((ps = i->i_pspec) != NULL) {
! 			if (ps->p_atdev != NULL &&
! 			    ps->p_atunit != WILD) {
! 				if (fprintf(fp, "%s%d", ps->p_atdev->d_name,
! 					    ps->p_atunit) < 0)
! 					return (1);
! 			} else if (ps->p_atdev != NULL) {
! 				if (fprintf(fp, "%s?", ps->p_atdev->d_name) < 0)
  					return (1);
  			} else {
! 				if (fprintf(fp, "%s?", ps->p_iattr->a_name) < 0)
  					return (1);
  			}
! 
! 			a = ps->p_iattr;
! 			for (nv = a->a_locs, v = 0; nv != NULL;
! 			     nv = nv->nv_next, v++) {
! 				if (ARRNAME(nv->nv_name, lastname)) {
! 					if (fprintf(fp, " %s %s",
! 					    nv->nv_name, i->i_locs[v]) < 0)
! 						return (1);
! 				} else {
! 					if (fprintf(fp, " %s %s",
! 						    nv->nv_name,
! 						    i->i_locs[v]) < 0)
! 						return (1);
! 					lastname = nv->nv_name;
! 				}
! 			}
! 		} else if (fputs("root", fp) < 0)
! 			return (1);
! 
  		if (fputs(" */\n", fp) < 0)
  			return (-1);
  
***************
*** 345,357 ****
  			loc = locbuf;
  		} else
  			loc = "loc";
! 		if (fprintf(fp, "\
!     {&%s_ca,%s&%s_cd,%s%2d, %s, %7s, %#6x, pv+%2d,\n\
!      %scf_locnames},\n",
! 		    attachment, strlen(attachment) < 6 ? "\t\t" : "\t",
! 		    basename, strlen(basename) < 3 ? "\t\t" : "\t", unit,
! 		    state, loc, i->i_cfflags, i->i_pvoff,
! 		    a->a_locs ? a->a_name : "null") < 0)
  			return (1);
  	}
  	return (fputs("    {0}\n};\n", fp) < 0);
--- 392,411 ----
  			loc = locbuf;
  		} else
  			loc = "loc";
! 		if (fprintf(fp, "    {&%s_ca,%s&%s_cd,%s%2d, %s, %7s, %#6x, ",
! 			    attachment, strlen(attachment) < 6 ? "\t\t"
! 			    				       : "\t",
! 			    basename, strlen(basename) < 3 ? "\t\t"
! 			    				   : "\t", unit,
! 			    state, loc, i->i_cfflags) < 0)
! 			return (1);
! 		if (ps != NULL) {
! 			if (fprintf(fp, "&pspec%d,\n", ps->p_inst) < 0)
! 				return (1);
! 		} else if (fputs("NULL,", fp) < 0)
! 			return (1);
! 		if (fprintf(fp, "     %scf_locnames},\n",
! 			    a->a_locs ? a->a_name : "null") < 0)
  			return (1);
  	}
  	return (fputs("    {0}\n};\n", fp) < 0);
Index: usr.sbin/config/pack.c
===================================================================
RCS file: /cvsroot/syssrc/usr.sbin/config/pack.c,v
retrieving revision 1.12
diff -c -r1.12 pack.c
*** usr.sbin/config/pack.c	2002/06/05 10:56:19	1.12
--- usr.sbin/config/pack.c	2002/09/25 15:13:32
***************
*** 52,64 ****
  /*
   * Packing.  We have three separate kinds of packing here.
   *
!  * First, we pack device instances, to collapse things like
   *
-  *	uba0 at sbi0 nexus ?
-  *	uba0 at bi0 nexus ?
-  *
-  * into a single instance that is "at sbi0 or bi0".
-  *
   * Second, we pack locators.  Given something like
   *
   *	hp0 at mba0 drive 0
--- 52,59 ----
  /*
   * Packing.  We have three separate kinds of packing here.
   *
!  * First, we pack device instances which have identical parent specs.
   *
   * Second, we pack locators.  Given something like
   *
   *	hp0 at mba0 drive 0
***************
*** 72,87 ****
   * locators whose value is 0 and three whose value is -1.  Rather than
   * emitting six integers, we emit just two.
   *
-  * Finally, we pack parent vectors.  This is very much like packing
-  * locators.  Unlike locators, however, parent vectors are always
-  * terminated by -1 (rather like the way C strings always end with
-  * a NUL).
-  *
   * When packing locators, we would like to find sequences such as
   *	{1 2 3} {2 3 4} {3} {4 5}
   * and turn this into the flat sequence {1 2 3 4 5}, with each subsequence
   * given by the appropriate offset (here 0, 1, 2, and 3 respectively).
-  * When we pack parent vectors, overlap of this sort is impossible.
   * Non-overlapping packing is much easier, and so we use that here
   * and miss out on the chance to squeeze the locator sequence optimally.
   * (So it goes.)
--- 67,76 ----
***************
*** 99,165 ****
  
  static struct tails *tails[TAILHSIZE];
  static int locspace;
- static int pvecspace;
- static int longest_pvec;
  
  static void packdevi(void);
  static void packlocs(void);
- static void packpvec(void);
  
- static void addparents(struct devi *src, struct devi *dst);
- static int nparents(struct devi **, struct devbase *, int);
  static int sameas(struct devi *, struct devi *);
  static int findvec(const void *, int, int, vec_cmp_func, int);
  static int samelocs(const void *, int, int);
  static int addlocs(const char **, int);
  static int loclencmp(const void *, const void *);
- static int samepv(const void *, int, int);
- static int addpv(short *, int);
- static int pvlencmp(const void *, const void *);
  static void resettails(void);
  
  void
  pack(void)
  {
  	struct devi *i;
- 	int n;
  
  	/* Pack instances and make parent vectors. */
  	packdevi();
  
  	/*
  	 * Now that we know what we have, find upper limits on space
! 	 * needed for the loc[] and pv[] tables, and find the longest
! 	 * single pvec.  The loc and pv table sizes are bounded by
  	 * what we would get if no packing occurred.
  	 */
! 	locspace = pvecspace = 0;
  	TAILQ_FOREACH(i, &alldevi, i_next) {
  		if (i->i_collapsed)
  			continue;
! 		locspace += i->i_atattr->a_loclen;
! 		n = i->i_pvlen + 1;
! 		if (n > longest_pvec)
! 			longest_pvec = n;
! 		pvecspace += n;
  	}
  
  	/* Allocate and pack loc[]. */
  	locators.vec = emalloc(locspace * sizeof(*locators.vec));
  	locators.used = 0;
  	packlocs();
- 
- 	/* Allocate and pack pv[]. */
- 	parents.vec = emalloc(pvecspace * sizeof(*parents.vec));
- 	parents.used = 0;
- 	packpvec();
  }
  
  /*
!  * Pack instances together wherever possible.  When everything is
!  * packed, go back and set up the parents for each.  We must do this
!  * on a second pass because during the first one, we do not know which,
!  * if any, of the parents will collapse during packing.
   */
  void
  packdevi(void)
--- 88,135 ----
  
  static struct tails *tails[TAILHSIZE];
  static int locspace;
  
  static void packdevi(void);
  static void packlocs(void);
  
  static int sameas(struct devi *, struct devi *);
  static int findvec(const void *, int, int, vec_cmp_func, int);
  static int samelocs(const void *, int, int);
  static int addlocs(const char **, int);
  static int loclencmp(const void *, const void *);
  static void resettails(void);
  
  void
  pack(void)
  {
+ 	struct pspec *p;
  	struct devi *i;
  
  	/* Pack instances and make parent vectors. */
  	packdevi();
  
  	/*
  	 * Now that we know what we have, find upper limits on space
! 	 * needed for the loc[] table.  The loc table size is bounded by
  	 * what we would get if no packing occurred.
  	 */
! 	locspace = 0;
  	TAILQ_FOREACH(i, &alldevi, i_next) {
  		if (i->i_collapsed)
  			continue;
! 		if ((p = i->i_pspec) == NULL)
! 			continue;
! 		locspace += p->p_iattr->a_loclen;
  	}
  
  	/* Allocate and pack loc[]. */
  	locators.vec = emalloc(locspace * sizeof(*locators.vec));
  	locators.used = 0;
  	packlocs();
  }
  
  /*
!  * Pack device instances together wherever possible.
   */
  void
  packdevi(void)
***************
*** 211,218 ****
  		for (i = d->d_ihead; i != NULL; i = i->i_bsame) {
  			m = n;
  			for (l = i; l != NULL; l = l->i_alias) {
- 				l->i_pvlen = 0;
- 				l->i_pvoff = -1;
  				l->i_locoff = -1;
  				/* try to find an equivalent for l */
  				for (j = m; j < n; j++) {
--- 181,186 ----
***************
*** 226,233 ****
  				/* could not find a suitable alias */
  				l->i_collapsed = 0;
  				l->i_cfindex = n;
- 				l->i_parents = emalloc(sizeof(*l->i_parents));
- 				l->i_parents[0] = NULL;
  				packed[n++] = l;
   nextalias:;
  			}
--- 194,199 ----
***************
*** 235,365 ****
  	}
  	npacked = n;
  	packed[n] = NULL;
- 	TAILQ_FOREACH(i, &alldevi, i_next) {
- 		addparents(i, packed[i->i_cfindex]);
- 	}
  }
  
  /*
   * Return true if two aliases are "the same".  In this case, they need
!  * to attach via the same attribute, have the same config flags, and
!  * have the same locators.
   */
  static int
  sameas(struct devi *i1, struct devi *i2)
  {
  	const char **p1, **p2;
  
! 	if (i1->i_atattr != i2->i_atattr)
  		return (0);
  	if (i1->i_cfflags != i2->i_cfflags)
  		return (0);
  	for (p1 = i1->i_locs, p2 = i2->i_locs; *p1 == *p2; p2++)
  		if (*p1++ == 0)
  			return (1);
! 	return 0;
  }
  
- /*
-  * Add the parents associated with "src" to the (presumably uncollapsed)
-  * instance "dst".
-  */
  static void
- addparents(struct devi *src, struct devi *dst)
- {
- 	struct nvlist *nv;
- 	struct devi *i, **p, **q;
- 	int j, n, old, new, ndup;
- 
- 	if (dst->i_collapsed)
- 		panic("addparents() i_collapsed");
- 
- 	/* Collect up list of parents to add. */
- 	if (src->i_at == NULL)	/* none, 'cuz "at root" */
- 		return;
- 	if (src->i_atdev != NULL) {
- 		n = nparents(NULL, src->i_atdev, src->i_atunit);
- 		p = emalloc(n * sizeof *p);
- 		if (n == 0)
- 			return;
- 		(void)nparents(p, src->i_atdev, src->i_atunit);
- 	} else {
- 		n = 0;
- 		for (nv = src->i_atattr->a_refs; nv != NULL; nv = nv->nv_next)
- 			n += nparents(NULL, nv->nv_ptr, src->i_atunit);
- 		if (n == 0)
- 			return;
- 		p = emalloc(n * sizeof *p);
- 		n = 0;
- 		for (nv = src->i_atattr->a_refs; nv != NULL; nv = nv->nv_next)
- 			n += nparents(p + n, nv->nv_ptr, src->i_atunit);
- 	}
- 	/* Now elide duplicates. */
- 	ndup = 0;
- 	for (j = 0; j < n; j++) {
- 		i = p[j];
- 		for (q = dst->i_parents; *q != NULL; q++) {
- 			if (*q == i) {
- 				ndup++;
- 				p[j] = NULL;
- 				break;
- 			}
- 		}
- 	}
- 	/* Finally, add all the non-duplicates. */
- 	old = dst->i_pvlen;
- 	new = old + (n - ndup);
- 	if (old > new)
- 		panic("addparents() old > new");
- 	if (old == new) {
- 		free(p);
- 		return;
- 	}
- 	dst->i_parents = q = erealloc(dst->i_parents, (new + 1) * sizeof(*q));
- 	dst->i_pvlen = new;
- 	q[new] = NULL;
- 	q += old;
- 	for (j = 0; j < n; j++)
- 		if (p[j] != NULL)
- 			*q++ = p[j];
- 	free(p);
- }
- 
- /*
-  * Count up parents, and optionally store pointers to each.
-  */
- static int
- nparents(struct devi **p, struct devbase *dev, int unit)
- {
- 	struct devi *i, *l;
- 	int n;
- 
- 	n = 0;
- 	/* for each instance ... */
- 	for (i = dev->d_ihead; i != NULL; i = i->i_bsame) {
- 		/* ... take each un-collapsed alias */
- 		for (l = i; l != NULL; l = l->i_alias) {
- 			if (!l->i_collapsed &&
- 			    (unit == WILD || unit == l->i_unit)) {
- 				if (p != NULL)
- 					*p++ = l;
- 				n++;
- 			}
- 		}
- 	}
- 	return (n);
- }
- 
- static void
  packlocs(void)
  {
  	struct devi **p, *i;
  	int l,o;
  	extern int Pflag;
  
  	qsort(packed, npacked, sizeof *packed, loclencmp);
  	for (p = packed; (i = *p) != NULL; p++) {
! 		if ((l = i->i_atattr->a_loclen) > 0) {
  			if (Pflag) {
  				o = findvec(i->i_locs, 
  				    LOCHASH(i->i_locs[l - 1]), l,
--- 201,240 ----
  	}
  	npacked = n;
  	packed[n] = NULL;
  }
  
  /*
   * Return true if two aliases are "the same".  In this case, they need
!  * to have the same parent spec, have the same config flags, and have
!  * the same locators.
   */
  static int
  sameas(struct devi *i1, struct devi *i2)
  {
  	const char **p1, **p2;
  
! 	if (i1->i_pspec != i2->i_pspec)
  		return (0);
  	if (i1->i_cfflags != i2->i_cfflags)
  		return (0);
  	for (p1 = i1->i_locs, p2 = i2->i_locs; *p1 == *p2; p2++)
  		if (*p1++ == 0)
  			return (1);
! 	return (0);
  }
  
  static void
  packlocs(void)
  {
+ 	struct pspec *ps;
  	struct devi **p, *i;
  	int l,o;
  	extern int Pflag;
  
  	qsort(packed, npacked, sizeof *packed, loclencmp);
  	for (p = packed; (i = *p) != NULL; p++) {
! 		if ((ps = i->i_pspec) != NULL &&
! 		    (l = ps->p_iattr->a_loclen) > 0) {
  			if (Pflag) {
  				o = findvec(i->i_locs, 
  				    LOCHASH(i->i_locs[l - 1]), l,
***************
*** 374,404 ****
  	resettails();
  }
  
- static void
- packpvec(void)
- {
- 	struct devi **p, *i, **par;
- 	int l, v, o;
- 	short *vec;
- 
- 	vec = emalloc(longest_pvec * sizeof(*vec));
- 	qsort(packed, npacked, sizeof *packed, pvlencmp);
- 	for (p = packed; (i = *p) != NULL; p++) {
- 		l = i->i_pvlen;
- if (l > longest_pvec) panic("packpvec");
- 		par = i->i_parents;
- 		for (v = 0; v < l; v++)
- 			vec[v] = par[v]->i_cfindex;
- 		if (l == 0 ||
- 		    (o = findvec(vec, PVHASH(vec[l - 1]), l,
- 			    samepv, parents.used)) < 0)
- 		    	o = addpv(vec, l);
- 		i->i_pvoff = o;
- 	}
- 	free(vec);
- 	resettails();
- }
- 
  /*
   * Return the index at which the given vector already exists, or -1
   * if it is not anywhere in the current set.  If we return -1, we assume
--- 249,254 ----
***************
*** 462,529 ****
  static int
  loclencmp(const void *a, const void *b)
  {
  	int l1, l2;
- 
- 	l1 = (*(struct devi **)a)->i_atattr->a_loclen;
- 	l2 = (*(struct devi **)b)->i_atattr->a_loclen;
- 	return (l2 - l1);
- }
- 
- /*
-  * Comparison function for parent vectors.
-  */
- static int
- samepv(const void *ptr, int off, int len)
- {
- 	short *p, *q;
- 
- 	for (p = &parents.vec[off], q = (short *)ptr; --len >= 0;)
- 		if (*p++ != *q++)
- 			return (0);	/* different */
- 	return (1);			/* same */
- }
  
! /*
!  * Add the given parent vectors at the end of the global pv[] table.
!  */
! static int
! addpv(short *pv, int len)
! {
! 	short *p;
! 	int ret;
! 	static int firstend = -1;
! 
! 	/*
! 	 * If the vector is empty, reuse the first -1.  It will be
! 	 * there if there are any nonempty vectors at all, since we
! 	 * do the longest first.  If there are no nonempty vectors,
! 	 * something is probably wrong, but we will ignore that here.
! 	 */
! 	if (len == 0 && firstend >= 0)
! 		return (firstend);
! 	len++;			/* account for trailing -1 */
! 	ret = parents.used;
! 	if ((parents.used = ret + len) > pvecspace)
! 		panic("addpv: overrun");
! 	for (p = &parents.vec[ret]; --len > 0;)
! 		*p++ = *pv++;
! 	*p = -1;
! 	if (firstend < 0)
! 		firstend = parents.used - 1;
! 	return (ret);
! }
  
! /*
!  * Comparison function for qsort-by-parent-vector-length, longest first.
!  * We rashly assume that subtraction of these lengths does not overflow.
!  */
! static int
! pvlencmp(const void *a, const void *b)
! {
! 	int l1, l2;
  
- 	l1 = (*(struct devi **)a)->i_pvlen;
- 	l2 = (*(struct devi **)b)->i_pvlen;
  	return (l2 - l1);
  }
  
--- 312,326 ----
  static int
  loclencmp(const void *a, const void *b)
  {
+ 	struct pspec *p1, *p2;
  	int l1, l2;
  
! 	p1 = (*(struct devi **)a)->i_pspec;
! 	l1 = p1 != NULL ? p1->p_iattr->a_loclen : 0;
  
! 	p2 = (*(struct devi **)b)->i_pspec;
! 	l2 = p2 != NULL ? p2->p_iattr->a_loclen : 0;
  
  	return (l2 - l1);
  }
  
Index: usr.sbin/config/sem.c
===================================================================
RCS file: /cvsroot/syssrc/usr.sbin/config/sem.c,v
retrieving revision 1.32
diff -c -r1.32 sem.c
*** usr.sbin/config/sem.c	2002/09/11 06:20:10	1.32
--- usr.sbin/config/sem.c	2002/09/25 15:13:33
***************
*** 73,78 ****
--- 73,79 ----
  static struct nvlist *addtoattr(struct nvlist *, struct devbase *);
  static int resolve(struct nvlist **, const char *, const char *,
  		   struct nvlist *, int);
+ static struct pspec *getpspec(struct attr *, struct devbase *, int);
  static struct devi *newdevi(const char *, int, struct devbase *d);
  static struct devi *getdevi(const char *);
  static const char *concat(const char *, int);
***************
*** 98,103 ****
--- 99,106 ----
  
  	TAILQ_INIT(&alldevas);
  
+ 	TAILQ_INIT(&allpspecs);
+ 
  	cfhashtab = ht_new();
  	TAILQ_INIT(&allcf);
  
***************
*** 802,809 ****
  	i->i_asame = NULL;
  	i->i_alias = NULL;
  	i->i_at = NULL;
! 	i->i_atattr = NULL;
! 	i->i_atdev = NULL;
  	i->i_atdeva = NULL;
  	i->i_locs = NULL;
  	i->i_cfflags = 0;
--- 805,811 ----
  	i->i_asame = NULL;
  	i->i_alias = NULL;
  	i->i_at = NULL;
! 	i->i_pspec = NULL;
  	i->i_atdeva = NULL;
  	i->i_locs = NULL;
  	i->i_cfflags = 0;
***************
*** 821,826 ****
--- 823,829 ----
  adddev(const char *name, const char *at, struct nvlist *loclist, int flags)
  {
  	struct devi *i;		/* the new instance */
+ 	struct pspec *p;	/* and its pspec */
  	struct attr *attr;	/* attribute that allows attach */
  	struct devbase *ib;	/* i->i_base */
  	struct devbase *ab;	/* not NULL => at another dev */
***************
*** 835,840 ****
--- 838,844 ----
  	iba = NULL;
  	if (at == NULL) {
  		/* "at root" */
+ 		p = NULL;
  		if ((i = getdevi(name)) == NULL)
  			goto bad;
  		/*
***************
*** 863,869 ****
  		if ((i = getdevi(name)) == NULL)
  			goto bad;
  		ib = i->i_base;
- 		cp = intern(atbuf);
  
  		/*
  		 * Devices can attach to two types of things: Attributes,
--- 867,872 ----
***************
*** 884,889 ****
--- 887,893 ----
  		 *     but is actually named in the config file, we still
  		 *     have to remember its devbase.
  		 */
+ 		cp = intern(atbuf);
  
  		/* Figure out parent's devbase, to satisfy case (3). */
  		ab = ht_lookup(devbasetab, cp);
***************
*** 922,927 ****
--- 926,938 ----
  		goto bad;
  
   findattachment:
+ 		/*
+ 		 * Find the parent spec.  If a matching one has not yet been
+ 		 * created, create one.
+ 		 */
+ 		p = getpspec(attr, ab, atunit);
+ 		p->p_devs = newnv(NULL, NULL, i, 0, p->p_devs);
+ 
  		/* find out which attachment it uses */
  		hit = 0;
  		for (iba = ib->d_ahead; iba != NULL; iba = iba->d_bsame)
***************
*** 935,944 ****
  	if ((i->i_locs = fixloc(name, attr, loclist)) == NULL)
  		goto bad;
  	i->i_at = at;
! 	i->i_atattr = attr;
! 	i->i_atdev = ab;
  	i->i_atdeva = iba;
- 	i->i_atunit = atunit;
  	i->i_cfflags = flags;
  
  	*iba->d_ipp = i;
--- 946,953 ----
  	if ((i->i_locs = fixloc(name, attr, loclist)) == NULL)
  		goto bad;
  	i->i_at = at;
! 	i->i_pspec = p;
  	i->i_atdeva = iba;
  	i->i_cfflags = flags;
  
  	*iba->d_ipp = i;
***************
*** 1086,1091 ****
--- 1095,1128 ----
  
  	TAILQ_FOREACH(i, &allpseudo, i_next)
  		selectbase(i->i_base, NULL);
+ }
+ 
+ /*
+  * Look up a parent spec, creating a new one if it does not exist.
+  */
+ static struct pspec *
+ getpspec(struct attr *attr, struct devbase *ab, int atunit)
+ {
+ 	struct pspec *p;
+ 
+ 	TAILQ_FOREACH(p, &allpspecs, p_list) {
+ 		if (p->p_iattr == attr &&
+ 		    p->p_atdev == ab &&
+ 		    p->p_atunit == atunit)
+ 			return (p);
+ 	}
+ 
+ 	p = emalloc(sizeof(*p));
+ 	memset(p, 0, sizeof(*p));
+ 
+ 	p->p_iattr = attr;
+ 	p->p_atdev = ab;
+ 	p->p_atunit = atunit;
+ 	p->p_inst = npspecs++;
+ 
+ 	TAILQ_INSERT_TAIL(&allpspecs, p, p_list);
+ 
+ 	return (p);
  }
  
  /*

--ZGiS0Q5IWpPtfppv--