LWN.net Logo

Add module_kernel_thread for threads that live in modules.

From:  NeilBrown <neilb@cse.unsw.edu.au>
To:  Linus Torvalds <torvalds@transmeta.com>
Subject:  [PATCH] Add module_kernel_thread for threads that live in modules.
Date:  Thu, 05 Jun 2003 12:30:32 +1000
Cc:  linux-kernel@vger.kernel.org

Nfsd (and lockd) run kernel threads that currently use
MOD_{INC,DEC}_USE_COUNT to manage references to the module on
behalf of those threads.  This is deprecated and unsafe.

This patch introduces module_kernel_thread which ensures that
references counts are taken and dropped as appropriate for kernel
threads, and uses it for nfsd and lockd.

NeilBrown

### Comments for ChangeSet

If a kernel thread runs code that is in a module, it can be started
with module_kernel_thread, and this will safely managed the reference
counts on the module implied by the existance of the thread.

This is then used for nfsd and lockd.

 ----------- Diffstat output ------------
 ./fs/lockd/svc.c             |    9 ++------
 ./fs/nfsd/nfssvc.c           |   11 +++-------
 ./include/linux/module.h     |   15 +++++++++++++
 ./include/linux/sunrpc/svc.h |    4 +--
 ./kernel/module.c            |   47 +++++++++++++++++++++++++++++++++++++++++++
 ./net/sunrpc/svc.c           |    7 ++++--
 6 files changed, 76 insertions(+), 17 deletions(-)

