diff --git a/sbin/kldunload/kldunload.8 b/sbin/kldunload/kldunload.8 index f74965613500..935e5afb71ed 100644 --- a/sbin/kldunload/kldunload.8 +++ b/sbin/kldunload/kldunload.8 @@ -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 diff --git a/sbin/kldunload/kldunload.c b/sbin/kldunload/kldunload.c index be86960b3e17..29da28d07774 100644 --- a/sbin/kldunload/kldunload.c +++ b/sbin/kldunload/kldunload.c @@ -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; diff --git a/share/man/man9/module.9 b/share/man/man9/module.9 index 6dc14b266888..39b95a40fd81 100644 --- a/share/man/man9/module.9 +++ b/share/man/man9/module.9 @@ -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 diff --git a/sys/compat/freebsd32/freebsd32_proto.h b/sys/compat/freebsd32/freebsd32_proto.h index 79fc978316aa..acebc695863b 100644 --- a/sys/compat/freebsd32/freebsd32_proto.h +++ b/sys/compat/freebsd32/freebsd32_proto.h @@ -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 diff --git a/sys/compat/freebsd32/freebsd32_syscall.h b/sys/compat/freebsd32/freebsd32_syscall.h index 524e7832c377..72fe352087d9 100644 --- a/sys/compat/freebsd32/freebsd32_syscall.h +++ b/sys/compat/freebsd32/freebsd32_syscall.h @@ -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 diff --git a/sys/compat/freebsd32/freebsd32_syscalls.c b/sys/compat/freebsd32/freebsd32_syscalls.c index 6d73c55e1960..291f8c4d922f 100644 --- a/sys/compat/freebsd32/freebsd32_syscalls.c +++ b/sys/compat/freebsd32/freebsd32_syscalls.c @@ -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 */ }; diff --git a/sys/compat/freebsd32/freebsd32_sysent.c b/sys/compat/freebsd32/freebsd32_sysent.c index dc4fd105a062..26144c6df1d6 100644 --- a/sys/compat/freebsd32/freebsd32_sysent.c +++ b/sys/compat/freebsd32/freebsd32_sysent.c @@ -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 */ }; diff --git a/sys/dev/digi/digi.c b/sys/dev/digi/digi.c index 68081d5f346c..b08cd5892f0b 100644 --- a/sys/dev/digi/digi.c +++ b/sys/dev/digi/digi.c @@ -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); } diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c index 9d0ecade6a44..1185cf817f81 100644 --- a/sys/kern/init_sysent.c +++ b/sys/kern/init_sysent.c @@ -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 */ }; diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c index 4ad549eec183..d2a2329a4880 100644 --- a/sys/kern/kern_linker.c +++ b/sys/kern/kern_linker.c @@ -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; } diff --git a/sys/kern/kern_module.c b/sys/kern/kern_module.c index 3832922f5961..158612e822c1 100644 --- a/sys/kern/kern_module.c +++ b/sys/kern/kern_module.c @@ -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)); } diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c index 0f00d904a978..5679cbf2f3fa 100644 --- a/sys/kern/link_elf.c +++ b/sys/kern/link_elf.c @@ -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) diff --git a/sys/kern/link_elf_obj.c b/sys/kern/link_elf_obj.c index 9049880fe8fb..e5fa30e98045 100644 --- a/sys/kern/link_elf_obj.c +++ b/sys/kern/link_elf_obj.c @@ -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); diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c index 202c7d136c77..9a2f79556b91 100644 --- a/sys/kern/syscalls.c +++ b/sys/kern/syscalls.c @@ -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 */ }; diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c index d697499a80a7..cf7b7c127257 100644 --- a/sys/kern/vfs_mount.c +++ b/sys/kern/vfs_mount.c @@ -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); } diff --git a/sys/sys/linker.h b/sys/sys/linker.h index 1815c47a607f..923febeb95b2 100644 --- a/sys/sys/linker.h +++ b/sys/sys/linker.h @@ -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 @@ -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); diff --git a/sys/sys/module.h b/sys/sys/module.h index 39f138d206b0..5beb9bb6f22c 100644 --- a/sys/sys/module.h +++ b/sys/sys/module.h @@ -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 *); diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h index 562de1331611..8767ff11aa82 100644 --- a/sys/sys/syscall.h +++ b/sys/sys/syscall.h @@ -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 diff --git a/sys/sys/syscall.mk b/sys/sys/syscall.mk index 2eac19a6abaf..9354f5aa7c63 100644 --- a/sys/sys/syscall.mk +++ b/sys/sys/syscall.mk @@ -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 diff --git a/sys/sys/sysproto.h b/sys/sys/sysproto.h index a8e3a6b5a7dd..ff283636feca 100644 --- a/sys/sys/sysproto.h +++ b/sys/sys/sysproto.h @@ -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