diff --git a/sys/kern/kern_environment.c b/sys/kern/kern_environment.c --- a/sys/kern/kern_environment.c +++ b/sys/kern/kern_environment.c @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -666,6 +667,7 @@ kenvp[i + 1] = NULL; mtx_unlock(&kenv_lock); } + EVENTHANDLER_INVOKE(setenv, name); return (0); } @@ -689,6 +691,7 @@ kenvp[i] = NULL; mtx_unlock(&kenv_lock); zfree(oldenv, M_KENV); + EVENTHANDLER_INVOKE(unsetenv, name); return (0); } mtx_unlock(&kenv_lock); diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c --- a/sys/kern/kern_sysctl.c +++ b/sys/kern/kern_sysctl.c @@ -127,6 +127,7 @@ int recurse); static int sysctl_old_kernel(struct sysctl_req *, const void *, size_t); static int sysctl_new_kernel(struct sysctl_req *, void *, size_t); +static int name2oid(char *, int *, int *, struct sysctl_oid **); static struct sysctl_oid * sysctl_find_oidname(const char *name, struct sysctl_oid_list *list) @@ -512,8 +513,14 @@ if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE && (oidp->oid_kind & CTLFLAG_TUN) != 0 && (oidp->oid_kind & CTLFLAG_NOFETCH) == 0) { - /* only fetch value once */ - oidp->oid_kind |= CTLFLAG_NOFETCH; +#ifdef VIMAGE + /* + * Can fetch value multiple times for VNET loader tunables. + * Only fetch once for non-VNET loader tunables. + */ + if ((oidp->oid_kind & CTLFLAG_VNET) == 0) +#endif + oidp->oid_kind |= CTLFLAG_NOFETCH; /* try to fetch value from kernel environment */ sysctl_load_tunable_by_oid_locked(oidp); } @@ -969,6 +976,102 @@ } SYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_FIRST, sysctl_register_all, NULL); +#ifdef VIMAGE +static void +sysctl_setenv_vnet(void *arg __unused, char *name) +{ + struct sysctl_oid *oidp; + int oid[CTL_MAXNAME]; + int error, nlen; + + SYSCTL_WLOCK(); + error = name2oid(name, oid, &nlen, &oidp); + if (error) + goto out; + + if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE && + (oidp->oid_kind & CTLFLAG_VNET) != 0 && + (oidp->oid_kind & CTLFLAG_TUN) != 0 && + (oidp->oid_kind & CTLFLAG_NOFETCH) == 0) { + /* Update value from kernel environment */ + sysctl_load_tunable_by_oid_locked(oidp); + } +out: + SYSCTL_WUNLOCK(); +} + +static void +sysctl_unsetenv_vnet(void *arg __unused, char *name) +{ + struct sysctl_oid *oidp; + int oid[CTL_MAXNAME]; + int error, nlen; + + SYSCTL_WLOCK(); + /* + * The setenv / unsetenv event handlers are invoked by kern_setenv() / + * kern_unsetenv() without exclusive locks. It is rare but still possible + * that the invoke order of event handlers is different from that of + * kern_setenv() and kern_unsetenv(). + * Re-check environment variable string to make sure it is unset. + */ + if (testenv(name)) + goto out; + error = name2oid(name, oid, &nlen, &oidp); + if (error) + goto out; + + if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE && + (oidp->oid_kind & CTLFLAG_VNET) != 0 && + (oidp->oid_kind & CTLFLAG_TUN) != 0 && + (oidp->oid_kind & CTLFLAG_NOFETCH) == 0) { + size_t size; + + switch (oidp->oid_kind & CTLTYPE) { + case CTLTYPE_INT: + case CTLTYPE_UINT: + size = sizeof(int); + break; + case CTLTYPE_LONG: + case CTLTYPE_ULONG: + size = sizeof(long); + break; + case CTLTYPE_S8: + case CTLTYPE_U8: + size = sizeof(int8_t); + break; + case CTLTYPE_S16: + case CTLTYPE_U16: + size = sizeof(int16_t); + break; + case CTLTYPE_S32: + case CTLTYPE_U32: + size = sizeof(int32_t); + break; + case CTLTYPE_S64: + case CTLTYPE_U64: + size = sizeof(int64_t); + break; + case CTLTYPE_STRING: + MPASS(oidp->oid_arg2 > 0); + size = oidp->oid_arg2; + break; + default: + goto out; + } + vnet_restore_init(oidp->oid_arg1, size); + } +out: + SYSCTL_WUNLOCK(); +} + +/* + * Register the kernel's setenv / unsetenv events. + */ +EVENTHANDLER_DEFINE(setenv, sysctl_setenv_vnet, NULL, EVENTHANDLER_PRI_ANY); +EVENTHANDLER_DEFINE(unsetenv, sysctl_unsetenv_vnet, NULL, EVENTHANDLER_PRI_ANY); +#endif + /* * "Staff-functions" * diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c --- a/sys/kern/link_elf.c +++ b/sys/kern/link_elf.c @@ -506,6 +506,7 @@ TAILQ_INIT(&set_pcpu_list); #ifdef VIMAGE TAILQ_INIT(&set_vnet_list); + vnet_save_init((void *)VNET_START, VNET_STOP - VNET_START); #endif } @@ -767,6 +768,7 @@ return (ENOSPC); } memcpy((void *)ef->vnet_base, (void *)ef->vnet_start, size); + vnet_save_init((void *)ef->vnet_base, size); elf_set_add(&set_vnet_list, ef->vnet_start, ef->vnet_stop, ef->vnet_base); diff --git a/sys/kern/link_elf_obj.c b/sys/kern/link_elf_obj.c --- a/sys/kern/link_elf_obj.c +++ b/sys/kern/link_elf_obj.c @@ -547,6 +547,8 @@ memcpy(vnet_data, ef->progtab[pb].addr, ef->progtab[pb].size); ef->progtab[pb].addr = vnet_data; + vnet_save_init(ef->progtab[pb].addr, + ef->progtab[pb].size); #endif } else if ((ef->progtab[pb].name != NULL && strcmp(ef->progtab[pb].name, ".ctors") == 0) || @@ -1120,6 +1122,12 @@ } else bzero(ef->progtab[pb].addr, shdr[i].sh_size); +#ifdef VIMAGE + if (ef->progtab[pb].addr != (void *)mapbase && + strcmp(ef->progtab[pb].name, VNET_SETNAME) == 0) + vnet_save_init(ef->progtab[pb].addr, + ef->progtab[pb].size); +#endif /* Update all symbol values with the offset. */ for (j = 0; j < ef->ddbsymcnt; j++) { es = &ef->ddbsymtab[j]; diff --git a/sys/net/vnet.h b/sys/net/vnet.h --- a/sys/net/vnet.h +++ b/sys/net/vnet.h @@ -311,6 +311,12 @@ void vnet_data_copy(void *start, int size); void vnet_data_free(void *start_arg, int size); +/* + * Interfaces to manipulate the initial values of virtualized global variables. + */ +void vnet_save_init(void *, size_t); +void vnet_restore_init(void *, size_t); + /* * Virtual sysinit mechanism, allowing network stack components to declare * startup and shutdown methods to be run when virtual network stack diff --git a/sys/net/vnet.c b/sys/net/vnet.c --- a/sys/net/vnet.c +++ b/sys/net/vnet.c @@ -178,6 +178,11 @@ */ VNET_DEFINE_STATIC(char, modspace[VNET_MODMIN] __aligned(__alignof(void *))); +/* + * A copy of the initial values of all virtualized global variables. + */ +static uintptr_t vnet_init_var; + /* * Global lists of subsystem constructor and destructors for vnets. They are * registered via VNET_SYSINIT() and VNET_SYSUNINIT(). Both lists are @@ -356,6 +361,7 @@ df->vnd_len = VNET_MODMIN; TAILQ_INSERT_HEAD(&vnet_data_free_head, df, vnd_link); sx_init(&vnet_data_free_lock, "vnet_data alloc lock"); + vnet_init_var = (uintptr_t)malloc(VNET_BYTES, M_VNET_DATA, M_WAITOK); } SYSINIT(vnet_data, SI_SUB_KLD, SI_ORDER_FIRST, vnet_data_startup, NULL); @@ -473,6 +479,33 @@ VNET_LIST_RUNLOCK(); } +/* + * Save a copy of the initial values of virtualized global variables. + */ +void +vnet_save_init(void *start, size_t size) +{ + MPASS(vnet_init_var != 0); + MPASS(VNET_START <= (uintptr_t)start && + (uintptr_t)start + size <= VNET_STOP); + memcpy((void *)(vnet_init_var + ((uintptr_t)start - VNET_START)), + start, size); +} + +/* + * Restore the 'master' copies of virtualized global variables to theirs + * initial values. + */ +void +vnet_restore_init(void *start, size_t size) +{ + MPASS(vnet_init_var != 0); + MPASS(VNET_START <= (uintptr_t)start && + (uintptr_t)start + size <= VNET_STOP); + memcpy(start, + (void *)(vnet_init_var + ((uintptr_t)start - VNET_START)), size); +} + /* * Support for special SYSINIT handlers registered via VNET_SYSINIT() * and VNET_SYSUNINIT(). diff --git a/sys/sys/eventhandler.h b/sys/sys/eventhandler.h --- a/sys/sys/eventhandler.h +++ b/sys/sys/eventhandler.h @@ -326,4 +326,9 @@ typedef void (*rt_addrmsg_fn)(void *, struct ifaddr *, int); EVENTHANDLER_DECLARE(rt_addrmsg, rt_addrmsg_fn); +/* Kernel environment variable change event */ +typedef void (*env_change_fn)(void *, const char *); +EVENTHANDLER_DECLARE(setenv, env_change_fn); +EVENTHANDLER_DECLARE(unsetenv, env_change_fn); + #endif /* _SYS_EVENTHANDLER_H_ */