Blame view

fs/nfsd/nfs4callback.c 25.5 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
   *  Copyright (c) 2001 The Regents of the University of Michigan.
   *  All rights reserved.
   *
   *  Kendrick Smith <kmsmith@umich.edu>
   *  Andy Adamson <andros@umich.edu>
   *
   *  Redistribution and use in source and binary forms, with or without
   *  modification, are permitted provided that the following conditions
   *  are met:
   *
   *  1. Redistributions of source code must retain the above copyright
   *     notice, this list of conditions and the following disclaimer.
   *  2. Redistributions in binary form must reproduce the above copyright
   *     notice, this list of conditions and the following disclaimer in the
   *     documentation and/or other materials provided with the distribution.
   *  3. Neither the name of the University nor the names of its
   *     contributors may be used to endorse or promote products derived
   *     from this software without specific prior written permission.
   *
   *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
   *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
   *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
  #include <linux/sunrpc/clnt.h>
b5a1a81e5   J. Bruce Fields   nfsd4: don't slee...
34
  #include <linux/sunrpc/svc_xprt.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
35
  #include <linux/slab.h>
9a74af213   Boaz Harrosh   nfsd: Move privat...
36
37
  #include "nfsd.h"
  #include "state.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
40
41
42
  
  #define NFSDDBG_FACILITY                NFSDDBG_PROC
  
  #define NFSPROC4_CB_NULL 0
  #define NFSPROC4_CB_COMPOUND 1
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
  /* Index of predefined Linux callback client operations */
  
  enum {
4be36ca0c   Benny Halevy   nfsd4: fix whites...
46
  	NFSPROC4_CLNT_CB_NULL = 0,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
  	NFSPROC4_CLNT_CB_RECALL,
38524ab38   Andy Adamson   nfsd41: Backchann...
48
  	NFSPROC4_CLNT_CB_SEQUENCE,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
51
52
53
54
55
  #define NFS4_MAXTAGLEN		20
  
  #define NFS4_enc_cb_null_sz		0
  #define NFS4_dec_cb_null_sz		0
  #define cb_compound_enc_hdr_sz		4
  #define cb_compound_dec_hdr_sz		(3 + (NFS4_MAXTAGLEN >> 2))
38524ab38   Andy Adamson   nfsd41: Backchann...
56
57
58
59
  #define sessionid_sz			(NFS4_MAX_SESSIONID_LEN >> 2)
  #define cb_sequence_enc_sz		(sessionid_sz + 4 +             \
  					1 /* no referring calls list yet */)
  #define cb_sequence_dec_sz		(op_dec_sz + sessionid_sz + 4)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
61
62
  #define op_enc_sz			1
  #define op_dec_sz			2
  #define enc_nfs4_fh_sz			(1 + (NFS4_FHSIZE >> 2))
0ac68d179   Benny Halevy   knfsd: nfsd4: fix...
63
  #define enc_stateid_sz			(NFS4_STATEID_SIZE >> 2)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
  #define NFS4_enc_cb_recall_sz		(cb_compound_enc_hdr_sz +       \
38524ab38   Andy Adamson   nfsd41: Backchann...
65
  					cb_sequence_enc_sz +            \
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
67
68
69
  					1 + enc_stateid_sz +            \
  					enc_nfs4_fh_sz)
  
  #define NFS4_dec_cb_recall_sz		(cb_compound_dec_hdr_sz  +      \
38524ab38   Andy Adamson   nfsd41: Backchann...
70
  					cb_sequence_dec_sz +            \
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
  					op_dec_sz)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
  struct nfs4_cb_compound_hdr {
38524ab38   Andy Adamson   nfsd41: Backchann...
73
74
  	/* args */
  	u32		ident;	/* minorversion 0 only */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
  	u32		nops;
ef52bff84   Andy Adamson   nfsd41: Backchann...
76
  	__be32		*nops_p;
ab52ae6db   Andy Adamson   nfsd41: Backchann...
77
  	u32		minorversion;
38524ab38   Andy Adamson   nfsd41: Backchann...
78
79
  	/* res */
  	int		status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
  };
