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