callback.c 5.1 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
	int ret = 0;

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

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

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

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

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

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

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

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

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

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