Subject: A potential step towards modularisation
To: None <tech-kern@netbsd.org>
From: Quentin Garnier <cube@NetBSD.org>
List: tech-kern
Date: 02/07/2004 01:10:35
This is a multi-part message in MIME format.

--Multipart=_Sat__7_Feb_2004_01_10_35_+0100_WzxD4GVPFC9eC+eU
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit

Hi folks,

First, please don't disregard this post just because it is about modules.
By no mean I want to drop the current practice of having a monolithic
kernel.  I would never compile with options LKM most of my systems.  But
sometimes, modularity is useful.

The attached diff is only against config(8).  Its aim is to provide ways
to compile modules out of the standard kernel compilation scheme.

Currently, the LKM that are part of the NetBSD distribution are built as
part of userland, whereas it should be part of kernel compilation.  It is
even more true now that we have Jaromir's versioning framework for LKMs:
if you rebuild your kernel, you have to rebuild your modules.  I value the
security belt enough, but it can be annoying.

What I propose here is a slight change of config(8) grammar to allow a
property of modularity accross the various kernel and configuration
components.  To declare an attribute or a device as _potentially_ (i.e.,
if and only if the user's config file says to compile it as module), it
should be prefixed with 'modular'.  Also, an attachment that creates an
attribute (with <attr>) can be made modular by using 'with module <attr>'
syntax.

Then, the user can make a device compiled as a module by prefixing the
instance line with 'module'.

Example for ex(4):

> conf/files <
modular device ex: arp, ether, ifnet, mii, mii_bitbang
file       dev/ic/elinkxl.c                ex

> dev/pci/files.pci <
attach ex at pci with module ex_pci
file       dev/pci/if_ex_pci.c             ex_pci

> dev/cardbus/files.cardbus <
attach ex at carbus with module ex_cardbus
file       dev/cardbus/if_ex_cardbus.c     ex_cardbus

The user will have several choices:

Case 1, good'old GENERIC_LAPTOP:

ex* at pci? device ? function ?
ex* at cardbus? cardslot ?

===> No module is created

Case 2:

module ex* at pci? device ? function ?
module ex* at cardbus? cardslot ?

===> 3 modules are created:  ex.ko, ex_pci.ko and ex_cardbus.ko.

Case 3:

module ex* at pci? device ? function ?
ex* at cardbus? cardslot ?

===> only one module is created, since attribute ex_cardbus is static and
     depends on attribute ex.  The module name is ex_pci.ko.

Also, you can use 'modular define' to define modular attributes that are
not directly linked to a device, such as 'ac97', 'aurateconv'...  Then
those can be built as modules if whatever needs them is a module.

It is also possible to include files for an attribute only if it is
compiled as module.  This would be really useful since most of our devices
drivers don't provide detach() function, which are usually required for a
LKM to work properly (unless it is specifically made not-unloadable). 
detach() functions have little use in a monolithic kernel and it is
perfectly understandable to leave them out.  Thus a way is provided to
include ``modular'' files (using the 'modular' keyword as a flag at the
same level as need-count and need-flag).

config(8) then adds all the modules targets at the %MODULES token inside
the template Makefile.  Makefile.kern.inc should define a few variable to
compile the modules, but it is not yet the most important point.

This patch certainly does not provide modularisation for NetBSD.  It
merely makes it possible to do this a near future.  Of course, our current
LKM implementation is not really adapted to modularisation: no
dependencies (mmm, unloading in the wrong order), no in-kernel linker that
would help with module classes and resource allocation, no way to easily
add a device driver for a bus and have it rescanned (e.g., PCI), etc.  But
this config(8) patch neither prevents nor restricts implementation of
solutions for those issues.

I'm posting this now because I feel I've worked on it long enough to have
a clear idea of what it actually provides and what would be the needs for
modularisation, but there's still some work to make it complete: for
example, file-systems are treated as regular options, but get a different
syntax so config(8) can know that at least one is compiled and barf if
it's not the case.  Since the patch does not provide a way to declare
modular options, it is not possible yet to compile module for
file-systems.

Comments?  Flames?

-- 
Quentin Garnier - cube@NetBSD.org
The NetBSD Project - http://www.NetBSD.org/

--Multipart=_Sat__7_Feb_2004_01_10_35_+0100_WzxD4GVPFC9eC+eU
Content-Type: text/plain;
 name="config.diff"
Content-Disposition: attachment;
 filename="config.diff"
Content-Transfer-Encoding: 7bit

Index: Makefile
===================================================================
RCS file: /pub/NetBSD-CVS/src/usr.sbin/config/Makefile,v
retrieving revision 1.27
diff -u -r1.27 Makefile
--- Makefile	2003/05/18 07:57:38	1.27
+++ Makefile	2004/02/06 23:09:21
@@ -4,12 +4,13 @@
 PROG=	config
 BINDIR=	/usr/sbin
 SRCS=	files.c gram.y hash.c main.c mkdevsw.c mkheaders.c mkioconf.c \
-	mkmakefile.c mkswap.c pack.c scan.l sem.c util.c
+	mkmakefile.c mkmodules.c mkswap.c pack.c scan.l sem.c util.c
 YFLAGS=
 CPPFLAGS+=-I${.CURDIR} -I.
 CLEANFILES+=gram.h
 
 CWARNFLAGS+=-Wno-format-y2k
+CFLAGS+=-g
 
 MAN=	config.8
 
Index: defs.h
===================================================================
RCS file: /pub/NetBSD-CVS/src/usr.sbin/config/defs.h,v
retrieving revision 1.13
diff -u -r1.13 defs.h
--- defs.h	2003/09/03 18:56:37	1.13
+++ defs.h	2004/02/06 23:09:21
@@ -151,8 +151,18 @@
 	struct	nvlist *a_refs;		/* parents */
 	struct	nvlist *a_deps;		/* we depend on these other attrs */
 	int	a_expanding;		/* to detect cycles in attr graph */
+	int	a_modprops;		/* modularity */
 };
 
+#define	MOD_STATIC	0x01		/* this element will be statically built */
+#define	MOD_MODABLE	0x02		/* this element can be built as a module */
+#define	MOD_MAXNAME	64
+
+#define	CTX_STATIC		0
+#define	CTX_MODULAR		1
+#define	CTX_DEVCLASS		2
+#define	CTX_PSEUDO		4
+
 /*
  * Parent specification.  Multiple device instances may share a
  * given parent spec.  Parent specs are emitted only if there are
@@ -203,6 +213,7 @@
 	struct	deva *d_ahead;		/* first attachment, if any */
 	struct	deva **d_app;		/* used for tacking on attachments */
 	struct	attr *d_classattr;	/* device class attribute (if any) */
+	int	d_modprops;		/* module properties, see struct attr */
 };
 
 struct deva {
@@ -215,6 +226,7 @@
 	struct	nvlist *d_attrs;	/* attributes, if any */
 	struct	devi *d_ihead;		/* first instance, if any */
 	struct	devi **d_ipp;		/* used for tacking on more instances */
+	int	d_modprops;		/* module properties, see struct attr */
 };
 
 /*
@@ -242,6 +254,7 @@
 	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 */
+	int	i_modprops;	/* modularity properties, see struct attr */
 
 	/* created during packing or ioconf.c generation */
 	short	i_collapsed;	/* set => this alias no longer needed */
@@ -285,6 +298,7 @@
 #define	FI_NEEDSCOUNT	0x02	/* needs-count */
 #define	FI_NEEDSFLAG	0x04	/* needs-flag */
 #define	FI_HIDDEN	0x08	/* obscured by other(s), base names overlap */
