xref: /dpdk/drivers/net/bnxt/tf_core/tf_session.c (revision aa2be5093363b57a6ad102bad9732b08c8c1f5ab)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2019-2020 Broadcom
3  * All rights reserved.
4  */
5 
6 #include <string.h>
7 
8 #include <rte_common.h>
9 
10 #include "tf_session.h"
11 #include "tf_common.h"
12 #include "tf_msg.h"
13 #include "tfp.h"
14 
15 struct tf_session_client_create_parms {
16 	/**
17 	 * [in] Pointer to the control channel name string
18 	 */
19 	char *ctrl_chan_name;
20 
21 	/**
22 	 * [out] Firmware Session Client ID
23 	 */
24 	union tf_session_client_id *session_client_id;
25 };
26 
27 struct tf_session_client_destroy_parms {
28 	/**
29 	 * FW Session Client Identifier
30 	 */
31 	union tf_session_client_id session_client_id;
32 };
33 
34 /**
35  * Creates a Session and the associated client.
36  *
37  * [in] tfp
38  *   Pointer to TF handle
39  *
40  * [in] parms
41  *   Pointer to session client create parameters
42  *
43  * Returns
44  *   - (0) if successful.
45  *   - (-EINVAL) on failure.
46  *   - (-ENOMEM) if max session clients has been reached.
47  */
48 static int
49 tf_session_create(struct tf *tfp,
50 		  struct tf_session_open_session_parms *parms)
51 {
52 	int rc;
53 	struct tf_session *session = NULL;
54 	struct tf_session_client *client;
55 	struct tfp_calloc_parms cparms;
56 	uint8_t fw_session_id;
57 	uint8_t fw_session_client_id;
58 	union tf_session_id *session_id;
59 
60 	TF_CHECK_PARMS2(tfp, parms);
61 
62 	/* Open FW session and get a new session_id */
63 	rc = tf_msg_session_open(tfp,
64 				 parms->open_cfg->ctrl_chan_name,
65 				 &fw_session_id,
66 				 &fw_session_client_id);
67 	if (rc) {
68 		/* Log error */
69 		if (rc == -EEXIST)
70 			TFP_DRV_LOG(ERR,
71 				    "Session is already open, rc:%s\n",
72 				    strerror(-rc));
73 		else
74 			TFP_DRV_LOG(ERR,
75 				    "Open message send failed, rc:%s\n",
76 				    strerror(-rc));
77 
78 		parms->open_cfg->session_id.id = TF_FW_SESSION_ID_INVALID;
79 		return rc;
80 	}
81 
82 	/* Allocate session */
83 	cparms.nitems = 1;
84 	cparms.size = sizeof(struct tf_session_info);
85 	cparms.alignment = 0;
86 	rc = tfp_calloc(&cparms);
87 	if (rc) {
88 		/* Log error */
89 		TFP_DRV_LOG(ERR,
90 			    "Failed to allocate session info, rc:%s\n",
91 			    strerror(-rc));
92 		goto cleanup;
93 	}
94 	tfp->session = (struct tf_session_info *)cparms.mem_va;
95 
96 	/* Allocate core data for the session */
97 	cparms.nitems = 1;
98 	cparms.size = sizeof(struct tf_session);
99 	cparms.alignment = 0;
100 	rc = tfp_calloc(&cparms);
101 	if (rc) {
102 		/* Log error */
103 		TFP_DRV_LOG(ERR,
104 			    "Failed to allocate session data, rc:%s\n",
105 			    strerror(-rc));
106 		goto cleanup;
107 	}
108 	tfp->session->core_data = cparms.mem_va;
109 	session_id = &parms->open_cfg->session_id;
110 
111 	/* Update Session Info, which is what is visible to the caller */
112 	tfp->session->ver.major = 0;
113 	tfp->session->ver.minor = 0;
114 	tfp->session->ver.update = 0;
115 
116 	tfp->session->session_id.internal.domain = session_id->internal.domain;
117 	tfp->session->session_id.internal.bus = session_id->internal.bus;
118 	tfp->session->session_id.internal.device = session_id->internal.device;
119 	tfp->session->session_id.internal.fw_session_id = fw_session_id;
120 
121 	/* Initialize Session and Device, which is private */
122 	session = (struct tf_session *)tfp->session->core_data;
123 	session->ver.major = 0;
124 	session->ver.minor = 0;
125 	session->ver.update = 0;
126 
127 	session->session_id.internal.domain = session_id->internal.domain;
128 	session->session_id.internal.bus = session_id->internal.bus;
129 	session->session_id.internal.device = session_id->internal.device;
130 	session->session_id.internal.fw_session_id = fw_session_id;
131 	/* Return the allocated session id */
132 	session_id->id = session->session_id.id;
133 
134 	session->shadow_copy = parms->open_cfg->shadow_copy;
135 
136 	/* Init session client list */
137 	ll_init(&session->client_ll);
138 
139 	/* Create the local session client, initialize and attach to
140 	 * the session
141 	 */
142 	cparms.nitems = 1;
143 	cparms.size = sizeof(struct tf_session_client);
144 	cparms.alignment = 0;
145 	rc = tfp_calloc(&cparms);
146 	if (rc) {
147 		/* Log error */
148 		TFP_DRV_LOG(ERR,
149 			    "Failed to allocate session client, rc:%s\n",
150 			    strerror(-rc));
151 		goto cleanup;
152 	}
153 	client = cparms.mem_va;
154 
155 	/* Register FID with the client */
156 	rc = tfp_get_fid(tfp, &client->fw_fid);
157 	if (rc)
158 		return rc;
159 
160 	client->session_client_id.internal.fw_session_id = fw_session_id;
161 	client->session_client_id.internal.fw_session_client_id =
162 		fw_session_client_id;
163 
164 	tfp_memcpy(client->ctrl_chan_name,
165 		   parms->open_cfg->ctrl_chan_name,
166 		   TF_SESSION_NAME_MAX);
167 
168 	ll_insert(&session->client_ll, &client->ll_entry);
169 	session->ref_count++;
170 
171 	rc = tf_dev_bind(tfp,
172 			 parms->open_cfg->device_type,
173 			 session->shadow_copy,
174 			 &parms->open_cfg->resources,
175 			 &session->dev);
176 	/* Logging handled by dev_bind */
177 	if (rc)
178 		return rc;
179 
180 	session->dev_init = true;
181 
182 	return 0;
183 
184  cleanup:
185 	tfp_free(tfp->session->core_data);
186 	tfp_free(tfp->session);
187 	tfp->session = NULL;
188 	return rc;
189 }
190 
191 /**
192  * Creates a Session Client on an existing Session.
193  *
194  * [in] tfp
195  *   Pointer to TF handle
196  *
197  * [in] parms
198  *   Pointer to session client create parameters
199  *
200  * Returns
201  *   - (0) if successful.
202  *   - (-EINVAL) on failure.
203  *   - (-ENOMEM) if max session clients has been reached.
204  */
205 static int
206 tf_session_client_create(struct tf *tfp,
207 			 struct tf_session_client_create_parms *parms)
208 {
209 	int rc;
210 	struct tf_session *session = NULL;
211 	struct tf_session_client *client;
212 	struct tfp_calloc_parms cparms;
213 	union tf_session_client_id session_client_id;
214 
215 	TF_CHECK_PARMS2(tfp, parms);
216 
217 	/* Using internal version as session client may not exist yet */
218 	rc = tf_session_get_session_internal(tfp, &session);
219 	if (rc) {
220 		TFP_DRV_LOG(ERR,
221 			    "Failed to lookup session, rc:%s\n",
222 			    strerror(-rc));
223 		return rc;
224 	}
225 
226 	client = tf_session_find_session_client_by_name(session,
227 							parms->ctrl_chan_name);
228 	if (client) {
229 		TFP_DRV_LOG(ERR,
230 			    "Client %s, already registered with this session\n",
231 			    parms->ctrl_chan_name);
232 		return -EOPNOTSUPP;
233 	}
234 
235 	rc = tf_msg_session_client_register
236 		    (tfp,
237 		    parms->ctrl_chan_name,
238 		    &session_client_id.internal.fw_session_client_id);
239 	if (rc) {
240 		TFP_DRV_LOG(ERR,
241 			    "Failed to create client on session, rc:%s\n",
242 			    strerror(-rc));
243 		return rc;
244 	}
245 
246 	/* Create the local session client, initialize and attach to
247 	 * the session
248 	 */
249 	cparms.nitems = 1;
250 	cparms.size = sizeof(struct tf_session_client);
251 	cparms.alignment = 0;
252 	rc = tfp_calloc(&cparms);
253 	if (rc) {
254 		TFP_DRV_LOG(ERR,
255 			    "Failed to allocate session client, rc:%s\n",
256 			    strerror(-rc));
257 		goto cleanup;
258 	}
259 	client = cparms.mem_va;
260 
261 	/* Register FID with the client */
262 	rc = tfp_get_fid(tfp, &client->fw_fid);
263 	if (rc)
264 		return rc;
265 
266 	/* Build the Session Client ID by adding the fw_session_id */
267 	rc = tf_session_get_fw_session_id
268 			(tfp,
269 			&session_client_id.internal.fw_session_id);
270 	if (rc) {
271 		TFP_DRV_LOG(ERR,
272 			    "Session Firmware id lookup failed, rc:%s\n",
273 			    strerror(-rc));
274 		return rc;
275 	}
276 
277 	tfp_memcpy(client->ctrl_chan_name,
278 		   parms->ctrl_chan_name,
279 		   TF_SESSION_NAME_MAX);
280 
281 	client->session_client_id.id = session_client_id.id;
282 
283 	ll_insert(&session->client_ll, &client->ll_entry);
284 
285 	session->ref_count++;
286 
287 	/* Build the return value */
288 	parms->session_client_id->id = session_client_id.id;
289 
290  cleanup:
291 	/* TBD - Add code to unregister newly create client from fw */
292 
293 	return rc;
294 }
295 
296 
297 /**
298  * Destroys a Session Client on an existing Session.
299  *
300  * [in] tfp
301  *   Pointer to TF handle
302  *
303  * [in] parms
304  *   Pointer to the session client destroy parameters
305  *
306  * Returns
307  *   - (0) if successful.
308  *   - (-EINVAL) on failure.
309  *   - (-ENOTFOUND) error, client not owned by the session.
310  *   - (-ENOTSUPP) error, unable to destroy client as its the last
311  *                 client. Please use the tf_session_close().
312  */
313 static int
314 tf_session_client_destroy(struct tf *tfp,
315 			  struct tf_session_client_destroy_parms *parms)
316 {
317 	int rc;
318 	struct tf_session *tfs;
319 	struct tf_session_client *client;
320 
321 	TF_CHECK_PARMS2(tfp, parms);
322 
323 	rc = tf_session_get_session(tfp, &tfs);
324 	if (rc) {
325 		TFP_DRV_LOG(ERR,
326 			    "Failed to lookup session, rc:%s\n",
327 			    strerror(-rc));
328 		return rc;
329 	}
330 
331 	/* Check session owns this client and that we're not the last client */
332 	client = tf_session_get_session_client(tfs,
333 					       parms->session_client_id);
334 	if (client == NULL) {
335 		TFP_DRV_LOG(ERR,
336 			    "Client %d, not found within this session\n",
337 			    parms->session_client_id.id);
338 		return -EINVAL;
339 	}
340 
341 	/* If last client the request is rejected and cleanup should
342 	 * be done by session close.
343 	 */
344 	if (tfs->ref_count == 1)
345 		return -EOPNOTSUPP;
346 
347 	rc = tf_msg_session_client_unregister
348 			(tfp,
349 			parms->session_client_id.internal.fw_session_client_id);
350 
351 	/* Log error, but continue. If FW fails we do not really have
352 	 * a way to fix this but the client would no longer be valid
353 	 * thus we remove from the session.
354 	 */
355 	if (rc) {
356 		TFP_DRV_LOG(ERR,
357 			    "Client destroy on FW Failed, rc:%s\n",
358 			    strerror(-rc));
359 	}
360 
361 	ll_delete(&tfs->client_ll, &client->ll_entry);
362 
363 	/* Decrement the session ref_count */
364 	tfs->ref_count--;
365 
366 	tfp_free(client);
367 
368 	return rc;
369 }
370 
371 int
372 tf_session_open_session(struct tf *tfp,
373 			struct tf_session_open_session_parms *parms)
374 {
375 	int rc;
376 	struct tf_session_client_create_parms scparms;
377 
378 	TF_CHECK_PARMS2(tfp, parms);
379 
380 	/* Decide if we're creating a new session or session client */
381 	if (tfp->session == NULL) {
382 		rc = tf_session_create(tfp, parms);
383 		if (rc) {
384 			TFP_DRV_LOG(ERR,
385 				    "Failed to create session, ctrl_chan_name:%s, rc:%s\n",
386 				    parms->open_cfg->ctrl_chan_name,
387 				    strerror(-rc));
388 			return rc;
389 		}
390 
391 		TFP_DRV_LOG(INFO,
392 		       "Session created, session_client_id:%d, session_id:%d\n",
393 		       parms->open_cfg->session_client_id.id,
394 		       parms->open_cfg->session_id.id);
395 	} else {
396 		scparms.ctrl_chan_name = parms->open_cfg->ctrl_chan_name;
397 		scparms.session_client_id = &parms->open_cfg->session_client_id;
398 
399 		/* Create the new client and get it associated with
400 		 * the session.
401 		 */
402 		rc = tf_session_client_create(tfp, &scparms);
403 		if (rc) {
404 			TFP_DRV_LOG(ERR,
405 			      "Failed to create client on session %d, rc:%s\n",
406 			      parms->open_cfg->session_id.id,
407 			      strerror(-rc));
408 			return rc;
409 		}
410 
411 		TFP_DRV_LOG(INFO,
412 			    "Session Client:%d created on session:%d\n",
413 			    parms->open_cfg->session_client_id.id,
414 			    parms->open_cfg->session_id.id);
415 	}
416 
417 	return 0;
418 }
419 
420 int
421 tf_session_attach_session(struct tf *tfp __rte_unused,
422 			  struct tf_session_attach_session_parms *parms __rte_unused)
423 {
424 	int rc = -EOPNOTSUPP;
425 
426 	TF_CHECK_PARMS2(tfp, parms);
427 
428 	TFP_DRV_LOG(ERR,
429 		    "Attach not yet supported, rc:%s\n",
430 		    strerror(-rc));
431 	return rc;
432 }
433 
434 int
435 tf_session_close_session(struct tf *tfp,
436 			 struct tf_session_close_session_parms *parms)
437 {
438 	int rc;
439 	struct tf_session *tfs = NULL;
440 	struct tf_session_client *client;
441 	struct tf_dev_info *tfd = NULL;
442 	struct tf_session_client_destroy_parms scdparms;
443 	uint16_t fid;
444 
445 	TF_CHECK_PARMS2(tfp, parms);
446 
447 	rc = tf_session_get_session(tfp, &tfs);
448 	if (rc) {
449 		TFP_DRV_LOG(ERR,
450 			    "Session lookup failed, rc:%s\n",
451 			    strerror(-rc));
452 		return rc;
453 	}
454 
455 	if (tfs->session_id.id == TF_SESSION_ID_INVALID) {
456 		rc = -EINVAL;
457 		TFP_DRV_LOG(ERR,
458 			    "Invalid session id, unable to close, rc:%s\n",
459 			    strerror(-rc));
460 		return rc;
461 	}
462 
463 	/* Get the client, we need it independently of the closure
464 	 * type (client or session closure).
465 	 *
466 	 * We find the client by way of the fid. Thus one cannot close
467 	 * a client on behalf of someone else.
468 	 */
469 	rc = tfp_get_fid(tfp, &fid);
470 	if (rc)
471 		return rc;
472 
473 	client = tf_session_find_session_client_by_fid(tfs,
474 						       fid);
475 	/* In case multiple clients we chose to close those first */
476 	if (tfs->ref_count > 1) {
477 		/* Linaro gcc can't static init this structure */
478 		memset(&scdparms,
479 		       0,
480 		       sizeof(struct tf_session_client_destroy_parms));
481 
482 		scdparms.session_client_id = client->session_client_id;
483 		/* Destroy requested client so its no longer
484 		 * registered with this session.
485 		 */
486 		rc = tf_session_client_destroy(tfp, &scdparms);
487 		if (rc) {
488 			TFP_DRV_LOG(ERR,
489 				    "Failed to unregister Client %d, rc:%s\n",
490 				    client->session_client_id.id,
491 				    strerror(-rc));
492 			return rc;
493 		}
494 
495 		TFP_DRV_LOG(INFO,
496 			    "Closed session client, session_client_id:%d\n",
497 			    client->session_client_id.id);
498 
499 		TFP_DRV_LOG(INFO,
500 			    "session_id:%d, ref_count:%d\n",
501 			    tfs->session_id.id,
502 			    tfs->ref_count);
503 
504 		return 0;
505 	}
506 
507 	/* Record the session we're closing so the caller knows the
508 	 * details.
509 	 */
510 	*parms->session_id = tfs->session_id;
511 
512 	rc = tf_session_get_device(tfs, &tfd);
513 	if (rc) {
514 		TFP_DRV_LOG(ERR,
515 			    "Device lookup failed, rc:%s\n",
516 			    strerror(-rc));
517 		return rc;
518 	}
519 
520 	/* Unbind the device */
521 	rc = tf_dev_unbind(tfp, tfd);
522 	if (rc) {
523 		/* Log error */
524 		TFP_DRV_LOG(ERR,
525 			    "Device unbind failed, rc:%s\n",
526 			    strerror(-rc));
527 	}
528 
529 	rc = tf_msg_session_close(tfp);
530 	if (rc) {
531 		/* Log error */
532 		TFP_DRV_LOG(ERR,
533 			    "FW Session close failed, rc:%s\n",
534 			    strerror(-rc));
535 	}
536 
537 	/* Final cleanup as we're last user of the session thus we
538 	 * also delete the last client.
539 	 */
540 	ll_delete(&tfs->client_ll, &client->ll_entry);
541 	tfp_free(client);
542 
543 	tfs->ref_count--;
544 
545 	TFP_DRV_LOG(INFO,
546 		    "Closed session, session_id:%d, ref_count:%d\n",
547 		    tfs->session_id.id,
548 		    tfs->ref_count);
549 
550 	tfs->dev_init = false;
551 
552 	tfp_free(tfp->session->core_data);
553 	tfp_free(tfp->session);
554 	tfp->session = NULL;
555 
556 	return 0;
557 }
558 
559 bool
560 tf_session_is_fid_supported(struct tf_session *tfs,
561 			    uint16_t fid)
562 {
563 	struct ll_entry *c_entry;
564 	struct tf_session_client *client;
565 
566 	for (c_entry = tfs->client_ll.head;
567 	     c_entry != NULL;
568 	     c_entry = c_entry->next) {
569 		client = (struct tf_session_client *)c_entry;
570 		if (client->fw_fid == fid)
571 			return true;
572 	}
573 
574 	return false;
575 }
576 
577 int
578 tf_session_get_session_internal(struct tf *tfp,
579 				struct tf_session **tfs)
580 {
581 	int rc = 0;
582 
583 	/* Skip using the check macro as we want to control the error msg */
584 	if (tfp->session == NULL || tfp->session->core_data == NULL) {
585 		rc = -EINVAL;
586 		TFP_DRV_LOG(ERR,
587 			    "Session not created, rc:%s\n",
588 			    strerror(-rc));
589 		return rc;
590 	}
591 
592 	*tfs = (struct tf_session *)(tfp->session->core_data);
593 
594 	return rc;
595 }
596 
597 int
598 tf_session_get_session(struct tf *tfp,
599 		       struct tf_session **tfs)
600 {
601 	int rc;
602 	uint16_t fw_fid;
603 	bool supported = false;
604 
605 	rc = tf_session_get_session_internal(tfp,
606 					     tfs);
607 	/* Logging done by tf_session_get_session_internal */
608 	if (rc)
609 		return rc;
610 
611 	/* As session sharing among functions aka 'individual clients'
612 	 * is supported we have to assure that the client is indeed
613 	 * registered before we get deep in the TruFlow api stack.
614 	 */
615 	rc = tfp_get_fid(tfp, &fw_fid);
616 	if (rc) {
617 		TFP_DRV_LOG(ERR,
618 			    "Internal FID lookup\n, rc:%s\n",
619 			    strerror(-rc));
620 		return rc;
621 	}
622 
623 	supported = tf_session_is_fid_supported(*tfs, fw_fid);
624 	if (!supported) {
625 		TFP_DRV_LOG
626 			(ERR,
627 			"Ctrl channel not registered with session\n, rc:%s\n",
628 			strerror(-rc));
629 		return -EINVAL;
630 	}
631 
632 	return rc;
633 }
634 
635 struct tf_session_client *
636 tf_session_get_session_client(struct tf_session *tfs,
637 			      union tf_session_client_id session_client_id)
638 {
639 	struct ll_entry *c_entry;
640 	struct tf_session_client *client;
641 
642 	/* Skip using the check macro as we just want to return */
643 	if (tfs == NULL)
644 		return NULL;
645 
646 	for (c_entry = tfs->client_ll.head;
647 	     c_entry != NULL;
648 	     c_entry = c_entry->next) {
649 		client = (struct tf_session_client *)c_entry;
650 		if (client->session_client_id.id == session_client_id.id)
651 			return client;
652 	}
653 
654 	return NULL;
655 }
656 
657 struct tf_session_client *
658 tf_session_find_session_client_by_name(struct tf_session *tfs,
659 				       const char *ctrl_chan_name)
660 {
661 	struct ll_entry *c_entry;
662 	struct tf_session_client *client;
663 
664 	/* Skip using the check macro as we just want to return */
665 	if (tfs == NULL || ctrl_chan_name == NULL)
666 		return NULL;
667 
668 	for (c_entry = tfs->client_ll.head;
669 	     c_entry != NULL;
670 	     c_entry = c_entry->next) {
671 		client = (struct tf_session_client *)c_entry;
672 		if (strncmp(client->ctrl_chan_name,
673 			    ctrl_chan_name,
674 			    TF_SESSION_NAME_MAX) == 0)
675 			return client;
676 	}
677 
678 	return NULL;
679 }
680 
681 struct tf_session_client *
682 tf_session_find_session_client_by_fid(struct tf_session *tfs,
683 				      uint16_t fid)
684 {
685 	struct ll_entry *c_entry;
686 	struct tf_session_client *client;
687 
688 	/* Skip using the check macro as we just want to return */
689 	if (tfs == NULL)
690 		return NULL;
691 
692 	for (c_entry = tfs->client_ll.head;
693 	     c_entry != NULL;
694 	     c_entry = c_entry->next) {
695 		client = (struct tf_session_client *)c_entry;
696 		if (client->fw_fid == fid)
697 			return client;
698 	}
699 
700 	return NULL;
701 }
702 
703 int
704 tf_session_get_device(struct tf_session *tfs,
705 		      struct tf_dev_info **tfd)
706 {
707 	*tfd = &tfs->dev;
708 
709 	return 0;
710 }
711 
712 int
713 tf_session_get_fw_session_id(struct tf *tfp,
714 			     uint8_t *fw_session_id)
715 {
716 	int rc;
717 	struct tf_session *tfs = NULL;
718 
719 	/* Skip using the check macro as we want to control the error msg */
720 	if (tfp->session == NULL) {
721 		rc = -EINVAL;
722 		TFP_DRV_LOG(ERR,
723 			    "Session not created, rc:%s\n",
724 			    strerror(-rc));
725 		return rc;
726 	}
727 
728 	if (fw_session_id == NULL) {
729 		rc = -EINVAL;
730 		TFP_DRV_LOG(ERR,
731 			    "Invalid Argument(s), rc:%s\n",
732 			    strerror(-rc));
733 		return rc;
734 	}
735 
736 	rc = tf_session_get_session_internal(tfp, &tfs);
737 	if (rc)
738 		return rc;
739 
740 	*fw_session_id = tfs->session_id.internal.fw_session_id;
741 
742 	return 0;
743 }
744 
745 int
746 tf_session_get_session_id(struct tf *tfp,
747 			  union tf_session_id *session_id)
748 {
749 	int rc;
750 	struct tf_session *tfs = NULL;
751 
752 	if (tfp->session == NULL) {
753 		rc = -EINVAL;
754 		TFP_DRV_LOG(ERR,
755 			    "Session not created, rc:%s\n",
756 			    strerror(-rc));
757 		return rc;
758 	}
759 
760 	if (session_id == NULL) {
761 		rc = -EINVAL;
762 		TFP_DRV_LOG(ERR,
763 			    "Invalid Argument(s), rc:%s\n",
764 			    strerror(-rc));
765 		return rc;
766 	}
767 
768 	/* Using internal version as session client may not exist yet */
769 	rc = tf_session_get_session_internal(tfp, &tfs);
770 	if (rc)
771 		return rc;
772 
773 	*session_id = tfs->session_id;
774 
775 	return 0;
776 }
777