diff --git a/sys/compat/linuxkpi/common/include/linux/moduleparam.h b/sys/compat/linuxkpi/common/include/linux/moduleparam.h --- a/sys/compat/linuxkpi/common/include/linux/moduleparam.h +++ b/sys/compat/linuxkpi/common/include/linux/moduleparam.h @@ -36,6 +36,8 @@ #include +int lkpi_sysctl_handle_charp(SYSCTL_HANDLER_ARGS); + #ifndef LINUXKPI_PARAM_PARENT #define LINUXKPI_PARAM_PARENT _compat_linuxkpi #endif @@ -104,9 +106,10 @@ #define LINUXKPI_PARAM_charp(name, var, perm) \ extern const char LINUXKPI_PARAM_DESC(name)[]; \ - LINUXKPI_PARAM_PASS(SYSCTL_STRING(LINUXKPI_PARAM_PARENT, OID_AUTO, \ - LINUXKPI_PARAM_NAME(name), LINUXKPI_PARAM_PERM(perm), &(var), 0, \ - LINUXKPI_PARAM_DESC(name))) + LINUXKPI_PARAM_PASS(SYSCTL_PROC(LINUXKPI_PARAM_PARENT, OID_AUTO, \ + LINUXKPI_PARAM_NAME(name), \ + LINUXKPI_PARAM_PERM(perm) | CTLTYPE_STRING | CTLFLAG_MPSAFE, \ + &(var), 0, lkpi_sysctl_handle_charp, "A", LINUXKPI_PARAM_DESC(name))) #define module_param_string(name, str, len, perm) \ extern const char LINUXKPI_PARAM_DESC(name)[]; \ diff --git a/sys/compat/linuxkpi/common/src/linux_compat.c b/sys/compat/linuxkpi/common/src/linux_compat.c --- a/sys/compat/linuxkpi/common/src/linux_compat.c +++ b/sys/compat/linuxkpi/common/src/linux_compat.c @@ -117,6 +117,7 @@ &lkpi_net_maxpps, 0, "Limit number of LinuxKPI net messages per second."); MALLOC_DEFINE(M_KMALLOC, "lkpikmalloc", "Linux kmalloc compat"); +static MALLOC_DEFINE(M_LKPI_MODPARM, "lkpi_modparm", "LinuxKPI module parameters"); #include /* Undo Linux compat changes. */ @@ -2632,6 +2633,54 @@ lkpi_net_maxpps)); } +static struct rwlock lkpi_charp_lock; +RW_SYSINIT(lkpi_charp_init, &lkpi_charp_lock, "lkpi charp module param lock"); + +int +lkpi_sysctl_handle_charp(SYSCTL_HANDLER_ARGS) +{ + char **charp = arg1; + int error = 0; + + rw_rlock(&lkpi_charp_lock); + + if (req->oldptr != NULL) { + error = SYSCTL_OUT_STR(req, *charp == NULL ? "" : *charp); + } else { + error = SYSCTL_OUT(req, NULL, *charp == NULL ? 1 : strlen(*charp) + 1); + } + if (error || !req->newptr) { + rw_runlock(&lkpi_charp_lock); + return (error); + } + + if (!rw_try_upgrade(&lkpi_charp_lock)) { + rw_runlock(&lkpi_charp_lock); + rw_wlock(&lkpi_charp_lock); + } + + if (*charp != NULL) + free(*charp, M_LKPI_MODPARM); + + if (req->newlen == 0) { + *charp = NULL; + goto end; + } + + *charp = malloc(req->newlen + 1, M_LKPI_MODPARM, M_WAITOK); + error = SYSCTL_IN(req, *charp, req->newlen); + if (error) { + free(*charp, M_LKPI_MODPARM); + *charp = NULL; + } else { + (*charp)[req->newlen] = '\0'; + } + +end: + rw_wunlock(&lkpi_charp_lock); + return (error); +} + #if defined(__i386__) || defined(__amd64__) bool linux_cpu_has_clflush; #endif