+#define	FI_MODULAR	0x10	/* only compile if part of a module */
 
 /*
  * Objects and libraries.  This allows precompiled object and library
@@ -304,6 +318,7 @@
 /* flags */
 #define	OI_SEL		0x01	/* selected */
 #define	OI_NEEDSFLAG	0x02	/* needs-flag */
+#define	OI_MODULAR	0x04	/* only compile if part of a module */
 
 #define	FX_ATOM		0	/* atom (in nv_name) */
 #define	FX_NOT		1	/* NOT expr (subexpression in nv_next) */
@@ -333,6 +348,17 @@
 };
 
 /*
+ * Module informations.
+ */
+struct module {
+	const char	*m_name;
+	int		m_count;
+	struct nvlist	*m_filelist;
+	struct nvlist	*m_objlist;
+	struct nvlist	*m_attrlist;
+};
+
+/*
  * Hash tables look up name=value pairs.  The pointer value of the name
  * is assumed to be constant forever; this can be arranged by interning
  * the name.  (This is fairly convenient since our lexer does this for
@@ -363,6 +389,7 @@
 struct	hashtab *devatab;	/* devbase attachment lookup */
 struct	hashtab *devitab;	/* device instance lookup */
 struct	hashtab *selecttab;	/* selects things that are "optional foo" */
+struct	hashtab *modulestab;	/* modular attributes and associated module structs */
 struct	hashtab *needcnttab;	/* retains names marked "needs-count" */
 struct	hashtab *opttab;	/* table of configured options */
 struct	hashtab *fsopttab;	/* table of configured file systems */
@@ -424,12 +451,13 @@
 const char *intern(const char *);
 typedef int (*ht_callback)(const char *, void *, void *);
 int	ht_enumerate(struct hashtab *, ht_callback, void *);
+u_int	ht_count(struct hashtab *);
 
 /* main.c */
 void	addoption(const char *, const char *);
 void	addfsoption(const char *);
 void	addmkoption(const char *, const char *);
-void	deffilesystem(const char *, struct nvlist *);
+void	deffilesystem(const char *, struct nvlist *, int);
 void	defoption(const char *, struct nvlist *, struct nvlist *);
 void	defflag(const char *, struct nvlist *, struct nvlist *);
 void	defparam(const char *, struct nvlist *, struct nvlist *);
@@ -467,6 +495,9 @@
 /* mkswap.c */
 int	mkswap(void);
 
+/* mkmodules.c */
+int	mkmodules(void);
+
 /* pack.c */
 void	pack(void);
 
@@ -497,6 +528,7 @@
 struct nvlist *newnv(const char *, const char *, void *, int, struct nvlist *);
 void	nvfree(struct nvlist *);
 void	nvfreel(struct nvlist *);
+struct nvlist	*nvcat(struct nvlist *, struct nvlist *);
 
 /* liby */
 void	yyerror(const char *);
Index: files.c
===================================================================
RCS file: /pub/NetBSD-CVS/src/usr.sbin/config/files.c,v
retrieving revision 1.22
diff -u -r1.22 files.c
--- files.c	2003/08/07 11:25:15	1.22
+++ files.c	2004/02/06 23:09:21
@@ -61,9 +61,14 @@
 static struct files **unchecked;
 
 static int	checkaux(const char *, void *);
+static int	addmoduleitem(void *, int, struct nvlist *);
+#define	AMI_OBJECT	1
+#define	AMI_FILE	2
+static int	buildmodule(const char *, void *, void *);
 static int	fixcount(const char *, void *);
 static int	fixfsel(const char *, void *);
 static int	fixsel(const char *, void *);
+static int	fixmodules(const char *, void *);
 static int	expr_eval(struct nvlist *,
 		    int (*)(const char *, void *), void *);
 static void	expr_free(struct nvlist *);
@@ -249,17 +254,34 @@
 			continue;
 
 		/* Optional: see if it is to be included. */
+		/* Note: a modular file can't have optx == NULL */
 		if (fi->fi_optx != NULL) {
-			flathead = NULL;
-			flatp = &flathead;
-			sel = expr_eval(fi->fi_optx,
-			    fi->fi_flags & FI_NEEDSCOUNT ? fixcount :
-			    fi->fi_flags & FI_NEEDSFLAG ? fixfsel :
-			    fixsel,
-			    &flatp);
-			fi->fi_optf = flathead;
-			if (!sel)
-				continue;
+			if (!(fi->fi_flags & FI_MODULAR)) {
+				flathead = NULL;
+				flatp = &flathead;
+				sel = expr_eval(fi->fi_optx,
+				    fi->fi_flags & FI_NEEDSCOUNT ? fixcount :
+				    fi->fi_flags & FI_NEEDSFLAG ? fixfsel :
+				    fixsel,
+				    &flatp);
+				fi->fi_optf = flathead;
+			}
+
+			/*
+			 * It won't be included in the monolithic kernel,
+			 * though it might be compiled as part of a module
+			 */
+			if ((fi->fi_flags & FI_MODULAR) || !sel) {
+				flathead = NULL;
+				sel = expr_eval(fi->fi_optx, fixmodules, &flathead);
+				if (sel) {
+					/* Ok, we will produce a module out of this */
+					err = addmoduleitem(fi, AMI_FILE, flathead);
+					fi->fi_flags |= FI_MODULAR;
+				} else
+					/* Not selected */
+					continue;
+			}
 		}
 
 		/* We like this file.  Make sure it generates a unique .o. */
@@ -274,7 +296,7 @@
 				if (ht_replace(basetab, fi->fi_base, fi) != 1)
 					panic("fixfiles ht_replace(%s)",
 					    fi->fi_base);
-				ofi->fi_flags &= ~FI_SEL;
+				ofi->fi_flags &= ~(FI_SEL | FI_MODULAR);
 				ofi->fi_flags |= FI_HIDDEN;
 			} else {
 				xerror(fi->fi_srcfile, fi->fi_srcline,
@@ -286,11 +308,104 @@
 				err = 1;
 			}
 		}
-		fi->fi_flags |= FI_SEL;
+		/* FI_MODULAR and FI_SEL are exclusive */
+		if (!(fi->fi_flags & FI_MODULAR))
+			fi->fi_flags |= FI_SEL;
 	}
 	return (err);
 }
 
