Fix a panic when unloading firmware

LIST_FOREACH_SAFE() is not safe in the presence
of other threads removing list entries when a
mutex is released.

This is not in the critical path, so just restart
the scan each time we drop the lock, rather than
using a marker.

Reviewed by:	jhb, markj
Sponsored by:	Netflix
This commit is contained in:
Andrew Gallatin 2020-06-29 21:35:50 +00:00
parent bbfbc439f2
commit 46cac10b3b

View File

@ -394,14 +394,12 @@ EVENTHANDLER_DEFINE(mountroot, firmware_mountroot, NULL, 0);
static void
unloadentry(void *unused1, int unused2)
{
struct priv_fw *fp, *tmp;
struct priv_fw *fp;
int err;
bool changed;
mtx_lock(&firmware_mtx);
changed = false;
restart:
LIST_FOREACH_SAFE(fp, &firmware_table, link, tmp) {
LIST_FOREACH(fp, &firmware_table, link) {
if (fp->file == NULL || fp->refcnt != 0 ||
(fp->flags & FW_UNLOAD) == 0)
continue;
@ -412,7 +410,6 @@ unloadentry(void *unused1, int unused2)
* 2. clear FW_UNLOAD so we don't try this entry again.
* 3. release the lock while trying to unload the module.
*/
changed = true;
fp->flags &= ~FW_UNLOAD; /* do not try again */
/*
@ -422,9 +419,11 @@ unloadentry(void *unused1, int unused2)
mtx_unlock(&firmware_mtx);
err = linker_release_module(NULL, NULL, fp->file);
mtx_lock(&firmware_mtx);
}
if (changed) {
changed = false;
/*
* When we dropped the lock, another thread could have
* removed an element, so we must restart the scan.
*/
goto restart;
}
mtx_unlock(&firmware_mtx);