Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/subr_firmware.c
Show First 20 Lines • Show All 232 Lines • ▼ Show 20 Lines | if (fp == NULL) { | ||||
free(__DECONST(char *, fp->fw.name), M_FIRMWARE); | free(__DECONST(char *, fp->fw.name), M_FIRMWARE); | ||||
free(fp, M_FIRMWARE); | free(fp, M_FIRMWARE); | ||||
err = 0; | err = 0; | ||||
} | } | ||||
mtx_unlock(&firmware_mtx); | mtx_unlock(&firmware_mtx); | ||||
return (err); | return (err); | ||||
} | } | ||||
struct fw_loadimage { | |||||
const char *imagename; | |||||
uint32_t flags; | |||||
}; | |||||
static void | static void | ||||
loadimage(void *arg, int npending) | loadimage(void *arg, int npending __unused) | ||||
{ | { | ||||
char *imagename = arg; | struct fw_loadimage *fwli = arg; | ||||
struct priv_fw *fp; | struct priv_fw *fp; | ||||
linker_file_t result; | linker_file_t result; | ||||
int error; | int error; | ||||
error = linker_reference_module(imagename, NULL, &result); | error = linker_reference_module(fwli->imagename, NULL, &result); | ||||
if (error != 0) { | if (error != 0) { | ||||
if (bootverbose || (fwli->flags & FIRMWARE_GET_NOWARN) == 0) | |||||
printf("%s: could not load firmware image, error %d\n", | printf("%s: could not load firmware image, error %d\n", | ||||
imagename, error); | fwli->imagename, error); | ||||
mtx_lock(&firmware_mtx); | mtx_lock(&firmware_mtx); | ||||
goto done; | goto done; | ||||
} | } | ||||
mtx_lock(&firmware_mtx); | mtx_lock(&firmware_mtx); | ||||
fp = lookup(imagename); | fp = lookup(fwli->imagename); | ||||
if (fp == NULL || fp->file != NULL) { | if (fp == NULL || fp->file != NULL) { | ||||
mtx_unlock(&firmware_mtx); | mtx_unlock(&firmware_mtx); | ||||
if (fp == NULL) | if (fp == NULL) | ||||
printf("%s: firmware image loaded, " | printf("%s: firmware image loaded, " | ||||
"but did not register\n", imagename); | "but did not register\n", fwli->imagename); | ||||
(void) linker_release_module(imagename, NULL, NULL); | (void) linker_release_module(fwli->imagename, NULL, NULL); | ||||
mtx_lock(&firmware_mtx); | mtx_lock(&firmware_mtx); | ||||
goto done; | goto done; | ||||
} | } | ||||
fp->file = result; /* record the module identity */ | fp->file = result; /* record the module identity */ | ||||
done: | done: | ||||
wakeup_one(imagename); | wakeup_one(arg); | ||||
mtx_unlock(&firmware_mtx); | mtx_unlock(&firmware_mtx); | ||||
} | } | ||||
/* | /* | ||||
* Lookup and potentially load the specified firmware image. | * Lookup and potentially load the specified firmware image. | ||||
* If the firmware is not found in the registry, try to load a kernel | * If the firmware is not found in the registry, try to load a kernel | ||||
* module named as the image name. | * module named as the image name. | ||||
* If the firmware is located, a reference is returned. The caller must | * If the firmware is located, a reference is returned. The caller must | ||||
* release this reference for the image to be eligible for removal/unload. | * release this reference for the image to be eligible for removal/unload. | ||||
*/ | */ | ||||
const struct firmware * | const struct firmware * | ||||
firmware_get(const char *imagename) | firmware_get_flags(const char *imagename, uint32_t flags) | ||||
{ | { | ||||
struct task fwload_task; | struct task fwload_task; | ||||
struct thread *td; | struct thread *td; | ||||
struct priv_fw *fp; | struct priv_fw *fp; | ||||
mtx_lock(&firmware_mtx); | mtx_lock(&firmware_mtx); | ||||
fp = lookup(imagename); | fp = lookup(imagename); | ||||
if (fp != NULL) | if (fp != NULL) | ||||
Show All 10 Lines | if (priv_check(td, PRIV_FIRMWARE_LOAD) != 0 || | ||||
return NULL; | return NULL; | ||||
} | } | ||||
/* | /* | ||||
* Defer load to a thread with known context. linker_reference_module | * Defer load to a thread with known context. linker_reference_module | ||||
* may do filesystem i/o which requires root & current dirs, etc. | * 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. | * Also we must not hold any mtx's over this call which is problematic. | ||||
*/ | */ | ||||
if (!cold) { | if (!cold) { | ||||
TASK_INIT(&fwload_task, 0, loadimage, __DECONST(void *, | struct fw_loadimage fwli; | ||||
imagename)); | |||||
fwli.imagename = imagename; | |||||
fwli.flags = flags; | |||||
TASK_INIT(&fwload_task, 0, loadimage, (void *)&fwli); | |||||
taskqueue_enqueue(firmware_tq, &fwload_task); | taskqueue_enqueue(firmware_tq, &fwload_task); | ||||
msleep(__DECONST(void *, imagename), &firmware_mtx, 0, | PHOLD(curproc); | ||||
"fwload", 0); | msleep((void *)&fwli, &firmware_mtx, 0, "fwload", 0); | ||||
PRELE(curproc); | |||||
markj: Do you have to PHOLD across this sleep? If the kernel stack is swapped out, the thread… | |||||
Done Inline ActionsGood catch. I've been generous with the amount under the "hold" beyond the msleep. bz: Good catch. I've been generous with the amount under the "hold" beyond the msleep. | |||||
Not Done Inline ActionsReally you only need it across the msleep() call. markj: Really you only need it across the msleep() call. | |||||
} | } | ||||
/* | /* | ||||
* After attempting to load the module, see if the image is registered. | * After attempting to load the module, see if the image is registered. | ||||
*/ | */ | ||||
fp = lookup(imagename); | fp = lookup(imagename); | ||||
if (fp == NULL) { | if (fp == NULL) { | ||||
mtx_unlock(&firmware_mtx); | mtx_unlock(&firmware_mtx); | ||||
return NULL; | return NULL; | ||||
} | } | ||||
found: /* common exit point on success */ | found: /* common exit point on success */ | ||||
if (fp->refcnt == 0 && fp->parent != NULL) | if (fp->refcnt == 0 && fp->parent != NULL) | ||||
fp->parent->refcnt++; | fp->parent->refcnt++; | ||||
fp->refcnt++; | fp->refcnt++; | ||||
mtx_unlock(&firmware_mtx); | mtx_unlock(&firmware_mtx); | ||||
return &fp->fw; | return &fp->fw; | ||||
} | |||||
const struct firmware * | |||||
firmware_get(const char *imagename) | |||||
{ | |||||
return (firmware_get_flags(imagename, 0)); | |||||
} | } | ||||
/* | /* | ||||
* Release a reference to a firmware image returned by firmware_get. | * Release a reference to a firmware image returned by firmware_get. | ||||
* The caller may specify, with the FIRMWARE_UNLOAD flag, its desire | * The caller may specify, with the FIRMWARE_UNLOAD flag, its desire | ||||
* to release the resource, but the flag is only advisory. | * to release the resource, but the flag is only advisory. | ||||
* | * | ||||
* If this is the last reference to the firmware image, and this is an | * If this is the last reference to the firmware image, and this is an | ||||
▲ Show 20 Lines • Show All 156 Lines • Show Last 20 Lines |
Do you have to PHOLD across this sleep? If the kernel stack is swapped out, the thread accessing fwli can trigger a page fault otherwise. The likelihood of this happening is very low.