Give kldunload a -f(orce) argument.

Add a MOD_QUIESCE event for modules.  This should return error (EBUSY)
of the module is in use.

MOD_UNLOAD should now only fail if it is impossible (as opposed to
inconvenient) to unload the module.  Valid reasons are memory references
into the module which cannot be tracked down and eliminated.

When kldunloading, we abandon if MOD_UNLOAD fails, and if -force is
not given, MOD_QUIESCE failing will also prevent the unload.

For backwards compatibility, we treat EOPNOTSUPP from MOD_QUIESCE as
success.

Document that modules should return EOPNOTSUPP for unknown events.
This commit is contained in:
Poul-Henning Kamp 2004-07-13 19:36:59 +00:00
parent 1a946b9fef
commit 65a311fcb2
20 changed files with 123 additions and 36 deletions

View File

@ -33,9 +33,11 @@
.Nd unload a file from the kernel
.Sh SYNOPSIS
.Nm
.Op Fl f
.Op Fl v
.Fl i Ar id
.Nm
.Op Fl f
.Op Fl v
.Op Fl n
.Ar name
@ -47,6 +49,11 @@ utility unloads a file which was previously loaded with
.Pp
The following options are available:
.Bl -tag -width indentXX
.It Fl f
Force the unload.
This ignores error returns to MOD_QUISCE from the module and implies
that the module should be unloaded even if it is currently in use.
The users are left to cope as best they can.
.It Fl v
Be more verbose.
.It Fl i Ar id

View File

@ -48,10 +48,14 @@ main(int argc, char** argv)
int c;
int verbose = 0;
int fileid = 0;
int force = LINKER_UNLOAD_NORMAL;
char* filename = NULL;
while ((c = getopt(argc, argv, "i:n:v")) != -1)
while ((c = getopt(argc, argv, "fi:n:v")) != -1)
switch (c) {
case 'f':
force = LINKER_UNLOAD_FORCE;
break;
case 'i':
fileid = atoi(optarg);
if (!fileid)
@ -93,7 +97,7 @@ main(int argc, char** argv)
printf("Unloading %s, id=%d\n", stat.name, fileid);
}
if (kldunload(fileid) < 0)
if (kldunloadf(fileid, force) < 0)
err(1, "can't unload file");
return 0;

View File

@ -47,19 +47,37 @@ The
.Xr DECLARE_MODULE 9
macro
registers the module with the system.
.Pp
When the module is loaded, the event handler function is called with
the
.Fa what
argument set to
.Dv MOD_LOAD .
On unload,
.Pp
On unload it is first called with
.Fa what
set to MOD_QUIESCE .
If the unload was not forced, a non-zero return will prevent the
unload from happening.
.Pp
If the unload continues
.Fa what
is set to
.Dv MOD_UNLOAD .
If the module returns non-zero to this, the unload will not happen.
.Pp
The difference between MOD_QUIESCE and MOD_UNLOAD is that the module
should fail MOD_QUIESCE if it is currently in use, whereas MOD_UNLOAD
should only fail if it is impossible to unload the module, for instance
because there are memory references to the module which cannot be revoked.
.Pp
When the system is shutting down,
.Fa what
contains the value of
.Dv MOD_SHUTDOWN .
.Pp
The module should return EOPNOTSUPP for unrecognized values of
.Fa what .
.Sh EXAMPLES
.Bd -literal
#include <sys/param.h>

View File

