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>

authored by Jeff Layton and committed by Trond Myklebust 8e60029f e760e716

+12 -6
+12 -6
fs/nfs/callback.c
··· 105 */ 106 int nfs_callback_up(void) 107 { 108 - struct svc_serv *serv; 109 int ret = 0; 110 111 lock_kernel(); ··· 122 ret = svc_create_xprt(serv, "tcp", nfs_callback_set_tcpport, 123 SVC_SOCK_ANONYMOUS); 124 if (ret <= 0) 125 - goto out_destroy; 126 nfs_callback_tcpport = ret; 127 dprintk("Callback port = 0x%x\n", nfs_callback_tcpport); 128 129 ret = svc_create_thread(nfs_callback_svc, serv); 130 if (ret < 0) 131 - goto out_destroy; 132 nfs_callback_info.serv = serv; 133 wait_for_completion(&nfs_callback_info.started); 134 out: 135 mutex_unlock(&nfs_callback_mutex); 136 unlock_kernel(); 137 return ret; 138 - out_destroy: 139 dprintk("Couldn't create callback socket or server thread; err = %d\n", 140 ret); 141 - svc_destroy(serv); 142 - out_err: 143 nfs_callback_info.users--; 144 goto out; 145 }
··· 105 */ 106 int nfs_callback_up(void) 107 { 108 + struct svc_serv *serv = NULL; 109 int ret = 0; 110 111 lock_kernel(); ··· 122 ret = svc_create_xprt(serv, "tcp", nfs_callback_set_tcpport, 123 SVC_SOCK_ANONYMOUS); 124 if (ret <= 0) 125 + goto out_err; 126 nfs_callback_tcpport = ret; 127 dprintk("Callback port = 0x%x\n", nfs_callback_tcpport); 128 129 ret = svc_create_thread(nfs_callback_svc, serv); 130 if (ret < 0) 131 + goto out_err; 132 nfs_callback_info.serv = serv; 133 wait_for_completion(&nfs_callback_info.started); 134 out: 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); 143 mutex_unlock(&nfs_callback_mutex); 144 unlock_kernel(); 145 return ret; 146 + out_err: 147 dprintk("Couldn't create callback socket or server thread; err = %d\n", 148 ret); 149 nfs_callback_info.users--; 150 goto out; 151 }