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 *
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 * iSCSI Software Initiator
25 */
26
27 /*
28 * Framework interface routines for iSCSI
29 */
30 #include "iscsi.h" /* main header */
31 #include <sys/idm/idm_text.h> /* main header */
32 #include <sys/iscsi_protocol.h> /* protocol structs */
33 #include <sys/scsi/adapters/iscsi_if.h> /* ioctl interfaces */
34 #include "persistent.h"
35 #include <sys/scsi/adapters/iscsi_door.h>
36 #include "iscsi_targetparam.h"
37 #include <sys/strsubr.h>
38 #include <sys/socketvar.h>
39 #include <sys/bootprops.h>
40
41 extern ib_boot_prop_t *iscsiboot_prop;
42
43 static iscsi_status_t iscsi_create_sendtgts_list(iscsi_conn_t *icp,
44 char *data, int data_len, iscsi_sendtgts_list_t *stl);
45
46 /*
47 * iscsi_ioctl_copyin -
48 */
49 void *
iscsi_ioctl_copyin(caddr_t arg,int mode,size_t size)50 iscsi_ioctl_copyin(caddr_t arg, int mode, size_t size)
51 {
52 void *data = NULL;
53
54 ASSERT(arg != NULL);
55 ASSERT(size != 0);
56
57 data = kmem_alloc(size, KM_SLEEP);
58
59 if (ddi_copyin(arg, data, size, mode) != 0) {
60 kmem_free(data, size);
61 data = NULL;
62 }
63 return (data);
64 }
65
66 /*
67 * iscsi_ioctl_copyout -
68 */
69 int
iscsi_ioctl_copyout(void * data,size_t size,caddr_t arg,int mode)70 iscsi_ioctl_copyout(void *data, size_t size, caddr_t arg, int mode)
71 {
72 int rtn;
73
74 rtn = EFAULT;
75 if (ddi_copyout(data, arg, size, mode) == 0) {
76 rtn = 0;
77 }
78 kmem_free(data, size);
79 return (rtn);
80 }
81
82 /*
83 * iscsi_conn_list_get_copyin -
84 */
85 iscsi_conn_list_t *
iscsi_ioctl_conn_oid_list_get_copyin(caddr_t arg,int mode)86 iscsi_ioctl_conn_oid_list_get_copyin(caddr_t arg, int mode)
87 {
88 iscsi_conn_list_t *cl_tmp;
89 iscsi_conn_list_t *cl = NULL;
90 size_t alloc_len;
91
92 ASSERT(arg != NULL);
93
94 cl_tmp = (iscsi_conn_list_t *)kmem_zalloc(sizeof (*cl_tmp), KM_SLEEP);
95
96 if (ddi_copyin(arg, cl_tmp, sizeof (*cl_tmp), mode) == 0) {
97
98 if (cl_tmp->cl_vers == ISCSI_INTERFACE_VERSION) {
99 alloc_len = sizeof (*cl);
100 if (cl_tmp->cl_in_cnt != 0) {
101 alloc_len += ((cl_tmp->cl_in_cnt - 1) *
102 sizeof (iscsi_if_conn_t));
103 }
104
105 cl = (iscsi_conn_list_t *)kmem_zalloc(alloc_len,
106 KM_SLEEP);
107 bcopy(cl_tmp, cl, sizeof (*cl_tmp));
108 }
109 }
110 kmem_free(cl_tmp, sizeof (*cl_tmp));
111 return (cl);
112 }
113
114 /*
115 * iscsi_conn_list_get_copyout -
116 */
117 int
iscsi_ioctl_conn_oid_list_get_copyout(iscsi_conn_list_t * cl,caddr_t arg,int mode)118 iscsi_ioctl_conn_oid_list_get_copyout(iscsi_conn_list_t *cl, caddr_t arg,
119 int mode)
120 {
121 size_t alloc_len;
122 int rtn;
123
124 ASSERT(cl != NULL);
125 ASSERT(arg != NULL);
126
127 rtn = EFAULT;
128 alloc_len = sizeof (*cl);
129 if (cl->cl_in_cnt != 0) {
130 alloc_len += ((cl->cl_in_cnt - 1) * sizeof (iscsi_if_conn_t));
131 }
132
133 if (ddi_copyout(cl, arg, alloc_len, mode) == 0) {
134 rtn = 0;
135 }
136 kmem_free(cl, alloc_len);
137 return (rtn);
138 }
139
140 /*
141 * iscsi_conn_oid_list_get -
142 */
143 boolean_t
iscsi_ioctl_conn_oid_list_get(iscsi_hba_t * ihp,iscsi_conn_list_t * cl)144 iscsi_ioctl_conn_oid_list_get(iscsi_hba_t *ihp, iscsi_conn_list_t *cl)
145 {
146 iscsi_sess_t *isp;
147 iscsi_conn_t *icp;
148 iscsi_if_conn_t *cnx;
149 uint32_t target_oid;
150
151 /* Let's check the version. */
152 if (cl->cl_vers != ISCSI_INTERFACE_VERSION) {
153 return (B_FALSE);
154 }
155
156 /* We preinitialize the output connection counter. */
157 cl->cl_out_cnt = 0;
158
159 /* The list of sessions is walked holding the HBA mutex. */
160 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER);
161 isp = ihp->hba_sess_list;
162
163 /*
164 * Check to see if oid references a target-param oid. If so,
165 * find the associated session oid before getting lu list.
166 */
167 if (iscsi_targetparam_get_name(cl->cl_sess_oid) != NULL) {
168 for (isp = ihp->hba_sess_list; isp; isp = isp->sess_next) {
169 if (isp->sess_target_oid == cl->cl_sess_oid) {
170 target_oid = isp->sess_oid;
171 break;
172 }
173 }
174 } else {
175 target_oid = cl->cl_sess_oid;
176 }
177
178 while (isp != NULL) {
179 ASSERT(isp->sess_sig == ISCSI_SIG_SESS);
180
181 /* return connections for NORMAL sessions only */
182 if ((isp->sess_type == ISCSI_SESS_TYPE_NORMAL) &&
183 ((cl->cl_all_sess == B_TRUE) ||
184 (target_oid == isp->sess_oid))) {
185 /*
186 * The list of connections is walked holding
187 * the session mutex.
188 */
189 rw_enter(&isp->sess_conn_list_rwlock, RW_READER);
190 icp = isp->sess_conn_list;
191
192 while (icp != NULL) {
193 ASSERT(icp->conn_sig == ISCSI_SIG_CONN);
194
195 if (icp->conn_state ==
196 ISCSI_CONN_STATE_LOGGED_IN) {
197
198 if (cl->cl_out_cnt < cl->cl_in_cnt) {
199 /* There's still room. */
200 cnx =
201 &cl->cl_list[
202 cl->cl_out_cnt];
203
204 bzero(cnx, sizeof (*cnx));
205
206 cnx->c_cid = icp->conn_cid;
207 cnx->c_oid = icp->conn_oid;
208 cnx->c_sess_oid = isp->sess_oid;
209 }
210 ++cl->cl_out_cnt;
211 }
212 icp = icp->conn_next;
213 }
214 rw_exit(&isp->sess_conn_list_rwlock);
215
216 if (cl->cl_all_sess == B_FALSE) {
217 /*
218 * We got here because it was the only session
219 * we were looking for. We can exit now.
220 */
221 break;
222 }
223 }
224 isp = isp->sess_next;
225 }
226 rw_exit(&ihp->hba_sess_list_rwlock);
227 return (B_TRUE);
228 }
229
230 /*
231 * iscsi_ioctl_conn_props_get -
232 */
233 boolean_t
iscsi_ioctl_conn_props_get(iscsi_hba_t * ihp,iscsi_conn_props_t * cp)234 iscsi_ioctl_conn_props_get(iscsi_hba_t *ihp, iscsi_conn_props_t *cp)
235 {
236 iscsi_sess_t *isp;
237 iscsi_conn_t *icp;
238 boolean_t rtn;
239 idm_conn_t *idm_conn;
240
241 /* Let's check the version. */
242 if (cp->cp_vers != ISCSI_INTERFACE_VERSION) {
243 return (B_FALSE);
244 }
245
246 /* Let's find the session. */
247 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER);
248 if (iscsi_sess_get(cp->cp_sess_oid, ihp, &isp) != 0) {
249 rw_exit(&ihp->hba_sess_list_rwlock);
250 return (B_FALSE);
251 }
252
253 ASSERT(isp->sess_sig == ISCSI_SIG_SESS);
254
255 rtn = B_FALSE;
256
257 rw_enter(&isp->sess_conn_list_rwlock, RW_READER);
258 icp = isp->sess_conn_list;
259 cp->cp_params_valid = B_FALSE;
260
261 while (icp != NULL) {
262
263 ASSERT(icp->conn_sig == ISCSI_SIG_CONN);
264
265 if (icp->conn_oid == cp->cp_oid) {
266 struct sockaddr_storage *sal;
267 struct sockaddr_storage *sar;
268
269 idm_conn =
270 (idm_conn_t *)icp->conn_ic;
271
272 sal = &idm_conn->ic_laddr;
273 sar = &idm_conn->ic_raddr;
274
275 /* Local Address */
276 if (sal->ss_family == AF_INET) {
277 bcopy(&idm_conn->ic_laddr,
278 &cp->cp_local,
279 sizeof (struct sockaddr_in));
280 } else {
281 bcopy(&idm_conn->ic_laddr,
282 &cp->cp_local,
283 sizeof (struct sockaddr_in6));
284 }
285
286 /* Peer Address */
287 if (sar->ss_family == AF_INET) {
288 bcopy(&idm_conn->ic_raddr,
289 &cp->cp_peer,
290 sizeof (struct sockaddr_in));
291 } else {
292 bcopy(&idm_conn->ic_raddr,
293 &cp->cp_peer,
294 sizeof (struct sockaddr_in6));
295 }
296
297 if (icp->conn_state == ISCSI_CONN_STATE_LOGGED_IN) {
298 cp->cp_params_valid = B_TRUE;
299 bcopy(&icp->conn_params, &cp->cp_params,
300 sizeof (icp->conn_params));
301 }
302
303 rtn = B_TRUE;
304 break;
305 }
306 icp = icp->conn_next;
307 }
308 rw_exit(&isp->sess_conn_list_rwlock);
309 rw_exit(&ihp->hba_sess_list_rwlock);
310 return (rtn);
311 }
312
313
314 /*
315 * iscsi_ioctl_sendtgts_get - 0 on success; errno on failure
316 *
317 */
318 int
iscsi_ioctl_sendtgts_get(iscsi_hba_t * ihp,iscsi_sendtgts_list_t * stl)319 iscsi_ioctl_sendtgts_get(iscsi_hba_t *ihp, iscsi_sendtgts_list_t *stl)
320 {
321 #define ISCSI_SENDTGTS_REQ_STR "SendTargets=All"
322
323 int rtn = EFAULT;
324 iscsi_status_t status;
325 iscsi_sess_t *isp;
326 iscsi_conn_t *icp;
327 uint32_t oid;
328 char *data;
329 uint32_t data_len;
330 uint32_t rx_data_len;
331 iscsi_sockaddr_t addr_snd;
332
333 ASSERT(ihp != NULL);
334 ASSERT(stl != NULL);
335
336 iscsid_addr_to_sockaddr(stl->stl_entry.e_insize,
337 &stl->stl_entry.e_u, stl->stl_entry.e_port,
338 &addr_snd.sin);
339
340 /* create discovery session */
341 rw_enter(&ihp->hba_sess_list_rwlock, RW_WRITER);
342 isp = iscsi_sess_create(ihp, iSCSIDiscoveryMethodSendTargets,
343 NULL, SENDTARGETS_DISCOVERY, ISCSI_DEFAULT_TPGT,
344 ISCSI_SUN_ISID_5, ISCSI_SESS_TYPE_DISCOVERY, &oid);
345 if (isp == NULL) {
346 rw_exit(&ihp->hba_sess_list_rwlock);
347 return (1);
348 }
349
350 /* create connection */
351 rw_enter(&isp->sess_conn_list_rwlock, RW_WRITER);
352 status = iscsi_conn_create(&addr_snd.sin, isp, &icp);
353 rw_exit(&isp->sess_conn_list_rwlock);
354
355 if (!ISCSI_SUCCESS(status)) {
356 (void) iscsi_sess_destroy(isp);
357 rw_exit(&ihp->hba_sess_list_rwlock);
358 return (1);
359 }
360 rw_exit(&ihp->hba_sess_list_rwlock);
361
362 /* start login */
363 mutex_enter(&icp->conn_state_mutex);
364 status = iscsi_conn_online(icp);
365 mutex_exit(&icp->conn_state_mutex);
366
367 if (status == ISCSI_STATUS_SUCCESS) {
368 data_len = icp->conn_params.max_xmit_data_seg_len;
369 retry_sendtgts:
370 /* alloc/init buffer for SendTargets req/resp */
371 data = kmem_zalloc(data_len, KM_SLEEP);
372 bcopy(ISCSI_SENDTGTS_REQ_STR, data,
373 sizeof (ISCSI_SENDTGTS_REQ_STR));
374
375 /* execute SendTargets operation */
376 status = iscsi_handle_text(icp, data, data_len,
377 sizeof (ISCSI_SENDTGTS_REQ_STR), &rx_data_len);
378
379 /* check if allocated buffer is too small for response */
380 if (status == ISCSI_STATUS_DATA_OVERFLOW) {
381 kmem_free(data, data_len);
382 data_len = rx_data_len;
383 goto retry_sendtgts;
384 }
385
386 if (ISCSI_SUCCESS(status)) {
387 status = iscsi_create_sendtgts_list(icp, data,
388 rx_data_len, stl);
389 if (ISCSI_SUCCESS(status)) {
390 rtn = 0;
391 }
392 } else {
393 rtn = EFAULT;
394 }
395
396 kmem_free(data, data_len);
397 } else {
398 rtn = EFAULT;
399 }
400
401 /*
402 * check if session is still alive. It may have been destroyed
403 * by a driver unload
404 */
405 rw_enter(&ihp->hba_sess_list_rwlock, RW_WRITER);
406 if (iscsi_sess_get(oid, ihp, &isp) == 0) {
407 (void) iscsi_sess_destroy(isp);
408 }
409 rw_exit(&ihp->hba_sess_list_rwlock);
410
411 return (rtn);
412 }
413
414
415 /*
416 * iscsi_create_sendtgts_list - Based upon the given data, build a
417 * linked list of SendTarget information. The data passed into this
418 * function is expected to be the data portion(s) of SendTarget text
419 * response.
420 */
421 static iscsi_status_t
iscsi_create_sendtgts_list(iscsi_conn_t * icp,char * data,int data_len,iscsi_sendtgts_list_t * stl)422 iscsi_create_sendtgts_list(iscsi_conn_t *icp, char *data, int data_len,
423 iscsi_sendtgts_list_t *stl)
424 {
425 char *line = NULL;
426 boolean_t targetname_added = B_FALSE;
427 iscsi_sendtgts_entry_t *curr_ste = NULL,
428 *prev_ste = NULL;
429 struct hostent *hptr;
430 int error_num;
431
432 /* initialize number of targets found */
433 stl->stl_out_cnt = 0;
434
435 if (data_len == 0)
436 return (ISCSI_STATUS_SUCCESS);
437
438 while ((line = iscsi_get_next_text(data, data_len, line)) != NULL) {
439 if (strncmp(TARGETNAME, line, strlen(TARGETNAME)) == 0) {
440 /* check if this is first targetname */
441 if (prev_ste != NULL) {
442 stl->stl_out_cnt++;
443 }
444 if (stl->stl_out_cnt >= stl->stl_in_cnt) {
445 /*
446 * continue processing the data so that
447 * the total number of targets are known
448 * and the caller can retry with the correct
449 * number of entries in the list
450 */
451 continue;
452 }
453 curr_ste = &(stl->stl_list[stl->stl_out_cnt]);
454
455 /*
456 * This entry will use the IP address and port
457 * that was passed into this routine. If the next
458 * line that we receive is a TargetAddress we will
459 * know to modify this entry with the new IP address,
460 * port and portal group tag. If this state flag
461 * is not set we'll just create a new entry using
462 * only the previous entries targetname.
463 */
464 (void) strncpy((char *)curr_ste->ste_name,
465 line + strlen(TARGETNAME),
466 sizeof (curr_ste->ste_name));
467
468 if (icp->conn_base_addr.sin.sa_family == AF_INET) {
469
470 struct sockaddr_in *addr_in =
471 &icp->conn_base_addr.sin4;
472 curr_ste->ste_ipaddr.a_addr.i_insize =
473 sizeof (struct in_addr);
474 bcopy(&addr_in->sin_addr.s_addr,
475 &curr_ste->ste_ipaddr.a_addr.i_addr,
476 sizeof (struct in_addr));
477 curr_ste->ste_ipaddr.a_port =
478 htons(addr_in->sin_port);
479
480 } else {
481
482 struct sockaddr_in6 *addr_in6 =
483 &icp->conn_base_addr.sin6;
484 curr_ste->ste_ipaddr.a_addr.i_insize =
485 sizeof (struct in6_addr);
486 bcopy(&addr_in6->sin6_addr.s6_addr,
487 &curr_ste->ste_ipaddr.a_addr.i_addr,
488 sizeof (struct in6_addr));
489 curr_ste->ste_ipaddr.a_port =
490 htons(addr_in6->sin6_port);
491 }
492 curr_ste->ste_tpgt = -1;
493
494 targetname_added = B_TRUE;
495
496 } else if (strncmp(TARGETADDRESS, line,
497 strlen(TARGETADDRESS)) == 0) {
498
499 char *in_str,
500 *tmp_buf,
501 *addr_str,
502 *port_str,
503 *tpgt_str;
504 int type,
505 tmp_buf_len;
506 long result;
507
508 /*
509 * If TARGETADDRESS is first line a SendTarget response
510 * (i.e. no TARGETNAME lines preceding), treat as
511 * an error. To check this an assumption is made that
512 * at least one sendtarget_entry_t should exist prior
513 * to entering this code.
514 */
515 if (prev_ste == NULL) {
516 cmn_err(CE_NOTE, "SendTargets protocol error: "
517 "TARGETADDRESS first");
518 return (ISCSI_STATUS_PROTOCOL_ERROR);
519 }
520
521 /*
522 * If we can't find an '=' then the sendtargets
523 * response if invalid per spec. Return empty list.
524 */
525 in_str = strchr(line, '=');
526 if (in_str == NULL) {
527 return (ISCSI_STATUS_PROTOCOL_ERROR);
528 }
529
530 /* move past the '=' */
531 in_str++;
532
533 /* Copy addr, port, and tpgt into temporary buffer */
534 tmp_buf_len = strlen(in_str) + 1;
535 tmp_buf = kmem_zalloc(tmp_buf_len, KM_SLEEP);
536 (void) strncpy(tmp_buf, in_str, tmp_buf_len);
537
538 /*
539 * Parse the addr, port, and tpgt from
540 * sendtarget response
541 */
542 if (parse_addr_port_tpgt(tmp_buf, &addr_str, &type,
543 &port_str, &tpgt_str) == B_FALSE) {
544 /* Unable to extract addr */
545 kmem_free(tmp_buf, tmp_buf_len);
546 return (ISCSI_STATUS_PROTOCOL_ERROR);
547 }
548
549 /* Now convert string addr to binary */
550 hptr = kgetipnodebyname(addr_str, type,
551 AI_ALL, &error_num);
552 if (!hptr) {
553 /* Unable to get valid address */
554 kmem_free(tmp_buf, tmp_buf_len);
555 return (ISCSI_STATUS_PROTOCOL_ERROR);
556 }
557
558 /* Check if space for response */
559 if (targetname_added == B_FALSE) {
560 stl->stl_out_cnt++;
561 if (stl->stl_out_cnt >= stl->stl_in_cnt) {
562 /*
563 * continue processing the data so that
564 * the total number of targets are
565 * known and the caller can retry with
566 * the correct number of entries in
567 * the list
568 */
569 kfreehostent(hptr);
570 kmem_free(tmp_buf, tmp_buf_len);
571 continue;
572 }
573 curr_ste = &(stl->stl_list[stl->stl_out_cnt]);
574 (void) strcpy((char *)curr_ste->ste_name,
575 (char *)prev_ste->ste_name);
576 }
577
578 curr_ste->ste_ipaddr.a_addr.i_insize = hptr->h_length;
579 bcopy(*hptr->h_addr_list,
580 &(curr_ste->ste_ipaddr.a_addr.i_addr),
581 curr_ste->ste_ipaddr.a_addr.i_insize);
582 kfreehostent(hptr);
583
584 if (port_str != NULL) {
585 (void) ddi_strtol(port_str, NULL, 0, &result);
586 curr_ste->ste_ipaddr.a_port = (short)result;
587 } else {
588 curr_ste->ste_ipaddr.a_port = ISCSI_LISTEN_PORT;
589 }
590
591 if (tpgt_str != NULL) {
592 (void) ddi_strtol(tpgt_str, NULL, 0, &result);
593 curr_ste->ste_tpgt = (short)result;
594 } else {
595 cmn_err(CE_NOTE, "SendTargets protocol error: "
596 "TPGT not specified");
597 kmem_free(tmp_buf, tmp_buf_len);
598 return (ISCSI_STATUS_PROTOCOL_ERROR);
599 }
600
601 kmem_free(tmp_buf, tmp_buf_len);
602
603 targetname_added = B_FALSE;
604
605 } else if (strlen(line) != 0) {
606 /*
607 * Any other string besides an empty string
608 * is a protocol error
609 */
610 cmn_err(CE_NOTE, "SendTargets protocol error: "
611 "unexpected response");
612 return (ISCSI_STATUS_PROTOCOL_ERROR);
613 }
614
615 prev_ste = curr_ste;
616 }
617
618 /*
619 * If target found increment out count one more time because
620 * this is the total number of entries in the list not an index
621 * like it was used above
622 */
623 if (prev_ste != NULL) {
624 stl->stl_out_cnt++;
625 }
626
627 return (ISCSI_STATUS_SUCCESS);
628 }
629
630 /*
631 * iscsi_set_param - This function is a helper to ISCSI_SET_PARAM
632 * IOCTL
633 */
634 int
iscsi_set_param(iscsi_login_params_t * params,iscsi_param_set_t * ipsp)635 iscsi_set_param(iscsi_login_params_t *params, iscsi_param_set_t *ipsp)
636 {
637 int rtn = 0;
638 iscsi_param_get_t *ipgp;
639
640 /*
641 * Use get param to get the min, max and increment values for the
642 * given parameter so validation can be done on the new value.
643 */
644 ipgp = (iscsi_param_get_t *)kmem_alloc(sizeof (*ipgp), KM_SLEEP);
645 ipgp->g_param = ipsp->s_param;
646 rtn = iscsi_get_param(params, B_TRUE, ipgp);
647 if (rtn != 0) {
648 kmem_free(ipgp, sizeof (*ipgp));
649 return (rtn);
650 }
651
652 if (ipsp->s_param == ISCSI_LOGIN_PARAM_HEADER_DIGEST ||
653 ipsp->s_param == ISCSI_LOGIN_PARAM_DATA_DIGEST ||
654 ipsp->s_param == ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN ||
655 ipsp->s_param == ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT ||
656 ipsp->s_param == ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH ||
657 ipsp->s_param == ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH ||
658 ipsp->s_param == ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH) {
659
660 if (ipsp->s_value.v_integer < ipgp->g_value.v_integer.i_min ||
661 ipsp->s_value.v_integer > ipgp->g_value.v_integer.i_max ||
662 (ipsp->s_value.v_integer %
663 ipgp->g_value.v_integer.i_incr) != 0) {
664 rtn = EINVAL;
665 kmem_free(ipgp, sizeof (*ipgp));
666 return (rtn);
667 }
668
669 }
670 kmem_free(ipgp, sizeof (*ipgp));
671
672
673 switch (ipsp->s_param) {
674
675 /*
676 * Boolean parameters
677 */
678 case ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER:
679 params->data_sequence_in_order = ipsp->s_value.v_bool;
680 break;
681 case ISCSI_LOGIN_PARAM_IMMEDIATE_DATA:
682 params->immediate_data = ipsp->s_value.v_bool;
683 break;
684 case ISCSI_LOGIN_PARAM_INITIAL_R2T:
685 params->initial_r2t = ipsp->s_value.v_bool;
686 break;
687 case ISCSI_LOGIN_PARAM_DATA_PDU_IN_ORDER:
688 params->data_pdu_in_order = ipsp->s_value.v_bool;
689 break;
690
691 /*
692 * Integer parameters
693 */
694 case ISCSI_LOGIN_PARAM_HEADER_DIGEST:
695 params->header_digest = ipsp->s_value.v_integer;
696 break;
697 case ISCSI_LOGIN_PARAM_DATA_DIGEST:
698 params->data_digest = ipsp->s_value.v_integer;
699 break;
700 case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN:
701 params->default_time_to_retain = ipsp->s_value.v_integer;
702 break;
703 case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT:
704 params->default_time_to_wait = ipsp->s_value.v_integer;
705 break;
706 case ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH:
707 params->max_recv_data_seg_len = ipsp->s_value.v_integer;
708 break;
709 case ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH:
710 if (ipsp->s_value.v_integer <= params->max_burst_length) {
711 params->first_burst_length = ipsp->s_value.v_integer;
712 } else {
713 rtn = EINVAL;
714 }
715 break;
716 case ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH:
717 if (ipsp->s_value.v_integer >= params->first_burst_length) {
718 params->max_burst_length = ipsp->s_value.v_integer;
719 } else {
720 rtn = EINVAL;
721 }
722 break;
723
724 /*
725 * Integer parameters which currently are unsettable
726 */
727 case ISCSI_LOGIN_PARAM_MAX_CONNECTIONS:
728 case ISCSI_LOGIN_PARAM_OUTSTANDING_R2T:
729 case ISCSI_LOGIN_PARAM_ERROR_RECOVERY_LEVEL:
730 rtn = ENOTSUP;
731 break;
732
733 default:
734 rtn = EINVAL;
735 break;
736 }
737 return (rtn);
738 }
739
740 int
iscsi_set_params(iscsi_param_set_t * ils,iscsi_hba_t * ihp,boolean_t persist)741 iscsi_set_params(iscsi_param_set_t *ils, iscsi_hba_t *ihp, boolean_t persist)
742 {
743 iscsi_login_params_t *params = NULL;
744 uchar_t *name = NULL;
745 iscsi_sess_t *isp = NULL;
746 iscsi_param_get_t *ilg;
747 int rtn = 0;
748 uint32_t event_count;
749
750 /* handle special case for Initiator name */
751 if (ils->s_param == ISCSI_LOGIN_PARAM_INITIATOR_NAME) {
752 (void) strlcpy((char *)ihp->hba_name,
753 (char *)ils->s_value.v_name, ISCSI_MAX_NAME_LEN);
754 if (persist) {
755 char *name;
756 boolean_t rval;
757
758 /* save off old Initiator name */
759 name = kmem_alloc(ISCSI_MAX_NAME_LEN, KM_SLEEP);
760 rval = persistent_initiator_name_get(name,
761 ISCSI_MAX_NAME_LEN);
762
763 (void) persistent_initiator_name_set(
764 (char *)ihp->hba_name);
765 if (rval == B_TRUE) {
766 /*
767 * check to see if we have login param,
768 * chap param, or authentication params
769 * loaded in persistent that we have to change
770 * the name of
771 */
772 persistent_param_t *pp;
773 iscsi_chap_props_t *chap;
774 iscsi_auth_props_t *auth;
775
776 /* checking login params */
777 pp = kmem_zalloc(sizeof (persistent_param_t),
778 KM_SLEEP);
779 if (persistent_param_get(name, pp)) {
780 rval = persistent_param_clear(name);
781 if (rval == B_TRUE) {
782 rval = persistent_param_set(
783 (char *)ihp->hba_name, pp);
784 }
785 if (rval == B_FALSE) {
786 rtn = EFAULT;
787 }
788 }
789 kmem_free(pp, sizeof (persistent_param_t));
790
791 /* check chap params */
792 chap = kmem_zalloc(sizeof (iscsi_chap_props_t),
793 KM_SLEEP);
794 if (persistent_chap_get(name, chap)) {
795 rval = persistent_chap_clear(name);
796 if (rval == B_TRUE) {
797 /*
798 * Update CHAP user name only if the
799 * original username was set to the
800 * initiator node name. Otherwise
801 * leave it the way it is.
802 */
803 int userSize;
804 userSize =
805 sizeof (chap->c_user);
806 if (strncmp((char *)
807 chap->c_user, name,
808 sizeof (chap->c_user))
809 == 0) {
810 bzero(chap->c_user,
811 userSize);
812 bcopy((char *)
813 ihp->hba_name,
814 chap->c_user,
815 strlen((char *)
816 ihp->hba_name));
817 chap->c_user_len =
818 strlen((char *)
819 ihp->hba_name);
820
821 }
822 rval = persistent_chap_set(
823 (char *)ihp->hba_name, chap);
824 }
825 if (rval == B_FALSE) {
826 rtn = EFAULT;
827 }
828 }
829 kmem_free(chap, sizeof (iscsi_chap_props_t));
830
831 /* check authentication params */
832 auth = kmem_zalloc(sizeof (iscsi_auth_props_t),
833 KM_SLEEP);
834 if (persistent_auth_get(name, auth)) {
835 rval = persistent_auth_clear(name);
836 if (rval == B_TRUE) {
837 rval = persistent_auth_set(
838 (char *)ihp->hba_name,
839 auth);
840 }
841 if (rval == B_FALSE) {
842 rtn = EFAULT;
843 }
844 }
845 kmem_free(auth, sizeof (iscsi_auth_props_t));
846 }
847 kmem_free(name, ISCSI_MAX_NAME_LEN);
848 }
849 } else if (ils->s_param == ISCSI_LOGIN_PARAM_INITIATOR_ALIAS) {
850 (void) strlcpy((char *)ihp->hba_alias,
851 (char *)ils->s_value.v_name, ISCSI_MAX_NAME_LEN);
852 ihp->hba_alias_length =
853 strlen((char *)ils->s_value.v_name);
854 if (persist) {
855 (void) persistent_alias_name_set(
856 (char *)ihp->hba_alias);
857 }
858 } else {
859 /* switch login based if looking for initiator params */
860 if (ils->s_oid == ihp->hba_oid) {
861 /* initiator */
862 params = &ihp->hba_params;
863 name = ihp->hba_name;
864 rtn = iscsi_set_param(params, ils);
865 } else {
866 /* session */
867 name = iscsi_targetparam_get_name(ils->s_oid);
868 if (name == NULL)
869 rtn = EFAULT;
870
871 if (persist && (rtn == 0)) {
872 boolean_t rval;
873 persistent_param_t *pp;
874
875 pp = (persistent_param_t *)
876 kmem_zalloc(sizeof (*pp), KM_SLEEP);
877 if (!persistent_param_get((char *)name, pp)) {
878 iscsi_set_default_login_params(
879 &pp->p_params);
880 }
881
882 pp->p_bitmap |= (1 << ils->s_param);
883 rtn = iscsi_set_param(&pp->p_params, ils);
884 if (rtn == 0) {
885 rval = persistent_param_set(
886 (char *)name, pp);
887 if (rval == B_FALSE) {
888 rtn = EFAULT;
889 }
890 }
891 kmem_free(pp, sizeof (*pp));
892 }
893
894 /*
895 * Here may have multiple sessions with different
896 * tpgt values. So it is needed to loop through
897 * the sessions and update all sessions.
898 */
899 if (rtn == 0) {
900 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER);
901 for (isp = ihp->hba_sess_list; isp;
902 isp = isp->sess_next) {
903 if (iscsiboot_prop &&
904 isp->sess_boot &&
905 iscsi_chk_bootlun_mpxio(ihp)) {
906 /*
907 * MPxIO is enabled so capable
908 * of changing. All changes
909 * will be applied later,
910 * after this function
911 */
912 continue;
913 }
914
915 if (strncmp((char *)isp->sess_name,
916 (char *)name,
917 ISCSI_MAX_NAME_LEN) == 0) {
918 event_count = atomic_inc_32_nv(&isp->sess_state_event_count);
919 iscsi_sess_enter_state_zone(isp);
920 iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N7, event_count);
921 iscsi_sess_exit_state_zone(isp);
922 }
923 }
924 rw_exit(&ihp->hba_sess_list_rwlock);
925 }
926
927 } /* end of 'else' */
928
929 if (params && persist && (rtn == 0)) {
930 boolean_t rval;
931 persistent_param_t *pp;
932
933 pp = (persistent_param_t *)
934 kmem_zalloc(sizeof (*pp), KM_SLEEP);
935 (void) persistent_param_get((char *)name, pp);
936 pp->p_bitmap |= (1 << ils->s_param);
937 bcopy(params, &pp->p_params, sizeof (*params));
938 rval = persistent_param_set((char *)name, pp);
939 if (rval == B_FALSE) {
940 rtn = EFAULT;
941 }
942 kmem_free(pp, sizeof (*pp));
943 }
944 /*
945 * if initiator parameter set, modify all associated
946 * sessions that don't already have the parameter
947 * overriden
948 */
949 if ((ils->s_oid == ihp->hba_oid) && (rtn == 0)) {
950 ilg = (iscsi_param_get_t *)
951 kmem_alloc(sizeof (*ilg), KM_SLEEP);
952
953 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER);
954 for (isp = ihp->hba_sess_list; isp;
955 isp = isp->sess_next) {
956 ilg->g_param = ils->s_param;
957 params = &isp->sess_params;
958 if (iscsi_get_persisted_param(
959 isp->sess_name, ilg, params) != 0) {
960 rtn = iscsi_set_param(params, ils);
961 if (rtn != 0) {
962 break;
963 }
964 if (iscsiboot_prop &&
965 isp->sess_boot &&
966 iscsi_chk_bootlun_mpxio(ihp)) {
967 /*
968 * MPxIO is enabled so capable
969 * of changing. Changes will
970 * be applied later, right
971 * after this function
972 */
973 continue;
974 }
975
976 /*
977 * Notify the session that
978 * the login parameters have
979 * changed.
980 */
981 event_count = atomic_inc_32_nv(
982 &isp->sess_state_event_count);
983 iscsi_sess_enter_state_zone(isp);
984 iscsi_sess_state_machine(isp,
985 ISCSI_SESS_EVENT_N7, event_count);
986 iscsi_sess_exit_state_zone(isp);
987 }
988 }
989 kmem_free(ilg, sizeof (*ilg));
990 rw_exit(&ihp->hba_sess_list_rwlock);
991 }
992 }
993 return (rtn);
994 }
995
996 int
iscsi_target_prop_mod(iscsi_hba_t * ihp,iscsi_property_t * ipp,int cmd)997 iscsi_target_prop_mod(iscsi_hba_t *ihp, iscsi_property_t *ipp, int cmd)
998 {
999 iscsi_sess_t *isp = NULL;
1000 iscsi_conn_t *icp;
1001 int rtn;
1002 char *name;
1003
1004 /*
1005 * If we're just attempting to get the target properties don't
1006 * create the session if it doesn't already exist. If we setting
1007 * the property then create the session if needed because we'll
1008 * most likely see an ISCSI_LOGIN in a few.
1009 */
1010 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER);
1011
1012 /*
1013 * If the oid does represent a session check to see
1014 * if it is a target oid. If so, return the target's
1015 * associated session.
1016 */
1017 rtn = iscsi_sess_get(ipp->p_oid, ihp, &isp);
1018 if (rtn != 0) {
1019 rtn = iscsi_sess_get_by_target(ipp->p_oid, ihp, &isp);
1020 }
1021
1022 /*
1023 * If rtn is zero then we have found an existing session.
1024 * Use the session name for database lookup. If rtn is
1025 * non-zero then create a targetparam object and use
1026 * its name for database lookup.
1027 */
1028 if (rtn == 0) {
1029 name = (char *)isp->sess_name;
1030 } else {
1031 name = (char *)iscsi_targetparam_get_name(ipp->p_oid);
1032 isp = NULL;
1033 }
1034
1035 if (name == NULL) {
1036 rw_exit(&ihp->hba_sess_list_rwlock);
1037 rtn = EFAULT;
1038 return (rtn);
1039 }
1040
1041 rtn = 0;
1042 if (cmd == ISCSI_TARGET_PROPS_GET) {
1043 /*
1044 * If isp is not null get the session's parameters, otherwise
1045 * the get is for a target-param object so defaults need to
1046 * be returned.
1047 */
1048 if (isp != NULL) {
1049 int conn_count = 0;
1050
1051 bcopy(isp->sess_alias, ipp->p_alias,
1052 isp->sess_alias_length);
1053 bcopy(isp->sess_name, ipp->p_name,
1054 isp->sess_name_length);
1055 ipp->p_alias_len = isp->sess_alias_length;
1056 ipp->p_name_len = isp->sess_name_length;
1057 ipp->p_discovery = isp->sess_discovered_by;
1058 ipp->p_last_err = isp->sess_last_err;
1059 ipp->p_tpgt_conf = isp->sess_tpgt_conf;
1060 ipp->p_tpgt_nego = isp->sess_tpgt_nego;
1061 bcopy(isp->sess_isid, ipp->p_isid, ISCSI_ISID_LEN);
1062
1063 rw_enter(&isp->sess_conn_list_rwlock, RW_READER);
1064 for (icp = isp->sess_conn_list; icp;
1065 icp = icp->conn_next) {
1066 if (icp->conn_state ==
1067 ISCSI_CONN_STATE_LOGGED_IN) {
1068 conn_count++;
1069 }
1070 }
1071 rw_exit(&isp->sess_conn_list_rwlock);
1072 ipp->p_num_of_connections = conn_count;
1073 ipp->p_connected = (conn_count > 0) ? B_TRUE : B_FALSE;
1074 } else {
1075 bcopy(name, ipp->p_name, strlen(name));
1076 ipp->p_name_len = strlen(name);
1077 bcopy("", ipp->p_alias, strlen(""));
1078 ipp->p_alias_len = strlen("");
1079 ipp->p_discovery = iSCSIDiscoveryMethodUnknown;
1080 ipp->p_last_err = NoError;
1081 ipp->p_tpgt_conf = ISCSI_DEFAULT_TPGT;
1082 ipp->p_tpgt_nego = ISCSI_DEFAULT_TPGT;
1083 ipp->p_num_of_connections = 0;
1084 ipp->p_connected = B_FALSE;
1085 }
1086 } else {
1087 if (isp == NULL) {
1088 rw_exit(&ihp->hba_sess_list_rwlock);
1089 rtn = EFAULT;
1090 return (rtn);
1091 }
1092
1093 /* ISCSI_TARGET_PROPS_SET */
1094 /*
1095 * only update if new, otherwise could clear out alias
1096 * if just updating the discovery.
1097 */
1098 if (ipp->p_alias_len != 0) {
1099 bcopy(ipp->p_alias, isp->sess_alias,
1100 ipp->p_alias_len);
1101 isp->sess_alias_length = ipp->p_alias_len;
1102 }
1103 isp->sess_discovered_by = ipp->p_discovery;
1104 }
1105 rw_exit(&ihp->hba_sess_list_rwlock);
1106 return (rtn);
1107 }
1108
1109 /*
1110 * iscsi_ioctl_get_config_sess - gets configured session information
1111 *
1112 * This function is an ioctl helper function to get the
1113 * configured session information from the persistent store.
1114 */
1115 int
iscsi_ioctl_get_config_sess(iscsi_hba_t * ihp,iscsi_config_sess_t * ics)1116 iscsi_ioctl_get_config_sess(iscsi_hba_t *ihp, iscsi_config_sess_t *ics)
1117 {
1118 uchar_t *name;
1119
1120 /* Get the matching iscsi node name for the oid */
1121 if (ics->ics_oid == ISCSI_INITIATOR_OID) {
1122 /* initiator name */
1123 name = ihp->hba_name;
1124 } else {
1125 /* target name */
1126 name = iscsi_targetparam_get_name(ics->ics_oid);
1127 if (name == NULL) {
1128 /* invalid node name */
1129 return (EINVAL);
1130 }
1131 }
1132
1133 /* get configured session information */
1134 if (persistent_get_config_session((char *)name, ics) == B_FALSE) {
1135 /*
1136 * There might not be anything in the database yet. If
1137 * this is a request for the target check the initiator
1138 * value. If neither is set return the default value.
1139 */
1140 if (ics->ics_oid != ISCSI_INITIATOR_OID) {
1141 if (persistent_get_config_session(
1142 (char *)ihp->hba_name, ics) == B_FALSE) {
1143 /*
1144 * No initiator value is set.
1145 * Return the defaults.
1146 */
1147 ics->ics_out = ISCSI_DEFAULT_SESS_NUM;
1148 ics->ics_bound = ISCSI_DEFAULT_SESS_BOUND;
1149 }
1150 } else {
1151 ics->ics_out = ISCSI_DEFAULT_SESS_NUM;
1152 ics->ics_bound = ISCSI_DEFAULT_SESS_BOUND;
1153 }
1154 }
1155
1156 return (0);
1157 }
1158
1159 /*
1160 * iscsi_ioctl_set_config_sess - sets configured session information
1161 *
1162 * This function is an ioctl helper function to set the
1163 * configured session information in the persistent store.
1164 * In addition it will notify any active sessions of the
1165 * changed so this can update binding information. It
1166 * will also destroy sessions that were removed and add
1167 * new sessions.
1168 */
1169 int
iscsi_ioctl_set_config_sess(iscsi_hba_t * ihp,iscsi_config_sess_t * ics)1170 iscsi_ioctl_set_config_sess(iscsi_hba_t *ihp, iscsi_config_sess_t *ics)
1171 {
1172 uchar_t *name;
1173 iscsi_sess_t *isp;
1174 uint32_t event_count;
1175
1176 /* check range infomration */
1177 if ((ics->ics_in < ISCSI_MIN_CONFIG_SESSIONS) ||
1178 (ics->ics_in > ISCSI_MAX_CONFIG_SESSIONS)) {
1179 /* invalid range information */
1180 return (EINVAL);
1181 }
1182
1183 if (ics->ics_oid == ISCSI_INITIATOR_OID) {
1184 name = ihp->hba_name;
1185 } else {
1186 /* get target name */
1187 name = iscsi_targetparam_get_name(ics->ics_oid);
1188 if (name == NULL) {
1189 /* invalid node name */
1190 return (EINVAL);
1191 }
1192 }
1193
1194 /* store the new information */
1195 if (persistent_set_config_session((char *)name, ics) == B_FALSE) {
1196 /* failed to store new information */
1197 return (EINVAL);
1198 }
1199
1200 /* notify existing sessions of change */
1201 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER);
1202 isp = ihp->hba_sess_list;
1203 while (isp != NULL) {
1204
1205 if ((ics->ics_oid == ISCSI_INITIATOR_OID) ||
1206 (strncmp((char *)isp->sess_name, (char *)name,
1207 ISCSI_MAX_NAME_LEN) == 0)) {
1208
1209 /*
1210 * If this sessions least signficant byte
1211 * of the isid is less than or equal to
1212 * the the number of configured sessions
1213 * then we need to tear down this session.
1214 */
1215 if (ics->ics_in <= isp->sess_isid[5]) {
1216 /* First attempt to destory the session */
1217 if (ISCSI_SUCCESS(iscsi_sess_destroy(isp))) {
1218 isp = ihp->hba_sess_list;
1219 } else {
1220 /*
1221 * If we can't destroy it then
1222 * atleast poke it to disconnect
1223 * it.
1224 */
1225 event_count = atomic_inc_32_nv(
1226 &isp->sess_state_event_count);
1227 iscsi_sess_enter_state_zone(isp);
1228 iscsi_sess_state_machine(isp,
1229 ISCSI_SESS_EVENT_N7, event_count);
1230 iscsi_sess_exit_state_zone(isp);
1231
1232 isp = isp->sess_next;
1233 }
1234 } else {
1235 isp = isp->sess_next;
1236 }
1237 } else {
1238 isp = isp->sess_next;
1239 }
1240 }
1241 rw_exit(&ihp->hba_sess_list_rwlock);
1242
1243 /*
1244 * The number of targets has changed. Since we don't expect
1245 * this to be a common operation lets keep the code simple and
1246 * just use a slightly larger hammer and poke discovery. This
1247 * force the reevaulation of this target and all other targets.
1248 */
1249 iscsid_poke_discovery(ihp, iSCSIDiscoveryMethodUnknown);
1250 /* lock so only one config operation occrs */
1251 sema_p(&iscsid_config_semaphore);
1252 iscsid_config_all(ihp, B_FALSE);
1253 sema_v(&iscsid_config_semaphore);
1254
1255 return (0);
1256 }
1257
1258 int
iscsi_ioctl_set_tunable_param(iscsi_hba_t * ihp,iscsi_tunable_object_t * tpss)1259 iscsi_ioctl_set_tunable_param(iscsi_hba_t *ihp, iscsi_tunable_object_t *tpss)
1260 {
1261 uchar_t *name;
1262 iscsi_sess_t *isp;
1263 iscsi_conn_t *icp;
1264 int param_id = 0;
1265 persistent_tunable_param_t *pparam;
1266
1267 if (tpss->t_oid == ihp->hba_oid) {
1268 name = ihp->hba_name;
1269 } else {
1270 /* get target name */
1271 name = iscsi_targetparam_get_name(tpss->t_oid);
1272 if (name == NULL) {
1273 /* invalid node name */
1274 return (EINVAL);
1275 }
1276 }
1277
1278 pparam = (persistent_tunable_param_t *)kmem_zalloc(sizeof (*pparam),
1279 KM_SLEEP);
1280 if (persistent_get_tunable_param((char *)name, pparam) == B_FALSE) {
1281 /* use default value */
1282 pparam->p_params.recv_login_rsp_timeout =
1283 ISCSI_DEFAULT_RX_TIMEOUT_VALUE;
1284 pparam->p_params.polling_login_delay =
1285 ISCSI_DEFAULT_LOGIN_POLLING_DELAY;
1286 pparam->p_params.conn_login_max =
1287 ISCSI_DEFAULT_CONN_DEFAULT_LOGIN_MAX;
1288 }
1289
1290 pparam->p_bitmap |= (1 << (tpss->t_param -1));
1291 param_id = 1 << (tpss->t_param -1);
1292 switch (param_id) {
1293 case ISCSI_TUNABLE_PARAM_RX_TIMEOUT_VALUE:
1294 pparam->p_params.recv_login_rsp_timeout =
1295 tpss->t_value.v_integer;
1296 break;
1297 case ISCSI_TUNABLE_PARAM_LOGIN_POLLING_DELAY:
1298 pparam->p_params.polling_login_delay =
1299 tpss->t_value.v_integer;
1300 break;
1301 case ISCSI_TUNABLE_PARAM_CONN_LOGIN_MAX:
1302 pparam->p_params.conn_login_max =
1303 tpss->t_value.v_integer;
1304 break;
1305 default:
1306 break;
1307 }
1308 if (persistent_set_tunable_param((char *)name,
1309 pparam) == B_FALSE) {
1310 kmem_free(pparam, sizeof (*pparam));
1311 return (EINVAL);
1312 }
1313
1314 if (tpss->t_oid == ihp->hba_oid) {
1315 bcopy(&pparam->p_params, &ihp->hba_tunable_params,
1316 sizeof (iscsi_tunable_params_t));
1317 }
1318
1319 rw_enter(&ihp->hba_sess_list_rwlock, RW_READER);
1320 for (isp = ihp->hba_sess_list; isp; isp = isp->sess_next) {
1321 if (isp->sess_type != ISCSI_SESS_TYPE_NORMAL) {
1322 continue;
1323 }
1324 rw_enter(&isp->sess_conn_list_rwlock, RW_READER);
1325 icp = isp->sess_conn_list;
1326 while (icp != NULL) {
1327 if (strcmp((const char *)name,
1328 (const char *)isp->sess_name) == 0) {
1329 bcopy(&pparam->p_params,
1330 &icp->conn_tunable_params,
1331 sizeof (iscsi_tunable_params_t));
1332 } else {
1333 /*
1334 * this session connected target
1335 * tunable parameters not set,
1336 * use initiator's default
1337 */
1338 bcopy(&ihp->hba_tunable_params,
1339 &icp->conn_tunable_params,
1340 sizeof (iscsi_tunable_params_t));
1341 }
1342 icp = icp->conn_next;
1343 }
1344 rw_exit(&isp->sess_conn_list_rwlock);
1345 }
1346 rw_exit(&ihp->hba_sess_list_rwlock);
1347 kmem_free(pparam, sizeof (*pparam));
1348 return (0);
1349 }
1350