/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2019 Joshua M. Clulow */ /* * ISCSID -- * * Discovery of targets and access to the persistent storage starts here. */ #include #include #include /* declares: p0 */ #include #include #include #include "iscsi_targetparam.h" #include "isns_client.h" #include "isns_protocol.h" #include "persistent.h" #include "iscsi.h" #include #include /* * local function prototypes */ static boolean_t iscsid_init_config(iscsi_hba_t *ihp); static boolean_t iscsid_init_targets(iscsi_hba_t *ihp); static void iscsid_thread_static(iscsi_thread_t *thread, void *p); static void iscsid_thread_sendtgts(iscsi_thread_t *thread, void *p); static void iscsid_thread_isns(iscsi_thread_t *thread, void *p); static void iscsid_thread_slp(iscsi_thread_t *thread, void *p); static void iscsid_thread_boot_wd(iscsi_thread_t *thread, void *p); static void iscsid_threads_create(iscsi_hba_t *ihp); static void iscsid_threads_destroy(void); static int iscsid_copyto_param_set(uint32_t param_id, iscsi_login_params_t *params, iscsi_param_set_t *ipsp); static void iscsid_add_pg_list_to_cache(iscsi_hba_t *ihp, isns_portal_group_list_t *pg_list); static void iscsid_remove_target_param(char *name); static boolean_t iscsid_add(iscsi_hba_t *ihp, iSCSIDiscoveryMethod_t method, struct sockaddr *addr_dsc, char *target_name, int tpgt, struct sockaddr *addr_tgt); static void iscsi_discovery_event(iscsi_hba_t *ihp, iSCSIDiscoveryMethod_t m, boolean_t start); static boolean_t iscsid_boot_init_config(iscsi_hba_t *ihp); static iscsi_sess_t *iscsi_add_boot_sess(iscsi_hba_t *ihp, int isid); static boolean_t iscsid_make_entry(ib_boot_prop_t *boot_prop_entry, entry_t *entry); static boolean_t iscsid_check_active_boot_conn(iscsi_hba_t *ihp); extern int modrootloaded; int iscsi_configroot_retry = 20; static boolean_t iscsi_configroot_printed = FALSE; static int iscsi_net_up = 0; extern ib_boot_prop_t *iscsiboot_prop; #define ISCSI_CONFIGROOT_DELAY 1 /* * iSCSI target discovery thread table */ typedef struct iscsid_thr_table { void (*func_start)(iscsi_thread_t *, void *); iscsi_thread_t *thr_id; iSCSIDiscoveryMethod_t method; char *name; } iscsid_thr_table; static iscsid_thr_table iscsid_thr[] = { { iscsid_thread_static, NULL, iSCSIDiscoveryMethodStatic, "Static" }, { iscsid_thread_sendtgts, NULL, iSCSIDiscoveryMethodSendTargets, "SendTarget" }, { iscsid_thread_slp, NULL, iSCSIDiscoveryMethodSLP, "SLP" }, { iscsid_thread_isns, NULL, iSCSIDiscoveryMethodISNS, "iSNS" }, { NULL, NULL, iSCSIDiscoveryMethodUnknown, NULL } }; /* * discovery method event table */ iSCSIDiscoveryMethod_t for_failure[] = { iSCSIDiscoveryMethodStatic, iSCSIDiscoveryMethodSLP, iSCSIDiscoveryMethodISNS, iSCSIDiscoveryMethodSendTargets, iSCSIDiscoveryMethodUnknown /* terminating value */ }; /* * The following private tunable, set in /etc/system, e.g., * set iscsi:iscsi_boot_max_delay = 360 * , provides with customer a max wait time in * seconds to wait for boot lun online during iscsi boot. * Defaults to 180s. */ int iscsi_boot_max_delay = ISCSI_BOOT_DEFAULT_MAX_DELAY; /* * discovery configuration semaphore */ ksema_t iscsid_config_semaphore; static iscsi_thread_t *iscsi_boot_wd_handle = NULL; #define CHECK_METHOD(v) ((dm & v) ? B_TRUE : B_FALSE) /* * Check if IP is valid */ static boolean_t iscsid_ip_check(char *ip) { int i = 0; if (!ip) return (B_FALSE); for (; (ip[i] == 0) && (i < IB_IP_BUFLEN); i++) {} if (i == IB_IP_BUFLEN) { /* invalid IP address */ return (B_FALSE); } return (B_TRUE); } /* * Make an entry for the boot target. * return B_TRUE upon success * B_FALSE if fail */ static boolean_t iscsid_make_entry(ib_boot_prop_t *boot_prop_entry, entry_t *entry) { if (entry == NULL || boot_prop_entry == NULL) { return (B_FALSE); } if (!iscsid_ip_check( (char *)&boot_prop_entry->boot_tgt.tgt_ip_u)) return (B_FALSE); if (boot_prop_entry->boot_tgt.sin_family != AF_INET && boot_prop_entry->boot_tgt.sin_family != AF_INET6) return (B_FALSE); entry->e_vers = ISCSI_INTERFACE_VERSION; mutex_enter(&iscsi_oid_mutex); entry->e_oid = iscsi_oid++; mutex_exit(&iscsi_oid_mutex); entry->e_tpgt = ISCSI_DEFAULT_TPGT; if (boot_prop_entry->boot_tgt.sin_family == AF_INET) { entry->e_u.u_in4.s_addr = boot_prop_entry->boot_tgt.tgt_ip_u.u_in4.s_addr; entry->e_insize = sizeof (struct in_addr); } else { (void) bcopy( &boot_prop_entry->boot_tgt.tgt_ip_u.u_in6.s6_addr, entry->e_u.u_in6.s6_addr, 16); entry->e_insize = sizeof (struct in6_addr); } entry->e_port = boot_prop_entry->boot_tgt.tgt_port; entry->e_boot = B_TRUE; return (B_TRUE); } /* * Create the boot session */ static void iscsi_boot_session_create(iscsi_hba_t *ihp, ib_boot_prop_t *boot_prop_table) { iSCSIDiscoveryMethod_t dm; entry_t e; iscsi_sockaddr_t addr_dsc; if (ihp == NULL || boot_prop_table == NULL) { return; } if (!iscsid_ip_check( (char *)&boot_prop_table->boot_tgt.tgt_ip_u)) { return; } if (boot_prop_table->boot_tgt.tgt_name != NULL) { dm = iSCSIDiscoveryMethodStatic | iSCSIDiscoveryMethodBoot; if (!iscsid_make_entry(boot_prop_table, &e)) return; iscsid_addr_to_sockaddr(e.e_insize, &e.e_u, e.e_port, &addr_dsc.sin); (void) iscsid_add(ihp, dm, &addr_dsc.sin, (char *)boot_prop_table->boot_tgt.tgt_name, e.e_tpgt, &addr_dsc.sin); } else { dm = iSCSIDiscoveryMethodSendTargets | iSCSIDiscoveryMethodBoot; if (!iscsid_make_entry(boot_prop_table, &e)) return; iscsid_addr_to_sockaddr(e.e_insize, &e.e_u, e.e_port, &addr_dsc.sin); iscsid_do_sendtgts(&e); (void) iscsid_login_tgt(ihp, NULL, dm, &addr_dsc.sin); } } /* * iscsid_init -- to initialize stuffs related to iscsi daemon, * and to create boot session if needed */ boolean_t iscsid_init(iscsi_hba_t *ihp) { boolean_t rval = B_TRUE; sema_init(&iscsid_config_semaphore, 1, NULL, SEMA_DRIVER, NULL); persistent_init(); iscsid_threads_create(ihp); if (modrootloaded) { /* * The root file system is available so we can load the * persistent store. */ if (persistent_load() == B_TRUE) { ihp->hba_persistent_loaded = B_TRUE; } else { return (B_FALSE); } } else { /* * If the root file system is not yet mounted then we _must_ be * booting from an iSCSI device. If not, we want to fail to * attach so that we can try again after the VFS root is * available. */ if (iscsiboot_prop == NULL) { return (B_FALSE); } if (!iscsid_boot_init_config(ihp)) { rval = B_FALSE; } else { iscsi_boot_session_create(ihp, iscsiboot_prop); iscsi_boot_wd_handle = iscsi_thread_create(ihp->hba_dip, "BootWD", iscsid_thread_boot_wd, ihp); if (iscsi_boot_wd_handle != NULL) { rval = iscsi_thread_start( iscsi_boot_wd_handle); } else { rval = B_FALSE; } } if (rval == B_FALSE) { cmn_err(CE_NOTE, "Initializaton of iscsi boot session" " partially failed"); } } return (rval); } /* * iscsid_start -- start the iscsi initiator daemon, actually this code * is just to enable discovery methods which are set enabled in * persistent store, as an economic way to present the 'daemon' funtionality */ boolean_t iscsid_start(iscsi_hba_t *ihp) { boolean_t rval = B_FALSE; iSCSIDiscoveryMethod_t dm; iSCSIDiscoveryMethod_t *fdm; rval = iscsid_init_config(ihp); if (rval == B_TRUE) { rval = iscsid_init_targets(ihp); } if (rval == B_TRUE) { dm = persistent_disc_meth_get(); rval = iscsid_enable_discovery(ihp, dm, B_TRUE); if (rval == B_TRUE) { iscsid_poke_discovery(ihp, iSCSIDiscoveryMethodUnknown); (void) iscsid_login_tgt(ihp, NULL, iSCSIDiscoveryMethodUnknown, NULL); } } if (rval == B_FALSE) { /* * In case of failure the events still need to be sent * because the door daemon will pause until all these * events have occurred. */ for (fdm = &for_failure[0]; *fdm != iSCSIDiscoveryMethodUnknown; fdm++) { /* ---- Send both start and end events ---- */ iscsi_discovery_event(ihp, *fdm, B_TRUE); iscsi_discovery_event(ihp, *fdm, B_FALSE); } } return (rval); } /* * iscsid_stop -- stop the iscsi initiator daemon, by disabling * all the discovery methods first, and then try to stop all * related threads. This is a try-best effort, leave any 'busy' device * (and therefore session) there and just return. */ boolean_t iscsid_stop(iscsi_hba_t *ihp) { boolean_t rval = B_FALSE; iscsi_sess_t *isp = NULL; (void) iscsid_disable_discovery(ihp, ISCSI_ALL_DISCOVERY_METHODS); /* final check */ rw_enter(&ihp->hba_sess_list_rwlock, RW_READER); if (ihp->hba_sess_list == NULL) { rval = B_TRUE; } else { /* * If only boot session is left, that is OK. * Otherwise, we should report that some sessions are left. */ rval = B_TRUE; for (isp = ihp->hba_sess_list; isp != NULL; isp = isp->sess_next) { if (isp->sess_boot == B_FALSE) { rval = B_FALSE; break; } } } rw_exit(&ihp->hba_sess_list_rwlock); return (rval); } /* * iscsid_fini -- do whatever is required to clean up */ /* ARGSUSED */ void iscsid_fini() { if (iscsi_boot_wd_handle != NULL) { iscsi_thread_destroy(iscsi_boot_wd_handle); iscsi_boot_wd_handle = NULL; } iscsid_threads_destroy(); persistent_fini(); sema_destroy(&iscsid_config_semaphore); } /* * iscsid_props -- returns discovery thread information, used by ioctl code */ void iscsid_props(iSCSIDiscoveryProperties_t *props) { iSCSIDiscoveryMethod_t dm; dm = persistent_disc_meth_get(); props->vers = ISCSI_INTERFACE_VERSION; /* ---- change once thread is implemented ---- */ props->iSNSDiscoverySettable = B_FALSE; props->SLPDiscoverySettable = B_FALSE; props->StaticDiscoverySettable = B_TRUE; props->SendTargetsDiscoverySettable = B_TRUE; props->iSNSDiscoveryMethod = iSNSDiscoveryMethodStatic; props->iSNSDiscoveryEnabled = CHECK_METHOD(iSCSIDiscoveryMethodISNS); props->StaticDiscoveryEnabled = CHECK_METHOD(iSCSIDiscoveryMethodStatic); props->SendTargetsDiscoveryEnabled = CHECK_METHOD(iSCSIDiscoveryMethodSendTargets); props->SLPDiscoveryEnabled = CHECK_METHOD(iSCSIDiscoveryMethodSLP); } /* * iscsid_enable_discovery - start specified discovery methods */ /* ARGSUSED */ boolean_t iscsid_enable_discovery(iscsi_hba_t *ihp, iSCSIDiscoveryMethod_t idm, boolean_t poke) { boolean_t rval = B_TRUE; iscsid_thr_table *dt; /* * start the specified discovery method(s) */ for (dt = &iscsid_thr[0]; dt->method != iSCSIDiscoveryMethodUnknown; dt++) { if (idm & dt->method) { if (dt->thr_id != NULL) { rval = iscsi_thread_start(dt->thr_id); if (rval == B_FALSE) { break; } if (poke == B_TRUE) { (void) iscsi_thread_send_wakeup( dt->thr_id); } } else { /* * unexpected condition. The threads for each * discovery method should have started at * initialization */ ASSERT(B_FALSE); } } } /* END for() */ return (rval); } /* * iscsid_disable_discovery - stop specified discovery methods */ boolean_t iscsid_disable_discovery(iscsi_hba_t *ihp, iSCSIDiscoveryMethod_t idm) { boolean_t rval = B_TRUE; iscsid_thr_table *dt; /* * stop the specified discovery method(s) */ for (dt = &iscsid_thr[0]; dt->method != iSCSIDiscoveryMethodUnknown; dt++) { if (idm & dt->method) { /* signal discovery event change - begin */ iscsi_discovery_event(ihp, dt->method, B_TRUE); /* Attempt to logout of all associated targets */ rval = iscsid_del(ihp, NULL, dt->method, NULL); if (rval == B_TRUE) { /* Successfully logged out of targets */ if (dt->thr_id != NULL) { rval = iscsi_thread_stop(dt->thr_id); if (rval == B_FALSE) { /* * signal discovery * event change - end */ iscsi_discovery_event(ihp, dt->method, B_FALSE); break; } } else { /* * unexpected condition. The threads * for each discovery method should * have started at initialization */ ASSERT(B_FALSE); } } /* signal discovery event change - end */ iscsi_discovery_event(ihp, dt->method, B_FALSE); } } /* END for() */ return (rval); } /* * iscsid_poke_discovery - wakeup discovery methods to find any new targets * and wait for all discovery processes to complete. */ void iscsid_poke_discovery(iscsi_hba_t *ihp, iSCSIDiscoveryMethod_t method) { #define ISCSI_DISCOVERY_DELAY 1 iSCSIDiscoveryMethod_t dm; iscsid_thr_table *dt; boolean_t send_wakeup; ASSERT(ihp != NULL); /* reset discovery flags */ mutex_enter(&ihp->hba_discovery_events_mutex); ihp->hba_discovery_in_progress = B_TRUE; ihp->hba_discovery_events = iSCSIDiscoveryMethodUnknown; mutex_exit(&ihp->hba_discovery_events_mutex); /* start all enabled discovery methods */ dm = persistent_disc_meth_get(); for (dt = &iscsid_thr[0]; dt->method != iSCSIDiscoveryMethodUnknown; dt++) { send_wakeup = B_FALSE; if ((method == iSCSIDiscoveryMethodUnknown) || (method == dt->method)) { if ((dm & dt->method) && (dt->thr_id != NULL)) { if (iscsi_thread_send_wakeup(dt->thr_id) == B_TRUE) { send_wakeup = B_TRUE; } } } if (send_wakeup == B_FALSE) { iscsi_discovery_event(ihp, dt->method, B_TRUE); iscsi_discovery_event(ihp, dt->method, B_FALSE); } } mutex_enter(&ihp->hba_discovery_events_mutex); while (ihp->hba_discovery_events != ISCSI_ALL_DISCOVERY_METHODS) { mutex_exit(&ihp->hba_discovery_events_mutex); delay(SEC_TO_TICK(ISCSI_DISCOVERY_DELAY)); mutex_enter(&ihp->hba_discovery_events_mutex); } ihp->hba_discovery_in_progress = B_FALSE; mutex_exit(&ihp->hba_discovery_events_mutex); } /* * iscsid_do_sendtgts - issue send targets command to the given discovery * address and then add the discovered targets to the discovery queue */ void iscsid_do_sendtgts(entry_t *disc_addr) { #define SENDTGTS_DEFAULT_NUM_TARGETS 10 int stl_sz; int stl_num_tgts = SENDTGTS_DEFAULT_NUM_TARGETS; iscsi_sendtgts_list_t *stl_hdr = NULL; boolean_t retry = B_TRUE; char inp_buf[INET6_ADDRSTRLEN]; const char *ip; int ctr; int rc; iscsi_hba_t *ihp; iSCSIDiscoveryMethod_t dm = iSCSIDiscoveryMethodSendTargets; /* allocate and initialize sendtargets list header */ stl_sz = sizeof (*stl_hdr) + ((stl_num_tgts - 1) * sizeof (iscsi_sendtgts_entry_t)); stl_hdr = kmem_zalloc(stl_sz, KM_SLEEP); retry_sendtgts: stl_hdr->stl_in_cnt = stl_num_tgts; bcopy(disc_addr, &(stl_hdr->stl_entry), sizeof (stl_hdr->stl_entry)); stl_hdr->stl_entry.e_vers = ISCSI_INTERFACE_VERSION; /* lock interface so only one SendTargets operation occurs */ if ((ihp = (iscsi_hba_t *)ddi_get_soft_state(iscsi_state, 0)) == NULL) { cmn_err(CE_NOTE, "!iscsi discovery failure - SendTargets. " "failure to get soft state"); kmem_free(stl_hdr, stl_sz); return; } sema_p(&ihp->hba_sendtgts_semaphore); rc = iscsi_ioctl_sendtgts_get(ihp, stl_hdr); sema_v(&ihp->hba_sendtgts_semaphore); if (rc) { ip = inet_ntop((disc_addr->e_insize == sizeof (struct in_addr) ? AF_INET : AF_INET6), &disc_addr->e_u, inp_buf, sizeof (inp_buf)); cmn_err(CE_NOTE, "iscsi discovery failure - SendTargets (%s)\n", ip); kmem_free(stl_hdr, stl_sz); return; } /* check if all targets received */ if (stl_hdr->stl_in_cnt < stl_hdr->stl_out_cnt) { if (retry == B_TRUE) { stl_num_tgts = stl_hdr->stl_out_cnt; kmem_free(stl_hdr, stl_sz); stl_sz = sizeof (*stl_hdr) + ((stl_num_tgts - 1) * sizeof (iscsi_sendtgts_entry_t)); stl_hdr = kmem_zalloc(stl_sz, KM_SLEEP); retry = B_FALSE; goto retry_sendtgts; } else { ip = inet_ntop((disc_addr->e_insize == sizeof (struct in_addr) ? AF_INET : AF_INET6), &disc_addr->e_u, inp_buf, sizeof (inp_buf)); cmn_err(CE_NOTE, "iscsi discovery failure - " "SendTargets overflow (%s)\n", ip); kmem_free(stl_hdr, stl_sz); return; } } for (ctr = 0; ctr < stl_hdr->stl_out_cnt; ctr++) { iscsi_sockaddr_t addr_dsc; iscsi_sockaddr_t addr_tgt; iscsid_addr_to_sockaddr(disc_addr->e_insize, &disc_addr->e_u, disc_addr->e_port, &addr_dsc.sin); iscsid_addr_to_sockaddr( stl_hdr->stl_list[ctr].ste_ipaddr.a_addr.i_insize, &(stl_hdr->stl_list[ctr].ste_ipaddr.a_addr.i_addr), stl_hdr->stl_list[ctr].ste_ipaddr.a_port, &addr_tgt.sin); if (disc_addr->e_boot == B_TRUE) { dm = dm | iSCSIDiscoveryMethodBoot; } (void) iscsid_add(ihp, dm, &addr_dsc.sin, (char *)stl_hdr->stl_list[ctr].ste_name, stl_hdr->stl_list[ctr].ste_tpgt, &addr_tgt.sin); } kmem_free(stl_hdr, stl_sz); } void iscsid_do_isns_query_one_server(iscsi_hba_t *ihp, entry_t *isns_server) { int pg_sz, query_status; iscsi_addr_t *ap; isns_portal_group_list_t *pg_list; ap = (iscsi_addr_t *)kmem_zalloc(sizeof (iscsi_addr_t), KM_SLEEP); ap->a_port = isns_server->e_port; ap->a_addr.i_insize = isns_server->e_insize; if (isns_server->e_insize == sizeof (struct in_addr)) { ap->a_addr.i_addr.in4.s_addr = (isns_server->e_u.u_in4.s_addr); } else if (isns_server->e_insize == sizeof (struct in6_addr)) { bcopy(&(isns_server->e_u.u_in6.s6_addr), ap->a_addr.i_addr.in6.s6_addr, 16); } else { kmem_free(ap, sizeof (iscsi_addr_t)); return; } pg_list = NULL; query_status = isns_query_one_server( ap, ihp->hba_isid, ihp->hba_name, ihp->hba_alias, ISNS_INITIATOR_NODE_TYPE, &pg_list); kmem_free(ap, sizeof (iscsi_addr_t)); if (query_status != isns_ok || pg_list == NULL) { DTRACE_PROBE1(iscsid_do_isns_query_one_server_status, int, query_status); return; } iscsid_add_pg_list_to_cache(ihp, pg_list); pg_sz = sizeof (isns_portal_group_list_t); if (pg_list->pg_out_cnt > 0) { pg_sz += (pg_list->pg_out_cnt - 1) * sizeof (isns_portal_group_t); } kmem_free(pg_list, pg_sz); } void iscsid_do_isns_query(iscsi_hba_t *ihp) { int pg_sz, query_status; isns_portal_group_list_t *pg_list; pg_list = NULL; query_status = isns_query(ihp->hba_isid, ihp->hba_name, ihp->hba_alias, ISNS_INITIATOR_NODE_TYPE, &pg_list); if (pg_list == NULL) { DTRACE_PROBE1(iscsid_do_isns_query_status, int, query_status); return; } if ((query_status != isns_ok && query_status != isns_op_partially_failed)) { DTRACE_PROBE1(iscsid_do_isns_query_status, int, query_status); pg_sz = sizeof (isns_portal_group_list_t); if (pg_list->pg_out_cnt > 0) { pg_sz += (pg_list->pg_out_cnt - 1) * sizeof (isns_portal_group_t); } kmem_free(pg_list, pg_sz); return; } iscsid_add_pg_list_to_cache(ihp, pg_list); pg_sz = sizeof (isns_portal_group_list_t); if (pg_list->pg_out_cnt > 0) { pg_sz += (pg_list->pg_out_cnt - 1) * sizeof (isns_portal_group_t); } kmem_free(pg_list, pg_sz); } /* * iscsid_config_one - for the given target name, attempt * to login to all targets associated with name. If target * name is not found in discovery queue, reset the discovery * queue, kick the discovery processes, and then retry. * * NOTE: The caller of this function must hold the * iscsid_config_semaphore across this call. */ void iscsid_config_one(iscsi_hba_t *ihp, char *name, boolean_t protect) { boolean_t rc = B_FALSE; int retry = 0; int lun_online = 0; int cur_sec = 0; if (!modrootloaded && (iscsiboot_prop != NULL)) { if (!iscsi_configroot_printed) { cmn_err(CE_NOTE, "Configuring" " iSCSI boot session..."); iscsi_configroot_printed = B_TRUE; } if (iscsi_net_up == 0) { if (iscsi_net_interface(B_FALSE) == ISCSI_STATUS_SUCCESS) { iscsi_net_up = 1; } else { cmn_err(CE_WARN, "Failed to configure interface" " for iSCSI boot session"); return; } } while (rc == B_FALSE && retry < iscsi_configroot_retry) { rc = iscsid_login_tgt(ihp, name, iSCSIDiscoveryMethodBoot, NULL); if (rc == B_FALSE) { /* * create boot session */ iscsi_boot_session_create(ihp, iscsiboot_prop); retry++; continue; } rc = iscsid_check_active_boot_conn(ihp); if (rc == B_FALSE) { /* * no active connection for the boot * session, retry the login until * one is found or the retry count * is exceeded */ delay(SEC_TO_TICK(ISCSI_CONFIGROOT_DELAY)); retry++; continue; } /* * The boot session has been created with active * connection. If the target lun has not been online, * we should wait here for a while */ do { lun_online = iscsiboot_prop->boot_tgt.lun_online; if (lun_online == 0) { delay(SEC_TO_TICK( ISCSI_CONFIGROOT_DELAY)); cur_sec++; } } while ((lun_online == 0) && (cur_sec < iscsi_boot_max_delay)); retry++; } if (!rc) { cmn_err(CE_WARN, "Failed to configure iSCSI" " boot session"); } } else { rc = iscsid_login_tgt(ihp, name, iSCSIDiscoveryMethodUnknown, NULL); /* * If we didn't login to the device we might have * to update our discovery information and attempt * the login again. */ if (rc == B_FALSE) { /* * Stale /dev links can cause us to get floods * of config requests. Prevent these repeated * requests from causing unneeded discovery updates * if ISCSI_CONFIG_STORM_PROTECT is set. */ if ((protect == B_FALSE) || (ddi_get_lbolt() > ihp->hba_config_lbolt + SEC_TO_TICK(ihp->hba_config_storm_delay))) { ihp->hba_config_lbolt = ddi_get_lbolt(); iscsid_poke_discovery(ihp, iSCSIDiscoveryMethodUnknown); (void) iscsid_login_tgt(ihp, name, iSCSIDiscoveryMethodUnknown, NULL); } } } } /* * iscsid_config_all - reset the discovery queue, kick the * discovery processes, and login to all targets found * * NOTE: The caller of this function must hold the * iscsid_config_semaphore across this call. */ void iscsid_config_all(iscsi_hba_t *ihp, boolean_t protect) { boolean_t rc = B_FALSE; int retry = 0; int lun_online = 0; int cur_sec = 0; if (!modrootloaded && iscsiboot_prop != NULL) { if (!iscsi_configroot_printed) { cmn_err(CE_NOTE, "Configuring" " iSCSI boot session..."); iscsi_configroot_printed = B_TRUE; } if (iscsi_net_up == 0) { if (iscsi_net_interface(B_FALSE) == ISCSI_STATUS_SUCCESS) { iscsi_net_up = 1; } } while (rc == B_FALSE && retry < iscsi_configroot_retry) { rc = iscsid_login_tgt(ihp, NULL, iSCSIDiscoveryMethodBoot, NULL); if (rc == B_FALSE) { /* * No boot session has been created. * We would like to create the boot * Session first. */ iscsi_boot_session_create(ihp, iscsiboot_prop); retry++; continue; } rc = iscsid_check_active_boot_conn(ihp); if (rc == B_FALSE) { /* * no active connection for the boot * session, retry the login until * one is found or the retry count * is exceeded */ delay(SEC_TO_TICK(ISCSI_CONFIGROOT_DELAY)); retry++; continue; } /* * The boot session has been created with active * connection. If the target lun has not been online, * we should wait here for a while */ do { lun_online = iscsiboot_prop->boot_tgt.lun_online; if (lun_online == 0) { delay(SEC_TO_TICK( ISCSI_CONFIGROOT_DELAY)); cur_sec++; } } while ((lun_online == 0) && (cur_sec < iscsi_boot_max_delay)); retry++; } if (!rc) { cmn_err(CE_WARN, "Failed to configure" " boot session"); } } else { /* * Stale /dev links can cause us to get floods * of config requests. Prevent these repeated * requests from causing unneeded discovery updates * if ISCSI_CONFIG_STORM_PROTECT is set. */ if ((protect == B_FALSE) || (ddi_get_lbolt() > ihp->hba_config_lbolt + SEC_TO_TICK(ihp->hba_config_storm_delay))) { ihp->hba_config_lbolt = ddi_get_lbolt(); iscsid_poke_discovery(ihp, iSCSIDiscoveryMethodUnknown); } (void) iscsid_login_tgt(ihp, NULL, iSCSIDiscoveryMethodUnknown, NULL); } } /* * isns_scn_callback - iSNS client received an SCN * * This code processes the iSNS client SCN events. These * could relate to the addition, removal, or update of a * logical unit. */ void isns_scn_callback(void *arg) { int i, pg_sz; int qry_status; isns_portal_group_list_t *pg_list; uint32_t scn_type; iscsi_hba_t *ihp; if (arg == NULL) { /* No argument */ return; } if ((ihp = (iscsi_hba_t *)ddi_get_soft_state(iscsi_state, 0)) == NULL) { kmem_free(arg, sizeof (isns_scn_callback_arg_t)); return; } /* * All isns callbacks are from a standalone taskq * therefore the blocking here doesn't affect the enable/disable * of isns discovery method */ if (iscsi_client_request_service(ihp) == B_FALSE) { kmem_free(arg, sizeof (isns_scn_callback_arg_t)); return; } scn_type = ((isns_scn_callback_arg_t *)arg)->scn_type; DTRACE_PROBE1(isns_scn_callback_scn_type, int, scn_type); switch (scn_type) { /* * ISNS_OBJ_ADDED - An object has been added. */ case ISNS_OBJ_ADDED: /* Query iSNS server for contact information */ pg_list = NULL; qry_status = isns_query_one_node( ((isns_scn_callback_arg_t *)arg)->source_key_attr, ihp->hba_isid, ihp->hba_name, (uint8_t *)"", ISNS_INITIATOR_NODE_TYPE, &pg_list); /* Verify portal group is found */ if ((qry_status != isns_ok && qry_status != isns_op_partially_failed) || pg_list == NULL) { break; } DTRACE_PROBE1(pg_list, isns_portal_group_list_t *, pg_list); /* Add all portals for logical unit to discovery cache */ for (i = 0; i < pg_list->pg_out_cnt; i++) { iscsi_sockaddr_t addr_dsc; iscsi_sockaddr_t addr_tgt; iscsid_addr_to_sockaddr( pg_list->pg_list[i].isns_server_ip.i_insize, &pg_list->pg_list[i].isns_server_ip.i_addr, pg_list->pg_list[i].isns_server_port, &addr_dsc.sin); iscsid_addr_to_sockaddr(pg_list->pg_list[i].insize, &pg_list->pg_list[i].pg_ip_addr, pg_list->pg_list[i].pg_port, &addr_tgt.sin); (void) iscsid_add(ihp, iSCSIDiscoveryMethodISNS, &addr_dsc.sin, (char *)pg_list->pg_list[i]. pg_iscsi_name, pg_list->pg_list[i].pg_tag, &addr_tgt.sin); /* Force target to login */ (void) iscsid_login_tgt(ihp, (char *)pg_list-> pg_list[i].pg_iscsi_name, iSCSIDiscoveryMethodISNS, NULL); } if (pg_list != NULL) { pg_sz = sizeof (isns_portal_group_list_t); if (pg_list->pg_out_cnt > 0) { pg_sz += (pg_list->pg_out_cnt - 1) * sizeof (isns_portal_group_t); } kmem_free(pg_list, pg_sz); } break; /* * ISNS_OBJ_REMOVED - logical unit has been removed */ case ISNS_OBJ_REMOVED: if (iscsid_del(ihp, (char *)((isns_scn_callback_arg_t *)arg)-> source_key_attr, iSCSIDiscoveryMethodISNS, NULL) != B_TRUE) { cmn_err(CE_NOTE, "iscsi initiator - " "isns remove scn failed for target %s\n", (char *)((isns_scn_callback_arg_t *)arg)-> source_key_attr); } break; /* * ISNS_OBJ_UPDATED - logical unit has changed */ case ISNS_OBJ_UPDATED: cmn_err(CE_NOTE, "iscsi initiator - " "received iSNS update SCN for %s\n", (char *)((isns_scn_callback_arg_t *)arg)-> source_key_attr); break; /* * ISNS_OBJ_UNKNOWN - */ default: cmn_err(CE_NOTE, "iscsi initiator - " "received unknown iSNS SCN type 0x%x\n", scn_type); break; } iscsi_client_release_service(ihp); kmem_free(arg, sizeof (isns_scn_callback_arg_t)); } /* * iscsid_add - Creates discovered session and connection */ static boolean_t iscsid_add(iscsi_hba_t *ihp, iSCSIDiscoveryMethod_t method, struct sockaddr *addr_dsc, char *target_name, int tpgt, struct sockaddr *addr_tgt) { boolean_t rtn = B_TRUE; iscsi_sess_t *isp; iscsi_conn_t *icp; uint_t oid; int idx; int isid; iscsi_config_sess_t *ics; int size; char *tmp; ASSERT(ihp != NULL); ASSERT(addr_dsc != NULL); ASSERT(target_name != NULL); ASSERT(addr_tgt != NULL); /* setup initial buffer for configured session information */ size = sizeof (*ics); ics = kmem_zalloc(size, KM_SLEEP); ics->ics_in = 1; /* get configured sessions information */ tmp = target_name; if (persistent_get_config_session(tmp, ics) == B_FALSE) { /* * No target information available check for * initiator information. */ tmp = (char *)ihp->hba_name; if (persistent_get_config_session(tmp, ics) == B_FALSE) { /* * No hba information is * found. So assume default * one session unbound behavior. */ ics->ics_out = 1; ics->ics_bound = B_TRUE; } } if (iscsiboot_prop && (ics->ics_out > 1) && !iscsi_chk_bootlun_mpxio(ihp)) { /* * iscsi boot with mpxio disabled * no need to search configured boot session */ if (iscsi_cmp_boot_ini_name(tmp) || iscsi_cmp_boot_tgt_name(tmp)) { ics->ics_out = 1; ics->ics_bound = B_FALSE; } } /* Check to see if we need to get more information */ if (ics->ics_out > 1) { /* record new size and free last buffer */ idx = ics->ics_out; size = ISCSI_SESSION_CONFIG_SIZE(ics->ics_out); kmem_free(ics, sizeof (*ics)); /* allocate new buffer */ ics = kmem_zalloc(size, KM_SLEEP); ics->ics_in = idx; /* get configured sessions information */ if (persistent_get_config_session(tmp, ics) != B_TRUE) { cmn_err(CE_NOTE, "iscsi session(%s) - " "unable to get configured session information\n", target_name); kmem_free(ics, size); return (B_FALSE); } } /* loop for all configured sessions */ rw_enter(&ihp->hba_sess_list_rwlock, RW_WRITER); for (isid = 0; isid < ics->ics_out; isid++) { /* create or find matching session */ isp = iscsi_sess_create(ihp, method, addr_dsc, target_name, tpgt, isid, ISCSI_SESS_TYPE_NORMAL, &oid); if (isp == NULL) { rtn = B_FALSE; break; } /* create or find matching connection */ if (!ISCSI_SUCCESS(iscsi_conn_create(addr_tgt, isp, &icp))) { /* * Teardown the session we just created. It can't * have any luns or connections associated with it * so this should always succeed (luckily since what * would we do if it failed?) */ (void) iscsi_sess_destroy(isp); rtn = B_FALSE; break; } } rw_exit(&ihp->hba_sess_list_rwlock); kmem_free(ics, size); return (rtn); } /* * iscsid_del - Attempts to delete all associated sessions */ boolean_t iscsid_del(iscsi_hba_t *ihp, char *target_name, iSCSIDiscoveryMethod_t method, struct sockaddr *addr_dsc) { boolean_t rtn = B_TRUE; iscsi_status_t status; iscsi_sess_t *isp; char name[ISCSI_MAX_NAME_LEN]; ASSERT(ihp != NULL); /* target name can be NULL or !NULL */ /* addr_dsc can be NULL or !NULL */ rw_enter(&ihp->hba_sess_list_rwlock, RW_WRITER); isp = ihp->hba_sess_list; while (isp != NULL) { /* * If no target_name is listed (meaning all targets) * or this specific target was listed. And the same * discovery method discovered this target then * continue evaulation. Otherwise fail. */ if (((target_name == NULL) || (strcmp((char *)isp->sess_name, target_name) == 0)) && (isp->sess_discovered_by == method)) { boolean_t try_destroy; /* * If iSNS, SendTargets, or Static then special * handling for disc_addr. */ if ((method == iSCSIDiscoveryMethodISNS) || (method == iSCSIDiscoveryMethodSendTargets)) { /* * If NULL addr_dsc (meaning all disc_addr) * or matching discovered addr. */ if ((addr_dsc == NULL) || (bcmp(addr_dsc, &isp->sess_discovered_addr, SIZEOF_SOCKADDR( &isp->sess_discovered_addr.sin)) == 0)) { try_destroy = B_TRUE; } else { try_destroy = B_FALSE; } } else if (method == iSCSIDiscoveryMethodStatic) { /* * If NULL addr_dsc (meaning all disc_addr) * or matching active connection. */ if ((addr_dsc == NULL) || ((isp->sess_conn_act != NULL) && (bcmp(addr_dsc, &isp->sess_conn_act->conn_base_addr.sin, SIZEOF_SOCKADDR( &isp->sess_conn_act->conn_base_addr.sin)) == 0))) { try_destroy = B_TRUE; } else { try_destroy = B_FALSE; } } else { /* Unknown discovery specified */ try_destroy = B_TRUE; } if (try_destroy == B_TRUE && isp->sess_boot == B_FALSE) { (void) strcpy(name, (char *)isp->sess_name); status = iscsi_sess_destroy(isp); if (ISCSI_SUCCESS(status)) { iscsid_remove_target_param(name); isp = ihp->hba_sess_list; } else if (status == ISCSI_STATUS_BUSY) { /* * The most likely destroy failure * is that ndi/mdi offline failed. * This means that the resource is * in_use/busy. */ cmn_err(CE_NOTE, "iscsi session(%d) - " "resource is in use\n", isp->sess_oid); isp = isp->sess_next; rtn = B_FALSE; } else { cmn_err(CE_NOTE, "iscsi session(%d) - " "session logout failed (%d)\n", isp->sess_oid, status); isp = isp->sess_next; rtn = B_FALSE; } } else { isp = isp->sess_next; } } else { isp = isp->sess_next; } } rw_exit(&ihp->hba_sess_list_rwlock); return (rtn); } /* * iscsid_login_tgt - request target(s) to login */ boolean_t iscsid_login_tgt(iscsi_hba_t *ihp, char *target_name, iSCSIDiscoveryMethod_t method, struct sockaddr *addr_dsc) { boolean_t rtn = B_FALSE; iscsi_sess_t *isp = NULL; iscsi_sess_list_t *isp_list = NULL; iscsi_sess_list_t *last_sess = NULL; iscsi_sess_list_t *cur_sess = NULL; int total = 0; ddi_taskq_t *login_taskq = NULL; char taskq_name[ISCSI_TH_MAX_NAME_LEN] = {0}; time_t time_stamp; ASSERT(ihp != NULL); rw_enter(&ihp->hba_sess_list_rwlock, RW_WRITER); /* Loop thru sessions */ isp = ihp->hba_sess_list; while (isp != NULL) { boolean_t try_online; if (!(method & iSCSIDiscoveryMethodBoot)) { if (target_name == NULL) { if (method == iSCSIDiscoveryMethodUnknown) { /* unknown method mean login to all */ try_online = B_TRUE; } else if (isp->sess_discovered_by & method) { if ((method == iSCSIDiscoveryMethodISNS) || (method == iSCSIDiscoveryMethodSendTargets)) { #define SESS_DISC_ADDR isp->sess_discovered_addr.sin if ((addr_dsc == NULL) || (bcmp( &isp->sess_discovered_addr, addr_dsc, SIZEOF_SOCKADDR( &SESS_DISC_ADDR)) == 0)) { /* * iSNS or sendtarget * discovery and * discovery address * is NULL or match */ try_online = B_TRUE; } else { /* addr_dsc not a match */ try_online = B_FALSE; } #undef SESS_DISC_ADDR } else { /* static configuration */ try_online = B_TRUE; } } else { /* method not a match */ try_online = B_FALSE; } } else if (strcmp(target_name, (char *)isp->sess_name) == 0) { /* target_name match */ try_online = B_TRUE; } else { /* target_name not a match */ try_online = B_FALSE; } } else { /* * online the boot session. */ if (isp->sess_boot == B_TRUE) { try_online = B_TRUE; } } if (try_online == B_TRUE && isp->sess_type == ISCSI_SESS_TYPE_NORMAL) { total++; /* Copy these sessions to the list. */ if (isp_list == NULL) { isp_list = (iscsi_sess_list_t *)kmem_zalloc( sizeof (iscsi_sess_list_t), KM_SLEEP); last_sess = isp_list; last_sess->session = isp; last_sess->next = NULL; } else { last_sess->next = (iscsi_sess_list_t *)kmem_zalloc( sizeof (iscsi_sess_list_t), KM_SLEEP); last_sess->next->session = isp; last_sess->next->next = NULL; last_sess = last_sess->next; } rtn = B_TRUE; } isp = isp->sess_next; } if (total > 0) { time_stamp = ddi_get_time(); (void) snprintf(taskq_name, (ISCSI_TH_MAX_NAME_LEN - 1), "login_queue.%lx", time_stamp); login_taskq = ddi_taskq_create(ihp->hba_dip, taskq_name, total, TASKQ_DEFAULTPRI, 0); if (login_taskq == NULL) { while (isp_list != NULL) { cur_sess = isp_list; isp_list = isp_list->next; kmem_free(cur_sess, sizeof (iscsi_sess_list_t)); } rtn = B_FALSE; rw_exit(&ihp->hba_sess_list_rwlock); return (rtn); } for (cur_sess = isp_list; cur_sess != NULL; cur_sess = cur_sess->next) { if (ddi_taskq_dispatch(login_taskq, iscsi_sess_online, (void *)cur_sess->session, DDI_SLEEP) != DDI_SUCCESS) { cmn_err(CE_NOTE, "Can't dispatch the task " "for login to the target: %s", cur_sess->session->sess_name); } } ddi_taskq_wait(login_taskq); ddi_taskq_destroy(login_taskq); while (isp_list != NULL) { cur_sess = isp_list; isp_list = isp_list->next; kmem_free(cur_sess, sizeof (iscsi_sess_list_t)); } } rw_exit(&ihp->hba_sess_list_rwlock); return (rtn); } /* * +--------------------------------------------------------------------+ * | Local Helper Functions | * +--------------------------------------------------------------------+ */ /* * iscsid_init_config -- initialize configuration parameters of iSCSI initiator */ static boolean_t iscsid_init_config(iscsi_hba_t *ihp) { iscsi_param_set_t ips; void *v = NULL; char *name; char *initiatorName; persistent_param_t pp; persistent_tunable_param_t pparam; uint32_t param_id; int rc; /* allocate memory to hold initiator names */ initiatorName = kmem_zalloc(ISCSI_MAX_NAME_LEN, KM_SLEEP); /* * initialize iSCSI initiator name */ bzero(&ips, sizeof (ips)); if (persistent_initiator_name_get(initiatorName, ISCSI_MAX_NAME_LEN) == B_TRUE) { ips.s_vers = ISCSI_INTERFACE_VERSION; ips.s_param = ISCSI_LOGIN_PARAM_INITIATOR_NAME; if (iscsiboot_prop && !iscsi_cmp_boot_ini_name(initiatorName)) { (void) strncpy(initiatorName, (const char *)iscsiboot_prop->boot_init.ini_name, ISCSI_MAX_NAME_LEN); (void) strncpy((char *)ips.s_value.v_name, (const char *)iscsiboot_prop->boot_init.ini_name, sizeof (ips.s_value.v_name)); (void) iscsi_set_params(&ips, ihp, B_TRUE); /* use default tunable value */ ihp->hba_tunable_params.recv_login_rsp_timeout = ISCSI_DEFAULT_RX_TIMEOUT_VALUE; ihp->hba_tunable_params.polling_login_delay = ISCSI_DEFAULT_LOGIN_POLLING_DELAY; ihp->hba_tunable_params.conn_login_max = ISCSI_DEFAULT_CONN_DEFAULT_LOGIN_MAX; cmn_err(CE_NOTE, "Set initiator's name" " from firmware"); } else { (void) strncpy((char *)ips.s_value.v_name, initiatorName, sizeof (ips.s_value.v_name)); (void) iscsi_set_params(&ips, ihp, B_FALSE); if (persistent_get_tunable_param(initiatorName, &pparam) == B_FALSE) { /* use default value */ pparam.p_params.recv_login_rsp_timeout = ISCSI_DEFAULT_RX_TIMEOUT_VALUE; pparam.p_params.polling_login_delay = ISCSI_DEFAULT_LOGIN_POLLING_DELAY; pparam.p_params.conn_login_max = ISCSI_DEFAULT_CONN_DEFAULT_LOGIN_MAX; } bcopy(&pparam.p_params, &ihp->hba_tunable_params, sizeof (iscsi_tunable_params_t)); } } else { /* * if no initiator-node name available it is most * likely due to a fresh install, or the persistent * store is not working correctly. Set * a default initiator name so that the initiator can * be brought up properly. */ iscsid_set_default_initiator_node_settings(ihp, B_FALSE); (void) strncpy(initiatorName, (const char *)ihp->hba_name, ISCSI_MAX_NAME_LEN); } /* * initialize iSCSI initiator alias (if any) */ bzero(&ips, sizeof (ips)); if (persistent_alias_name_get((char *)ips.s_value.v_name, sizeof (ips.s_value.v_name)) == B_TRUE) { ips.s_param = ISCSI_LOGIN_PARAM_INITIATOR_ALIAS; (void) iscsi_set_params(&ips, ihp, B_FALSE); } else { /* EMPTY */ /* No alias defined - not a problem. */ } /* * load up the overriden iSCSI initiator parameters */ name = kmem_zalloc(ISCSI_MAX_NAME_LEN, KM_SLEEP); persistent_param_lock(); v = NULL; while (persistent_param_next(&v, name, &pp) == B_TRUE) { if (strncmp(name, initiatorName, ISCSI_MAX_NAME_LEN) == 0) { ips.s_oid = ihp->hba_oid; ips.s_vers = ISCSI_INTERFACE_VERSION; for (param_id = 0; param_id < ISCSI_NUM_LOGIN_PARAM; param_id++) { if (pp.p_bitmap & (1 << param_id)) { rc = iscsid_copyto_param_set(param_id, &pp.p_params, &ips); if (rc == 0) { rc = iscsi_set_params(&ips, ihp, B_FALSE); } if (rc != 0) { /* note error but continue */ cmn_err(CE_NOTE, "Failed to set " "param %d for OID %d", ips.s_param, ips.s_oid); } } } /* END for() */ if (iscsiboot_prop && iscsi_chk_bootlun_mpxio(ihp)) { (void) iscsi_reconfig_boot_sess(ihp); } break; } } /* END while() */ persistent_param_unlock(); kmem_free(initiatorName, ISCSI_MAX_NAME_LEN); kmem_free(name, ISCSI_MAX_NAME_LEN); return (B_TRUE); } /* * iscsid_init_targets -- Load up the driver with known static targets and * targets whose parameters have been modified. * * This is done so that the CLI can find a list of targets the driver * currently knows about. * * The driver doesn't need to log into these targets. Log in is done based * upon the enabled discovery methods. */ static boolean_t iscsid_init_targets(iscsi_hba_t *ihp) { void *v = NULL; char *name; iscsi_param_set_t ips; persistent_param_t pp; char *iname; uint32_t param_id; int rc; ASSERT(ihp != NULL); /* allocate memory to hold target names */ name = kmem_zalloc(ISCSI_MAX_NAME_LEN, KM_SLEEP); /* * load up targets whose parameters have been overriden */ /* ---- only need to be set once ---- */ bzero(&ips, sizeof (ips)); ips.s_vers = ISCSI_INTERFACE_VERSION; /* allocate memory to hold initiator name */ iname = kmem_zalloc(ISCSI_MAX_NAME_LEN, KM_SLEEP); (void) persistent_initiator_name_get(iname, ISCSI_MAX_NAME_LEN); persistent_param_lock(); v = NULL; while (persistent_param_next(&v, name, &pp) == B_TRUE) { if (strncmp(iname, name, ISCSI_MAX_NAME_LEN) == 0) { /* * target name matched initiator's name so, * continue to next target. Initiator's * parmeters have already been set. */ continue; } if (iscsiboot_prop && iscsi_cmp_boot_tgt_name(name) && !iscsi_chk_bootlun_mpxio(ihp)) { /* * boot target is not mpxio enabled * simply ignore these overriden parameters */ continue; } ips.s_oid = iscsi_targetparam_get_oid((unsigned char *)name); for (param_id = 0; param_id < ISCSI_NUM_LOGIN_PARAM; param_id++) { if (pp.p_bitmap & (1 << param_id)) { rc = iscsid_copyto_param_set(param_id, &pp.p_params, &ips); if (rc == 0) { rc = iscsi_set_params(&ips, ihp, B_FALSE); } if (rc != 0) { /* note error but continue ---- */ cmn_err(CE_NOTE, "Failed to set " "param %d for OID %d", ips.s_param, ips.s_oid); } } } /* END for() */ if (iscsiboot_prop && iscsi_cmp_boot_tgt_name(name) && iscsi_chk_bootlun_mpxio(ihp)) { (void) iscsi_reconfig_boot_sess(ihp); } } /* END while() */ persistent_param_unlock(); kmem_free(iname, ISCSI_MAX_NAME_LEN); kmem_free(name, ISCSI_MAX_NAME_LEN); return (B_TRUE); } /* * iscsid_thread_static -- If static discovery is enabled, this routine obtains * all statically configured targets from the peristent store and issues a * login request to the driver. */ /* ARGSUSED */ static void iscsid_thread_static(iscsi_thread_t *thread, void *p) { iSCSIDiscoveryMethod_t dm; entry_t entry; char name[ISCSI_MAX_NAME_LEN]; void *v = NULL; iscsi_hba_t *ihp = (iscsi_hba_t *)p; while (iscsi_thread_wait(thread, -1) != 0) { iscsi_discovery_event(ihp, iSCSIDiscoveryMethodStatic, B_TRUE); /* ---- ensure static target discovery is enabled ---- */ dm = persistent_disc_meth_get(); if ((dm & iSCSIDiscoveryMethodStatic) == 0) { cmn_err(CE_NOTE, "iscsi discovery failure - " "StaticTargets method is not enabled"); iscsi_discovery_event(ihp, iSCSIDiscoveryMethodStatic, B_FALSE); continue; } /* * walk list of the statically configured targets from the * persistent store */ v = NULL; persistent_static_addr_lock(); while (persistent_static_addr_next(&v, name, &entry) == B_TRUE) { iscsi_sockaddr_t addr; iscsid_addr_to_sockaddr(entry.e_insize, &(entry.e_u), entry.e_port, &addr.sin); (void) iscsid_add(ihp, iSCSIDiscoveryMethodStatic, &addr.sin, name, entry.e_tpgt, &addr.sin); } persistent_static_addr_unlock(); iscsi_discovery_event(ihp, iSCSIDiscoveryMethodStatic, B_FALSE); } } /* * iscsid_thread_sendtgts -- If SendTargets discovery is enabled, this routine * obtains all target discovery addresses configured from the peristent store * and probe the IP/port addresses for possible targets. It will then issue * a login request to the driver for all discoveryed targets. */ static void iscsid_thread_sendtgts(iscsi_thread_t *thread, void *p) { iscsi_hba_t *ihp = (iscsi_hba_t *)p; iSCSIDiscoveryMethod_t dm; entry_t entry; void *v = NULL; while (iscsi_thread_wait(thread, -1) != 0) { iscsi_discovery_event(ihp, iSCSIDiscoveryMethodSendTargets, B_TRUE); /* ---- ensure SendTargets discovery is enabled ---- */ dm = persistent_disc_meth_get(); if ((dm & iSCSIDiscoveryMethodSendTargets) == 0) { cmn_err(CE_NOTE, "iscsi discovery failure - " "SendTargets method is not enabled"); iscsi_discovery_event(ihp, iSCSIDiscoveryMethodSendTargets, B_FALSE); continue; } /* * walk list of the SendTarget discovery addresses from the * persistent store */ v = NULL; persistent_disc_addr_lock(); while (persistent_disc_addr_next(&v, &entry) == B_TRUE) { iscsid_do_sendtgts(&entry); } persistent_disc_addr_unlock(); iscsi_discovery_event(ihp, iSCSIDiscoveryMethodSendTargets, B_FALSE); } } /* * iscsid_thread_slp -- If SLP discovery is enabled, this routine provides * the SLP discovery service. */ static void iscsid_thread_slp(iscsi_thread_t *thread, void *p) { iscsi_hba_t *ihp = (iscsi_hba_t *)p; do { /* * Even though we don't have support for SLP at this point * we'll send the events if someone has enabled this thread. * If this is not done the daemon waiting for discovery to * complete will pause forever holding up the boot process. */ iscsi_discovery_event(ihp, iSCSIDiscoveryMethodSLP, B_TRUE); iscsi_discovery_event(ihp, iSCSIDiscoveryMethodSLP, B_FALSE); } while (iscsi_thread_wait(thread, -1) != 0); } /* * iscsid_thread_isns -- */ static void iscsid_thread_isns(iscsi_thread_t *thread, void *ptr) { iscsi_hba_t *ihp = (iscsi_hba_t *)ptr; iSCSIDiscoveryMethod_t dm; while (iscsi_thread_wait(thread, -1) != 0) { iscsi_discovery_event(ihp, iSCSIDiscoveryMethodISNS, B_TRUE); /* ---- ensure iSNS discovery is enabled ---- */ dm = persistent_disc_meth_get(); if ((dm & iSCSIDiscoveryMethodISNS) == 0) { cmn_err(CE_NOTE, "iscsi discovery failure - " "iSNS method is not enabled"); iscsi_discovery_event(ihp, iSCSIDiscoveryMethodISNS, B_FALSE); continue; } (void) isns_reg(ihp->hba_isid, ihp->hba_name, ISCSI_MAX_NAME_LEN, ihp->hba_alias, ISCSI_MAX_NAME_LEN, ISNS_INITIATOR_NODE_TYPE, isns_scn_callback); iscsid_do_isns_query(ihp); iscsi_discovery_event(ihp, iSCSIDiscoveryMethodISNS, B_FALSE); } /* Thread stopped. Deregister from iSNS servers(s). */ (void) isns_dereg(ihp->hba_isid, ihp->hba_name); } /* * iscsid_threads_create -- Creates all the discovery threads. */ static void iscsid_threads_create(iscsi_hba_t *ihp) { iscsid_thr_table *t; /* * start a thread for each discovery method */ for (t = &iscsid_thr[0]; t->method != iSCSIDiscoveryMethodUnknown; t++) { if (t->thr_id == NULL) { t->thr_id = iscsi_thread_create(ihp->hba_dip, t->name, t->func_start, ihp); } } } /* * iscsid_threads_destroy -- Destroys all the discovery threads. */ static void iscsid_threads_destroy(void) { iscsid_thr_table *t; for (t = &iscsid_thr[0]; t->method != iSCSIDiscoveryMethodUnknown; t++) { if (t->thr_id != NULL) { iscsi_thread_destroy(t->thr_id); t->thr_id = NULL; } } } /* * iscsid_copyto_param_set - helper function for iscsid_init_params. */ static int iscsid_copyto_param_set(uint32_t param_id, iscsi_login_params_t *params, iscsi_param_set_t *ipsp) { int rtn = 0; if (param_id >= ISCSI_NUM_LOGIN_PARAM) { return (EINVAL); } switch (param_id) { /* * Boolean parameters */ case ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER: ipsp->s_value.v_bool = params->data_pdu_in_order; break; case ISCSI_LOGIN_PARAM_IMMEDIATE_DATA: ipsp->s_value.v_bool = params->immediate_data; break; case ISCSI_LOGIN_PARAM_INITIAL_R2T: ipsp->s_value.v_bool = params->initial_r2t; break; case ISCSI_LOGIN_PARAM_DATA_PDU_IN_ORDER: ipsp->s_value.v_bool = params->data_pdu_in_order; break; /* * Integer parameters */ case ISCSI_LOGIN_PARAM_HEADER_DIGEST: ipsp->s_value.v_integer = params->header_digest; break; case ISCSI_LOGIN_PARAM_DATA_DIGEST: ipsp->s_value.v_integer = params->data_digest; break; case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN: ipsp->s_value.v_integer = params->default_time_to_retain; break; case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT: ipsp->s_value.v_integer = params->default_time_to_wait; break; case ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH: ipsp->s_value.v_integer = params->max_recv_data_seg_len; break; case ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH: ipsp->s_value.v_integer = params->first_burst_length; break; case ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH: ipsp->s_value.v_integer = params->max_burst_length; break; /* * Integer parameters which currently are unsettable */ case ISCSI_LOGIN_PARAM_MAX_CONNECTIONS: case ISCSI_LOGIN_PARAM_OUTSTANDING_R2T: case ISCSI_LOGIN_PARAM_ERROR_RECOVERY_LEVEL: /* ---- drop through to default case ---- */ default: rtn = EINVAL; break; } /* if all is well, set the parameter identifier */ if (rtn == 0) { ipsp->s_param = param_id; } return (rtn); } /* * iscsid_add_pg_list_to_cache - Add portal groups in the list to the * discovery cache. */ static void iscsid_add_pg_list_to_cache(iscsi_hba_t *ihp, isns_portal_group_list_t *pg_list) { int i; for (i = 0; i < pg_list->pg_out_cnt; i++) { iscsi_sockaddr_t addr_dsc; iscsi_sockaddr_t addr_tgt; iscsid_addr_to_sockaddr( pg_list->pg_list[i].isns_server_ip.i_insize, &pg_list->pg_list[i].isns_server_ip.i_addr, pg_list->pg_list[i].isns_server_port, &addr_dsc.sin); iscsid_addr_to_sockaddr( pg_list->pg_list[i].insize, &pg_list->pg_list[i].pg_ip_addr, pg_list->pg_list[i].pg_port, &addr_tgt.sin); (void) iscsid_add(ihp, iSCSIDiscoveryMethodISNS, &addr_dsc.sin, (char *)pg_list->pg_list[i].pg_iscsi_name, pg_list->pg_list[i].pg_tag, &addr_tgt.sin); } } /* * set_initiator_name - set default initiator name and alias. * * This sets the default initiator name and alias. The * initiator name is composed of sun's reverse domain name * and registration followed and a unique classifier. This * classifier is the mac address of the first NIC in the * host and a timestamp to make sure the classifier is * unique if the NIC is moved between hosts. The alias * is just the hostname. */ void iscsid_set_default_initiator_node_settings(iscsi_hba_t *ihp, boolean_t minimal) { int i; time_t x; struct ether_addr eaddr; char val[10]; iscsi_chap_props_t *chap = NULL; /* Set default initiator-node name */ if (iscsiboot_prop && iscsiboot_prop->boot_init.ini_name != NULL) { (void) strncpy((char *)ihp->hba_name, (const char *)iscsiboot_prop->boot_init.ini_name, ISCSI_MAX_NAME_LEN); } else { (void) snprintf((char *)ihp->hba_name, ISCSI_MAX_NAME_LEN, "iqn.1986-03.com.sun:01:"); (void) localetheraddr(NULL, &eaddr); for (i = 0; i < ETHERADDRL; i++) { (void) snprintf(val, sizeof (val), "%02x", eaddr.ether_addr_octet[i]); (void) strncat((char *)ihp->hba_name, val, ISCSI_MAX_NAME_LEN); } /* Set default initiator-node alias */ x = ddi_get_time(); (void) snprintf(val, sizeof (val), ".%lx", x); (void) strncat((char *)ihp->hba_name, val, ISCSI_MAX_NAME_LEN); if (ihp->hba_alias[0] == '\0') { (void) strncpy((char *)ihp->hba_alias, utsname.nodename, ISCSI_MAX_NAME_LEN); ihp->hba_alias_length = strlen((char *)ihp->hba_alias); if (minimal == B_FALSE) { (void) persistent_alias_name_set( (char *)ihp->hba_alias); } } } if (minimal == B_TRUE) { return; } (void) persistent_initiator_name_set((char *)ihp->hba_name); /* Set default initiator-node CHAP settings */ if (persistent_initiator_name_get((char *)ihp->hba_name, ISCSI_MAX_NAME_LEN) == B_TRUE) { chap = (iscsi_chap_props_t *)kmem_zalloc(sizeof (*chap), KM_SLEEP); if (persistent_chap_get((char *)ihp->hba_name, chap) == B_FALSE) { bcopy((char *)ihp->hba_name, chap->c_user, strlen((char *)ihp->hba_name)); chap->c_user_len = strlen((char *)ihp->hba_name); (void) persistent_chap_set((char *)ihp->hba_name, chap); } kmem_free(chap, sizeof (*chap)); } } static void iscsid_remove_target_param(char *name) { persistent_param_t *pparam; uint32_t t_oid; iscsi_config_sess_t *ics; ASSERT(name != NULL); /* * Remove target-param <-> target mapping. * Only remove if there is not any overridden * parameters in the persistent store */ pparam = (persistent_param_t *)kmem_zalloc(sizeof (*pparam), KM_SLEEP); /* * setup initial buffer for configured session * information */ ics = (iscsi_config_sess_t *)kmem_zalloc(sizeof (*ics), KM_SLEEP); ics->ics_in = 1; if ((persistent_param_get(name, pparam) == B_FALSE) && (persistent_get_config_session(name, ics) == B_FALSE)) { t_oid = iscsi_targetparam_get_oid((uchar_t *)name); (void) iscsi_targetparam_remove_target(t_oid); } kmem_free(pparam, sizeof (*pparam)); pparam = NULL; kmem_free(ics, sizeof (*ics)); ics = NULL; } /* * iscsid_addr_to_sockaddr - convert other types to struct sockaddr */ void iscsid_addr_to_sockaddr(int src_insize, void *src_addr, int src_port, struct sockaddr *dst_addr) { ASSERT((src_insize == sizeof (struct in_addr)) || (src_insize == sizeof (struct in6_addr))); ASSERT(src_addr != NULL); ASSERT(dst_addr != NULL); bzero(dst_addr, sizeof (*dst_addr)); /* translate discovery information */ if (src_insize == sizeof (struct in_addr)) { struct sockaddr_in *addr_in = (struct sockaddr_in *)dst_addr; addr_in->sin_family = AF_INET; bcopy(src_addr, &addr_in->sin_addr.s_addr, sizeof (struct in_addr)); addr_in->sin_port = htons(src_port); } else { struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)dst_addr; addr_in6->sin6_family = AF_INET6; bcopy(src_addr, &addr_in6->sin6_addr.s6_addr, sizeof (struct in6_addr)); addr_in6->sin6_port = htons(src_port); } } /* * iscsi_discovery_event -- send event associated with discovery operations * * Each discovery event has a start and end event. Which is sent is based * on the boolean argument start with the obvious results. */ static void iscsi_discovery_event(iscsi_hba_t *ihp, iSCSIDiscoveryMethod_t m, boolean_t start) { char *subclass = NULL; mutex_enter(&ihp->hba_discovery_events_mutex); switch (m) { case iSCSIDiscoveryMethodStatic: if (start == B_TRUE) { subclass = ESC_ISCSI_STATIC_START; } else { ihp->hba_discovery_events |= iSCSIDiscoveryMethodStatic; subclass = ESC_ISCSI_STATIC_END; } break; case iSCSIDiscoveryMethodSendTargets: if (start == B_TRUE) { subclass = ESC_ISCSI_SEND_TARGETS_START; } else { ihp->hba_discovery_events |= iSCSIDiscoveryMethodSendTargets; subclass = ESC_ISCSI_SEND_TARGETS_END; } break; case iSCSIDiscoveryMethodSLP: if (start == B_TRUE) { subclass = ESC_ISCSI_SLP_START; } else { ihp->hba_discovery_events |= iSCSIDiscoveryMethodSLP; subclass = ESC_ISCSI_SLP_END; } break; case iSCSIDiscoveryMethodISNS: if (start == B_TRUE) { subclass = ESC_ISCSI_ISNS_START; } else { ihp->hba_discovery_events |= iSCSIDiscoveryMethodISNS; subclass = ESC_ISCSI_ISNS_END; } break; } mutex_exit(&ihp->hba_discovery_events_mutex); iscsi_send_sysevent(ihp, EC_ISCSI, subclass, NULL); } /* * iscsi_send_sysevent -- send sysevent using specified class */ void iscsi_send_sysevent( iscsi_hba_t *ihp, char *eventclass, char *subclass, nvlist_t *np) { (void) ddi_log_sysevent(ihp->hba_dip, DDI_VENDOR_SUNW, eventclass, subclass, np, NULL, DDI_SLEEP); } static boolean_t iscsid_boot_init_config(iscsi_hba_t *ihp) { if (strlen((const char *)iscsiboot_prop->boot_init.ini_name) != 0) { bcopy(iscsiboot_prop->boot_init.ini_name, ihp->hba_name, strlen((const char *)iscsiboot_prop->boot_init.ini_name)); } /* or using default login param for boot session */ return (B_TRUE); } boolean_t iscsi_reconfig_boot_sess(iscsi_hba_t *ihp) { iscsi_config_sess_t *ics; int idx; iscsi_sess_t *isp, *t_isp; int isid, size; char *name; boolean_t rtn = B_TRUE; uint32_t event_count; if (iscsiboot_prop == NULL) { return (B_FALSE); } size = sizeof (*ics); ics = kmem_zalloc(size, KM_SLEEP); ics->ics_in = 1; /* get information of number of sessions to be configured */ name = (char *)iscsiboot_prop->boot_tgt.tgt_name; if (persistent_get_config_session(name, ics) == B_FALSE) { /* * No target information available to check * initiator information. Assume one session * by default. */ name = (char *)iscsiboot_prop->boot_init.ini_name; if (persistent_get_config_session(name, ics) == B_FALSE) { ics->ics_out = 1; ics->ics_bound = B_TRUE; } } /* get necessary information */ if (ics->ics_out > 1) { idx = ics->ics_out; size = ISCSI_SESSION_CONFIG_SIZE(ics->ics_out); kmem_free(ics, sizeof (*ics)); ics = kmem_zalloc(size, KM_SLEEP); ics->ics_in = idx; /* get configured sessions information */ if (persistent_get_config_session((char *)name, ics) != B_TRUE) { cmn_err(CE_NOTE, "session(%s) - " "failed to setup multiple sessions", name); kmem_free(ics, size); return (B_FALSE); } } /* create a temporary session to keep boot session connective */ t_isp = iscsi_add_boot_sess(ihp, ISCSI_MAX_CONFIG_SESSIONS); if (t_isp == NULL) { cmn_err(CE_NOTE, "session(%s) - " "failed to setup multiple sessions", name); rw_exit(&ihp->hba_sess_list_rwlock); kmem_free(ics, size); return (B_FALSE); } /* destroy all old boot sessions */ rw_enter(&ihp->hba_sess_list_rwlock, RW_WRITER); isp = ihp->hba_sess_list; while (isp != NULL) { if (iscsi_chk_bootlun_mpxio(ihp) && isp->sess_boot) { if (isp->sess_isid[5] != ISCSI_MAX_CONFIG_SESSIONS) { /* * destroy all stale sessions * except temporary boot session */ if (ISCSI_SUCCESS(iscsi_sess_destroy( isp))) { isp = ihp->hba_sess_list; } else { /* * couldn't destroy stale sessions * at least poke it to disconnect */ event_count = atomic_inc_32_nv( &isp->sess_state_event_count); iscsi_sess_enter_state_zone(isp); iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N7, event_count); iscsi_sess_exit_state_zone(isp); isp = isp->sess_next; cmn_err(CE_NOTE, "session(%s) - " "failed to setup multiple" " sessions", name); } } else { isp = isp->sess_next; } } else { isp = isp->sess_next; } } rw_exit(&ihp->hba_sess_list_rwlock); for (isid = 0; isid < ics->ics_out; isid++) { isp = iscsi_add_boot_sess(ihp, isid); if (isp == NULL) { cmn_err(CE_NOTE, "session(%s) - failed to setup" " multiple sessions", name); rtn = B_FALSE; break; } } if (!rtn && (isid == 0)) { /* * fail to create any new boot session * so only the temporary session is alive * quit without destroying it */ kmem_free(ics, size); return (rtn); } rw_enter(&ihp->hba_sess_list_rwlock, RW_WRITER); if (!ISCSI_SUCCESS(iscsi_sess_destroy(t_isp))) { /* couldn't destroy temp boot session */ cmn_err(CE_NOTE, "session(%s) - " "failed to setup multiple sessions", name); rw_exit(&ihp->hba_sess_list_rwlock); rtn = B_FALSE; } rw_exit(&ihp->hba_sess_list_rwlock); kmem_free(ics, size); return (rtn); } static iscsi_sess_t * iscsi_add_boot_sess(iscsi_hba_t *ihp, int isid) { iscsi_sess_t *isp; iscsi_conn_t *icp; uint_t oid; iscsi_sockaddr_t addr_dst; addr_dst.sin.sa_family = iscsiboot_prop->boot_tgt.sin_family; if (addr_dst.sin.sa_family == AF_INET) { bcopy(&iscsiboot_prop->boot_tgt.tgt_ip_u.u_in4.s_addr, &addr_dst.sin4.sin_addr.s_addr, sizeof (struct in_addr)); addr_dst.sin4.sin_port = htons(iscsiboot_prop->boot_tgt.tgt_port); } else { bcopy(&iscsiboot_prop->boot_tgt.tgt_ip_u.u_in6.s6_addr, &addr_dst.sin6.sin6_addr.s6_addr, sizeof (struct in6_addr)); addr_dst.sin6.sin6_port = htons(iscsiboot_prop->boot_tgt.tgt_port); } rw_enter(&ihp->hba_sess_list_rwlock, RW_WRITER); isp = iscsi_sess_create(ihp, iSCSIDiscoveryMethodBoot|iSCSIDiscoveryMethodStatic, (struct sockaddr *)&addr_dst, (char *)iscsiboot_prop->boot_tgt.tgt_name, ISCSI_DEFAULT_TPGT, isid, ISCSI_SESS_TYPE_NORMAL, &oid); if (isp == NULL) { /* create temp booting session failed */ rw_exit(&ihp->hba_sess_list_rwlock); return (NULL); } isp->sess_boot = B_TRUE; if (!ISCSI_SUCCESS(iscsi_conn_create((struct sockaddr *)&addr_dst, isp, &icp))) { rw_exit(&ihp->hba_sess_list_rwlock); return (NULL); } rw_exit(&ihp->hba_sess_list_rwlock); /* now online created session */ if (iscsid_login_tgt(ihp, (char *)iscsiboot_prop->boot_tgt.tgt_name, iSCSIDiscoveryMethodBoot|iSCSIDiscoveryMethodStatic, (struct sockaddr *)&addr_dst) == B_FALSE) { return (NULL); } return (isp); } static void iscsid_thread_boot_wd(iscsi_thread_t *thread, void *p) { int rc = 1; iscsi_hba_t *ihp = (iscsi_hba_t *)p; boolean_t reconfigured = B_FALSE; while (rc != 0) { if (iscsiboot_prop && (modrootloaded == 1)) { if (ihp->hba_persistent_loaded == B_FALSE) { if (persistent_load() == B_TRUE) { ihp->hba_persistent_loaded = B_TRUE; } } if ((ihp->hba_persistent_loaded == B_TRUE) && (reconfigured == B_FALSE)) { if (iscsi_chk_bootlun_mpxio(ihp) == B_TRUE) { (void) iscsi_reconfig_boot_sess(ihp); iscsid_poke_discovery(ihp, iSCSIDiscoveryMethodUnknown); (void) iscsid_login_tgt(ihp, NULL, iSCSIDiscoveryMethodUnknown, NULL); } reconfigured = B_TRUE; } break; } rc = iscsi_thread_wait(thread, SEC_TO_TICK(1)); } } boolean_t iscsi_cmp_boot_tgt_name(char *name) { if (iscsiboot_prop && (strncmp((const char *)name, (const char *)iscsiboot_prop->boot_tgt.tgt_name, ISCSI_MAX_NAME_LEN) == 0)) { return (B_TRUE); } else { return (B_FALSE); } } boolean_t iscsi_cmp_boot_ini_name(char *name) { if (iscsiboot_prop && (strncmp((const char *)name, (const char *)iscsiboot_prop->boot_init.ini_name, ISCSI_MAX_NAME_LEN) == 0)) { return (B_TRUE); } else { return (B_FALSE); } } boolean_t iscsi_chk_bootlun_mpxio(iscsi_hba_t *ihp) { iscsi_sess_t *isp; iscsi_lun_t *ilp; isp = ihp->hba_sess_list; boolean_t tgt_mpxio_enabled = B_FALSE; boolean_t bootlun_found = B_FALSE; uint16_t lun_num; if (iscsiboot_prop == NULL) { return (B_FALSE); } if (!ihp->hba_mpxio_enabled) { return (B_FALSE); } lun_num = *((uint64_t *)(iscsiboot_prop->boot_tgt.tgt_boot_lun)); while (isp != NULL) { if ((strncmp((char *)isp->sess_name, (const char *)iscsiboot_prop->boot_tgt.tgt_name, ISCSI_MAX_NAME_LEN) == 0) && (isp->sess_boot == B_TRUE)) { /* * found boot session. * check its mdi path info is null or not */ ilp = isp->sess_lun_list; while (ilp != NULL) { if (lun_num == ilp->lun_num) { if (ilp->lun_pip) { tgt_mpxio_enabled = B_TRUE; } bootlun_found = B_TRUE; } ilp = ilp->lun_next; } } isp = isp->sess_next; } if (bootlun_found) { return (tgt_mpxio_enabled); } else { /* * iscsiboot_prop not NULL while no boot lun found * in most cases this is none iscsi boot while iscsiboot_prop * is not NULL, in this scenario return iscsi HBA's mpxio config */ return (ihp->hba_mpxio_enabled); } } static boolean_t iscsid_check_active_boot_conn(iscsi_hba_t *ihp) { iscsi_sess_t *isp = NULL; iscsi_conn_t *icp = NULL; rw_enter(&ihp->hba_sess_list_rwlock, RW_READER); isp = ihp->hba_sess_list; while (isp != NULL) { if (isp->sess_boot == B_TRUE) { rw_enter(&isp->sess_conn_list_rwlock, RW_READER); icp = isp->sess_conn_list; while (icp != NULL) { if (icp->conn_state == ISCSI_CONN_STATE_LOGGED_IN) { rw_exit(&isp->sess_conn_list_rwlock); rw_exit(&ihp->hba_sess_list_rwlock); return (B_TRUE); } icp = icp->conn_next; } rw_exit(&isp->sess_conn_list_rwlock); } isp = isp->sess_next; } rw_exit(&ihp->hba_sess_list_rwlock); return (B_FALSE); }