]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
[SCSI] qla2xxx: add support for NPIV
authorSeokmann Ju <seokmann.ju@qlogic.com>
Thu, 5 Jul 2007 20:16:51 +0000 (13:16 -0700)
committerJames Bottomley <jejb@mulgrave.localdomain>
Sun, 15 Jul 2007 00:08:05 +0000 (19:08 -0500)
Following patch adds support for NPIV (N-Port ID Virtualization) to the
qla2xxx.

- supported within switched-fabric topologies only.
- supports up to 63 virtual ports on each physical port.

Signed-off-by: Seokmann Ju <seokmann.ju@qlogic.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
13 files changed:
drivers/scsi/qla2xxx/Makefile
drivers/scsi/qla2xxx/qla_attr.c
drivers/scsi/qla2xxx/qla_dbg.h
drivers/scsi/qla2xxx/qla_def.h
drivers/scsi/qla2xxx/qla_fw.h
drivers/scsi/qla2xxx/qla_gbl.h
drivers/scsi/qla2xxx/qla_gs.c
drivers/scsi/qla2xxx/qla_init.c
drivers/scsi/qla2xxx/qla_iocb.c
drivers/scsi/qla2xxx/qla_isr.c
drivers/scsi/qla2xxx/qla_mbx.c
drivers/scsi/qla2xxx/qla_mid.c [new file with mode: 0644]
drivers/scsi/qla2xxx/qla_os.c

index 411663af7bb71c4365b0f05f0ce991479cdc4049..71ddb5db4944e0321b1a89ce5960fe33db8a3ff8 100644 (file)
@@ -1,4 +1,4 @@
 qla2xxx-y := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \
-               qla_dbg.o qla_sup.o qla_attr.o
+               qla_dbg.o qla_sup.o qla_attr.o qla_mid.o
 
 obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx.o
index 8081b637d97e06550253eb0ddb12bfba0ef27ba5..b79c4dfc2a9c239c60a08e0a44896906dc0e8049 100644 (file)
@@ -6,8 +6,11 @@
  */
 #include "qla_def.h"
 
+#include <linux/kthread.h>
 #include <linux/vmalloc.h>
 
+int qla24xx_vport_disable(struct fc_vport *, bool);
+
 /* SYSFS attributes --------------------------------------------------------- */
 
 static ssize_t
@@ -959,6 +962,122 @@ qla2x00_get_host_port_state(struct Scsi_Host *shost)
                fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
 }
 
+static int
+qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
+{
+       int     ret = 0;
+       scsi_qla_host_t *ha = (scsi_qla_host_t *) fc_vport->shost->hostdata;
+       scsi_qla_host_t *vha;
+
+       ret = qla24xx_vport_create_req_sanity_check(fc_vport);
+       if (ret) {
+               DEBUG15(printk("qla24xx_vport_create_req_sanity_check failed, "
+                   "status %x\n", ret));
+               return (ret);
+       }
+
+       vha = qla24xx_create_vhost(fc_vport);
+       if (vha == NULL) {
+               DEBUG15(printk ("qla24xx_create_vhost failed, vha = %p\n",
+                   vha));
+               return FC_VPORT_FAILED;
+       }
+       if (disable) {
+               atomic_set(&vha->vp_state, VP_OFFLINE);
+               fc_vport_set_state(fc_vport, FC_VPORT_DISABLED);
+       } else
+               atomic_set(&vha->vp_state, VP_FAILED);
+
+       /* ready to create vport */
+       qla_printk(KERN_INFO, vha, "VP entry id %d assigned.\n", vha->vp_idx);
+
+       /* initialized vport states */
+       atomic_set(&vha->loop_state, LOOP_DOWN);
+       vha->vp_err_state=  VP_ERR_PORTDWN;
+       vha->vp_prev_err_state=  VP_ERR_UNKWN;
+       /* Check if physical ha port is Up */
+       if (atomic_read(&ha->loop_state) == LOOP_DOWN ||
+           atomic_read(&ha->loop_state) == LOOP_DEAD) {
+               /* Don't retry or attempt login of this virtual port */
+               DEBUG15(printk ("scsi(%ld): pport loop_state is not UP.\n",
+                   vha->host_no));
+               atomic_set(&vha->loop_state, LOOP_DEAD);
+               if (!disable)
+                       fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN);
+       }
+
+       if (scsi_add_host(vha->host, &fc_vport->dev)) {
+               DEBUG15(printk("scsi(%ld): scsi_add_host failure for VP[%d].\n",
+                       vha->host_no, vha->vp_idx));
+               goto vport_create_failed_2;
+       }
+
+       /* initialize attributes */
+       fc_host_node_name(vha->host) = wwn_to_u64(vha->node_name);
+       fc_host_port_name(vha->host) = wwn_to_u64(vha->port_name);
+       fc_host_supported_classes(vha->host) =
+               fc_host_supported_classes(ha->host);
+       fc_host_supported_speeds(vha->host) =
+               fc_host_supported_speeds(ha->host);
+
+       qla24xx_vport_disable(fc_vport, disable);
+
+       return 0;
+vport_create_failed_2:
+       qla24xx_disable_vp(vha);
+       qla24xx_deallocate_vp_id(vha);
+       kfree(vha->port_name);
+       kfree(vha->node_name);
+       scsi_host_put(vha->host);
+       return FC_VPORT_FAILED;
+}
+
+int
+qla24xx_vport_delete(struct fc_vport *fc_vport)
+{
+       scsi_qla_host_t *ha = (scsi_qla_host_t *) fc_vport->shost->hostdata;
+       scsi_qla_host_t *vha = fc_vport->dd_data;
+
+       qla24xx_disable_vp(vha);
+       qla24xx_deallocate_vp_id(vha);
+
+       down(&ha->vport_sem);
+       ha->cur_vport_count--;
+       clear_bit(vha->vp_idx, (unsigned long *)ha->vp_idx_map);
+       up(&ha->vport_sem);
+
+       kfree(vha->node_name);
+       kfree(vha->port_name);
+
+       if (vha->timer_active) {
+               qla2x00_vp_stop_timer(vha);
+               DEBUG15(printk ("scsi(%ld): timer for the vport[%d] = %p "
+                   "has stopped\n",
+                   vha->host_no, vha->vp_idx, vha));
+        }
+
+       fc_remove_host(vha->host);
+
+       scsi_remove_host(vha->host);
+
+       scsi_host_put(vha->host);
+
+       return 0;
+}
+
+int
+qla24xx_vport_disable(struct fc_vport *fc_vport, bool disable)
+{
+       scsi_qla_host_t *vha = fc_vport->dd_data;
+
+       if (disable)
+               qla24xx_disable_vp(vha);
+       else
+               qla24xx_enable_vp(vha);
+
+       return 0;
+}
+
 struct fc_function_template qla2xxx_transport_functions = {
 
        .show_host_node_name = 1,
@@ -996,6 +1115,49 @@ struct fc_function_template qla2xxx_transport_functions = {
 
        .issue_fc_host_lip = qla2x00_issue_lip,
        .get_fc_host_stats = qla2x00_get_fc_host_stats,
+
+       .vport_create = qla24xx_vport_create,
+       .vport_disable = qla24xx_vport_disable,
+       .vport_delete = qla24xx_vport_delete,
+};
+
+struct fc_function_template qla2xxx_transport_vport_functions = {
+
+       .show_host_node_name = 1,
+       .show_host_port_name = 1,
+       .show_host_supported_classes = 1,
+
+       .get_host_port_id = qla2x00_get_host_port_id,
+       .show_host_port_id = 1,
+       .get_host_speed = qla2x00_get_host_speed,
+       .show_host_speed = 1,
+       .get_host_port_type = qla2x00_get_host_port_type,
+       .show_host_port_type = 1,
+       .get_host_symbolic_name = qla2x00_get_host_symbolic_name,
+       .show_host_symbolic_name = 1,
+       .set_host_system_hostname = qla2x00_set_host_system_hostname,
+       .show_host_system_hostname = 1,
+       .get_host_fabric_name = qla2x00_get_host_fabric_name,
+       .show_host_fabric_name = 1,
+       .get_host_port_state = qla2x00_get_host_port_state,
+       .show_host_port_state = 1,
+
+       .dd_fcrport_size = sizeof(struct fc_port *),
+       .show_rport_supported_classes = 1,
+
+       .get_starget_node_name = qla2x00_get_starget_node_name,
+       .show_starget_node_name = 1,
+       .get_starget_port_name = qla2x00_get_starget_port_name,
+       .show_starget_port_name = 1,
+       .get_starget_port_id  = qla2x00_get_starget_port_id,
+       .show_starget_port_id = 1,
+
+       .get_rport_dev_loss_tmo = qla2x00_get_rport_loss_tmo,
+       .set_rport_dev_loss_tmo = qla2x00_set_rport_loss_tmo,
+       .show_rport_dev_loss_tmo = 1,
+
+       .issue_fc_host_lip = qla2x00_issue_lip,
+       .get_fc_host_stats = qla2x00_get_fc_host_stats,
 };
 
 void
@@ -1004,4 +1166,6 @@ qla2x00_init_host_attr(scsi_qla_host_t *ha)
        fc_host_node_name(ha->host) = wwn_to_u64(ha->node_name);
        fc_host_port_name(ha->host) = wwn_to_u64(ha->port_name);
        fc_host_supported_classes(ha->host) = FC_COS_CLASS3;
+       fc_host_max_npiv_vports(ha->host) = MAX_NUM_VPORT_FABRIC;
+       fc_host_npiv_vports_inuse(ha->host) = ha->cur_vport_count;
 }
index 5b12278968e0b4d3001ff318fbc5e87024db1629..49dffeb785121dfa01bb458f74fe14359dd0ad2f 100644 (file)
@@ -21,6 +21,7 @@
 /* #define QL_DEBUG_LEVEL_12 */ /* Output IP trace msgs */
 /* #define QL_DEBUG_LEVEL_13 */ /* Output fdmi function trace msgs */
 /* #define QL_DEBUG_LEVEL_14 */ /* Output RSCN trace msgs */
+/* #define QL_DEBUG_LEVEL_15 */ /* Output NPIV trace msgs */
 /*
  *  Local Macro Definitions.
  */
@@ -30,7 +31,8 @@
     defined(QL_DEBUG_LEVEL_7)  || defined(QL_DEBUG_LEVEL_8) || \
     defined(QL_DEBUG_LEVEL_9)  || defined(QL_DEBUG_LEVEL_10) || \
     defined(QL_DEBUG_LEVEL_11) || defined(QL_DEBUG_LEVEL_12) || \
-    defined(QL_DEBUG_LEVEL_13) || defined(QL_DEBUG_LEVEL_14)
+    defined(QL_DEBUG_LEVEL_13) || defined(QL_DEBUG_LEVEL_14) || \
+    defined(QL_DEBUG_LEVEL_15)
     #define QL_DEBUG_ROUTINES
 #endif
 
 #define DEBUG14(x)     do {} while (0)
 #endif
 
+#if defined(QL_DEBUG_LEVEL_15)
+#define DEBUG15(x)      do {x;} while (0)
+#else
+#define DEBUG15(x)     do {} while (0)
+#endif
+
 /*
  * Firmware Dump structure definition
  */
index e8948b679f5baa7fcb0bbd817641629c653d94eb..a1ca590ba447c22c8c9ae615a36d3f8ef914dab8 100644 (file)
@@ -1551,6 +1551,9 @@ typedef struct fc_port {
 
        unsigned long last_queue_full;
        unsigned long last_ramp_up;
+
+       struct list_head vp_fcport;
+       uint16_t vp_idx;
 } fc_port_t;
 
 /*
@@ -1999,6 +2002,36 @@ struct gid_list_info {
 };
 #define GID_LIST_SIZE (sizeof(struct gid_list_info) * MAX_FIBRE_DEVICES)
 
+/* NPIV */
+typedef struct vport_info {
+       uint8_t         port_name[WWN_SIZE];
+       uint8_t         node_name[WWN_SIZE];
+       int             vp_id;
+       uint16_t        loop_id;
+       unsigned long   host_no;
+       uint8_t         port_id[3];
+       int             loop_state;
+} vport_info_t;
+
+typedef struct vport_params {
+       uint8_t         port_name[WWN_SIZE];
+       uint8_t         node_name[WWN_SIZE];
+       uint32_t        options;
+#define        VP_OPTS_RETRY_ENABLE    BIT_0
+#define        VP_OPTS_VP_DISABLE      BIT_1
+} vport_params_t;
+
+/* NPIV - return codes of VP create and modify */
+#define VP_RET_CODE_OK                 0
+#define VP_RET_CODE_FATAL              1
+#define VP_RET_CODE_WRONG_ID           2
+#define VP_RET_CODE_WWPN               3
+#define VP_RET_CODE_RESOURCES          4
+#define VP_RET_CODE_NO_MEM             5
+#define VP_RET_CODE_NOT_FOUND          6
+
+#define to_qla_parent(x) (((x)->parent) ? (x)->parent : (x))
+
 /*
  * ISP operations
  */
@@ -2073,6 +2106,16 @@ struct qla_msix_entry {
        uint16_t msix_entry;
 };
 
+#define        WATCH_INTERVAL          1       /* number of seconds */
+
+/* NPIV */
+#define MAX_MULTI_ID_LOOP                     126
+#define MAX_MULTI_ID_FABRIC                    64
+#define MAX_NUM_VPORT_LOOP                      (MAX_MULTI_ID_LOOP - 1)
+#define MAX_NUM_VPORT_FABRIC                    (MAX_MULTI_ID_FABRIC - 1)
+#define MAX_NUM_VHBA_LOOP                       (MAX_MULTI_ID_LOOP - 1)
+#define MAX_NUM_VHBA_FABRIC                     (MAX_MULTI_ID_FABRIC - 1)
+
 /*
  * Linux Host Adapter structure
  */
@@ -2108,6 +2151,8 @@ typedef struct scsi_qla_host {
                uint32_t        msix_enabled            :1;
                uint32_t        disable_serdes          :1;
                uint32_t        gpsc_supported          :1;
+               uint32_t        vsan_enabled            :1;
+               uint32_t        npiv_supported          :1;
        } flags;
 
        atomic_t        loop_state;