85a564801   Chuck Lever   NFSD: Update XDR ...
81
82
83
84
  /*
   * Handle decode buffer overflows out-of-line.
   */
  static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
  {
85a564801   Chuck Lever   NFSD: Update XDR ...
86
87
88
89
  	dprintk("NFS: %s prematurely hit the end of our receive buffer. "
  		"Remaining buffer length is %tu words.
  ",
  		func, xdr->end - xdr->p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
  }
a033db487   Chuck Lever   NFSD: Update XDR ...
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
  static __be32 *xdr_encode_empty_array(__be32 *p)
  {
  	*p++ = xdr_zero;
  	return p;
  }
  
  /*
   * Encode/decode NFSv4 CB basic data types
   *
   * Basic NFSv4 callback data types are defined in section 15 of RFC
   * 3530: "Network File System (NFS) version 4 Protocol" and section
   * 20 of RFC 5661: "Network File System (NFS) Version 4 Minor Version
   * 1 Protocol"
   */
  
  /*
   *	nfs_cb_opnum4
   *
   *	enum nfs_cb_opnum4 {
   *		OP_CB_GETATTR		= 3,
   *		  ...
   *	};
   */
  enum nfs_cb_opnum4 {
  	OP_CB_GETATTR			= 3,
  	OP_CB_RECALL			= 4,
  	OP_CB_LAYOUTRECALL		= 5,
  	OP_CB_NOTIFY			= 6,
  	OP_CB_PUSH_DELEG		= 7,
  	OP_CB_RECALL_ANY		= 8,
  	OP_CB_RECALLABLE_OBJ_AVAIL	= 9,
  	OP_CB_RECALL_SLOT		= 10,
  	OP_CB_SEQUENCE			= 11,
  	OP_CB_WANTS_CANCELLED		= 12,
  	OP_CB_NOTIFY_LOCK		= 13,
  	OP_CB_NOTIFY_DEVICEID		= 14,
  	OP_CB_ILLEGAL			= 10044
  };
  
  static void encode_nfs_cb_opnum4(struct xdr_stream *xdr, enum nfs_cb_opnum4 op)
  {
  	__be32 *p;
  
  	p = xdr_reserve_space(xdr, 4);
  	*p = cpu_to_be32(op);
  }
  
  /*
   * nfs_fh4
   *
   *	typedef opaque nfs_fh4<NFS4_FHSIZE>;
   */
  static void encode_nfs_fh4(struct xdr_stream *xdr, const struct knfsd_fh *fh)
  {
  	u32 length = fh->fh_size;
  	__be32 *p;
  
  	BUG_ON(length > NFS4_FHSIZE);
  	p = xdr_reserve_space(xdr, 4 + length);
  	xdr_encode_opaque(p, &fh->fh_base, length);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
  /*
a033db487   Chuck Lever   NFSD: Update XDR ...
153
154
155
156
157
158
   * stateid4
   *
   *	struct stateid4 {
   *		uint32_t	seqid;
   *		opaque		other[12];
   *	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
   */
a033db487   Chuck Lever   NFSD: Update XDR ...
160
161
162
  static void encode_stateid4(struct xdr_stream *xdr, const stateid_t *sid)
  {
  	__be32 *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163

a033db487   Chuck Lever   NFSD: Update XDR ...
164
165
166
167
168
169
170
171
172
173
174
175
  	p = xdr_reserve_space(xdr, NFS4_STATEID_SIZE);
  	*p++ = cpu_to_be32(sid->si_generation);
  	xdr_encode_opaque_fixed(p, &sid->si_opaque, NFS4_STATEID_OTHER_SIZE);
  }
  
  /*
   * sessionid4
   *
   *	typedef opaque sessionid4[NFS4_SESSIONID_SIZE];
   */
  static void encode_sessionid4(struct xdr_stream *xdr,
  			      const struct nfsd4_session *session)
9303bbd3d   Benny Halevy   nfsd: nfs4callbac...
176
177
  {
  	__be32 *p;
a033db487   Chuck Lever   NFSD: Update XDR ...
178
179
180
  	p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN);
  	xdr_encode_opaque_fixed(p, session->se_sessionid.data,
  					NFS4_MAX_SESSIONID_LEN);
9303bbd3d   Benny Halevy   nfsd: nfs4callbac...
181
  }
a033db487   Chuck Lever   NFSD: Update XDR ...
182
  /*
85a564801   Chuck Lever   NFSD: Update XDR ...
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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
   * nfsstat4
   */
  static const struct {
  	int stat;
  	int errno;
  } nfs_cb_errtbl[] = {
  	{ NFS4_OK,		0		},
  	{ NFS4ERR_PERM,		-EPERM		},
  	{ NFS4ERR_NOENT,	-ENOENT		},
  	{ NFS4ERR_IO,		-EIO		},
  	{ NFS4ERR_NXIO,		-ENXIO		},
  	{ NFS4ERR_ACCESS,	-EACCES		},
  	{ NFS4ERR_EXIST,	-EEXIST		},
  	{ NFS4ERR_XDEV,		-EXDEV		},
  	{ NFS4ERR_NOTDIR,	-ENOTDIR	},
  	{ NFS4ERR_ISDIR,	-EISDIR		},
  	{ NFS4ERR_INVAL,	-EINVAL		},
  	{ NFS4ERR_FBIG,		-EFBIG		},
  	{ NFS4ERR_NOSPC,	-ENOSPC		},
  	{ NFS4ERR_ROFS,		-EROFS		},
  	{ NFS4ERR_MLINK,	-EMLINK		},
  	{ NFS4ERR_NAMETOOLONG,	-ENAMETOOLONG	},
  	{ NFS4ERR_NOTEMPTY,	-ENOTEMPTY	},
  	{ NFS4ERR_DQUOT,	-EDQUOT		},
  	{ NFS4ERR_STALE,	-ESTALE		},
  	{ NFS4ERR_BADHANDLE,	-EBADHANDLE	},
  	{ NFS4ERR_BAD_COOKIE,	-EBADCOOKIE	},
  	{ NFS4ERR_NOTSUPP,	-ENOTSUPP	},
  	{ NFS4ERR_TOOSMALL,	-ETOOSMALL	},
  	{ NFS4ERR_SERVERFAULT,	-ESERVERFAULT	},
  	{ NFS4ERR_BADTYPE,	-EBADTYPE	},
  	{ NFS4ERR_LOCKED,	-EAGAIN		},
  	{ NFS4ERR_RESOURCE,	-EREMOTEIO	},
  	{ NFS4ERR_SYMLINK,	-ELOOP		},
  	{ NFS4ERR_OP_ILLEGAL,	-EOPNOTSUPP	},
  	{ NFS4ERR_DEADLOCK,	-EDEADLK	},
  	{ -1,			-EIO		}
  };
  
  /*
   * If we cannot translate the error, the recovery routines should
   * handle it.
   *
   * Note: remaining NFSv4 error codes have values > 10000, so should
   * not conflict with native Linux error codes.
   */
  static int nfs_cb_stat_to_errno(int status)
  {
  	int i;
  
  	for (i = 0; nfs_cb_errtbl[i].stat != -1; i++) {
  		if (nfs_cb_errtbl[i].stat == status)
  			return nfs_cb_errtbl[i].errno;
  	}
  
  	dprintk("NFSD: Unrecognized NFS CB status value: %u
  ", status);
  	return -status;
  }
  
  static int decode_cb_op_status(struct xdr_stream *xdr, enum nfs_opnum4 expected,
  			       enum nfsstat4 *status)
  {
  	__be32 *p;
  	u32 op;
  
  	p = xdr_inline_decode(xdr, 4 + 4);
  	if (unlikely(p == NULL))
  		goto out_overflow;
  	op = be32_to_cpup(p++);
  	if (unlikely(op != expected))
  		goto out_unexpected;
  	*status = be32_to_cpup(p);
  	return 0;
  out_overflow:
  	print_overflow_msg(__func__, xdr);
  	return -EIO;
  out_unexpected:
  	dprintk("NFSD: Callback server returned operation %d but "
  		"we issued a request for %d
  ", op, expected);
  	return -EIO;
  }
  
  /*
a033db487   Chuck Lever   NFSD: Update XDR ...
268
269
270
271
272
273
274
275
276
277
278
   * CB_COMPOUND4args
   *
   *	struct CB_COMPOUND4args {
   *		utf8str_cs	tag;
   *		uint32_t	minorversion;
   *		uint32_t	callback_ident;
   *		nfs_cb_argop4	argarray<>;
   *	};
  */
  static void encode_cb_compound4args(struct xdr_stream *xdr,
  				    struct nfs4_cb_compound_hdr *hdr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
279
  {
f00f328fd   Al Viro   [PATCH] xdr annot...
280
  	__be32 * p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281

a033db487   Chuck Lever   NFSD: Update XDR ...
282
283
284
285
  	p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4);
  	p = xdr_encode_empty_array(p);		/* empty tag */
  	*p++ = cpu_to_be32(hdr->minorversion);
  	*p++ = cpu_to_be32(hdr->ident);
ef52bff84   Andy Adamson   nfsd41: Backchann...
286
  	hdr->nops_p = p;
a033db487   Chuck Lever   NFSD: Update XDR ...
287
  	*p = cpu_to_be32(hdr->nops);		/* argarray element count */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
  }
a033db487   Chuck Lever   NFSD: Update XDR ...
289
290
291
  /*
   * Update argarray element count
   */
ef52bff84   Andy Adamson   nfsd41: Backchann...
292
293
  static void encode_cb_nops(struct nfs4_cb_compound_hdr *hdr)
  {
a033db487   Chuck Lever   NFSD: Update XDR ...
294
295
  	BUG_ON(hdr->nops > NFS4_MAX_BACK_CHANNEL_OPS);
  	*hdr->nops_p = cpu_to_be32(hdr->nops);
ef52bff84   Andy Adamson   nfsd41: Backchann...
296
  }
a033db487   Chuck Lever   NFSD: Update XDR ...
297
  /*
85a564801   Chuck Lever   NFSD: Update XDR ...
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
   * CB_COMPOUND4res
   *
   *	struct CB_COMPOUND4res {
   *		nfsstat4	status;
   *		utf8str_cs	tag;
   *		nfs_cb_resop4	resarray<>;
   *	};
   */
  static int decode_cb_compound4res(struct xdr_stream *xdr,
  				  struct nfs4_cb_compound_hdr *hdr)
  {
  	u32 length;
  	__be32 *p;
  
  	p = xdr_inline_decode(xdr, 4 + 4);
  	if (unlikely(p == NULL))
  		goto out_overflow;
  	hdr->status = be32_to_cpup(p++);
  	/* Ignore the tag */
  	length = be32_to_cpup(p++);
  	p = xdr_inline_decode(xdr, length + 4);
  	if (unlikely(p == NULL))
  		goto out_overflow;
  	hdr->nops = be32_to_cpup(p);
  	return 0;
  out_overflow:
  	print_overflow_msg(__func__, xdr);
  	return -EIO;
  }
  
  /*
a033db487   Chuck Lever   NFSD: Update XDR ...
329
330
331
332
333
334
335
336
337
338
339
   * CB_RECALL4args
   *
   *	struct CB_RECALL4args {
   *		stateid4	stateid;
   *		bool		truncate;
   *		nfs_fh4		fh;
   *	};
   */
  static void encode_cb_recall4args(struct xdr_stream *xdr,
  				  const struct nfs4_delegation *dp,
  				  struct nfs4_cb_compound_hdr *hdr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
  {
f00f328fd   Al Viro   [PATCH] xdr annot...
341
  	__be32 *p;
a033db487   Chuck Lever   NFSD: Update XDR ...
342
343
344
345
346
347
348
349
  
  	encode_nfs_cb_opnum4(xdr, OP_CB_RECALL);
  	encode_stateid4(xdr, &dp->dl_stateid);
  
  	p = xdr_reserve_space(xdr, 4);
  	*p++ = xdr_zero;			/* truncate */
  
  	encode_nfs_fh4(xdr, &dp->dl_fh);
ef52bff84   Andy Adamson   nfsd41: Backchann...
350
  	hdr->nops++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
  }
a033db487   Chuck Lever   NFSD: Update XDR ...
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
  /*
   * CB_SEQUENCE4args
   *
   *	struct CB_SEQUENCE4args {
   *		sessionid4		csa_sessionid;
   *		sequenceid4		csa_sequenceid;
   *		slotid4			csa_slotid;
   *		slotid4			csa_highest_slotid;
   *		bool			csa_cachethis;
   *		referring_call_list4	csa_referring_call_lists<>;
   *	};
   */
  static void encode_cb_sequence4args(struct xdr_stream *xdr,
  				    const struct nfsd4_callback *cb,
  				    struct nfs4_cb_compound_hdr *hdr)
2af73580b   Benny Halevy   nfsd41: Backchann...
367
  {
a033db487   Chuck Lever   NFSD: Update XDR ...
368
  	struct nfsd4_session *session = cb->cb_clp->cl_cb_session;
2af73580b   Benny Halevy   nfsd41: Backchann...
369
370
371
372
  	__be32 *p;
  
  	if (hdr->minorversion == 0)
  		return;
a033db487   Chuck Lever   NFSD: Update XDR ...
373
374
375
376
377
378
379
380
381
  	encode_nfs_cb_opnum4(xdr, OP_CB_SEQUENCE);
  	encode_sessionid4(xdr, session);
  
  	p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4 + 4);
  	*p++ = cpu_to_be32(session->se_cb_seq_nr);	/* csa_sequenceid */
  	*p++ = xdr_zero;			/* csa_slotid */
  	*p++ = xdr_zero;			/* csa_highest_slotid */
  	*p++ = xdr_zero;			/* csa_cachethis */
  	xdr_encode_empty_array(p);		/* csa_referring_call_lists */
2af73580b   Benny Halevy   nfsd41: Backchann...
382

2af73580b   Benny Halevy   nfsd41: Backchann...
383
384
  	hdr->nops++;
  }
a033db487   Chuck Lever   NFSD: Update XDR ...
385
  /*
85a564801   Chuck Lever   NFSD: Update XDR ...
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
   * CB_SEQUENCE4resok
   *
   *	struct CB_SEQUENCE4resok {
   *		sessionid4	csr_sessionid;
   *		sequenceid4	csr_sequenceid;
   *		slotid4		csr_slotid;
   *		slotid4		csr_highest_slotid;
   *		slotid4		csr_target_highest_slotid;
   *	};
   *
   *	union CB_SEQUENCE4res switch (nfsstat4 csr_status) {
   *	case NFS4_OK:
   *		CB_SEQUENCE4resok	csr_resok4;
   *	default:
   *		void;
   *	};
   *
   * Our current back channel implmentation supports a single backchannel
   * with a single slot.
   */
  static int decode_cb_sequence4resok(struct xdr_stream *xdr,
  				    struct nfsd4_callback *cb)
  {
  	struct nfsd4_session *session = cb->cb_clp->cl_cb_session;
  	struct nfs4_sessionid id;
  	int status;
  	__be32 *p;
  	u32 dummy;
  
  	status = -ESERVERFAULT;
  
  	/*
  	 * If the server returns different values for sessionID, slotID or
  	 * sequence number, the server is looney tunes.
  	 */
2c9c8f36c   Benny Halevy   NFSD: fix decode_...
421
  	p = xdr_inline_decode(xdr, NFS4_MAX_SESSIONID_LEN + 4 + 4 + 4 + 4);
85a564801   Chuck Lever   NFSD: Update XDR ...
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
  	if (unlikely(p == NULL))
  		goto out_overflow;
  	memcpy(id.data, p, NFS4_MAX_SESSIONID_LEN);
  	if (memcmp(id.data, session->se_sessionid.data,
  					NFS4_MAX_SESSIONID_LEN) != 0) {
  		dprintk("NFS: %s Invalid session id
  ", __func__);
  		goto out;
  	}
  	p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN);
  
  	dummy = be32_to_cpup(p++);
  	if (dummy != session->se_cb_seq_nr) {
  		dprintk("NFS: %s Invalid sequence number
  ", __func__);
  		goto out;
  	}
  
  	dummy = be32_to_cpup(p++);
  	if (dummy != 0) {
  		dprintk("NFS: %s Invalid slotid
  ", __func__);
  		goto out;
  	}
  
  	/*
  	 * FIXME: process highest slotid and target highest slotid
  	 */
  	status = 0;
  out:
  	return status;
  out_overflow:
  	print_overflow_msg(__func__, xdr);
  	return -EIO;
  }
  
  static int decode_cb_sequence4res(struct xdr_stream *xdr,
  				  struct nfsd4_callback *cb)
  {
  	enum nfsstat4 nfserr;
  	int status;
  
  	if (cb->cb_minorversion == 0)
  		return 0;
  
  	status = decode_cb_op_status(xdr, OP_CB_SEQUENCE, &nfserr);
  	if (unlikely(status))
  		goto out;
  	if (unlikely(nfserr != NFS4_OK))
  		goto out_default;
  	status = decode_cb_sequence4resok(xdr, cb);
  out:
  	return status;
  out_default:
0af3f814c   Benny Halevy   NFSD: use nfserr ...
476
  	return nfs_cb_stat_to_errno(nfserr);
85a564801   Chuck Lever   NFSD: Update XDR ...
477
478
479
  }
  
  /*
a033db487   Chuck Lever   NFSD: Update XDR ...
480
481
482
483
484
485
486
487
488
489
490
   * NFSv4.0 and NFSv4.1 XDR encode functions
   *
   * NFSv4.0 callback argument types are defined in section 15 of RFC
   * 3530: "Network File System (NFS) version 4 Protocol" and section 20
   * of RFC 5661:  "Network File System (NFS) Version 4 Minor Version 1
   * Protocol".
   */
  
  /*
   * NB: Without this zero space reservation, callbacks over krb5p fail
   */
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
491
492
  static void nfs4_xdr_enc_cb_null(struct rpc_rqst *req, struct xdr_stream *xdr,
  				 void *__unused)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
493
  {
a033db487   Chuck Lever   NFSD: Update XDR ...
494
  	xdr_reserve_space(xdr, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
  }
a033db487   Chuck Lever   NFSD: Update XDR ...
496
497
498
  /*
   * 20.2. Operation 4: CB_RECALL - Recall a Delegation
   */
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
499
500
  static void nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, struct xdr_stream *xdr,
  				   const struct nfsd4_callback *cb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
  {
a033db487   Chuck Lever   NFSD: Update XDR ...
502
  	const struct nfs4_delegation *args = cb->cb_op;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503
  	struct nfs4_cb_compound_hdr hdr = {
6ff8da088   J. Bruce Fields   nfsd4: Move callb...
504
  		.ident = cb->cb_clp->cl_cb_ident,
fb0039232   J. Bruce Fields   nfsd4: remove sep...
505
  		.minorversion = cb->cb_minorversion,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506
  	};
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
507
508
509
  	encode_cb_compound4args(xdr, &hdr);
  	encode_cb_sequence4args(xdr, cb, &hdr);
  	encode_cb_recall4args(xdr, args, &hdr);
ef52bff84   Andy Adamson   nfsd41: Backchann...
510
  	encode_cb_nops(&hdr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511
  }
2af73580b   Benny Halevy   nfsd41: Backchann...
512
  /*
85a564801   Chuck Lever   NFSD: Update XDR ...
513
514
515
516
517
518
   * NFSv4.0 and NFSv4.1 XDR decode functions
   *
   * NFSv4.0 callback result types are defined in section 15 of RFC
   * 3530: "Network File System (NFS) version 4 Protocol" and section 20
   * of RFC 5661:  "Network File System (NFS) Version 4 Minor Version 1
   * Protocol".
2af73580b   Benny Halevy   nfsd41: Backchann...
519
   */
2af73580b   Benny Halevy   nfsd41: Backchann...
520

bf2695516   Chuck Lever   SUNRPC: New xdr_s...
521
522
  static int nfs4_xdr_dec_cb_null(struct rpc_rqst *req, struct xdr_stream *xdr,
  				void *__unused)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
524
525
  {
  	return 0;
  }
85a564801   Chuck Lever   NFSD: Update XDR ...
526
527
528
  /*
   * 20.2. Operation 4: CB_RECALL - Recall a Delegation
   */
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
529
530
  static int nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp,
  				  struct xdr_stream *xdr,
85a564801   Chuck Lever   NFSD: Update XDR ...
531
  				  struct nfsd4_callback *cb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
  	struct nfs4_cb_compound_hdr hdr;
85a564801   Chuck Lever   NFSD: Update XDR ...
534
  	enum nfsstat4 nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
  	int status;
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
536
  	status = decode_cb_compound4res(xdr, &hdr);
85a564801   Chuck Lever   NFSD: Update XDR ...
537
  	if (unlikely(status))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538
  		goto out;
85a564801   Chuck Lever   NFSD: Update XDR ...
539
540
  
  	if (cb != NULL) {
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
541
  		status = decode_cb_sequence4res(xdr, cb);
85a564801   Chuck Lever   NFSD: Update XDR ...
542
  		if (unlikely(status))
0421b5c55   Ricardo Labiaga   nfsd41: Backchann...
543
544
  			goto out;
  	}
85a564801   Chuck Lever   NFSD: Update XDR ...
545

bf2695516   Chuck Lever   SUNRPC: New xdr_s...
546
  	status = decode_cb_op_status(xdr, OP_CB_RECALL, &nfserr);
85a564801   Chuck Lever   NFSD: Update XDR ...
547
548
549
  	if (unlikely(status))
  		goto out;
  	if (unlikely(nfserr != NFS4_OK))
0af3f814c   Benny Halevy   NFSD: use nfserr ...
550
  		status = nfs_cb_stat_to_errno(nfserr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551
552
553
554
555
556
557
  out:
  	return status;
  }
  
  /*
   * RPC procedure tables
   */
7d93bd71c   Chuck Lever   NFS: Repair white...
558
559
560
  #define PROC(proc, call, argtype, restype)				\
  [NFSPROC4_CLNT_##proc] = {						\
  	.p_proc    = NFSPROC4_CB_##call,				\
9f06c719f   Chuck Lever   SUNRPC: New xdr_s...
561
  	.p_encode  = (kxdreproc_t)nfs4_xdr_enc_##argtype,		\
bf2695516   Chuck Lever   SUNRPC: New xdr_s...
562
  	.p_decode  = (kxdrdproc_t)nfs4_xdr_dec_##restype,		\
7d93bd71c   Chuck Lever   NFS: Repair white...
563
564
565
566
  	.p_arglen  = NFS4_enc_##argtype##_sz,				\
  	.p_replen  = NFS4_dec_##restype##_sz,				\
  	.p_statidx = NFSPROC4_CB_##call,				\
  	.p_name    = #proc,						\
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567
  }
7d93bd71c   Chuck Lever   NFS: Repair white...
568
569
570
  static struct rpc_procinfo nfs4_cb_procedures[] = {
  	PROC(CB_NULL,	NULL,		cb_null,	cb_null),
  	PROC(CB_RECALL,	COMPOUND,	cb_recall,	cb_recall),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
571
  };
7d93bd71c   Chuck Lever   NFS: Repair white...
572
  static struct rpc_version nfs_cb_version4 = {
b7299f443   J. Bruce Fields   nfs4: minor callb...
573
574
575
576
577
578
579
  /*
   * Note on the callback rpc program version number: despite language in rfc
   * 5661 section 18.36.3 requiring servers to use 4 in this field, the
   * official xdr descriptions for both 4.0 and 4.1 specify version 1, and
   * in practice that appears to be what implementations use.  The section
   * 18.36.3 language is expected to be fixed in an erratum.
   */
7d93bd71c   Chuck Lever   NFS: Repair white...
580
581
582
  	.number			= 1,
  	.nrprocs		= ARRAY_SIZE(nfs4_cb_procedures),
  	.procs			= nfs4_cb_procedures
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583
  };
7d93bd71c   Chuck Lever   NFS: Repair white...
584
  static struct rpc_version *nfs_cb_version[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
585
586
  	&nfs_cb_version4,
  };
ff7d9756b   Olga Kornievskaia   nfsd: use static ...
587
588
589
  static struct rpc_program cb_program;
  
  static struct rpc_stat cb_stats = {
7d93bd71c   Chuck Lever   NFS: Repair white...
590
  	.program		= &cb_program
ff7d9756b   Olga Kornievskaia   nfsd: use static ...
591
592
593
594
  };
  
  #define NFS4_CALLBACK 0x40000000
  static struct rpc_program cb_program = {
7d93bd71c   Chuck Lever   NFS: Repair white...
595
596
597
598
599
600
  	.name			= "nfs4_cb",
  	.number			= NFS4_CALLBACK,
  	.nrvers			= ARRAY_SIZE(nfs_cb_version),
  	.version		= nfs_cb_version,
  	.stats			= &cb_stats,
  	.pipe_dir_name		= "/nfsd4_cb",
ff7d9756b   Olga Kornievskaia   nfsd: use static ...
601
  };
595947aca   J. Bruce Fields   nfsd4: set shorte...
602
603
  static int max_cb_time(void)
  {
cf07d2ea4   J. Bruce Fields   nfsd4: simplify r...
604
  	return max(nfsd4_lease/10, (time_t)1) * HZ;
595947aca   J. Bruce Fields   nfsd4: set shorte...
605
  }
2b47eece1   J. Bruce Fields   knfsd: spawn kern...
606

dcbeaa68d   J. Bruce Fields   nfsd4: allow back...
607
  static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn, struct nfsd4_session *ses)
2b47eece1   J. Bruce Fields   knfsd: spawn kern...
608
  {
ae5c79476   Chuck Lever   NFSD: Convert NFS...
609
  	struct rpc_timeout	timeparms = {
595947aca   J. Bruce Fields   nfsd4: set shorte...
610
611
  		.to_initval	= max_cb_time(),
  		.to_retries	= 0,
ae5c79476   Chuck Lever   NFSD: Convert NFS...
612
  	};
ae5c79476   Chuck Lever   NFSD: Convert NFS...
613
  	struct rpc_create_args args = {
c653ce3f0   Pavel Emelyanov   sunrpc: Add net t...
614
  		.net		= &init_net,
07263f1ef   J. Bruce Fields   nfsd4: minor vari...
615
616
  		.address	= (struct sockaddr *) &conn->cb_addr,
  		.addrsize	= conn->cb_addrlen,
6f3d772fb   Takuma Umeya   nfs4: set source ...
617
  		.saddress	= (struct sockaddr *) &conn->cb_saddr,
ae5c79476   Chuck Lever   NFSD: Convert NFS...
618
  		.timeout	= &timeparms,
ff7d9756b   Olga Kornievskaia   nfsd: use static ...
619
  		.program	= &cb_program,
b7299f443   J. Bruce Fields   nfs4: minor callb...
620
  		.version	= 0,
61054b14d   Olga Kornievskaia   nfsd: support cal...
621
  		.authflavor	= clp->cl_flavor,
b6b6152c4   Olga Kornievskaia   rpc: bring back c...
622
  		.flags		= (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
ae5c79476   Chuck Lever   NFSD: Convert NFS...
623
  	};
63c86716e   J. Bruce Fields   nfsd: move callba...
624
  	struct rpc_clnt *client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
625

5d18c1c2a   J. Bruce Fields   nfsd4: only requi...
626
627
628
629
630
631
632
633
  	if (clp->cl_minorversion == 0) {
  		if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
  			return -EINVAL;
  		args.client_name = clp->cl_principal;
  		args.prognumber	= conn->cb_prog,
  		args.protocol = XPRT_TRANSPORT_TCP;
  		clp->cl_cb_ident = conn->cb_ident;
  	} else {
dcbeaa68d   J. Bruce Fields   nfsd4: allow back...
634
635
636
637
  		if (!conn->cb_xprt)
  			return -EINVAL;
  		clp->cl_cb_conn.cb_xprt = conn->cb_xprt;
  		clp->cl_cb_session = ses;
07263f1ef   J. Bruce Fields   nfsd4: minor vari...
638
  		args.bc_xprt = conn->cb_xprt;
8b5ce5cd4   J. Bruce Fields   nfsd4: callback p...
639
  		args.prognumber = clp->cl_cb_session->se_cb_prog;
3ddc8bf5f   Alexandros Batsakis   nfsd41: modify nf...
640
641
  		args.protocol = XPRT_TRANSPORT_BC_TCP;
  	}
ae5c79476   Chuck Lever   NFSD: Convert NFS...
642
  	/* Create RPC client */
63c86716e   J. Bruce Fields   nfsd: move callba...
643
  	client = rpc_create(&args);
e1cab5a58   J. Bruce Fields   nfsd4: set cb_cli...
644
  	if (IS_ERR(client)) {
a601caeda   J. Bruce Fields   nfsd4: move rpc_c...
645
646
647
  		dprintk("NFSD: couldn't create callback client: %ld
  ",
  			PTR_ERR(client));
e1cab5a58   J. Bruce Fields   nfsd4: set cb_cli...
648
649
  		return PTR_ERR(client);
  	}
6ff8da088   J. Bruce Fields   nfsd4: Move callb...
650
  	clp->cl_cb_client = client;
e1cab5a58   J. Bruce Fields   nfsd4: set cb_cli...
651
  	return 0;
a601caeda   J. Bruce Fields   nfsd4: move rpc_c...
652
653
  
  }
ecdd03b79   J. Bruce Fields   nfsd4: create rpc...
654
655
656
657
658
659
  static void warn_no_callback_path(struct nfs4_client *clp, int reason)
  {
  	dprintk("NFSD: warning: no callback path to client %.*s: error %d
  ",
  		(int)clp->cl_name.len, clp->cl_name.data, reason);
  }
77a3569d6   J. Bruce Fields   nfsd4: keep finer...
660
661
662
663
664
  static void nfsd4_mark_cb_down(struct nfs4_client *clp, int reason)
  {
  	clp->cl_cb_state = NFSD4_CB_DOWN;
  	warn_no_callback_path(clp, reason);
  }
e300a63ce   J. Bruce Fields   nfsd4: replace ca...
665
666
  static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata)
  {
cee277d92   J. Bruce Fields   nfsd4: use generi...
667
  	struct nfs4_client *clp = container_of(calldata, struct nfs4_client, cl_cb_null);
e300a63ce   J. Bruce Fields   nfsd4: replace ca...
668
669
  
  	if (task->tk_status)
77a3569d6   J. Bruce Fields   nfsd4: keep finer...
670
  		nfsd4_mark_cb_down(clp, task->tk_status);
e300a63ce   J. Bruce Fields   nfsd4: replace ca...
671
  	else
77a3569d6   J. Bruce Fields   nfsd4: keep finer...
672
  		clp->cl_cb_state = NFSD4_CB_UP;
e300a63ce   J. Bruce Fields   nfsd4: replace ca...
673
674
675
  }
  
  static const struct rpc_call_ops nfsd4_cb_probe_ops = {
cee277d92   J. Bruce Fields   nfsd4: use generi...
676
677
  	/* XXX: release method to ensure we set the cb channel down if
  	 * necessary on early failure? */
e300a63ce   J. Bruce Fields   nfsd4: replace ca...
678
679
  	.rpc_call_done = nfsd4_cb_probe_done,
  };
80fc015bd   J. Bruce Fields   nfsd4: use common...
680
681
682
  static struct rpc_cred *callback_cred;
  
  int set_callback_cred(void)
3cef9ab26   J. Bruce Fields   nfsd4: lookup up ...
683
  {
8d75da8af   J. Bruce Fields   nfsd4: fix minor ...
684
685
  	if (callback_cred)
  		return 0;
80fc015bd   J. Bruce Fields   nfsd4: use common...
686
687
688
689
  	callback_cred = rpc_lookup_machine_cred();
  	if (!callback_cred)
  		return -ENOMEM;
  	return 0;
3cef9ab26   J. Bruce Fields   nfsd4: lookup up ...
690
  }
cee277d92   J. Bruce Fields   nfsd4: use generi...
691
  static struct workqueue_struct *callback_wq;
80fc015bd   J. Bruce Fields   nfsd4: use common...
692

229b2a083   J. Bruce Fields   nfsd4: add helper...
693
694
695
696
  static void run_nfsd4_cb(struct nfsd4_callback *cb)
  {
  	queue_work(callback_wq, &cb->cb_work);
  }
5a3c9d713   J. Bruce Fields   nfsd4: separate c...
697
  static void do_probe_callback(struct nfs4_client *clp)
a601caeda   J. Bruce Fields   nfsd4: move rpc_c...
698
  {
cee277d92   J. Bruce Fields   nfsd4: use generi...
699
  	struct nfsd4_callback *cb = &clp->cl_cb_null;
a601caeda   J. Bruce Fields   nfsd4: move rpc_c...
700

fb0039232   J. Bruce Fields   nfsd4: remove sep...
701
702
  	cb->cb_op = NULL;
  	cb->cb_clp = clp;
cee277d92   J. Bruce Fields   nfsd4: use generi...
703
704
705
706
707
708
709
  
  	cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL];
  	cb->cb_msg.rpc_argp = NULL;
  	cb->cb_msg.rpc_resp = NULL;
  	cb->cb_msg.rpc_cred = callback_cred;
  
  	cb->cb_ops = &nfsd4_cb_probe_ops;
229b2a083   J. Bruce Fields   nfsd4: add helper...
710
  	run_nfsd4_cb(cb);
63c86716e   J. Bruce Fields   nfsd: move callba...
711
712
713
  }
  
  /*
5a3c9d713   J. Bruce Fields   nfsd4: separate c...
714
715
   * Poke the callback thread to process any updates to the callback
   * parameters, and send a null probe.
63c86716e   J. Bruce Fields   nfsd: move callba...
716
   */
5a3c9d713   J. Bruce Fields   nfsd4: separate c...
717
718
  void nfsd4_probe_callback(struct nfs4_client *clp)
  {
77a3569d6   J. Bruce Fields   nfsd4: keep finer...
719
720
  	/* XXX: atomicity?  Also, should we be using cl_cb_flags? */
  	clp->cl_cb_state = NFSD4_CB_UNKNOWN;
5a3c9d713   J. Bruce Fields   nfsd4: separate c...
721
722
723
  	set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags);
  	do_probe_callback(clp);
  }
84f5f7ccc   J. Bruce Fields   nfsd4: make sure ...
724
  void nfsd4_probe_callback_sync(struct nfs4_client *clp)
63c86716e   J. Bruce Fields   nfsd: move callba...
725
  {
84f5f7ccc   J. Bruce Fields   nfsd4: make sure ...
726
727
728
  	nfsd4_probe_callback(clp);
  	flush_workqueue(callback_wq);
  }
63c86716e   J. Bruce Fields   nfsd: move callba...
729

5a3c9d713   J. Bruce Fields   nfsd4: separate c...
730
  void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
63c86716e   J. Bruce Fields   nfsd: move callba...
731
  {
77a3569d6   J. Bruce Fields   nfsd4: keep finer...
732
  	clp->cl_cb_state = NFSD4_CB_UNKNOWN;
6ff8da088   J. Bruce Fields   nfsd4: Move callb...
733
734
  	spin_lock(&clp->cl_lock);
  	memcpy(&clp->cl_cb_conn, conn, sizeof(struct nfs4_cb_conn));
6ff8da088   J. Bruce Fields   nfsd4: Move callb...
735
  	spin_unlock(&clp->cl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
  }
2a1d1b593   Ricardo Labiaga   nfsd41: Backchann...
737
738
739
740
741
  /*
   * There's currently a single callback channel slot.
   * If the slot is available, then mark it busy.  Otherwise, set the
   * thread for sleeping on the callback RPC wait queue.
   */
3ff3600e7   J. Bruce Fields   nfsd4: simplify n...
742
  static bool nfsd41_cb_get_slot(struct nfs4_client *clp, struct rpc_task *task)
2a1d1b593   Ricardo Labiaga   nfsd41: Backchann...
743
  {
2a1d1b593   Ricardo Labiaga   nfsd41: Backchann...
744
745
746
747
  	if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
  		rpc_sleep_on(&clp->cl_cb_waitq, task, NULL);
  		dprintk("%s slot is busy
  ", __func__);
3ff3600e7   J. Bruce Fields   nfsd4: simplify n...
748
  		return false;
2a1d1b593   Ricardo Labiaga   nfsd41: Backchann...
749
  	}
3ff3600e7   J. Bruce Fields   nfsd4: simplify n...
750
  	return true;
2a1d1b593   Ricardo Labiaga   nfsd41: Backchann...
751
752
753
754
755
756
757
758
  }
  
  /*
   * TODO: cb_sequence should support referring call lists, cachethis, multiple
   * slots, and mark callback channel down on communication errors.
   */
  static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
  {
5878453db   J. Bruce Fields   nfsd4: generic ca...
759
760
  	struct nfsd4_callback *cb = calldata;
  	struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
2a1d1b593   Ricardo Labiaga   nfsd41: Backchann...
761
  	struct nfs4_client *clp = dp->dl_client;
8323c3b2a   J. Bruce Fields   nfsd4: move minor...
762
  	u32 minorversion = clp->cl_minorversion;
2a1d1b593   Ricardo Labiaga   nfsd41: Backchann...
763

fb0039232   J. Bruce Fields   nfsd4: remove sep...
764
  	cb->cb_minorversion = minorversion;
2a1d1b593   Ricardo Labiaga   nfsd41: Backchann...
765
  	if (minorversion) {
3ff3600e7   J. Bruce Fields   nfsd4: simplify n...
766
  		if (!nfsd41_cb_get_slot(clp, task))
2a1d1b593   Ricardo Labiaga   nfsd41: Backchann...
767
  			return;
2a1d1b593   Ricardo Labiaga   nfsd41: Backchann...
768
  	}
5ce8ba25d   J. Bruce Fields   nfsd4: allow rest...
769
  	spin_lock(&clp->cl_lock);
a8f2800b4   J. Bruce Fields   nfsd4: fix callba...
770
771
772
773
774
  	if (list_empty(&cb->cb_per_client)) {
  		/* This is the first call, not a restart */
  		cb->cb_done = false;
  		list_add(&cb->cb_per_client, &clp->cl_callbacks);
  	}
5ce8ba25d   J. Bruce Fields   nfsd4: allow rest...
775
  	spin_unlock(&clp->cl_lock);
2a1d1b593   Ricardo Labiaga   nfsd41: Backchann...
776
777
  	rpc_call_start(task);
  }
0421b5c55   Ricardo Labiaga   nfsd41: Backchann...
778
779
  static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
  {
5878453db   J. Bruce Fields   nfsd4: generic ca...
780
781
  	struct nfsd4_callback *cb = calldata;
  	struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
0421b5c55   Ricardo Labiaga   nfsd41: Backchann...
782
783
784
785
  	struct nfs4_client *clp = dp->dl_client;
  
  	dprintk("%s: minorversion=%d
  ", __func__,
8323c3b2a   J. Bruce Fields   nfsd4: move minor...
786
  		clp->cl_minorversion);
0421b5c55   Ricardo Labiaga   nfsd41: Backchann...
787

8323c3b2a   J. Bruce Fields   nfsd4: move minor...
788
  	if (clp->cl_minorversion) {
0421b5c55   Ricardo Labiaga   nfsd41: Backchann...
789
  		/* No need for lock, access serialized in nfsd4_cb_prepare */
ac7c46f29   J. Bruce Fields   nfsd4: make backc...
790
  		++clp->cl_cb_session->se_cb_seq_nr;
0421b5c55   Ricardo Labiaga   nfsd41: Backchann...
791
792
793
794
  		clear_bit(0, &clp->cl_cb_slot_busy);
  		rpc_wake_up_next(&clp->cl_cb_waitq);
  		dprintk("%s: freed slot, new seqid=%d
  ", __func__,
ac7c46f29   J. Bruce Fields   nfsd4: make backc...
795
  			clp->cl_cb_session->se_cb_seq_nr);
0421b5c55   Ricardo Labiaga   nfsd41: Backchann...
796
797
798
799
800
  
  		/* We're done looking into the sequence information */
  		task->tk_msg.rpc_resp = NULL;
  	}
  }
4b21d0def   J. Bruce Fields   nfsd4: allow 4.0 ...
801

63e4863fa   J. Bruce Fields   nfsd4: make recal...
802
803
  static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
  {
5878453db   J. Bruce Fields   nfsd4: generic ca...
804
805
  	struct nfsd4_callback *cb = calldata;
  	struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
63e4863fa   J. Bruce Fields   nfsd4: make recal...
806
  	struct nfs4_client *clp = dp->dl_client;
4b21d0def   J. Bruce Fields   nfsd4: allow 4.0 ...
807
  	struct rpc_clnt *current_rpc_client = clp->cl_cb_client;
63e4863fa   J. Bruce Fields   nfsd4: make recal...
808

0421b5c55   Ricardo Labiaga   nfsd41: Backchann...
809
  	nfsd4_cb_done(task, calldata);
a8f2800b4   J. Bruce Fields   nfsd4: fix callba...
810
811
812
813
  	if (current_rpc_client != task->tk_client) {
  		/* We're shutting down or changing cl_cb_client; leave
  		 * it to nfsd4_process_cb_update to restart the call if
  		 * necessary. */
4b21d0def   J. Bruce Fields   nfsd4: allow 4.0 ...
814
815
  		return;
  	}
5ce8ba25d   J. Bruce Fields   nfsd4: allow rest...
816
817
  	if (cb->cb_done)
  		return;
63e4863fa   J. Bruce Fields   nfsd4: make recal...
818
  	switch (task->tk_status) {
172c85dd5   J. Bruce Fields   nfsd4: treat more...
819
  	case 0:
5ce8ba25d   J. Bruce Fields   nfsd4: allow rest...
820
  		cb->cb_done = true;
172c85dd5   J. Bruce Fields   nfsd4: treat more...
821
822
823
824
825
826
827
  		return;
  	case -EBADHANDLE:
  	case -NFS4ERR_BAD_STATEID:
  		/* Race: client probably got cb_recall
  		 * before open reply granting delegation */
  		break;
  	default:
63e4863fa   J. Bruce Fields   nfsd4: make recal...
828
  		/* Network partition? */
77a3569d6   J. Bruce Fields   nfsd4: keep finer...
829
  		nfsd4_mark_cb_down(clp, task->tk_status);
63e4863fa   J. Bruce Fields   nfsd4: make recal...
830
831
832
833
  	}
  	if (dp->dl_retries--) {
  		rpc_delay(task, 2*HZ);
  		task->tk_status = 0;
c18c821fd   Boaz Harrosh   nfsd41: Fix a cra...
834
  		rpc_restart_call_prepare(task);
0421b5c55   Ricardo Labiaga   nfsd41: Backchann...
835
  		return;
63e4863fa   J. Bruce Fields   nfsd4: make recal...
836
  	}
5ce8ba25d   J. Bruce Fields   nfsd4: allow rest...
837
838
  	nfsd4_mark_cb_down(clp, task->tk_status);
  	cb->cb_done = true;
63e4863fa   J. Bruce Fields   nfsd4: make recal...
839
840
841
842
  }
  
  static void nfsd4_cb_recall_release(void *calldata)
  {
5878453db   J. Bruce Fields   nfsd4: generic ca...
843
  	struct nfsd4_callback *cb = calldata;
5ce8ba25d   J. Bruce Fields   nfsd4: allow rest...
844
  	struct nfs4_client *clp = cb->cb_clp;
5878453db   J. Bruce Fields   nfsd4: generic ca...
845
  	struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
63e4863fa   J. Bruce Fields   nfsd4: make recal...
846

5ce8ba25d   J. Bruce Fields   nfsd4: allow rest...
847
848
849
850
851
852
  	if (cb->cb_done) {
  		spin_lock(&clp->cl_lock);
  		list_del(&cb->cb_per_client);
  		spin_unlock(&clp->cl_lock);
  		nfs4_put_delegation(dp);
  	}
63e4863fa   J. Bruce Fields   nfsd4: make recal...
853
854
855
  }
  
  static const struct rpc_call_ops nfsd4_cb_recall_ops = {
2a1d1b593   Ricardo Labiaga   nfsd41: Backchann...
856
  	.rpc_call_prepare = nfsd4_cb_prepare,
63e4863fa   J. Bruce Fields   nfsd4: make recal...
857
858
859
  	.rpc_call_done = nfsd4_cb_recall_done,
  	.rpc_release = nfsd4_cb_recall_release,
  };
b5a1a81e5   J. Bruce Fields   nfsd4: don't slee...
860
861
862
863
864
865
866
867
868
869
870
871
  int nfsd4_create_callback_queue(void)
  {
  	callback_wq = create_singlethread_workqueue("nfsd4_callbacks");
  	if (!callback_wq)
  		return -ENOMEM;
  	return 0;
  }
  
  void nfsd4_destroy_callback_queue(void)
  {
  	destroy_workqueue(callback_wq);
  }
ab707e156   Benny Halevy   nfsd4: nfsd4_dest...
872
  /* must be called under the state lock */
6ff8da088   J. Bruce Fields   nfsd4: Move callb...
873
  void nfsd4_shutdown_callback(struct nfs4_client *clp)
b5a1a81e5   J. Bruce Fields   nfsd4: don't slee...
874
  {
6ff8da088   J. Bruce Fields   nfsd4: Move callb...
875
  	set_bit(NFSD4_CLIENT_KILL, &clp->cl_cb_flags);
b5a1a81e5   J. Bruce Fields   nfsd4: don't slee...
876
  	/*
6ff8da088   J. Bruce Fields   nfsd4: Move callb...
877
878
879
  	 * Note this won't actually result in a null callback;
  	 * instead, nfsd4_do_callback_rpc() will detect the killed
  	 * client, destroy the rpc client, and stop:
b5a1a81e5   J. Bruce Fields   nfsd4: don't slee...
880
  	 */
6ff8da088   J. Bruce Fields   nfsd4: Move callb...
881
  	do_probe_callback(clp);
b5a1a81e5   J. Bruce Fields   nfsd4: don't slee...
882
  	flush_workqueue(callback_wq);
b5a1a81e5   J. Bruce Fields   nfsd4: don't slee...
883
  }
65e4c8945   Kirill A. Shutemov   nfsd: declare sev...
884
  static void nfsd4_release_cb(struct nfsd4_callback *cb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
885
  {
5878453db   J. Bruce Fields   nfsd4: generic ca...
886
887
888
  	if (cb->cb_ops->rpc_release)
  		cb->cb_ops->rpc_release(cb);
  }
dcbeaa68d   J. Bruce Fields   nfsd4: allow back...
889
890
891
892
893
894
895
896
897
898
899
900
901
902
  /* requires cl_lock: */
  static struct nfsd4_conn * __nfsd4_find_backchannel(struct nfs4_client *clp)
  {
  	struct nfsd4_session *s;
  	struct nfsd4_conn *c;
  
  	list_for_each_entry(s, &clp->cl_sessions, se_perclnt) {
  		list_for_each_entry(c, &s->se_conns, cn_persession) {
  			if (c->cn_flags & NFS4_CDFC4_BACK)
  				return c;
  		}
  	}
  	return NULL;
  }
65e4c8945   Kirill A. Shutemov   nfsd: declare sev...
903
  static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
6ff8da088   J. Bruce Fields   nfsd4: Move callb...
904
905
906
  {
  	struct nfs4_cb_conn conn;
  	struct nfs4_client *clp = cb->cb_clp;
dcbeaa68d   J. Bruce Fields   nfsd4: allow back...
907
908
  	struct nfsd4_session *ses = NULL;
  	struct nfsd4_conn *c;
6ff8da088   J. Bruce Fields   nfsd4: Move callb...
909
910
911
912
913
914
915
916
917
918
  	int err;
  
  	/*
  	 * This is either an update, or the client dying; in either case,
  	 * kill the old client:
  	 */
  	if (clp->cl_cb_client) {
  		rpc_shutdown_client(clp->cl_cb_client);
  		clp->cl_cb_client = NULL;
  	}
dcbeaa68d   J. Bruce Fields   nfsd4: allow back...
919
920
921
922
  	if (clp->cl_cb_conn.cb_xprt) {
  		svc_xprt_put(clp->cl_cb_conn.cb_xprt);
  		clp->cl_cb_conn.cb_xprt = NULL;
  	}
6ff8da088   J. Bruce Fields   nfsd4: Move callb...
923
924
925
926
927
928
929
930
931
932
  	if (test_bit(NFSD4_CLIENT_KILL, &clp->cl_cb_flags))
  		return;
  	spin_lock(&clp->cl_lock);
  	/*
  	 * Only serialized callback code is allowed to clear these
  	 * flags; main nfsd code can only set them:
  	 */
  	BUG_ON(!clp->cl_cb_flags);
  	clear_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags);
  	memcpy(&conn, &cb->cb_clp->cl_cb_conn, sizeof(struct nfs4_cb_conn));
dcbeaa68d   J. Bruce Fields   nfsd4: allow back...
933
934
935
936
937
938
  	c = __nfsd4_find_backchannel(clp);
  	if (c) {
  		svc_xprt_get(c->cn_xprt);
  		conn.cb_xprt = c->cn_xprt;
  		ses = c->cn_session;
  	}
6ff8da088   J. Bruce Fields   nfsd4: Move callb...
939
  	spin_unlock(&clp->cl_lock);
dcbeaa68d   J. Bruce Fields   nfsd4: allow back...
940
  	err = setup_callback_client(clp, &conn, ses);
5ce8ba25d   J. Bruce Fields   nfsd4: allow rest...
941
  	if (err) {
6ff8da088   J. Bruce Fields   nfsd4: Move callb...
942
  		warn_no_callback_path(clp, err);
5ce8ba25d   J. Bruce Fields   nfsd4: allow rest...
943
944
945
946
947
  		return;
  	}
  	/* Yay, the callback channel's back! Restart any callbacks: */
  	list_for_each_entry(cb, &clp->cl_callbacks, cb_per_client)
  		run_nfsd4_cb(cb);
6ff8da088   J. Bruce Fields   nfsd4: Move callb...
948
  }
5878453db   J. Bruce Fields   nfsd4: generic ca...
949
950
951
  void nfsd4_do_callback_rpc(struct work_struct *w)
  {
  	struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
fb0039232   J. Bruce Fields   nfsd4: remove sep...
952
  	struct nfs4_client *clp = cb->cb_clp;
6ff8da088   J. Bruce Fields   nfsd4: Move callb...
953
  	struct rpc_clnt *clnt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
954

6ff8da088   J. Bruce Fields   nfsd4: Move callb...
955
956
957
958
959
960
  	if (clp->cl_cb_flags)
  		nfsd4_process_cb_update(cb);
  
  	clnt = clp->cl_cb_client;
  	if (!clnt) {
  		/* Callback channel broken, or client killed; give up: */
5878453db   J. Bruce Fields   nfsd4: generic ca...
961
  		nfsd4_release_cb(cb);
6ff8da088   J. Bruce Fields   nfsd4: Move callb...
962
  		return;
ac94bf582   J. Bruce Fields   nfsd4: fix deleg ...
963
  	}
cee277d92   J. Bruce Fields   nfsd4: use generi...
964
965
  	rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
  			cb->cb_ops, cb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
966
  }
b5a1a81e5   J. Bruce Fields   nfsd4: don't slee...
967

5878453db   J. Bruce Fields   nfsd4: generic ca...
968
  void nfsd4_cb_recall(struct nfs4_delegation *dp)
b5a1a81e5   J. Bruce Fields   nfsd4: don't slee...
969
  {
5878453db   J. Bruce Fields   nfsd4: generic ca...
970
  	struct nfsd4_callback *cb = &dp->dl_recall;
5ce8ba25d   J. Bruce Fields   nfsd4: allow rest...
971
  	struct nfs4_client *clp = dp->dl_client;
b5a1a81e5   J. Bruce Fields   nfsd4: don't slee...
972

5878453db   J. Bruce Fields   nfsd4: generic ca...
973
  	dp->dl_retries = 1;
fb0039232   J. Bruce Fields   nfsd4: remove sep...
974
  	cb->cb_op = dp;
5ce8ba25d   J. Bruce Fields   nfsd4: allow rest...
975
  	cb->cb_clp = clp;
5878453db   J. Bruce Fields   nfsd4: generic ca...
976
  	cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL];
fb0039232   J. Bruce Fields   nfsd4: remove sep...
977
978
  	cb->cb_msg.rpc_argp = cb;
  	cb->cb_msg.rpc_resp = cb;
5878453db   J. Bruce Fields   nfsd4: generic ca...
979
980
981
982
  	cb->cb_msg.rpc_cred = callback_cred;
  
  	cb->cb_ops = &nfsd4_cb_recall_ops;
  	dp->dl_retries = 1;
b5a1a81e5   J. Bruce Fields   nfsd4: don't slee...
983

9ee1ba540   J. Bruce Fields   nfsd4: initialize...
984
  	INIT_LIST_HEAD(&cb->cb_per_client);
5ce8ba25d   J. Bruce Fields   nfsd4: allow rest...
985
  	cb->cb_done = true;
229b2a083   J. Bruce Fields   nfsd4: add helper...
986
  	run_nfsd4_cb(&dp->dl_recall);
b5a1a81e5   J. Bruce Fields   nfsd4: don't slee...
987
  }