From 8c331477805bdba406d64c7212d1467d26f2c18b Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Fri, 5 Nov 2010 18:56:54 +0000 Subject: [PATCH] - Node name setting added - qlini_mode "disabled" parameter handling improved - Docs updated git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@2607 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- qla2x00t/qla2x00-target/ChangeLog | 6 + qla2x00t/qla2x00-target/README | 10 +- qla2x00t/qla2x00-target/qla2x00t.c | 131 +++++-- qla2x00t/qla2x_tgt_def.h | 4 +- qla2x00t/qla_def.h | 3 + qla2x00t/qla_gs.c | 2 +- qla2x00t/qla_init.c | 52 ++- qla2x00t/qla_mbx.c | 8 + ...d_hw_init_tgt_mode_from_the_beginning.diff | 324 ------------------ 9 files changed, 159 insertions(+), 381 deletions(-) delete mode 100644 qla2x00t/unsupported-patches/qla_delayed_hw_init_tgt_mode_from_the_beginning.diff diff --git a/qla2x00t/qla2x00-target/ChangeLog b/qla2x00t/qla2x00-target/ChangeLog index 1e06274b1..b352b2d0f 100644 --- a/qla2x00t/qla2x00-target/ChangeLog +++ b/qla2x00t/qla2x00-target/ChangeLog @@ -6,6 +6,12 @@ Summary of changes between versions 2.0.0 and 2.1.0 control for them. Thanks a lot to Uri Yanai and Dorit Halsadi! + - Node name setting added + + - qlini_mode "disabled" parameter handling improved + + - Other minor fixes and cleanups + Summary of changes between versions 1.0.2 and 2.0.0 --------------------------------------------------- diff --git a/qla2x00t/qla2x00-target/README b/qla2x00t/qla2x00-target/README index 35ac00d8a..68310f994 100644 --- a/qla2x00t/qla2x00-target/README +++ b/qla2x00t/qla2x00-target/README @@ -142,15 +142,7 @@ enabled back. Usage of mode "disabled" is recommended, if you have incorrectly functioning your target's initiators, which if once seen a port in -initiator mode, later refuse to see it as a target. Although this mode -does make a noticeable difference, it isn't absolutely strong, since the -firmware once initialized requires a HBA to be in either initiator, or -target mode, so until you enable target mode on a port, your initiators -will report this port as working in initiator mode. If you need -absolutely strong assurance that initiator mode never enabled, you can -consider using patch -unsupported-patches/qla_delayed_hw_init_tgt_mode_from_the_beginning.diff. -See description of it inside the patch. +initiator mode, later refuse to see it as a target. Use mode "enabled" if you need your QLA adapters to work in both initiator and target modes at the same time. diff --git a/qla2x00t/qla2x00-target/qla2x00t.c b/qla2x00t/qla2x00-target/qla2x00t.c index 018e8c751..0dfcd9de8 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.c +++ b/qla2x00t/qla2x00-target/qla2x00t.c @@ -133,23 +133,24 @@ static ssize_t q2t_abort_isp_store(struct kobject *kobj, struct kobj_attribute q2t_abort_isp_attr = __ATTR(abort_isp, S_IWUSR, NULL, q2t_abort_isp_store); -static const struct attribute *q2t_tgt_attrs[] = { - &q2t_expl_conf_attr.attr, - &q2t_abort_isp_attr.attr, - NULL, -}; - static ssize_t q2t_hw_target_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); static struct kobj_attribute q2t_hw_target_attr = __ATTR(hw_target, S_IRUGO, q2t_hw_target_show, NULL); -static ssize_t q2t_vp_node_name_show(struct kobject *kobj, +static ssize_t q2t_node_name_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); static struct kobj_attribute q2t_vp_node_name_attr = - __ATTR(node_name, S_IRUGO, q2t_vp_node_name_show, NULL); + __ATTR(node_name, S_IRUGO, q2t_node_name_show, NULL); + +static ssize_t q2t_node_name_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buffer, size_t size); + +static struct kobj_attribute q2t_hw_node_name_attr = + __ATTR(node_name, S_IRUGO|S_IWUSR, q2t_node_name_show, + q2t_node_name_store); static ssize_t q2t_vp_parent_host_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); @@ -157,6 +158,12 @@ static ssize_t q2t_vp_parent_host_show(struct kobject *kobj, static struct kobj_attribute q2t_vp_parent_host_attr = __ATTR(parent_host, S_IRUGO, q2t_vp_parent_host_show, NULL); +static const struct attribute *q2t_tgt_attrs[] = { + &q2t_expl_conf_attr.attr, + &q2t_abort_isp_attr.attr, + NULL, +}; + #endif /* CONFIG_SCST_PROC */ static int q2t_enable_tgt(struct scst_tgt *tgt, bool enable); @@ -5310,7 +5317,8 @@ static int q2t_add_target(scsi_qla_host_t *ha) ha->q2t_tgt = tgt; - if (q2t_get_target_name(ha->port_name, &wwn) != 0) + res = q2t_get_target_name(ha->port_name, &wwn); + if (res != 0) goto out_free; tgt->scst_tgt = scst_register_target(&tgt2x_template, wwn); @@ -5368,6 +5376,13 @@ static int q2t_add_target(scsi_qla_host_t *ha) PRINT_ERROR("qla2x00t(%ld): Unable to create " "\"hw_target\" file for target %s", ha->instance, scst_get_tgt_name(tgt->scst_tgt)); + + rc = sysfs_create_file(scst_sysfs_get_tgt_kobj(tgt->scst_tgt), + &q2t_hw_node_name_attr.attr); + if (rc != 0) + PRINT_ERROR("qla2x00t(%ld): Unable to create " + "\"node_name\" file for HW target %s", + ha->instance, scst_get_tgt_name(tgt->scst_tgt)); } else { rc = sysfs_create_file(scst_sysfs_get_tgt_kobj(tgt->scst_tgt), &q2t_vp_node_name_attr.attr); @@ -5534,7 +5549,7 @@ static int q2t_parse_wwn(const char *ns, u64 *nm) u8 wwn[8]; /* validate we have enough characters for WWPN */ - if (strlen(ns) != 23) + if (strnlen(ns, 23) != 23) return -EINVAL; memset(wwn, 0, sizeof(wwn)); @@ -5824,27 +5839,93 @@ static ssize_t q2t_hw_target_show(struct kobject *kobj, return sprintf(buf, "%d\n", 1); } -static ssize_t q2t_vp_node_name_show(struct kobject *kobj, +static ssize_t q2t_node_name_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct scst_tgt *scst_tgt; struct q2t_tgt *tgt; scsi_qla_host_t *ha; - ssize_t size; + ssize_t res; char *wwn; + uint8_t *node_name; scst_tgt = container_of(kobj, struct scst_tgt, tgt_kobj); tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); ha = tgt->ha; - if (q2t_get_target_name(ha->node_name, &wwn) != 0) - return 0; + if (ha->parent == NULL) { + if (qla_tgt_mode_enabled(ha) || !ha->node_name_set) + node_name = ha->node_name; + else + node_name = ha->tgt_node_name; + } else + node_name = ha->node_name; - size = sprintf(buf, "%s\n%s\n", wwn, SCST_SYSFS_KEY_MARK); + res = q2t_get_target_name(node_name, &wwn); + if (res != 0) + goto out; + + res = sprintf(buf, "%s\n", wwn); + if ((ha->parent != NULL) || ha->node_name_set) + res += sprintf(&buf[res], "%s\n", SCST_SYSFS_KEY_MARK); kfree(wwn); - return size; +out: + return res; +} + +static ssize_t q2t_node_name_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buffer, size_t size) +{ + struct scst_tgt *scst_tgt; + struct q2t_tgt *tgt; + scsi_qla_host_t *ha; + u64 node_name, old_node_name; + int res; + + TRACE_ENTRY(); + + scst_tgt = container_of(kobj, struct scst_tgt, tgt_kobj); + tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); + ha = tgt->ha; + + sBUG_ON(ha->parent != NULL); + + if (size == 0) + goto out_default; + + res = q2t_parse_wwn(buffer, &node_name); + if (res != 0) { + if ((buffer[0] == '\0') || (buffer[0] == '\n')) + goto out_default; + PRINT_ERROR("qla2x00t(%ld): Wrong node name", ha->instance); + goto out; + } + + old_node_name = wwn_to_u64(ha->node_name); + if (old_node_name == node_name) + goto out_success; + + u64_to_wwn(node_name, ha->tgt_node_name); + ha->node_name_set = 1; + +abort: + if (qla_tgt_mode_enabled(ha)) { + set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + qla2x00_wait_for_hba_online(ha); + } + +out_success: + res = size; + +out: + TRACE_EXIT_RES(res); + return res; + +out_default: + ha->node_name_set = 0; + goto abort; } static ssize_t q2t_vp_parent_host_show(struct kobject *kobj, @@ -5853,27 +5934,23 @@ static ssize_t q2t_vp_parent_host_show(struct kobject *kobj, struct scst_tgt *scst_tgt; struct q2t_tgt *tgt; scsi_qla_host_t *ha; - ssize_t size; + ssize_t res; char *wwn; scst_tgt = container_of(kobj, struct scst_tgt, tgt_kobj); tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); ha = to_qla_parent(tgt->ha); - if (!ha) { - PRINT_ERROR("qla2x00t(%ld): No parent for NPIV target %s", - tgt->ha->instance, scst_get_tgt_name(scst_tgt)); - return 0; - } + res = q2t_get_target_name(ha->port_name, &wwn); + if (res != 0) + goto out; - if (q2t_get_target_name(ha->port_name, &wwn) != 0) - return 0; - - size = sprintf(buf, "%s\n%s\n", wwn, SCST_SYSFS_KEY_MARK); + res = sprintf(buf, "%s\n%s\n", wwn, SCST_SYSFS_KEY_MARK); kfree(wwn); - return size; +out: + return res; } #else /* CONFIG_SCST_PROC */ diff --git a/qla2x00t/qla2x_tgt_def.h b/qla2x00t/qla2x_tgt_def.h index 120bc919d..e14ba9be0 100644 --- a/qla2x00t/qla2x_tgt_def.h +++ b/qla2x00t/qla2x_tgt_def.h @@ -44,13 +44,13 @@ * Must be changed on any change in any initiator visible interfaces or * data in the target add-on */ -#define QLA2X_TARGET_MAGIC 268 +#define QLA2X_TARGET_MAGIC 269 /* * Must be changed on any change in any target visible interfaces or * data in the initiator */ -#define QLA2X_INITIATOR_MAGIC 57221 +#define QLA2X_INITIATOR_MAGIC 57222 #define QLA2X_INI_MODE_STR_EXCLUSIVE "exclusive" #define QLA2X_INI_MODE_STR_DISABLED "disabled" diff --git a/qla2x00t/qla_def.h b/qla2x00t/qla_def.h index dde5f2839..525db6f09 100644 --- a/qla2x00t/qla_def.h +++ b/qla2x00t/qla_def.h @@ -2353,6 +2353,7 @@ typedef struct scsi_qla_host { unsigned int enable_explicit_conf :1; unsigned int host_shutting_down :1; unsigned int ini_mode_force_reverse :1; + unsigned int node_name_set :1; dma_addr_t atio_dma; /* Physical address. */ atio_t *atio_ring; /* Base virtual address */ @@ -2643,6 +2644,8 @@ typedef struct scsi_qla_host { uint32_t saved_firmware_options_3; uint8_t saved_firmware_options[2]; uint8_t saved_add_firmware_options[2]; + + uint8_t tgt_node_name[WWN_SIZE]; #endif /* CONFIG_SCSI_QLA2XXX_TARGET */ struct scsi_qla_host *parent; /* holds pport */ diff --git a/qla2x00t/qla_gs.c b/qla2x00t/qla_gs.c index 34883845c..c1b0dea61 100644 --- a/qla2x00t/qla_gs.c +++ b/qla2x00t/qla_gs.c @@ -550,7 +550,7 @@ qla2x00_rff_id(scsi_qla_host_t *ha) ct_req->req.rff_id.fc4_feature = BIT_0 | BIT_1; else ct_req->req.rff_id.fc4_feature = BIT_0; - } else + } else if (qla_ini_mode_enabled(ha)) #endif ct_req->req.rff_id.fc4_feature = BIT_1; diff --git a/qla2x00t/qla_init.c b/qla2x00t/qla_init.c index 07359b540..c8fc1188b 100644 --- a/qla2x00t/qla_init.c +++ b/qla2x00t/qla_init.c @@ -129,7 +129,7 @@ qla2x00_initialize_adapter(scsi_qla_host_t *ha) /* Enable target response to SCSI bus. */ if (qla_tgt_mode_enabled(ha)) qla2x00_send_enable_lun(ha, true); - else + else if (qla_ini_mode_enabled(ha)) qla2x00_send_enable_lun(ha, false); } #endif @@ -1766,10 +1766,10 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) /* Disable Full Login after LIP */ nv->firmware_options[1] &= ~BIT_5; - /* Enable FC tapes support, just in case, it is ON by default */ + /* Enable initial LIP */ + nv->firmware_options[1] &= BIT_1; + /* Enable FC tapes support */ nv->add_firmware_options[1] |= BIT_4; - /* Enable Fibre Channel Confirm */ - nv->add_firmware_options[1] |= BIT_5; /* Enable Command Queuing in Target Mode */ nv->add_firmware_options[1] |= BIT_6; } else { @@ -1809,10 +1809,12 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) if (IS_QLA23XX(ha)) { /* Enable full duplex */ nv->firmware_options[0] |= BIT_2; - /* Disable Fast Sataus Posting */ + /* Disable Fast Status Posting */ nv->firmware_options[0] &= ~BIT_3; + /* P2P preferred, otherwise loop */ nv->add_firmware_options[1] |= BIT_5 | BIT_4; + /* out-of-order frames rassembly */ nv->special_options[0] |= BIT_6; @@ -1859,13 +1861,19 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) while (cnt--) *dptr1++ = *dptr2++; - /* Use alternate WWN? */ - if (nv->host_p[1] & BIT_7) { - memcpy(icb->node_name, nv->alternate_node_name, WWN_SIZE); - memcpy(icb->port_name, nv->alternate_port_name, WWN_SIZE); - } - /* Prepare nodename */ +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (ha->node_name_set) { + memcpy(icb->node_name, ha->tgt_node_name, WWN_SIZE); + icb->firmware_options[1] |= BIT_6; + } else +#endif + if (nv->host_p[1] & BIT_7) { + /* Use alternate WWN? */ + memcpy(icb->node_name, nv->alternate_node_name, WWN_SIZE); + memcpy(icb->port_name, nv->alternate_port_name, WWN_SIZE); + } + if ((icb->firmware_options[1] & BIT_6) == 0) { /* * Firmware will apply the following mask if the nodename was @@ -3759,7 +3767,9 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) /* Disable Full Login after LIP */ nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_13); - /* Enable FC tapes support, just in case, it is ON by default */ + /* Enable initial LIP */ + nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_9); + /* Enable FC tapes support */ nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_12); } else { if (ha->saved_set) { @@ -3820,13 +3830,19 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) qla2x00_set_model_info(ha, nv->model_name, sizeof(nv->model_name), "QLA2462"); - /* Use alternate WWN? */ - if (nv->host_p & __constant_cpu_to_le32(BIT_15)) { - memcpy(icb->node_name, nv->alternate_node_name, WWN_SIZE); - memcpy(icb->port_name, nv->alternate_port_name, WWN_SIZE); - } - /* Prepare nodename */ +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (ha->node_name_set) { + memcpy(icb->node_name, ha->tgt_node_name, WWN_SIZE); + icb->firmware_options_1 |= __constant_cpu_to_le32(BIT_14); + } else +#endif + if (nv->host_p & __constant_cpu_to_le32(BIT_15)) { + /* Use alternate WWN? */ + memcpy(icb->node_name, nv->alternate_node_name, WWN_SIZE); + memcpy(icb->port_name, nv->alternate_port_name, WWN_SIZE); + } + if ((icb->firmware_options_1 & __constant_cpu_to_le32(BIT_14)) == 0) { /* * Firmware will apply the following mask if the nodename was diff --git a/qla2x00t/qla_mbx.c b/qla2x00t/qla_mbx.c index d1890c2a9..fe6f73de3 100644 --- a/qla2x00t/qla_mbx.c +++ b/qla2x00t/qla_mbx.c @@ -1020,6 +1020,13 @@ qla2x00_init_firmware(scsi_qla_host_t *ha, uint16_t size) DEBUG11(printk("qla2x00_init_firmware(%ld): entered.\n", ha->host_no)); + if (!qla_tgt_mode_enabled(ha) && !qla_ini_mode_enabled(ha)) { + DEBUG11(printk("qla2x00_init_firmware(%ld): neither initiator, " + "nor target mode enabled, exiting\n", ha->host_no)); + rval = QLA_SUCCESS; + goto out; + } + #ifdef QL_DEBUG_LEVEL_5 if (IS_FWI2_CAPABLE(ha)) { struct init_cb_24xx *icb = (struct init_cb_24xx *)ha->init_cb; @@ -1071,6 +1078,7 @@ qla2x00_init_firmware(scsi_qla_host_t *ha, uint16_t size) ha->host_no)); } +out: return rval; } diff --git a/qla2x00t/unsupported-patches/qla_delayed_hw_init_tgt_mode_from_the_beginning.diff b/qla2x00t/unsupported-patches/qla_delayed_hw_init_tgt_mode_from_the_beginning.diff deleted file mode 100644 index 41af251b2..000000000 --- a/qla2x00t/unsupported-patches/qla_delayed_hw_init_tgt_mode_from_the_beginning.diff +++ /dev/null @@ -1,324 +0,0 @@ -The below patch fixes a problem that RHEL 5 initiators encounter when -the target is running SCST + qla2x00tgt. The qla2xxx driver that is -shipped with Red Hat supplied kernels for RHEL 5 does not handle -fc_remote_port role changes. Specifically, a role change of: - -FCP Target -> unknown -> FCP Initiator -> FCP Target - -results in the fc_remote_port failing to transition to FCP Target. -The above role change is usually triggered by a reboot of the FC -target. After the target comes up after its reboot, the initiator(s) -will be unable to re-attach their storage without a reboot of their -own (or possibly an unload/reload of the qla2xxx driver). - -The problem lies in the initiator briefly seeing the target as a FCP -Initiator, an artifact from the qla2xxx and qla2x00tgt modules loading on -the target. This patch fixes the problem by delaying firmware loading -until the qla2x00tgt module is loaded. When the qla2x00tgt is loaded, -it is forced into target mode (i.e. target_mode_enabled = 1). This -requires that SCST have its configuration loaded _before_ qla2x00tgt -is loaded. Otherwise the initiators will log into the target without -security groups in place, etc... The qla2xxx driver needs to be loaded -with the parameter "qlini_mode=disabled" to postpone the firmware load -until qla2x00tgt is loaded. See below for loading example: - - 1. modprobe scst - 2. modprobe scst_vdisk - 3. modprobe qla2xxx qlini_mode=disabled - (NOTE: You should see that firmware initialization - was posponed by looking at the output of dmesg.) - 4. Load SCST configuration (scstadmin or other utility) - 5. modprobe qla2x00tgt - -Also required is that SCST and qla2x00tgt be compiled with PROCFS -support enabled (see below why). Starting at version 2.0.0, SCST -defaults to SYSFS, so make sure "make enable_procfs" is run before -building. - -With the patch applied, the resulting role change will be the -following during a target reboot: - -FCP Target -> unknown -> FCP Target - -The isp_mod target driver does not have this problem since the card -stays uninitialized until target mode in enabled. It is, however, still -possible to have problems during role transitioning with both isp_mod -and qla2x00tgt modules. This occurs if the Qlogic HBA is configured to -load BIOS during boot. This is typically used for SAN booting and -probably should be disabled on the target to keep things simple. - -Andrew Vasquez fixed this issue for the -main line kernels and was added in 2.6.27. RHEL 5 initiators running -kernels >= 2.6.27 do not need this patch applied. - -commit 6390d1f33faecf48e31f27dd7dbe928540f8acfc -Author: Andrew Vasquez -Date: Wed Aug 13 21:36:56 2008 -0700 - - [SCSI] qla2xxx: Correct synchronization of software/firmware fcport states. - - Greg Wettstein (greg@enjellic.com) noted: - http://article.gmane.org/gmane.linux.scsi/43409 - - on a reboot of a previously recognized SCST target, the initiator - driver would be unable to re-recognize the device as a target. - It turns out that prior to the SCST software reloading and - returning it's "target-capable" abilities in the PRLI payload, - the HBA would be re-initialized as an initiator-only type port. - Since initiators typically classify themselves as an FCP-2 - capable device, both software and firmware do not perform an - explicit logout during port-loss. Unfortunately, as can be seen - by the failure case, when the port (now target-capable) returns, - firmware performs an ADISC without a follow-on PRLI, leaving - stale 'initiator-only' data in the firmware's port database. - - Correct the discrepancy by performing the explicit logout during - the transport's request to terminate-rport-io, thus synchronizing - port states and ensuring a follow-on PRLI is performed. - - Reported-by: Greg Wettstein - Signed-off-by: Andrew Vasquez - Cc: Stable Tree - Signed-off-by: James Bottomley - - -This patch wasn't applied to the qla2x00t, because it can work only with -the obsolete procfs-based interface, where LUNs initialization for the -QLA targets happens before the corresponding SCST targets created. With -the sysfs-based interface it isn't possible, because in it all LUNs are -bound to target. It's so, at least, until SCST has persistent LUN -groups, so each target immediately after it's created would have all its -LUNs configured from the corresponding persistent group. - --- - -Index: qla_init.c -=================================================================== ---- qla_init.c (revision 2148) -+++ qla_init.c (working copy) -@@ -4202,6 +4202,10 @@ int qla2xxx_tgt_register_driver(struct q - - memcpy(&qla_target, tgt_data, sizeof(qla_target)); - -+ res = qla2xxx_check_init_hardware(); -+ if (res != 0) -+ goto out; -+ - res = QLA2X_INITIATOR_MAGIC; - - out: -Index: qla_gbl.h -=================================================================== ---- qla_gbl.h (revision 2148) -+++ qla_gbl.h (working copy) -@@ -82,6 +82,8 @@ extern int qla2x00_post_hwe_work(struct - - extern void qla2x00_abort_fcport_cmds(fc_port_t *); - -+extern int qla2xxx_check_init_hardware(void); -+ - /* - * Global Functions in qla_mid.c source file. - */ -Index: qla2x_tgt.h -=================================================================== ---- qla2x_tgt.h (revision 2148) -+++ qla2x_tgt.h (working copy) -@@ -34,6 +34,8 @@ extern request_t *qla2x00_req_pkt(scsi_q - - extern struct qla_tgt_data qla_target; - -+extern bool ql2x_hardware_initialized; -+ - void qla_set_tgt_mode(scsi_qla_host_t *ha); - void qla_clear_tgt_mode(scsi_qla_host_t *ha); - -Index: qla_os.c -=================================================================== ---- qla_os.c (revision 2148) -+++ qla_os.c (working copy) -@@ -50,6 +50,9 @@ MODULE_PARM_DESC(qlini_mode, - static int ql2x_ini_mode = QLA2X_INI_MODE_EXCLUSIVE; - #endif - -+bool ql2x_hardware_initialized; -+EXPORT_SYMBOL(ql2x_hardware_initialized); -+ - /* - * SRB allocation cache - */ -@@ -3124,7 +3127,7 @@ void qla_clear_tgt_mode(scsi_qla_host_t - { - switch (ql2x_ini_mode) { - case QLA2X_INI_MODE_DISABLED: -- ha->host->active_mode = MODE_UNKNOWN; -+ ha->host->active_mode = MODE_TARGET; - break; - case QLA2X_INI_MODE_EXCLUSIVE: - ha->host->active_mode = MODE_INITIATOR; -@@ -3157,6 +3160,22 @@ static bool __init qla2x00_parse_ini_mod - - return true; - } -+ -+int qla2xxx_check_init_hardware(void) -+{ -+ int res = 0; -+ -+ if (!ql2x_hardware_initialized) { -+ res = pci_register_driver(&qla2xxx_pci_driver); -+ if (res != 0) -+ goto out; -+ ql2x_hardware_initialized = true; -+ } -+ -+out: -+ return res; -+} -+ - #else - static inline bool qla2x00_parse_ini_mode(void) - { -@@ -3193,7 +3212,8 @@ qla2x00_module_init(void) - - if (!qla2x00_parse_ini_mode()) { - printk("Wrong qlini_mode value %s\n", qlini_mode); -- return -EINVAL; -+ ret = -EINVAL; -+ goto out; - } - - /* Allocate cache for SRBs. */ -@@ -3202,7 +3222,8 @@ qla2x00_module_init(void) - if (srb_cachep == NULL) { - printk(KERN_ERR - "qla2xxx: Unable to allocate SRB cache...Failing load!\n"); -- return -ENOMEM; -+ ret = -ENOMEM; -+ goto out; - } - - /* Derive version string. */ -@@ -3213,26 +3234,46 @@ qla2x00_module_init(void) - qla2xxx_transport_template = - fc_attach_transport(&qla2xxx_transport_functions); - if (!qla2xxx_transport_template) { -- kmem_cache_destroy(srb_cachep); -- return -ENODEV; -+ ret = -ENODEV; -+ goto out_cache_destroy; - } - qla2xxx_transport_vport_template = - fc_attach_transport(&qla2xxx_transport_vport_functions); - if (!qla2xxx_transport_vport_template) { -- kmem_cache_destroy(srb_cachep); -- fc_release_transport(qla2xxx_transport_template); -- return -ENODEV; -+ ret = -ENODEV; -+ goto out_fc_release_transport_template; - } - - printk(KERN_INFO "QLogic Fibre Channel HBA Driver: %s\n", - qla2x00_version_str); -- ret = pci_register_driver(&qla2xxx_pci_driver); -- if (ret) { -- kmem_cache_destroy(srb_cachep); -- fc_release_transport(qla2xxx_transport_template); -- fc_release_transport(qla2xxx_transport_vport_template); -+ -+#ifdef CONFIG_SCSI_QLA2XXX_TARGET -+ if (ql2x_ini_mode == QLA2X_INI_MODE_DISABLED) { -+ printk(KERN_INFO "qlini_mode disabled (%d), delaying hardware " -+ "initialization until target mode add-on load\n", -+ QLA2X_INI_MODE_DISABLED); -+ goto out; - } -+#endif -+ -+ ret = pci_register_driver(&qla2xxx_pci_driver); -+ if (ret) -+ goto out_fc_release_vport_template; -+ -+ ql2x_hardware_initialized = true; -+ -+out: - return ret; -+ -+out_fc_release_vport_template: -+ fc_release_transport(qla2xxx_transport_vport_template); -+ -+out_fc_release_transport_template: -+ fc_release_transport(qla2xxx_transport_template); -+ -+out_cache_destroy: -+ kmem_cache_destroy(srb_cachep); -+ goto out; - } - - /** -@@ -3241,8 +3282,10 @@ qla2x00_module_init(void) - static void __exit - qla2x00_module_exit(void) - { -- pci_unregister_driver(&qla2xxx_pci_driver); -- qla2x00_release_firmware(); -+ if (ql2x_hardware_initialized) { -+ pci_unregister_driver(&qla2xxx_pci_driver); -+ qla2x00_release_firmware(); -+ } - kmem_cache_destroy(srb_cachep); - fc_release_transport(qla2xxx_transport_template); - fc_release_transport(qla2xxx_transport_vport_template); -Index: qla2x00-target/qla2x00t.c -=================================================================== ---- qla2x00-target/qla2x00t.c (revision 2148) -+++ qla2x00-target/qla2x00t.c (working copy) -@@ -314,7 +314,8 @@ static int q2t_target_detect(struct scst - goto out; - } - -- qla2xxx_add_targets(); -+ if (ql2x_hardware_initialized) -+ qla2xxx_add_targets(); - - res = 0; - -@@ -5000,9 +5001,12 @@ static int q2t_add_target(scsi_qla_host_ - - TRACE_ENTRY(); - -- TRACE_DBG("Registering target for host %ld(%p)", ha->host_no, ha); -+ if ((ha->q2t_tgt != NULL) || (ha->tgt != NULL)) { -+ res = 0; -+ goto out; -+ } - -- sBUG_ON((ha->q2t_tgt != NULL) || (ha->tgt != NULL)); -+ TRACE_DBG("Registering target for host %ld(%p)", ha->host_no, ha); - - tgt = kzalloc(sizeof(*tgt), GFP_KERNEL); - if (tgt == NULL) { -@@ -5029,7 +5033,8 @@ static int q2t_add_target(scsi_qla_host_ - - ha->q2t_tgt = tgt; - -- if (q2t_get_target_name(ha, &wwn) != 0) -+ res = q2t_get_target_name(ha, &wwn); -+ if (res != 0) - goto out_free; - - tgt->scst_tgt = scst_register_target(&tgt2x_template, wwn); -@@ -5084,6 +5089,13 @@ static int q2t_add_target(scsi_qla_host_ - scst_tgt_set_sg_tablesize(tgt->scst_tgt, sg_tablesize); - scst_tgt_set_tgt_priv(tgt->scst_tgt, tgt); - -+ if (qla_tgt_mode_enabled(ha)) { -+ ha->tgt = ha->q2t_tgt; -+ ha->tgt->tgt_stop = 0; -+ } -+ -+ res = 0; -+ - out: - TRACE_EXIT_RES(res); - return res;