+/*
+ * Manage modules and the associated list of files
+ */
+static int
+addmoduleitem(void *file, int type, struct nvlist *attr)
+{
+	struct nvlist *nv = NULL;
+	struct module *mod;
+	struct hashtab *mods = ht_new();
+	const char *name;
+
+	/*
+	 * Get list of existing modules this file should
+	 * be part of.
+	 */
+	for (;attr != NULL; attr = attr->nv_next) {
+		if ((mod = ht_lookup(modulestab, attr->nv_name)) == NULL) {
+			mod = emalloc(sizeof(struct module));
+			mod->m_name = attr->nv_name; /* Already interned */
+			mod->m_filelist = NULL;
+			mod->m_objlist = NULL;
+			mod->m_attrlist = newnv(attr->nv_name, NULL, NULL, 0, NULL);
+			mod->m_count = 1;
+			ht_insert(modulestab, mod->m_name, mod);
+		} else if (ht_lookup(mods, mod->m_name))
+			continue;
+
+		ht_insert(mods, mod->m_name, mod);
+		nv = newnv(attr->nv_name, NULL, NULL, 0, nv);
+	}
+
+	/*
+	 * mods now contains a list of the modules that file should be part of.
+	 * Since a file should only be inculded once in the kernel, we have to
+	 * merge that list into one modules.
+	 * No need to do anything if mods only contain one name.
+	 */
+	if (ht_count(mods) > 1) {
+		void *old;
+		int i =
+		ht_enumerate(mods, buildmodule, (void *)mod);
+		printf("Merged %d modules into one, m_count = %d.\n", i, mod->m_count);
+		for (;nv != NULL; nv = nv->nv_next) {
+			old = ht_lookup(modulestab, nv->nv_name);
+			if (old != mod)
+				free(old);
+			ht_replace(modulestab, nv->nv_name, mod);
+		}
+	}
+
+	/*
+	 * Add either a file or an object to the module's list
+	 */
+	if (type == AMI_OBJECT) {
+		mod->m_objlist = newnv(NULL, NULL, file, 0, mod->m_objlist);
+		name = ((struct objects *)file)->oi_path;
+	} else {
+		mod->m_filelist = newnv(NULL, NULL, file, 0, mod->m_filelist);
+		name = ((struct files *)file)->fi_path;
+	}
+
+	printf("%s in %s.\n", name, mod->m_name);
+
+	return (0);
+}
+
+/*
+ * Construct a new module from a list enumerated through
+ * modname.
+ * arg is a pointer to an existing module struct which is shared by
+ * the attributes listed in modulestab.
+ */
+static int
+buildmodule(const char *modname, void *val, void *arg)
+{
+	struct module *mod = val, *newmod = arg;
+	char newname[128] = { 0 };
+
+	if (newmod != mod) {
+		newmod->m_filelist = nvcat(newmod->m_filelist, mod->m_filelist);
+		newmod->m_objlist = nvcat(newmod->m_objlist, mod->m_objlist);
+		newmod->m_attrlist = nvcat(newmod->m_attrlist, mod->m_attrlist);
+		strlcpy(newname, newmod->m_name, 128);
+		strlcat(newname, mod->m_name, 128);
+		newmod->m_name = intern(newname);
+		newmod->m_count += mod->m_count;
+	}
+
+	return (1);
+}
+
 /*    
  * We have finished reading everything.  Tack the objects down: calculate
  * selection.
@@ -306,18 +421,34 @@
 	TAILQ_FOREACH(oi, &allobjects, oi_next) {
 		/* Optional: see if it is to be included. */
 		if (oi->oi_optx != NULL) {
-			flathead = NULL;
-			flatp = &flathead;
-			sel = expr_eval(oi->oi_optx,
-			    oi->oi_flags & OI_NEEDSFLAG ? fixfsel :
-			    fixsel,
-			    &flatp);
-			oi->oi_optf = flathead;
-			if (!sel)
-				continue;
+			if (!(oi->oi_flags & OI_MODULAR)) {
+				flathead = NULL;
+				flatp = &flathead;
+				sel = expr_eval(oi->oi_optx,
+				    oi->oi_flags & OI_NEEDSFLAG ? fixfsel :
+				    fixsel,
+				    &flatp);
+				oi->oi_optf = flathead;
+			}
+			/*
+			 * It won't be included in the monolithic kernel,
+			 * though it might be compiled as part of a module
+			 */
+			if ((oi->oi_flags & OI_MODULAR) || !sel) {
+				flathead = NULL;
+				sel = expr_eval(oi->oi_optx, fixmodules, &flathead);
+				if (sel) {
+					/* Ok, we will produce a module out of this */
+					err += addmoduleitem(oi, AMI_OBJECT, flathead);
+					oi->oi_flags |= OI_MODULAR;
+				} else
+					/* Not selected */
+					continue;
+			}
 		}
 
-		oi->oi_flags |= OI_SEL;  
+		if (!(oi->oi_flags & OI_MODULAR))
+			oi->oi_flags |= OI_SEL;  
 	}
 	return (err);
 }     
@@ -427,6 +558,7 @@
 	struct nvlist ***p = context;
 	struct devbase *dev;
 	struct nvlist *nv;
+	int *pmod = ht_lookup(selecttab, name);
 
 	dev = ht_lookup(devbasetab, name);
 	if (dev == NULL)	/* cannot occur here; we checked earlier */
@@ -435,7 +567,12 @@
 	**p = nv;
 	*p = &nv->nv_next;
 	(void)ht_insert(needcnttab, name, nv);
-	return (dev->d_umax != 0);
+#ifdef notaproblem
+	if (pmod && *pmod == MOD_MODABLE)
+		printf("%s will be made a module. needs-count, though :/\n",
+		    name);
+#endif
+	return (dev->d_umax != 0 && (pmod == NULL || *pmod == MOD_STATIC));
 }
 
 /*
@@ -447,9 +584,15 @@
 {
 	struct nvlist ***p = context;
 	struct nvlist *nv;
-	int sel;
+	int sel, *pmod;
 
-	sel = ht_lookup(selecttab, name) != NULL;
+	pmod = ht_lookup(selecttab, name);
+#ifdef notaproblem
+	if (pmod && *pmod == MOD_MODABLE)
+		printf("%s will be made a module. needs-flag, though :/\n",
+		    name);
+#endif
+	sel = (pmod && *pmod == MOD_STATIC);
 	nv = newnv(name, NULL, NULL, sel, NULL);
 	**p = nv;
 	*p = &nv->nv_next;
@@ -462,8 +605,37 @@
 static int
 fixsel(const char *name, void *context)
 {
+	int *pmod = ht_lookup(selecttab, name);
+
+	if (pmod == NULL)
+		return (0);
+
+#ifdef notaproblem
+	if (*pmod == MOD_MODABLE)
+		printf("%s will be made a module.  Don't include here.\n",
+		    name);
+#endif
+	return (*pmod == MOD_STATIC);
+}
+
+/*
+ * Check if the file can be part of a module, and add the attribute to
+ * the flat list of dependencies.
+ */
+static int
+fixmodules(const char *name, void *context)
+{
+	int *pmod = ht_lookup(selecttab, name);
+	struct nvlist *modl, **p = (struct nvlist **)context;
+
+	if (pmod == NULL || *pmod != MOD_MODABLE)
+		return (0);
+
+	/* Add to the list of modules the file will be included in */
+	modl = newnv(name, NULL, NULL, 0, *p);
+	*p = modl;
 
-	return (ht_lookup(selecttab, name) != NULL);
+	return (1);
 }
 
 /*
Index: gram.y
===================================================================
RCS file: /pub/NetBSD-CVS/src/usr.sbin/config/gram.y,v
retrieving revision 1.44
diff -u -r1.44 gram.y
--- gram.y	2003/08/07 11:25:15	1.44
+++ gram.y	2004/02/06 23:09:22
@@ -97,10 +97,11 @@
 	int	val;
 }
 
-%token	AND AT ATTACH BUILD CINCLUDE COMPILE_WITH CONFIG DEFFS DEFINE DEFOPT 
+%token	AND AT ATTACH BUILD CINCLUDE COMPILE_WITH CONFIG DEFFS DEFINE DEFOPT
 %token	DEFPARAM DEFFLAG DEFPSEUDO DEVICE DEVCLASS DUMPS ENDFILE XFILE XOBJECT
 %token	FILE_SYSTEM FLAGS IDENT INCLUDE XMACHINE MAJOR MAKEOPTIONS
-%token	MAXUSERS MAXPARTITIONS MINOR ON OPTIONS PACKAGE PREFIX PSEUDO_DEVICE
+%token	MAXUSERS MAXPARTITIONS MINOR MODULAR MODULE
+%token	ON OPTIONS PACKAGE PREFIX PSEUDO_DEVICE
 %token	ROOT SOURCE TYPE WITH NEEDS_COUNT NEEDS_FLAG NO BLOCK CHAR DEVICE_MAJOR
 %token	<val> NUMBER
 %token	<str> PATHNAME QSTRING WORD EMPTY
@@ -225,14 +226,16 @@
 
 fflag:
 	NEEDS_COUNT			{ $$ = FI_NEEDSCOUNT; } |
-	NEEDS_FLAG			{ $$ = FI_NEEDSFLAG; };
+	NEEDS_FLAG			{ $$ = FI_NEEDSFLAG; } |
+	MODULAR				{ $$ = FI_MODULAR; };
 
 oflgs:
 	oflgs oflag			{ $$ = $1 | $2; } |
 	/* empty */			{ $$ = 0; };
 
 oflag:
-	NEEDS_FLAG			{ $$ = OI_NEEDSFLAG; };
+	NEEDS_FLAG			{ $$ = OI_NEEDSFLAG; } |
+	MODULAR				{ $$ = OI_MODULAR; };
 
 rule:
 	COMPILE_WITH stringvalue	{ $$ = $2; } |
@@ -269,10 +272,13 @@
 	include |
 	package |
 	prefix |
-	DEVCLASS WORD			{ (void)defattr($2, NULL, NULL, 1); } |
-	DEFFS fsoptfile_opt deffses	{ deffilesystem($2, $3); } |
+	DEVCLASS WORD			{ (void)defattr($2, NULL, NULL, CTX_DEVCLASS); } |
+	DEFFS fsoptfile_opt deffses	{ deffilesystem($2, $3, CTX_STATIC); } |
+	MODULAR DEFFS fsoptfile_opt deffses	{ deffilesystem($3, $4, CTX_MODULAR); } |
 	DEFINE WORD interface_opt attrs_opt
-					{ (void)defattr($2, $3, $4, 0); } |
+					{ (void)defattr($2, $3, $4, CTX_STATIC); } |
+	MODULAR DEFINE WORD interface_opt attrs_opt
+					{ (void)defattr($3, $4, $5, CTX_MODULAR); } |
 	DEFOPT optfile_opt defopts defoptdeps
 					{ defoption($2, $3, $4); } |
 	DEFFLAG optfile_opt defopts defoptdeps
@@ -280,13 +286,17 @@
 	DEFPARAM optfile_opt defopts defoptdeps
 					{ defparam($2, $3, $4); } |
 	DEVICE devbase interface_opt attrs_opt
-					{ defdev($2, $3, $4, 0); } |
+					{ defdev($2, $3, $4, CTX_STATIC); } |
+	MODULAR DEVICE devbase interface_opt attrs_opt
+					{ defdev($3, $4, $5, CTX_MODULAR); } |
 	ATTACH devbase AT atlist devattach_opt attrs_opt
 					{ defdevattach($5, $2, $4, $6); } |
 	MAXPARTITIONS NUMBER		{ maxpartitions = $2; } |
 	MAXUSERS NUMBER NUMBER NUMBER	{ setdefmaxusers($2, $3, $4); } |
 	DEFPSEUDO devbase interface_opt attrs_opt
-					{ defdev($2, $3, $4, 1); } |
+					{ defdev($2, $3, $4, CTX_PSEUDO); } |
+	MODULAR DEFPSEUDO devbase interface_opt attrs_opt
+					{ defdev($3, $4, $5, CTX_MODULAR | CTX_PSEUDO); } |
 	MAJOR '{' majorlist '}';
 
 atlist:
@@ -326,7 +336,8 @@
 	WORD				{ $$ = getdevbase($1); };
 
 devattach_opt:
-	WITH WORD			{ $$ = getdevattach($2); } |
+	WITH MODULE WORD		{ $$ = getdevattach($3, CTX_MODULAR); } |
+	WITH WORD			{ $$ = getdevattach($2, CTX_STATIC); } |
 	/* empty */			{ $$ = NULL; };
 
 interface_opt:
@@ -440,11 +451,14 @@
 	CONFIG conf root_spec sysparam_list
 					{ addconf(&conf); } |
 	NO PSEUDO_DEVICE WORD		{ delpseudo($3); } |
-	PSEUDO_DEVICE WORD npseudo	{ addpseudo($2, $3); } |
+	PSEUDO_DEVICE WORD npseudo	{ addpseudo($2, $3, CTX_STATIC); } |
+	MODULE PSEUDO_DEVICE WORD npseudo	{ addpseudo($3, $4, CTX_MODULAR); } |
 	NO device_instance AT attachment
 					{ deldev($2, $4); } |
 	device_instance AT attachment locators flags_opt
-					{ adddev($1, $3, $4, $5); };
+					{ adddev($1, $3, $4, $5, CTX_STATIC); } |
+	MODULE device_instance AT attachment locators flags_opt
+					{ adddev($2, $4, $5, $6, CTX_MODULAR); };
 
 fs_list:
 	fs_list ',' fsoption |
Index: hash.c
===================================================================
RCS file: /pub/NetBSD-CVS/src/usr.sbin/config/hash.c,v
retrieving revision 1.11
diff -u -r1.11 hash.c
--- hash.c	2003/08/07 11:25:15	1.11
+++ hash.c	2004/02/06 23:09:22
@@ -294,3 +294,9 @@
 	}
 	return rval;
 }
+
+u_int
+ht_count(struct hashtab *ht)
+{
+	return (ht->ht_used);
+}
Index: main.c
===================================================================
RCS file: /pub/NetBSD-CVS/src/usr.sbin/config/main.c,v
retrieving revision 1.86
diff -u -r1.86 main.c
--- main.c	2003/09/14 12:43:04	1.86
+++ main.c	2004/02/06 23:09:22
@@ -76,6 +76,8 @@
 int	vflag;				/* verbose output */
 int	Pflag;				/* pack locators */
 
+int	mod_static = MOD_STATIC;	/* for options */
+
 int	yyparse(void);
 
 #ifndef MAKE_BOOTSTRAP
@@ -243,6 +245,7 @@
 	selecttab = ht_new();
 	needcnttab = ht_new();
 	opttab = ht_new();
+	modulestab = ht_new();
 	mkopttab = ht_new();
 	fsopttab = ht_new();
 	deffstab = ht_new();
@@ -384,7 +387,7 @@
 	/*
 	 * Ready to go.  Build all the various files.
 	 */