@@ -2147,6 +2192,7 @@ typedef struct scsi_qla_host {
 #define BEACON_BLINK_NEEDED    25
 #define REGISTER_FDMI_NEEDED   26
 #define FCPORT_UPDATE_NEEDED   27
+#define VP_DPC_NEEDED          28      /* wake up for VP dpc handling */
 
        uint32_t        device_flags;
 #define DFLG_LOCAL_DEVICES             BIT_0
@@ -2237,6 +2283,11 @@ typedef struct scsi_qla_host {
 
        /* ISP configuration data. */
        uint16_t        loop_id;                /* Host adapter loop id */
+       uint16_t        switch_cap;
+#define FLOGI_SEQ_DEL          BIT_8
+#define FLOGI_MID_SUPPORT      BIT_10
+#define FLOGI_VSAN_SUPPORT     BIT_12
+#define FLOGI_SP_SUPPORT       BIT_13
        uint16_t        fb_rev;
 
        port_id_t       d_id;                   /* Host adapter port id */
@@ -2344,6 +2395,7 @@ typedef struct scsi_qla_host {
 #define MBX_UPDATE_FLASH_ACTIVE        3
 
        struct semaphore mbx_cmd_sem;   /* Serialialize mbx access */
+       struct semaphore vport_sem;     /* Virtual port synchronization */
        struct semaphore mbx_intr_sem;  /* Used for completion notification */
 
        uint32_t        mbx_flags;
@@ -2428,6 +2480,37 @@ typedef struct scsi_qla_host {
        struct fc_host_statistics fc_host_stat;
 
        struct qla_msix_entry msix_entries[QLA_MSIX_ENTRIES];
+
+       struct list_head        vp_list;        /* list of VP */
+       struct fc_vport *fc_vport;      /* holds fc_vport * for each vport */
+       uint8_t         vp_idx_map[16];
+       uint16_t        num_vhosts;     /* number of vports created */
+       uint16_t        num_vsans;      /* number of vsan created */
+       uint16_t        vp_idx;         /* vport ID */
+
+       struct scsi_qla_host    *parent;        /* holds pport */
+       unsigned long           vp_flags;
+       struct list_head        vp_fcports;     /* list of fcports */
+#define VP_IDX_ACQUIRED                0       /* bit no 0 */
+#define VP_CREATE_NEEDED       1
+#define VP_BIND_NEEDED         2
+#define VP_DELETE_NEEDED       3
+#define VP_SCR_NEEDED          4       /* State Change Request registration */
+       atomic_t                vp_state;
+#define VP_OFFLINE             0
+#define VP_ACTIVE              1
+#define VP_FAILED              2
+// #define VP_DISABLE          3
+       uint16_t        vp_err_state;
+       uint16_t        vp_prev_err_state;
+#define VP_ERR_UNKWN           0
+#define VP_ERR_PORTDWN         1
+#define VP_ERR_FAB_UNSUPPORTED 2
+#define VP_ERR_FAB_NORESOURCES 3
+#define VP_ERR_FAB_LOGOUT      4
+#define VP_ERR_ADAP_NORESOURCES        5
+       int             max_npiv_vports;        /* 63 or 125 per topoloty */
+       int             cur_vport_count;
 } scsi_qla_host_t;
 
 
index a0a722cf423769fb8fd152eaeff5aa140a9ccdb3..63a11fef5d1bcf8d048bc0f6d6056b9ab9f8fcc9 100644 (file)
@@ -69,6 +69,16 @@ struct port_database_24xx {
        uint8_t reserved_3[24];
 };
 
+struct vp_database_24xx {
+       uint16_t vp_status;
+       uint8_t  options;
+       uint8_t  id;
+       uint8_t  port_name[WWN_SIZE];
+       uint8_t  node_name[WWN_SIZE];
+       uint16_t port_id_low;
+       uint16_t port_id_high;
+};
+
 struct nvram_24xx {
        /* NVRAM header. */
        uint8_t id[4];
@@ -962,6 +972,25 @@ struct mid_db_24xx {
        struct mid_db_entry_24xx entries[MAX_MID_VPS];
 };
 
+ /*
+ * Virtual Fabric ID type definition.
+ */
+typedef struct vf_id {
+        uint16_t id : 12;
+        uint16_t priority : 4;
+} vf_id_t;
+
+/*
+ * Virtual Fabric HopCt type definition.
+ */
+typedef struct vf_hopct {
+        uint16_t reserved : 8;
+        uint16_t hopct : 8;
+} vf_hopct_t;
+
+/*
+ * Virtual Port Control IOCB
+ */
 #define VP_CTRL_IOCB_TYPE      0x30    /* Vitual Port Control entry. */
 struct vp_ctrl_entry_24xx {
        uint8_t entry_type;             /* Entry type. */
@@ -974,6 +1003,7 @@ struct vp_ctrl_entry_24xx {
        uint16_t vp_idx_failed;
 
        uint16_t comp_status;           /* Completion status. */
+#define CS_VCE_IOCB_ERROR       0x01    /* Error processing IOCB */
 #define CS_VCE_ACQ_ID_ERROR    0x02    /* Error while acquireing ID. */
 #define CS_VCE_BUSY            0x05    /* Firmware not ready to accept cmd. */
 
@@ -982,24 +1012,34 @@ struct vp_ctrl_entry_24xx {
 #define VCE_COMMAND_DISABLE_VPS        0x08    /* Disable VPs. */
 #define VCE_COMMAND_DISABLE_VPS_REINIT 0x09 /* Disable VPs and reinit link. */
 #define VCE_COMMAND_DISABLE_VPS_LOGO   0x0a /* Disable VPs and LOGO ports. */
+#define VCE_COMMAND_DISABLE_VPS_LOGO_ALL        0x0b /* Disable VPs and LOGO ports. */
 
        uint16_t vp_count;
 
        uint8_t vp_idx_map[16];
-
-       uint8_t reserved_4[32];
+       uint16_t flags;
+       struct vf_id    id;
+       uint16_t reserved_4;
+       struct vf_hopct  hopct;
+       uint8_t reserved_5[8];
 };
 
+/*
+ * Modify Virtual Port Configuration IOCB
+ */
 #define VP_CONFIG_IOCB_TYPE    0x31    /* Vitual Port Config entry. */
 struct vp_config_entry_24xx {
        uint8_t entry_type;             /* Entry type. */
        uint8_t entry_count;            /* Entry count. */
-       uint8_t sys_define;             /* System defined. */
+       uint8_t handle_count;
        uint8_t entry_status;           /* Entry Status. */
 
        uint32_t handle;                /* System handle. */
 
-       uint16_t reserved_1;
+       uint16_t flags;
+#define CS_VF_BIND_VPORTS_TO_VF         BIT_0
+#define CS_VF_SET_QOS_OF_VPORTS         BIT_1
+#define CS_VF_SET_HOPS_OF_VPORTS        BIT_2
 
        uint16_t comp_status;           /* Completion status. */
 #define CS_VCT_STS_ERROR       0x01    /* Specified VPs were not disabled. */
@@ -1009,27 +1049,29 @@ struct vp_config_entry_24xx {
 #define CS_VCT_BUSY            0x05    /* Firmware not ready to accept cmd. */
 
        uint8_t command;
-#define VCT_COMMAND_MOD_VPS    0x00    /* Enable VPs. */
-#define VCT_COMMAND_MOD_ENABLE_VPS 0x08        /* Disable VPs. */
+#define VCT_COMMAND_MOD_VPS     0x00    /* Modify VP configurations. */
+#define VCT_COMMAND_MOD_ENABLE_VPS 0x01 /* Modify configuration & enable VPs. */
 
        uint8_t vp_count;
 
-       uint8_t vp_idx1;
-       uint8_t vp_idx2;
+       uint8_t vp_index1;
+       uint8_t vp_index2;
 
        uint8_t options_idx1;
        uint8_t hard_address_idx1;
-       uint16_t reserved_2;
+       uint16_t reserved_vp1;
        uint8_t port_name_idx1[WWN_SIZE];
        uint8_t node_name_idx1[WWN_SIZE];
 
        uint8_t options_idx2;
        uint8_t hard_address_idx2;
-       uint16_t reserved_3;
+       uint16_t reserved_vp2;
        uint8_t port_name_idx2[WWN_SIZE];
        uint8_t node_name_idx2[WWN_SIZE];
-
-       uint8_t reserved_4[8];
+       struct vf_id    id;
+       uint16_t reserved_4;
+       struct vf_hopct  hopct;
+       uint8_t reserved_5;
 };
 
 #define VP_RPT_ID_IOCB_TYPE    0x32    /* Report ID Acquisition entry. */
@@ -1054,5 +1096,30 @@ struct vp_rpt_id_entry_24xx {
        uint8_t reserved_4[32];
 };
 
+#define VF_EVFP_IOCB_TYPE       0x26    /* Exchange Virtual Fabric Parameters entry. */
+struct vf_evfp_entry_24xx {
+        uint8_t entry_type;             /* Entry type. */
+        uint8_t entry_count;            /* Entry count. */
+        uint8_t sys_define;             /* System defined. */
+        uint8_t entry_status;           /* Entry Status. */
+
+        uint32_t handle;                /* System handle. */
+        uint16_t comp_status;           /* Completion status. */
+        uint16_t timeout;               /* timeout */
+        uint16_t adim_tagging_mode;
+
+        uint16_t vfport_id;
+        uint32_t exch_addr;
+
+        uint16_t nport_handle;          /* N_PORT handle. */
+        uint16_t control_flags;
+        uint32_t io_parameter_0;
+        uint32_t io_parameter_1;
+        uint32_t tx_address[2];         /* Data segment 0 address. */
+        uint32_t tx_len;                /* Data segment 0 length. */
+        uint32_t rx_address[2];         /* Data segment 1 address. */
+        uint32_t rx_len;                /* Data segment 1 length. */
+};
+
 /* END MID Support ***********************************************************/
 #endif
index 74544ae4b0e270128de9976c4397b31703802dad..b44eff2803ce01bd717fcf0ef46c2ddbb5fd6d73 100644 (file)
@@ -62,6 +62,38 @@ extern int ql2xfdmienable;
 extern int ql2xallocfwdump;
 extern int ql2xextended_error_logging;
 extern int ql2xqfullrampup;
+extern int num_hosts;
+
+/*
+ * Global Functions in qla_mid.c source file.
+ */
+extern struct scsi_host_template qla2x00_driver_template;
+extern struct scsi_host_template qla24xx_driver_template;
+extern struct scsi_transport_template *qla2xxx_transport_vport_template;
+extern uint8_t qla2x00_mem_alloc(scsi_qla_host_t *);
+extern void qla2x00_timer(scsi_qla_host_t *);
+extern void qla2x00_start_timer(scsi_qla_host_t *, void *, unsigned long);
+extern void qla2x00_stop_timer(scsi_qla_host_t *);
+extern uint32_t qla24xx_allocate_vp_id(scsi_qla_host_t *);
+extern void qla24xx_deallocate_vp_id(scsi_qla_host_t *);
+extern int qla24xx_disable_vp (scsi_qla_host_t *);
+extern int qla24xx_enable_vp (scsi_qla_host_t *);
+extern void qla2x00_mem_free(scsi_qla_host_t *);
+extern int qla24xx_control_vp(scsi_qla_host_t *, int );
+extern int qla24xx_modify_vp_config(scsi_qla_host_t *);
+extern int qla2x00_send_change_request(scsi_qla_host_t *, uint16_t, uint16_t);
+extern void qla2x00_vp_stop_timer(scsi_qla_host_t *);
+extern int qla24xx_configure_vhba (scsi_qla_host_t *);
+extern int qla24xx_get_vp_entry(scsi_qla_host_t *, uint16_t, int);
+extern int qla24xx_get_vp_database(scsi_qla_host_t *, uint16_t);
+extern int qla2x00_do_dpc_vp(scsi_qla_host_t *);
+extern void qla24xx_report_id_acquisition(scsi_qla_host_t *,
+    struct vp_rpt_id_entry_24xx *);
+extern scsi_qla_host_t * qla24xx_find_vhost_by_name(scsi_qla_host_t *,
+    uint8_t *);
+extern void qla2x00_do_dpc_all_vps(scsi_qla_host_t *);
+extern int qla24xx_vport_create_req_sanity_check(struct fc_vport *);
+extern scsi_qla_host_t * qla24xx_create_vhost(struct fc_vport *);
 
 extern void qla2x00_sp_compl(scsi_qla_host_t *, srb_t *);
 
@@ -77,6 +109,10 @@ extern struct fw_blob *qla2x00_request_firmware(scsi_qla_host_t *);
 extern int qla2x00_wait_for_hba_online(scsi_qla_host_t *);
 
 extern void qla2xxx_wake_dpc(scsi_qla_host_t *);
+extern void qla2x00_alert_all_vps(scsi_qla_host_t *, uint16_t *);
+extern void qla2x00_async_event(scsi_qla_host_t *, uint16_t *);
+extern void qla2x00_vp_abort_isp(scsi_qla_host_t *);
+extern int qla24xx_vport_delete(struct fc_vport *);
 
 /*
  * Global Function Prototypes in qla_iocb.c source file.
@@ -128,7 +164,7 @@ qla2x00_abort_target(fc_port_t *);
 
 extern int
 qla2x00_get_adapter_id(scsi_qla_host_t *, uint16_t *, uint8_t *, uint8_t *,
-    uint8_t *, uint16_t *);
+    uint8_t *, uint16_t *, uint16_t *);
 
 extern int
 qla2x00_get_retry_cnt(scsi_qla_host_t *, uint8_t *, uint8_t *, uint16_t *);
@@ -303,6 +339,7 @@ struct class_device_attribute;
 extern struct class_device_attribute *qla2x00_host_attrs[];
 struct fc_function_template;
 extern struct fc_function_template qla2xxx_transport_functions;
+extern struct fc_function_template qla2xxx_transport_vport_functions;
 extern void qla2x00_alloc_sysfs_attr(scsi_qla_host_t *);
 extern void qla2x00_free_sysfs_attr(scsi_qla_host_t *);
 extern void qla2x00_init_host_attr(scsi_qla_host_t *);
index ec5b2dd90d6a6c27445765c8ebbc70c99dcdf012..a086b3f0df653bfe542e5b1ec43e0f6bc0b37b9b 100644 (file)
@@ -88,6 +88,7 @@ qla24xx_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size)
        ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
        ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
        ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count;
+       ct_pkt->vp_index = ha->vp_idx;
 
        return (ct_pkt);
 }
@@ -1186,6 +1187,7 @@ qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size,
        ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
        ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
        ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count;
+       ct_pkt->vp_index = ha->vp_idx;
 
        return ct_pkt;
 }
@@ -1746,6 +1748,7 @@ qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *ha, uint32_t req_size,
        ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
        ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
        ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count;
+       ct_pkt->vp_index = ha->vp_idx;
 
        return ct_pkt;
 }
index 2a45aec4ff29e6499a36456d24f6f496caf9ff4a..bd95f7dc5cfce9a9279d71268952ad354f92b7f9 100644 (file)
@@ -899,6 +899,10 @@ qla2x00_setup_chip(scsi_qla_host_t *ha)
                                    &ha->fw_subminor_version,
                                    &ha->fw_attributes, &ha->fw_memory_size);
                                qla2x00_resize_request_q(ha);
+                               ha->flags.npiv_supported = 0;
+                               if (IS_QLA24XX(ha) &&
+                                   (ha->fw_attributes & BIT_2))
+                                       ha->flags.npiv_supported = 1;
 
                                if (ql2xallocfwdump)
                                        qla2x00_alloc_fw_dump(ha);
@@ -1101,6 +1105,8 @@ qla2x00_init_rings(scsi_qla_host_t *ha)
        int     rval;
        unsigned long flags = 0;
        int cnt;
+       struct mid_init_cb_24xx *mid_init_cb =
+           (struct mid_init_cb_24xx *) ha->init_cb;
 
        spin_lock_irqsave(&ha->hardware_lock, flags);
 
@@ -1132,6 +1138,10 @@ qla2x00_init_rings(scsi_qla_host_t *ha)
        ha->isp_ops.update_fw_options(ha);
 
        DEBUG(printk("scsi(%ld): Issue init firmware.\n", ha->host_no));
+
+       mid_init_cb->count = MAX_NUM_VPORT_FABRIC;
+       ha->max_npiv_vports = MAX_NUM_VPORT_FABRIC;
+
        rval = qla2x00_init_firmware(ha, ha->init_cb_size);
        if (rval) {
                DEBUG2_3(printk("scsi(%ld): Init firmware **** FAILED ****.\n",
@@ -1263,6 +1273,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
        int       rval;
        uint16_t      loop_id;
        uint16_t      topo;
+       uint16_t      sw_cap;
        uint8_t       al_pa;
        uint8_t       area;
        uint8_t       domain;
@@ -1270,7 +1281,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
 
        /* Get host addresses. */
        rval = qla2x00_get_adapter_id(ha,
-           &loop_id, &al_pa, &area, &domain, &topo);
+           &loop_id, &al_pa, &area, &domain, &topo, &sw_cap);
        if (rval != QLA_SUCCESS) {
                if (LOOP_TRANSITION(ha) || atomic_read(&ha->loop_down_timer) ||
                    (rval == QLA_COMMAND_ERROR && loop_id == 0x7)) {
@@ -1295,6 +1306,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
        /* initialize */
        ha->min_external_loopid = SNS_FIRST_LOOP_ID;
        ha->operating_mode = LOOP;
+       ha->switch_cap = 0;
 
        switch (topo) {
        case 0:
@@ -1307,6 +1319,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
        case 1:
                DEBUG3(printk("scsi(%ld): HBA in FL topology.\n",
                    ha->host_no));
+               ha->switch_cap = sw_cap;
                ha->current_topology = ISP_CFG_FL;
                strcpy(connect_type, "(FL_Port)");
                break;
@@ -1322,6 +1335,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
        case 3:
                DEBUG3(printk("scsi(%ld): HBA in F P2P topology.\n",
                    ha->host_no));
+               ha->switch_cap = sw_cap;
                ha->operating_mode = P2P;
                ha->current_topology = ISP_CFG_F;
                strcpy(connect_type, "(F_Port)");
@@ -1743,7 +1757,6 @@ qla2x00_rport_del(void *data)
        spin_unlock_irqrestore(&fcport->rport_lock, flags);
        if (rport)
                fc_remote_port_delete(rport);
-
 }
 
 /**
@@ -1765,6 +1778,7 @@ qla2x00_alloc_fcport(scsi_qla_host_t *ha, gfp_t flags)
        /* Setup fcport template structure. */
        memset(fcport, 0, sizeof (fc_port_t));
        fcport->ha = ha;
+       fcport->vp_idx = ha->vp_idx;
        fcport->port_type = FCT_UNKNOWN;
        fcport->loop_id = FC_NO_LOOP_ID;
        atomic_set(&fcport->state, FCS_UNCONFIGURED);
@@ -1911,6 +1925,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
        char            *id_iter;
        uint16_t        loop_id;
        uint8_t         domain, area, al_pa;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
        found_devs = 0;
        new_fcport = NULL;
@@ -1942,7 +1957,10 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
        /*
         * Mark local devices that were present with FCF_DEVICE_LOST for now.
         */
-       list_for_each_entry(fcport, &ha->fcports, list) {
+       list_for_each_entry(fcport, &pha->fcports, list) {
+               if (fcport->vp_idx != ha->vp_idx)
+                       continue;
+
                if (atomic_read(&fcport->state) == FCS_ONLINE &&
                    fcport->port_type != FCT_BROADCAST &&
                    (fcport->flags & FCF_FABRIC_DEVICE) == 0) {
@@ -1988,6 +2006,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
                new_fcport->d_id.b.area = area;
                new_fcport->d_id.b.al_pa = al_pa;
                new_fcport->loop_id = loop_id;
+               new_fcport->vp_idx = ha->vp_idx;
                rval2 = qla2x00_get_port_database(ha, new_fcport, 0);
                if (rval2 != QLA_SUCCESS) {
                        DEBUG2(printk("scsi(%ld): Failed to retrieve fcport "
@@ -2003,7 +2022,10 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
                /* Check for matching device in port list. */
                found = 0;
                fcport = NULL;
-               list_for_each_entry(fcport, &ha->fcports, list) {
+               list_for_each_entry(fcport, &pha->fcports, list) {
+                       if (fcport->vp_idx != ha->vp_idx)
+                               continue;
+
                        if (memcmp(new_fcport->port_name, fcport->port_name,
                            WWN_SIZE))
                                continue;
@@ -2023,7 +2045,13 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
                if (!found) {
                        /* New device, add to fcports list. */
                        new_fcport->flags &= ~FCF_PERSISTENT_BOUND;
-                       list_add_tail(&new_fcport->list, &ha->fcports);
+                       if (ha->parent) {
+                               new_fcport->ha = ha;
+                               new_fcport->vp_idx = ha->vp_idx;
+                               list_add_tail(&new_fcport->vp_fcport,
+                                   &ha->vp_fcports);
+                       }
+                       list_add_tail(&new_fcport->list, &pha->fcports);
 
                        /* Allocate a new replacement fcport. */
                        fcport = new_fcport;
@@ -2199,11 +2227,13 @@ qla2x00_reg_remote_port(scsi_qla_host_t *ha, fc_port_t *fcport)
 void
 qla2x00_update_fcport(scsi_qla_host_t *ha, fc_port_t *fcport)
 {
+       scsi_qla_host_t *pha = to_qla_parent(ha);
+
        fcport->ha = ha;
        fcport->login_retry = 0;
-       fcport->port_login_retry_count = ha->port_down_retry_count *
+       fcport->port_login_retry_count = pha->port_down_retry_count *
            PORT_RETRY_TIME;
-       atomic_set(&fcport->port_down_timer, ha->port_down_retry_count *
+       atomic_set(&fcport->port_down_timer, pha->port_down_retry_count *
            PORT_RETRY_TIME);
        fcport->flags &= ~FCF_LOGIN_NEEDED;
 
@@ -2234,6 +2264,7 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
        uint16_t        mb[MAILBOX_REGISTER_COUNT];
        uint16_t        loop_id;
        LIST_HEAD(new_fcports);
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
        /* If FL port exists, then SNS is present */
        if (IS_QLA24XX(ha) || IS_QLA54XX(ha))
@@ -2307,7 +2338,10 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
                 * Logout all previous fabric devices marked lost, except
                 * tape devices.
                 */
-               list_for_each_entry(fcport, &ha->fcports, list) {
+               list_for_each_entry(fcport, &pha->fcports, list) {
+                       if (fcport->vp_idx !=ha->vp_idx)
+                               continue;
+
                        if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
                                break;
 
@@ -2332,13 +2366,16 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
                }
 
                /* Starting free loop ID. */
-               next_loopid = ha->min_external_loopid;
+               next_loopid = pha->min_external_loopid;
 
                /*
                 * Scan through our port list and login entries that need to be
                 * logged in.
                 */
-               list_for_each_entry(fcport, &ha->fcports, list) {
+               list_for_each_entry(fcport, &pha->fcports, list) {
+                       if (fcport->vp_idx != ha->vp_idx)
+                               continue;
+
                        if (atomic_read(&ha->loop_down_timer) ||
                            test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
                                break;
@@ -2380,11 +2417,18 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
                                break;
                        }
 
-                       /* Remove device from the new list and add it to DB */
-                       list_move_tail(&fcport->list, &ha->fcports);
-
                        /* Login and update database */
                        qla2x00_fabric_dev_login(ha, fcport, &next_loopid);
+
+                       if (ha->parent) {
+                               fcport->ha = ha;
+                               fcport->vp_idx = ha->vp_idx;
+                               list_add_tail(&fcport->vp_fcport,
+                                   &ha->vp_fcports);
+                               list_move_tail(&fcport->list,
+                                   &ha->parent->fcports);
+                       } else
+                               list_move_tail(&fcport->list, &ha->fcports);
                }
        } while (0);
 
@@ -2428,6 +2472,11 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
        int             swl_idx;
        int             first_dev, last_dev;
        port_id_t       wrap, nxt_d_id;
+       int             vp_index;
+       int             empty_vp_index;
+       int             found_vp;
+       scsi_qla_host_t *vha;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
        rval = QLA_SUCCESS;
 
@@ -2461,13 +2510,13 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
                return (QLA_MEMORY_ALLOC_FAILED);
        }
        new_fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED);
-
+       new_fcport->vp_idx = ha->vp_idx;
        /* Set start port ID scan at adapter ID. */
        first_dev = 1;
        last_dev = 0;
 
        /* Starting free loop ID. */
-       loop_id = ha->min_external_loopid;
+       loop_id = pha->min_external_loopid;
        for (; loop_id <= ha->last_loop_id; loop_id++) {
                if (qla2x00_is_reserved_id(ha, loop_id))
                        continue;
@@ -2521,10 +2570,42 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
                        break;
                }
 
-               /* Bypass if host adapter. */
-               if (new_fcport->d_id.b24 == ha->d_id.b24)
+               /* Bypass if same physical adapter. */
+               if (new_fcport->d_id.b24 == pha->d_id.b24)
                        continue;
 
+               /* Bypass virtual ports of the same host. */
+               if (pha->num_vhosts) {
+                       vp_index = find_next_bit(
+                           (unsigned long *)pha->vp_idx_map,
+                           MAX_MULTI_ID_FABRIC + 1, 1);
+
+                       for (;vp_index <= MAX_MULTI_ID_FABRIC;
+                           vp_index = find_next_bit(
+                           (unsigned long *)pha->vp_idx_map,
+                           MAX_MULTI_ID_FABRIC + 1, vp_index + 1)) {
+                               empty_vp_index = 1;
+                               found_vp = 0;
+                               list_for_each_entry(vha, &pha->vp_list,
+                                   vp_list) {
+                                       if (vp_index == vha->vp_idx) {
+                                               empty_vp_index = 0;
+                                               found_vp = 1;
+                                               break;
+                                       }
+                               }
+
+                               if (empty_vp_index)
+                                       continue;
+
+                               if (found_vp &&
+                                   new_fcport->d_id.b24 == vha->d_id.b24)
+                                       break;
+                       }
+                       if (vp_index <= MAX_MULTI_ID_FABRIC)
+                               continue;
+               }
+
                /* Bypass if same domain and area of adapter. */
                if (((new_fcport->d_id.b24 & 0xffff00) ==
                    (ha->d_id.b24 & 0xffff00)) && ha->current_topology ==
@@ -2537,7 +2618,9 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
 
                /* Locate matching device in database. */
                found = 0;
-               list_for_each_entry(fcport, &ha->fcports, list) {
+               list_for_each_entry(fcport, &pha->fcports, list) {
+                       if (new_fcport->vp_idx != fcport->vp_idx)
+                               continue;
                        if (memcmp(new_fcport->port_name, fcport->port_name,
                            WWN_SIZE))
                                continue;
@@ -2605,6 +2688,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
                }
                new_fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED);
                new_fcport->d_id.b24 = nxt_d_id.b24;
+               new_fcport->vp_idx = ha->vp_idx;
        }
 
        kfree(swl);
@@ -2637,6 +2721,7 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev)
        int     found;
        fc_port_t *fcport;
        uint16_t first_loop_id;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
        rval = QLA_SUCCESS;
 
@@ -2663,7 +2748,7 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev)
                /* Check for loop ID being already in use. */
                found = 0;
                fcport = NULL;
-               list_for_each_entry(fcport, &ha->fcports, list) {
+               list_for_each_entry(fcport, &pha->fcports, list) {
                        if (fcport->loop_id == dev->loop_id && fcport != dev) {
                                /* ID possibly in use */
                                found++;
@@ -2710,6 +2795,7 @@ qla2x00_device_resync(scsi_qla_host_t *ha)
        uint8_t rscn_out_iter;
        uint8_t format;
        port_id_t d_id;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
        rval = QLA_RSCNS_HANDLED;
 
@@ -2776,7 +2862,10 @@ qla2x00_device_resync(scsi_qla_host_t *ha)
 
                rval = QLA_SUCCESS;
 
-               list_for_each_entry(fcport, &ha->fcports, list) {
+               list_for_each_entry(fcport, &pha->fcports, list) {
+                       if (fcport->vp_idx != ha->vp_idx)
+                               continue;
+
                        if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 ||
                            (fcport->d_id.b24 & mask) != d_id.b24 ||
                            fcport->port_type == FCT_BROADCAST)
@@ -3940,3 +4029,40 @@ qla2x00_try_to_stop_firmware(scsi_qla_host_t *ha)
                ret = qla2x00_stop_firmware(ha);
        }
 }
+
+int
+qla24xx_configure_vhba(scsi_qla_host_t *ha)
+{
+       int rval = QLA_SUCCESS;
+       uint16_t mb[MAILBOX_REGISTER_COUNT];
+
+       if (!ha->parent)
+               return -EINVAL;
+
+       rval = qla2x00_fw_ready(ha);
+       if (rval == QLA_SUCCESS) {
+               clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+               qla2x00_marker(ha, 0, 0, MK_SYNC_ALL);
+       }
+
+       ha->flags.management_server_logged_in = 0;
+
+       /* Login to SNS first */
+       qla24xx_login_fabric(ha, NPH_SNS, 0xff, 0xff, 0xfc,
+           mb, BIT_1);
+       if (mb[0] != MBS_COMMAND_COMPLETE) {
+               DEBUG15(qla_printk(KERN_INFO, ha,
+                   "Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x "
+                   "mb[2]=%x mb[6]=%x mb[7]=%x\n", NPH_SNS,
+                   mb[0], mb[1], mb[2], mb[6], mb[7]));
+               return (QLA_FUNCTION_FAILED);
+       }
+
+       atomic_set(&ha->loop_down_timer, 0);
+       atomic_set(&ha->loop_state, LOOP_UP);
+       set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
+       set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
+       rval = qla2x00_loop_resync(ha);
+
+       return rval;
+}
index c517a1478e442b781936e10f809c9271eedc6dbe..c71863ff5489e7db2ba9445fdc3d381f133fc7a6 100644 (file)
@@ -179,7 +179,6 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
        cur_dsd = (uint32_t *)&cmd_pkt->dseg_0_address;
 
        /* Load data segments */
-
        scsi_for_each_sg(cmd, sg, tot_dsds, i) {
                cont_entry_t *cont_pkt;
 
@@ -316,9 +315,14 @@ qla2x00_start_scsi(srb_t *sp)
                goto queuing_error;
 
        /* Map the sg table so we have an accurate count of sg entries needed */
-       nseg = scsi_dma_map(cmd);
-       if (nseg < 0)
-               goto queuing_error;
+       if (scsi_sg_count(cmd)) {
+               nseg = dma_map_sg(&ha->pdev->dev, scsi_sglist(cmd),
+                   scsi_sg_count(cmd), cmd->sc_data_direction);
+               if (unlikely(!nseg))
+                       goto queuing_error;
+       } else
+               nseg = 0;
+
        tot_dsds = nseg;
 
        /* Calculate the number of request entries needed. */
@@ -414,9 +418,10 @@ __qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
 {
        mrk_entry_t *mrk;
        struct mrk_entry_24xx *mrk24;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
        mrk24 = NULL;
-       mrk = (mrk_entry_t *)qla2x00_req_pkt(ha);
+       mrk = (mrk_entry_t *)qla2x00_req_pkt(pha);
        if (mrk == NULL) {
                DEBUG2_3(printk("%s(%ld): failed to allocate Marker IOCB.\n",
                    __func__, ha->host_no));
@@ -433,6 +438,7 @@ __qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
                        mrk24->lun[1] = LSB(lun);
                        mrk24->lun[2] = MSB(lun);
                        host_to_fcp_swap(mrk24->lun, sizeof(mrk24->lun));
+                       mrk24->vp_index = ha->vp_idx;
                } else {
                        SET_TARGET_ID(ha, mrk->target, loop_id);
                        mrk->lun = cpu_to_le16(lun);
@@ -440,7 +446,7 @@ __qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
        }
        wmb();
 
-       qla2x00_isp_cmd(ha);
+       qla2x00_isp_cmd(pha);
 
        return (QLA_SUCCESS);
 }
@@ -712,9 +718,14 @@ qla24xx_start_scsi(srb_t *sp)
                goto queuing_error;
 
        /* Map the sg table so we have an accurate count of sg entries needed */
-       nseg = scsi_dma_map(cmd);
-       if (nseg < 0)
+       if (scsi_sg_count(cmd)) {
+               nseg = dma_map_sg(&ha->pdev->dev, scsi_sglist(cmd),
+                   scsi_sg_count(cmd), cmd->sc_data_direction);
+               if (unlikely(!nseg))
                        goto queuing_error;
+       } else
+               nseg = 0;
+
        tot_dsds = nseg;
 
        req_cnt = qla24xx_calc_iocbs(tot_dsds);
@@ -750,6 +761,7 @@ qla24xx_start_scsi(srb_t *sp)
        cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
        cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
        cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+       cmd_pkt->vp_index = sp->fcport->vp_idx;
 
        int_to_scsilun(sp->cmd->device->lun, &cmd_pkt->lun);
        host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
index 6ce532cdc4c1ee6b148ddea22f8c08ee33f1a205..0ba4c8d378790c759071cba64dbbb635809a6cd8 100644 (file)
@@ -9,7 +9,6 @@
 #include <scsi/scsi_tcq.h>
 
 static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t);
-static void qla2x00_async_event(scsi_qla_host_t *, uint16_t *);
 static void qla2x00_process_completed_request(struct scsi_qla_host *, uint32_t);
 static void qla2x00_status_entry(scsi_qla_host_t *, void *);
 static void qla2x00_status_cont_entry(scsi_qla_host_t *, sts_cont_entry_t *);
@@ -244,7 +243,7 @@ qla2x00_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0)
  * @ha: SCSI driver HA context
  * @mb: Mailbox registers (0 - 3)
  */
-static void
+void
 qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
 {
 #define LS_UNKNOWN     2
@@ -386,6 +385,11 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                        qla2x00_mark_all_devices_lost(ha, 1);
                }
 
+               if (ha->parent) {
+                       atomic_set(&ha->vp_state, VP_FAILED);
+                       fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED);
+               }
+
                set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags);
 
                ha->flags.management_server_logged_in = 0;
@@ -422,6 +426,11 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                        qla2x00_mark_all_devices_lost(ha, 1);
                }
 
+               if (ha->parent) {
+                       atomic_set(&ha->vp_state, VP_FAILED);
+                       fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED);
+               }
+
                ha->flags.management_server_logged_in = 0;
                ha->link_data_rate = PORT_SPEED_UNKNOWN;
                if (ql2xfdmienable)
@@ -440,6 +449,11 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                        qla2x00_mark_all_devices_lost(ha, 1);
                }
 
+               if (ha->parent) {
+                       atomic_set(&ha->vp_state, VP_FAILED);
+                       fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED);
+               }
+
                set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
 
                ha->operating_mode = LOOP;
@@ -465,6 +479,11 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                        qla2x00_mark_all_devices_lost(ha, 1);
                }
 
+               if (ha->parent) {
+                       atomic_set(&ha->vp_state, VP_FAILED);
+                       fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED);
+               }
+
                if (!(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags))) {
                        set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
                }
@@ -491,6 +510,11 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                        qla2x00_mark_all_devices_lost(ha, 1);
                }
 
+               if (ha->parent) {
+                       atomic_set(&ha->vp_state, VP_FAILED);
+                       fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED);
+               }
+
                set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
                set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
                break;
@@ -530,6 +554,10 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                break;
 
        case MBA_RSCN_UPDATE:           /* State Change Registration */
+               /* Check if the Vport has issued a SCR */
+               if (ha->parent && test_bit(VP_SCR_NEEDED, &ha->vp_flags))
+                       break;
+
                DEBUG2(printk("scsi(%ld): Asynchronous RSCR UPDATE.\n",
                    ha->host_no));
                DEBUG(printk(KERN_INFO
@@ -589,6 +617,9 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                ha->host_no, mb[1], mb[2]));
                break;
        }
+
+       if (!ha->parent && ha->num_vhosts)
+               qla2x00_alert_all_vps(ha, mb);
 }
 
 static void
@@ -1393,6 +1424,10 @@ qla24xx_process_response_queue(struct scsi_qla_host *ha)
                case MS_IOCB_TYPE:
                        qla24xx_ms_entry(ha, (struct ct_entry_24xx *)pkt);
                        break;
+               case VP_RPT_ID_IOCB_TYPE:
+                       qla24xx_report_id_acquisition(ha,
+                           (struct vp_rpt_id_entry_24xx *)pkt);
+                       break;
                default:
                        /* Type Not Supported. */
                        DEBUG4(printk(KERN_WARNING
index 71e32a24852890711c4d273863004b8c7c978881..2cd0cff25928453440d2889aee4f806d726eae6d 100644 (file)
@@ -42,25 +42,29 @@ qla2x00_mbx_sem_timeout(unsigned long data)
  *     Kernel context.
  */
 static int
-qla2x00_mailbox_command(scsi_qla_host_t *ha, mbx_cmd_t *mcp)
+qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
 {
        int             rval;
        unsigned long    flags = 0;
-       device_reg_t __iomem *reg = ha->iobase;
+       device_reg_t __iomem *reg;
        struct timer_list       tmp_intr_timer;
        uint8_t         abort_active;
-       uint8_t         io_lock_on = ha->flags.init_done;
+       uint8_t         io_lock_on;
        uint16_t        command;
        uint16_t        *iptr;
        uint16_t __iomem *optr;
        uint32_t        cnt;
        uint32_t        mboxes;
        unsigned long   wait_time;
+       scsi_qla_host_t *ha = to_qla_parent(pvha);
+
+       reg = ha->iobase;
+       io_lock_on = ha->flags.init_done;
 
        rval = QLA_SUCCESS;
        abort_active = test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
 
-       DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+       DEBUG11(printk("%s(%ld): entered.\n", __func__, pvha->host_no));
 
        /*
         * Wait for active mailbox commands to finish by waiting at most tov
@@ -889,7 +893,7 @@ qla2x00_abort_target(fc_port_t *fcport)
  */
 int
 qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa,
-    uint8_t *area, uint8_t *domain, uint16_t *top)
+    uint8_t *area, uint8_t *domain, uint16_t *top, uint16_t *sw_cap)
 {
        int rval;
        mbx_cmd_t mc;
@@ -899,8 +903,9 @@ qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa,
            ha->host_no));
 
        mcp->mb[0] = MBC_GET_ADAPTER_LOOP_ID;
+       mcp->mb[9] = ha->vp_idx;
        mcp->out_mb = MBX_0;
-       mcp->in_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+       mcp->in_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
        mcp->tov = 30;
        mcp->flags = 0;
        rval = qla2x00_mailbox_command(ha, mcp);
@@ -913,6 +918,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa,
        *area = MSB(mcp->mb[2]);
        *domain = LSB(mcp->mb[3]);
        *top = mcp->mb[6];
+       *sw_cap = mcp->mb[7];
 
        if (rval != QLA_SUCCESS) {
                /*EMPTY*/
@@ -1009,7 +1015,11 @@ qla2x00_init_firmware(scsi_qla_host_t *ha, uint16_t size)
        DEBUG11(printk("qla2x00_init_firmware(%ld): entered.\n",
            ha->host_no));
 
-       mcp->mb[0] = MBC_INITIALIZE_FIRMWARE;
+       if (ha->flags.npiv_supported)
+               mcp->mb[0] = MBC_MID_INITIALIZE_FIRMWARE;
+       else
+               mcp->mb[0] = MBC_INITIALIZE_FIRMWARE;
+
        mcp->mb[2] = MSW(ha->init_cb_dma);
        mcp->mb[3] = LSW(ha->init_cb_dma);
        mcp->mb[4] = 0;
@@ -1081,7 +1091,8 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt)
        mcp->mb[3] = LSW(pd_dma);
        mcp->mb[6] = MSW(MSD(pd_dma));
        mcp->mb[7] = LSW(MSD(pd_dma));
-       mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
+       mcp->mb[9] = ha->vp_idx;
+       mcp->out_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
        mcp->in_mb = MBX_0;
        if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) {
                mcp->mb[1] = fcport->loop_id;
@@ -1259,7 +1270,8 @@ qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name,
            ha->host_no));
 
        mcp->mb[0] = MBC_GET_PORT_NAME;
-       mcp->out_mb = MBX_1|MBX_0;
+       mcp->mb[9] = ha->vp_idx;
+       mcp->out_mb = MBX_9|MBX_1|MBX_0;
        if (HAS_EXTENDED_IDS(ha)) {
                mcp->mb[1] = loop_id;
                mcp->mb[10] = opt;
@@ -1447,6 +1459,7 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
        lg->port_id[0] = al_pa;
        lg->port_id[1] = area;
        lg->port_id[2] = domain;
+       lg->vp_index = cpu_to_le16(ha->vp_idx);
        rval = qla2x00_issue_iocb(ha, lg, lg_dma, 0);
        if (rval != QLA_SUCCESS) {
                DEBUG2_3_11(printk("%s(%ld): failed to issue Login IOCB "
@@ -1701,6 +1714,7 @@ qla24xx_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
        lg->port_id[0] = al_pa;
        lg->port_id[1] = area;
        lg->port_id[2] = domain;
+       lg->vp_index = cpu_to_le16(ha->vp_idx);
        rval = qla2x00_issue_iocb(ha, lg, lg_dma, 0);
        if (rval != QLA_SUCCESS) {
                DEBUG2_3_11(printk("%s(%ld): failed to issue Logout IOCB "
@@ -1863,7 +1877,8 @@ qla2x00_get_id_list(scsi_qla_host_t *ha, void *id_list, dma_addr_t id_list_dma,
                mcp->mb[6] = MSW(MSD(id_list_dma));
                mcp->mb[7] = LSW(MSD(id_list_dma));
                mcp->mb[8] = 0;
-               mcp->out_mb |= MBX_8|MBX_7|MBX_6|MBX_3|MBX_2;
+               mcp->mb[9] = ha->vp_idx;
+               mcp->out_mb |= MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2;
        } else {
                mcp->mb[1] = MSW(id_list_dma);
                mcp->mb[2] = LSW(id_list_dma);
@@ -2212,6 +2227,7 @@ qla24xx_abort_command(scsi_qla_host_t *ha, srb_t *sp)
        abt->port_id[0] = fcport->d_id.b.al_pa;
        abt->port_id[1] = fcport->d_id.b.area;
        abt->port_id[2] = fcport->d_id.b.domain;
+       abt->vp_index = fcport->vp_idx;
        rval = qla2x00_issue_iocb(ha, abt, abt_dma, 0);
        if (rval != QLA_SUCCESS) {
                DEBUG2_3_11(printk("%s(%ld): failed to issue IOCB (%x).\n",
@@ -2249,7 +2265,7 @@ qla24xx_abort_target(fc_port_t *fcport)
        int             rval;
        struct tsk_mgmt_cmd *tsk;
        dma_addr_t      tsk_dma;
-       scsi_qla_host_t *ha;
+       scsi_qla_host_t *ha, *pha;
 
        if (fcport == NULL)
                return 0;
@@ -2257,7 +2273,8 @@ qla24xx_abort_target(fc_port_t *fcport)
        DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->ha->host_no));
 
        ha = fcport->ha;
-       tsk = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &tsk_dma);
+       pha = to_qla_parent(ha);
+       tsk = dma_pool_alloc(pha->s_dma_pool, GFP_KERNEL, &tsk_dma);
        if (tsk == NULL) {
                DEBUG2_3(printk("%s(%ld): failed to allocate Task Management "
                    "IOCB.\n", __func__, ha->host_no));
@@ -2273,6 +2290,8 @@ qla24xx_abort_target(fc_port_t *fcport)
        tsk->p.tsk.port_id[0] = fcport->d_id.b.al_pa;
        tsk->p.tsk.port_id[1] = fcport->d_id.b.area;
        tsk->p.tsk.port_id[2] = fcport->d_id.b.domain;
+       tsk->p.tsk.vp_index = fcport->vp_idx;
+
        rval = qla2x00_issue_iocb(ha, tsk, tsk_dma, 0);
        if (rval != QLA_SUCCESS) {
                DEBUG2_3_11(printk("%s(%ld): failed to issue Target Reset IOCB "
@@ -2303,7 +2322,7 @@ qla24xx_abort_target(fc_port_t *fcport)
        }
 
 atarget_done:
-       dma_pool_free(ha->s_dma_pool, tsk, tsk_dma);
+       dma_pool_free(pha->s_dma_pool, tsk, tsk_dma);
 
        return rval;
 }
@@ -2610,3 +2629,354 @@ qla2x00_set_idma_speed(scsi_qla_host_t *ha, uint16_t loop_id,
 
        return rval;
 }
+
+/*
+ * qla24xx_get_vp_database
+ *     Get the VP's database for all configured ports.
+ *
+ * Input:
+ *     ha = adapter block pointer.
+ *     size = size of initialization control block.
+ *
+ * Returns:
+ *     qla2x00 local function return status code.
+ *
+ * Context:
+ *     Kernel context.
+ */
+int
+qla24xx_get_vp_database(scsi_qla_host_t *ha, uint16_t size)
+{
+       int rval;
+       mbx_cmd_t mc;
+       mbx_cmd_t *mcp = &mc;
+
+       DEBUG11(printk("scsi(%ld):%s - entered.\n",
+           ha->host_no, __func__));
+
+       mcp->mb[0] = MBC_MID_GET_VP_DATABASE;
+       mcp->mb[2] = MSW(ha->init_cb_dma);
+       mcp->mb[3] = LSW(ha->init_cb_dma);
+       mcp->mb[4] = 0;
+       mcp->mb[5] = 0;
+       mcp->mb[6] = MSW(MSD(ha->init_cb_dma));
+       mcp->mb[7] = LSW(MSD(ha->init_cb_dma));
+       mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
+       mcp->in_mb = MBX_1|MBX_0;
+       mcp->buf_size = size;
+       mcp->flags = MBX_DMA_OUT;
+       mcp->tov = MBX_TOV_SECONDS;
+       rval = qla2x00_mailbox_command(ha, mcp);
+
+       if (rval != QLA_SUCCESS) {
+               /*EMPTY*/
+               DEBUG2_3_11(printk("%s(%ld): failed=%x "
+                   "mb0=%x.\n",
+                   __func__, ha->host_no, rval, mcp->mb[0]));
+       } else {
+               /*EMPTY*/
+               DEBUG11(printk("%s(%ld): done.\n",
+                   __func__, ha->host_no));
+       }
+
+       return rval;
+}
+
+int
+qla24xx_get_vp_entry(scsi_qla_host_t *ha, uint16_t size, int vp_id)
+{
+       int rval;
+       mbx_cmd_t mc;
+       mbx_cmd_t *mcp = &mc;
+
+       DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+
+       mcp->mb[0] = MBC_MID_GET_VP_ENTRY;
+       mcp->mb[2] = MSW(ha->init_cb_dma);
+       mcp->mb[3] = LSW(ha->init_cb_dma);
+       mcp->mb[4] = 0;
+       mcp->mb[5] = 0;
+       mcp->mb[6] = MSW(MSD(ha->init_cb_dma));
+       mcp->mb[7] = LSW(MSD(ha->init_cb_dma));
+       mcp->mb[9] = vp_id;
+       mcp->out_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
+       mcp->in_mb = MBX_0;
+       mcp->buf_size = size;
+       mcp->flags = MBX_DMA_OUT;
+       mcp->tov = 30;
+       rval = qla2x00_mailbox_command(ha, mcp);
+
+       if (rval != QLA_SUCCESS) {
+               /*EMPTY*/
+               DEBUG2_3_11(printk("qla24xx_get_vp_entry(%ld): failed=%x "
+                   "mb0=%x.\n",
+                   ha->host_no, rval, mcp->mb[0]));
+       } else {
+               /*EMPTY*/
+               DEBUG11(printk("qla24xx_get_vp_entry(%ld): done.\n",
+                   ha->host_no));
+       }
+
+       return rval;
+}
+
+void
+qla24xx_report_id_acquisition(scsi_qla_host_t *ha,
+       struct vp_rpt_id_entry_24xx *rptid_entry)
+{
+       uint8_t vp_idx;
+       scsi_qla_host_t *vha;
+
+       if (rptid_entry->entry_status != 0)
+               return;
+       if (rptid_entry->entry_status != __constant_cpu_to_le16(CS_COMPLETE))
+               return;
+
+       if (rptid_entry->format == 0) {
+               DEBUG15(printk("%s:format 0 : scsi(%ld) number of VPs setup %d,"
+                       " number of VPs acquired %d\n", __func__, ha->host_no,
+                       MSB(rptid_entry->vp_count), LSB(rptid_entry->vp_count)));
+               DEBUG15(printk("%s primary port id %02x%02x%02x\n", __func__,
+                       rptid_entry->port_id[2], rptid_entry->port_id[1],
+                       rptid_entry->port_id[0]));
+       } else if (rptid_entry->format == 1) {
+               vp_idx = LSB(rptid_entry->vp_idx);
+               DEBUG15(printk("%s:format 1: scsi(%ld): VP[%d] enabled "
+                   "- status %d - "
+                   "with port id %02x%02x%02x\n",__func__,ha->host_no,
+                   vp_idx, MSB(rptid_entry->vp_idx),
+                   rptid_entry->port_id[2], rptid_entry->port_id[1],
+                   rptid_entry->port_id[0]));
+               if (vp_idx == 0)
+                       return;
+
+               if (MSB(rptid_entry->vp_idx) == 1)
+                       return;
+
+               list_for_each_entry(vha, &ha->vp_list, vp_list)
+                       if (vp_idx == vha->vp_idx)
+                               break;
+
+               if (!vha)
+                       return;
+
+               vha->d_id.b.domain = rptid_entry->port_id[2];
+               vha->d_id.b.area =  rptid_entry->port_id[1];
+               vha->d_id.b.al_pa = rptid_entry->port_id[0];
+
+               /*
+                * Cannot configure here as we are still sitting on the
+                * response queue. Handle it in dpc context.
+                */
+               set_bit(VP_IDX_ACQUIRED, &vha->vp_flags);
+               set_bit(VP_DPC_NEEDED, &ha->dpc_flags);
+
+               wake_up_process(ha->dpc_thread);
+       }
+}
+
+/*
+ * qla24xx_modify_vp_config
+ *     Change VP configuration for vha
+ *
+ * Input:
+ *     vha = adapter block pointer.
+ *
+ * Returns:
+ *     qla2xxx local function return status code.
+ *
+ * Context:
+ *     Kernel context.
+ */
+int
+qla24xx_modify_vp_config(scsi_qla_host_t *vha)
+{
+       int             rval;
+       struct vp_config_entry_24xx *vpmod;
+       dma_addr_t      vpmod_dma;
+       scsi_qla_host_t *pha;
+
+       /* This can be called by the parent */
+       pha = to_qla_parent(vha);
+
+       vpmod = dma_pool_alloc(pha->s_dma_pool, GFP_KERNEL, &vpmod_dma);
+       if (!vpmod) {
+               DEBUG2_3(printk("%s(%ld): failed to allocate Modify VP "
+                   "IOCB.\n", __func__, pha->host_no));
+               return QLA_MEMORY_ALLOC_FAILED;
+       }
+
+       memset(vpmod, 0, sizeof(struct vp_config_entry_24xx));
+       vpmod->entry_type = VP_CONFIG_IOCB_TYPE;
+       vpmod->entry_count = 1;
+       vpmod->command = VCT_COMMAND_MOD_ENABLE_VPS;
+       vpmod->vp_count = 1;
+       vpmod->vp_index1 = vha->vp_idx;
+       vpmod->options_idx1 = BIT_3|BIT_4|BIT_5;
+       memcpy(vpmod->node_name_idx1, vha->node_name, WWN_SIZE);
+       memcpy(vpmod->port_name_idx1, vha->port_name, WWN_SIZE);
+       vpmod->entry_count = 1;
+
+       rval = qla2x00_issue_iocb(pha, vpmod, vpmod_dma, 0);
+       if (rval != QLA_SUCCESS) {
+               DEBUG2_3_11(printk("%s(%ld): failed to issue VP config IOCB"
+                       "(%x).\n", __func__, pha->host_no, rval));
+       } else if (vpmod->comp_status != 0) {
+               DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+                       "-- error status (%x).\n", __func__, pha->host_no,
+                       vpmod->comp_status));
+               rval = QLA_FUNCTION_FAILED;
+       } else if (vpmod->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
+               DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+                   "-- completion status (%x).\n", __func__, pha->host_no,
+                   le16_to_cpu(vpmod->comp_status)));
+               rval = QLA_FUNCTION_FAILED;
+       } else {
+               /* EMPTY */
+               DEBUG11(printk("%s(%ld): done.\n", __func__, pha->host_no));
+               fc_vport_set_state(vha->fc_vport, FC_VPORT_INITIALIZING);
+       }
+       dma_pool_free(pha->s_dma_pool, vpmod, vpmod_dma);
+
+       return rval;
+}
+
+/*
+ * qla24xx_control_vp
+ *     Enable a virtual port for given host
+ *
+ * Input:
+ *     ha = adapter block pointer.
+ *     vhba = virtual adapter (unused)
+ *     index = index number for enabled VP
+ *
+ * Returns:
+ *     qla2xxx local function return status code.
+ *
+ * Context:
+ *     Kernel context.
+ */
+int
+qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
+{
+       int             rval;
+       int             map, pos;
+       struct vp_ctrl_entry_24xx   *vce;
+       dma_addr_t      vce_dma;
+       scsi_qla_host_t *ha = vha->parent;
+       int     vp_index = vha->vp_idx;
+
+       DEBUG11(printk("%s(%ld): entered. Enabling index %d\n", __func__,
+           ha->host_no, vp_index));
+
+       if (vp_index == 0 || vp_index >= MAX_MULTI_ID_LOOP)
+               return QLA_PARAMETER_ERROR;
+
+       vce = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &vce_dma);
+       if (!vce) {
+               DEBUG2_3(printk("%s(%ld): "
+                   "failed to allocate VP Control IOCB.\n", __func__,
+                   ha->host_no));
+               return QLA_MEMORY_ALLOC_FAILED;
+       }
+       memset(vce, 0, sizeof(struct vp_ctrl_entry_24xx));
+
+       vce->entry_type = VP_CTRL_IOCB_TYPE;
+       vce->entry_count = 1;
+       vce->command = cpu_to_le16(cmd);
+       vce->vp_count = __constant_cpu_to_le16(1);
+
+       /* index map in firmware starts with 1; decrement index
+        * this is ok as we never use index 0
+        */
+       map = (vp_index - 1) / 8;
+       pos = (vp_index - 1) & 7;
+       down(&ha->vport_sem);
+       vce->vp_idx_map[map] |= 1 << pos;
+       up(&ha->vport_sem);
+
+       rval = qla2x00_issue_iocb(ha, vce, vce_dma, 0);
+       if (rval != QLA_SUCCESS) {
+               DEBUG2_3_11(printk("%s(%ld): failed to issue VP control IOCB"
+                   "(%x).\n", __func__, ha->host_no, rval));
+               printk("%s(%ld): failed to issue VP control IOCB"
+                   "(%x).\n", __func__, ha->host_no, rval);
+       } else if (vce->entry_status != 0) {
+               DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+                   "-- error status (%x).\n", __func__, ha->host_no,
+                   vce->entry_status));
+               printk("%s(%ld): failed to complete IOCB "
+                   "-- error status (%x).\n", __func__, ha->host_no,
+                   vce->entry_status);
+               rval = QLA_FUNCTION_FAILED;
+       } else if (vce->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
+               DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+                   "-- completion status (%x).\n", __func__, ha->host_no,
+                   le16_to_cpu(vce->comp_status)));
+               printk("%s(%ld): failed to complete IOCB "
+                   "-- completion status (%x).\n", __func__, ha->host_no,
+                   le16_to_cpu(vce->comp_status));
+               rval = QLA_FUNCTION_FAILED;
+       } else {
+               DEBUG2(printk("%s(%ld): done.\n", __func__, ha->host_no));
+       }
+
+       dma_pool_free(ha->s_dma_pool, vce, vce_dma);
+
+       return rval;
+}
+
+/*
+ * qla2x00_send_change_request
+ *     Receive or disable RSCN request from fabric controller
+ *
+ * Input:
+ *     ha = adapter block pointer
+ *     format = registration format:
+ *             0 - Reserved
+ *             1 - Fabric detected registration
+ *             2 - N_port detected registration
+ *             3 - Full registration
+ *             FF - clear registration
+ *     vp_idx = Virtual port index
+ *
+ * Returns:
+ *     qla2x00 local function return status code.
+ *
+ * Context:
+ *     Kernel Context
+ */
+
+int
+qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format,
+                           uint16_t vp_idx)
+{
+       int rval;
+       mbx_cmd_t mc;
+       mbx_cmd_t *mcp = &mc;
+
+       /*
+        * This command is implicitly executed by firmware during login for the
+        * physical hosts
+        */
+       if (vp_idx == 0)
+               return QLA_FUNCTION_FAILED;
+
+       mcp->mb[0] = MBC_SEND_CHANGE_REQUEST;
+       mcp->mb[1] = format;
+       mcp->mb[9] = vp_idx;
+       mcp->out_mb = MBX_9|MBX_1|MBX_0;
+       mcp->in_mb = MBX_0|MBX_1;
+       mcp->tov = MBX_TOV_SECONDS;
+       mcp->flags = 0;
+       rval = qla2x00_mailbox_command(ha, mcp);
+
+       if (rval == QLA_SUCCESS) {
+               if (mcp->mb[0] != MBS_COMMAND_COMPLETE) {
+                       rval = BIT_1;
+               }
+       } else
+               rval = BIT_1;
+
+       return rval;
+}
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
new file mode 100644 (file)
index 0000000..54dc415
--- /dev/null
@@ -0,0 +1,497 @@
+/*
+ *                  QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2005 QLogic Corporation
+ * (www.qlogic.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ */
+#include "qla_def.h"
+
+#include <linux/version.h>
+#include <linux/moduleparam.h>
+#include <linux/vmalloc.h>
+#include <linux/smp_lock.h>
+#include <linux/list.h>
+
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsicam.h>
+#include <linux/delay.h>
+
+void qla2x00_vp_stop_timer(scsi_qla_host_t *);
+
+void
+qla2x00_vp_stop_timer(scsi_qla_host_t *vha)
+{
+       if (vha->parent && vha->timer_active) {
+               del_timer_sync(&vha->timer);
+               vha->timer_active = 0;
+       }
+}
+
+uint32_t
+qla24xx_allocate_vp_id(scsi_qla_host_t *vha)
+{
+       uint32_t vp_id;
+       scsi_qla_host_t *ha = vha->parent;
+
+       /* Find an empty slot and assign an vp_id */
+       down(&ha->vport_sem);
+       vp_id = find_first_zero_bit((unsigned long *)ha->vp_idx_map,
+                               MAX_MULTI_ID_FABRIC);
+       if (vp_id > MAX_MULTI_ID_FABRIC) {
+               DEBUG15(printk ("vp_id %d is bigger than MAX_MULTI_ID_FABRID\n",
+                   vp_id));
+               up(&ha->vport_sem);
+               return vp_id;
+       }
+
+       set_bit(vp_id, (unsigned long *)ha->vp_idx_map);
+       ha->num_vhosts++;
+       vha->vp_idx = vp_id;
+       list_add_tail(&vha->vp_list, &ha->vp_list);
+       up(&ha->vport_sem);
+       return vp_id;
+}
+
+void
+qla24xx_deallocate_vp_id(scsi_qla_host_t *vha)
+{
+       uint16_t vp_id;
+       scsi_qla_host_t *ha = vha->parent;
+
+       down(&ha->vport_sem);
+       vp_id = vha->vp_idx;
+       ha->num_vhosts--;
+       clear_bit(vp_id, (unsigned long *)ha->vp_idx_map);
+       list_del(&vha->vp_list);
+       up(&ha->vport_sem);
+}
+
+scsi_qla_host_t *
+qla24xx_find_vhost_by_name(scsi_qla_host_t *ha, uint8_t *port_name)
+{
+       scsi_qla_host_t *vha;
+
+       /* Locate matching device in database. */
+       list_for_each_entry(vha, &ha->vp_list, vp_list) {
+               if (!memcmp(port_name, vha->port_name, WWN_SIZE))
+                       return vha;
+       }
+       return NULL;
+}
+
+/*
+ * qla2x00_mark_vp_devices_dead
+ *     Updates fcport state when device goes offline.
+ *
+ * Input:
+ *     ha = adapter block pointer.
+ *     fcport = port structure pointer.
+ *
+ * Return:
+ *     None.
+ *
+ * Context:
+ */
+void
+qla2x00_mark_vp_devices_dead(scsi_qla_host_t *vha)
+{
+       fc_port_t *fcport;
+       scsi_qla_host_t *pha = to_qla_parent(vha);
+
+       list_for_each_entry(fcport, &pha->fcports, list) {
+               if (fcport->vp_idx != vha->vp_idx)
+                       continue;
+
+               DEBUG15(printk("scsi(%ld): Marking port dead, "
+                   "loop_id=0x%04x :%x\n",
+                   vha->host_no, fcport->loop_id, fcport->vp_idx));
+
+               atomic_set(&fcport->state, FCS_DEVICE_DEAD);
+               qla2x00_mark_device_lost(vha, fcport, 0, 0);
+       }
+}
+
+int
+qla24xx_disable_vp(scsi_qla_host_t *vha)
+{
+       int ret;
+
+       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);
+
+       /* Delete all vp's fcports from parent's list */
+       qla2x00_mark_vp_devices_dead(vha);
+       atomic_set(&vha->vp_state, VP_FAILED);
+       vha->flags.management_server_logged_in = 0;
+       if (ret == QLA_SUCCESS) {
+               fc_vport_set_state(vha->fc_vport, FC_VPORT_DISABLED);
+       } else {
+               fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
+               return -1;
+       }
+       return 0;
+}
+
+int
+qla24xx_enable_vp(scsi_qla_host_t *vha)
+{
+       int ret;
+       scsi_qla_host_t *ha = vha->parent;
+
+       /* Check if physical ha port is Up */
+       if (atomic_read(&ha->loop_state) == LOOP_DOWN  ||
+               atomic_read(&ha->loop_state) == LOOP_DEAD ) {
+               vha->vp_err_state =  VP_ERR_PORTDWN;
+               fc_vport_set_state(vha->fc_vport, FC_VPORT_LINKDOWN);
+               goto enable_failed;
+       }
+
+       /* Initialize the new vport unless it is a persistent port */
+       down(&ha->vport_sem);
+       ret = qla24xx_modify_vp_config(vha);
+       up(&ha->vport_sem);
+
+       if (ret != QLA_SUCCESS) {
+               fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
+               goto enable_failed;
+       }
+
+       DEBUG15(qla_printk(KERN_INFO, ha,
+           "Virtual port with id: %d - Enabled\n", vha->vp_idx));
+       return 0;
+
+enable_failed:
+       DEBUG15(qla_printk(KERN_INFO, ha,
+           "Virtual port with id: %d - Disabled\n", vha->vp_idx));
+       return 1;
+}
+
+/**
+ * qla24xx_modify_vport() -  Modifies the virtual fabric port's configuration
+ * @ha: HA context
+ * @vp: pointer to buffer of virtual port parameters.
+ * @ret_code: return error code:
+ *
+ * Returns the virtual port id, or MAX_VSAN_ID, if couldn't create.
+ */
+uint32_t
+qla24xx_modify_vhba(scsi_qla_host_t *ha, vport_params_t *vp, uint32_t *vp_id)
+{
+       scsi_qla_host_t *vha;
+
+       vha = qla24xx_find_vhost_by_name(ha, vp->port_name);
+       if (!vha) {
+               *vp_id = MAX_NUM_VPORT_LOOP;
+               return VP_RET_CODE_WWPN;
+       }
+
+       if (qla24xx_enable_vp(vha)) {
+               scsi_host_put(vha->host);
+               qla2x00_mem_free(vha);
+               *vp_id = MAX_NUM_VPORT_LOOP;
+               return VP_RET_CODE_RESOURCES;
+       }
+
+       *vp_id = vha->vp_idx;
+       return VP_RET_CODE_OK;
+}
+
+void
+qla24xx_configure_vp(scsi_qla_host_t *vha)
+{
+       struct fc_vport *fc_vport;
+       int ret;
+
+       fc_vport = vha->fc_vport;
+
+       DEBUG15(printk("scsi(%ld): %s: change request #3 for this host.\n",
+           vha->host_no, __func__));
+       ret = qla2x00_send_change_request(vha, 0x3, vha->vp_idx);
+       if (ret != QLA_SUCCESS) {
+               DEBUG15(qla_printk(KERN_ERR, vha, "Failed to enable receiving"
+                   " of RSCN requests: 0x%x\n", ret));
+               return;
+       } else {
+               /* Corresponds to SCR enabled */
+               clear_bit(VP_SCR_NEEDED, &vha->vp_flags);
+       }
+
+       vha->flags.online = 1;
+       if (qla24xx_configure_vhba(vha))
+               return;
+
+       atomic_set(&vha->vp_state, VP_ACTIVE);
+       fc_vport_set_state(fc_vport, FC_VPORT_ACTIVE);
+}
+
+void
+qla2x00_alert_all_vps(scsi_qla_host_t *ha, uint16_t *mb)
+{
+       int i, vp_idx_matched;
+       scsi_qla_host_t *vha;
+
+       if (ha->parent)
+               return;
+
+       i = find_next_bit((unsigned long *)ha->vp_idx_map,
+           MAX_MULTI_ID_FABRIC + 1, 1);
+       for (;i <= MAX_MULTI_ID_FABRIC;
+           i = find_next_bit((unsigned long *)ha->vp_idx_map,
+           MAX_MULTI_ID_FABRIC + 1, i + 1)) {
+               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) {
+                       switch (mb[0]) {
+                       case MBA_LIP_OCCURRED:
+                       case MBA_LOOP_UP:
+                       case MBA_LOOP_DOWN:
+                       case MBA_LIP_RESET:
+                       case MBA_POINT_TO_POINT:
+                       case MBA_CHG_IN_CONNECTION:
+                       case MBA_PORT_UPDATE:
+                       case MBA_RSCN_UPDATE:
+                               DEBUG15(printk("scsi(%ld)%s: Async_event for"
+                                   " VP[%d], mb = 0x%x, vha=%p\n",
+                                   vha->host_no, __func__,i, *mb, vha));
+                               qla2x00_async_event(vha, mb);
+                               break;
+                       }
+               }
+       }
+}
+
+void
+qla2x00_vp_abort_isp(scsi_qla_host_t *vha)
+{
+       /*
+        * Physical port will do most of the abort and recovery work. We can
+        * just treat it as a loop down
+        */
+       if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+               atomic_set(&vha->loop_state, LOOP_DOWN);
+               qla2x00_mark_all_devices_lost(vha, 0);
+       } else {
+               if (!atomic_read(&vha->loop_down_timer))
+                       atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+       }
+
+       DEBUG15(printk("scsi(%ld): Scheduling enable of Vport %d...\n",
+           vha->host_no, vha->vp_idx));
+       qla24xx_enable_vp(vha);
+}
+
+int
+qla2x00_do_dpc_vp(scsi_qla_host_t *vha)
+{
+       if (test_and_clear_bit(VP_IDX_ACQUIRED, &vha->vp_flags)) {
+               /* VP acquired. complete port configuration */
+               qla24xx_configure_vp(vha);
+               return 0;
+       }
+
+       if (test_and_clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags))
+               qla2x00_vp_abort_isp(vha);
+
+       if (test_and_clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) &&
+           (!(test_and_set_bit(RESET_ACTIVE, &vha->dpc_flags)))) {
+               clear_bit(RESET_ACTIVE, &vha->dpc_flags);
+       }
+
+       if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
+               if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags))) {
+                       qla2x00_loop_resync(vha);
+                       clear_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags);
+               }
+       }
+
+       return 0;
+}
+
+void
+qla2x00_do_dpc_all_vps(scsi_qla_host_t *ha)
+{
+       int ret;
+       int i, vp_idx_matched;
+       scsi_qla_host_t *vha;
+
+       if (ha->parent)
+               return;
+       if (list_empty(&ha->vp_list))
+               return;
+
+       clear_bit(VP_DPC_NEEDED, &ha->dpc_flags);
+
+       i = find_next_bit((unsigned long *)ha->vp_idx_map,
+           MAX_MULTI_ID_FABRIC + 1, 1);
+       for (;i <= MAX_MULTI_ID_FABRIC;
+           i = find_next_bit((unsigned long *)ha->vp_idx_map,
+           MAX_MULTI_ID_FABRIC + 1, i + 1)) {
+               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)
+                       ret = qla2x00_do_dpc_vp(vha);
+       }
+}
+
+int
+qla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport)
+{
+       scsi_qla_host_t *ha = (scsi_qla_host_t *) fc_vport->shost->hostdata;
+       scsi_qla_host_t *vha;
+       uint8_t port_name[WWN_SIZE];
+
+       if (fc_vport->roles != FC_PORT_ROLE_FCP_INITIATOR)
+               return VPCERR_UNSUPPORTED;
+
+       /* Check up the F/W and H/W support NPIV */
+       if (!ha->flags.npiv_supported)
+               return VPCERR_UNSUPPORTED;
+
+       /* Check up whether npiv supported switch presented */
+       if (!(ha->switch_cap & FLOGI_MID_SUPPORT))
+               return VPCERR_NO_FABRIC_SUPP;
+
+       /* Check up unique WWPN */
+       u64_to_wwn(fc_vport->port_name, port_name);
+       vha = qla24xx_find_vhost_by_name(ha, port_name);
+       if (vha)
+               return VPCERR_BAD_WWN;
+
+       /* Check up max-npiv-supports */
+       if (ha->num_vhosts > ha->max_npiv_vports) {
+               DEBUG15(printk("scsi(%ld): num_vhosts %d is bigger than "
+                   "max_npv_vports %d.\n", ha->host_no,
+                   (uint16_t) ha->num_vhosts, (int) ha->max_npiv_vports));
+               return VPCERR_UNSUPPORTED;
+       }
+       return 0;
+}
+
+scsi_qla_host_t *
+qla24xx_create_vhost(struct fc_vport *fc_vport)
+{
+       scsi_qla_host_t *ha = (scsi_qla_host_t *) fc_vport->shost->hostdata;
+       scsi_qla_host_t *vha;
+       struct Scsi_Host *host;
+
+       host = scsi_host_alloc(&qla24xx_driver_template,
+           sizeof(scsi_qla_host_t));
+       if (!host) {
+               printk(KERN_WARNING
+                   "qla2xxx: scsi_host_alloc() failed for vport\n");
+               return(NULL);
+       }
+
+       vha = (scsi_qla_host_t *)host->hostdata;
+
+       /* clone the parent hba */
+       memcpy(vha, ha, sizeof (scsi_qla_host_t));
+
+       fc_vport->dd_data = vha;
+
+       vha->node_name = kmalloc(WWN_SIZE * sizeof(char), GFP_KERNEL);
+       if (!vha->node_name)
+               goto create_vhost_failed_1;
+
+       vha->port_name = kmalloc(WWN_SIZE * sizeof(char), GFP_KERNEL);
+       if (!vha->port_name)
+               goto create_vhost_failed_2;
+
+       /* New host info */
+       u64_to_wwn(fc_vport->node_name, vha->node_name);
+       u64_to_wwn(fc_vport->port_name, vha->port_name);
+
+       vha->host = host;
+       vha->host_no = host->host_no;
+       vha->parent = ha;
+       vha->fc_vport = fc_vport;
+       vha->device_flags = 0;
+       vha->instance = num_hosts;
+       vha->vp_idx = 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));
+               goto create_vhost_failed_3;
+       }
+       vha->mgmt_svr_loop_id = 10 + vha->vp_idx;
+
+       init_MUTEX(&vha->mbx_cmd_sem);
+       init_MUTEX_LOCKED(&vha->mbx_intr_sem);
+
+       INIT_LIST_HEAD(&vha->list);
+       INIT_LIST_HEAD(&vha->fcports);
+       INIT_LIST_HEAD(&vha->vp_fcports);
+
+       vha->dpc_flags = 0L;
+       set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
+       set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
+
+       /*
+        * To fix the issue of processing a parent's RSCN for the vport before
+        * its SCR is complete.
+        */
+       set_bit(VP_SCR_NEEDED, &vha->vp_flags);
+       atomic_set(&vha->loop_state, LOOP_DOWN);
+       atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+
+       qla2x00_start_timer(vha, qla2x00_timer, WATCH_INTERVAL);
+
+       host->can_queue = vha->request_q_length + 128;
+       host->this_id = 255;
+       host->cmd_per_lun = 3;
+       host->max_cmd_len = MAX_CMDSZ;
+       host->max_channel = MAX_BUSES - 1;
+       host->max_lun = MAX_LUNS;
+       host->unique_id = vha->instance;
+       host->max_id = MAX_TARGETS_2200;
+       host->transportt = qla2xxx_transport_vport_template;
+
+       DEBUG15(printk("DEBUG: detect vport hba %ld at address = %p\n",
+           vha->host_no, vha));
+
+       vha->flags.init_done = 1;
+       num_hosts++;
+
+       down(&ha->vport_sem);
+       set_bit(vha->vp_idx, (unsigned long *)ha->vp_idx_map);
+       ha->cur_vport_count++;
+       up(&ha->vport_sem);
+
+       return vha;
+
+create_vhost_failed_3:
+       kfree(vha->port_name);
+
+create_vhost_failed_2:
+       kfree(vha->node_name);
+
+create_vhost_failed_1:
+       return NULL;
+}
index 18baa5bf69c40af27c05e6debf4555c79d5c2613..a8658b1d284f687e0bcca8bc4f79fe0451cd371c 100644 (file)
@@ -29,8 +29,7 @@ static struct kmem_cache *srb_cachep;
 /*
  * Ioctl related information.
  */
