callback.c 5.6 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 * linux/fs/nfs/callback.c
 *
 * Copyright (C) 2004 Trond Myklebust
 *
 * NFSv4 callback handling
 */

#include <linux/completion.h>
#include <linux/ip.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/nfs_fs.h>
I
Ingo Molnar 已提交
16
#include <linux/mutex.h>
17
#include <linux/freezer.h>
18
#include <linux/kthread.h>
19 20 21

#include <net/inet_sock.h>

22
#include "nfs4_fs.h"
L
Linus Torvalds 已提交
23
#include "callback.h"
24
#include "internal.h"
L
Linus Torvalds 已提交
25 26 27 28 29

#define NFSDBG_FACILITY NFSDBG_CALLBACK

struct nfs_callback_data {
	unsigned int users;
30
	struct svc_rqst *rqst;
31
	struct task_struct *task;
L
Linus Torvalds 已提交
32 33 34
};

static struct nfs_callback_data nfs_callback_info;
I
Ingo Molnar 已提交
35
static DEFINE_MUTEX(nfs_callback_mutex);
L
Linus Torvalds 已提交
36 37
static struct svc_program nfs4_callback_program;

38
unsigned int nfs_callback_set_tcpport;
L
Linus Torvalds 已提交
39
unsigned short nfs_callback_tcpport;
40 41 42
static const int nfs_set_port_min = 0;
static const int nfs_set_port_max = 65535;

43 44 45 46 47 48 49 50 51 52
/*
 * If the kernel has IPv6 support available, always listen for
 * both AF_INET and AF_INET6 requests.
 */
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static const sa_family_t	nfs_callback_family = AF_INET6;
#else
static const sa_family_t	nfs_callback_family = AF_INET;
#endif

53 54 55 56 57 58 59 60 61 62 63 64
static int param_set_port(const char *val, struct kernel_param *kp)
{
	char *endp;
	int num = simple_strtol(val, &endp, 0);
	if (endp == val || *endp || num < nfs_set_port_min || num > nfs_set_port_max)
		return -EINVAL;
	*((int *)kp->arg) = num;
	return 0;
}

module_param_call(callback_tcpport, param_set_port, param_get_int,
		 &nfs_callback_set_tcpport, 0644);
L
Linus Torvalds 已提交
65 66 67 68

/*
 * This is the callback kernel thread.
 */
69 70
static int
nfs_callback_svc(void *vrqstp)
L
Linus Torvalds 已提交
71
{
72
	int err, preverr = 0;
73
	struct svc_rqst *rqstp = vrqstp;
L
Linus Torvalds 已提交
74

75
	set_freezable();
L
Linus Torvalds 已提交
76

77 78 79 80 81 82
	/*
	 * FIXME: do we really need to run this under the BKL? If so, please
	 * add a comment about what it's intended to protect.
	 */
	lock_kernel();
	while (!kthread_should_stop()) {
L
Linus Torvalds 已提交
83 84 85
		/*
		 * Listen for a request on the socket
		 */
86
		err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT);
87 88
		if (err == -EAGAIN || err == -EINTR) {
			preverr = err;
L
Linus Torvalds 已提交
89
			continue;
90
		}
L
Linus Torvalds 已提交
91
		if (err < 0) {
92 93 94 95 96 97 98
			if (err != preverr) {
				printk(KERN_WARNING "%s: unexpected error "
					"from svc_recv (%d)\n", __func__, err);
				preverr = err;
			}
			schedule_timeout_uninterruptible(HZ);
			continue;
L
Linus Torvalds 已提交
99
		}
100
		preverr = err;
101
		svc_process(rqstp);
L
Linus Torvalds 已提交
102 103
	}
	unlock_kernel();
104
	return 0;
L
Linus Torvalds 已提交
105 106 107
}

/*
108
 * Bring up the callback thread if it is not already up.
L
Linus Torvalds 已提交
109 110 111
 */
int nfs_callback_up(void)
{
112
	struct svc_serv *serv = NULL;
L
Linus Torvalds 已提交
113 114
	int ret = 0;

I
Ingo Molnar 已提交
115
	mutex_lock(&nfs_callback_mutex);
116
	if (nfs_callback_info.users++ || nfs_callback_info.task != NULL)
L
Linus Torvalds 已提交
117
		goto out;
118
	serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE,
119
				nfs_callback_family, NULL);
L
Linus Torvalds 已提交
120 121 122
	ret = -ENOMEM;
	if (!serv)
		goto out_err;
123