-	if (mksymlinks() || mkmakefile() || mkheaders() || mkswap() ||
+	if (mksymlinks() || mkmodules() || mkmakefile() || mkheaders() || mkswap() ||
 	    mkioconf() || (do_devsw ? mkdevsw() : 0) || mkident())
 		stop();
 	(void)printf("Build directory is %s\n", builddir);
@@ -518,9 +521,12 @@
  * specified, a preprocessor #define for that file system will be placed
  * in that file.  In this case, only one file system may be specified.
  * Otherwise, no preprocessor #defines will be generated.
+ *
+ * XXX `module' ignored for now, until either another *tab var is created
+ * or a real struct is used for file-systems.
  */
 void
-deffilesystem(const char *fname, struct nvlist *fses)
+deffilesystem(const char *fname, struct nvlist *fses, int module)
 {
 	struct nvlist *nv;
 
@@ -792,7 +798,7 @@
 		*p++ = isupper(c) ? tolower(c) : c;
 	*p = 0;
 	n = intern(low);
-	(void)ht_insert(selecttab, n, (void *)n);
+	(void)ht_insert(selecttab, n, &mod_static);
 }
 
 void
@@ -844,7 +850,8 @@
 		panic("addfsoption: already in table");
 
 	/* Add to select table. */
-	(void)ht_insert(selecttab, n, (void *)n);
+	/* XXX modular file-systems */
+	(void)ht_insert(selecttab, n, &mod_static);
 }
 
 void
Index: mkmakefile.c
===================================================================
RCS file: /pub/NetBSD-CVS/src/usr.sbin/config/mkmakefile.c,v
retrieving revision 1.59
diff -u -r1.59 mkmakefile.c
--- mkmakefile.c	2003/08/07 11:25:16	1.59
+++ mkmakefile.c	2004/02/06 23:09:22
@@ -60,12 +60,20 @@
 static int emitdefs(FILE *);
 static int emitfiles(FILE *, int, int);
 
+static int printfileobj(FILE *, int *, int *, char *, int);
+static int printfile(FILE *, int *, int *, void *, int);
+
 static int emitobjs(FILE *);
 static int emitcfiles(FILE *);
 static int emitsfiles(FILE *);
+
+static int printrule(FILE *, struct files *, const char *);
+
 static int emitrules(FILE *);
 static int emitload(FILE *);
 static int emitincludes(FILE *);
+static int emitmodules(FILE *);
+static int emitmodule(const char *, void *, void *);
 
 int
 mkmakefile(void)
@@ -122,6 +130,8 @@
 			fn = emitload;
 		else if (strcmp(line, "%INCLUDES\n") == 0)
 			fn = emitincludes;
+		else if (strcmp(line, "%MODULES\n") == 0)
+			fn = emitmodules;
 		else {
 			xerror(ifname, lineno,
 			    "unknown %% construct ignored: %s", line);
@@ -246,11 +256,76 @@
 }
 
 static int
+printfileobj(FILE *fp, int *lpos, int *sp, char *name, int suffix)
+{
+	int len;
+	len = strlen(name) + 2;
+	if (*lpos + len > 72) {
+		if (fputs(" \\\n", fp) < 0)
+			return (1);
+		*sp = '\t';
+		*lpos = 7;
+	}
+	if (fprintf(fp, "%c%s.%c", *sp, name, suffix) < 0)
+		return (1);
+	*lpos += len + 1;
+	*sp = ' ';
+	return (0);
+}
+
+#define	TYPE_OBJECT	0
+#define	TYPE_FILE	1
+static int
+printfile(FILE *fp, int *lpos, int *sp, void *arg, int type)
+{
+	int len;
+	const char *prefix, *path;
+
+	if (type == TYPE_OBJECT) {
+		prefix = ((struct objects *)arg)->oi_prefix;
+		path = ((struct objects *)arg)->oi_path;
+	} else {
+		prefix = ((struct files *)arg)->fi_prefix;
+		path = ((struct files *)arg)->fi_path;
+	}
+
+	len = strlen(path);
+	if (*path != '/') {
+		len += 3;	/* "$S/" */
+		if (prefix != NULL)
+			len += strlen(prefix) + 1;
+	}
+	if (*lpos + len > 72) {
+		if (fputs(" \\\n", fp) < 0)
+			return (1);
+		*sp = '\t';
+		*lpos = 7;
+	}
+	if (*path == '/') {
+		if (fprintf(fp, "%c%s", *sp, path) < 0)
+			return (1);
+	} else {
+		if (prefix != NULL) {
+			if (fprintf(fp, "%c%s%s/%s", *sp,
+			    prefix_prologue(prefix),
+			    prefix, path) < 0)
+				return (1);
+		} else {
+			if (fprintf(fp, "%c$S/%s", *sp, path) < 0)
+				return (1);
+		}
+	}
+	*lpos += len + 1;
+	*sp = ' ';
+	return (0);
+}
+
+static int
 emitobjs(FILE *fp)
 {
 	struct files *fi;
 	struct objects *oi;
-	int lpos, len, sp;
+	int lpos, sp;
 
 	if (fputs("OBJS=", fp) < 0)
 		return (1);
@@ -259,49 +334,14 @@
 	TAILQ_FOREACH(fi, &allfiles, fi_next) {
 		if ((fi->fi_flags & FI_SEL) == 0)
 			continue;
-		len = strlen(fi->fi_base) + 2;
-		if (lpos + len > 72) {
-			if (fputs(" \\\n", fp) < 0)
-				return (1);
-			sp = '\t';
-			lpos = 7;
-		}
-		if (fprintf(fp, "%c%s.o", sp, fi->fi_base) < 0)
+		if (printfileobj(fp, &lpos, &sp, (char *)fi->fi_base, 'o') != 0)
 			return (1);
-		lpos += len + 1;
-		sp = ' ';
 	}
 	TAILQ_FOREACH(oi, &allobjects, oi_next) {
 		if ((oi->oi_flags & OI_SEL) == 0)
 			continue;
-		len = strlen(oi->oi_path);
-		if (*oi->oi_path != '/') {
-			len += 3;	/* "$S/" */
-			if (oi->oi_prefix != NULL)
-				len += strlen(oi->oi_prefix) + 1;
-		}
-		if (lpos + len > 72) {
-			if (fputs(" \\\n", fp) < 0)
-				return (1);
-			sp = '\t';
-			lpos = 7;
-		}
-		if (*oi->oi_path == '/') {
-			if (fprintf(fp, "%c%s", sp, oi->oi_path) < 0)
-				return (1);
-		} else {
-			if (oi->oi_prefix != NULL) {
-				if (fprintf(fp, "%c%s%s/%s", sp,
-				    prefix_prologue(oi->oi_prefix),
-				    oi->oi_prefix, oi->oi_path) < 0)
-					return (1);
-			} else {
-				if (fprintf(fp, "%c$S/%s", sp, oi->oi_path) < 0)
-					return (1);
-			}
-		}
-		lpos += len + 1;
-		sp = ' ';
+		if (printfile(fp, &lpos, &sp, oi, TYPE_OBJECT) != 0)
+			return (1);
 	}
 	if (putc('\n', fp) < 0)
 		return (1);
@@ -344,33 +384,8 @@
 		if (! ((fpath[len - 1] == suffix) ||
 		    (upper_suffix && fpath[len - 1] == toupper(suffix))))
 			continue;
-		if (*fpath != '/') {
-			len += 3;	/* "$S/" */
-			if (fi->fi_prefix != NULL)
-				len += strlen(fi->fi_prefix) + 1;
-		}
-		if (lpos + len > 72) {
-			if (fputs(" \\\n", fp) < 0)
-				return (1);
-			sp = '\t';
-			lpos = 7;
-		}
-		if (*fi->fi_path == '/') {
-			if (fprintf(fp, "%c%s", sp, fi->fi_path) < 0)
-				return (1);
-		} else {
-			if (fi->fi_prefix != NULL) {
-				if (fprintf(fp, "%c%s%s/%s", sp,
-				    prefix_prologue(fi->fi_prefix),
-				    fi->fi_prefix, fi->fi_path) < 0)
-					return (1);
-			} else {
-				if (fprintf(fp, "%c$S/%s", sp, fi->fi_path) < 0)
-					return (1);
-			}
-		}
-		lpos += len + 1;
-		sp = ' ';
+		if (printfile(fp, &lpos, &sp, fi, TYPE_FILE)  != 0)
+			return (1);
 	}
 	/*
 	 * The allfiles list does not include the configuration-specific
@@ -379,19 +394,10 @@
 	 */
 	if (suffix == 'c') {
 		TAILQ_FOREACH(cf, &allcf, cf_next) {
-			(void)snprintf(swapname, sizeof(swapname), "swap%s.c",
+			(void)snprintf(swapname, sizeof(swapname), "swap%s",
 			    cf->cf_name);
-			len = strlen(swapname);
-			if (lpos + len > 72) {
-				if (fputs(" \\\n", fp) < 0)
-					return (1);
-				sp = '\t';
-				lpos = 7;
-			}
-			if (fprintf(fp, "%c%s", sp, swapname) < 0)
+			if (printfileobj(fp, &lpos, &sp, swapname, suffix) != 0)
 				return (1);
-			lpos += len + 1;
-			sp = ' ';
 		}
 	}
 	if (putc('\n', fp) < 0)
@@ -399,6 +405,43 @@
 	return (0);
 }
 
+static int
+printrule(FILE *fp, struct files *fi, const char *defrule)
+{
+	const char *cp, *fpath;
+	int ch;
+	char buf[200];
+
+	if ((fpath = srcpath(fi)) == NULL)
+		return (1);
+	if (*fpath == '/') {
+		if (fprintf(fp, "%s.o: %s\n", fi->fi_base, fpath) < 0)
+			return (1);
+	} else {
+		if (fi->fi_prefix != NULL) {
+			if (fprintf(fp, "%s.o: %s%s/%s\n", fi->fi_base,
+			    prefix_prologue(fi->fi_prefix),
+			    fi->fi_prefix, fpath) < 0)
+				return (1);
+		} else {
+			if (fprintf(fp, "%s.o: $S/%s\n", fi->fi_base,
+			    fpath) < 0)
+				return (1);
+		}
+	}
+	if ((cp = fi->fi_mkrule) == NULL) {
+		cp = defrule;
+		ch = fpath[strlen(fpath) - 1];
+		if (islower(ch))
+			ch = toupper(ch);
+		(void)snprintf(buf, sizeof(buf), "${%s_%c}", cp, ch);
+		cp = buf;
+	}
+	if (fprintf(fp, "\t%s\n\n", cp) < 0)
+		return (1);
+	return (0);
+}
+
 /*
  * Emit the make-rules.
  */
@@ -406,40 +449,12 @@
 emitrules(FILE *fp)
 {
 	struct files *fi;
-	const char *cp, *fpath;
-	int ch;
-	char buf[200];
 
 	TAILQ_FOREACH(fi, &allfiles, fi_next) {
 		if ((fi->fi_flags & FI_SEL) == 0)
 			continue;
-		if ((fpath = srcpath(fi)) == NULL)
+		if (printrule(fp, fi, "NORMAL") != 0)
 			return (1);
-		if (*fpath == '/') {
-			if (fprintf(fp, "%s.o: %s\n", fi->fi_base, fpath) < 0)
-				return (1);
-		} else {
-			if (fi->fi_prefix != NULL) {
-				if (fprintf(fp, "%s.o: %s%s/%s\n", fi->fi_base,
-				    prefix_prologue(fi->fi_prefix),
-				    fi->fi_prefix, fpath) < 0)
-					return (1);
-			} else {
-				if (fprintf(fp, "%s.o: $S/%s\n", fi->fi_base,
-				    fpath) < 0)
-					return (1);
-			}
-		}
-		if ((cp = fi->fi_mkrule) == NULL) {
-			cp = "NORMAL";
-			ch = fpath[strlen(fpath) - 1];
-			if (islower(ch))
-				ch = toupper(ch);
-			(void)snprintf(buf, sizeof(buf), "${%s_%c}", cp, ch);
-			cp = buf;
-		}
-		if (fprintf(fp, "\t%s\n\n", cp) < 0)
-			return (1);
 	}
 	return (0);
 }
@@ -509,5 +524,63 @@
 			return (1);
 	}
 
+	return (0);
+}
+
+/*
+ * Emit modules and objects needed to build them
+ */
+static int
+emitmodules(FILE *fp)
+{
+	return (ht_enumerate(modulestab, emitmodule, (void *)fp));
+}
+
+static int
+emitmodule(const char *attr, void *val, void *arg)
+{
+	FILE *fp = arg;
+	struct module *mod = val;
+	struct files *fi;
+	struct nvlist *nv;
+	int lpos, sp;
+
+	/* Pass modules that are already emitted */
+	if (mod->m_name == NULL)
+		return (0);
+
+	/* Rule for the module */
+	if (fprintf(fp, "modules:\t%s.ko\n%s.ko:", mod->m_name, mod->m_name) < 0)
+		return (1);
+
+	lpos = strlen(mod->m_name) + 4;
+	sp = '\t';
+	for (nv = mod->m_objlist; nv != NULL; nv = nv->nv_next)
+		if (printfile(fp, &lpos, &sp, nv->nv_ptr, TYPE_OBJECT) != 0)
+			return (1);
+
+	for (nv = mod->m_filelist; nv != NULL; nv = nv->nv_next) {
+		fi = nv->nv_ptr;
+		if (printfileobj(fp, &lpos, &sp, (char *)fi->fi_base, 'o') != 0)
+			return (1);
+	}
+	if (mod->m_count > 1) {
+		char modentry[MOD_MAXNAME+10]; /* _lkmentry is appended */
+
+		strlcpy(modentry, mod->m_name, MOD_MAXNAME+10);
+		strlcat(modentry, "_lkmentry", MOD_MAXNAME+10);
+		if (printfileobj(fp, &lpos, &sp, modentry, 'o') != 0)
+			return (1);
+	}
+
+	if (fprintf(fp, "\n\t${BUILD_MODULE}\n\n") < 0)
+		return (1);
+
+	/* Rules for the files */
+	for (nv = mod->m_filelist; nv != NULL; nv = nv->nv_next)
+		if (printrule(fp, nv->nv_ptr, "NORMAL_MODULE") != 0)
+			return (1);
+
+	mod->m_name = NULL;
 	return (0);
 }
Index: scan.l
===================================================================
RCS file: /pub/NetBSD-CVS/src/usr.sbin/config/scan.l,v
retrieving revision 1.40
diff -u -r1.40 scan.l
--- scan.l	2003/09/08 17:50:12	1.40
+++ scan.l	2004/02/06 23:09:23
@@ -110,6 +110,8 @@
 maxpartitions	return MAXPARTITIONS;
 maxusers	return MAXUSERS;
 minor		return MINOR;
+modular		return MODULAR;
+module		return MODULE;
 needs-count	return NEEDS_COUNT;
 needs-flag	return NEEDS_FLAG;
 no		return NO;
Index: sem.c
===================================================================
RCS file: /pub/NetBSD-CVS/src/usr.sbin/config/sem.c,v
retrieving revision 1.40
diff -u -r1.40 sem.c
--- sem.c	2003/08/07 11:25:17	1.40
+++ sem.c	2004/02/06 23:09:23
@@ -70,12 +70,12 @@
 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 struct devi *newdevi(const char *, int, struct devbase *d, int);
+static struct devi *getdevi(const char *, int);
 static const char *concat(const char *, int);
 static char *extend(char *, const char *);
 static int split(const char *, size_t, char *, size_t, int *);
-static void selectbase(struct devbase *, struct deva *);
+static void selectbase(struct devbase *, struct deva *, int);
 static int onlist(struct nvlist *, void *);
 static const char **fixloc(const char *, struct attr *, struct nvlist *);
 static const char *makedevstr(int, int);
@@ -185,16 +185,16 @@
  */
 int
 defattr(const char *name, struct nvlist *locs, struct nvlist *deps,
-    int devclass)
+    int context)
 {
 	struct attr *a, *dep;
 	struct nvlist *nv;
 	int len;
 
-	if (locs != NULL && devclass)
+	if (locs != NULL && (context == CTX_DEVCLASS))
 		panic("defattr(%s): locators and devclass", name);
 
-	if (deps != NULL && devclass)
+	if (deps != NULL && (context == CTX_DEVCLASS))
 		panic("defattr(%s): dependencies and devclass", name);
 
 	/*
@@ -211,6 +211,10 @@
 	}
 
 	a = emalloc(sizeof *a);
+	/*
+	 * Check if attribute alreday exists
+	 * insert into attrtab
+	 */
 	if (ht_insert(attrtab, name, a)) {
 		free(a);
 		error("attribute `%s' already defined", name);
@@ -219,6 +223,7 @@
 	}
 
 	a->a_name = name;
