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 nfsstat4
sess_chan_limits(sess_channel_t * scp)216 sess_chan_limits(sess_channel_t *scp)
217 {
218 if (scp->cn_attrs.ca_maxrequests > rfs4_max_slots) {
219 scp->cn_attrs.ca_maxrequests = rfs4_max_slots;
220 }
221
222 if (scp->cn_back_attrs.ca_maxrequests > rfs4_back_max_slots)
223 scp->cn_back_attrs.ca_maxrequests = rfs4_back_max_slots;
224
225
226 if (scp->cn_attrs.ca_maxoperations > NFS4_COMPOUND_LIMIT)
227 scp->cn_attrs.ca_maxoperations = NFS4_COMPOUND_LIMIT;
228
229 /*
230 * Lower limit should be set to smallest sane COMPOUND. Even
231 * though a singleton SEQUENCE op is the very smallest COMPOUND,
232 * it's also quite boring. For all practical purposes, the lower
233 * limit for creating a sess is limited to:
234 *
235 * [SEQUENCE + PUTROOTFH + GETFH]
236 *
237 * Can't limit READ's to a specific threshold, otherwise
238 * we artificially limit the clients to perform reads of
239 * AT LEAST that granularity, which is WRONG !!! Same goes
240 * for READDIR's and GETATTR's.
241 */
242 if (scp->cn_attrs.ca_maxresponsesize < (sizeof (SEQUENCE4res) +
243 sizeof (PUTROOTFH4res) + sizeof (GETFH4res)))
244 return (NFS4ERR_TOOSMALL);
245 return (NFS4_OK);
246 }
247
248 /*
249 * NFSv4.1 Slot replay cache
250 */
251 static void
rfs41_cleanup_slot(rfs4_slot_t * se)252 rfs41_cleanup_slot(rfs4_slot_t *se)
253 {
254 rfs4_compound_free((COMPOUND4res *)&se->se_buf);
255 }
256
257 static rfs4_slot_t *
slots_alloc(size_t n)258 slots_alloc(size_t n)
259 {
260 rfs4_slot_t *p;
261 int i;
262
263 p = kmem_zalloc(sizeof (rfs4_slot_t) * n, KM_SLEEP);
264 for (i = 0; i < n; i++) {
265 mutex_init(&p[i].se_lock, NULL, MUTEX_DEFAULT, NULL);
266 }
267
268 return (p);
269 }
270
271 static void
slots_free(rfs4_slot_t * slots,size_t n)272 slots_free(rfs4_slot_t *slots, size_t n)
273 {
274 int i;
275
276 for (i = 0; i < n; i++) {
277 rfs4_slot_t *slot = &slots[i];
278
279 mutex_destroy(&slot->se_lock);
280
281 if (slot->se_flags & RFS4_SLOT_CACHED) {
282 rfs41_cleanup_slot(slot);
283 }
284 }
285 kmem_free(slots, sizeof (rfs4_slot_t) * n);
286 }
287
288 /* Additional functions */
289
290 /* check csa_flags for OP_CREATE_SESSION */
291 bool_t
nfs4x_csa_flags_valid(uint32_t flags)292 nfs4x_csa_flags_valid(uint32_t flags)
293 {
294 if (flags & ~CREATE_SESSION4_FLAG_MASK)
295 return (FALSE);
296
297 return (TRUE);
298 }
299
300 sess_channel_t *
rfs41_create_session_channel(channel_dir_from_server4 dir)301 rfs41_create_session_channel(channel_dir_from_server4 dir)
302 {
303 sess_channel_t *cp;
304 sess_bcsd_t *bp;
305
306 cp = (sess_channel_t *)kmem_zalloc(sizeof (sess_channel_t), KM_SLEEP);
307 rw_init(&cp->cn_lock, NULL, RW_DEFAULT, NULL);
308
309 switch (dir) {
310 case CDFS4_FORE:
311 break;
312
313 case CDFS4_BOTH:
314 case CDFS4_BACK:
315 /* BackChan Specific Data */
316 bp = (sess_bcsd_t *)kmem_zalloc(sizeof (sess_bcsd_t), KM_SLEEP);
317 rw_init(&bp->bsd_rwlock, NULL, RW_DEFAULT, NULL);
318 cp->cn_csd = (sess_bcsd_t *)bp;
319 break;
320 }
321 return (cp);
322 }
323
324 void
rfs41_destroy_session_channel(rfs4_session_t * sp)325 rfs41_destroy_session_channel(rfs4_session_t *sp)
326 {
327 sess_channel_t *cp;
328 sess_bcsd_t *bp;
329
330 if (sp->sn_back != NULL) {
331 /* only one channel for both direction for now */
332 ASSERT(sp->sn_fore == sp->sn_back);
333
334 cp = sp->sn_back;
335 bp = (sess_bcsd_t *)cp->cn_csd;
336 rw_destroy(&bp->bsd_rwlock);
337 kmem_free(bp, sizeof (sess_bcsd_t));
338 } else {
339 cp = sp->sn_fore;
340 }
341
342 rw_destroy(&cp->cn_lock);
343 kmem_free(cp, sizeof (sess_channel_t));
344
345 sp->sn_back = NULL;
346 sp->sn_fore = NULL;
347 }
348
349 static bool_t
rfs4_session_create(rfs4_entry_t u_entry,void * arg)350 rfs4_session_create(rfs4_entry_t u_entry, void *arg)
351 {
352 rfs4_session_t *sp = (rfs4_session_t *)u_entry;
353 session41_create_t *ap = (session41_create_t *)arg;
354 sess_channel_t *ocp = NULL;
355 rfs4_sid *sidp;
356 bool_t bdrpc = FALSE;
357 channel_dir_from_server4 dir;
358 nfsstat4 sle;
359 nfs4_srv_t *nsrv4 = nfs4_get_srv();
360
361 ASSERT(sp != NULL);
362 if (sp == NULL)
363 return (FALSE);
364
365 /*
366 * Back pointer/ref to parent data struct (rfs4_client_t)
367 */
368 sp->sn_clnt = (rfs4_client_t *)ap->cs_client;
369 rfs4_dbe_hold(sp->sn_clnt->rc_dbe);
370
371 /*
372 * Handcrafting the session id
373 */
374 sidp = (rfs4_sid *)&sp->sn_sessid;
375 sidp->impl_id.pad0 = 0x00000000;
376 sidp->impl_id.pad1 = 0xFFFFFFFF;
377 sidp->impl_id.start_time = nsrv4->rfs4_start_time;
378 sidp->impl_id.s_id = ap->cs_id;
379
380 /*
381 * Process csa_flags; note that CREATE_SESSION4_FLAG_CONN_BACK_CHAN
382 * is processed below since it affects direction and setup of the
383 * backchannel accordingly.
384 */
385 if (!nfs4x_csa_flags_valid(ap->cs_aotw.csa_flags)) {
386 ap->cs_error = NFS4ERR_INVAL;
387 goto err;
388 }
389
390 sp->sn_csflags = ap->cs_aotw.csa_flags;
391 if (ap->cs_aotw.csa_flags & CREATE_SESSION4_FLAG_PERSIST)
392 /* Do not support persistent reply cache (yet). */
393 sp->sn_csflags &= ~CREATE_SESSION4_FLAG_PERSIST;
394
395 if (ap->cs_aotw.csa_flags & CREATE_SESSION4_FLAG_CONN_RDMA)
396 /* No RDMA for now */
397 sp->sn_csflags &= ~CREATE_SESSION4_FLAG_CONN_RDMA;
398
399 /*
400 * Initialize some overall sessions values
401 */
402 sp->sn_bc.progno = ap->cs_aotw.csa_cb_program;
403 sp->sn_laccess = nfs_sys_uptime();
404 sp->sn_flags = 0;
405 sp->sn_rcached = 0;
406
407 /*
408 * Check if client has specified that the FORE channel should
409 * also be used for call back traffic (ie. bidir RPC). If so,
410 * let's try to accomodate the request.
411 */
412 DTRACE_PROBE1(csa__flags, uint32_t, ap->cs_aotw.csa_flags);
413
414 /*
415 * Session's channel flags depending on bdrpc
416 * TODO: Add backchannel handling, i.e. when bdrpc is TRUE
417 */
418 dir = bdrpc ? (CDFS4_FORE | CDFS4_BACK) : CDFS4_FORE;
419 ocp = rfs41_create_session_channel(dir);
420 ocp->cn_dir = dir;
421 sp->sn_fore = ocp;
422
423 /*
424 * Check if channel attrs will be flexible enough for future
425 * purposes. Channel attribute enforcement is done as part of
426 * COMPOUND processing.
427 */
428 ocp->cn_attrs = ap->cs_aotw.csa_fore_chan_attrs;
429 ocp->cn_back_attrs = ap->cs_aotw.csa_back_chan_attrs;
430 sle = sess_chan_limits(ocp);
431 if (sle != NFS4_OK) {
432 ap->cs_error = sle;
433 goto err_free_chan;
434 }
435
436 /* will fail if client is going to destroy */
437 if (!client_insert_session(sp->sn_clnt, sp)) {
438 ap->cs_error = NFS4ERR_DELAY;
439 goto err_free_chan;
440 }
441
442 /*
443 * No need for locks/synchronization at this time,
444 * since we're barely creating the session.
445 */
446 if (bdrpc) {
447 /* Need to be implemented */
448 VERIFY(0);
449 } else {
450 sp->sn_csflags &= ~CREATE_SESSION4_FLAG_CONN_BACK_CHAN;
451 sp->sn_back = NULL;
452 }
453
454 /*
455 * Now we allocate space for the slrc, initializing each slot's
456 * sequenceid and slotid to zero and a (pre)cached result of
457 * NFS4ERR_SEQ_MISORDERED. Note that we zero out the entries
458 * by virtue of the z-alloc.
459 */
460 sp->sn_slots = slots_alloc(ocp->cn_attrs.ca_maxrequests);
461
462 return (TRUE);
463
464 err_free_chan:
465 rfs41_destroy_session_channel(sp);
466 err:
467 rfs4_dbe_rele(sp->sn_clnt->rc_dbe);
468 return (FALSE);
469 }
470
471 static void
rfs4_session_destroy(rfs4_entry_t u_entry)472 rfs4_session_destroy(rfs4_entry_t u_entry)
473 {
474 rfs4_session_t *sp = (rfs4_session_t *)u_entry;
475 sess_bcsd_t *bsdp;
476
477 if (SN_CB_CHAN_EST(sp) && (bsdp = sp->sn_back->cn_csd) != NULL) {
478 slots_free(bsdp->bsd_slots,
479 sp->sn_back->cn_back_attrs.ca_maxrequests);
480 bsdp->bsd_slots = NULL;
481 }
482
483 /*
484 * Nuke slot replay cache for this session
485 */
486 if (sp->sn_slots) {
487 slots_free(sp->sn_slots, sp->sn_fore->cn_attrs.ca_maxrequests);
488 sp->sn_slots = NULL;
489 }
490
491 /*
492 * Remove the fore and back channels.
493 */
494 rfs41_destroy_session_channel(sp);
495
496 client_remove_session(sp->sn_clnt, sp);
497
498 rfs4_client_rele(sp->sn_clnt);
499 }
500
501 static bool_t
rfs4_session_expiry(rfs4_entry_t u_entry)502 rfs4_session_expiry(rfs4_entry_t u_entry)
503 {
504 rfs4_session_t *sp = (rfs4_session_t *)u_entry;
505
506 if (sp == NULL || rfs4_dbe_is_invalid(sp->sn_dbe))
507 return (TRUE);
508
509 if (rfs4_lease_expired(sp->sn_clnt))
510 return (TRUE);
511
512 return (FALSE);
513 }
514
515 void
rfs4x_state_init_locked(nfs4_srv_t * nsrv4)516 rfs4x_state_init_locked(nfs4_srv_t *nsrv4)
517 {
518 nsrv4->rfs4_session_tab = rfs4_table_create(nsrv4->nfs4_server_state,
519 "Session", 5 * rfs4_lease_time, 1, rfs4_session_create,
520 rfs4_session_destroy, rfs4_session_expiry, sizeof (rfs4_session_t),
521 RFS4_TABSIZE, RFS4_MAXTABSZ/8, -1);
522
523 nsrv4->rfs4_session_idx = rfs4_index_create(nsrv4->rfs4_session_tab,
524 "session_idx", sessid_hash, sessid_compare, sessid_mkkey, TRUE);
525 }
526
527 void
rfs4x_state_fini(nfs4_srv_t * nsrv4)528 rfs4x_state_fini(nfs4_srv_t *nsrv4)
529 {
530 /* All tables will be destroyed by caller */
531 }
532