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 30

#define NFSDBG_FACILITY NFSDBG_CALLBACK

struct nfs_callback_data {
	unsigned int users;
	struct svc_serv *serv;
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 95 96
	nfs_callback_info.task = NULL;
	svc_exit_thread(rqstp);
	return 0;
L
Linus Torvalds 已提交
97 98 99 100 101 102 103
}

/*
 * Bring up the server process if it is not already up.
 */
int nfs_callback_up(void)
{
104
	struct svc_serv *serv = NULL;
105
	struct svc_rqst *rqstp;
L
Linus Torvalds 已提交
106 107 108
	int ret = 0;

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

117 118
	ret = svc_create_xprt(serv, "tcp", nfs_callback_set_tcpport,
			      SVC_SOCK_ANONYMOUS);
119
	if (ret <= 0)
120
		goto out_err;
121 122 123
	nfs_callback_tcpport = ret;
	dprintk("Callback port = 0x%x\n", nfs_callback_tcpport);

124 125 126
	rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]);
	if (IS_ERR(rqstp)) {
		ret = PTR_ERR(rqstp);
127
		goto out_err;
128 129 130
	}

	svc_sock_update_bufs(serv);
L
Linus Torvalds 已提交
131
	nfs_callback_info.serv = serv;
132 133 134 135 136 137 138 139 140 141

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

/*
J
J. Bruce Fields 已提交
162
 * Kill the server process if it is not already down.
L
Linus Torvalds 已提交
163
 */
164
void nfs_callback_down(void)
L
Linus Torvalds 已提交
165 166
{
	lock_kernel();
I
Ingo Molnar 已提交
167
	mutex_lock(&nfs_callback_mutex);
168
	nfs_callback_info.users--;
169 170
	if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL)
		kthread_stop(nfs_callback_info.task);
I
Ingo Molnar 已提交
171
	mutex_unlock(&nfs_callback_mutex);
L
Linus Torvalds 已提交
172 173 174 175 176
	unlock_kernel();
}

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

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

	dprintk("%s: %s NFSv4 callback!\n", __FUNCTION__,
			svc_print_addr(rqstp, buf, sizeof(buf)));
187
	nfs_put_client(clp);
188

L
Linus Torvalds 已提交
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 220 221
	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,
};