+	a->a_modprops = MOD_STATIC;
 	if (locs != NULL) {
 		a->a_iattr = 1;
 		a->a_locs = locs->nv_next;
@@ -227,7 +232,7 @@
 		a->a_iattr = 0;
 		a->a_locs = NULL;
 	}
-	if (devclass) {
+	if (context == CTX_DEVCLASS) {
 		size_t l = strlen(name) + 4;
 		char *classenum = alloca(l), *cp;
 		int errored = 0;
@@ -244,8 +249,13 @@
 			*cp = toupper(*cp);
 		}
 		a->a_devclass = intern(classenum);
-	} else
+	} else {
 		a->a_devclass = NULL;
+
+		if (context == CTX_MODULAR) /* XXX module, devclasses can't be modules */
+			a->a_modprops = MOD_MODABLE;
+	}
+
 	len = 0;
 	for (nv = a->a_locs; nv != NULL; nv = nv->nv_next)
 		len++;
@@ -313,7 +323,7 @@
  */
 void
 defdev(struct devbase *dev, struct nvlist *loclist, struct nvlist *attrs,
-       int ispseudo)
+       int flags)
 {
 	struct nvlist *nv;
 	struct attr *a;
@@ -345,7 +355,8 @@
 	}
 
 	/* Committed!  Set up fields. */