-static int num_hosts;
-
+int num_hosts;
 int ql2xlogintimeout = 20;
 module_param(ql2xlogintimeout, int, S_IRUGO|S_IRUSR);
 MODULE_PARM_DESC(ql2xlogintimeout,
@@ -112,7 +111,7 @@ static int qla2x00_device_reset(scsi_qla_host_t *, fc_port_t *);
 static int qla2x00_change_queue_depth(struct scsi_device *, int);
 static int qla2x00_change_queue_type(struct scsi_device *, int);
 
-static struct scsi_host_template qla2x00_driver_template = {
+struct scsi_host_template qla2x00_driver_template = {
        .module                 = THIS_MODULE,
        .name                   = QLA2XXX_DRIVER_NAME,
        .queuecommand           = qla2x00_queuecommand,
@@ -143,7 +142,7 @@ static struct scsi_host_template qla2x00_driver_template = {
        .shost_attrs            = qla2x00_host_attrs,
 };
 
-static struct scsi_host_template qla24xx_driver_template = {
+struct scsi_host_template qla24xx_driver_template = {
        .module                 = THIS_MODULE,
        .name                   = QLA2XXX_DRIVER_NAME,
        .queuecommand           = qla24xx_queuecommand,
@@ -171,21 +170,21 @@ static struct scsi_host_template qla24xx_driver_template = {
 };
 
 static struct scsi_transport_template *qla2xxx_transport_template = NULL;
+struct scsi_transport_template *qla2xxx_transport_vport_template = NULL;
 
 /* TODO Convert to inlines
  *
  * Timer routines
  */
-#define        WATCH_INTERVAL          1       /* number of seconds */
 
-static void qla2x00_timer(scsi_qla_host_t *);
+void qla2x00_timer(scsi_qla_host_t *);
 
-static __inline__ void qla2x00_start_timer(scsi_qla_host_t *,
+__inline__ void qla2x00_start_timer(scsi_qla_host_t *,
     void *, unsigned long);
 static __inline__ void qla2x00_restart_timer(scsi_qla_host_t *, unsigned long);
-static __inline__ void qla2x00_stop_timer(scsi_qla_host_t *);
+__inline__ void qla2x00_stop_timer(scsi_qla_host_t *);
 
-static inline void
+__inline__ void
 qla2x00_start_timer(scsi_qla_host_t *ha, void *func, unsigned long interval)
 {
        init_timer(&ha->timer);
@@ -202,7 +201,7 @@ qla2x00_restart_timer(scsi_qla_host_t *ha, unsigned long interval)
        mod_timer(&ha->timer, jiffies + interval * HZ);
 }
 
-static __inline__ void
+__inline__ void
 qla2x00_stop_timer(scsi_qla_host_t *ha)
 {
        del_timer_sync(&ha->timer);
@@ -213,8 +212,8 @@ static int qla2x00_do_dpc(void *data);
 
 static void qla2x00_rst_aen(scsi_qla_host_t *);
 
-static uint8_t qla2x00_mem_alloc(scsi_qla_host_t *);
-static void qla2x00_mem_free(scsi_qla_host_t *ha);
+uint8_t qla2x00_mem_alloc(scsi_qla_host_t *);
+void qla2x00_mem_free(scsi_qla_host_t *ha);
 static int qla2x00_allocate_sp_pool( scsi_qla_host_t *ha);
 static void qla2x00_free_sp_pool(scsi_qla_host_t *ha);
 static void qla2x00_sp_free_dma(scsi_qla_host_t *, srb_t *);
@@ -438,6 +437,7 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
        struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device));
        srb_t *sp;
        int rval;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
        rval = fc_remote_port_chkready(rport);
        if (rval) {
@@ -453,7 +453,7 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
 
        if (atomic_read(&fcport->state) != FCS_ONLINE) {
                if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||
-                   atomic_read(&ha->loop_state) == LOOP_DEAD) {
+                   atomic_read(&pha->loop_state) == LOOP_DEAD) {
                        cmd->result = DID_NO_CONNECT << 16;
                        goto qc24_fail_command;
                }
@@ -462,7 +462,7 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
 
        spin_unlock_irq(ha->host->host_lock);
 
-       sp = qla2x00_get_new_sp(ha, fcport, cmd, done);
+       sp = qla2x00_get_new_sp(pha, fcport, cmd, done);
        if (!sp)
                goto qc24_host_busy_lock;
 
@@ -475,8 +475,8 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
        return 0;
 
 qc24_host_busy_free_sp:
-       qla2x00_sp_free_dma(ha, sp);
-       mempool_free(sp, ha->srb_mempool);
+       qla2x00_sp_free_dma(pha, sp);
+       mempool_free(sp, pha->srb_mempool);
 
 qc24_host_busy_lock:
        spin_lock_irq(ha->host->host_lock);
@@ -548,16 +548,17 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *ha)
 {
        int             return_status;
        unsigned long   wait_online;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
        wait_online = jiffies + (MAX_LOOP_TIMEOUT * HZ);
-       while (((test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) ||
-           test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags) ||
-           test_bit(ISP_ABORT_RETRY, &ha->dpc_flags) ||
-           ha->dpc_active) && time_before(jiffies, wait_online)) {
+       while (((test_bit(ISP_ABORT_NEEDED, &pha->dpc_flags)) ||
+           test_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags) ||
+           test_bit(ISP_ABORT_RETRY, &pha->dpc_flags) ||
+           pha->dpc_active) && time_before(jiffies, wait_online)) {
 
                msleep(1000);
        }
-       if (ha->flags.online)
+       if (pha->flags.online)
                return_status = QLA_SUCCESS;
        else
                return_status = QLA_FUNCTION_FAILED;
@@ -588,14 +589,15 @@ qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha)
 {
        int      return_status = QLA_SUCCESS;
        unsigned long loop_timeout ;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
        /* wait for 5 min at the max for loop to be ready */
        loop_timeout = jiffies + (MAX_LOOP_TIMEOUT * HZ);
 
-       while ((!atomic_read(&ha->loop_down_timer) &&
-           atomic_read(&ha->loop_state) == LOOP_DOWN) ||
-           atomic_read(&ha->loop_state) != LOOP_READY) {
-               if (atomic_read(&ha->loop_state) == LOOP_DEAD) {
+       while ((!atomic_read(&pha->loop_down_timer) &&
+           atomic_read(&pha->loop_state) == LOOP_DOWN) ||
+           atomic_read(&pha->loop_state) != LOOP_READY) {
+               if (atomic_read(&pha->loop_state) == LOOP_DEAD) {
                        return_status = QLA_FUNCTION_FAILED;
                        break;
                }
@@ -650,6 +652,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
        unsigned long serial;
        unsigned long flags;
        int wait = 0;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
        qla2x00_block_error_handler(cmd);
 
@@ -663,9 +666,9 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
        serial = cmd->serial_number;
 
        /* Check active list for command command. */
-       spin_lock_irqsave(&ha->hardware_lock, flags);
+       spin_lock_irqsave(&pha->hardware_lock, flags);
        for (i = 1; i < MAX_OUTSTANDING_COMMANDS; i++) {
-               sp = ha->outstanding_cmds[i];
+               sp = pha->outstanding_cmds[i];
 
                if (sp == NULL)
                        continue;
@@ -677,7 +680,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
                    __func__, ha->host_no, sp, serial));
                DEBUG3(qla2x00_print_scsi_cmd(cmd));
 
-               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+               spin_unlock_irqrestore(&pha->hardware_lock, flags);
                if (ha->isp_ops.abort_command(ha, sp)) {
                        DEBUG2(printk("%s(%ld): abort_command "
                            "mbx failed.\n", __func__, ha->host_no));
@@ -686,11 +689,11 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
                            "mbx success.\n", __func__, ha->host_no));
                        wait = 1;
                }
-               spin_lock_irqsave(&ha->hardware_lock, flags);
+               spin_lock_irqsave(&pha->hardware_lock, flags);
 
                break;
        }
-       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       spin_unlock_irqrestore(&pha->hardware_lock, flags);
 
        /* Wait for the command to be returned. */
        if (wait) {
@@ -731,6 +734,7 @@ qla2x00_eh_wait_for_pending_target_commands(scsi_qla_host_t *ha, unsigned int t)
        srb_t           *sp;
        struct scsi_cmnd *cmd;
        unsigned long flags;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
        status = 0;
 
@@ -739,19 +743,20 @@ qla2x00_eh_wait_for_pending_target_commands(scsi_qla_host_t *ha, unsigned int t)
         * array
         */
        for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
-               spin_lock_irqsave(&ha->hardware_lock, flags);
-               sp = ha->outstanding_cmds[cnt];
+               spin_lock_irqsave(&pha->hardware_lock, flags);
+               sp = pha->outstanding_cmds[cnt];
                if (sp) {
                        cmd = sp->cmd;
-                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
-                       if (cmd->device->id == t) {
+                       spin_unlock_irqrestore(&pha->hardware_lock, flags);
+                       if (cmd->device->id == t &&
+                           ha->vp_idx == sp->ha->vp_idx) {
                                if (!qla2x00_eh_wait_on_command(ha, cmd)) {
                                        status = 1;
                                        break;
                                }
                        }
                } else {
-                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+                       spin_unlock_irqrestore(&pha->hardware_lock, flags);
                }
        }
        return (status);
@@ -782,14 +787,12 @@ qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
 {
        scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
        fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
-       int ret;
+       int ret = FAILED;
        unsigned int id, lun;
        unsigned long serial;
 
        qla2x00_block_error_handler(cmd);
 
-       ret = FAILED;
-
        id = cmd->device->id;
        lun = cmd->device->lun;
        serial = cmd->serial_number;
@@ -912,15 +915,14 @@ static int
 qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
 {
        scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
+       scsi_qla_host_t *pha = to_qla_parent(ha);
        fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
-       int ret;
+       int ret = FAILED;
        unsigned int id, lun;
        unsigned long serial;
 
        qla2x00_block_error_handler(cmd);
 
-       ret = FAILED;
-
        id = cmd->device->id;
        lun = cmd->device->lun;
        serial = cmd->serial_number;
@@ -944,7 +946,7 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
                goto eh_bus_reset_done;
 
        /* Flush outstanding commands. */
-       if (!qla2x00_eh_wait_for_pending_commands(ha))
+       if (!qla2x00_eh_wait_for_pending_commands(pha))
                ret = FAILED;
 
 eh_bus_reset_done:
@@ -974,14 +976,13 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
 {
        scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
        fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
-       int ret;
+       int ret = FAILED;
        unsigned int id, lun;
        unsigned long serial;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
        qla2x00_block_error_handler(cmd);
 
-       ret = FAILED;
-
        id = cmd->device->id;
        lun = cmd->device->lun;
        serial = cmd->serial_number;
@@ -1004,21 +1005,24 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
         * while dpc is stuck for the mailbox to complete.
         */
        qla2x00_wait_for_loop_ready(ha);
-       set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
-       if (qla2x00_abort_isp(ha)) {
-               clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+       set_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags);
+       if (qla2x00_abort_isp(pha)) {
+               clear_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags);
                /* failed. schedule dpc to try */
-               set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+               set_bit(ISP_ABORT_NEEDED, &pha->dpc_flags);
 
                if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS)
                        goto eh_host_reset_lock;
        }
-       clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+       clear_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags);
 
        /* Waiting for our command in done_queue to be returned to OS.*/
-       if (qla2x00_eh_wait_for_pending_commands(ha))
+       if (qla2x00_eh_wait_for_pending_commands(pha))
                ret = SUCCESS;
 
+       if (ha->parent)
+               qla2x00_vp_abort_isp(ha);
+
 eh_host_reset_lock:
        qla_printk(KERN_INFO, ha, "%s: reset %s\n", __func__,
            (ret == FAILED) ? "failed" : "succeded");
@@ -1435,6 +1439,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        ha->host = host;
        ha->host_no = host->host_no;
        sprintf(ha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, ha->host_no);
+       ha->parent = NULL;
 
        /* Set ISP-type information. */
        qla2x00_set_isp_flags(ha);
@@ -1452,7 +1457,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 
        ha->prev_topology = 0;
        ha->init_cb_size = sizeof(init_cb_t);
-       ha->mgmt_svr_loop_id = MANAGEMENT_SERVER;
+       ha->mgmt_svr_loop_id = MANAGEMENT_SERVER + ha->vp_idx;
        ha->link_data_rate = PORT_SPEED_UNKNOWN;
        ha->optrom_size = OPTROM_SIZE_2300;
 
@@ -1524,8 +1529,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
                ha->request_q_length = REQUEST_ENTRY_CNT_24XX;
                ha->response_q_length = RESPONSE_ENTRY_CNT_2300;
                ha->last_loop_id = SNS_LAST_LOOP_ID_2300;
-               ha->init_cb_size = sizeof(struct init_cb_24xx);
-               ha->mgmt_svr_loop_id = 10;
+               ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
+               ha->mgmt_svr_loop_id = 10 + ha->vp_idx;
                ha->isp_ops.pci_config = qla24xx_pci_config;
                ha->isp_ops.reset_chip = qla24xx_reset_chip;
                ha->isp_ops.chip_diag = qla24xx_chip_diag;
@@ -1563,10 +1568,14 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        ha->instance = num_hosts;
 
        init_MUTEX(&ha->mbx_cmd_sem);
+       init_MUTEX(&ha->vport_sem);
        init_MUTEX_LOCKED(&ha->mbx_intr_sem);
 
        INIT_LIST_HEAD(&ha->list);
        INIT_LIST_HEAD(&ha->fcports);
+       INIT_LIST_HEAD(&ha->vp_list);
+
+       set_bit(0, (unsigned long *) ha->vp_idx_map);
 
        qla2x00_config_dma_addressing(ha);
        if (qla2x00_mem_alloc(ha)) {
@@ -1789,7 +1798,8 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *ha, fc_port_t *fcport,
 void qla2x00_mark_device_lost(scsi_qla_host_t *ha, fc_port_t *fcport,
     int do_login, int defer)
 {
-       if (atomic_read(&fcport->state) == FCS_ONLINE)
+       if (atomic_read(&fcport->state) == FCS_ONLINE &&
+           ha->vp_idx == fcport->vp_idx)
                qla2x00_schedule_rport_del(ha, fcport, defer);
 
        /*
@@ -1840,19 +1850,23 @@ void
 qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer)
 {
        fc_port_t *fcport;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
-       list_for_each_entry(fcport, &ha->fcports, list) {
-               if (fcport->port_type != FCT_TARGET)
+       list_for_each_entry(fcport, &pha->fcports, list) {
+               if (ha->vp_idx != 0 && ha->vp_idx != fcport->vp_idx)
                        continue;
-
                /*
                 * No point in marking the device as lost, if the device is
                 * already DEAD.
                 */
                if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD)
                        continue;
-               if (atomic_read(&fcport->state) == FCS_ONLINE)
-                       qla2x00_schedule_rport_del(ha, fcport, defer);
+               if (atomic_read(&fcport->state) == FCS_ONLINE) {
+                       if (defer)
+                               qla2x00_schedule_rport_del(ha, fcport, defer);
+                       else if (ha->vp_idx == fcport->vp_idx)
+                               qla2x00_schedule_rport_del(ha, fcport, defer);
+               }
                atomic_set(&fcport->state, FCS_DEVICE_LOST);
        }
 
@@ -1868,7 +1882,7 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer)
 *      0  = success.
 *      1  = failure.
 */
-static uint8_t
+uint8_t
 qla2x00_mem_alloc(scsi_qla_host_t *ha)
 {
        char    name[16];
@@ -1920,33 +1934,33 @@ qla2x00_mem_alloc(scsi_qla_host_t *ha)
                        continue;
                }
 
-               snprintf(name, sizeof(name), "%s_%ld", QLA2XXX_DRIVER_NAME,
-                   ha->host_no);
-               ha->s_dma_pool = dma_pool_create(name, &ha->pdev->dev,
-                   DMA_POOL_SIZE, 8, 0);
-               if (ha->s_dma_pool == NULL) {
+               /* get consistent memory allocated for init control block */
+               ha->init_cb = dma_alloc_coherent(&ha->pdev->dev,
+                   ha->init_cb_size, &ha->init_cb_dma, GFP_KERNEL);
+               if (ha->init_cb == NULL) {
                        qla_printk(KERN_WARNING, ha,
-                           "Memory Allocation failed - s_dma_pool\n");
+                           "Memory Allocation failed - init_cb\n");
 
                        qla2x00_mem_free(ha);
                        msleep(100);
 
                        continue;
                }
+               memset(ha->init_cb, 0, ha->init_cb_size);
 
-               /* get consistent memory allocated for init control block */
-               ha->init_cb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
-                   &ha->init_cb_dma);
-               if (ha->init_cb == NULL) {
+               snprintf(name, sizeof(name), "%s_%ld", QLA2XXX_DRIVER_NAME,
+                   ha->host_no);
+               ha->s_dma_pool = dma_pool_create(name, &ha->pdev->dev,
+                   DMA_POOL_SIZE, 8, 0);
+               if (ha->s_dma_pool == NULL) {
                        qla_printk(KERN_WARNING, ha,
-                           "Memory Allocation failed - init_cb\n");
+                           "Memory Allocation failed - s_dma_pool\n");
 
                        qla2x00_mem_free(ha);
                        msleep(100);
 
                        continue;
                }
-               memset(ha->init_cb, 0, ha->init_cb_size);
 
                if (qla2x00_allocate_sp_pool(ha)) {
                        qla_printk(KERN_WARNING, ha,
@@ -2052,7 +2066,7 @@ qla2x00_mem_alloc(scsi_qla_host_t *ha)
 * Input:
 *      ha = adapter block pointer.
 */
-static void
+void
 qla2x00_mem_free(scsi_qla_host_t *ha)
 {
        struct list_head        *fcpl, *fcptemp;
@@ -2088,12 +2102,13 @@ qla2x00_mem_free(scsi_qla_host_t *ha)
        if (ha->ms_iocb)
                dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
 
-       if (ha->init_cb)
-               dma_pool_free(ha->s_dma_pool, ha->init_cb, ha->init_cb_dma);
-
        if (ha->s_dma_pool)
                dma_pool_destroy(ha->s_dma_pool);
 
+       if (ha->init_cb)
+               dma_free_coherent(&ha->pdev->dev, ha->init_cb_size,
+                   ha->init_cb, ha->init_cb_dma);
+
        if (ha->gid_list)
                dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list,
                    ha->gid_list_dma);
@@ -2199,6 +2214,7 @@ qla2x00_free_sp_pool( scsi_qla_host_t *ha)
 static int
 qla2x00_do_dpc(void *data)
 {
+       int             rval;
        scsi_qla_host_t *ha;
        fc_port_t       *fcport;
        uint8_t         status;
@@ -2347,7 +2363,7 @@ qla2x00_do_dpc(void *data)
                        if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE,
                            &ha->dpc_flags))) {
 
-                               qla2x00_loop_resync(ha);
+                               rval = qla2x00_loop_resync(ha);
 
                                clear_bit(LOOP_RESYNC_ACTIVE, &ha->dpc_flags);
                        }
@@ -2374,6 +2390,8 @@ qla2x00_do_dpc(void *data)
                if (test_and_clear_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags))
                        ha->isp_ops.beacon_blink(ha);
 
+               qla2x00_do_dpc_all_vps(ha);
+
                ha->dpc_active = 0;
        } /* End of while(1) */
 
@@ -2452,7 +2470,7 @@ qla2x00_sp_compl(scsi_qla_host_t *ha, srb_t *sp)
 *
 * Context: Interrupt
 ***************************************************************************/
-static void
+void
 qla2x00_timer(scsi_qla_host_t *ha)
 {
        unsigned long   cpu_flags = 0;
@@ -2461,6 +2479,7 @@ qla2x00_timer(scsi_qla_host_t *ha)
        int             index;
        srb_t           *sp;
        int             t;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
        /*
         * Ports - Port down timer.
@@ -2506,23 +2525,29 @@ qla2x00_timer(scsi_qla_host_t *ha)
                                atomic_set(&ha->loop_state, LOOP_DEAD);
 
                        /* Schedule an ISP abort to return any tape commands. */
-                       spin_lock_irqsave(&ha->hardware_lock, cpu_flags);
-                       for (index = 1; index < MAX_OUTSTANDING_COMMANDS;
-                           index++) {
-                               fc_port_t *sfcp;
+                       /* NPIV - scan physical port only */
+                       if (!ha->parent) {
+                               spin_lock_irqsave(&ha->hardware_lock,
+                                   cpu_flags);
+                               for (index = 1;
+                                   index < MAX_OUTSTANDING_COMMANDS;
+                                   index++) {
+                                       fc_port_t *sfcp;
+
+                                       sp = ha->outstanding_cmds[index];
+                                       if (!sp)
+                                               continue;
+                                       sfcp = sp->fcport;
+                                       if (!(sfcp->flags & FCF_TAPE_PRESENT))
+                                               continue;
 
-                               sp = ha->outstanding_cmds[index];
-                               if (!sp)
-                                       continue;
-                               sfcp = sp->fcport;
-                               if (!(sfcp->flags & FCF_TAPE_PRESENT))
-                                       continue;
-
-                               set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
-                               break;
+                                       set_bit(ISP_ABORT_NEEDED,
+                                           &ha->dpc_flags);
+                                       break;
+                               }
+                               spin_unlock_irqrestore(&ha->hardware_lock,
+                                   cpu_flags);
                        }
-                       spin_unlock_irqrestore(&ha->hardware_lock, cpu_flags);
-
                        set_bit(ABORT_QUEUES_NEEDED, &ha->dpc_flags);
                        start_dpc++;
                }
@@ -2566,8 +2591,9 @@ qla2x00_timer(scsi_qla_host_t *ha)
            test_bit(LOGIN_RETRY_NEEDED, &ha->dpc_flags) ||
            test_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) ||
            test_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags) ||
+           test_bit(VP_DPC_NEEDED, &ha->dpc_flags) ||
            test_bit(RELOGIN_NEEDED, &ha->dpc_flags)))
-               qla2xxx_wake_dpc(ha);
+               qla2xxx_wake_dpc(pha);
 
        qla2x00_restart_timer(ha, WATCH_INTERVAL);
 }
@@ -2711,14 +2737,24 @@ qla2x00_module_init(void)
 
        qla2xxx_transport_template =
            fc_attach_transport(&qla2xxx_transport_functions);
-       if (!qla2xxx_transport_template)
+       if (!qla2xxx_transport_template) {
+               kmem_cache_destroy(srb_cachep);
+               return -ENODEV;
+       }
+       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;
+       }
 
        printk(KERN_INFO "QLogic Fibre Channel HBA Driver\n");
        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);
        }
        return ret;
 }
@@ -2733,6 +2769,7 @@ qla2x00_module_exit(void)
        qla2x00_release_firmware();
        kmem_cache_destroy(srb_cachep);
        fc_release_transport(qla2xxx_transport_template);
+       fc_release_transport(qla2xxx_transport_vport_template);
 }
 
 module_init(qla2x00_module_init);