124 125
	ret = svc_create_xprt(serv, "tcp", nfs_callback_set_tcpport,
			      SVC_SOCK_ANONYMOUS);
126
	if (ret <= 0)
127
		goto out_err;
128
	nfs_callback_tcpport = ret;
129 130
	dprintk("NFS: Callback listener port = %u (af %u)\n",
			nfs_callback_tcpport, nfs_callback_family);
131

132 133 134 135
	nfs_callback_info.rqst = svc_prepare_thread(serv, &serv->sv_pools[0]);
	if (IS_ERR(nfs_callback_info.rqst)) {
		ret = PTR_ERR(nfs_callback_info.rqst);
		nfs_callback_info.rqst = NULL;
136
		goto out_err;
137 138 139 140
	}

	svc_sock_update_bufs(serv);

141 142
	nfs_callback_info.task = kthread_run(nfs_callback_svc,
					     nfs_callback_info.rqst,
143 144 145
					     "nfsv4-svc");
	if (IS_ERR(nfs_callback_info.task)) {
		ret = PTR_ERR(nfs_callback_info.task);
146 147
		svc_exit_thread(nfs_callback_info.rqst);
		nfs_callback_info.rqst = NULL;
148 149 150
		nfs_callback_info.task = NULL;
		goto out_err;
	}
L
Linus Torvalds 已提交
151
out:
152 153
	/*
	 * svc_create creates the svc_serv with sv_nrthreads == 1, and then
154
	 * svc_prepare_thread increments that. So we need to call svc_destroy
155 156 157 158 159
	 * on both success and failure so that the refcount is 1 when the
	 * thread exits.
	 */
	if (serv)
		svc_destroy(serv);
I
Ingo Molnar 已提交
160
	mutex_unlock(&nfs_callback_mutex);
L
Linus Torvalds 已提交
161
	return ret;
162
out_err:
163 164
	dprintk("NFS: Couldn't create callback socket or server thread; "
		"err = %d\n", ret);
L
Linus Torvalds 已提交
165 166 167 168 169
	nfs_callback_info.users--;
	goto out;
}

/*
170
 * Kill the callback thread if it's no longer being used.
L
Linus Torvalds 已提交
171
 */
172
void nfs_callback_down(void)
L
Linus Torvalds 已提交
173
{
I
Ingo Molnar 已提交
174
	mutex_lock(&nfs_callback_mutex);
175
	nfs_callback_info.users--;
176
	if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) {
177
		kthread_stop(nfs_callback_info.task);
178 179 180 181
		svc_exit_thread(nfs_callback_info.rqst);
		nfs_callback_info.rqst = NULL;
		nfs_callback_info.task = NULL;
	}
I
Ingo Molnar 已提交
182
	mutex_unlock(&nfs_callback_mutex);
L
Linus Torvalds 已提交
183 184 185 186
}

static int nfs_callback_authenticate(struct svc_rqst *rqstp)
{
187
	struct nfs_client *clp;
188
	RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
L
Linus Torvalds 已提交
189 190

	/* Don't talk to strangers */
191
	clp = nfs_find_client(svc_addr(rqstp), 4);
L
Linus Torvalds 已提交
192 193
	if (clp == NULL)
		return SVC_DROP;
194

195
	dprintk("%s: %s NFSv4 callback!\n", __func__,
196
			svc_print_addr(rqstp, buf, sizeof(buf)));
197
	nfs_put_client(clp);
198

L
Linus Torvalds 已提交
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
	switch (rqstp->rq_authop->flavour) {
		case RPC_AUTH_NULL:
			if (rqstp->rq_proc != CB_NULL)
				return SVC_DENIED;
			break;
		case RPC_AUTH_UNIX:
			break;
		case RPC_AUTH_GSS:
			/* FIXME: RPCSEC_GSS handling? */
		default:
			return SVC_DENIED;
	}
	return SVC_OK;
}

/*
 * Define NFS4 callback program
 */
static struct svc_version *nfs4_callback_version[] = {
	[1] = &nfs4_callback_version1,
};

static struct svc_stat nfs4_callback_stats;

static struct svc_program nfs4_callback_program = {
	.pg_prog = NFS4_CALLBACK,			/* RPC service number */
	.pg_nvers = ARRAY_SIZE(nfs4_callback_version),	/* Number of entries */
	.pg_vers = nfs4_callback_version,		/* version table */
	.pg_name = "NFSv4 callback",			/* service name */
	.pg_class = "nfs",				/* authentication class */
	.pg_stats = &nfs4_callback_stats,
	.pg_authenticate = nfs_callback_authenticate,
};