xref: /titanic_52/usr/src/uts/common/fs/nfs/nfs4_dispatch.c (revision c93d332c7127fa77fd1d38a46c87a7d24d7a9ef8)
1d9ad96c1Srg137905 /*
2d9ad96c1Srg137905  * CDDL HEADER START
3d9ad96c1Srg137905  *
4d9ad96c1Srg137905  * The contents of this file are subject to the terms of the
548a34407Srmesta  * Common Development and Distribution License (the "License").
648a34407Srmesta  * You may not use this file except in compliance with the License.
7d9ad96c1Srg137905  *
8d9ad96c1Srg137905  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d9ad96c1Srg137905  * or http://www.opensolaris.org/os/licensing.
10d9ad96c1Srg137905  * See the License for the specific language governing permissions
11d9ad96c1Srg137905  * and limitations under the License.
12d9ad96c1Srg137905  *
13d9ad96c1Srg137905  * When distributing Covered Code, include this CDDL HEADER in each
14d9ad96c1Srg137905  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d9ad96c1Srg137905  * If applicable, add the following below this CDDL HEADER, with the
16d9ad96c1Srg137905  * fields enclosed by brackets "[]" replaced with your own identifying
17d9ad96c1Srg137905  * information: Portions Copyright [yyyy] [name of copyright owner]
18d9ad96c1Srg137905  *
19d9ad96c1Srg137905  * CDDL HEADER END
20d9ad96c1Srg137905  */
21d9ad96c1Srg137905 
22d9ad96c1Srg137905 /*
2367e4138cSDai Ngo  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24d9ad96c1Srg137905  * Use is subject to license terms.
25d9ad96c1Srg137905  */
26d9ad96c1Srg137905 
276cb01462Srg137905 #include <sys/systm.h>
286cb01462Srg137905 #include <sys/sdt.h>
29d9ad96c1Srg137905 #include <rpc/types.h>
30d9ad96c1Srg137905 #include <rpc/auth.h>
31d9ad96c1Srg137905 #include <rpc/auth_unix.h>
32d9ad96c1Srg137905 #include <rpc/auth_des.h>
33d9ad96c1Srg137905 #include <rpc/svc.h>
34d9ad96c1Srg137905 #include <rpc/xdr.h>
35d9ad96c1Srg137905 #include <nfs/nfs4.h>
36d9ad96c1Srg137905 #include <nfs/nfs_dispatch.h>
37d9ad96c1Srg137905 #include <nfs/nfs4_drc.h>
38d9ad96c1Srg137905 
397d12f3bcSmaheshvs #define	NFS4_MAX_MINOR_VERSION	0
407d12f3bcSmaheshvs 
41d9ad96c1Srg137905 /*
42d9ad96c1Srg137905  * This is the duplicate request cache for NFSv4
43d9ad96c1Srg137905  */
44d9ad96c1Srg137905 rfs4_drc_t *nfs4_drc = NULL;
45d9ad96c1Srg137905 
46d9ad96c1Srg137905 /*
47d9ad96c1Srg137905  * The default size of the duplicate request cache
48d9ad96c1Srg137905  */
49d9ad96c1Srg137905 uint32_t nfs4_drc_max = 8 * 1024;
50d9ad96c1Srg137905 
51d9ad96c1Srg137905 /*
52d9ad96c1Srg137905  * The number of buckets we'd like to hash the
53d9ad96c1Srg137905  * replies into.. do not change this on the fly.
54d9ad96c1Srg137905  */
55d9ad96c1Srg137905 uint32_t nfs4_drc_hash = 541;
56d9ad96c1Srg137905 
5767e4138cSDai Ngo static void rfs4_resource_err(struct svc_req *req, COMPOUND4args *argsp);
5867e4138cSDai Ngo 
59d9ad96c1Srg137905 /*
60d9ad96c1Srg137905  * Initialize a duplicate request cache.
61d9ad96c1Srg137905  */
62d9ad96c1Srg137905 rfs4_drc_t *
6348a34407Srmesta rfs4_init_drc(uint32_t drc_size, uint32_t drc_hash_size)
64d9ad96c1Srg137905 {
65d9ad96c1Srg137905 	rfs4_drc_t *drc;
66d9ad96c1Srg137905 	uint32_t   bki;
67d9ad96c1Srg137905 
68d9ad96c1Srg137905 	ASSERT(drc_size);
69d9ad96c1Srg137905 	ASSERT(drc_hash_size);
70d9ad96c1Srg137905 
71d9ad96c1Srg137905 	drc = kmem_alloc(sizeof (rfs4_drc_t), KM_SLEEP);
72d9ad96c1Srg137905 
73d9ad96c1Srg137905 	drc->max_size = drc_size;
74d9ad96c1Srg137905 	drc->in_use = 0;
75d9ad96c1Srg137905 
76d9ad96c1Srg137905 	mutex_init(&drc->lock, NULL, MUTEX_DEFAULT, NULL);
77d9ad96c1Srg137905 
78d9ad96c1Srg137905 	drc->dr_hash = drc_hash_size;
79d9ad96c1Srg137905 
80d9ad96c1Srg137905 	drc->dr_buckets = kmem_alloc(sizeof (list_t)*drc_hash_size, KM_SLEEP);
81d9ad96c1Srg137905 
82d9ad96c1Srg137905 	for (bki = 0; bki < drc_hash_size; bki++) {
83d9ad96c1Srg137905 		list_create(&drc->dr_buckets[bki], sizeof (rfs4_dupreq_t),
84d9ad96c1Srg137905 		    offsetof(rfs4_dupreq_t, dr_bkt_next));
85d9ad96c1Srg137905 	}
86d9ad96c1Srg137905 
87d9ad96c1Srg137905 	list_create(&(drc->dr_cache), sizeof (rfs4_dupreq_t),
88d9ad96c1Srg137905 	    offsetof(rfs4_dupreq_t, dr_next));
89d9ad96c1Srg137905 
90d9ad96c1Srg137905 	return (drc);
91d9ad96c1Srg137905 }
92d9ad96c1Srg137905 
93d9ad96c1Srg137905 /*
94d9ad96c1Srg137905  * Destroy a duplicate request cache.
95d9ad96c1Srg137905  */
96d9ad96c1Srg137905 void
97d9ad96c1Srg137905 rfs4_fini_drc(rfs4_drc_t *drc)
98d9ad96c1Srg137905 {
99d9ad96c1Srg137905 	rfs4_dupreq_t *drp, *drp_next;
100d9ad96c1Srg137905 
101d9ad96c1Srg137905 	ASSERT(drc);
102d9ad96c1Srg137905 
103d9ad96c1Srg137905 	/* iterate over the dr_cache and free the enties */
104d9ad96c1Srg137905 	for (drp = list_head(&(drc->dr_cache)); drp != NULL; drp = drp_next) {
105d9ad96c1Srg137905 
106d9ad96c1Srg137905 		if (drp->dr_state == NFS4_DUP_REPLAY)
107d9ad96c1Srg137905 			rfs4_compound_free(&(drp->dr_res));
108d9ad96c1Srg137905 
109d9ad96c1Srg137905 		if (drp->dr_addr.buf != NULL)
110d9ad96c1Srg137905 			kmem_free(drp->dr_addr.buf, drp->dr_addr.maxlen);
111d9ad96c1Srg137905 
112d9ad96c1Srg137905 		drp_next = list_next(&(drc->dr_cache), drp);
113d9ad96c1Srg137905 
114d9ad96c1Srg137905 		kmem_free(drp, sizeof (rfs4_dupreq_t));
115d9ad96c1Srg137905 	}
116d9ad96c1Srg137905 
117d9ad96c1Srg137905 	mutex_destroy(&drc->lock);
118d9ad96c1Srg137905 	kmem_free(drc->dr_buckets,
119d9ad96c1Srg137905 	    sizeof (list_t)*drc->dr_hash);
120d9ad96c1Srg137905 	kmem_free(drc, sizeof (rfs4_drc_t));
121d9ad96c1Srg137905 }
122d9ad96c1Srg137905 
123d9ad96c1Srg137905 /*
124d9ad96c1Srg137905  * rfs4_dr_chstate:
125d9ad96c1Srg137905  *
126d9ad96c1Srg137905  * Change the state of a rfs4_dupreq. If it's not in transition
127b4467216Spf199842  * to the FREE state, return. If we are moving to the FREE state
128b4467216Spf199842  * then we need to clean up the compound results and move the entry
129b4467216Spf199842  * to the end of the list.
130d9ad96c1Srg137905  */
131d9ad96c1Srg137905 void
132d9ad96c1Srg137905 rfs4_dr_chstate(rfs4_dupreq_t *drp, int new_state)
133d9ad96c1Srg137905 {
134d9ad96c1Srg137905 	rfs4_drc_t *drc;
135d9ad96c1Srg137905 
136d9ad96c1Srg137905 	ASSERT(drp);
137d9ad96c1Srg137905 	ASSERT(drp->drc);
138d9ad96c1Srg137905 	ASSERT(drp->dr_bkt);
139d9ad96c1Srg137905 	ASSERT(MUTEX_HELD(&drp->drc->lock));
140d9ad96c1Srg137905 
141d9ad96c1Srg137905 	drp->dr_state = new_state;
142d9ad96c1Srg137905 
143b4467216Spf199842 	if (new_state != NFS4_DUP_FREE)
144d9ad96c1Srg137905 		return;
145d9ad96c1Srg137905 
146d9ad96c1Srg137905 	drc = drp->drc;
147d9ad96c1Srg137905 
148d9ad96c1Srg137905 	/*
149d9ad96c1Srg137905 	 * Remove entry from the bucket and
150d9ad96c1Srg137905 	 * dr_cache list, free compound results.
151d9ad96c1Srg137905 	 */
152d9ad96c1Srg137905 	list_remove(drp->dr_bkt, drp);
153d9ad96c1Srg137905 	list_remove(&(drc->dr_cache), drp);
154d9ad96c1Srg137905 	rfs4_compound_free(&(drp->dr_res));
155d9ad96c1Srg137905 }
156d9ad96c1Srg137905 
157d9ad96c1Srg137905 /*
158d9ad96c1Srg137905  * rfs4_alloc_dr:
159d9ad96c1Srg137905  *
16048a34407Srmesta  * Malloc a new one if we have not reached our maximum cache
16148a34407Srmesta  * limit, otherwise pick an entry off the tail -- Use if it
16248a34407Srmesta  * is marked as NFS4_DUP_FREE, or is an entry in the
16348a34407Srmesta  * NFS4_DUP_REPLAY state.
164d9ad96c1Srg137905  */
165d9ad96c1Srg137905 rfs4_dupreq_t *
166d9ad96c1Srg137905 rfs4_alloc_dr(rfs4_drc_t *drc)
167d9ad96c1Srg137905 {
168d9ad96c1Srg137905 	rfs4_dupreq_t *drp_tail, *drp = NULL;
169d9ad96c1Srg137905 
170d9ad96c1Srg137905 	ASSERT(drc);
171d9ad96c1Srg137905 	ASSERT(MUTEX_HELD(&drc->lock));
172d9ad96c1Srg137905 
17348a34407Srmesta 	/*
17448a34407Srmesta 	 * Have we hit the cache limit yet ?
17548a34407Srmesta 	 */
17648a34407Srmesta 	if (drc->in_use < drc->max_size) {
17748a34407Srmesta 		/*
17848a34407Srmesta 		 * nope, so let's malloc a new one
17948a34407Srmesta 		 */
18048a34407Srmesta 		drp = kmem_zalloc(sizeof (rfs4_dupreq_t), KM_SLEEP);
18148a34407Srmesta 		drp->drc = drc;
18248a34407Srmesta 		drc->in_use++;
18348a34407Srmesta 		DTRACE_PROBE1(nfss__i__drc_new, rfs4_dupreq_t *, drp);
18448a34407Srmesta 		return (drp);
18548a34407Srmesta 	}
18648a34407Srmesta 
18748a34407Srmesta 	/*
18848a34407Srmesta 	 * Cache is all allocated now traverse the list
18948a34407Srmesta 	 * backwards to find one we can reuse.
19048a34407Srmesta 	 */
19148a34407Srmesta 	for (drp_tail = list_tail(&drc->dr_cache); drp_tail != NULL;
19248a34407Srmesta 	    drp_tail = list_prev(&drc->dr_cache, drp_tail)) {
193d9ad96c1Srg137905 
194d9ad96c1Srg137905 		switch (drp_tail->dr_state) {
195d9ad96c1Srg137905 
196d9ad96c1Srg137905 		case NFS4_DUP_FREE:
197d9ad96c1Srg137905 			list_remove(&(drc->dr_cache), drp_tail);
198d9ad96c1Srg137905 			DTRACE_PROBE1(nfss__i__drc_freeclaim,
199d9ad96c1Srg137905 			    rfs4_dupreq_t *, drp_tail);
200d9ad96c1Srg137905 			return (drp_tail);
201d9ad96c1Srg137905 			/* NOTREACHED */
202d9ad96c1Srg137905 
203d9ad96c1Srg137905 		case NFS4_DUP_REPLAY:
20448a34407Srmesta 			/* grab it. */
205d9ad96c1Srg137905 			rfs4_dr_chstate(drp_tail, NFS4_DUP_FREE);
20648a34407Srmesta 			DTRACE_PROBE1(nfss__i__drc_replayclaim,
207d9ad96c1Srg137905 			    rfs4_dupreq_t *, drp_tail);
208d9ad96c1Srg137905 			return (drp_tail);
20948a34407Srmesta 			/* NOTREACHED */
210d9ad96c1Srg137905 		}
211d9ad96c1Srg137905 	}
21248a34407Srmesta 	DTRACE_PROBE1(nfss__i__drc_full, rfs4_drc_t *, drc);
213d9ad96c1Srg137905 	return (NULL);
214d9ad96c1Srg137905 }
215d9ad96c1Srg137905 
216d9ad96c1Srg137905 /*
217d9ad96c1Srg137905  * rfs4_find_dr:
218d9ad96c1Srg137905  *
219d9ad96c1Srg137905  * Search for an entry in the duplicate request cache by
220d9ad96c1Srg137905  * calculating the hash index based on the XID, and examining
221b4467216Spf199842  * the entries in the hash bucket. If we find a match, return.
222b4467216Spf199842  * Once we have searched the bucket we call rfs4_alloc_dr() to
223b4467216Spf199842  * allocate a new entry, or reuse one that is available.
224d9ad96c1Srg137905  */
225d9ad96c1Srg137905 int
226d9ad96c1Srg137905 rfs4_find_dr(struct svc_req *req, rfs4_drc_t *drc, rfs4_dupreq_t **dup)
227d9ad96c1Srg137905 {
228d9ad96c1Srg137905 
229d9ad96c1Srg137905 	uint32_t	the_xid;
230d9ad96c1Srg137905 	list_t		*dr_bkt;
231d9ad96c1Srg137905 	rfs4_dupreq_t	*drp;
232d9ad96c1Srg137905 	int		bktdex;
233d9ad96c1Srg137905 
234d9ad96c1Srg137905 	/*
235d9ad96c1Srg137905 	 * Get the XID, calculate the bucket and search to
236d9ad96c1Srg137905 	 * see if we need to replay from the cache.
237d9ad96c1Srg137905 	 */
238d9ad96c1Srg137905 	the_xid = req->rq_xprt->xp_xid;
239d9ad96c1Srg137905 	bktdex = the_xid % drc->dr_hash;
240d9ad96c1Srg137905 
241d9ad96c1Srg137905 	dr_bkt = (list_t *)
242d9ad96c1Srg137905 	    &(drc->dr_buckets[(the_xid % drc->dr_hash)]);
243d9ad96c1Srg137905 
244d9ad96c1Srg137905 	DTRACE_PROBE3(nfss__i__drc_bktdex,
245d9ad96c1Srg137905 	    int, bktdex,
246d9ad96c1Srg137905 	    uint32_t, the_xid,
247d9ad96c1Srg137905 	    list_t *, dr_bkt);
248d9ad96c1Srg137905 
249d9ad96c1Srg137905 	*dup = NULL;
250d9ad96c1Srg137905 
251d9ad96c1Srg137905 	mutex_enter(&drc->lock);
252d9ad96c1Srg137905 	/*
253d9ad96c1Srg137905 	 * Search the bucket for a matching xid and address.
254d9ad96c1Srg137905 	 */
255d9ad96c1Srg137905 	for (drp = list_head(dr_bkt); drp != NULL;
256d9ad96c1Srg137905 	    drp = list_next(dr_bkt, drp)) {
257d9ad96c1Srg137905 
258d9ad96c1Srg137905 		if (drp->dr_xid == the_xid &&
259d9ad96c1Srg137905 		    drp->dr_addr.len == req->rq_xprt->xp_rtaddr.len &&
260d9ad96c1Srg137905 		    bcmp((caddr_t)drp->dr_addr.buf,
261d9ad96c1Srg137905 		    (caddr_t)req->rq_xprt->xp_rtaddr.buf,
262d9ad96c1Srg137905 		    drp->dr_addr.len) == 0) {
263d9ad96c1Srg137905 
264d9ad96c1Srg137905 			/*
265d9ad96c1Srg137905 			 * Found a match so REPLAY the Reply
266d9ad96c1Srg137905 			 */
267d9ad96c1Srg137905 			if (drp->dr_state == NFS4_DUP_REPLAY) {
26848a34407Srmesta 				rfs4_dr_chstate(drp, NFS4_DUP_INUSE);
269d9ad96c1Srg137905 				mutex_exit(&drc->lock);
270d9ad96c1Srg137905 				*dup = drp;
271d9ad96c1Srg137905 				DTRACE_PROBE1(nfss__i__drc_replay,
272d9ad96c1Srg137905 				    rfs4_dupreq_t *, drp);
273d9ad96c1Srg137905 				return (NFS4_DUP_REPLAY);
274d9ad96c1Srg137905 			}
275d9ad96c1Srg137905 
276d9ad96c1Srg137905 			/*
277d9ad96c1Srg137905 			 * This entry must be in transition, so return
278d9ad96c1Srg137905 			 * the 'pending' status.
279d9ad96c1Srg137905 			 */
280d9ad96c1Srg137905 			mutex_exit(&drc->lock);
281d9ad96c1Srg137905 			return (NFS4_DUP_PENDING);
282d9ad96c1Srg137905 		}
283d9ad96c1Srg137905 	}
284d9ad96c1Srg137905 
285d9ad96c1Srg137905 	drp = rfs4_alloc_dr(drc);
286d9ad96c1Srg137905 	mutex_exit(&drc->lock);
287d9ad96c1Srg137905 
28848a34407Srmesta 	/*
28948a34407Srmesta 	 * The DRC is full and all entries are in use. Upper function
29048a34407Srmesta 	 * should error out this request and force the client to
29148a34407Srmesta 	 * retransmit -- effectively this is a resource issue. NFSD
29248a34407Srmesta 	 * threads tied up with native File System, or the cache size
29348a34407Srmesta 	 * is too small for the server load.
29448a34407Srmesta 	 */
29548a34407Srmesta 	if (drp == NULL)
296d9ad96c1Srg137905 		return (NFS4_DUP_ERROR);
297d9ad96c1Srg137905 
298d9ad96c1Srg137905 	/*
299b4467216Spf199842 	 * Init the state to NEW.
300d9ad96c1Srg137905 	 */
301d9ad96c1Srg137905 	drp->dr_state = NFS4_DUP_NEW;
302d9ad96c1Srg137905 
303d9ad96c1Srg137905 	/*
304d9ad96c1Srg137905 	 * If needed, resize the address buffer
305d9ad96c1Srg137905 	 */
306d9ad96c1Srg137905 	if (drp->dr_addr.maxlen < req->rq_xprt->xp_rtaddr.len) {
307d9ad96c1Srg137905 		if (drp->dr_addr.buf != NULL)
308d9ad96c1Srg137905 			kmem_free(drp->dr_addr.buf, drp->dr_addr.maxlen);
309d9ad96c1Srg137905 		drp->dr_addr.maxlen = req->rq_xprt->xp_rtaddr.len;
310d9ad96c1Srg137905 		drp->dr_addr.buf = kmem_alloc(drp->dr_addr.maxlen, KM_NOSLEEP);
311d9ad96c1Srg137905 		if (drp->dr_addr.buf == NULL) {
312d9ad96c1Srg137905 			/*
313d9ad96c1Srg137905 			 * If the malloc fails, mark the entry
314d9ad96c1Srg137905 			 * as free and put on the tail.
315d9ad96c1Srg137905 			 */
316d9ad96c1Srg137905 			drp->dr_addr.maxlen = 0;
317d9ad96c1Srg137905 			drp->dr_state = NFS4_DUP_FREE;
318d9ad96c1Srg137905 			mutex_enter(&drc->lock);
319d9ad96c1Srg137905 			list_insert_tail(&(drc->dr_cache), drp);
320d9ad96c1Srg137905 			mutex_exit(&drc->lock);
321d9ad96c1Srg137905 			return (NFS4_DUP_ERROR);
322d9ad96c1Srg137905 		}
323d9ad96c1Srg137905 	}
324d9ad96c1Srg137905 
325d9ad96c1Srg137905 
326d9ad96c1Srg137905 	/*
327d9ad96c1Srg137905 	 * Copy the address.
328d9ad96c1Srg137905 	 */
329d9ad96c1Srg137905 	drp->dr_addr.len = req->rq_xprt->xp_rtaddr.len;
330d9ad96c1Srg137905 
331d9ad96c1Srg137905 	bcopy((caddr_t)req->rq_xprt->xp_rtaddr.buf,
332d9ad96c1Srg137905 	    (caddr_t)drp->dr_addr.buf,
333d9ad96c1Srg137905 	    drp->dr_addr.len);
334d9ad96c1Srg137905 
335d9ad96c1Srg137905 	drp->dr_xid = the_xid;
336d9ad96c1Srg137905 	drp->dr_bkt = dr_bkt;
337d9ad96c1Srg137905 
338d9ad96c1Srg137905 	/*
339d9ad96c1Srg137905 	 * Insert at the head of the bucket and
340d9ad96c1Srg137905 	 * the drc lists..
341d9ad96c1Srg137905 	 */
342d9ad96c1Srg137905 	mutex_enter(&drc->lock);
343d9ad96c1Srg137905 	list_insert_head(&drc->dr_cache, drp);
344d9ad96c1Srg137905 	list_insert_head(dr_bkt, drp);
345d9ad96c1Srg137905 	mutex_exit(&drc->lock);
346d9ad96c1Srg137905 
347d9ad96c1Srg137905 	*dup = drp;
348d9ad96c1Srg137905 
349d9ad96c1Srg137905 	return (NFS4_DUP_NEW);
350d9ad96c1Srg137905 }
351d9ad96c1Srg137905 
352d9ad96c1Srg137905 /*
353d9ad96c1Srg137905  *
354d9ad96c1Srg137905  * This function handles the duplicate request cache,
355d9ad96c1Srg137905  * NULL_PROC and COMPOUND procedure calls for NFSv4;
356d9ad96c1Srg137905  *
357d9ad96c1Srg137905  * Passed into this function are:-
358d9ad96c1Srg137905  *
359d9ad96c1Srg137905  * 	disp	A pointer to our dispatch table entry
360d9ad96c1Srg137905  * 	req	The request to process
361d9ad96c1Srg137905  * 	xprt	The server transport handle
362d9ad96c1Srg137905  * 	ap	A pointer to the arguments
363d9ad96c1Srg137905  *
364d9ad96c1Srg137905  *
365d9ad96c1Srg137905  * When appropriate this function is responsible for inserting
366d9ad96c1Srg137905  * the reply into the duplicate cache or replaying an existing
367d9ad96c1Srg137905  * cached reply.
368d9ad96c1Srg137905  *
369d9ad96c1Srg137905  * dr_stat 	reflects the state of the duplicate request that
370d9ad96c1Srg137905  * 		has been inserted into or retrieved from the cache
371d9ad96c1Srg137905  *
372d9ad96c1Srg137905  * drp		is the duplicate request entry
373d9ad96c1Srg137905  *
374d9ad96c1Srg137905  */
375d9ad96c1Srg137905 int
376d9ad96c1Srg137905 rfs4_dispatch(struct rpcdisp *disp, struct svc_req *req,
377*c93d332cSDan McDonald 		SVCXPRT *xprt, char *ap)
378d9ad96c1Srg137905 {
379d9ad96c1Srg137905 
3802e9d26a4Srmesta 	COMPOUND4res	 res_buf;
3812e9d26a4Srmesta 	COMPOUND4res	*rbp;
382d9ad96c1Srg137905 	COMPOUND4args	*cap;
383d9ad96c1Srg137905 	cred_t		*cr = NULL;
384d9ad96c1Srg137905 	int		 error = 0;
385d9ad96c1Srg137905 	int		 dis_flags = 0;
386d9ad96c1Srg137905 	int		 dr_stat = NFS4_NOT_DUP;
387d9ad96c1Srg137905 	rfs4_dupreq_t	*drp = NULL;
3882e9d26a4Srmesta 	int		 rv;
389d9ad96c1Srg137905 
390d9ad96c1Srg137905 	ASSERT(disp);
391d9ad96c1Srg137905 
392d9ad96c1Srg137905 	/*
393d9ad96c1Srg137905 	 * Short circuit the RPC_NULL proc.
394d9ad96c1Srg137905 	 */
395d9ad96c1Srg137905 	if (disp->dis_proc == rpc_null) {
396f3b585ceSsamf 		DTRACE_NFSV4_1(null__start, struct svc_req *, req);
397d9ad96c1Srg137905 		if (!svc_sendreply(xprt, xdr_void, NULL)) {
398f3b585ceSsamf 			DTRACE_NFSV4_1(null__done, struct svc_req *, req);
39971c6e709Srmesta 			svcerr_systemerr(xprt);
400d9ad96c1Srg137905 			return (1);
401d9ad96c1Srg137905 		}
402f3b585ceSsamf 		DTRACE_NFSV4_1(null__done, struct svc_req *, req);
403d9ad96c1Srg137905 		return (0);
404d9ad96c1Srg137905 	}
405d9ad96c1Srg137905 
406d9ad96c1Srg137905 	/* Only NFSv4 Compounds from this point onward */
407d9ad96c1Srg137905 
408d9ad96c1Srg137905 	rbp = &res_buf;
409d9ad96c1Srg137905 	cap = (COMPOUND4args *)ap;
410d9ad96c1Srg137905 
411d9ad96c1Srg137905 	/*
412d9ad96c1Srg137905 	 * Figure out the disposition of the whole COMPOUND
413d9ad96c1Srg137905 	 * and record it's IDEMPOTENTCY.
414d9ad96c1Srg137905 	 */
415d9ad96c1Srg137905 	rfs4_compound_flagproc(cap, &dis_flags);
416d9ad96c1Srg137905 
417d9ad96c1Srg137905 	/*
418d9ad96c1Srg137905 	 * If NON-IDEMPOTENT then we need to figure out if this
419d9ad96c1Srg137905 	 * request can be replied from the duplicate cache.
420d9ad96c1Srg137905 	 *
421d9ad96c1Srg137905 	 * If this is a new request then we need to insert the
422d9ad96c1Srg137905 	 * reply into the duplicate cache.
423d9ad96c1Srg137905 	 */
424d9ad96c1Srg137905 	if (!(dis_flags & RPC_IDEMPOTENT)) {
425d9ad96c1Srg137905 		/* look for a replay from the cache or allocate */
426d9ad96c1Srg137905 		dr_stat = rfs4_find_dr(req, nfs4_drc, &drp);
427d9ad96c1Srg137905 
428d9ad96c1Srg137905 		switch (dr_stat) {
429d9ad96c1Srg137905 
430d9ad96c1Srg137905 		case NFS4_DUP_ERROR:
43167e4138cSDai Ngo 			rfs4_resource_err(req, cap);
432d9ad96c1Srg137905 			return (1);
433d9ad96c1Srg137905 			/* NOTREACHED */
434d9ad96c1Srg137905 
435d9ad96c1Srg137905 		case NFS4_DUP_PENDING:
436d9ad96c1Srg137905 			/*
437d9ad96c1Srg137905 			 * reply has previously been inserted into the
438d9ad96c1Srg137905 			 * duplicate cache, however the reply has
439d9ad96c1Srg137905 			 * not yet been sent via svc_sendreply()
440d9ad96c1Srg137905 			 */
441d9ad96c1Srg137905 			return (1);
442d9ad96c1Srg137905 			/* NOTREACHED */
443d9ad96c1Srg137905 
444d9ad96c1Srg137905 		case NFS4_DUP_NEW:
445d9ad96c1Srg137905 			curthread->t_flag |= T_DONTPEND;
446d9ad96c1Srg137905 			/* NON-IDEMPOTENT proc call */
4472e9d26a4Srmesta 			rfs4_compound(cap, rbp, NULL, req, cr, &rv);
448d9ad96c1Srg137905 			curthread->t_flag &= ~T_DONTPEND;
4493f720b33Sjasmith 
4502e9d26a4Srmesta 			if (rv)		/* short ckt sendreply on error */
4512e9d26a4Srmesta 				return (rv);
4522e9d26a4Srmesta 
4533f720b33Sjasmith 			/*
4543f720b33Sjasmith 			 * dr_res must be initialized before calling
4553f720b33Sjasmith 			 * rfs4_dr_chstate (it frees the reply).
4563f720b33Sjasmith 			 */
4573f720b33Sjasmith 			drp->dr_res = res_buf;
458d9ad96c1Srg137905 			if (curthread->t_flag & T_WOULDBLOCK) {
459d9ad96c1Srg137905 				curthread->t_flag &= ~T_WOULDBLOCK;
460d9ad96c1Srg137905 				/*
461d9ad96c1Srg137905 				 * mark this entry as FREE and plop
462d9ad96c1Srg137905 				 * on the end of the cache list
463d9ad96c1Srg137905 				 */
464d9ad96c1Srg137905 				mutex_enter(&drp->drc->lock);
465d9ad96c1Srg137905 				rfs4_dr_chstate(drp, NFS4_DUP_FREE);
466d9ad96c1Srg137905 				list_insert_tail(&(drp->drc->dr_cache), drp);
467d9ad96c1Srg137905 				mutex_exit(&drp->drc->lock);
468d9ad96c1Srg137905 				return (1);
469d9ad96c1Srg137905 			}
470d9ad96c1Srg137905 			break;
471d9ad96c1Srg137905 
472d9ad96c1Srg137905 		case NFS4_DUP_REPLAY:
473d9ad96c1Srg137905 			/* replay from the cache */
474d9ad96c1Srg137905 			rbp = &(drp->dr_res);
475d9ad96c1Srg137905 			break;
476d9ad96c1Srg137905 		}
477d9ad96c1Srg137905 	} else {
478d9ad96c1Srg137905 		curthread->t_flag |= T_DONTPEND;
479d9ad96c1Srg137905 		/* IDEMPOTENT proc call */
4802e9d26a4Srmesta 		rfs4_compound(cap, rbp, NULL, req, cr, &rv);
481d9ad96c1Srg137905 		curthread->t_flag &= ~T_DONTPEND;
4822e9d26a4Srmesta 
4832e9d26a4Srmesta 		if (rv)		/* short ckt sendreply on error */
4842e9d26a4Srmesta 			return (rv);
4852e9d26a4Srmesta 
486d9ad96c1Srg137905 		if (curthread->t_flag & T_WOULDBLOCK) {
487d9ad96c1Srg137905 			curthread->t_flag &= ~T_WOULDBLOCK;
488d9ad96c1Srg137905 			return (1);
489d9ad96c1Srg137905 		}
490d9ad96c1Srg137905 	}
491d9ad96c1Srg137905 
492d9ad96c1Srg137905 	/*
493d9ad96c1Srg137905 	 * Send out the replayed reply or the 'real' one.
494d9ad96c1Srg137905 	 */
4957c46fb7fSek110237 	if (!svc_sendreply(xprt,  xdr_COMPOUND4res_srv, (char *)rbp)) {
496d9ad96c1Srg137905 		DTRACE_PROBE2(nfss__e__dispatch_sendfail,
497d9ad96c1Srg137905 		    struct svc_req *, xprt,
498d9ad96c1Srg137905 		    char *, rbp);
49971c6e709Srmesta 		svcerr_systemerr(xprt);
500d9ad96c1Srg137905 		error++;
501d9ad96c1Srg137905 	}
502d9ad96c1Srg137905 
503d9ad96c1Srg137905 	/*
504d9ad96c1Srg137905 	 * If this reply was just inserted into the duplicate cache
50548a34407Srmesta 	 * or it was replayed from the dup cache; (re)mark it as
50648a34407Srmesta 	 * available for replay
50748a34407Srmesta 	 *
50848a34407Srmesta 	 * At first glance, this 'if' statement seems a little strange;
50948a34407Srmesta 	 * testing for NFS4_DUP_REPLAY, and then calling...
51048a34407Srmesta 	 *
51148a34407Srmesta 	 *	rfs4_dr_chatate(NFS4_DUP_REPLAY)
51248a34407Srmesta 	 *
51348a34407Srmesta 	 * ... but notice that we are checking dr_stat, and not the
51448a34407Srmesta 	 * state of the entry itself, the entry will be NFS4_DUP_INUSE,
51548a34407Srmesta 	 * we do that so that we know not to prematurely reap it whilst
51648a34407Srmesta 	 * we resent it to the client.
51748a34407Srmesta 	 *
518d9ad96c1Srg137905 	 */
51948a34407Srmesta 	if (dr_stat == NFS4_DUP_NEW || dr_stat == NFS4_DUP_REPLAY) {
520d9ad96c1Srg137905 		mutex_enter(&drp->drc->lock);
521d9ad96c1Srg137905 		rfs4_dr_chstate(drp, NFS4_DUP_REPLAY);
522d9ad96c1Srg137905 		mutex_exit(&drp->drc->lock);
523d9ad96c1Srg137905 	} else if (dr_stat == NFS4_NOT_DUP) {
524d9ad96c1Srg137905 		rfs4_compound_free(rbp);
525d9ad96c1Srg137905 	}
526d9ad96c1Srg137905 
527d9ad96c1Srg137905 	return (error);
528d9ad96c1Srg137905 }
5297d12f3bcSmaheshvs 
5307d12f3bcSmaheshvs bool_t
5317d12f3bcSmaheshvs rfs4_minorvers_mismatch(struct svc_req *req, SVCXPRT *xprt, void *args)
5327d12f3bcSmaheshvs {
5337d12f3bcSmaheshvs 	COMPOUND4args *argsp;
5347d12f3bcSmaheshvs 	COMPOUND4res res_buf, *resp;
5357d12f3bcSmaheshvs 
5367d12f3bcSmaheshvs 	if (req->rq_vers != 4)
5377d12f3bcSmaheshvs 		return (FALSE);
5387d12f3bcSmaheshvs 
5397d12f3bcSmaheshvs 	argsp = (COMPOUND4args *)args;
5407d12f3bcSmaheshvs 
5417d12f3bcSmaheshvs 	if (argsp->minorversion <= NFS4_MAX_MINOR_VERSION)
5427d12f3bcSmaheshvs 		return (FALSE);
5437d12f3bcSmaheshvs 
5447d12f3bcSmaheshvs 	resp = &res_buf;
5457d12f3bcSmaheshvs 
5467d12f3bcSmaheshvs 	/*
5477d12f3bcSmaheshvs 	 * Form a reply tag by copying over the reqeuest tag.
5487d12f3bcSmaheshvs 	 */
5497d12f3bcSmaheshvs 	resp->tag.utf8string_val =
5507d12f3bcSmaheshvs 	    kmem_alloc(argsp->tag.utf8string_len, KM_SLEEP);
5517d12f3bcSmaheshvs 	resp->tag.utf8string_len = argsp->tag.utf8string_len;
5527d12f3bcSmaheshvs 	bcopy(argsp->tag.utf8string_val, resp->tag.utf8string_val,
5537d12f3bcSmaheshvs 	    resp->tag.utf8string_len);
5547d12f3bcSmaheshvs 	resp->array_len = 0;
5557d12f3bcSmaheshvs 	resp->array = NULL;
5567d12f3bcSmaheshvs 	resp->status = NFS4ERR_MINOR_VERS_MISMATCH;
5577d12f3bcSmaheshvs 	if (!svc_sendreply(xprt,  xdr_COMPOUND4res_srv, (char *)resp)) {
5587d12f3bcSmaheshvs 		DTRACE_PROBE2(nfss__e__minorvers_mismatch,
5597d12f3bcSmaheshvs 		    SVCXPRT *, xprt, char *, resp);
56071c6e709Srmesta 		svcerr_systemerr(xprt);
5617d12f3bcSmaheshvs 	}
5627d12f3bcSmaheshvs 	rfs4_compound_free(resp);
5637d12f3bcSmaheshvs 	return (TRUE);
5647d12f3bcSmaheshvs }
56567e4138cSDai Ngo 
56667e4138cSDai Ngo void
56767e4138cSDai Ngo rfs4_resource_err(struct svc_req *req, COMPOUND4args *argsp)
56867e4138cSDai Ngo {
56967e4138cSDai Ngo 	COMPOUND4res res_buf, *rbp;
57067e4138cSDai Ngo 	nfs_resop4 *resop;
57167e4138cSDai Ngo 	PUTFH4res *resp;
57267e4138cSDai Ngo 
57367e4138cSDai Ngo 	rbp = &res_buf;
57467e4138cSDai Ngo 
57567e4138cSDai Ngo 	/*
57667e4138cSDai Ngo 	 * Form a reply tag by copying over the request tag.
57767e4138cSDai Ngo 	 */
57867e4138cSDai Ngo 	rbp->tag.utf8string_val =
57967e4138cSDai Ngo 	    kmem_alloc(argsp->tag.utf8string_len, KM_SLEEP);
58067e4138cSDai Ngo 	rbp->tag.utf8string_len = argsp->tag.utf8string_len;
58167e4138cSDai Ngo 	bcopy(argsp->tag.utf8string_val, rbp->tag.utf8string_val,
58267e4138cSDai Ngo 	    rbp->tag.utf8string_len);
58367e4138cSDai Ngo 
58467e4138cSDai Ngo 	rbp->array_len = 1;
58567e4138cSDai Ngo 	rbp->array = kmem_zalloc(rbp->array_len * sizeof (nfs_resop4),
58667e4138cSDai Ngo 	    KM_SLEEP);
58767e4138cSDai Ngo 	resop = &rbp->array[0];
58867e4138cSDai Ngo 	resop->resop = argsp->array[0].argop;	/* copy first op over */
58967e4138cSDai Ngo 
59067e4138cSDai Ngo 	/* Any op will do, just need to access status field */
59167e4138cSDai Ngo 	resp = &resop->nfs_resop4_u.opputfh;
59267e4138cSDai Ngo 
59367e4138cSDai Ngo 	/*
59467e4138cSDai Ngo 	 * NFS4ERR_RESOURCE is allowed for all ops, except OP_ILLEGAL.
59567e4138cSDai Ngo 	 * Note that all op numbers in the compound array were already
59667e4138cSDai Ngo 	 * validated by the XDR decoder (xdr_COMPOUND4args_srv()).
59767e4138cSDai Ngo 	 */
59867e4138cSDai Ngo 	resp->status = (resop->resop == OP_ILLEGAL ?
59967e4138cSDai Ngo 	    NFS4ERR_OP_ILLEGAL : NFS4ERR_RESOURCE);
60067e4138cSDai Ngo 
60167e4138cSDai Ngo 	/* compound status is same as first op status */
60267e4138cSDai Ngo 	rbp->status = resp->status;
60367e4138cSDai Ngo 
60467e4138cSDai Ngo 	if (!svc_sendreply(req->rq_xprt, xdr_COMPOUND4res_srv, (char *)rbp)) {
60567e4138cSDai Ngo 		DTRACE_PROBE2(nfss__rsrc_err__sendfail,
60667e4138cSDai Ngo 		    struct svc_req *, req->rq_xprt, char *, rbp);
60767e4138cSDai Ngo 		svcerr_systemerr(req->rq_xprt);
60867e4138cSDai Ngo 	}
60967e4138cSDai Ngo 
61067e4138cSDai Ngo 	UTF8STRING_FREE(rbp->tag);
61167e4138cSDai Ngo 	kmem_free(rbp->array, rbp->array_len * sizeof (nfs_resop4));
61267e4138cSDai Ngo }
613