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 *
rfs4_init_drc(uint32_t drc_size,uint32_t drc_hash_size)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
rfs4_fini_drc(rfs4_drc_t * drc)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
rfs4_dr_chstate(rfs4_dupreq_t * drp,int new_state)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 *
rfs4_alloc_dr(rfs4_drc_t * drc)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
rfs4_find_dr(struct svc_req * req,rfs4_drc_t * drc,rfs4_dupreq_t ** dup)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
rfs4_dispatch(struct rpcdisp * disp,struct svc_req * req,SVCXPRT * xprt,char * ap)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
rfs4_minorvers_mismatch(struct svc_req * req,SVCXPRT * xprt,void * args)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
rfs4_resource_err(struct svc_req * req,COMPOUND4args * argsp)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