@ -3,7 +3,7 @@
*
* DO NOT EDIT-- this file is automatically generated.
* $FreeBSD$
* created from FreeBSD: src/sys/compat/freebsd32/syscalls.master,v 1.38 2004/07/02 00:35:52 marcel Exp
* created from FreeBSD: src/sys/compat/freebsd32/syscalls.master,v 1.39 2004/07/13 19:35:11 phk Exp
*/
#ifndef _FREEBSD32_SYSPROTO_H_
@ -205,6 +205,10 @@ struct freebsd32_sigaction_args {
struct freebsd32_sigreturn_args {
char sigcntxp_l_[PADL_(const struct freebsd32_ucontext *)]; const struct freebsd32_ucontext * sigcntxp; char sigcntxp_r_[PADR_(const struct freebsd32_ucontext *)];
};
struct kldunloadf_args {
char fileid_l_[PADL_(int)]; int fileid; char fileid_r_[PADR_(int)];
char flags_l_[PADL_(int)]; int flags; char flags_r_[PADR_(int)];
};
int freebsd32_wait4(struct thread *, struct freebsd32_wait4_args *);
int freebsd32_sigaltstack(struct thread *, struct freebsd32_sigaltstack_args *);
int freebsd32_execve(struct thread *, struct freebsd32_execve_args *);
@ -235,6 +239,7 @@ int freebsd32_kevent(struct thread *, struct freebsd32_kevent_args *);
int freebsd32_sendfile(struct thread *, struct freebsd32_sendfile_args *);
int freebsd32_sigaction(struct thread *, struct freebsd32_sigaction_args *);
int freebsd32_sigreturn(struct thread *, struct freebsd32_sigreturn_args *);
int kldunloadf(struct thread *, struct kldunloadf_args *);
#ifdef COMPAT_43

View File

@ -3,7 +3,7 @@
*
* DO NOT EDIT-- this file is automatically generated.
* $FreeBSD$
* created from FreeBSD: src/sys/compat/freebsd32/syscalls.master,v 1.38 2004/07/02 00:35:52 marcel Exp
* created from FreeBSD: src/sys/compat/freebsd32/syscalls.master,v 1.39 2004/07/13 19:35:11 phk Exp
*/
#define FREEBSD32_SYS_syscall 0
@ -301,4 +301,5 @@
#define FREEBSD32_SYS_jail_attach 436
#define FREEBSD32_SYS_thr_suspend 442
#define FREEBSD32_SYS_thr_wake 443
#define FREEBSD32_SYS_MAXSYSCALL 444
#define FREEBSD32_SYS_kldunloadf 444
#define FREEBSD32_SYS_MAXSYSCALL 445

View File

@ -3,7 +3,7 @@
*
* DO NOT EDIT-- this file is automatically generated.
* $FreeBSD$
* created from FreeBSD: src/sys/compat/freebsd32/syscalls.master,v 1.38 2004/07/02 00:35:52 marcel Exp
* created from FreeBSD: src/sys/compat/freebsd32/syscalls.master,v 1.39 2004/07/13 19:35:11 phk Exp
*/
const char *freebsd32_syscallnames[] = {
@ -451,4 +451,5 @@ const char *freebsd32_syscallnames[] = {
"#441", /* 441 = ksem_timedwait */
"thr_suspend", /* 442 = thr_suspend */
"thr_wake", /* 443 = thr_wake */
"kldunloadf", /* 444 = kldunloadf */
};

View File

@ -3,7 +3,7 @@
*
* DO NOT EDIT-- this file is automatically generated.
* $FreeBSD$
* created from FreeBSD: src/sys/compat/freebsd32/syscalls.master,v 1.38 2004/07/02 00:35:52 marcel Exp
* created from FreeBSD: src/sys/compat/freebsd32/syscalls.master,v 1.39 2004/07/13 19:35:11 phk Exp
*/
#include "opt_compat.h"
@ -469,4 +469,5 @@ struct sysent freebsd32_sysent[] = {
{ 0, (sy_call_t *)nosys }, /* 441 = ksem_timedwait */
{ SYF_MPSAFE | AS(thr_suspend_args), (sy_call_t *)thr_suspend }, /* 442 = thr_suspend */
{ SYF_MPSAFE | AS(thr_wake_args), (sy_call_t *)thr_wake }, /* 443 = thr_wake */
{ SYF_MPSAFE | AS(kldunloadf_args), (sy_call_t *)kldunloadf }, /* 444 = kldunloadf */
};

View File

@ -1042,7 +1042,7 @@ digi_loadmoduledata(struct digi_softc *sc)
if (digi_mod->dm_version != DIGI_MOD_VERSION) {
printf("digi_%s.ko: Invalid version %d (need %d)\n",
sc->module, digi_mod->dm_version, DIGI_MOD_VERSION);
linker_file_unload(lf);
linker_file_unload(lf, LINKER_UNLOAD_FORCE);
return (EINVAL);
}
@ -1064,7 +1064,7 @@ digi_loadmoduledata(struct digi_softc *sc)
bcopy(digi_mod->dm_link.data, sc->link.data, sc->link.size);
}
linker_file_unload(lf);
linker_file_unload(lf, LINKER_UNLOAD_FORCE);
return (0);
}

