Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F157767990
D25161.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D25161.diff
View Options
Index: head/sys/kern/subr_firmware.c
===================================================================
--- head/sys/kern/subr_firmware.c
+++ head/sys/kern/subr_firmware.c
@@ -53,12 +53,10 @@
* form more details on the subsystem.
*
* 'struct firmware' is the user-visible part of the firmware table.
- * Additional internal information is stored in a 'struct priv_fw'
- * (currently a static array). A slot is in use if FW_INUSE is true:
+ * Additional internal information is stored in a 'struct priv_fw',
+ * which embeds the public firmware structure.
*/
-#define FW_INUSE(p) ((p)->file != NULL || (p)->fw.name != NULL)
-
/*
* fw.name != NULL when an image is registered; file != NULL for
* autoloaded images whose handling has not been completed.
@@ -82,6 +80,7 @@
struct priv_fw {
int refcnt; /* reference count */
+ LIST_ENTRY(priv_fw) link; /* table linkage */
/*
* parent entry, see above. Set on firmware_register(),
@@ -118,13 +117,9 @@
((intptr_t)(x) - offsetof(struct priv_fw, fw)) )
/*
- * At the moment we use a static array as backing store for the registry.
- * Should we move to a dynamic structure, keep in mind that we cannot
- * reallocate the array because pointers are held externally.
- * A list may work, though.
+ * Global firmware image registry.
*/
-#define FIRMWARE_MAX 50
-static struct priv_fw firmware_table[FIRMWARE_MAX];
+static LIST_HEAD(, priv_fw) firmware_table;
/*
* Firmware module operations are handled in a separate task as they
@@ -139,6 +134,8 @@
static struct mtx firmware_mtx;
MTX_SYSINIT(firmware, &firmware_mtx, "firmware table", MTX_DEF);
+static MALLOC_DEFINE(M_FIRMWARE, "firmware", "device firmware images");
+
/*
* Helper function to lookup a name.
* As a side effect, it sets the pointer to a free slot, if any.
@@ -147,23 +144,17 @@
* with some other data structure.
*/
static struct priv_fw *
-lookup(const char *name, struct priv_fw **empty_slot)
+lookup(const char *name)
{
- struct priv_fw *fp = NULL;
- struct priv_fw *dummy;
- int i;
+ struct priv_fw *fp;
- if (empty_slot == NULL)
- empty_slot = &dummy;
- *empty_slot = NULL;
- for (i = 0; i < FIRMWARE_MAX; i++) {
- fp = &firmware_table[i];
+ mtx_assert(&firmware_mtx, MA_OWNED);
+
+ LIST_FOREACH(fp, &firmware_table, link) {
if (fp->fw.name != NULL && strcasecmp(name, fp->fw.name) == 0)
break;
- else if (!FW_INUSE(fp))
- *empty_slot = fp;
}
- return (i < FIRMWARE_MAX ) ? fp : NULL;
+ return (fp);
}
/*
@@ -176,42 +167,42 @@
firmware_register(const char *imagename, const void *data, size_t datasize,
unsigned int version, const struct firmware *parent)
{
- struct priv_fw *match, *frp;
- char *str;
+ struct priv_fw *frp;
+ char *name;
- str = strdup(imagename, M_TEMP);
-
mtx_lock(&firmware_mtx);
- /*
- * Do a lookup to make sure the name is unique or find a free slot.
- */
- match = lookup(imagename, &frp);
- if (match != NULL) {
+ frp = lookup(imagename);
+ if (frp != NULL) {
mtx_unlock(&firmware_mtx);
printf("%s: image %s already registered!\n",
- __func__, imagename);
- free(str, M_TEMP);
- return NULL;
+ __func__, imagename);
+ return (NULL);
}
- if (frp == NULL) {
+ mtx_unlock(&firmware_mtx);
+
+ frp = malloc(sizeof(*frp), M_FIRMWARE, M_WAITOK | M_ZERO);
+ name = strdup(imagename, M_FIRMWARE);
+
+ mtx_lock(&firmware_mtx);
+ if (lookup(imagename) != NULL) {
+ /* We lost a race. */
mtx_unlock(&firmware_mtx);
- printf("%s: cannot register image %s, firmware table full!\n",
- __func__, imagename);
- free(str, M_TEMP);
- return NULL;
+ free(name, M_FIRMWARE);
+ free(frp, M_FIRMWARE);
+ return (NULL);
}
- bzero(frp, sizeof(*frp)); /* start from a clean record */
- frp->fw.name = str;
+ frp->fw.name = name;
frp->fw.data = data;
frp->fw.datasize = datasize;
frp->fw.version = version;
if (parent != NULL)
frp->parent = PRIV_FW(parent);
+ LIST_INSERT_HEAD(&firmware_table, frp, link);
mtx_unlock(&firmware_mtx);
if (bootverbose)
printf("firmware: '%s' version %u: %zu bytes loaded at %p\n",
imagename, version, datasize, data);
- return &frp->fw;
+ return (&frp->fw);
}
/*
@@ -226,7 +217,7 @@
int err;
mtx_lock(&firmware_mtx);
- fp = lookup(imagename, NULL);
+ fp = lookup(imagename);
if (fp == NULL) {
/*
* It is ok for the lookup to fail; this can happen
@@ -238,20 +229,13 @@
} else if (fp->refcnt != 0) { /* cannot unregister */
err = EBUSY;
} else {
- linker_file_t x = fp->file; /* save value */
-
- /*
- * Clear the whole entry with bzero to make sure we
- * do not forget anything. Then restore 'file' which is
- * non-null for autoloaded images.
- */
- free((void *) (uintptr_t) fp->fw.name, M_TEMP);
- bzero(fp, sizeof(struct priv_fw));
- fp->file = x;
+ LIST_REMOVE(fp, link);
+ free(__DECONST(char *, fp->fw.name), M_FIRMWARE);
+ free(fp, M_FIRMWARE);
err = 0;
}
mtx_unlock(&firmware_mtx);
- return err;
+ return (err);
}
static void
@@ -262,31 +246,29 @@
linker_file_t result;
int error;
- /* synchronize with the thread that dispatched us */
- mtx_lock(&firmware_mtx);
- mtx_unlock(&firmware_mtx);
-
error = linker_reference_module(imagename, NULL, &result);
if (error != 0) {
printf("%s: could not load firmware image, error %d\n",
imagename, error);
+ mtx_lock(&firmware_mtx);
goto done;
}
mtx_lock(&firmware_mtx);
- fp = lookup(imagename, NULL);
+ fp = lookup(imagename);
if (fp == NULL || fp->file != NULL) {
mtx_unlock(&firmware_mtx);
if (fp == NULL)
printf("%s: firmware image loaded, "
"but did not register\n", imagename);
(void) linker_release_module(imagename, NULL, NULL);
+ mtx_lock(&firmware_mtx);
goto done;
}
fp->file = result; /* record the module identity */
- mtx_unlock(&firmware_mtx);
done:
- wakeup_one(imagename); /* we're done */
+ wakeup_one(imagename);
+ mtx_unlock(&firmware_mtx);
}
/*
@@ -304,7 +286,7 @@
struct priv_fw *fp;
mtx_lock(&firmware_mtx);
- fp = lookup(imagename, NULL);
+ fp = lookup(imagename);
if (fp != NULL)
goto found;
/*
@@ -318,7 +300,7 @@
"load firmware image %s\n", __func__, imagename);
return NULL;
}
- /*
+ /*
* Defer load to a thread with known context. linker_reference_module
* may do filesystem i/o which requires root & current dirs, etc.
* Also we must not hold any mtx's over this call which is problematic.
@@ -333,7 +315,7 @@
/*
* After attempting to load the module, see if the image is registered.
*/
- fp = lookup(imagename, NULL);
+ fp = lookup(imagename);
if (fp == NULL) {
mtx_unlock(&firmware_mtx);
return NULL;
@@ -381,7 +363,6 @@
{
pwd_ensure_dirs();
-
free(arg, M_TEMP);
}
@@ -413,50 +394,39 @@
static void
unloadentry(void *unused1, int unused2)
{
- int limit = FIRMWARE_MAX;
- int i; /* current cycle */
+ struct priv_fw *fp, *tmp;
+ int err;
+ bool changed;
mtx_lock(&firmware_mtx);
- /*
- * Scan the table. limit is set to make sure we make another
- * full sweep after matching an entry that requires unloading.
- */
- for (i = 0; i < limit; i++) {
- struct priv_fw *fp;
- int err;
-
- fp = &firmware_table[i % FIRMWARE_MAX];
- if (fp->fw.name == NULL || fp->file == NULL ||
- fp->refcnt != 0 || (fp->flags & FW_UNLOAD) == 0)
+ changed = false;
+restart:
+ LIST_FOREACH_SAFE(fp, &firmware_table, link, tmp) {
+ if (fp->file == NULL || fp->refcnt != 0 ||
+ (fp->flags & FW_UNLOAD) == 0)
continue;
/*
* Found an entry. Now:
- * 1. bump up limit to make sure we make another full round;
+ * 1. make sure we scan the table again
* 2. clear FW_UNLOAD so we don't try this entry again.
* 3. release the lock while trying to unload the module.
- * 'file' remains set so that the entry cannot be reused
- * in the meantime (it also means that fp->file will
- * not change while we release the lock).
*/
- limit = i + FIRMWARE_MAX; /* make another full round */
+ changed = true;
fp->flags &= ~FW_UNLOAD; /* do not try again */
- mtx_unlock(&firmware_mtx);
- err = linker_release_module(NULL, NULL, fp->file);
- mtx_lock(&firmware_mtx);
-
/*
* We rely on the module to call firmware_unregister()
- * on unload to actually release the entry.
- * If err = 0 we can drop our reference as the system
- * accepted it. Otherwise unloading failed (e.g. the
- * module itself gave an error) so our reference is
- * still valid.
+ * on unload to actually free the entry.
*/
- if (err == 0)
- fp->file = NULL;
+ mtx_unlock(&firmware_mtx);
+ err = linker_release_module(NULL, NULL, fp->file);
+ mtx_lock(&firmware_mtx);
}
+ if (changed) {
+ changed = false;
+ goto restart;
+ }
mtx_unlock(&firmware_mtx);
}
@@ -467,8 +437,9 @@
firmware_modevent(module_t mod, int type, void *unused)
{
struct priv_fw *fp;
- int i, err;
+ int err;
+ err = 0;
switch (type) {
case MOD_LOAD:
TASK_INIT(&firmware_unload_task, 0, unloadentry, NULL);
@@ -478,39 +449,39 @@
(void) taskqueue_start_threads(&firmware_tq, 1, PWAIT,
"firmware taskq");
if (rootvnode != NULL) {
- /*
+ /*
* Root is already mounted so we won't get an event;
* simulate one here.
*/
firmware_mountroot(NULL);
}
- return 0;
+ break;
case MOD_UNLOAD:
/* request all autoloaded modules to be released */
mtx_lock(&firmware_mtx);
- for (i = 0; i < FIRMWARE_MAX; i++) {
- fp = &firmware_table[i];
+ LIST_FOREACH(fp, &firmware_table, link)
fp->flags |= FW_UNLOAD;
- }
mtx_unlock(&firmware_mtx);
taskqueue_enqueue(firmware_tq, &firmware_unload_task);
taskqueue_drain(firmware_tq, &firmware_unload_task);
- err = 0;
- for (i = 0; i < FIRMWARE_MAX; i++) {
- fp = &firmware_table[i];
+
+ LIST_FOREACH(fp, &firmware_table, link) {
if (fp->fw.name != NULL) {
- printf("%s: image %p ref %d still active slot %d\n",
- __func__, fp->fw.name,
- fp->refcnt, i);
+ printf("%s: image %s still active, %d refs\n",
+ __func__, fp->fw.name, fp->refcnt);
err = EINVAL;
}
}
if (err == 0)
taskqueue_free(firmware_tq);
- return err;
+ break;
+
+ default:
+ err = EOPNOTSUPP;
+ break;
}
- return EINVAL;
+ return (err);
}
static moduledata_t firmware_mod = {
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, May 25, 11:45 PM (10 h, 41 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33519368
Default Alt Text
D25161.diff (10 KB)
Attached To
Mode
D25161: Remove the FIRMWARE_MAX limit.
Attached
Detach File
Event Timeline
Log In to Comment