callback.c 5.0 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;
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);
L
Linus Torvalds 已提交
77 78 79 80 81 82 83 84
		if (err == -EAGAIN || err == -EINTR)
			continue;
		if (err < 0) {
			printk(KERN_WARNING
					"%s: terminating on error %d\n",
					__FUNCTION__, -err);
			break;
		}
85
		svc_process(rqstp);
L
Linus Torvalds 已提交
86 87
	}
	unlock_kernel();
88 89 90
	nfs_callback_info.task = NULL;
	svc_exit_thread(rqstp);
	return 0;
L
Linus Torvalds 已提交
91 92 93 94 95 96 97
}

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

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

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

118 119 120
	rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]);
	if (IS_ERR(rqstp)) {
		ret = PTR_ERR(rqstp);
121
		goto out_err;
122 123 124
	}

	svc_sock_update_bufs(serv);
L
Linus Torvalds 已提交
125
	nfs_callback_info.serv = serv;
126 127 128 129 130 131 132 133 134 135

	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 已提交
136
out:
137 138
	/*
	 * svc_create creates the svc_serv with sv_nrthreads == 1, and then
139
	 * svc_prepare_thread increments that. So we need to call svc_destroy
140 141 142 143 144
	 * on both success and failure so that the refcount is 1 when the
	 * thread exits.
	 */
	if (serv)
		svc_destroy(serv);
I
Ingo Molnar 已提交
145
	mutex_unlock(&nfs_callback_mutex);
L
Linus Torvalds 已提交
146 147
	unlock_kernel();
	return ret;
148
out_err:
149 150
	dprintk("Couldn't create callback socket or server thread; err = %d\n",
		ret);
L
Linus Torvalds 已提交
151 152 153 154 155
	nfs_callback_info.users--;
	goto out;
}

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

static int nfs_callback_authenticate(struct svc_rqst *rqstp)
{
171
	struct nfs_client *clp;
172
	RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
L
Linus Torvalds 已提交
173 174

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

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

L
Linus Torvalds 已提交
183 184 185 186 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
	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,
};