From 993460fbb182ddf51628f928deba8f89a17ae992 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 25 Oct 2004 22:59:12 +0000 Subject: [PATCH] Change the static array to a linked list, removes the limit of max 20 LKMs. Mostly from OpenBSD. Reviewed by several people on tech-kern. --- sys/kern/kern_lkm.c | 234 +++++++++++++++++++++++--------------------- sys/sys/lkm.h | 9 +- 2 files changed, 127 insertions(+), 116 deletions(-) diff --git a/sys/kern/kern_lkm.c b/sys/kern/kern_lkm.c index 76436c8a0f0a..247b17f9fc03 100644 --- a/sys/kern/kern_lkm.c +++ b/sys/kern/kern_lkm.c @@ -1,4 +1,4 @@ -/* $NetBSD: kern_lkm.c,v 1.75 2004/10/22 09:49:18 peter Exp $ */ +/* $NetBSD: kern_lkm.c,v 1.76 2004/10/25 22:59:12 peter Exp $ */ /* * Copyright (c) 1994 Christopher G. Demetriou @@ -41,7 +41,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: kern_lkm.c,v 1.75 2004/10/22 09:49:18 peter Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_lkm.c,v 1.76 2004/10/25 22:59:12 peter Exp $"); #include "opt_ddb.h" #include "opt_malloclog.h" @@ -98,13 +98,12 @@ int lkmdebug = 0; static int lkm_v = 0; static int lkm_state = LKMS_IDLE; -#ifndef MAXLKMS -#define MAXLKMS 20 -#endif - -static struct lkm_table lkmods[MAXLKMS]; /* table of loaded modules */ +static TAILQ_HEAD(lkms_head, lkm_table) lkmods; /* table of loaded modules */ static struct lkm_table *curp; /* global for in-progress ops */ +static struct lkm_table *lkmlookup(int, char *, int *); +static struct lkm_table *lkmalloc(void); +static void lkmfree(void); static void lkmunreserve(void); static int _lkm_syscall(struct lkm_table *, int); static int _lkm_vfs(struct lkm_table *, int); @@ -136,6 +135,8 @@ lkm_init(void) */ if (lkm_map == NULL) lkm_map = kernel_map; + + TAILQ_INIT(&lkmods); } /*ARGSUSED*/ @@ -172,13 +173,102 @@ lkmopen(dev, flag, devtype, p) return (0); /* pseudo-device open */ } +/* + * Look up for a LKM in the list. + */ +static struct lkm_table * +lkmlookup(int i, char *name, int *error) +{ + struct lkm_table *p; + char istr[MAXLKMNAME]; + + /* + * p being NULL here implies the list is empty, so any lookup is + * invalid (name based or otherwise). Since the list of modules is + * kept sorted by id, lowest to highest, the id of the last entry + * will be the highest in use. + */ + p = TAILQ_LAST(&lkmods, lkms_head); + if (p == NULL || i > p->id) { + *error = EINVAL; + return (NULL); + } + + if (i < 0) { /* unload by name */ + /* + * Copy name and lookup id from all loaded + * modules. May fail. + */ + *error = copyinstr(name, istr, MAXLKMNAME - 1, NULL); + if (*error) + return (NULL); + istr[MAXLKMNAME - 1] = '\0'; + + TAILQ_FOREACH(p, &lkmods, link) { + if (strcmp(istr, p->private.lkm_any->lkm_name) == 0) + break; + } + } else + TAILQ_FOREACH(p, &lkmods, link) + if (i == p->id) + break; + + if (p == NULL) + *error = ENOENT; + + return (p); +} + +/* + * Allocates memory for a new LKM table entry and inserts in the list. + * Returns NULL on failure. + */ +static struct lkm_table * +lkmalloc(void) +{ + struct lkm_table *p, *ret; + int id = 0; + + ret = malloc(sizeof(struct lkm_table), M_DEVBUF, M_NOWAIT); + if (ret == NULL) + return (NULL); + ret->refcnt = 0; + ret->forced = 0; + + /* find the first unused id */ + TAILQ_FOREACH(p, &lkmods, link) { + if (id != p->id) + break; + id++; + } + ret->id = id; + + if (p == NULL) + TAILQ_INSERT_TAIL(&lkmods, ret, link); + else + TAILQ_INSERT_BEFORE(p, ret, link); + + return (ret); +} + +/* + * Frees the current LKM table entry. + */ +static void +lkmfree(void) +{ + TAILQ_REMOVE(&lkmods, curp, link); + free(curp, M_DEVBUF); + curp = NULL; +} + /* * Unreserve the memory associated with the current loaded module; done on * a coerced close of the lkm device (close on premature exit of modload) * or explicitly by modload as a result of a link failure. */ static void -lkmunreserve() +lkmunreserve(void) { if (lkm_state == LKMS_IDLE) @@ -220,13 +310,14 @@ lkmclose(dev, flag, mode, p) } /* do this before waking the herd... */ - if (curp && !curp->used) { + if (curp != NULL && curp->refcnt == 0) { /* * If we close before setting used, we have aborted * by way of error or by way of close-on-exit from * a premature exit of "modload". */ lkmunreserve(); /* coerce state to LKM_IDLE */ + lkmfree(); } lkm_v &= ~LKM_ALLOC; @@ -244,13 +335,11 @@ lkmioctl(dev, cmd, data, flag, p) int flag; struct proc *p; { - int error = 0; - int i; + int i, error = 0; struct lmc_resrv *resrvp; struct lmc_loadbuf *loadbufp; struct lmc_unload *unloadp; struct lmc_stat *statp; - char istr[MAXLKMNAME]; switch(cmd) { case LMRESERV: /* reserve pages for a module */ @@ -262,30 +351,21 @@ lkmioctl(dev, cmd, data, flag, p) resrvp = (struct lmc_resrv *)data; - /* - * Find a free slot. - */ - for (i = 0; i < MAXLKMS; i++) - if (!lkmods[i].used) - break; - if (i == MAXLKMS) { - error = ENOMEM; /* no slots available */ + curp = lkmalloc(); + if (curp == NULL) { + error = ENOMEM; break; } - curp = &lkmods[i]; - - resrvp->slot = i; /* return slot */ + resrvp->slot = curp->id; /* return slot */ /* * Get memory for module */ curp->size = resrvp->size; - curp->area = LKM_SPACE_ALLOC(curp->size); - curp->offset = 0; /* load offset */ - resrvp->addr = curp->area; /* ret kernel addr */ + resrvp->addr = curp->area; /* ret kernel addr */ if (cmd == LMRESERV && resrvp->sym_size) { curp->sym_size = resrvp->sym_size; @@ -387,6 +467,8 @@ lkmioctl(dev, cmd, data, flag, p) return EPERM; lkmunreserve(); /* coerce state to LKM_IDLE */ + if (curp != NULL) + lkmfree(); #ifdef DEBUG if (lkmdebug & LKMDB_INFO) printf("LKM: LMUNRESERV\n"); @@ -445,7 +527,7 @@ lkmioctl(dev, cmd, data, flag, p) */ lkm_state = LKMS_UNLOADING; /* for lkmunreserve */ lkmunreserve(); /* free memory */ - curp->used = 0; /* free slot */ + lkmfree(); /* free slot */ #ifdef DEBUG if (lkmdebug & LKMDB_INFO) printf("lkm entry point failed with error %d\n", @@ -453,8 +535,8 @@ lkmioctl(dev, cmd, data, flag, p) #endif /* DEBUG */ break; } + curp->refcnt++; - curp->used = 1; #ifdef DEBUG if (lkmdebug & LKMDB_INFO) printf("LKM: LMREADY\n"); @@ -471,42 +553,9 @@ lkmioctl(dev, cmd, data, flag, p) unloadp = (struct lmc_unload *)data; - if ((i = unloadp->id) == -1) { /* unload by name */ - /* - * Copy name and lookup id from all loaded - * modules. May fail. - */ - error = copyinstr(unloadp->name, istr, MAXLKMNAME-1, - NULL); - if (error) - break; - - /* - * look up id... - */ - for (i = 0; i < MAXLKMS; i++) { - if (!lkmods[i].used) - continue; - if (!strcmp(istr, - lkmods[i].private.lkm_any->lkm_name)) - break; - } - } - - /* - * Range check the value; on failure, return EINVAL - */ - if (i < 0 || i >= MAXLKMS) { - error = EINVAL; + curp = lkmlookup(unloadp->id, unloadp->name, &error); + if (curp == NULL) break; - } - - curp = &lkmods[i]; - - if (!curp->used) { - error = ENOENT; - break; - } /* call entry(unload) */ if ((*(curp->entry))(curp, LKM_E_UNLOAD, LKM_VERSION)) { @@ -516,7 +565,7 @@ lkmioctl(dev, cmd, data, flag, p) lkm_state = LKMS_UNLOADING; /* non-idle for lkmunreserve */ lkmunreserve(); /* free memory */ - curp->used = 0; /* free slot */ + lkmfree(); /* free slot */ break; case LMSTAT: /* stat a module by id/name */ @@ -524,43 +573,8 @@ lkmioctl(dev, cmd, data, flag, p) statp = (struct lmc_stat *)data; - if ((i = statp->id) == -1) { /* stat by name */ - /* - * Copy name and lookup id from all loaded - * modules. - */ - copystr(statp->name, istr, MAXLKMNAME-1, (size_t *)0); - /* - * look up id... - */ - for (i = 0; i < MAXLKMS; i++) { - if (!lkmods[i].used) - continue; - if (!strcmp(istr, - lkmods[i].private.lkm_any->lkm_name)) - break; - } - - if (i == MAXLKMS) { /* Not found */ - error = ENOENT; - break; - } - } - - /* - * Range check the value; on failure, return EINVAL - */ - if (i < 0 || i >= MAXLKMS) { - error = EINVAL; + if ((curp = lkmlookup(statp->id, statp->name, &error)) == NULL) break; - } - - curp = &lkmods[i]; - - if (!curp->used) { /* Not found */ - error = ENOENT; - break; - } if ((error = (*curp->entry)(curp, LKM_E_STAT, LKM_VERSION))) break; @@ -568,7 +582,7 @@ lkmioctl(dev, cmd, data, flag, p) /* * Copy out stat information for this module... */ - statp->id = i; + statp->id = curp->id; statp->offset = curp->private.lkm_any->lkm_offset; statp->type = curp->private.lkm_any->lkm_type; statp->area = curp->area; @@ -640,20 +654,12 @@ int lkmexists(lkmtp) struct lkm_table *lkmtp; { - int i; + struct lkm_table *p; - /* - * see if name exists... - */ - for (i = 0; i < MAXLKMS; i++) { - /* - * An unused module and the one we are testing are not - * considered. - */ - if (!lkmods[i].used || &lkmods[i] == lkmtp) - continue; - if (!strcmp(lkmtp->private.lkm_any->lkm_name, - lkmods[i].private.lkm_any->lkm_name)) + /* see if name exists... */ + TAILQ_FOREACH(p, &lkmods, link) { + if (strcmp(lkmtp->private.lkm_any->lkm_name, + p->private.lkm_any->lkm_name) == 0 && (p->refcnt != 0)) return (1); /* already loaded... */ } diff --git a/sys/sys/lkm.h b/sys/sys/lkm.h index af608cd5fbf6..36be6c143224 100644 --- a/sys/sys/lkm.h +++ b/sys/sys/lkm.h @@ -1,4 +1,4 @@ -/* $NetBSD: lkm.h,v 1.33 2004/08/17 22:38:50 drochner Exp $ */ +/* $NetBSD: lkm.h,v 1.34 2004/10/25 22:59:12 peter Exp $ */ /* * Header file used by loadable kernel modules and loadable kernel module @@ -40,6 +40,8 @@ #ifndef _SYS_LKM_H_ #define _SYS_LKM_H_ +#include + /* * Supported module types */ @@ -177,7 +179,8 @@ union lkm_generic { * Per module information structure */ struct lkm_table { - char used; + int id; /* Identifier */ + char refcnt; /* Reference count */ char forced; /* Forced load, skipping compatibility check */ int (*entry) __P((struct lkm_table *, int, int));/* entry function */ @@ -192,6 +195,8 @@ struct lkm_table { u_long sym_size; /* size of symbol table (syms+strings) */ u_long sym_offset; /* offset of next symbol chunk */ u_long sym_symsize; /* size of symbol part only */ + + TAILQ_ENTRY(lkm_table) link; };