xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi_ioctl.c (revision 904e51f67bfac9f3ec88d9254757474c448808eb)
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