diff --git a/qla2x00t/qla2x00-target/ChangeLog b/qla2x00t/qla2x00-target/ChangeLog index aae7fd4f5..1e06274b1 100644 --- a/qla2x00t/qla2x00-target/ChangeLog +++ b/qla2x00t/qla2x00-target/ChangeLog @@ -1,8 +1,17 @@ +Summary of changes between versions 2.0.0 and 2.1.0 +--------------------------------------------------- + + - Complete NPIV support added. Particularly, now SCST core sees + all the virtual targets and provide separate target-oriented access + control for them. Thanks a lot to Uri Yanai and Dorit + Halsadi! + + Summary of changes between versions 1.0.2 and 2.0.0 --------------------------------------------------- - Support for 24xx/25xx added - + - Disable by default initiator mode if target mode enabled. It can be changed using .config option CONFIG_SCSI_QLA2XXX_TARGET_DISABLE_INI_MODE. @@ -17,7 +26,7 @@ Summary of changes between versions 1.0.1 and 1.0.2 module unload fixed - Implemented abort on timeout of stuck in the firmware commands - + Summary of changes between versions 1.0.0 and 1.0.1 --------------------------------------------------- diff --git a/qla2x00t/qla2x00-target/README b/qla2x00t/qla2x00-target/README index 5da8229fd..8692974bf 100644 --- a/qla2x00t/qla2x00-target/README +++ b/qla2x00t/qla2x00-target/README @@ -17,14 +17,6 @@ Linux kernel 2.6.26 and higher. Sorry, kernels below 2.6.26 are not supported, because it's too hard to backport used initiator driver to older kernels. -NPIV is partially supported by this driver. You can create virtual -targets using standard Linux interface by echoing wwpn:wwnn into -/sys/class/fc_host/hostX/vport_create and work with them, but SCST core -will not see those virtual targets and, hence, provide the -target-oriented access control for them. However, the initiator-oriented -access control will still work very well. Note, you need NPIV-supporting -firmware as well as NPIV-supporting switches to use NPIV. - The original initiator driver was taken from the kernel 2.6.26. Also the following 2.6.26.x commits have been applied to it (upstream ID): 048feec5548c0582ee96148c61b87cccbcb5f9be, @@ -167,6 +159,34 @@ option needs a special firmware with class 2 support. Disabled by default. +N_Port ID Virtualization +------------------------ + +N_Port ID Virtualization(NPIV) is a Fibre Channel facility allowing +multiple N_Port IDs to share a single physical N_Port. NPIV is fully +supported by this driver. You must have 24xx+ ISPs with NPIV-supporting +and NPIV-switches switch(es) to use this facility. + +You can add NPIV targets by echoing + +add_target target_name node_name=node_name_value; parent_host=parent_host_value + +in /sys/kernel/scst_tgt/targets/qla2x00t/mgmt. + +Removing NPIV targets is done by echoing + +del_target target_name + +in/sys/kernel/scst_tgt/targets/qla2x00t/mgmt. + +Also, you can create and remove NPIV targets using the standard Linux +interface (i.e. echoing wwpn:wwnn into /sys/class/fc_host/hostX/vport_create +and /sys/class/fc_host/hostX/vport_delete). + +It is recommended to use scstadmin utility and its config file to +configure virtual NPIV targets instead of the above direct interface. + + Compilation options ------------------- @@ -211,6 +231,12 @@ entries: - version - read-only attribute, which allows to see version of this driver and enabled optional features. + - mgmt - main management entry, which allows to configure NPIV targets. + See content of this file for help how to use it. + + - hw_target (hardware target only) - read-only attribute with value 1. + It allows to distinguish hardware and virtual targets. + Each target subdirectory contains the following entries: - host - link pointing on the corresponding scsi_host of the initiator @@ -241,6 +267,12 @@ Each target subdirectory contains the following entries: until rel_tgt_id becomes unique. This attribute initialized unique by SCST by default. + - node_name (NPIV targets only) - read-only attribute, which allows to see + the target World Wide Node Name. + + - parent_host (NPIV target only) - read-only attribute, which allows to see + the parent HBA World Wide Port Name (WWPN). + Subdirectory "sessions" contains one subdirectory for each connected session with name equal to port name of the connected initiator. @@ -253,9 +285,10 @@ Each session subdirectory contains the following entries: - commands - contains overall number of SCSI commands in this session. -Below is a sample script, which configures 1 virtual disk "disk1" using -/disk1 image for usage with 25:00:00:f0:98:87:92:f3 target. All -initiators connected to this target will see this device. +Below is a sample script, which configures 2 virtual disk "disk1" using +/disk1 image for usage with 25:00:00:f0:98:87:92:f3 hardware target, and +"disk2" using /disk2 image for usage with 50:50:00:00:00:00:00:11 NPIV +target. All initiators connected to this targets will see those devices. #!/bin/bash @@ -263,11 +296,17 @@ modprobe scst modprobe scst_vdisk echo "add_device disk1 filename=/disk1; nv_cache=1" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt +echo "add_device disk2 filename=/disk2; nv_cache=1" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt modprobe qla2x00tgt +echo "add_target 50:50:00:00:00:00:00:11 node_name=50:50:00:00:00:00:00:00;parent_host=25:00:00:f0:98:87:92:f3" >\ +/sys/kernel/scst_tgt/targets/qla2x00t/mgmt + echo "add disk1 0" >/sys/kernel/scst_tgt/targets/qla2x00t/25:00:00:f0:98:87:92:f3/luns/mgmt +echo "add disk2 0" >/sys/kernel/scst_tgt/targets/qla2x00t/50:50:00:00:00:00:00:11/luns/mgmt echo 1 >/sys/kernel/scst_tgt/targets/qla2x00t/25:00:00:f0:98:87:92:f3/enabled +echo 1 >/sys/kernel/scst_tgt/targets/qla2x00t/50:50:00:00:00:00:00:11/enabled Below is another sample script, which configures 1 real local SCSI disk 0:0:1:0 for usage with 25:00:00:f0:98:87:92:f3 target: @@ -456,6 +495,7 @@ The resulting overall SCST sysfs hierarchy with initiator | | | | `-- read_only | | | `-- mgmt | | |-- rel_tgt_id +| | |-- hw_target | | `-- sessions | | `-- 25:00:00:f0:99:87:94:a3 | | |-- active_commands @@ -463,7 +503,8 @@ The resulting overall SCST sysfs hierarchy with initiator | | |-- initiator_name | | `-- luns -> ../../ini_groups/25:00:00:f0:99:87:94:a3/luns | |-- trace_level -| `-- version +| |-- version +| `-- mgmt |-- threads |-- trace_level `-- version @@ -503,4 +544,7 @@ help in debugging. * Ming Zhang for fixes. + * Uri Yanai and Dorit Halsadi + for adding full NPIV support. + Vladislav Bolkhovitin , http://scst.sourceforge.net diff --git a/qla2x00t/qla2x00-target/ToDo b/qla2x00t/qla2x00-target/ToDo index 2351a6775..c6c185e7d 100644 --- a/qla2x00t/qla2x00-target/ToDo +++ b/qla2x00t/qla2x00-target/ToDo @@ -3,9 +3,6 @@ Known issues and unimplemented features - Allow to set port and node names through sysfs. - - Complete NPIV support. Particularly, SCST core should see all the virtual - targets and provide separate target-oriented access control for them. - - Minor "ToDo"'s spread in the code. - If a Linux initiator asks for devices using INQUIRY command too soon diff --git a/qla2x00t/qla2x00-target/qla2x00t.c b/qla2x00t/qla2x00-target/qla2x00t.c index a7e85efae..5aa096aaf 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.c +++ b/qla2x00t/qla2x00-target/qla2x00t.c @@ -139,10 +139,30 @@ static const struct attribute *q2t_tgt_attrs[] = { 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, + 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); + +static ssize_t q2t_vp_parent_host_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); + +static struct kobj_attribute q2t_vp_parent_host_attr = + __ATTR(parent_host, S_IRUGO, q2t_vp_parent_host_show, NULL); + #endif /* CONFIG_SCST_PROC */ static int q2t_enable_tgt(struct scst_tgt *tgt, bool enable); static bool q2t_is_tgt_enabled(struct scst_tgt *tgt); +static ssize_t q2t_add_vtarget(const char *target_name, char *params); +static ssize_t q2t_del_vtarget(const char *target_name); /* * Global Variables @@ -182,6 +202,9 @@ static struct scst_tgt_template tgt2x_template = { .enable_target = q2t_enable_tgt, .is_target_enabled = q2t_is_tgt_enabled, #ifndef CONFIG_SCST_PROC + .add_target = q2t_add_vtarget, + .del_target = q2t_del_vtarget, + .add_target_parameters = "node_name, parent_host", .tgtt_attrs = q2t_attrs, .tgt_attrs = q2t_tgt_attrs, #endif @@ -267,7 +290,13 @@ static inline struct q2t_sess *q2t_find_sess_by_s_id_le(struct q2t_tgt *tgt, /* ha->hardware_lock supposed to be held on entry */ static inline void q2t_exec_queue(scsi_qla_host_t *ha) { - qla2x00_isp_cmd(ha); + qla2x00_isp_cmd(to_qla_parent(ha)); +} + +/* ha->hardware_lock supposed to be held on entry */ +static inline request_t *q2t_req_pkt(scsi_qla_host_t *ha) +{ + return qla2x00_req_pkt(to_qla_parent(ha)); } /* Might release hw lock, then reaquire!! */ @@ -285,6 +314,193 @@ static inline int q2t_issue_marker(scsi_qla_host_t *ha, int ha_locked) return QLA_SUCCESS; } +static inline +scsi_qla_host_t *q2t_find_host_by_d_id(scsi_qla_host_t *ha, uint8_t *d_id) +{ + if ((ha->d_id.b.area != d_id[1]) || (ha->d_id.b.domain != d_id[0])) + return NULL; + + if (ha->d_id.b.al_pa == d_id[2]) + return ha; + + if (IS_FWI2_CAPABLE(ha)) { + uint8_t vp_idx; + sBUG_ON(ha->tgt_vp_map == NULL); + vp_idx = ha->tgt_vp_map[d_id[2]].idx; + if (likely(test_bit(vp_idx, ha->vp_idx_map))) + return ha->tgt_vp_map[vp_idx].vha; + } + + return NULL; +} + +static inline +scsi_qla_host_t *q2t_find_host_by_vp_idx(scsi_qla_host_t *ha, uint16_t vp_idx) +{ + if (ha->vp_idx == vp_idx) + return ha; + + if (IS_FWI2_CAPABLE(ha)) { + sBUG_ON(ha->tgt_vp_map == NULL); + if (likely(test_bit(vp_idx, ha->vp_idx_map))) + return ha->tgt_vp_map[vp_idx].vha; + } + + return NULL; +} + +static void q24_atio_pkt_all_vps(scsi_qla_host_t *ha, atio7_entry_t *atio) +{ + TRACE_ENTRY(); + + sBUG_ON(ha == NULL); + + switch (atio->entry_type) { + case ATIO_TYPE7: + { + scsi_qla_host_t *host = q2t_find_host_by_d_id(ha, atio->fcp_hdr.d_id); + if (unlikely(NULL == host)) { + PRINT_ERROR("qla2x00t(%ld): Received ATIO_TYPE7 " + "with unknown d_id %x:%x:%x", ha->instance, + atio->fcp_hdr.d_id[0], atio->fcp_hdr.d_id[1], + atio->fcp_hdr.d_id[2]); + break; + } + q24_atio_pkt(host, atio); + break; + } + + case IMMED_NOTIFY_TYPE: + { + scsi_qla_host_t *host = ha; + if (IS_FWI2_CAPABLE(ha)) { + notify24xx_entry_t *entry = (notify24xx_entry_t *)atio; + if (IMM_NTFY_LIP_LINK_REINIT != entry->status) { + host = q2t_find_host_by_vp_idx(ha, + entry->vp_index); + if (unlikely(!host)) { + PRINT_ERROR("qla2x00t(%ld): Received " + "ATIO (IMMED_NOTIFY_TYPE) " + "with unknown vp_index %d", + ha->instance, entry->vp_index); + break; + } + } + } + q24_atio_pkt(host, atio); + break; + } + + default: + PRINT_ERROR("qla2x00t(%ld): Received unknown ATIO atio " + "type %x", ha->instance, atio->entry_type); + break; + } + + TRACE_EXIT(); + return; +} + +static void q2t_response_pkt_all_vps(scsi_qla_host_t *ha, response_t *pkt) +{ + TRACE_ENTRY(); + + sBUG_ON(ha == NULL); + + switch (pkt->entry_type) { + case CTIO_TYPE7: + { + ctio7_fw_entry_t *entry = (ctio7_fw_entry_t *)pkt; + scsi_qla_host_t *host = q2t_find_host_by_vp_idx(ha, + entry->vp_index); + if (unlikely(!host)) { + PRINT_ERROR("qla2x00t(%ld): Response pkt (CTIO_TYPE7) " + "received, with unknown vp_index %d", + ha->instance, entry->vp_index); + break; + } + q2t_response_pkt(host, pkt); + break; + } + + case IMMED_NOTIFY_TYPE: + { + scsi_qla_host_t *host = ha; + if (IS_FWI2_CAPABLE(ha)) { + notify24xx_entry_t *entry = (notify24xx_entry_t *)pkt; + host = q2t_find_host_by_vp_idx(ha, entry->vp_index); + if (unlikely(!host)) { + PRINT_ERROR("qla2x00t(%ld): Response pkt " + "(IMMED_NOTIFY_TYPE) received, " + "with unknown vp_index %d", + ha->instance, entry->vp_index); + break; + } + } + q2t_response_pkt(host, pkt); + break; + } + + case NOTIFY_ACK_TYPE: + { + scsi_qla_host_t *host = ha; + if (IS_FWI2_CAPABLE(ha)) { + nack24xx_entry_t *entry = (nack24xx_entry_t *)pkt; + if (0xFF != entry->vp_index) { + host = q2t_find_host_by_vp_idx(ha, + entry->vp_index); + if (unlikely(!host)) { + PRINT_ERROR("qla2x00t(%ld): Response " + "pkt (NOTIFY_ACK_TYPE) " + "received, with unknown " + "vp_index %d", ha->instance, + entry->vp_index); + break; + } + } + } + q2t_response_pkt(host, pkt); + break; + } + + case ABTS_RECV_24XX: + { + abts24_recv_entry_t *entry = (abts24_recv_entry_t *)pkt; + scsi_qla_host_t *host = q2t_find_host_by_vp_idx(ha, + entry->vp_index); + if (unlikely(!host)) { + PRINT_ERROR("qla2x00t(%ld): Response pkt " + "(ABTS_RECV_24XX) received, with unknown " + "vp_index %d", ha->instance, entry->vp_index); + break; + } + q2t_response_pkt(host, pkt); + break; + } + + case ABTS_RESP_24XX: + { + abts24_resp_entry_t *entry = (abts24_resp_entry_t *)pkt; + scsi_qla_host_t *host = q2t_find_host_by_vp_idx(ha, + entry->vp_index); + if (unlikely(!host)) { + PRINT_ERROR("qla2x00t(%ld): Response pkt " + "(ABTS_RECV_24XX) received, with unknown " + "vp_index %d", ha->instance, entry->vp_index); + break; + } + q2t_response_pkt(host, pkt); + break; + } + + default: + q2t_response_pkt(ha, pkt); + break; + } + + TRACE_EXIT(); + return; +} /* * Registers with initiator driver (but target mode isn't enabled till * it's turned on via sysfs) @@ -294,8 +510,8 @@ static int q2t_target_detect(struct scst_tgt_template *tgtt) int res, rc; struct qla_tgt_data t = { .magic = QLA2X_TARGET_MAGIC, - .tgt24_atio_pkt = q24_atio_pkt, - .tgt_response_pkt = q2t_response_pkt, + .tgt24_atio_pkt = q24_atio_pkt_all_vps, + .tgt_response_pkt = q2t_response_pkt_all_vps, .tgt2x_ctio_completion = q2x_ctio_completion, .tgt_async_event = q2t_async_event, .tgt_host_action = q2t_host_action, @@ -334,7 +550,7 @@ static void q2t_free_session_done(struct scst_session *scst_sess) { struct q2t_sess *sess; struct q2t_tgt *tgt; - scsi_qla_host_t *ha; + scsi_qla_host_t *ha, *vha; unsigned long flags; TRACE_ENTRY(); @@ -355,16 +571,17 @@ static void q2t_free_session_done(struct scst_session *scst_sess) list_empty(&tgt->sess_list), tgt->sess_count); ha = tgt->ha; + vha = to_qla_parent(ha); /* * We need to protect against race, when tgt is freed before or * inside wake_up() */ - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(&vha->hardware_lock, flags); tgt->sess_count--; if (tgt->sess_count == 0) wake_up_all(&tgt->waitQ); - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&vha->hardware_lock, flags); out: TRACE_EXIT(); @@ -509,14 +726,15 @@ static void q2t_alloc_session_done(struct scst_session *scst_sess, struct q2t_sess *sess = (struct q2t_sess *)data; struct q2t_tgt *tgt = sess->tgt; scsi_qla_host_t *ha = tgt->ha; + scsi_qla_host_t *vha = to_qla_parent(ha); unsigned long flags; PRINT_INFO("qla2x00t(%ld): Session initialization failed", ha->instance); - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(&vha->hardware_lock, flags); q2t_sess_put(sess); - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&vha->hardware_lock, flags); } TRACE_EXIT(); @@ -527,12 +745,13 @@ static void q2t_del_sess_timer_fn(unsigned long arg) { struct q2t_tgt *tgt = (struct q2t_tgt *)arg; scsi_qla_host_t *ha = tgt->ha; + scsi_qla_host_t *vha = to_qla_parent(ha); struct q2t_sess *sess; unsigned long flags; TRACE_ENTRY(); - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(&vha->hardware_lock, flags); while (!list_empty(&tgt->del_sess_list)) { sess = list_entry(tgt->del_sess_list.next, typeof(*sess), del_list_entry); @@ -550,7 +769,7 @@ static void q2t_del_sess_timer_fn(unsigned long arg) break; } } - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&vha->hardware_lock, flags); TRACE_EXIT(); return; @@ -569,11 +788,12 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport, const int wwn_str_len = 3*WWN_SIZE+2; struct q2t_tgt *tgt = ha->tgt; struct q2t_sess *sess; + scsi_qla_host_t *vha = to_qla_parent(ha); TRACE_ENTRY(); /* Check to avoid double sessions */ - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) { if ((sess->port_name[0] == fcport->port_name[0]) && (sess->port_name[1] == fcport->port_name[1]) && @@ -602,11 +822,11 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport, sess->conf_compl_supported = fcport->conf_compl_supported; if (sess->local && !local) sess->local = false; - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); goto out; } } - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); /* We are under tgt_mutex, so a new sess can't be added behind us */ @@ -660,11 +880,11 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport, goto out_free_sess_wwn; } - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); TRACE_MGMT_DBG("Adding sess %p to tgt %p", sess, tgt); list_add_tail(&sess->sess_list_entry, &tgt->sess_list); tgt->sess_count++; - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); PRINT_INFO("qla2x00t(%ld): %ssession for wwn %s (loop_id %d, " "s_id %x:%x:%x, confirmed completion %ssupported) added", @@ -692,6 +912,7 @@ static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport) { struct q2t_tgt *tgt; struct q2t_sess *sess; + scsi_qla_host_t *vha = to_qla_parent(ha); TRACE_ENTRY(); @@ -705,13 +926,13 @@ static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport) if (tgt->tgt_stop) goto out_unlock; - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); sess = q2t_find_sess_by_loop_id(tgt, fcport->loop_id); if (sess == NULL) { - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); sess = q2t_create_sess(ha, fcport, false); - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); if (sess != NULL) q2t_sess_put(sess); /* put the extra creation ref */ } else { @@ -740,7 +961,7 @@ static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport) sess->local = 0; } - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); out_unlock: mutex_unlock(&ha->tgt_mutex); @@ -754,6 +975,7 @@ static void q2t_fc_port_deleted(scsi_qla_host_t *ha, fc_port_t *fcport) struct q2t_tgt *tgt; struct q2t_sess *sess; uint32_t dev_loss_tmo; + scsi_qla_host_t *vha = to_qla_parent(ha); TRACE_ENTRY(); @@ -769,7 +991,7 @@ static void q2t_fc_port_deleted(scsi_qla_host_t *ha, fc_port_t *fcport) if (tgt->tgt_stop) goto out_unlock; - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); sess = q2t_find_sess_by_loop_id(tgt, fcport->loop_id); if (sess == NULL) @@ -800,7 +1022,7 @@ static void q2t_fc_port_deleted(scsi_qla_host_t *ha, fc_port_t *fcport) } out_unlock_ha: - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); out_unlock: mutex_unlock(&ha->tgt_mutex); @@ -813,16 +1035,17 @@ static inline int test_tgt_sess_count(struct q2t_tgt *tgt) { unsigned long flags; int res; + scsi_qla_host_t *vha = to_qla_parent(tgt->ha); /* * We need to protect against race, when tgt is freed before or * inside wake_up() */ - spin_lock_irqsave(&tgt->ha->hardware_lock, flags); + spin_lock_irqsave(&vha->hardware_lock, flags); TRACE_DBG("tgt %p, empty(sess_list)=%d sess_count=%d", tgt, list_empty(&tgt->sess_list), tgt->sess_count); res = (tgt->sess_count == 0); - spin_unlock_irqrestore(&tgt->ha->hardware_lock, flags); + spin_unlock_irqrestore(&vha->hardware_lock, flags); return res; } @@ -832,6 +1055,7 @@ static void q2t_target_stop(struct scst_tgt *scst_tgt) { struct q2t_tgt *tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); scsi_qla_host_t *ha = tgt->ha; + scsi_qla_host_t *vha = to_qla_parent(ha); TRACE_ENTRY(); @@ -843,10 +1067,10 @@ static void q2t_target_stop(struct scst_tgt *scst_tgt) */ mutex_lock(&ha->tgt_mutex); - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); tgt->tgt_stop = 1; q2t_clear_tgt_db(tgt, false); - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); mutex_unlock(&ha->tgt_mutex); del_timer_sync(&tgt->sess_del_timer); @@ -867,7 +1091,7 @@ static void q2t_target_stop(struct scst_tgt *scst_tgt) wait_event(tgt->waitQ, test_tgt_sess_count(tgt)); /* Big hammer */ - if (!ha->host_shutting_down && qla_tgt_mode_enabled(ha)) + if (!vha->host_shutting_down && qla_tgt_mode_enabled(ha)) qla2x00_disable_tgt_mode(ha); /* Wait for sessions to clear out (just in case) */ @@ -877,14 +1101,14 @@ static void q2t_target_stop(struct scst_tgt *scst_tgt) tgt->irq_cmd_count, tgt); mutex_lock(&ha->tgt_mutex); - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); while (tgt->irq_cmd_count != 0) { - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); udelay(2); - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); } ha->tgt = NULL; - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); mutex_unlock(&ha->tgt_mutex); TRACE_MGMT_DBG("Stop of tgt %p finished", tgt); @@ -929,7 +1153,7 @@ static void q2x_modify_command_count(scsi_qla_host_t *ha, int cmd_count, /* Sending marker isn't necessary, since we called from ISR */ - pkt = (modify_lun_entry_t *)qla2x00_req_pkt(ha); + pkt = (modify_lun_entry_t *)q2t_req_pkt(ha); if (pkt == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " "request packet", ha->instance, __func__); @@ -984,7 +1208,7 @@ static void q2x_send_notify_ack(scsi_qla_host_t *ha, notify_entry_t *iocb, if (q2t_issue_marker(ha, 1) != QLA_SUCCESS) goto out; - ntfy = (nack_entry_t *)qla2x00_req_pkt(ha); + ntfy = (nack_entry_t *)q2t_req_pkt(ha); if (ntfy == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " "request packet", ha->instance, __func__); @@ -1018,8 +1242,8 @@ static void q2x_send_notify_ack(scsi_qla_host_t *ha, notify_entry_t *iocb, } TRACE(TRACE_SCSI, "Sending Notify Ack Seq %#x -> I %#x St %#x RC %#x", - le16_to_cpu(iocb->seq_id), GET_TARGET_ID(ha, iocb), - le16_to_cpu(iocb->status), le16_to_cpu(ntfy->resp_code)); + le16_to_cpu(iocb->seq_id), GET_TARGET_ID(ha, iocb), + le16_to_cpu(iocb->status), le16_to_cpu(ntfy->resp_code)); TRACE_BUFFER("Notify Ack packet data", ntfy, REQUEST_ENTRY_SIZE); q2t_exec_queue(ha); @@ -1048,7 +1272,7 @@ static void q24_send_abts_resp(scsi_qla_host_t *ha, if (q2t_issue_marker(ha, 1) != QLA_SUCCESS) goto out; - resp = (abts24_resp_entry_t *)qla2x00_req_pkt(ha); + resp = (abts24_resp_entry_t *)q2t_req_pkt(ha); if (resp == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " "request packet", ha->instance, __func__); @@ -1058,6 +1282,7 @@ static void q24_send_abts_resp(scsi_qla_host_t *ha, resp->entry_type = ABTS_RESP_24XX; resp->entry_count = 1; resp->nport_handle = abts->nport_handle; + resp->vp_index = ha->vp_idx; resp->sof_type = abts->sof_type; resp->exchange_address = abts->exchange_address; resp->fcp_hdr_le = abts->fcp_hdr_le; @@ -1125,7 +1350,7 @@ static void q24_retry_term_exchange(scsi_qla_host_t *ha, if (q2t_issue_marker(ha, 1) != QLA_SUCCESS) goto out; - ctio = (ctio7_status1_entry_t *)qla2x00_req_pkt(ha); + ctio = (ctio7_status1_entry_t *)q2t_req_pkt(ha); if (ctio == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " "request packet", ha->instance, __func__); @@ -1142,6 +1367,7 @@ static void q24_retry_term_exchange(scsi_qla_host_t *ha, ctio->common.nport_handle = entry->nport_handle; ctio->common.handle = Q2T_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; ctio->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT); + ctio->common.vp_index = ha->vp_idx; ctio->common.initiator_id[0] = entry->fcp_hdr_le.d_id[0]; ctio->common.initiator_id[1] = entry->fcp_hdr_le.d_id[1]; ctio->common.initiator_id[2] = entry->fcp_hdr_le.d_id[2]; @@ -1248,7 +1474,7 @@ static void q24_send_task_mgmt_ctio(scsi_qla_host_t *ha, if (q2t_issue_marker(ha, 1) != QLA_SUCCESS) goto out; - ctio = (ctio7_status1_entry_t *)qla2x00_req_pkt(ha); + ctio = (ctio7_status1_entry_t *)q2t_req_pkt(ha); if (ctio == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " "request packet", ha->instance, __func__); @@ -1260,6 +1486,7 @@ static void q24_send_task_mgmt_ctio(scsi_qla_host_t *ha, ctio->common.handle = Q2T_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; ctio->common.nport_handle = mcmd->sess->loop_id; ctio->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT); + ctio->common.vp_index = ha->vp_idx; ctio->common.initiator_id[0] = atio->fcp_hdr.s_id[2]; ctio->common.initiator_id[1] = atio->fcp_hdr.s_id[1]; ctio->common.initiator_id[2] = atio->fcp_hdr.s_id[0]; @@ -1300,7 +1527,7 @@ static void q24_send_notify_ack(scsi_qla_host_t *ha, if (ha->tgt != NULL) ha->tgt->notify_ack_expected++; - nack = (nack24xx_entry_t *)qla2x00_req_pkt(ha); + nack = (nack24xx_entry_t *)q2t_req_pkt(ha); if (nack == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " "request packet", ha->instance, __func__); @@ -1324,6 +1551,7 @@ static void q24_send_notify_ack(scsi_qla_host_t *ha, nack->srr_reject_code = srr_reject_code; nack->srr_reject_code_expl = srr_explan; nack->ox_id = iocb->ox_id; + nack->vp_index = iocb->vp_index; TRACE(TRACE_SCSI, "Sending 24xx Notify Ack %d", nack->status); TRACE_BUFFER("24xx Notify Ack packet data", nack, sizeof(*nack)); @@ -1366,7 +1594,7 @@ static void q2t_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd) { struct q2t_mgmt_cmd *mcmd; unsigned long flags; - scsi_qla_host_t *ha; + scsi_qla_host_t *ha, *vha; TRACE_ENTRY(); @@ -1380,8 +1608,9 @@ static void q2t_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd) } ha = mcmd->sess->tgt->ha; + vha = to_qla_parent(ha); - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(&vha->hardware_lock, flags); if (IS_FWI2_CAPABLE(ha)) { if (mcmd->flags == Q24_MGMT_SEND_NACK) { q24_send_notify_ack(ha, @@ -1402,7 +1631,7 @@ static void q2t_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd) q2x_send_notify_ack(ha, &mcmd->orig_iocb.notify_entry, 0, resp_code, 1, 0, 0, 0); } - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&vha->hardware_lock, flags); scst_mgmt_cmd_set_tgt_priv(scst_mcmd, NULL); mempool_free(mcmd, q2t_mgmt_cmd_mempool); @@ -1452,11 +1681,14 @@ out_err: static int q2t_check_reserve_free_req(scsi_qla_host_t *ha, uint32_t req_cnt) { int res = SCST_TGT_RES_SUCCESS; - device_reg_t __iomem *reg = ha->iobase; + device_reg_t __iomem *reg; uint32_t cnt; TRACE_ENTRY(); + ha = to_qla_parent(ha); + reg = ha->iobase; + if (ha->req_q_cnt < (req_cnt + 2)) { if (IS_FWI2_CAPABLE(ha)) cnt = (uint16_t)RD_REG_DWORD( @@ -1494,6 +1726,8 @@ out: */ static inline void *q2t_get_req_pkt(scsi_qla_host_t *ha) { + ha = to_qla_parent(ha); + /* Adjust ring index. */ ha->req_ring_index++; if (ha->req_ring_index == ha->request_q_length) { @@ -1582,12 +1816,13 @@ static int q24_build_ctio_pkt(struct q2t_prm *prm) TRACE_ENTRY(); - pkt = (ctio7_status0_entry_t *)ha->request_ring_ptr; + pkt = (ctio7_status0_entry_t *)to_qla_parent(ha)->request_ring_ptr; prm->pkt = pkt; memset(pkt, 0, sizeof(*pkt)); pkt->common.entry_type = CTIO_TYPE7; pkt->common.entry_count = (uint8_t)prm->req_cnt; + pkt->common.vp_index = ha->vp_idx; h = q2t_make_handle(ha); if (unlikely(h == Q2T_NULL_HANDLE)) { @@ -1829,6 +2064,7 @@ static int q2t_pre_xmit_response(struct q2t_cmd *cmd, int res; struct q2t_tgt *tgt = cmd->tgt; scsi_qla_host_t *ha = tgt->ha; + scsi_qla_host_t *vha = to_qla_parent(ha); uint16_t full_req_cnt; struct scst_cmd *scst_cmd = cmd->scst_cmd; @@ -1923,7 +2159,7 @@ static int q2t_pre_xmit_response(struct q2t_cmd *cmd, prm->req_cnt, full_req_cnt, prm->add_status_pkt); /* Acquire ring specific lock */ - spin_lock_irqsave(&ha->hardware_lock, *flags); + spin_lock_irqsave(&vha->hardware_lock, *flags); /* Does F/W have an IOCBs for this request */ res = q2t_check_reserve_free_req(ha, full_req_cnt); @@ -1941,7 +2177,7 @@ out_unlock_free_unmap: cmd->dma_data_direction); /* Release ring specific lock */ - spin_unlock_irqrestore(&ha->hardware_lock, *flags); + spin_unlock_irqrestore(&vha->hardware_lock, *flags); goto out; } @@ -1999,7 +2235,7 @@ static int __q2x_xmit_response(struct q2t_cmd *cmd, int xmit_type) { int res; unsigned long flags; - scsi_qla_host_t *ha; + scsi_qla_host_t *ha, *vha; struct q2t_prm prm; ctio_common_entry_t *pkt; @@ -2017,6 +2253,7 @@ static int __q2x_xmit_response(struct q2t_cmd *cmd, int xmit_type) /* Here ha->hardware_lock already locked */ ha = prm.tgt->ha; + vha = to_qla_parent(ha); q2x_build_ctio_pkt(&prm); pkt = (ctio_common_entry_t *)prm.pkt; @@ -2071,7 +2308,7 @@ static int __q2x_xmit_response(struct q2t_cmd *cmd, int xmit_type) q2t_exec_queue(ha); /* Release ring specific lock */ - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&vha->hardware_lock, flags); out: TRACE_EXIT_RES(res); @@ -2226,7 +2463,7 @@ static int __q24_xmit_response(struct q2t_cmd *cmd, int xmit_type) { int res; unsigned long flags; - scsi_qla_host_t *ha; + scsi_qla_host_t *ha, *vha; struct q2t_prm prm; ctio7_status0_entry_t *pkt; @@ -2244,6 +2481,7 @@ static int __q24_xmit_response(struct q2t_cmd *cmd, int xmit_type) /* Here ha->hardware_lock already locked */ ha = prm.tgt->ha; + vha = to_qla_parent(ha); res = q24_build_ctio_pkt(&prm); if (unlikely(res != SCST_TGT_RES_SUCCESS)) @@ -2305,7 +2543,7 @@ static int __q24_xmit_response(struct q2t_cmd *cmd, int xmit_type) out_unlock: /* Release ring specific lock */ - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&vha->hardware_lock, flags); out: TRACE_EXIT_RES(res); @@ -2322,7 +2560,7 @@ static int __q2t_rdy_to_xfer(struct q2t_cmd *cmd) { int res = SCST_TGT_RES_SUCCESS; unsigned long flags; - scsi_qla_host_t *ha; + scsi_qla_host_t *ha, *vha; struct q2t_tgt *tgt = cmd->tgt; struct q2t_prm prm; void *p; @@ -2335,6 +2573,7 @@ static int __q2t_rdy_to_xfer(struct q2t_cmd *cmd) prm.sg = NULL; prm.req_cnt = 1; ha = tgt->ha; + vha = to_qla_parent(ha); /* Send marker if required */ if (q2t_issue_marker(ha, 0) != QLA_SUCCESS) { @@ -2351,7 +2590,7 @@ static int __q2t_rdy_to_xfer(struct q2t_cmd *cmd) } /* Acquire ring specific lock */ - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(&vha->hardware_lock, flags); /* Does F/W have an IOCBs for this request */ res = q2t_check_reserve_free_req(ha, prm.req_cnt); @@ -2385,7 +2624,7 @@ static int __q2t_rdy_to_xfer(struct q2t_cmd *cmd) out_unlock: /* Release ring specific lock */ - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&vha->hardware_lock, flags); out: TRACE_EXIT_RES(res); @@ -2427,6 +2666,7 @@ static void q2x_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, ctio_ret_entry_t *ctio; unsigned long flags = 0; /* to stop compiler's warning */ int do_tgt_cmd_done = 0; + scsi_qla_host_t *vha = to_qla_parent(ha); TRACE_ENTRY(); @@ -2437,9 +2677,9 @@ static void q2x_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, goto out; if (!ha_locked) - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(&vha->hardware_lock, flags); - ctio = (ctio_ret_entry_t *)qla2x00_req_pkt(ha); + ctio = (ctio_ret_entry_t *)q2t_req_pkt(ha); if (ctio == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " "request packet", ha->instance, __func__); @@ -2477,7 +2717,7 @@ static void q2x_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, out_unlock: if (!ha_locked) - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&vha->hardware_lock, flags); if (do_tgt_cmd_done) { if (!ha_locked && !in_interrupt()) { @@ -2500,6 +2740,7 @@ static void q24_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, ctio7_status1_entry_t *ctio; unsigned long flags = 0; /* to stop compiler's warning */ int do_tgt_cmd_done = 0; + scsi_qla_host_t *vha = to_qla_parent(ha); TRACE_ENTRY(); @@ -2510,9 +2751,9 @@ static void q24_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, goto out; if (!ha_locked) - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(&vha->hardware_lock, flags); - ctio = (ctio7_status1_entry_t *)qla2x00_req_pkt(ha); + ctio = (ctio7_status1_entry_t *)q2t_req_pkt(ha); if (ctio == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " "request packet", ha->instance, __func__); @@ -2533,6 +2774,7 @@ static void q24_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, ctio->common.nport_handle = CTIO7_NHANDLE_UNRECOGNIZED; ctio->common.handle = Q2T_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; ctio->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT); + ctio->common.vp_index = ha->vp_idx; ctio->common.initiator_id[0] = atio->fcp_hdr.s_id[2]; ctio->common.initiator_id[1] = atio->fcp_hdr.s_id[1]; ctio->common.initiator_id[2] = atio->fcp_hdr.s_id[0]; @@ -2552,7 +2794,7 @@ static void q24_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, out_unlock: if (!ha_locked) - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&vha->hardware_lock, flags); if (do_tgt_cmd_done) { if (!ha_locked && !in_interrupt()) { @@ -3616,6 +3858,7 @@ static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, { notify24xx_entry_t *ntfy = &imm->imm.notify_entry24; struct q2t_cmd *cmd = sctio->cmd; + scsi_qla_host_t *vha = to_qla_parent(ha); TRACE_ENTRY(); @@ -3623,10 +3866,10 @@ static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, switch (ntfy->srr_ui) { case SRR_IU_STATUS: - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); q24_send_notify_ack(ha, ntfy, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); __q24_xmit_response(cmd, Q2T_XMIT_STATUS); break; case SRR_IU_DATA_IN: @@ -3639,10 +3882,10 @@ static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, offset = le32_to_cpu(imm->imm.notify_entry24.srr_rel_offs); if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0) goto out_reject; - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); q24_send_notify_ack(ha, ntfy, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); __q24_xmit_response(cmd, xmit_type); } else { PRINT_ERROR("qla2x00t(%ld): SRR for in data for cmd " @@ -3663,10 +3906,10 @@ static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, offset = le32_to_cpu(imm->imm.notify_entry24.srr_rel_offs); if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0) goto out_reject; - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); q24_send_notify_ack(ha, ntfy, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); __q2t_rdy_to_xfer(cmd); } else { PRINT_ERROR("qla2x00t(%ld): SRR for out data for cmd " @@ -3693,7 +3936,7 @@ out_unmap_reject: } out_reject: - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); q24_send_notify_ack(ha, ntfy, NOTIFY_ACK_SRR_FLAGS_REJECT, NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); @@ -3707,7 +3950,7 @@ out_reject: SCST_CONTEXT_THREAD); } else q24_send_term_exchange(ha, cmd, &cmd->atio.atio7, 1); - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); goto out; } @@ -3717,6 +3960,7 @@ static void q2x_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, { notify_entry_t *ntfy = &imm->imm.notify_entry; struct q2t_cmd *cmd = sctio->cmd; + scsi_qla_host_t *vha = to_qla_parent(ha); TRACE_ENTRY(); @@ -3724,10 +3968,10 @@ static void q2x_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, switch (ntfy->srr_ui) { case SRR_IU_STATUS: - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); q2x_send_notify_ack(ha, ntfy, 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); __q2x_xmit_response(cmd, Q2T_XMIT_STATUS); break; case SRR_IU_DATA_IN: @@ -3740,10 +3984,10 @@ static void q2x_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, offset = le32_to_cpu(imm->imm.notify_entry.srr_rel_offs); if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0) goto out_reject; - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); q2x_send_notify_ack(ha, ntfy, 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); __q2x_xmit_response(cmd, xmit_type); } else { PRINT_ERROR("qla2x00t(%ld): SRR for in data for cmd " @@ -3764,10 +4008,10 @@ static void q2x_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, offset = le32_to_cpu(imm->imm.notify_entry.srr_rel_offs); if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0) goto out_reject; - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); q2x_send_notify_ack(ha, ntfy, 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); __q2t_rdy_to_xfer(cmd); } else { PRINT_ERROR("qla2x00t(%ld): SRR for out data for cmd " @@ -3794,7 +4038,7 @@ out_unmap_reject: } out_reject: - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); q2x_send_notify_ack(ha, ntfy, 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_REJECT, NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); @@ -3808,15 +4052,17 @@ out_reject: SCST_CONTEXT_THREAD); } else q2x_send_term_exchange(ha, cmd, &cmd->atio.atio2x, 1); - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); goto out; } static void q2t_reject_free_srr_imm(scsi_qla_host_t *ha, struct srr_imm *imm, int ha_locked) { + scsi_qla_host_t *vha = to_qla_parent(ha); + if (!ha_locked) - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); if (IS_FWI2_CAPABLE(ha)) { q24_send_notify_ack(ha, &imm->imm.notify_entry24, @@ -3831,7 +4077,7 @@ static void q2t_reject_free_srr_imm(scsi_qla_host_t *ha, struct srr_imm *imm, } if (!ha_locked) - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); kfree(imm); return; @@ -4166,7 +4412,7 @@ static void q2x_send_busy(scsi_qla_host_t *ha, atio_entry_t *atio) /* Sending marker isn't necessary, since we called from ISR */ - ctio = (ctio_ret_entry_t *)qla2x00_req_pkt(ha); + ctio = (ctio_ret_entry_t *)q2t_req_pkt(ha); if (ctio == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " "request packet", ha->instance, __func__); @@ -4221,7 +4467,7 @@ static void q24_send_busy(scsi_qla_host_t *ha, atio7_entry_t *atio, /* Sending marker isn't necessary, since we called from ISR */ - ctio = (ctio7_status1_entry_t *)qla2x00_req_pkt(ha); + ctio = (ctio7_status1_entry_t *)q2t_req_pkt(ha); if (ctio == NULL) { PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate " "request packet", ha->instance, __func__); @@ -4233,6 +4479,7 @@ static void q24_send_busy(scsi_qla_host_t *ha, atio7_entry_t *atio, ctio->common.handle = Q2T_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; ctio->common.nport_handle = sess->loop_id; ctio->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT); + ctio->common.vp_index = ha->vp_idx; ctio->common.initiator_id[0] = atio->fcp_hdr.s_id[2]; ctio->common.initiator_id[1] = atio->fcp_hdr.s_id[1]; ctio->common.initiator_id[2] = atio->fcp_hdr.s_id[0]; @@ -4686,7 +4933,7 @@ out: return; } -static int q2t_get_target_name(scsi_qla_host_t *ha, char **wwn) +static int q2t_get_target_name(uint8_t *wwn, char **ppwwn_name) { const int wwn_len = 3*WWN_SIZE+2; int res = 0; @@ -4700,12 +4947,10 @@ static int q2t_get_target_name(scsi_qla_host_t *ha, char **wwn) } sprintf(name, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", - ha->port_name[0], ha->port_name[1], - ha->port_name[2], ha->port_name[3], - ha->port_name[4], ha->port_name[5], - ha->port_name[6], ha->port_name[7]); + wwn[0], wwn[1], wwn[2], wwn[3], + wwn[4], wwn[5], wwn[6], wwn[7]); - *wwn = name; + *ppwwn_name = name; out: return res; @@ -4815,6 +5060,7 @@ static int q2t_exec_sess_work(struct q2t_tgt *tgt, struct q2t_sess_work_param *prm) { scsi_qla_host_t *ha = tgt->ha; + scsi_qla_host_t *vha = to_qla_parent(ha); int res = 0; struct q2t_sess *sess = NULL; struct q2t_cmd *cmd = prm->cmd; @@ -4825,7 +5071,7 @@ static int q2t_exec_sess_work(struct q2t_tgt *tgt, TRACE_MGMT_DBG("cmd %p", cmd); mutex_lock(&ha->tgt_mutex); - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); if (tgt->tgt_stop) goto send; @@ -4845,9 +5091,9 @@ static int q2t_exec_sess_work(struct q2t_tgt *tgt, * We are under tgt_mutex, so a new sess can't be added * behind us. */ - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); sess = q2t_make_local_sess(ha, atio); - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); /* sess has got an extra creation ref */ } @@ -4872,7 +5118,7 @@ send: if (sess != NULL) q2t_sess_put(sess); - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); mutex_unlock(&ha->tgt_mutex); TRACE_EXIT_RES(res); @@ -4882,6 +5128,7 @@ send: static void q2t_sess_work_fn(struct work_struct *work) { struct q2t_tgt *tgt = container_of(work, struct q2t_tgt, sess_work); + scsi_qla_host_t *vha = to_qla_parent(tgt->ha); TRACE_ENTRY(); @@ -4915,14 +5162,14 @@ static void q2t_sess_work_fn(struct work_struct *work) } spin_unlock_irq(&tgt->sess_work_lock); - spin_lock_irq(&tgt->ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); spin_lock(&tgt->sess_work_lock); if (list_empty(&tgt->sess_works_list)) { tgt->sess_works_pending = 0; tgt->tm_to_unknown = 0; } spin_unlock(&tgt->sess_work_lock); - spin_unlock_irq(&tgt->ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); TRACE_EXIT(); return; @@ -4948,6 +5195,7 @@ static void q2t_on_hw_pending_cmd_timeout(struct scst_cmd *scst_cmd) struct q2t_cmd *cmd = (struct q2t_cmd *)scst_cmd_get_tgt_priv(scst_cmd); struct q2t_tgt *tgt = cmd->tgt; scsi_qla_host_t *ha = tgt->ha; + scsi_qla_host_t *vha = to_qla_parent(ha); unsigned long flags; TRACE_ENTRY(); @@ -4955,7 +5203,7 @@ static void q2t_on_hw_pending_cmd_timeout(struct scst_cmd *scst_cmd) TRACE_MGMT_DBG("Cmd %p HW pending for too long (state %x)", cmd, cmd->state); - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(&vha->hardware_lock, flags); if (cmd->state == Q2T_STATE_PROCESSED) { TRACE_MGMT_DBG("Force finishing cmd %p", cmd); @@ -4989,7 +5237,7 @@ static void q2t_on_hw_pending_cmd_timeout(struct scst_cmd *scst_cmd) scst_tgt_cmd_done(scst_cmd, SCST_CONTEXT_THREAD); out_unlock: - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&vha->hardware_lock, flags); TRACE_EXIT(); return; } @@ -5033,7 +5281,7 @@ static int q2t_add_target(scsi_qla_host_t *ha) ha->q2t_tgt = tgt; - if (q2t_get_target_name(ha, &wwn) != 0) + if (q2t_get_target_name(ha->port_name, &wwn) != 0) goto out_free; tgt->scst_tgt = scst_register_target(&tgt2x_template, wwn); @@ -5083,6 +5331,29 @@ static int q2t_add_target(scsi_qla_host_t *ha) if (rc != 0) PRINT_ERROR("Unable to create \"host\" link for target " "%s", scst_get_tgt_name(tgt->scst_tgt)); + if (!ha->parent) { + rc = sysfs_create_file(scst_sysfs_get_tgt_kobj(tgt->scst_tgt), + &q2t_hw_target_attr.attr); + if (rc != 0) + PRINT_ERROR("Unable to create \"hw_target\"" + " file for target %s", + 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); + if (rc != 0) + PRINT_ERROR("Unable to create \"node_name\"" + " file for NPIV target %s", + scst_get_tgt_name(tgt->scst_tgt)); + + rc = sysfs_create_file(scst_sysfs_get_tgt_kobj(tgt->scst_tgt), + &q2t_vp_parent_host_attr.attr); + if (rc != 0) + PRINT_ERROR("Unable to create \"parent_host\"" + " file for NPIV target %s", + scst_get_tgt_name(tgt->scst_tgt)); + } + #endif scst_tgt_set_sg_tablesize(tgt->scst_tgt, sg_tablesize); @@ -5109,7 +5380,7 @@ static int q2t_remove_target(scsi_qla_host_t *ha) } TRACE_DBG("Unregistering target for host %ld(%p)", ha->host_no, ha); - scst_unregister_target(ha->tgt->scst_tgt); + scst_unregister_target(ha->q2t_tgt->scst_tgt); /* * Free of tgt happens via callback q2t_target_release * called from scst_unregister_target, so we shouldn't touch @@ -5124,6 +5395,7 @@ static int q2t_host_action(scsi_qla_host_t *ha, qla2x_tgt_host_action_t action) { int res = 0; + scsi_qla_host_t *vha = to_qla_parent(ha); TRACE_ENTRY(); @@ -5161,10 +5433,10 @@ static int q2t_host_action(scsi_qla_host_t *ha, PRINT_INFO("qla2x00t(%ld): Enabling target mode", ha->instance); - spin_lock_irq(&ha->hardware_lock); + spin_lock_irq(&vha->hardware_lock); ha->tgt = ha->q2t_tgt; ha->tgt->tgt_stop = 0; - spin_unlock_irq(&ha->hardware_lock); + spin_unlock_irq(&vha->hardware_lock); list_for_each_entry_rcu(fcport, &ha->fcports, list) { q2t_fc_port_added(ha, fcport); } @@ -5226,6 +5498,153 @@ static bool q2t_is_tgt_enabled(struct scst_tgt *scst_tgt) return qla_tgt_mode_enabled(ha); } +static int q2t_parse_wwn(const char *ns, u64 *nm) +{ + unsigned int i, j; + u8 wwn[8]; + + /* validate we have enough characters for WWPN */ + if (strlen(ns) != 23) + return -EINVAL; + + memset(wwn, 0, sizeof(wwn)); + + /* Validate and store the new name */ + for (i = 0, j = 0; i < 16; i++) { + if ((*ns >= 'a') && (*ns <= 'f')) + j = ((j << 4) | ((*ns++ - 'a') + 10)); + else if ((*ns >= 'A') && (*ns <= 'F')) + j = ((j << 4) | ((*ns++ - 'A') + 10)); + else if ((*ns >= '0') && (*ns <= '9')) + j = ((j << 4) | (*ns++ - '0')); + else + return -EINVAL; + if (i % 2) { + wwn[i/2] = j & 0xff; + j = 0; + if ((i < 15) && (':' != *ns++)) + return -EINVAL; + } + } + + *nm = wwn_to_u64(wwn); + + return 0; +} + +static ssize_t q2t_add_vtarget(const char *target_name, char *params) +{ + int res; + char *param, *p, *pp; + u64 port_name, node_name, *pnode_name = NULL; + u64 parent_host, *pparent_host = NULL; + + TRACE_ENTRY(); + + res = q2t_parse_wwn(target_name, &port_name); + if (res) { + PRINT_ERROR("Syntax error at target name %s", target_name); + goto out; + } + + while (1) { + param = scst_get_next_token_str(¶ms); + if (param == NULL) + break; + + p = scst_get_next_lexem(¶m); + if (*p == '\0') { + PRINT_ERROR("Syntax error at %s (target %s)", + param, target_name); + res = -EINVAL; + goto out; + } + + pp = scst_get_next_lexem(¶m); + if (*pp == '\0') { + PRINT_ERROR("Parameter %s value missed for target %s", + p, target_name); + res = -EINVAL; + goto out; + } + + if (scst_get_next_lexem(¶m)[0] != '\0') { + PRINT_ERROR("Too many parameter's %s values " + "(target %s)", + p, target_name); + res = -EINVAL; + goto out; + } + + if (!strcasecmp("node_name", p)) { + res = q2t_parse_wwn(pp, &node_name); + if (res) { + PRINT_ERROR("Illegal node_name %s " + "(target %s)", pp, target_name); + res = -EINVAL; + goto out; + } + pnode_name = &node_name; + continue; + } + + if (!strcasecmp("parent_host", p)) { + res = q2t_parse_wwn(pp, &parent_host); + if (res != 0) { + PRINT_ERROR("Illegal parent_host %s" + " (target %s)", pp, target_name); + goto out; + } + pparent_host = &parent_host; + continue; + } + + PRINT_ERROR("Unknown parameter %s (target %s)", p, + target_name); + res = -EINVAL; + goto out; + } + + if (!pnode_name) { + PRINT_ERROR("Missing parameter node_name (target %s)", + target_name); + res = -EINVAL; + goto out; + } + + if (!pparent_host) { + PRINT_ERROR("Missing parameter parent_host (target %s)", + target_name); + res = -EINVAL; + goto out; + } + + res = qla2xxx_add_vtarget(&port_name, pnode_name, pparent_host); + +out: + TRACE_EXIT_RES(res); + return res; +} + +static ssize_t q2t_del_vtarget(const char *target_name) +{ + int res; + u64 port_name; + + TRACE_ENTRY(); + + res = q2t_parse_wwn(target_name, &port_name); + if (res) { + PRINT_ERROR("Syntax error at target name %s", target_name); + goto out; + } + + res = qla2xxx_del_vtarget(&port_name); +out: + TRACE_EXIT_RES(res); + return res; +} + static int q2t_get_initiator_port_transport_id(struct scst_session *scst_sess, uint8_t **transport_id) { @@ -5292,14 +5711,15 @@ static ssize_t q2t_store_expl_conf_enabled(struct kobject *kobj, { struct scst_tgt *scst_tgt; struct q2t_tgt *tgt; - scsi_qla_host_t *ha; + scsi_qla_host_t *ha, *vha; unsigned long flags; scst_tgt = container_of(kobj, struct scst_tgt, tgt_kobj); tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); ha = tgt->ha; + vha = to_qla_parent(ha); - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(&vha->hardware_lock, flags); switch (buffer[0]) { case '0': @@ -5320,7 +5740,7 @@ static ssize_t q2t_store_expl_conf_enabled(struct kobject *kobj, break; } - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&vha->hardware_lock, flags); return size; } @@ -5369,6 +5789,64 @@ static ssize_t q2t_version_show(struct kobject *kobj, return strlen(buf); } +static ssize_t q2t_hw_target_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", 1); +} + +static ssize_t q2t_vp_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; + char *wwn; + + 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; + + size = sprintf(buf, "%s\n%s\n", wwn, SCST_SYSFS_KEY_MARK); + + kfree(wwn); + + return size; +} + +static ssize_t q2t_vp_parent_host_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; + 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("No parent for NPIV target %s" + , scst_get_tgt_name(scst_tgt)); + return 0; + } + + if (q2t_get_target_name(ha->port_name, &wwn) != 0) + return 0; + + size = sprintf(buf, "%s\n%s\n", wwn, SCST_SYSFS_KEY_MARK); + + kfree(wwn); + + return size; +} + #else /* CONFIG_SCST_PROC */ #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) diff --git a/qla2x00t/qla2x_tgt.h b/qla2x00t/qla2x_tgt.h index b2ae54ea2..2bccdd597 100644 --- a/qla2x00t/qla2x_tgt.h +++ b/qla2x00t/qla2x_tgt.h @@ -126,7 +126,9 @@ qla2x00_send_enable_lun(scsi_qla_host_t *ha, bool enable) } extern void qla2xxx_add_targets(void); - +extern size_t +qla2xxx_add_vtarget(u64 *port_name, u64 *node_name, u64 *parent_host); +extern size_t qla2xxx_del_vtarget(u64 *port_name); #endif /* CONFIG_SCSI_QLA2XXX_TARGET */ #endif /* __QLA2X_TGT_H */ diff --git a/qla2x00t/qla2x_tgt_def.h b/qla2x00t/qla2x_tgt_def.h index d1f1233e9..8f44bdd61 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 267 +#define QLA2X_TARGET_MAGIC 268 /* * Must be changed on any change in any target visible interfaces or * data in the initiator */ -#define QLA2X_INITIATOR_MAGIC 57219 +#define QLA2X_INITIATOR_MAGIC 57220 #define QLA2X_INI_MODE_STR_EXCLUSIVE "exclusive" #define QLA2X_INI_MODE_STR_DISABLED "disabled" @@ -508,7 +508,8 @@ typedef struct { uint16_t status; uint16_t timeout; uint16_t dseg_count; /* Data segment count. */ - uint8_t reserved1[6]; + uint8_t vp_index; + uint8_t reserved1[5]; uint32_t exchange_address; uint16_t reserved2; uint16_t flags; @@ -614,7 +615,8 @@ typedef struct { uint8_t entry_status; /* Entry Status. */ uint8_t reserved_1[6]; uint16_t nport_handle; - uint8_t reserved_2[3]; + uint8_t reserved_2[2]; + uint8_t vp_index; uint8_t reserved_3:4; uint8_t sof_type:4; uint32_t exchange_address; @@ -656,7 +658,7 @@ typedef struct { uint16_t nport_handle; uint16_t control_flags; #define ABTS_CONTR_FLG_TERM_EXCHG BIT_0 - uint8_t reserved_2; + uint8_t vp_index; uint8_t reserved_3:4; uint8_t sof_type:4; uint32_t exchange_address; diff --git a/qla2x00t/qla_attr.c b/qla2x00t/qla_attr.c index 7288f49d8..22bbe5e0b 100644 --- a/qla2x00t/qla_attr.c +++ b/qla2x00t/qla_attr.c @@ -1783,6 +1783,26 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable) fc_host_supported_speeds(vha->host) = fc_host_supported_speeds(ha->host); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + vha->tgt = NULL; + vha->q2t_tgt = NULL; + mutex_init(&vha->tgt_mutex); + mutex_init(&vha->tgt_host_action_mutex); + qla_clear_tgt_mode(vha); + qla2x00_send_enable_lun(vha, false); + if (IS_QLA24XX_TYPE(vha)) + vha->atio_q_length = ATIO_ENTRY_CNT_24XX; + else if (IS_QLA25XX(vha)) + vha->atio_q_length = ATIO_ENTRY_CNT_24XX; + + if (qla_target.tgt_host_action != NULL) + qla_target.tgt_host_action(vha, ADD_TARGET); + + /* + * Must be after tgt_host_action() to not race with + * qla2xxx_add_targets(). + */ +#endif qla24xx_vport_disable(fc_vport, disable); return 0; @@ -1801,6 +1821,11 @@ qla24xx_vport_delete(struct fc_vport *fc_vport) scsi_qla_host_t *ha = shost_priv(fc_vport->shost); scsi_qla_host_t *vha = fc_vport->dd_data; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (qla_target.tgt_host_action != NULL) + qla_target.tgt_host_action(vha, REMOVE_TARGET); +#endif + qla24xx_disable_vp(vha); qla24xx_deallocate_vp_id(vha); diff --git a/qla2x00t/qla_def.h b/qla2x00t/qla_def.h index 5c02ad568..dde5f2839 100644 --- a/qla2x00t/qla_def.h +++ b/qla2x00t/qla_def.h @@ -2626,6 +2626,7 @@ typedef struct scsi_qla_host { uint16_t vp_idx; /* vport ID */ #ifdef CONFIG_SCSI_QLA2XXX_TARGET + struct qla_tgt_vp_map *tgt_vp_map; struct mutex tgt_mutex; struct mutex tgt_host_action_mutex; @@ -2671,6 +2672,12 @@ typedef struct scsi_qla_host { struct qla_chip_state_84xx *cs84xx; } scsi_qla_host_t; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET +struct qla_tgt_vp_map { + uint8_t idx; + scsi_qla_host_t *vha; +}; +#endif /* * Macros to help code, maintain, etc. diff --git a/qla2x00t/qla_init.c b/qla2x00t/qla_init.c index b17b57d30..07359b540 100644 --- a/qla2x00t/qla_init.c +++ b/qla2x00t/qla_init.c @@ -1460,7 +1460,9 @@ qla2x00_configure_hba(scsi_qla_host_t *ha) uint8_t area; uint8_t domain; char connect_type[22]; - +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + scsi_qla_host_t *pha = ha->parent; +#endif /* Get host addresses. */ rval = qla2x00_get_adapter_id(ha, &loop_id, &al_pa, &area, &domain, &topo, &sw_cap); @@ -1537,7 +1539,10 @@ qla2x00_configure_hba(scsi_qla_host_t *ha) ha->d_id.b.domain = domain; ha->d_id.b.area = area; ha->d_id.b.al_pa = al_pa; - +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (pha) + pha->tgt_vp_map[al_pa].idx = ha->vp_idx; +#endif if (!ha->flags.init_done) qla_printk(KERN_INFO, ha, "Topology - %s, Host Loop address 0x%x\n", @@ -4139,6 +4144,9 @@ qla2x00_enable_tgt_mode(scsi_qla_host_t *ha) qla_set_tgt_mode(ha); spin_unlock_irqrestore(&ha->hardware_lock, flags); + if (ha->parent) + ha = ha->parent; + set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); qla2xxx_wake_dpc(ha); qla2x00_wait_for_hba_online(ha); @@ -4154,14 +4162,15 @@ void qla2x00_disable_tgt_mode(scsi_qla_host_t *ha) { unsigned long flags; + scsi_qla_host_t *vha = to_qla_parent(ha); - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(&vha->hardware_lock, flags); qla_clear_tgt_mode(ha); - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&vha->hardware_lock, flags); - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); - qla2xxx_wake_dpc(ha); - qla2x00_wait_for_hba_online(ha); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + qla2x00_wait_for_hba_online(vha); } EXPORT_SYMBOL(qla2x00_disable_tgt_mode); diff --git a/qla2x00t/qla_mbx.c b/qla2x00t/qla_mbx.c index 556f1e005..effb0865e 100644 --- a/qla2x00t/qla_mbx.c +++ b/qla2x00t/qla_mbx.c @@ -6,6 +6,8 @@ */ #include "qla_def.h" +#include "qla2x_tgt.h" + #include @@ -2765,6 +2767,14 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha) vpmod->vp_count = 1; vpmod->vp_index1 = vha->vp_idx; vpmod->options_idx1 = BIT_3|BIT_4|BIT_5; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + /* Enable target mode */ + if (qla_tgt_mode_enabled(vha)) + vpmod->options_idx1 &= ~BIT_5; + /* Disable ini mode, if requested */ + if (!qla_ini_mode_enabled(vha)) + vpmod->options_idx1 &= ~BIT_4; +#endif memcpy(vpmod->node_name_idx1, vha->node_name, WWN_SIZE); memcpy(vpmod->port_name_idx1, vha->port_name, WWN_SIZE); vpmod->entry_count = 1; diff --git a/qla2x00t/qla_mid.c b/qla2x00t/qla_mid.c index 0fd28ac3a..e535bec2d 100644 --- a/qla2x00t/qla_mid.c +++ b/qla2x00t/qla_mid.c @@ -16,6 +16,8 @@ #include #include +#include "qla2x_tgt.h" + void qla2x00_vp_stop_timer(scsi_qla_host_t *vha) { @@ -25,7 +27,7 @@ qla2x00_vp_stop_timer(scsi_qla_host_t *vha) } } -static uint32_t +static void qla24xx_allocate_vp_id(scsi_qla_host_t *vha) { uint32_t vp_id; @@ -38,15 +40,16 @@ qla24xx_allocate_vp_id(scsi_qla_host_t *vha) DEBUG15(printk ("vp_id %d is bigger than max-supported %d.\n", vp_id, ha->max_npiv_vports)); mutex_unlock(&ha->vport_lock); - return vp_id; + } else { + set_bit(vp_id, ha->vp_idx_map); + ha->num_vhosts++; + vha->vp_idx = vp_id; + list_add_tail(&vha->vp_list, &ha->vp_list); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + ha->tgt_vp_map[vp_id].vha = vha; +#endif + mutex_unlock(&ha->vport_lock); } - - set_bit(vp_id, ha->vp_idx_map); - ha->num_vhosts++; - vha->vp_idx = vp_id; - list_add_tail(&vha->vp_list, &ha->vp_list); - mutex_unlock(&ha->vport_lock); - return vp_id; } void @@ -60,6 +63,9 @@ qla24xx_deallocate_vp_id(scsi_qla_host_t *vha) ha->num_vhosts--; clear_bit(vp_id, ha->vp_idx_map); list_del(&vha->vp_list); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + ha->tgt_vp_map[vp_id].vha = NULL; +#endif mutex_unlock(&ha->vport_lock); } @@ -116,6 +122,10 @@ qla24xx_disable_vp(scsi_qla_host_t *vha) ret = qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL); atomic_set(&vha->loop_state, LOOP_DOWN); atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + /* Remove port id from vp target map */ + vha->parent->tgt_vp_map[vha->d_id.b.al_pa].idx = 0; +#endif /* Delete all vp's fcports from parent's list */ qla2x00_mark_vp_devices_dead(vha); @@ -249,6 +259,11 @@ qla2x00_vp_abort_isp(scsi_qla_host_t *vha) DEBUG15(printk("scsi(%ld): Scheduling enable of Vport %d...\n", vha->host_no, vha->vp_idx)); qla24xx_enable_vp(vha); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + /* Enable target response to SCSI bus. */ + if (qla_tgt_mode_enabled(vha)) + qla2x00_send_enable_lun(vha, true); +#endif } static int @@ -391,7 +406,7 @@ qla24xx_create_vhost(struct fc_vport *fc_vport) vha->fc_vport = fc_vport; vha->device_flags = 0; vha->instance = num_hosts; - vha->vp_idx = qla24xx_allocate_vp_id(vha); + qla24xx_allocate_vp_id(vha); if (vha->vp_idx > ha->max_npiv_vports) { DEBUG15(printk("scsi(%ld): Couldn't allocate vp_id.\n", vha->host_no)); diff --git a/qla2x00t/qla_os.c b/qla2x00t/qla_os.c index 135aab15e..c09601e14 100644 --- a/qla2x00t/qla_os.c +++ b/qla2x00t/qla_os.c @@ -1649,13 +1649,104 @@ qla2xxx_add_targets(void) mutex_lock(&qla_ha_list_mutex); list_for_each_entry(ha, &qla_ha_list, ha_list_entry) { - if (qla_target.tgt_host_action != NULL) + if (qla_target.tgt_host_action != NULL) { + int i, vp_idx_matched; + qla_target.tgt_host_action(ha, ADD_TARGET); + for_each_mapped_vp_idx(ha, i) { + scsi_qla_host_t *vha; + vp_idx_matched = 0; + + list_for_each_entry(vha, &ha->vp_list, + vp_list) { + if (i == vha->vp_idx) { + vp_idx_matched = 1; + break; + } + } + + if (vp_idx_matched) + qla_target.tgt_host_action(vha, ADD_TARGET); + } + } } mutex_unlock(&qla_ha_list_mutex); return; } EXPORT_SYMBOL(qla2xxx_add_targets); + +size_t qla2xxx_add_vtarget(u64 *port_name, u64 *node_name, u64 *parent_host) +{ + struct Scsi_Host *shost = NULL; + scsi_qla_host_t *ha; + struct fc_vport_identifiers vid; + uint8_t parent_wwn[WWN_SIZE]; + + memset(&vid, 0, sizeof(vid)); + + u64_to_wwn(*parent_host, parent_wwn); + + mutex_lock(&qla_ha_list_mutex); + list_for_each_entry(ha, &qla_ha_list, ha_list_entry) { + if (!memcmp(parent_wwn, ha->port_name, WWN_SIZE)) { + shost = ha->host; + break; + } + } + mutex_unlock(&qla_ha_list_mutex); + if (!ha || !shost) + return -ENODEV; + + vid.port_name = *port_name; + vid.node_name = *node_name; + vid.roles = FC_PORT_ROLE_FCP_INITIATOR; + vid.vport_type = FC_PORTTYPE_NPIV; + /* vid.symbolic_name is already zero/NULL's */ + vid.disable = false; /* always enabled */ + + /* We only allow support on Channel 0 !!! */ + if (!fc_vport_create(shost, 0, &vid)) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL(qla2xxx_add_vtarget); + +size_t qla2xxx_del_vtarget(u64 *port_name) +{ + scsi_qla_host_t *ha; + struct Scsi_Host *shost; + struct fc_host_attrs *fc_host; + struct fc_vport *vport; + unsigned long flags; + int match = 0; + + mutex_lock(&qla_ha_list_mutex); + list_for_each_entry(ha, &qla_ha_list, ha_list_entry) { + shost = ha->host; + fc_host = shost_to_fc_host(shost); + spin_lock_irqsave(shost->host_lock, flags); + /* We only allow support on Channel 0 !!! */ + list_for_each_entry(vport, &fc_host->vports, peers) { + if ((vport->channel == 0) && + (vport->port_name == *port_name)) { + match = 1; + break; + } + } + spin_unlock_irqrestore(shost->host_lock, flags); + if (match) + break; + } + mutex_unlock(&qla_ha_list_mutex); + + if (!match) + return -ENODEV; + + return fc_vport_terminate(vport); +} +EXPORT_SYMBOL(qla2xxx_del_vtarget); + #endif /* CONFIG_SCSI_QLA2XXX_TARGET */ /* @@ -1729,6 +1820,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) mutex_init(&ha->tgt_mutex); mutex_init(&ha->tgt_host_action_mutex); qla_clear_tgt_mode(ha); + ha->tgt_vp_map = NULL; #endif /* Set ISP-type information. */ @@ -2184,11 +2276,16 @@ qla2x00_mem_alloc(scsi_qla_host_t *ha) #ifdef CONFIG_SCSI_QLA2XXX_TARGET if (IS_FWI2_CAPABLE(ha)) { + ha->tgt_vp_map = kzalloc(sizeof(struct qla_tgt_vp_map) * + MAX_MULTI_ID_FABRIC, GFP_KERNEL); + if (!ha->tgt_vp_map) + goto fail_free_response_ring; + ha->atio_ring = dma_alloc_coherent(&ha->pdev->dev, (ha->atio_q_length + 1) * sizeof(atio_t), &ha->atio_dma, GFP_KERNEL); if (ha->atio_ring == NULL) - goto fail_free_response_ring; + goto fail_free_vp_map; } #endif @@ -2270,6 +2367,9 @@ fail_free_atio_ring: sizeof(response_t), ha->atio_ring, ha->atio_dma); ha->atio_ring = NULL; ha->atio_dma = 0; +fail_free_vp_map: + kfree(ha->tgt_vp_map); + ha->tgt_vp_map = NULL; fail_free_response_ring: #endif dma_free_coherent(&ha->pdev->dev, (ha->response_q_length + 1) * @@ -2342,6 +2442,7 @@ qla2x00_mem_free(scsi_qla_host_t *ha) dma_free_coherent(&ha->pdev->dev, (ha->atio_q_length + 1) * sizeof(atio_t), ha->atio_ring, ha->atio_dma); + kfree(ha->tgt_vp_map); #endif if (ha->response_ring) @@ -2378,6 +2479,7 @@ qla2x00_mem_free(scsi_qla_host_t *ha) #ifdef CONFIG_SCSI_QLA2XXX_TARGET ha->atio_ring = NULL; ha->atio_dma = 0; + ha->tgt_vp_map = NULL; #endif list_for_each_safe(fcpl, fcptemp, &ha->fcports) {