-	dev->d_ispseudo = ispseudo;
+	dev->d_ispseudo = flags & CTX_PSEUDO;
+	dev->d_modprops = (flags & CTX_MODULAR) ? MOD_MODABLE : MOD_STATIC;
 	dev->d_attrs = attrs;
 	dev->d_classattr = NULL;		/* for now */
 
@@ -433,7 +444,7 @@
 	if (dev == &errdev)
 		goto bad;
 	if (deva == NULL)
-		deva = getdevattach(dev->d_name);
+		deva = getdevattach(dev->d_name, (dev->d_modprops & MOD_MODABLE));
 	if (deva == &errdeva)
 		goto bad;
 	if (!dev->d_isdef) {
@@ -501,6 +512,8 @@
 	/* attach to parent */
 	*dev->d_app = deva;
 	dev->d_app = &deva->d_bsame;
+	if (deva->d_modprops == MOD_STATIC)
+		dev->d_modprops = MOD_STATIC;
 	return;
  bad:
 	nvfreel(atlist);
@@ -512,7 +525,7 @@
  * name, i.e., does not contain digits or special characters.
  */
 struct deva *
-getdevattach(const char *name)
+getdevattach(const char *name, int module)
 {
 	u_char *p;
 	struct deva *deva;
@@ -540,10 +553,15 @@
 		deva->d_attrs = NULL;
 		deva->d_ihead = NULL;
 		deva->d_ipp = &deva->d_ihead;
+		deva->d_modprops = (module & CTX_MODULAR) ? MOD_MODABLE : MOD_STATIC;
 		TAILQ_INSERT_TAIL(&alldevas, deva, d_next);
 		if (ht_insert(devatab, name, deva))
 			panic("getdeva(%s)", name);
-	}
+	} else
+		/* if one attachment requires it static, all should be */
+		if (!(module & CTX_MODULAR))
+			deva->d_modprops = MOD_STATIC;
+
 	return (deva);
 }
 
@@ -555,10 +573,11 @@
 {
 	struct attr *a;
 
-	if ((a = ht_lookup(attrtab, name)) == NULL) {
+	if ((a = ht_lookup(attrtab, name))== NULL) {
 		error("undefined attribute `%s'", name);
 		a = &errattr;
 	}
+
 	return (a);
 }
 
@@ -578,17 +597,17 @@
 	}
 
 	a->a_expanding = 1;
+
+	/* First invoke the callback for ourself. */
+	if (callback != NULL)
+		(*callback)(a);
 
-	/* First expand all of this attribute's dependencies. */
+	/* ...and now expand all of this attribute's dependencies. */
 	for (nv = a->a_deps; nv != NULL; nv = nv->nv_next) {
 		dep = nv->nv_ptr;
 		expandattr(dep, callback);
 	}
 
-	/* ...and now invoke the callback for ourself. */
-	if (callback != NULL)
-		(*callback)(a);
-
 	a->a_expanding = 0;
 }
 
@@ -846,7 +865,7 @@
 }
 
 static struct devi *
-newdevi(const char *name, int unit, struct devbase *d)
+newdevi(const char *name, int unit, struct devbase *d, int module)
 {
 	struct devi *i;
 
@@ -863,6 +882,7 @@
 	i->i_locs = NULL;
 	i->i_cfflags = 0;
 	i->i_lineno = currentline();
+	i->i_modprops = (module & CTX_MODULAR) ? MOD_MODABLE : MOD_STATIC;
 	if (unit >= d->d_umax)
 		d->d_umax = unit + 1;
 	return (i);
@@ -873,7 +893,7 @@
  * another device instead) plus unit number.
  */
 void
-adddev(const char *name, const char *at, struct nvlist *loclist, int flags)
+adddev(const char *name, const char *at, struct nvlist *loclist, int flags, int module)
 {
 	struct devi *i;		/* the new instance */
 	struct pspec *p;	/* and its pspec */
@@ -892,7 +912,7 @@
 	if (at == NULL) {
 		/* "at root" */
 		p = NULL;
-		if ((i = getdevi(name)) == NULL)
+		if ((i = getdevi(name, module)) == NULL)
 			goto bad;
 		/*
 		 * Must warn about i_unit > 0 later, after taking care of
@@ -917,7 +937,7 @@
 			/* (void)getdevi(name); -- ??? */
 			goto bad;
 		}
-		if ((i = getdevi(name)) == NULL)
+		if ((i = getdevi(name, module)) == NULL)
 			goto bad;
 		ib = i->i_base;
 
@@ -1058,7 +1078,7 @@
 }
 
 void
-addpseudo(const char *name, int number)
+addpseudo(const char *name, int number, int module)
 {
 	struct devbase *d;
 	struct devi *i;
@@ -1076,7 +1096,7 @@
 		error("`%s' already defined", name);
 		return;
 	}
-	i = newdevi(name, number - 1, d);	/* foo 16 => "foo0..foo15" */
+	i = newdevi(name, number - 1, d, module);	/* foo 16 => "foo0..foo15" */
 	if (ht_insert(devitab, name, i))
 		panic("addpseudo(%s)", name);
 	TAILQ_INSERT_TAIL(&allpseudo, i, i_next);
@@ -1149,10 +1169,10 @@
 	struct devi *i;
 
 	TAILQ_FOREACH(i, &alldevi, i_next)
-		selectbase(i->i_base, i->i_atdeva);
+		selectbase(i->i_base, i->i_atdeva, i->i_modprops);
 
 	TAILQ_FOREACH(i, &allpseudo, i_next)
-		selectbase(i->i_base, NULL);
+		selectbase(i->i_base, NULL, i->i_modprops);
 }
 
 /*
@@ -1187,7 +1207,7 @@
  * Define a new instance of a specific device.
  */
 static struct devi *
-getdevi(const char *name)
+getdevi(const char *name, int module)
 {
 	struct devi *i, *firsti;
 	struct devbase *d;
@@ -1208,7 +1228,7 @@
 		return (NULL);
 	}
 	firsti = ht_lookup(devitab, name);
