1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
26 * Copyright 2017 RackTop Systems.
27 */
28
29 #include <sys/sdt.h>
30 #include <sys/atomic.h>
31 #include <nfs/nfs4.h>
32
33 #ifdef DEBUG
34 #define RFS4_TABSIZE 17
35 #else
36 #define RFS4_TABSIZE 2047
37 #endif
38
39 #define RFS4_MAXTABSZ 1024*1024
40
41 slotid4 rfs4_max_slots = MAXSLOTS; /* fore channel */
42 slotid4 rfs4_back_max_slots = MAXSLOTS_BACK; /* back channel */
43
44 typedef union {
45 /* Both members have the same size */
46 struct {
47 uint32_t pad0;
48 uint32_t pad1;
49 uint32_t start_time; /* NFS server start time */
50 uint32_t s_id; /* unique session index */
51 } impl_id;
52 sessionid4 id4;
53 } rfs4_sid;
54
55 /*
56 * --------------------------------------------------------
57 * MDS - NFSv4.1 Sessions
58 * --------------------------------------------------------
59 */
60 static uint32_t
sessid_hash(void * key)61 sessid_hash(void *key)
62 {
63 rfs4_sid *idp = key;
64
65 return (idp->impl_id.s_id);
66 }
67
68 static bool_t
sessid_compare(rfs4_entry_t entry,void * key)69 sessid_compare(rfs4_entry_t entry, void *key)
70 {
71 rfs4_session_t *sp = (rfs4_session_t *)entry;
72 sessionid4 *idp = (sessionid4 *)key;
73
74 return (bcmp(idp, &sp->sn_sessid, sizeof (sessionid4)) == 0);
75 }
76
77 static void *
sessid_mkkey(rfs4_entry_t entry)78 sessid_mkkey(rfs4_entry_t entry)
79 {
80 rfs4_session_t *sp = (rfs4_session_t *)entry;
81
82 return (&sp->sn_sessid);
83 }
84
85 void
rfs4x_session_rele(rfs4_session_t * sp)86 rfs4x_session_rele(rfs4_session_t *sp)
87 {
88 rfs4_dbe_rele(sp->sn_dbe);
89 }
90
91 void
rfs4x_session_hold(rfs4_session_t * sp)92 rfs4x_session_hold(rfs4_session_t *sp)
93 {
94 rfs4_dbe_hold(sp->sn_dbe);
95 }
96
97 rfs4_session_t *
rfs4x_findsession_by_id(sessionid4 sessid)98 rfs4x_findsession_by_id(sessionid4 sessid)
99 {
100 rfs4_session_t *sp;
101 bool_t create = FALSE;
102 nfs4_srv_t *nsrv4 = nfs4_get_srv();
103
104 sp = (rfs4_session_t *)rfs4_dbsearch(nsrv4->rfs4_session_idx,
105 sessid, &create, NULL, RFS4_DBS_VALID);
106
107 return (sp);
108 }
109
110 /*
111 * A clientid can have multiple sessions associated with it. Hence,
112 * performing a raw 'mds_findsession' (even for a create) might
113 * yield a list of sessions associated with the clientid in question.
114 * Call rfs4_dbseach() function with key that cannot be found
115 * and create an association between the session table and both
116 * primary (sessionid) index and secondary (clientid) index for the
117 * newly created session.
118 */
119
120 rfs4_session_t *
rfs4x_createsession(session41_create_t * ap)121 rfs4x_createsession(session41_create_t *ap)
122 {
123 static uint32_t session_id_counter;
124
125 rfs4_session_t *sp = NULL;
126 bool_t create = TRUE;
127 rfs4_sid key = {0, 0, 0, 0};
128 nfs4_srv_t *nsrv4 = nfs4_get_srv();
129
130 /*
131 * Use unique counter for s_id and s_id to ensure that
132 * created entry will have the same index in dbi_buckets[]
133 */
134 ap->cs_id = key.impl_id.s_id = atomic_inc_32_nv(&session_id_counter);
135
136 if ((sp = (rfs4_session_t *)rfs4_dbsearch(nsrv4->rfs4_session_idx,
137 &key, &create, (void *)ap, RFS4_DBS_VALID)) == NULL) {
138 DTRACE_PROBE1(mds__srv__createsession__fail,
139 session41_create_t *, ap);
140 }
141 return (sp);
142 }
143
144 /* return success of operation */
145 static bool_t
client_insert_session(rfs4_client_t * cp,rfs4_session_t * sp)146 client_insert_session(rfs4_client_t *cp, rfs4_session_t *sp)
147 {
148 bool_t res = TRUE;
149
150 rfs4_dbe_lock(cp->rc_dbe);
151 if (cp->rc_destroying)
152 res = FALSE;
153 else
154 list_insert_tail(&cp->rc_sessions, sp);
155 rfs4_dbe_unlock(cp->rc_dbe);
156
157 return (res);
158 }
159
160 static void
client_remove_session(rfs4_client_t * cp,rfs4_session_t * sp)161 client_remove_session(rfs4_client_t *cp, rfs4_session_t *sp)
162 {
163 rfs4_dbe_lock(cp->rc_dbe);
164 if (list_link_active(&sp->sn_node))
165 list_remove(&cp->rc_sessions, sp);
166 rfs4_dbe_unlock(cp->rc_dbe);
167 }
168
169 /*
170 * Invalidate the session in the DB (so it can't be found anymore)
171 */
172 nfsstat4
rfs4x_destroysession(rfs4_session_t * sp,unsigned useref)173 rfs4x_destroysession(rfs4_session_t *sp, unsigned useref)
174 {
175 nfsstat4 status = NFS4_OK;
176
177 /*
178 * RFC 7862 Section 14.1.3:
179 * In hindsight, the NFSv4.1 specification should have
180 * mandated that DESTROY_SESSION either abort or complete
181 * all outstanding operations.
182 */
183 rfs4_dbe_lock(sp->sn_dbe);
184 if (rfs4_dbe_refcnt(sp->sn_dbe) > useref)
185 status = NFS4ERR_DELAY;
186 else
187 rfs4_dbe_invalidate(sp->sn_dbe);
188 rfs4_dbe_unlock(sp->sn_dbe);
189
190 if (status == NFS4_OK)
191 client_remove_session(sp->sn_clnt, sp);
192
193 return (status);
194 }
195
196 /* Invalidate all client's sessions */
197 void
rfs4x_client_session_remove(rfs4_client_t * cp)198 rfs4x_client_session_remove(rfs4_client_t *cp)
199 {
200 rfs4_session_t *sp;
201
202 /*
203 * Client is forcibly closing so invalidate all sessions
204 * without checking the refcount.
205 */
206 rfs4_dbe_lock(cp->rc_dbe);
207 while ((sp = list_head(&cp->rc_sessions)) != NULL) {
208 list_remove(&cp->rc_sessions, sp);
209
210 rfs4_dbe_invalidate(sp->sn_dbe);
211 }
212 rfs4_dbe_unlock(cp->rc_dbe);
213 }
214
215 /*
216 * RFC5661 Section 18.36.3
217 * Definitions for ca_maxrequestsize:
218 * The maximum size of a COMPOUND or CB_COMPOUND request that will be sent.
219 */
220 #define NFS4_MIN_COMPOUND_REQSZ (( \
221 2 + 2 + /* credential, verifier: AUTH_NULL, Length 0 */ \
222 1 + /* zero length tag */ \
223 3 + /* minorversion, opcount, opcode */ \
224 (RNDUP(NFS4_SESSIONID_SIZE) / BYTES_PER_XDR_UNIT) + \
225 4) * /* seqid, slotid, high-slotid, cachethis */ \
226 BYTES_PER_XDR_UNIT)
227 /*
228 * ca_maxresponsesize:
229 * The maximum size of a COMPOUND or CB_COMPOUND reply that the
230 * requester will accept from the replier including RPC headers
231 * (see the ca_maxrequestsize definition).
232 */
233 #define NFS4_MIN_COMPOUND_RESPSZ (( \
234 2 + /* verifier: AUTH_NULL, Length 0 */ \
235 1 + /* status */ \
236 1 + /* zero length tag */ \
237 3 + /* opcount, opcode, status */ \
238 (RNDUP(NFS4_SESSIONID_SIZE) / BYTES_PER_XDR_UNIT) + \
239 4) * /* seqid, slotid, high-slotid, target-slotid, status */ \
240 BYTES_PER_XDR_UNIT)
241
242 #define NFS4_MIN_CB_COMPOUND_REQSZ NFS4_MIN_COMPOUND_REQSZ
243 #define NFS4_MIN_CB_COMPOUND_RESPSZ NFS4_MIN_COMPOUND_RESPSZ
244
245 #define NFS4_SLOT_CACHED_SIZE 2048
246
247 nfsstat4
sess_chan_limits(sess_channel_t * scp)248 sess_chan_limits(sess_channel_t *scp)
249 {
250 if (scp->cn_attrs.ca_maxrequests > rfs4_max_slots) {
251 scp->cn_attrs.ca_maxrequests = rfs4_max_slots;
252 }
253
254 if (scp->cn_back_attrs.ca_maxrequests > rfs4_back_max_slots)
255 scp->cn_back_attrs.ca_maxrequests = rfs4_back_max_slots;
256
257 if (scp->cn_attrs.ca_maxoperations > NFS4_COMPOUND_LIMIT)
258 scp->cn_attrs.ca_maxoperations = NFS4_COMPOUND_LIMIT;
259
260 /* maxreqsize, maxrespsize */
261 if (scp->cn_attrs.ca_maxrequestsize < NFS4_MIN_COMPOUND_REQSZ)
262 return (NFS4ERR_TOOSMALL);
263
264 if (scp->cn_attrs.ca_maxresponsesize < NFS4_MIN_COMPOUND_RESPSZ)
265 return (NFS4ERR_TOOSMALL);
266
267 if (scp->cn_back_attrs.ca_maxrequestsize < NFS4_MIN_CB_COMPOUND_REQSZ)
268 return (NFS4ERR_TOOSMALL);
269
270 if (scp->cn_back_attrs.ca_maxresponsesize < NFS4_MIN_CB_COMPOUND_RESPSZ)
271 return (NFS4ERR_TOOSMALL);
272
273 /* maxresp_cached */
274 scp->cn_attrs.ca_maxresponsesize_cached =
275 MIN(scp->cn_attrs.ca_maxresponsesize_cached,
276 NFS4_SLOT_CACHED_SIZE + NFS4_MIN_HDR_SEQSZ);
277
278 scp->cn_back_attrs.ca_maxresponsesize_cached = 0;
279
280 return (NFS4_OK);
281 }
282
283 /*
284 * NFSv4.1 Slot replay cache
285 */
286 static void
rfs41_cleanup_slot(rfs4_slot_t * se)287 rfs41_cleanup_slot(rfs4_slot_t *se)
288 {
289 rfs4_compound_free((COMPOUND4res *)&se->se_buf);
290 }
291
292 static rfs4_slot_t *
slots_alloc(size_t n)293 slots_alloc(size_t n)
294 {
295 rfs4_slot_t *p;
296 int i;
297
298 p = kmem_zalloc(sizeof (rfs4_slot_t) * n, KM_SLEEP);
299 for (i = 0; i < n; i++) {
300 mutex_init(&p[i].se_lock, NULL, MUTEX_DEFAULT, NULL);
301 }
302
303 return (p);
304 }
305
306 static void
slots_free(rfs4_slot_t * slots,size_t n)307 slots_free(rfs4_slot_t *slots, size_t n)
308 {
309 int i;
310
311 for (i = 0; i < n; i++) {
312 rfs4_slot_t *slot = &slots[i];
313
314 mutex_destroy(&slot->se_lock);
315
316 if (slot->se_flags & RFS4_SLOT_CACHED) {
317 rfs41_cleanup_slot(slot);
318 }
319 }
320 kmem_free(slots, sizeof (rfs4_slot_t) * n);
321 }
322
323 /* Additional functions */
324
325 /* check csa_flags for OP_CREATE_SESSION */
326 bool_t
nfs4x_csa_flags_valid(uint32_t flags)327 nfs4x_csa_flags_valid(uint32_t flags)
328 {
329 if (flags & ~CREATE_SESSION4_FLAG_MASK)
330 return (FALSE);
331
332 return (TRUE);
333 }
334
335 sess_channel_t *
rfs41_create_session_channel(channel_dir_from_server4 dir)336 rfs41_create_session_channel(channel_dir_from_server4 dir)
337 {
338 sess_channel_t *cp;
339 sess_bcsd_t *bp;
340
341 cp = (sess_channel_t *)kmem_zalloc(sizeof (sess_channel_t), KM_SLEEP);
342 rw_init(&cp->cn_lock, NULL, RW_DEFAULT, NULL);
343
344 switch (dir) {
345 case CDFS4_FORE:
346 break;
347
348 case CDFS4_BOTH:
349 case CDFS4_BACK:
350 /* BackChan Specific Data */
351 bp = (sess_bcsd_t *)kmem_zalloc(sizeof (sess_bcsd_t), KM_SLEEP);
352 rw_init(&bp->bsd_rwlock, NULL, RW_DEFAULT, NULL);
353 cp->cn_csd = (sess_bcsd_t *)bp;
354 break;
355 }
356 return (cp);
357 }
358
359 void
rfs41_destroy_session_channel(rfs4_session_t * sp)360 rfs41_destroy_session_channel(rfs4_session_t *sp)
361 {
362 sess_channel_t *cp;
363 sess_bcsd_t *bp;
364
365 if (sp->sn_back != NULL) {
366 /* only one channel for both direction for now */
367 ASSERT(sp->sn_fore == sp->sn_back);
368
369 cp = sp->sn_back;
370 bp = (sess_bcsd_t *)cp->cn_csd;
371 rw_destroy(&bp->bsd_rwlock);
372 kmem_free(bp, sizeof (sess_bcsd_t));
373 } else {
374 cp = sp->sn_fore;
375 }
376
377 rw_destroy(&cp->cn_lock);
378 kmem_free(cp, sizeof (sess_channel_t));
379
380 sp->sn_back = NULL;
381 sp->sn_fore = NULL;
382 }
383
384 static bool_t
rfs4_session_create(rfs4_entry_t u_entry,void * arg)385 rfs4_session_create(rfs4_entry_t u_entry, void *arg)
386 {
387 rfs4_session_t *sp = (rfs4_session_t *)u_entry;
388 session41_create_t *ap = (session41_create_t *)arg;
389 sess_channel_t *ocp = NULL;
390 rfs4_sid *sidp;
391 bool_t bdrpc = FALSE;
392 channel_dir_from_server4 dir;
393 nfsstat4 sle;
394 nfs4_srv_t *nsrv4 = nfs4_get_srv();
395
396 ASSERT(sp != NULL);
397 if (sp == NULL)
398 return (FALSE);
399
400 /*
401 * Back pointer/ref to parent data struct (rfs4_client_t)
402 */
403 sp->sn_clnt = (rfs4_client_t *)ap->cs_client;
404 rfs4_dbe_hold(sp->sn_clnt->rc_dbe);
405
406 /*
407 * Handcrafting the session id
408 */
409 sidp = (rfs4_sid *)&sp->sn_sessid;
410 sidp->impl_id.pad0 = 0x00000000;
411 sidp->impl_id.pad1 = 0xFFFFFFFF;
412 sidp->impl_id.start_time = nsrv4->rfs4_start_time;
413 sidp->impl_id.s_id = ap->cs_id;
414
415 /*
416 * Process csa_flags; note that CREATE_SESSION4_FLAG_CONN_BACK_CHAN
417 * is processed below since it affects direction and setup of the
418 * backchannel accordingly.
419 */
420 if (!nfs4x_csa_flags_valid(ap->cs_aotw.csa_flags)) {
421 ap->cs_error = NFS4ERR_INVAL;
422 goto err;
423 }
424
425 sp->sn_csflags = ap->cs_aotw.csa_flags;
426 if (ap->cs_aotw.csa_flags & CREATE_SESSION4_FLAG_PERSIST)
427 /* Do not support persistent reply cache (yet). */
428 sp->sn_csflags &= ~CREATE_SESSION4_FLAG_PERSIST;
429
430 if (ap->cs_aotw.csa_flags & CREATE_SESSION4_FLAG_CONN_RDMA)
431 /* No RDMA for now */
432 sp->sn_csflags &= ~CREATE_SESSION4_FLAG_CONN_RDMA;
433
434 /*
435 * Initialize some overall sessions values
436 */
437 sp->sn_bc.progno = ap->cs_aotw.csa_cb_program;
438 sp->sn_laccess = nfs_sys_uptime();
439 sp->sn_flags = 0;
440 sp->sn_rcached = 0;
441
442 /*
443 * Check if client has specified that the FORE channel should
444 * also be used for call back traffic (ie. bidir RPC). If so,
445 * let's try to accomodate the request.
446 */
447 DTRACE_PROBE1(csa__flags, uint32_t, ap->cs_aotw.csa_flags);
448
449 /*
450 * Session's channel flags depending on bdrpc
451 * TODO: Add backchannel handling, i.e. when bdrpc is TRUE
452 */
453 dir = bdrpc ? (CDFS4_FORE | CDFS4_BACK) : CDFS4_FORE;
454 ocp = rfs41_create_session_channel(dir);
455 ocp->cn_dir = dir;
456 sp->sn_fore = ocp;
457
458 /*
459 * Check if channel attrs will be flexible enough for future
460 * purposes. Channel attribute enforcement is done as part of
461 * COMPOUND processing.
462 */
463 ocp->cn_attrs = ap->cs_aotw.csa_fore_chan_attrs;
464 ocp->cn_back_attrs = ap->cs_aotw.csa_back_chan_attrs;
465 sle = sess_chan_limits(ocp);
466 if (sle != NFS4_OK) {
467 ap->cs_error = sle;
468 goto err_free_chan;
469 }
470
471 /* will fail if client is going to destroy */
472 if (!client_insert_session(sp->sn_clnt, sp)) {
473 ap->cs_error = NFS4ERR_DELAY;
474 goto err_free_chan;
475 }
476
477 /*
478 * No need for locks/synchronization at this time,
479 * since we're barely creating the session.
480 */
481 if (bdrpc) {
482 /* Need to be implemented */
483 VERIFY(0);
484 } else {
485 sp->sn_csflags &= ~CREATE_SESSION4_FLAG_CONN_BACK_CHAN;
486 sp->sn_back = NULL;
487 }
488
489 /*
490 * Now we allocate space for the slrc, initializing each slot's
491 * sequenceid and slotid to zero and a (pre)cached result of
492 * NFS4ERR_SEQ_MISORDERED. Note that we zero out the entries
493 * by virtue of the z-alloc.
494 */
495 sp->sn_slots = slots_alloc(ocp->cn_attrs.ca_maxrequests);
496
497 return (TRUE);
498
499 err_free_chan:
500 rfs41_destroy_session_channel(sp);
501 err:
502 rfs4_dbe_rele(sp->sn_clnt->rc_dbe);
503 return (FALSE);
504 }
505
506 static void
rfs4_session_destroy(rfs4_entry_t u_entry)507 rfs4_session_destroy(rfs4_entry_t u_entry)
508 {
509 rfs4_session_t *sp = (rfs4_session_t *)u_entry;
510 sess_bcsd_t *bsdp;
511
512 if (SN_CB_CHAN_EST(sp) && (bsdp = sp->sn_back->cn_csd) != NULL) {
513 slots_free(bsdp->bsd_slots,
514 sp->sn_back->cn_back_attrs.ca_maxrequests);
515 bsdp->bsd_slots = NULL;
516 }
517
518 /*
519 * Nuke slot replay cache for this session
520 */
521 if (sp->sn_slots) {
522 slots_free(sp->sn_slots, sp->sn_fore->cn_attrs.ca_maxrequests);
523 sp->sn_slots = NULL;
524 }
525
526 /*
527 * Remove the fore and back channels.
528 */
529 rfs41_destroy_session_channel(sp);
530
531 client_remove_session(sp->sn_clnt, sp);
532
533 rfs4_client_rele(sp->sn_clnt);
534 }
535
536 static bool_t
rfs4_session_expiry(rfs4_entry_t u_entry)537 rfs4_session_expiry(rfs4_entry_t u_entry)
538 {
539 rfs4_session_t *sp = (rfs4_session_t *)u_entry;
540
541 if (sp == NULL || rfs4_dbe_is_invalid(sp->sn_dbe))
542 return (TRUE);
543
544 if (rfs4_lease_expired(sp->sn_clnt))
545 return (TRUE);
546
547 return (FALSE);
548 }
549
550 void
rfs4x_state_init_locked(nfs4_srv_t * nsrv4)551 rfs4x_state_init_locked(nfs4_srv_t *nsrv4)
552 {
553 nsrv4->rfs4_session_tab = rfs4_table_create(nsrv4->nfs4_server_state,
554 "Session", 5 * rfs4_lease_time, 1, rfs4_session_create,
555 rfs4_session_destroy, rfs4_session_expiry, sizeof (rfs4_session_t),
556 RFS4_TABSIZE, RFS4_MAXTABSZ/8, -1);
557
558 nsrv4->rfs4_session_idx = rfs4_index_create(nsrv4->rfs4_session_tab,
559 "session_idx", sessid_hash, sessid_compare, sessid_mkkey, TRUE);
560 }
561
562 void
rfs4x_state_fini(nfs4_srv_t * nsrv4)563 rfs4x_state_fini(nfs4_srv_t *nsrv4)
564 {
565 /* All tables will be destroyed by caller */
566 }
567