mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-11-07 04:36:52 -10:00
exterror(9): dynamic kernel categories
Make it possible to define categories without compiling their paths into libc (important for third-party modules). The EXTERR_CATEGORY_DYNAMIC macro can be defined to a string describing the compilation unit (generally the path relative to src/sys) which takes the place of EXTERR_CATEGORY. These strings are assembled in linker sets with category numbers assigned at system startup or module load time. The strings can be retrieved from the kern.exterr.categories.<category> sysctl. Reviewed by: kib Sponsored by: Innovate UK Differential Revision: https://reviews.freebsd.org/D58237
This commit is contained in:
1 parent
bbf95a9b84
commit
295f102309
6 files changed
+264
-2
No files matched your search
@@ -6,7 +6,7 @@
|
||||
.\" Konstantin Belousov <[email protected]> under sponsorship
|
||||
.\" from the FreeBSD Foundation.
|
||||
.\"
|
||||
.Dd July 21, 2026
|
||||
.Dd August 3, 2026
|
||||
.Dt EXTERROR 9
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -14,6 +14,7 @@
|
||||
.Nd provide extended error information to userspace
|
||||
.Sh SYNOPSIS
|
||||
.Bd -literal -offset left -compact
|
||||
#define EXTERR_CATEGORY_DYNAMIC "path/to/this/file.c"
|
||||
#define EXTERR_CATEGORY EXTERR_CAT_MYCATEGORY
|
||||
.Ed
|
||||
.In sys/exterrvar.h
|
||||
@@ -56,7 +57,13 @@ is to make it easier for a user to identify the cause of the error.
|
||||
Before
|
||||
.Nm
|
||||
can be used in the given source .c file, the category of extended errors
|
||||
should be allocated in the
|
||||
must be defined.
|
||||
This can be done by setting the
|
||||
.Va EXTERR_CATEGORY_DYNAMIC
|
||||
macro to a string containing the name of the file relative to
|
||||
.Pa src/sys
|
||||
directory.
|
||||
Alternatively, an entry may be allocated in the
|
||||
.In sys/exterr_cat.h
|
||||
file.
|
||||
The category is the unique integer, that, together with the source
|
||||
|
||||
@@ -3988,6 +3988,7 @@ kern/subr_disk.c standard
|
||||
kern/subr_early.c standard
|
||||
kern/subr_epoch.c standard
|
||||
kern/subr_eventhandler.c standard
|
||||
kern/subr_exterr.c standard
|
||||
kern/subr_fattime.c standard
|
||||
kern/subr_firmware.c optional firmware
|
||||
kern/subr_filter.c standard
|
||||
|
||||
@@ -331,6 +331,40 @@ linker_file_register_sysctls(linker_file_t lf, bool enable)
|
||||
sx_xlock(&kld_sx);
|
||||
}
|
||||
|
||||
static void
|
||||
linker_file_register_exterr(linker_file_t lf)
|
||||
{
|
||||
struct exterr_cat **start, **stop;
|
||||
|
||||
KLD_DPF(FILE,
|
||||
(__func__ ": registering exterror categories for %s\n",
|
||||
lf->filename));
|
||||
|
||||
sx_assert(&kld_sx, SA_XLOCKED);
|
||||
|
||||
if (linker_file_lookup_set(lf, "exterr_cats", &start, &stop, NULL) != 0)
|
||||
return;
|
||||
|
||||
exterr_cat_register_module(start, stop);
|
||||
}
|
||||
|
||||
static void
|
||||
linker_file_unregister_exterr(linker_file_t lf)
|
||||
{
|
||||
struct exterr_cat **start, **stop;
|
||||
|
||||
KLD_DPF(FILE,
|
||||
(__func__ ": unregistering exterror categories for %s\n",
|
||||
lf->filename));
|
||||
|
||||
sx_assert(&kld_sx, SA_XLOCKED);
|
||||
|
||||
if (linker_file_lookup_set(lf, "exterr_cats", &start, &stop, NULL) != 0)
|
||||
return;
|
||||
|
||||
exterr_cat_unregister_module(start, stop);
|
||||
}
|
||||
|
||||
/*
|
||||
* Invoke the LINKER_CTF_GET implementation for this file. Existing
|
||||
* implementations will load CTF info from the filesystem upon the first call
|
||||
@@ -500,6 +534,7 @@ linker_load_file(const char *filename, linker_file_t *result)
|
||||
#ifdef VIMAGE
|
||||
LINKER_PROPAGATE_VNETS(lf);
|
||||
#endif
|
||||
linker_file_register_exterr(lf);
|
||||
linker_file_sysinit(lf);
|
||||
lf->flags |= LINKER_FILE_LINKED;
|
||||
|
||||
@@ -775,6 +810,7 @@ linker_file_unload(linker_file_t file, int flags)
|
||||
if ((file->flags & LINKER_FILE_LINKED) != 0) {
|
||||
file->flags &= ~LINKER_FILE_LINKED;
|
||||
linker_file_unregister_sysctls(file);
|
||||
linker_file_unregister_exterr(file);
|
||||
linker_file_sysuninit(file);
|
||||
EVENTHANDLER_INVOKE(kld_unload, file->filename, file->address,
|
||||
file->size);
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
/*-
|
||||
* Copyright (c) 2026 Capabilities Limited
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* This software was developed by Capabilities Limited with funding from
|
||||
* Innovate UK and the Department for Science, Innovation and Technology
|
||||
* for the adoption and diffusion of CHERI technology under project
|
||||
* 10168042 (“CheriBSD feature extraction, maturity, and testing”).
|
||||
*
|
||||
*/
|
||||
|
||||
#define EXTERR_CATEGORY_DYNAMIC "kern/subr_exterr.c"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/exterrvar.h>
|
||||
#include <sys/exterr_cat.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/libkern.h>
|
||||
#include <sys/linker_set.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/linker.h> /* Need MALLOC_DECLARE */
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/stddef.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
struct exterr_cat_span {
|
||||
unsigned int first;
|
||||
unsigned int count;
|
||||
struct exterr_cat **cat_sets;
|
||||
TAILQ_ENTRY(exterr_cat_span) entries;
|
||||
};
|
||||
|
||||
TAILQ_HEAD(exterr_cat_span_head, exterr_cat_span) cat_span_head;
|
||||
|
||||
SET_DECLARE(exterr_cats, struct exterr_cat);
|
||||
|
||||
static struct exterr_cat_span kern_cats;
|
||||
unsigned int ncats;
|
||||
struct rwlock cat_lock;
|
||||
|
||||
static bool
|
||||
exterr_cat_register_set(struct exterr_cat_span *span, struct exterr_cat **start,
|
||||
struct exterr_cat **stop)
|
||||
{
|
||||
struct exterr_cat **catp;
|
||||
ptrdiff_t count;
|
||||
|
||||
count = stop - start;
|
||||
if (count < 1)
|
||||
return (true);
|
||||
|
||||
rw_wlock(&cat_lock);
|
||||
|
||||
if (ncats + count < ncats) {
|
||||
printf("too many exterror categories\n");
|
||||
rw_wunlock(&cat_lock);
|
||||
return (false);
|
||||
}
|
||||
|
||||
span->first = ncats + 1;
|
||||
for (catp = start; catp < stop; catp++)
|
||||
(*catp)->cat = ++ncats;
|
||||
span->count = count;
|
||||
span->cat_sets = start;
|
||||
TAILQ_INSERT_TAIL(&cat_span_head, span, entries);
|
||||
|
||||
rw_wunlock(&cat_lock);
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
void
|
||||
exterr_cat_register_module(struct exterr_cat **start, struct exterr_cat **stop)
|
||||
{
|
||||
struct exterr_cat_span *span;
|
||||
|
||||
span = malloc(sizeof(*span), M_LINKER, M_WAITOK | M_ZERO);
|
||||
if (!exterr_cat_register_set(span, start, stop))
|
||||
free(span, M_LINKER);
|
||||
}
|
||||
|
||||
void
|
||||
exterr_cat_unregister_module(struct exterr_cat **start,
|
||||
struct exterr_cat **stop)
|
||||
{
|
||||
struct exterr_cat_span *span;
|
||||
|
||||
if (stop - start < 1)
|
||||
return;
|
||||
|
||||
rw_wlock(&cat_lock);
|
||||
|
||||
TAILQ_FOREACH(span, &cat_span_head, entries) {
|
||||
if (span->cat_sets == start) {
|
||||
MPASS(span->first > 1);
|
||||
MPASS(span->count == stop - start);
|
||||
TAILQ_REMOVE(&cat_span_head, span, entries);
|
||||
break;
|
||||
}
|
||||
}
|
||||
KASSERT(span != NULL, ("start not found in spans"));
|
||||
|
||||
/*
|
||||
* NB: we leak category numbers on module unload because we can't
|
||||
* reasonably know which ones are in use in running software.
|
||||
*/
|
||||
|
||||
rw_wunlock(&cat_lock);
|
||||
|
||||
free(span, M_LINKER);
|
||||
}
|
||||
|
||||
static void
|
||||
exterr_cat_register_kern(void *arg)
|
||||
{
|
||||
rw_init(&cat_lock, "exterr dynamic categories");
|
||||
|
||||
TAILQ_INIT(&cat_span_head);
|
||||
|
||||
if (SET_COUNT(exterr_cats) == 0)
|
||||
return;
|
||||
|
||||
exterr_cat_register_set(&kern_cats, SET_BEGIN(exterr_cats),
|
||||
SET_LIMIT(exterr_cats));
|
||||
}
|
||||
SYSINIT(exterr, SI_SUB_KMEM, SI_ORDER_FIRST, exterr_cat_register_kern, NULL);
|
||||
|
||||
static int
|
||||
sysctl_exterr_categories(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct exterr_cat_span *span;
|
||||
const struct exterr_cat *cat = NULL;
|
||||
int idx;
|
||||
|
||||
if (arg2 != 1)
|
||||
return (EXTERROR(EINVAL,
|
||||
"too many args to kern.exterr.categories %d", arg2));
|
||||
|
||||
idx = *(int *)arg1;
|
||||
|
||||
rw_rlock(&cat_lock);
|
||||
if (idx < 1 || idx > ncats) {
|
||||
rw_runlock(&cat_lock);
|
||||
return (EXTERROR(EINVAL, "category %d out of range (1...%d)",
|
||||
idx, ncats));
|
||||
}
|
||||
|
||||
TAILQ_FOREACH(span, &cat_span_head, entries) {
|
||||
if (idx < span->first)
|
||||
break; /* Not here any more */
|
||||
|
||||
if (idx < span->first + span->count)
|
||||
cat = span->cat_sets[idx - span->first];
|
||||
}
|
||||
rw_runlock(&cat_lock);
|
||||
|
||||
if (cat == NULL)
|
||||
return (EXTERROR(ENOENT, "category not found %d", idx));
|
||||
MPASS(cat->cat == idx);
|
||||
return (SYSCTL_OUT(req, cat->file, strlen(cat->file) + 1));
|
||||
}
|
||||
|
||||
SYSCTL_NODE(_kern, OID_AUTO, exterr, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
|
||||
"Extended error information");
|
||||
SYSCTL_UINT(_kern_exterr, OID_AUTO, ncategories, CTLFLAG_RD | CTLFLAG_MPSAFE,
|
||||
&ncats, 0, "Number of dynamic categories");
|
||||
SYSCTL_NODE(_kern_exterr, OID_AUTO, categories, CTLFLAG_RD | CTLFLAG_MPSAFE,
|
||||
sysctl_exterr_categories, "Extended error categories");
|
||||
@@ -14,6 +14,9 @@
|
||||
#include <sys/_exterr.h>
|
||||
#include <sys/_uexterror.h>
|
||||
#include <sys/exterr_cat.h>
|
||||
#ifdef EXTERR_CATEGORY_DYNAMIC
|
||||
#include <sys/linker_set.h>
|
||||
#endif
|
||||
|
||||
#define UEXTERROR_MAXLEN 256
|
||||
|
||||
@@ -27,8 +30,23 @@
|
||||
|
||||
#ifdef _KERNEL
|
||||
|
||||
struct exterr_cat {
|
||||
unsigned int cat;
|
||||
const char *file;
|
||||
};
|
||||
|
||||
struct thread;
|
||||
|
||||
#ifdef EXTERR_CATEGORY_DYNAMIC
|
||||
#ifdef EXTERR_STRINGS
|
||||
static struct exterr_cat __dynamic_cat = { .file = EXTERR_CATEGORY_DYNAMIC };
|
||||
DATA_WSET(exterr_cats, __dynamic_cat);
|
||||
#define EXTERR_CATEGORY (__dynamic_cat.cat | EXTERR_CAT_SRC_KERN_DYNAMIC)
|
||||
#else
|
||||
#define EXTERR_CATEGORY EXTERR_CAT_NONE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef EXTERR_CATEGORY
|
||||
#error "Specify error category before including sys/exterrvar.h"
|
||||
#endif
|
||||
@@ -78,6 +96,10 @@ int exterr_set(int eerror, int category, const char *mmsg, uint64ptr_t pp1,
|
||||
uint64ptr_t pp2, int line);
|
||||
int exterr_to_ue(struct thread *td, struct uexterror *ue);
|
||||
void ktrexterr(struct thread *td);
|
||||
void exterr_cat_register_module(struct exterr_cat **start,
|
||||
struct exterr_cat **stop);
|
||||
void exterr_cat_unregister_module(struct exterr_cat **start,
|
||||
struct exterr_cat **stop);
|
||||
|
||||
#else /* !_KERNEL */
|
||||
|
||||
|
||||
@@ -23,8 +23,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/exterrvar.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <atf-c.h>
|
||||
#include <errno.h>
|
||||
@@ -121,11 +123,36 @@ ATF_TC_BODY(gettext_noextended_after_extended, tc)
|
||||
ATF_CHECK_STREQ(exterr, "");
|
||||
}
|
||||
|
||||
ATF_TC(exterr_dynamic_categories);
|
||||
ATF_TC_HEAD(exterr_dynamic_categories, tc)
|
||||
{
|
||||
atf_tc_set_md_var(tc, "descr",
|
||||
"directly check there is at least one registered category");
|
||||
}
|
||||
ATF_TC_BODY(exterr_dynamic_categories, tc)
|
||||
{
|
||||
int mib[4];
|
||||
size_t len;
|
||||
char filename_buf[128];
|
||||
|
||||
len = nitems(mib);
|
||||
ATF_REQUIRE_EQ(sysctlnametomib("kern.exterr.categories", mib, &len),
|
||||
0);
|
||||
mib[3] = 1;
|
||||
len = sizeof(filename_buf);
|
||||
ATF_REQUIRE_EQ(sysctl(mib, nitems(mib), filename_buf, &len, NULL, 0),
|
||||
0);
|
||||
printf("%s\n", filename_buf);
|
||||
/* We can't know what it is, but make sure it's non-empty */
|
||||
ATF_REQUIRE(strlen(filename_buf) > 1);
|
||||
}
|
||||
|
||||
ATF_TP_ADD_TCS(tp)
|
||||
{
|
||||
ATF_TP_ADD_TC(tp, gettext_extended);
|
||||
ATF_TP_ADD_TC(tp, gettext_noextended);
|
||||
ATF_TP_ADD_TC(tp, gettext_noextended_after_extended);
|
||||
ATF_TP_ADD_TC(tp, exterr_dynamic_categories);
|
||||
|
||||
return (atf_no_error());
|
||||
}
|
||||
Reference in new issue
Block a user