callback.c 5.2 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 43 44 45 46 47 48 49 50 51 52 53 54
static const int nfs_set_port_min = 0;
static const int nfs_set_port_max = 65535;

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 已提交
55 56 57 58

/*
 * This is the callback kernel thread.
 */
59 60
static int
nfs_callback_svc(void *vrqstp)
L
Linus Torvalds 已提交
61
{
62
	int err, preverr = 0;
63
	struct svc_rqst *rqstp = vrqstp;
L
Linus Torvalds 已提交
64

65
	set_freezable();
L
Linus Torvalds 已提交
66

67 68 69 70 71 72
	/*
	 * 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 已提交
73 74 75
		/*
		 * Listen for a request on the socket
		 */
76
		err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT);
77 78
		if (err == -EAGAIN || err == -EINTR) {
			preverr = err;
L
Linus Torvalds 已提交
79
			continue;
80
		}
L
Linus Torvalds 已提交
81
		if (err < 0) {
82 83 84 85 86 87 88
			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 已提交
89
		}
90
		preverr = err;
91
		svc_process(rqstp);
L
Linus Torvalds 已提交
92 93
	}
	unlock_kernel();
94
	return 0;
L
Linus Torvalds 已提交
95 96 97
}

/*
98
 * Bring up the callback thread if it is not already up.
L
Linus Torvalds 已提交
99 100 101
 */
int nfs_callback_up(void)
{
102
	struct svc_serv *serv = NULL;
L
Linus Torvalds 已提交
103 104
	int ret = 0;

I
Ingo Molnar 已提交
105
	mutex_lock(&nfs_callback_mutex);
106
	if (nfs_callback_info.users++ || nfs_callback_info.task != NULL)
L
Linus Torvalds 已提交
107
		goto out;
108
	serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
L
Linus Torvalds 已提交
109 110 111
	ret = -ENOMEM;
	if (!serv)
		goto out_err;
112

113 114
	ret = svc_create_xprt(serv, "tcp", nfs_callback_set_tcpport,
			      SVC_SOCK_ANONYMOUS);
115
	if (ret <= 0)
116
		goto out_err;
117 118 119
	nfs_callback_tcpport = ret;
	dprintk("Callback port = 0x%x\n", nfs_callback_tcpport);

120 121 122 123
	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;
124
		goto out_err;
125 126 127 128
	}

	svc_sock_update_bufs(serv);

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

/*
158
 * Kill the callback thread if it's no longer being used.
L
Linus Torvalds 已提交
159
 */
160
void nfs_callback_down(void)
L
Linus Torvalds 已提交
161
{
I
Ingo Molnar 已提交
162
	mutex_lock(&nfs_callback_mutex);
163
	nfs_callback_info.users--;
164
	if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) {
165
		kthread_stop(nfs_callback_info.task);
166 167 168 169
		svc_exit_thread(nfs_callback_info.rqst);
		nfs_callback_info.rqst = NULL;
		nfs_callback_info.task = NULL;
	}
I
Ingo Molnar 已提交
170
	mutex_unlock(&nfs_callback_mutex);
L
Linus Torvalds 已提交
171 172 173 174
}

static int nfs_callback_authenticate(struct svc_rqst *rqstp)
{
175
	struct nfs_client *clp;
176
	RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
L
Linus Torvalds 已提交
177 178

	/* Don't talk to strangers */
179
	clp = nfs_find_client(svc_addr(rqstp), 4);
L
Linus Torvalds 已提交
180 181
	if (clp == NULL)
		return SVC_DROP;
182

183
	dprintk("%s: %s NFSv4 callback!\n", __func__,
184
			svc_print_addr(rqstp, buf, sizeof(buf)));
185
	nfs_put_client(clp);
186

L
Linus Torvalds 已提交
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
	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,
};