diff ./fs/lockd/svc.c~current~ ./fs/lockd/svc.c
--- ./fs/lockd/svc.c~current~	2003-06-05 12:04:57.000000000 +1000
+++ ./fs/lockd/svc.c	2003-06-05 12:04:51.000000000 +1000
@@ -80,7 +80,7 @@ static inline void clear_grace_period(vo
 /*
  * This is the lockd kernel thread
  */
-static void
+static int
 lockd(struct svc_rqst *rqstp)
 {
 	struct svc_serv	*serv = rqstp->rq_server;
@@ -88,7 +88,6 @@ lockd(struct svc_rqst *rqstp)
 	unsigned long grace_period_expire;
 
 	/* Lock module and set up kernel thread */
-	MOD_INC_USE_COUNT;
 	lock_kernel();
 
 	/*
@@ -181,9 +180,7 @@ lockd(struct svc_rqst *rqstp)
 	/* release rpciod */
 	rpciod_down();
 
-	/* Release module */
-	unlock_kernel();
-	MOD_DEC_USE_COUNT;
+	return 0;
 }
 
 /*
@@ -238,7 +235,7 @@ lockd_up(void)
 	/*
 	 * Create the kernel thread and wait for it to start.
 	 */
-	error = svc_create_thread(lockd, serv);
+	error = svc_create_thread(lockd, serv, THIS_MODULE);
 	if (error) {
 		printk(KERN_WARNING
 			"lockd_up: create thread failed, error=%d\n", error);

diff ./fs/nfsd/nfssvc.c~current~ ./fs/nfsd/nfssvc.c
--- ./fs/nfsd/nfssvc.c~current~	2003-06-05 12:04:57.000000000 +1000
+++ ./fs/nfsd/nfssvc.c	2003-06-05 12:04:51.000000000 +1000
@@ -48,7 +48,7 @@
 #define	SIG_NOCLEAN	SIGHUP
 
 extern struct svc_program	nfsd_program;
-static void			nfsd(struct svc_rqst *rqstp);
+static int			nfsd(struct svc_rqst *rqstp);
 struct timeval			nfssvc_boot;
 static struct svc_serv 		*nfsd_serv;
 static atomic_t			nfsd_busy;
@@ -114,7 +114,7 @@ nfsd_svc(unsigned short port, int nrserv
 	nrservs -= (nfsd_serv->sv_nrthreads-1);
 	while (nrservs > 0) {
 		nrservs--;
-		error = svc_create_thread(nfsd, nfsd_serv);
+		error = svc_create_thread(nfsd, nfsd_serv, THIS_MODULE);
 		if (error < 0)
 			break;
 	}
@@ -163,7 +163,7 @@ update_thread_usage(int busy_threads)
 /*
  * This is the NFS server kernel thread
  */
-static void
+static int
 nfsd(struct svc_rqst *rqstp)
 {
 	struct svc_serv	*serv = rqstp->rq_server;
@@ -172,7 +172,6 @@ nfsd(struct svc_rqst *rqstp)
 	sigset_t shutdown_mask, allowed_mask;
 
 	/* Lock module and set up kernel thread */
-	MOD_INC_USE_COUNT;
 	lock_kernel();
 	daemonize("nfsd");
 	current->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
@@ -255,9 +254,7 @@ nfsd(struct svc_rqst *rqstp)
 
 	/* Release the thread */
 	svc_exit_thread(rqstp);
-
-	/* Release module */
-	MOD_DEC_USE_COUNT;
+	return 0;
 }
 
 int

diff ./include/linux/module.h~current~ ./include/linux/module.h
--- ./include/linux/module.h~current~	2003-06-05 12:04:57.000000000 +1000
+++ ./include/linux/module.h	2003-06-05 11:34:24.000000000 +1000
@@ -314,6 +314,9 @@ static inline void module_put(struct mod
 	}
 }
 
+extern int module_kernel_thread(int (*fn)(void*), void *arg, 
+				unsigned long flags, struct module *owner);
+
 #else /*!CONFIG_MODULE_UNLOAD*/
 static inline int try_module_get(struct module *module)
 {
@@ -328,6 +331,12 @@ static inline void __module_get(struct m
 #define symbol_put(x) do { } while(0)
 #define symbol_put_addr(p) do { } while(0)
 
+static inline int module_kernel_thread(int (*fn)(void*), void *arg, 
+				       unsigned long flags, struct module *owner)
+{
+	return kernel_thread(fn, arg, flags);
+}
+	       
 #endif /* CONFIG_MODULE_UNLOAD */
 
 /* This is a #define so the string doesn't get put in every .o file */
@@ -419,6 +428,12 @@ static inline int unregister_module_noti
 	return 0;
 }
 
+static inline int module_kernel_thread(int (*fn)(void*), void *arg,
+				       unsigned long flags, struct module *owner)
+{
+	return kernel_thread(fn, arg, flags);
+}
+
 #endif /* CONFIG_MODULES */
 
 #ifdef MODULE

diff ./include/linux/sunrpc/svc.h~current~ ./include/linux/sunrpc/svc.h
--- ./include/linux/sunrpc/svc.h~current~	2003-06-05 12:04:57.000000000 +1000
+++ ./include/linux/sunrpc/svc.h	2003-06-05 12:04:51.000000000 +1000
@@ -276,13 +276,13 @@ struct svc_procedure {
 /*
  * This is the RPC server thread function prototype
  */
-typedef void		(*svc_thread_fn)(struct svc_rqst *);
+typedef int		(*svc_thread_fn)(struct svc_rqst *);
 
 /*
  * Function prototypes.
  */
 struct svc_serv *  svc_create(struct svc_program *, unsigned int);
-int		   svc_create_thread(svc_thread_fn, struct svc_serv *);
+int		   svc_create_thread(svc_thread_fn, struct svc_serv *, struct module *);
 void		   svc_exit_thread(struct svc_rqst *);
 void		   svc_destroy(struct svc_serv *);
 int		   svc_process(struct svc_serv *, struct svc_rqst *);

diff ./kernel/module.c~current~ ./kernel/module.c
--- ./kernel/module.c~current~	2003-06-05 12:04:57.000000000 +1000
+++ ./kernel/module.c	2003-06-05 11:34:24.000000000 +1000
@@ -616,6 +616,53 @@ void symbol_put_addr(void *addr)
 }
 EXPORT_SYMBOL_GPL(symbol_put_addr);
 
+
+/*
+ * If a kernel_thread runs in a module, we might want to
+ * module to be refcounted by the threads.  This code
+ * allows that to happen.
+ * When module_kernel_thread completes, the module will
+ * have been ref-counted iif the thread started.
+ */
+struct kern_thread_info {
+	int (*fn)(void*);
+	void *arg;
+	struct module *owner;
+};
+static int module_kernel_thread_helper(void *arg)
+{
+	struct kern_thread_info *kti = arg;
+	int rv;
+
+	rv = kti->fn(kti->arg);
+	module_put(kti->owner);
+	kfree(kti);
+	return rv;
+}
+int module_kernel_thread(int (*fn)(void*), void *arg, 
+			 unsigned long flags, struct module *owner)
+{
+	struct kern_thread_info *kti;
+	int err;
+
+	if (!owner)
+		return kernel_thread(fn, arg, flags);
+	kti = kmalloc(sizeof(*kti), GFP_KERNEL);
+	if (!kti)
+		return -ENOMEM;
+	kti->fn = fn;
+	kti->arg = arg;
+	kti->owner = owner;
+	__module_get(owner);
+	err = kernel_thread(module_kernel_thread_helper, kti, flags);
+	if (err < 0) {
+		module_put(owner);
+		kfree(kti);
+	}
+	return err;
+}
+EXPORT_SYMBOL(module_kernel_thread);
+
 #else /* !CONFIG_MODULE_UNLOAD */
 static void print_unload_info(struct seq_file *m, struct module *mod)
 {

diff ./net/sunrpc/svc.c~current~ ./net/sunrpc/svc.c
--- ./net/sunrpc/svc.c~current~	2003-06-05 12:04:57.000000000 +1000
+++ ./net/sunrpc/svc.c	2003-06-05 12:06:13.000000000 +1000
@@ -14,6 +14,7 @@
 #include <linux/in.h>
 #include <linux/unistd.h>
 #include <linux/mm.h>
+#include <linux/module.h>
 
 #include <linux/sunrpc/types.h>
 #include <linux/sunrpc/xdr.h>
@@ -150,7 +151,8 @@ svc_release_buffer(struct svc_rqst *rqst
  * Create a server thread
  */
 int
-svc_create_thread(svc_thread_fn func, struct svc_serv *serv)
+svc_create_thread(svc_thread_fn func, struct svc_serv *serv,
+		  struct module *owner)
 {
 	struct svc_rqst	*rqstp;
 	int		error = -ENOMEM;
@@ -169,7 +171,8 @@ svc_create_thread(svc_thread_fn func, st
 
 	serv->sv_nrthreads++;
 	rqstp->rq_server = serv;
-	error = kernel_thread((int (*)(void *)) func, rqstp, 0);
+	error = module_kernel_thread((int(*)(void*))func, rqstp,
+				     0, owner);
 	if (error < 0)
 		goto out_thread;
 	svc_sock_update_bufs(serv);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Copyright © 2003, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds