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
41extern ib_boot_prop_t	*iscsiboot_prop;
42
43static 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 */
49void *
50iscsi_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 */
69int
70iscsi_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 */
85iscsi_conn_list_t *
86iscsi_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 */
117int
118iscsi_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 */
143boolean_t
144iscsi_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 */
233boolean_t
234iscsi_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 */
318int
319iscsi_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;
369retry_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 */
421static iscsi_status_t
422iscsi_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 */
634int
635iscsi_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
740int
741iscsi_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) {
918event_count = atomic_inc_32_nv(&isp->sess_state_event_count);
919iscsi_sess_enter_state_zone(isp);
920iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N7, event_count);
921iscsi_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
996int
997iscsi_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 */
1115int
1116iscsi_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 */
1169int
1170iscsi_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
1258int
1259iscsi_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