-	i = newdevi(name, unit, d);
+	i = newdevi(name, unit, d, module);
 	if (firsti == NULL) {
 		if (ht_insert(devitab, name, i))
 			panic("getdevi(%s)", name);
@@ -1291,8 +1311,26 @@
 void
 selectattr(struct attr *a)
 {
+	struct nvlist *nv;
+	struct attr *dep;
+	/*
+	 * XXX Here is a good place to fix modularity
+	 * properties.
+	 *
+	 * If given attribute is static, make static all of its
+	 * dependencies.
+	 *
+	 * Note the change in expandattr that makes the callback be
+	 * invoked before the recursion.  We NEED it to be so here.
+	 */
+
+	if (a->a_modprops == MOD_STATIC)
+		for (nv = a->a_deps; nv != NULL; nv = nv->nv_next) {
+			dep = nv->nv_ptr;
+			dep->a_modprops = MOD_STATIC;
+		}
 
-	(void)ht_insert(selecttab, a->a_name, (char *)a->a_name);
+	(void)ht_insert(selecttab, a->a_name, &a->a_modprops);
 }
 
 /*
@@ -1300,20 +1338,35 @@
  * attributes for "optional foo".
  */
 static void
-selectbase(struct devbase *d, struct deva *da)
+selectbase(struct devbase *d, struct deva *da, int module)
 {
 	struct attr *a;
 	struct nvlist *nv;
+
+	/* A static instance of a device makes the device itself static */
+	if (module == MOD_STATIC) {
+		d->d_modprops = MOD_STATIC;
+		if (da != NULL)
+			da->d_modprops = MOD_STATIC;
+	}
 
-	(void)ht_insert(selecttab, d->d_name, (char *)d->d_name);
+	/* XXX expand attrs found on line '{device|moddev} foo: attrs' */
+	(void)ht_insert(selecttab, d->d_name, &d->d_modprops);
 	for (nv = d->d_attrs; nv != NULL; nv = nv->nv_next) {
 		a = nv->nv_ptr;
+		/* XXX If we're static, mark attribute as such */
+		if (d->d_modprops == MOD_STATIC)
+			a->a_modprops = MOD_STATIC;
 		expandattr(a, selectattr);
 	}
+	/* XXX expand attrs found on line 'attach foo at bar: attrs' */
 	if (da != NULL) {
-		(void)ht_insert(selecttab, da->d_name, (char *)da->d_name);
+		(void)ht_insert(selecttab, da->d_name, &da->d_modprops);
 		for (nv = da->d_attrs; nv != NULL; nv = nv->nv_next) {
 			a = nv->nv_ptr;
+			/* XXX If we're static, mark attribute as such */
+			if (da->d_modprops == MOD_STATIC)
+				a->a_modprops = MOD_STATIC;
 			expandattr(a, selectattr);
 		}
 	}
Index: sem.h
===================================================================
RCS file: /pub/NetBSD-CVS/src/usr.sbin/config/sem.h,v
retrieving revision 1.18
diff -u -r1.18 sem.h
--- sem.h	2003/08/07 11:25:17	1.18
+++ sem.h	2004/02/06 23:09:23
@@ -50,7 +50,7 @@
 void		defdevattach(struct deva *, struct devbase *, struct nvlist *,
 			     struct nvlist *);
 struct devbase *getdevbase(const char *);
-struct deva    *getdevattach(const char *);
+struct deva    *getdevattach(const char *, int);
 struct attr    *getattr(const char *);
 void		expandattr(struct attr *, void (*)(struct attr *));
 void		selectattr(struct attr *);
@@ -58,12 +58,13 @@
 void		addconf(struct config *);
 void		setconf(struct nvlist **, const char *, struct nvlist *);
 void		setfstype(const char **, const char *);
-void		adddev(const char *, const char *, struct nvlist *, int);
+void		adddev(const char *, const char *, struct nvlist *, int, int);
 void		deldev(const char *, const char *);
-void		addpseudo(const char *, int);
+void		addpseudo(const char *, int, int);
 void		delpseudo(const char *);
 void		adddevm(const char *, int, int, struct nvlist *);
 void		fixdevis(void);
+void		fixmodules(void);
 const char     *ref(const char *);
 const char     *starref(const char *);
 const char     *wildref(const char *);
Index: util.c
===================================================================
RCS file: /pub/NetBSD-CVS/src/usr.sbin/config/util.c,v
retrieving revision 1.18
diff -u -r1.18 util.c
--- util.c	2003/09/19 06:19:56	1.18
+++ util.c	2004/02/06 23:09:23
@@ -229,6 +229,26 @@
 	}
 }
 
+/*
+ * Concatenate two lists
+ */
+struct nvlist *
+nvcat(struct nvlist *l1, struct nvlist *l2)
+{
+	struct nvlist *tail;
+
+	if (l1 == NULL)
+		return (l2);
+	if (l2 == NULL)
+		return (l1);
+
+	/* Reach tail of l1 */
+	for (tail = l1; tail->nv_next != NULL; tail = tail->nv_next);
+
+	tail->nv_next = l2;
+	return (l1);
+}
+
 void
 warn(const char *fmt, ...)
 {
--- /dev/null	2004-02-07 00:05:16.000000000 +0100
+++ mkmodules.c	2004-02-04 10:56:46.000000000 +0100
@@ -0,0 +1,123 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2004 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Quentin Garnier.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "defs.h"
+
+static int	mkmodule(const char *, void *, void *);
+
+/*
+ * Construct files named <module>_lkmentry.c that calls all LKM entry points
+ * for a given set of modular attributes.
+ *
+ * Such a file won't be produced if only one attribute makes the module.
+ */
+
+int
+mkmodules(void)
+{
+	return (ht_enumerate(modulestab, mkmodule, NULL));
+}
+
+static int
+mkmodule(const char *attr, void *val, void *arg)
+{
+	struct module *mod = val;
+	struct nvlist *nv;
+	FILE *fp;
+	char modentry[MOD_MAXNAME+12]; /* _lkmentry.c is appended */
+	int error;
+
+	if (mod->m_count == 1 || mod->m_name == NULL)
+		return (0);
+
+	strlcpy(modentry, mod->m_name, MOD_MAXNAME+12);
+	strlcat(modentry, "_lkmentry.c", MOD_MAXNAME+12);
+	if ((fp = fopen(modentry, "w")) == NULL) {
+		(void)fprintf(stderr, "config: cannot write %s: %s\n", modentry,
+		    strerror(errno));
+		return (1);
+	}
+
+	error = fprintf(fp,
+"/*\n\
+  * MACHINE GENERATED: DO NOT EDIT\n\
+  *\n\
+  * %s, from %s\n\
+  */\n\
+\n\
+#include <sys/param.h>\n\
+#include <sys/systm.h>\n\
+#include <sys/lkm.h>\n\
+\n\
+MOD_MISC(\"%s\");\n\
+\n\
+int\t%s_lkmentry(struct lkm_table *, int, int);\n\
+\n\
+int\n\
+%s_lkmentry(struct lkm_table *lkmtp, int command, int version)\n\
+{\n\
+\tint error;\n",
+	    modentry, conffile, mod->m_name, mod->m_name, mod->m_name);
+	if (error < 0)
+		goto stop;
+
+	for (nv = mod->m_attrlist; nv != NULL; nv = nv->nv_next) {
+		error = fprintf(fp,
+"\tif ((error = %s_lkmentry(lkmtp, command, version)) != 0)\n\
+\t\treturn (error);\n", nv->nv_name);
+		if (error < 0)
+			goto stop;
+	}
+
+	fprintf(fp,
+"\treturn (0);\n\
+}\n");
+
+	fclose(fp);
+	return (0);
+
+stop:
+	fclose(fp);
+	return (1);
+}

--Multipart=_Sat__7_Feb_2004_01_10_35_+0100_WzxD4GVPFC9eC+eU--