View File

@ -3,7 +3,7 @@
*
* DO NOT EDIT-- this file is automatically generated.
* $FreeBSD$
* created from FreeBSD: src/sys/kern/syscalls.master,v 1.174 2004/07/02 00:35:52 marcel Exp
* created from FreeBSD: src/sys/kern/syscalls.master,v 1.176 2004/07/13 19:35:10 phk Exp
*/
#include "opt_compat.h"
@ -472,4 +472,5 @@ struct sysent sysent[] = {
{ SYF_MPSAFE | AS(ksem_timedwait_args), (sy_call_t *)lkmressys }, /* 441 = ksem_timedwait */
{ SYF_MPSAFE | AS(thr_suspend_args), (sy_call_t *)thr_suspend }, /* 442 = thr_suspend */
{ SYF_MPSAFE | AS(thr_wake_args), (sy_call_t *)thr_wake }, /* 443 = thr_wake */
{ SYF_MPSAFE | AS(kldunloadf_args), (sy_call_t *)kldunloadf }, /* 444 = kldunloadf */
};

View File

@ -466,7 +466,7 @@ linker_make_file(const char *pathname, linker_class_t lc)
}
int
linker_file_unload(linker_file_t file)
linker_file_unload(linker_file_t file, int flags)
{
module_t mod, next;
modlist_t ml, nextml;
@ -500,7 +500,7 @@ linker_file_unload(linker_file_t file)
/*
* Give the module a chance to veto the unload.
*/
if ((error = module_unload(mod)) != 0) {
if ((error = module_unload(mod, flags)) != 0) {
KLD_DPF(FILE, ("linker_file_unload: module %p"
" vetoes unload\n", mod));
goto out;
@ -536,7 +536,7 @@ linker_file_unload(linker_file_t file)
if (file->deps) {
for (i = 0; i < file->ndeps; i++)
linker_file_unload(file->deps[i]);
linker_file_unload(file->deps[i], flags);
free(file->deps, M_LINKER);
file->deps = NULL;
}
@ -789,8 +789,8 @@ kldload(struct thread *td, struct kldload_args *uap)
/*
* MPSAFE
*/
int
kldunload(struct thread *td, struct kldunload_args *uap)
static int
kern_kldunload(struct thread *td, int fileid, int flags)
{
linker_file_t lf;
int error = 0;
@ -803,17 +803,20 @@ kldunload(struct thread *td, struct kldunload_args *uap)
if ((error = suser(td)) != 0)
goto out;
lf = linker_find_file_by_id(uap->fileid);
lf = linker_find_file_by_id(fileid);
if (lf) {
KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs));
if (lf->userrefs == 0) {
/*
* XXX: maybe LINKER_UNLOAD_FORCE should override ?
*/
printf("kldunload: attempt to unload file that was"
" loaded by the kernel\n");
error = EBUSY;
goto out;
}
lf->userrefs--;
error = linker_file_unload(lf);
error = linker_file_unload(lf, flags);
if (error)
lf->userrefs++;
} else
@ -823,6 +826,29 @@ kldunload(struct thread *td, struct kldunload_args *uap)
return (error);
}
/*
* MPSAFE
*/
int
kldunload(struct thread *td, struct kldunload_args *uap)
{
return (kern_kldunload(td, uap->fileid, LINKER_UNLOAD_NORMAL));
}
/*
* MPSAFE
*/
int
kldunloadf(struct thread *td, struct kldunloadf_args *uap)
{
if (uap->flags != LINKER_UNLOAD_NORMAL &&
uap->flags != LINKER_UNLOAD_FORCE)
return (EINVAL);
return (kern_kldunload(td, uap->fileid, uap->flags));
}
/*
* MPSAFE
*/
@ -1250,7 +1276,8 @@ linker_preload(void *arg)
nver) != NULL) {
printf("module %s already"
" present!\n", modname);
linker_file_unload(lf);
linker_file_unload(lf,
LINKER_UNLOAD_FORCE);
TAILQ_REMOVE(&loaded_files,
lf, loaded);
/* we changed tailq next ptr */
@ -1276,7 +1303,7 @@ linker_preload(void *arg)
*/
TAILQ_FOREACH(lf, &loaded_files, loaded) {
printf("KLD file %s is missing dependencies\n", lf->filename);
linker_file_unload(lf);
linker_file_unload(lf, LINKER_UNLOAD_FORCE);
TAILQ_REMOVE(&loaded_files, lf, loaded);
}
@ -1317,7 +1344,7 @@ linker_preload(void *arg)
if (error) {
printf("KLD file %s - could not finalize loading\n",
lf->filename);
linker_file_unload(lf);
linker_file_unload(lf, LINKER_UNLOAD_FORCE);
continue;
}
linker_file_register_modules(lf);
@ -1676,7 +1703,7 @@ linker_load_module(const char *kldname, const char *modname,
break;
if (modname && verinfo &&
modlist_lookup2(modname, verinfo) == NULL) {
linker_file_unload(lfdep);
linker_file_unload(lfdep, LINKER_UNLOAD_FORCE);
error = ENOENT;
break;
}

View File

@ -215,9 +215,15 @@ module_lookupbyid(int modid)
}
int
module_unload(module_t mod)
module_unload(module_t mod, int flags)
{
int error;
error = MOD_EVENT(mod, MOD_QUIESCE);
if (error == EOPNOTSUPP)
error = 0;
if (flags == LINKER_UNLOAD_NORMAL && error != 0)
return (error);
return (MOD_EVENT(mod, MOD_UNLOAD));
}

View File

@ -492,7 +492,7 @@ link_elf_link_preload(linker_class_t cls,
error = parse_dynamic(ef);
if (error) {
linker_file_unload(lf);
linker_file_unload(lf, LINKER_UNLOAD_FORCE);
return error;
}
link_elf_reloc_local(lf);
@ -846,7 +846,7 @@ link_elf_load_file(linker_class_t cls, const char* filename,
out:
if (error && lf)
linker_file_unload(lf);
linker_file_unload(lf, LINKER_UNLOAD_FORCE);
if (shdr)
free(shdr, M_LINKER);
if (firstpage)

View File

@ -588,7 +588,7 @@ link_elf_load_file(linker_class_t cls, const char *filename,
out:
if (error && lf)
linker_file_unload(lf);
linker_file_unload(lf, LINKER_UNLOAD_FORCE);
if (hdr)
free(hdr, M_LINKER);
VOP_UNLOCK(nd.ni_vp, 0, td);

View File

@ -3,7 +3,7 @@
*
* DO NOT EDIT-- this file is automatically generated.
* $FreeBSD$
* created from FreeBSD: src/sys/kern/syscalls.master,v 1.174 2004/07/02 00:35:52 marcel Exp
* created from FreeBSD: src/sys/kern/syscalls.master,v 1.176 2004/07/13 19:35:10 phk Exp
*/
const char *syscallnames[] = {
@ -451,4 +451,5 @@ const char *syscallnames[] = {
"ksem_timedwait", /* 441 = ksem_timedwait */
"thr_suspend", /* 442 = thr_suspend */
"thr_wake", /* 443 = thr_wake */
"kldunloadf", /* 444 = kldunloadf */
};

View File

@ -799,7 +799,7 @@ vfs_domount(
break;
if (vfsp == NULL) {
lf->userrefs--;
linker_file_unload(lf);
linker_file_unload(lf, LINKER_UNLOAD_FORCE);
vput(vp);
return (ENODEV);
}

View File

@ -135,7 +135,7 @@ linker_file_t linker_make_file(const char* _filename, linker_class_t _cls);
/*
* Unload a file, freeing up memory.
*/
int linker_file_unload(linker_file_t _file);
int linker_file_unload(linker_file_t _file, int flags);
/*
* Add a dependency to a file.
@ -275,6 +275,12 @@ struct kld_sym_lookup {
};
#define KLDSYM_LOOKUP 1
/*
* Flags for kldunloadf() and linker_file_unload()
*/
#define LINKER_UNLOAD_NORMAL 0
#define LINKER_UNLOAD_FORCE 1
#ifndef _KERNEL
#include <sys/cdefs.h>
@ -282,6 +288,7 @@ struct kld_sym_lookup {
__BEGIN_DECLS
int kldload(const char* _file);
int kldunload(int _fileid);
int kldunloadf(int _fileid, int flags);
int kldfind(const char* _file);
int kldnext(int _fileid);
int kldstat(int _fileid, struct kld_file_stat* _stat);

View File

@ -42,7 +42,8 @@
typedef enum modeventtype {
MOD_LOAD,
MOD_UNLOAD,
MOD_SHUTDOWN
MOD_SHUTDOWN,
MOD_QUIESCE
} modeventtype_t;
typedef struct module *module_t;
@ -142,7 +143,7 @@ module_t module_lookupbyname(const char *);
module_t module_lookupbyid(int);
void module_reference(module_t);
void module_release(module_t);
int module_unload(module_t);
int module_unload(module_t, int flags);
int module_getid(module_t);
module_t module_getfnext(module_t);
void module_setspecific(module_t, modspecific_t *);

View File

@ -3,7 +3,7 @@
*
* DO NOT EDIT-- this file is automatically generated.
* $FreeBSD$
* created from FreeBSD: src/sys/kern/syscalls.master,v 1.174 2004/07/02 00:35:52 marcel Exp
* created from FreeBSD: src/sys/kern/syscalls.master,v 1.176 2004/07/13 19:35:10 phk Exp
*/
#define SYS_syscall 0
@ -356,4 +356,5 @@
#define SYS_ksem_timedwait 441
#define SYS_thr_suspend 442
#define SYS_thr_wake 443
#define SYS_MAXSYSCALL 444
#define SYS_kldunloadf 444
#define SYS_MAXSYSCALL 445

View File

@ -1,7 +1,7 @@
# FreeBSD system call names.
# DO NOT EDIT-- this file is automatically generated.
# $FreeBSD$
# created from FreeBSD: src/sys/kern/syscalls.master,v 1.174 2004/07/02 00:35:52 marcel Exp
# created from FreeBSD: src/sys/kern/syscalls.master,v 1.176 2004/07/13 19:35:10 phk Exp
MIASM = \
syscall.o \
exit.o \
@ -297,4 +297,5 @@ MIASM = \
kse_switchin.o \
ksem_timedwait.o \
thr_suspend.o \
thr_wake.o
thr_wake.o \
kldunloadf.o

View File

@ -3,7 +3,7 @@
*
* DO NOT EDIT-- this file is automatically generated.
* $FreeBSD$
* created from FreeBSD: src/sys/kern/syscalls.master,v 1.174 2004/07/02 00:35:52 marcel Exp
* created from FreeBSD: src/sys/kern/syscalls.master,v 1.176 2004/07/13 19:35:10 phk Exp
*/
#ifndef _SYS_SYSPROTO_H_
@ -1302,6 +1302,10 @@ struct thr_suspend_args {
struct thr_wake_args {
char id_l_[PADL_(long)]; long id; char id_r_[PADR_(long)];
};
struct kldunloadf_args {
char fileid_l_[PADL_(int)]; int fileid; char fileid_r_[PADR_(int)];
char flags_l_[PADL_(int)]; int flags; char flags_r_[PADR_(int)];
};
int nosys(struct thread *, struct nosys_args *);
void sys_exit(struct thread *, struct sys_exit_args *);
int fork(struct thread *, struct fork_args *);
@ -1596,6 +1600,7 @@ int kse_switchin(struct thread *, struct kse_switchin_args *);
int ksem_timedwait(struct thread *, struct ksem_timedwait_args *);
int thr_suspend(struct thread *, struct thr_suspend_args *);
int thr_wake(struct thread *, struct thr_wake_args *);
int kldunloadf(struct thread *, struct kldunloadf_args *);
#ifdef COMPAT_43