aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2008-02-11 10:00:20 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2008-02-13 23:24:04 -0500
commit8e60029f403781b8a63b7ffdb7dc1faff6ca651e (patch)
tree9eb13d36a8951ef160250bb973648426b456c46b
parente760e716d47b48caf98da348368fd41b4a9b9e7e (diff)
NFS: fix reference counting for NFSv4 callback thread
The reference counting for the NFSv4 callback thread stays artificially high. When this thread comes down, it doesn't properly tear down the svc_serv, causing a memory leak. In my testing on an older kernel on x86_64, memory would leak out of the 8k kmalloc slab. So, we're leaking at least a page of memory every time the thread comes down. svc_create() creates the svc_serv with a sv_nrthreads count of 1, and then svc_create_thread() increments that count. Whenever the callback thread is started it has a sv_nrthreads count of 2. When coming down, it calls svc_exit_thread() which decrements that count and if it hits 0, it tears everything down. That never happens here since the count is always at 2 when the thread exits. The problem is that nfs_callback_up() should be calling svc_destroy() on the svc_serv on both success and failure. This is how lockd_up_proto() handles the reference counting, and doing that here fixes the leak. Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r--fs/nfs/callback.c18
1 files changed, 12 insertions, 6 deletions
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index bd185a572a23..ecc06c619494 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -105,7 +105,7 @@ static void nfs_callback_svc(struct svc_rqst *rqstp)
105 */ 105 */
106int nfs_callback_up(void) 106int nfs_callback_up(void)
107{ 107{
108 struct svc_serv *serv; 108 struct svc_serv *serv = NULL;
109 int ret = 0; 109 int ret = 0;
110 110
111 lock_kernel(); 111 lock_kernel();
@@ -122,24 +122,30 @@ int nfs_callback_up(void)
122 ret = svc_create_xprt(serv, "tcp", nfs_callback_set_tcpport, 122 ret = svc_create_xprt(serv, "tcp", nfs_callback_set_tcpport,
123 SVC_SOCK_ANONYMOUS); 123 SVC_SOCK_ANONYMOUS);
124 if (ret <= 0) 124 if (ret <= 0)
125 goto out_destroy; 125 goto out_err;
126 nfs_callback_tcpport = ret; 126 nfs_callback_tcpport = ret;
127 dprintk("Callback port = 0x%x\n", nfs_callback_tcpport); 127 dprintk("Callback port = 0x%x\n", nfs_callback_tcpport);
128 128
129 ret = svc_create_thread(nfs_callback_svc, serv); 129 ret = svc_create_thread(nfs_callback_svc, serv);
130 if (ret < 0) 130 if (ret < 0)
131 goto out_destroy; 131 goto out_err;
132 nfs_callback_info.serv = serv; 132 nfs_callback_info.serv = serv;
133 wait_for_completion(&nfs_callback_info.started); 133 wait_for_completion(&nfs_callback_info.started);
134out: 134out:
135 /*
136 * svc_create creates the svc_serv with sv_nrthreads == 1, and then
137 * svc_create_thread increments that. So we need to call svc_destroy
138 * on both success and failure so that the refcount is 1 when the
139 * thread exits.
140 */
141 if (serv)
142 svc_destroy(serv);
135 mutex_unlock(&nfs_callback_mutex); 143 mutex_unlock(&nfs_callback_mutex);
136 unlock_kernel(); 144 unlock_kernel();
137 return ret; 145 return ret;
138out_destroy: 146out_err:
139 dprintk("Couldn't create callback socket or server thread; err = %d\n", 147 dprintk("Couldn't create callback socket or server thread; err = %d\n",
140 ret); 148 ret);
141 svc_destroy(serv);
142out_err:
143 nfs_callback_info.users--; 149 nfs_callback_info.users--;
144 goto out; 150 goto out;
145} 151}