--- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_fcp.c 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_fcp.c 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,2470 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_fcp.c 1.466.1.3 2005/06/21 15:48:55EDT sf_support Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_fcp.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" +#include "lpfc_version.h" +#include "lpfc_crtn.h" +#include "lpfc_compat.h" + +static char *lpfc_drvr_name = LPFC_DRIVER_NAME; + +static struct scsi_transport_template *lpfc_transport_template = NULL; + +struct list_head lpfc_hba_list = LIST_HEAD_INIT(lpfc_hba_list); +EXPORT_SYMBOL(lpfc_hba_list); + +static const char * +lpfc_info(struct Scsi_Host *host) +{ + struct lpfc_hba *phba = (struct lpfc_hba *) host->hostdata[0]; + int len; + static char lpfcinfobuf[384]; + + memset(lpfcinfobuf,0,384); + if (phba && phba->pcidev){ + strncpy(lpfcinfobuf, phba->ModelDesc, 256); + len = strlen(lpfcinfobuf); + snprintf(lpfcinfobuf + len, + 384-len, + " on PCI bus %02x device %02x irq %d", + phba->pcidev->bus->number, + phba->pcidev->devfn, + phba->pcidev->irq); + len = strlen(lpfcinfobuf); + if (phba->Port[0]) { + snprintf(lpfcinfobuf + len, + 384-len, + " port %s", + phba->Port); + } + } + return lpfcinfobuf; +} + +static void +lpfc_jedec_to_ascii(int incr, char hdw[]) +{ + int i, j; + for (i = 0; i < 8; i++) { + j = (incr & 0xf); + if (j <= 9) + hdw[7 - i] = 0x30 + j; + else + hdw[7 - i] = 0x61 + j - 10; + incr = (incr >> 4); + } + hdw[8] = 0; + return; +} + +static ssize_t +lpfc_drvr_version_show(struct class_device *cdev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, LPFC_MODULE_DESC "\n"); +} + +static ssize_t +management_version_show(struct class_device *cdev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, DFC_API_VERSION "\n"); +} + +static ssize_t +lpfc_info_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + return snprintf(buf, PAGE_SIZE, "%s\n",lpfc_info(host)); +} + +static ssize_t +lpfc_serialnum_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + return snprintf(buf, PAGE_SIZE, "%s\n",phba->SerialNumber); +} + +static ssize_t +lpfc_modeldesc_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + return snprintf(buf, PAGE_SIZE, "%s\n",phba->ModelDesc); +} + +static ssize_t +lpfc_modelname_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + return snprintf(buf, PAGE_SIZE, "%s\n",phba->ModelName); +} + +static ssize_t +lpfc_programtype_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + return snprintf(buf, PAGE_SIZE, "%s\n",phba->ProgramType); +} + +static ssize_t +lpfc_portnum_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + return snprintf(buf, PAGE_SIZE, "%s\n",phba->Port); +} + +static ssize_t +lpfc_fwrev_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + char fwrev[32]; + lpfc_decode_firmware_rev(phba, fwrev, 1); + return snprintf(buf, PAGE_SIZE, "%s\n",fwrev); +} + +static ssize_t +lpfc_hdw_show(struct class_device *cdev, char *buf) +{ + char hdw[9]; + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + lpfc_vpd_t *vp = &phba->vpd; + lpfc_jedec_to_ascii(vp->rev.biuRev, hdw); + return snprintf(buf, PAGE_SIZE, "%s\n", hdw); +} +static ssize_t +lpfc_option_rom_version_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + return snprintf(buf, PAGE_SIZE, "%s\n", phba->OptionROMVersion); +} +static ssize_t +lpfc_state_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + int len = 0; + switch (phba->hba_state) { + case LPFC_INIT_START: + case LPFC_INIT_MBX_CMDS: + case LPFC_LINK_DOWN: + len += snprintf(buf + len, PAGE_SIZE-len, "Link Down\n"); + break; + case LPFC_LINK_UP: + case LPFC_LOCAL_CFG_LINK: + len += snprintf(buf + len, PAGE_SIZE-len, "Link Up\n"); + break; + case LPFC_FLOGI: + case LPFC_FABRIC_CFG_LINK: + case LPFC_NS_REG: + case LPFC_NS_QRY: + case LPFC_BUILD_DISC_LIST: + case LPFC_DISC_AUTH: + case LPFC_CLEAR_LA: + len += snprintf(buf + len, PAGE_SIZE-len, + "Link Up - Discovery\n"); + break; + case LPFC_HBA_READY: + len += snprintf(buf + len, PAGE_SIZE-len, + "Link Up - Ready:\n"); + if (phba->fc_topology == TOPOLOGY_LOOP) { + if (phba->fc_flag & FC_PUBLIC_LOOP) + len += snprintf(buf + len, PAGE_SIZE-len, + " Public Loop\n"); + else + len += snprintf(buf + len, PAGE_SIZE-len, + " Private Loop\n"); + } else { + if (phba->fc_flag & FC_FABRIC) + len += snprintf(buf + len, PAGE_SIZE-len, + " Fabric\n"); + else + len += snprintf(buf + len, PAGE_SIZE-len, + " Point-2-Point\n"); + } + } + return len; +} + +static ssize_t +lpfc_num_discovered_ports_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + return snprintf(buf, PAGE_SIZE, "%d\n", phba->fc_map_cnt + + phba->fc_unmap_cnt); +} + +/* + * These are replaced by Generic FC transport attributes + */ +static ssize_t +lpfc_speed_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + int len = 0; + if (phba->fc_linkspeed == LA_4GHZ_LINK) + len += snprintf(buf + len, PAGE_SIZE-len, "4 Gigabit\n"); + else + if (phba->fc_linkspeed == LA_2GHZ_LINK) + len += snprintf(buf + len, PAGE_SIZE-len, "2 Gigabit\n"); + else + len += snprintf(buf + len, PAGE_SIZE-len, "1 Gigabit\n"); + return len; +} + +static ssize_t +lpfc_node_name_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + uint64_t node_name = 0; + memcpy (&node_name, &phba->fc_nodename, sizeof (struct lpfc_name)); + return snprintf(buf, PAGE_SIZE, "0x%llx\n", + (unsigned long long) be64_to_cpu(node_name)); +} +static ssize_t +lpfc_port_name_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + uint64_t port_name = 0; + memcpy (&port_name, &phba->fc_portname, sizeof (struct lpfc_name)); + return snprintf(buf, PAGE_SIZE, "0x%llx\n", + (unsigned long long) be64_to_cpu(port_name)); +} +static ssize_t +lpfc_did_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + return snprintf(buf, PAGE_SIZE, "0x%x\n", phba->fc_myDID); +} + +static ssize_t +lpfc_port_type_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + + size_t retval = -EPERM; + + if (phba->fc_topology == TOPOLOGY_LOOP) { + if (phba->fc_flag & FC_PUBLIC_LOOP) + retval = snprintf(buf, PAGE_SIZE, "NL_Port\n"); + else + retval = snprintf(buf, PAGE_SIZE, "L_Port\n"); + } else { + if (phba->fc_flag & FC_FABRIC) + retval = snprintf(buf, PAGE_SIZE, "N_Port\n"); + else + retval = snprintf(buf, PAGE_SIZE, + "Point-to-Point N_Port\n"); + } + + return retval; +} + +static ssize_t +lpfc_fabric_name_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + uint64_t node_name = 0; + memcpy (&node_name, &phba->fc_nodename, sizeof (struct lpfc_name)); + + if ((phba->fc_flag & FC_FABRIC) || + ((phba->fc_topology == TOPOLOGY_LOOP) && + (phba->fc_flag & FC_PUBLIC_LOOP))) { + memcpy(&node_name, + & phba->fc_fabparam.nodeName, + sizeof (struct lpfc_name)); + } + + return snprintf(buf, PAGE_SIZE, "0x%08llx\n", + (unsigned long long) be64_to_cpu(node_name)); +} + +static ssize_t +lpfc_events_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + int i = 0, len = 0, get = phba->hba_event_put; + struct lpfc_hba_event *rec; + + if (get == phba->hba_event_get) + return snprintf(buf, PAGE_SIZE, "None\n"); + + for (i = 0; i < MAX_HBAEVT; i++) { + if (get == 0) + get = MAX_HBAEVT; + get--; + rec = &phba->hbaevt[get]; + switch (rec->fc_eventcode) { + case 0: + len += snprintf(buf+len, PAGE_SIZE-len, + "---------"); + break; + case HBA_EVENT_RSCN: + len += snprintf(buf+len, PAGE_SIZE-len, + "RSCN "); + break; + case HBA_EVENT_LINK_UP: + len += snprintf(buf+len, PAGE_SIZE-len, + "LINK UP "); + break; + case HBA_EVENT_LINK_DOWN: + len += snprintf(buf+len, PAGE_SIZE-len, + "LINK DOWN"); + break; + default: + len += snprintf(buf+len, PAGE_SIZE-len, + "?????????"); + break; + + } + len += snprintf(buf+len, PAGE_SIZE-len, " %d,%d,%d,%d\n", + rec->fc_evdata1, rec->fc_evdata2, + rec->fc_evdata3, rec->fc_evdata4); + } + return len; +} + +static ssize_t +lpfc_issue_lip (struct class_device *cdev, const char *buf, size_t count) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba *) host->hostdata[0]; + int val = 0; + LPFC_MBOXQ_t *pmboxq; + int mbxstatus = MBXERR_ERROR; + + if ((sscanf(buf, "%d", &val) != 1) || + (val != 1)) + return -EINVAL; + + if ((phba->fc_flag & FC_OFFLINE_MODE) || + (phba->hba_state != LPFC_HBA_READY)) + return -EPERM; + + pmboxq = mempool_alloc(phba->mbox_mem_pool,GFP_KERNEL); + + if (!pmboxq) + return -ENOMEM; + + memset((void *)pmboxq, 0, sizeof (LPFC_MBOXQ_t)); + lpfc_init_link(phba, pmboxq, phba->cfg_topology, phba->cfg_link_speed); + mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2); + + if (mbxstatus == MBX_TIMEOUT) + pmboxq->mbox_cmpl = lpfc_sli_def_mbox_cmpl; + else + mempool_free( pmboxq, phba->mbox_mem_pool); + + if (mbxstatus == MBXERR_ERROR) + return -EIO; + + return strlen(buf); +} + +static ssize_t +lpfc_nport_evt_cnt_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + return snprintf(buf, PAGE_SIZE, "%d\n", phba->nport_event_cnt); +} + +static ssize_t +lpfc_board_online_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + + if (!phba) return -EPERM; + + if (phba->fc_flag & FC_OFFLINE_MODE) + return snprintf(buf, PAGE_SIZE, "0\n"); + else + return snprintf(buf, PAGE_SIZE, "1\n"); +} + +static ssize_t +lpfc_board_online_store(struct class_device *cdev, const char *buf, + size_t count) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + int val=0; + + if (!phba) return -EPERM; + + if (sscanf(buf, "%d", &val) != 1) + return -EINVAL; + + if (val && (phba->fc_flag & FC_OFFLINE_MODE)) { + lpfc_online(phba); + } + else if (!val && !(phba->fc_flag & FC_OFFLINE_MODE)) { + lpfc_offline(phba); + } + + return strlen(buf); +} + +static int +lpfc_disc_ndlp_show(struct lpfc_hba * phba, struct lpfc_nodelist *ndlp, + char *buf, int offset) +{ + int len = 0, pgsz = PAGE_SIZE; + uint8_t name[sizeof (struct lpfc_name)]; + + buf += offset; + pgsz -= offset; + len += snprintf(buf + len, pgsz -len, + "DID %06x WWPN ", ndlp->nlp_DID); + + /* A Fibre Channel node or port name is 8 octets + * long and delimited by colons. + */ + memcpy (&name[0], &ndlp->nlp_portname, + sizeof (struct lpfc_name)); + len += snprintf(buf + len, pgsz-len, + "%02x:%02x:%02x:%02x:%02x:%02x:" + "%02x:%02x", + name[0], name[1], name[2], + name[3], name[4], name[5], + name[6], name[7]); + + len += snprintf(buf + len, pgsz-len, + " WWNN "); + memcpy (&name[0], &ndlp->nlp_nodename, + sizeof (struct lpfc_name)); + len += snprintf(buf + len, pgsz-len, + "%02x:%02x:%02x:%02x:%02x:%02x:" + "%02x:%02x\n", + name[0], name[1], name[2], + name[3], name[4], name[5], + name[6], name[7]); + len += snprintf(buf + len, pgsz-len, + " INFO %02x:%08x:%02x:%02x:%02x:%02x:" + "%02x:%02x:%02x\n", + ndlp->nlp_state, ndlp->nlp_flag, ndlp->nlp_type, + ndlp->nlp_rpi, ndlp->nlp_sid, ndlp->nlp_failMask, + ndlp->nlp_retry, ndlp->nlp_disc_refcnt, + ndlp->nlp_fcp_info); + return len; +} + +#define LPFC_MAX_SYS_DISC_ENTRIES 35 + +static ssize_t +lpfc_disc_npr_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + unsigned long iflag; + int i = 0, len = 0; + + if (!phba) return -EPERM; + + spin_lock_irqsave(phba->host->host_lock, iflag); + listp = &phba->fc_npr_list; + if (list_empty(listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return snprintf(buf, PAGE_SIZE, "NPR list: Empty\n"); + } + + len += snprintf(buf+len, PAGE_SIZE-len, "NPR list: %d Entries\n", + phba->fc_npr_cnt); + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + i++; + if(i > LPFC_MAX_SYS_DISC_ENTRIES) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed %d entries - sysfs %ld limit exceeded\n", + (phba->fc_npr_cnt - i + 1), PAGE_SIZE); + break; + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + len += lpfc_disc_ndlp_show(phba, ndlp, buf, len); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +static ssize_t +lpfc_disc_map_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + unsigned long iflag; + int i = 0, len = 0; + + if (!phba) return -EPERM; + + spin_lock_irqsave(phba->host->host_lock, iflag); + listp = &phba->fc_nlpmap_list; + if (list_empty(listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return snprintf(buf, PAGE_SIZE, "Map list: Empty\n"); + } + + len += snprintf(buf+len, PAGE_SIZE-len, "Map list: %d Entries\n", + phba->fc_map_cnt); + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + i++; + if(i > LPFC_MAX_SYS_DISC_ENTRIES) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed %d entries - sysfs %ld limit exceeded\n", + (phba->fc_map_cnt - i + 1), PAGE_SIZE); + break; + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + len += lpfc_disc_ndlp_show(phba, ndlp, buf, len); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +static ssize_t +lpfc_disc_unmap_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + unsigned long iflag; + int i = 0, len = 0; + + if (!phba) return -EPERM; + + spin_lock_irqsave(phba->host->host_lock, iflag); + listp = &phba->fc_nlpunmap_list; + if (list_empty(listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return snprintf(buf, PAGE_SIZE, "Unmap list: Empty\n"); + } + + len += snprintf(buf+len, PAGE_SIZE-len, "Unmap list: %d Entries\n", + phba->fc_unmap_cnt); + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + i++; + if(i > LPFC_MAX_SYS_DISC_ENTRIES) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed %d entries - sysfs %ld limit exceeded\n", + (phba->fc_unmap_cnt - i + 1), PAGE_SIZE); + break; + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + len += lpfc_disc_ndlp_show(phba, ndlp, buf, len); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +static ssize_t +lpfc_disc_prli_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + unsigned long iflag; + int i = 0, len = 0; + + if (!phba) return -EPERM; + + spin_lock_irqsave(phba->host->host_lock, iflag); + listp = &phba->fc_prli_list; + if (list_empty(listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return snprintf(buf, PAGE_SIZE, "PRLI list: Empty\n"); + } + + len += snprintf(buf+len, PAGE_SIZE-len, "PRLI list: %d Entries\n", + phba->fc_prli_cnt); + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + i++; + if(i > LPFC_MAX_SYS_DISC_ENTRIES) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed %d entries - sysfs %ld limit exceeded\n", + (phba->fc_prli_cnt - i + 1), PAGE_SIZE); + break; + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + len += lpfc_disc_ndlp_show(phba, ndlp, buf, len); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +static ssize_t +lpfc_disc_reglgn_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + unsigned long iflag; + int i = 0, len = 0; + + if (!phba) return -EPERM; + + spin_lock_irqsave(phba->host->host_lock, iflag); + listp = &phba->fc_reglogin_list; + if (list_empty(listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return snprintf(buf, PAGE_SIZE, "RegLgn list: Empty\n"); + } + + len += snprintf(buf+len, PAGE_SIZE-len, "RegLgn list: %d Entries\n", + phba->fc_reglogin_cnt); + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + i++; + if(i > LPFC_MAX_SYS_DISC_ENTRIES) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed %d entries - sysfs %ld limit exceeded\n", + (phba->fc_reglogin_cnt - i + 1), PAGE_SIZE); + break; + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + len += lpfc_disc_ndlp_show(phba, ndlp, buf, len); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +static ssize_t +lpfc_disc_adisc_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + unsigned long iflag; + int i = 0, len = 0; + + if (!phba) return -EPERM; + + spin_lock_irqsave(phba->host->host_lock, iflag); + listp = &phba->fc_adisc_list; + if (list_empty(listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return snprintf(buf, PAGE_SIZE, "ADISC list: Empty\n"); + } + + len += snprintf(buf+len, PAGE_SIZE-len, "ADISC list: %d Entries\n", + phba->fc_adisc_cnt); + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + i++; + if(i > LPFC_MAX_SYS_DISC_ENTRIES) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed %d entries - sysfs %ld limit exceeded\n", + (phba->fc_adisc_cnt - i + 1), PAGE_SIZE); + break; + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + len += lpfc_disc_ndlp_show(phba, ndlp, buf, len); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +static ssize_t +lpfc_disc_plogi_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + unsigned long iflag; + int i = 0, len = 0; + + if (!phba) return -EPERM; + + spin_lock_irqsave(phba->host->host_lock, iflag); + listp = &phba->fc_plogi_list; + if (list_empty(listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return snprintf(buf, PAGE_SIZE, "PLOGI list: Empty\n"); + } + + len += snprintf(buf+len, PAGE_SIZE-len, "PLOGI list: %d Entries\n", + phba->fc_plogi_cnt); + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + i++; + if(i > LPFC_MAX_SYS_DISC_ENTRIES) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed %d entries - sysfs %ld limit exceeded\n", + (phba->fc_plogi_cnt - i + 1), PAGE_SIZE); + break; + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + len += lpfc_disc_ndlp_show(phba, ndlp, buf, len); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +static ssize_t +lpfc_disc_unused_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + unsigned long iflag; + int i = 0, len = 0; + + if (!phba) return -EPERM; + + spin_lock_irqsave(phba->host->host_lock, iflag); + listp = &phba->fc_unused_list; + if (list_empty(listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return snprintf(buf, PAGE_SIZE, "Unused list: Empty\n"); + } + + len += snprintf(buf+len, PAGE_SIZE-len, "Unused list: %d Entries\n", + phba->fc_unused_cnt); + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + i++; + if(i > LPFC_MAX_SYS_DISC_ENTRIES) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed %d entries - sysfs %ld limit exceeded\n", + (phba->fc_unused_cnt - i + 1), PAGE_SIZE); + break; + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + len += lpfc_disc_ndlp_show(phba, ndlp, buf, len); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +#define LPFC_MAX_SYS_OUTFCPIO_ENTRIES 50 + +static ssize_t +lpfc_outfcpio_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_target *targetp; + struct lpfc_nodelist *ndlp; + struct lpfc_scsi_buf *lpfc_cmd; + struct list_head *curr, *next; + struct lpfc_iocbq *iocb; + struct lpfc_iocbq *next_iocb; + IOCB_t *cmd; + unsigned long iflag; + int i = 0, len = 0; + int cnt = 0, unused = 0, total = 0; + int tx_count, txcmpl_count; + + if (!phba) return -EPERM; + psli = &phba->sli; + pring = &psli->ring[psli->fcp_ring]; + + + spin_lock_irqsave(phba->host->host_lock, iflag); + + for(i=0;idevice_queue_hash[i]; + if(targetp) { + if(cnt >= LPFC_MAX_SYS_OUTFCPIO_ENTRIES) { + unused++; + continue; + } + cnt++; + len += snprintf(buf+len, PAGE_SIZE-len, + "ID %03d:qcmd %08x done %08x err %08x " + "slv %03x ", targetp->scsi_id, targetp->qcmdcnt, + targetp->iodonecnt, targetp->errorcnt, + targetp->slavecnt); + total += (targetp->qcmdcnt - targetp->iodonecnt); + + tx_count = 0; + txcmpl_count = 0; + + /* Count I/Os on txq and txcmplq. */ + list_for_each_safe(curr, next, &pring->txq) { + next_iocb = list_entry(curr, struct lpfc_iocbq, + list); + iocb = next_iocb; + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = + (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) + || (lpfc_cmd->target->scsi_id != + targetp->scsi_id)) { + continue; + } + tx_count++; + } + + /* Next check the txcmplq */ + list_for_each_safe(curr, next, &pring->txcmplq) { + next_iocb = list_entry(curr, struct lpfc_iocbq, + list); + iocb = next_iocb; + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = + (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) + || (lpfc_cmd->target->scsi_id != + targetp->scsi_id)) { + continue; + } + + txcmpl_count++; + } + len += snprintf(buf+len, PAGE_SIZE-len, + "tx %04x txc %04x ", + tx_count, txcmpl_count); + + ndlp = targetp->pnode; + if(ndlp == NULL) { + len += snprintf(buf+len, PAGE_SIZE-len, + "DISAPPERED\n"); + } + else { + if(ndlp->nlp_state == NLP_STE_MAPPED_NODE) { + len += snprintf(buf+len, PAGE_SIZE-len, + "MAPPED\n"); + } + else { + len += snprintf(buf+len, PAGE_SIZE-len, + "RECOVERY (%d)\n", + ndlp->nlp_state); + } + } + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + } + if(unused) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed x%x entries - sysfs %ld limit exceeded\n", + unused, PAGE_SIZE); + } + len += snprintf(buf+len, PAGE_SIZE-len, + "x%x total I/Os outstanding\n", total); + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +#define lpfc_param_show(attr) \ +static ssize_t \ +lpfc_##attr##_show(struct class_device *cdev, char *buf) \ +{ \ + struct Scsi_Host *host = class_to_shost(cdev);\ + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];\ + int val = 0;\ + if (phba){\ + val = phba->cfg_##attr;\ + return snprintf(buf, PAGE_SIZE, "%d\n",\ + phba->cfg_##attr);\ + }\ + return -EPERM;\ +} + +#define lpfc_param_set(attr, default, minval, maxval) \ +static int \ +lpfc_##attr##_set(struct lpfc_hba *phba, int val) \ +{ \ + if (val >= minval && val <= maxval) {\ + phba->cfg_##attr = val;\ + return 0;\ + }\ + phba->cfg_##attr = default;\ + return -EINVAL;\ +} + +#define lpfc_param_store(attr) \ +static ssize_t \ +lpfc_##attr##_store(struct class_device *cdev, const char *buf, size_t count) \ +{ \ + struct Scsi_Host *host = class_to_shost(cdev);\ + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];\ + int val=0;\ + if (sscanf(buf, "%d", &val) != 1)\ + return -EPERM;\ + if (phba){\ + if (lpfc_##attr##_set(phba, val) == 0) \ + return strlen(buf);\ + }\ + return -EINVAL;\ +} + +#define LPFC_ATTR(name, defval, minval, maxval, desc) \ +static int lpfc_##name = defval;\ +module_param(lpfc_##name, int, 0);\ +MODULE_PARM_DESC(lpfc_##name, desc);\ +lpfc_param_set(name, defval, minval, maxval)\ + + +#define LPFC_ATTR_R(name, defval, minval, maxval, desc) \ +static int lpfc_##name = defval;\ +module_param(lpfc_##name, int, 0);\ +MODULE_PARM_DESC(lpfc_##name, desc);\ +lpfc_param_show(name)\ +lpfc_param_set(name, defval, minval, maxval)\ +static CLASS_DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL) + +#define LPFC_ATTR_RW(name, defval, minval, maxval, desc) \ +static int lpfc_##name = defval;\ +module_param(lpfc_##name, int, 0);\ +MODULE_PARM_DESC(lpfc_##name, desc);\ +lpfc_param_show(name)\ +lpfc_param_set(name, defval, minval, maxval)\ +lpfc_param_store(name)\ +static CLASS_DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\ + lpfc_##name##_show, lpfc_##name##_store) + +static CLASS_DEVICE_ATTR(info, S_IRUGO, lpfc_info_show, NULL); +static CLASS_DEVICE_ATTR(serialnum, S_IRUGO, lpfc_serialnum_show, NULL); +static CLASS_DEVICE_ATTR(modeldesc, S_IRUGO, lpfc_modeldesc_show, NULL); +static CLASS_DEVICE_ATTR(modelname, S_IRUGO, lpfc_modelname_show, NULL); +static CLASS_DEVICE_ATTR(programtype, S_IRUGO, lpfc_programtype_show, NULL); +static CLASS_DEVICE_ATTR(portnum, S_IRUGO, lpfc_portnum_show, NULL); +static CLASS_DEVICE_ATTR(fwrev, S_IRUGO, lpfc_fwrev_show, NULL); +static CLASS_DEVICE_ATTR(hdw, S_IRUGO, lpfc_hdw_show, NULL); +static CLASS_DEVICE_ATTR(state, S_IRUGO, lpfc_state_show, NULL); +static CLASS_DEVICE_ATTR(option_rom_version, S_IRUGO, + lpfc_option_rom_version_show, NULL); +static CLASS_DEVICE_ATTR(num_discovered_ports, S_IRUGO, + lpfc_num_discovered_ports_show, NULL); +static CLASS_DEVICE_ATTR(speed, S_IRUGO, lpfc_speed_show, NULL); +static CLASS_DEVICE_ATTR(node_name, S_IRUGO, lpfc_node_name_show, NULL); +static CLASS_DEVICE_ATTR(port_name, S_IRUGO, lpfc_port_name_show, NULL); +static CLASS_DEVICE_ATTR(portfcid, S_IRUGO, lpfc_did_show, NULL); +static CLASS_DEVICE_ATTR(port_type, S_IRUGO, lpfc_port_type_show, NULL); +static CLASS_DEVICE_ATTR(fabric_name, S_IRUGO, lpfc_fabric_name_show, NULL); +static CLASS_DEVICE_ATTR(events, S_IRUGO, lpfc_events_show, NULL); +static CLASS_DEVICE_ATTR(nport_evt_cnt, S_IRUGO, lpfc_nport_evt_cnt_show, NULL); +static CLASS_DEVICE_ATTR(lpfc_drvr_version, S_IRUGO, lpfc_drvr_version_show, + NULL); +static CLASS_DEVICE_ATTR(management_version, S_IRUGO, management_version_show, + NULL); +static CLASS_DEVICE_ATTR(issue_lip, S_IWUSR, NULL, lpfc_issue_lip); +static CLASS_DEVICE_ATTR(board_online, S_IRUGO | S_IWUSR, + lpfc_board_online_show, lpfc_board_online_store); + +static CLASS_DEVICE_ATTR(disc_npr, S_IRUGO, lpfc_disc_npr_show, NULL); +static CLASS_DEVICE_ATTR(disc_map, S_IRUGO, lpfc_disc_map_show, NULL); +static CLASS_DEVICE_ATTR(disc_unmap, S_IRUGO, lpfc_disc_unmap_show, NULL); +static CLASS_DEVICE_ATTR(disc_prli, S_IRUGO, lpfc_disc_prli_show, NULL); +static CLASS_DEVICE_ATTR(disc_reglgn, S_IRUGO, lpfc_disc_reglgn_show, NULL); +static CLASS_DEVICE_ATTR(disc_adisc, S_IRUGO, lpfc_disc_adisc_show, NULL); +static CLASS_DEVICE_ATTR(disc_plogi, S_IRUGO, lpfc_disc_plogi_show, NULL); +static CLASS_DEVICE_ATTR(disc_unused, S_IRUGO, lpfc_disc_unused_show, NULL); +static CLASS_DEVICE_ATTR(outfcpio, S_IRUGO, lpfc_outfcpio_show, NULL); + +/* +# lpfc_log_verbose: Only turn this flag on if you are willing to risk being +# deluged with LOTS of information. +# You can set a bit mask to record specific types of verbose messages: +# +# LOG_ELS 0x1 ELS events +# LOG_DISCOVERY 0x2 Link discovery events +# LOG_MBOX 0x4 Mailbox events +# LOG_INIT 0x8 Initialization events +# LOG_LINK_EVENT 0x10 Link events +# LOG_IP 0x20 IP traffic history +# LOG_FCP 0x40 FCP traffic history +# LOG_NODE 0x80 Node table events +# LOG_MISC 0x400 Miscellaneous events +# LOG_SLI 0x800 SLI events +# LOG_CHK_COND 0x1000 FCP Check condition flag +# LOG_LIBDFC 0x2000 LIBDFC events +# LOG_ALL_MSG 0xffff LOG all messages +*/ +LPFC_ATTR_RW(log_verbose, 0x0, 0x0, 0xffff, "Verbose logging bit-mask"); + +/* +# lun_queue_depth: This parameter is used to limit the number of outstanding +# commands per FCP LUN. Value range is [1,128]. Default value is 30. +*/ +LPFC_ATTR_R(lun_queue_depth, 30, 1, 128, + "Max number of FCP commands we can queue to a specific LUN"); + +/* +# Some disk devices have a "select ID" or "select Target" capability. +# From a protocol standpoint "select ID" usually means select the +# Fibre channel "ALPA". In the FC-AL Profile there is an "informative +# annex" which contains a table that maps a "select ID" (a number +# between 0 and 7F) to an ALPA. By default, for compatibility with +# older drivers, the lpfc driver scans this table from low ALPA to high +# ALPA. +# +# Turning on the scan-down variable (on = 1, off = 0) will +# cause the lpfc driver to use an inverted table, effectively +# scanning ALPAs from high to low. Value range is [0,1]. Default value is 1. +# +# (Note: This "select ID" functionality is a LOOP ONLY characteristic +# and will not work across a fabric. Also this parameter will take +# effect only in the case when ALPA map is not available.) +*/ +LPFC_ATTR_R(scan_down, 1, 0, 1, + "Start scanning for devices from highest ALPA to lowest"); + +/* +# lpfc_nodev_tmo: If set, it will hold all I/O errors on devices that disappear +# until the timer expires. Value range is [0,255]. Default value is 20. +# NOTE: this MUST be less then the SCSI Layer command timeout - 1. +*/ +LPFC_ATTR_RW(nodev_tmo, 30, 0, 255, + "Seconds driver will hold I/O waiting for a device to come back"); + +/* +# lpfc_topology: link topology for init link +# 0x0 = attempt loop mode then point-to-point +# 0x02 = attempt point-to-point mode only +# 0x04 = attempt loop mode only +# 0x06 = attempt point-to-point mode then loop +# Set point-to-point mode if you want to run as an N_Port. +# Set loop mode if you want to run as an NL_Port. Value range is [0,0x6]. +# Default value is 0. +*/ +LPFC_ATTR_R(topology, 0, 0, 6, "Select Fibre Channel topology"); + +/* +# lpfc_link_speed: Link speed selection for initializing the Fibre Channel +# connection. +# 0 = auto select (default) +# 1 = 1 Gigabaud +# 2 = 2 Gigabaud +# 4 = 4 Gigabaud +# Value range is [0,4]. Default value is 0. +*/ +LPFC_ATTR_R(link_speed, 0, 0, 4, "Select link speed"); + +/* +# lpfc_fcp_class: Determines FC class to use for the FCP protocol. +# Value range is [2,3]. Default value is 3. +*/ +LPFC_ATTR_R(fcp_class, 3, 2, 3, + "Select Fibre Channel class of service for FCP sequences"); + +/* +# lpfc_use_adisc: Use ADISC for FCP rediscovery instead of PLOGI. Value range +# is [0,1]. Default value is 0. +*/ +LPFC_ATTR_RW(use_adisc, 0, 0, 1, + "Use ADISC on rediscovery to authenticate FCP devices"); + +/* +# lpfc_ack0: Use ACK0, instead of ACK1 for class 2 acknowledgement. Value +# range is [0,1]. Default value is 0. +*/ +LPFC_ATTR_R(ack0, 0, 0, 1, "Enable ACK0 support"); + +/* +# lpfc_fcp_bind_method: It specifies the method of binding to be used for each +# port. This binding method is used for consistent binding and mapped +# binding. A value of 1 will force WWNN binding, value of 2 will force WWPN +# binding, value of 3 will force DID binding and value of 4 will force the +# driver to derive binding from ALPA. Any consistent binding whose type does +# not match with the bind method of the port will be ignored. Value range +# is [1,4]. Default value is 2. +*/ +LPFC_ATTR_R(fcp_bind_method, 2, 0, 4, + "Select the bind method to be used"); + +/* +# lpfc_cr_delay & lpfc_cr_count: Default values for I/O colaesing +# cr_delay (msec) or cr_count outstanding commands. cr_delay can take +# value [0,63]. cr_count can take value [0,255]. Default value of cr_delay +# is 0. Default value of cr_count is 1. The cr_count feature is disabled if +# cr_delay is set to 0. +*/ +LPFC_ATTR(cr_delay, 0, 0, 63, "A count of milliseconds after which an" + "interrupt response is generated"); + +LPFC_ATTR(cr_count, 1, 1, 255, "A count of I/O completions after which an" + "interrupt response is generated"); + +/* +# lpfc_fdmi_on: controls FDMI support. +# 0 = no FDMI support +# 1 = support FDMI without attribute of hostname +# 2 = support FDMI with attribute of hostname +# Value range [0,2]. Default value is 0. +*/ +LPFC_ATTR_RW(fdmi_on, 0, 0, 2, "Enable FDMI support"); + +/* +# Specifies the maximum number of ELS cmds we can have outstanding (for +# discovery). Value range is [1,64]. Default value = 32. +*/ +LPFC_ATTR(discovery_threads, 32, 1, 64, "Maximum number of ELS commands" + "during discovery"); + +/* +# lpfc_max_luns: maximum number of LUNs per target driver will support +# Value range is [1,32768]. Default value is 256. +# NOTE: The SCSI layer will scan each target for this many luns +*/ +LPFC_ATTR_R(max_luns, 256, 1, 32768, + "Maximum number of LUNs per target driver will support"); + + +static ssize_t +sysfs_ctlreg_write(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + unsigned long iflag; + size_t buf_off; + struct Scsi_Host *host = class_to_shost(container_of(kobj, + struct class_device, kobj)); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + + if ((off + count) > FF_REG_AREA_SIZE) + return -ERANGE; + + if (count == 0) return 0; + + if (off % 4 || count % 4 || (unsigned long)buf % 4) + return -EINVAL; + + spin_lock_irqsave(phba->host->host_lock, iflag); + + if (!(phba->fc_flag & FC_OFFLINE_MODE)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return -EPERM; + } + + for (buf_off = 0; buf_off < count; buf_off += sizeof(uint32_t)) + writel(*((uint32_t *)(buf + buf_off)), + (uint8_t *)phba->ctrl_regs_memmap_p + off + buf_off); + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + + return count; +} + +static ssize_t +sysfs_ctlreg_read(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + unsigned long iflag; + size_t buf_off; + uint32_t * tmp_ptr; + struct Scsi_Host *host = class_to_shost(container_of(kobj, + struct class_device, kobj)); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + + if (off > FF_REG_AREA_SIZE) + return -ERANGE; + + if ((off + count) > FF_REG_AREA_SIZE) + count = FF_REG_AREA_SIZE - off; + + if (count == 0) return 0; + + if (off % 4 || count % 4 || (unsigned long)buf % 4) + return -EINVAL; + + spin_lock_irqsave(phba->host->host_lock, iflag); + + for (buf_off = 0; buf_off < count; buf_off += sizeof(uint32_t)) { + tmp_ptr = (uint32_t *)(buf + buf_off); + *tmp_ptr = readl((uint8_t *)(phba->ctrl_regs_memmap_p + + off + buf_off)); + } + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + + return count; +} + +static struct bin_attribute sysfs_ctlreg_attr = { + .attr = { + .name = "ctlreg", + .mode = S_IRUSR | S_IWUSR, + .owner = THIS_MODULE, + }, + .size = 256, + .read = sysfs_ctlreg_read, + .write = sysfs_ctlreg_write, +}; + + +#define MBOX_BUFF_SIZE (MAILBOX_CMD_WSIZE*sizeof(uint32_t)) + +static void +sysfs_mbox_idle (struct lpfc_hba * phba) +{ + phba->sysfs_mbox.state = SMBOX_IDLE; + phba->sysfs_mbox.offset = 0; + + if (phba->sysfs_mbox.mbox) { + mempool_free(phba->sysfs_mbox.mbox, + phba->mbox_mem_pool); + phba->sysfs_mbox.mbox = NULL; + } +} + +static ssize_t +sysfs_mbox_write(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + unsigned long iflag; + struct Scsi_Host * host = + class_to_shost(container_of(kobj, struct class_device, kobj)); + struct lpfc_hba * phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfcMboxq * mbox = NULL; + + if ((count + off) > MBOX_BUFF_SIZE) + return -ERANGE; + + if (off % 4 || count % 4 || (unsigned long)buf % 4) + return -EINVAL; + + if (count == 0) + return 0; + + if (off == 0) { + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + } + + spin_lock_irqsave(host->host_lock, iflag); + + if (off == 0) { + if (phba->sysfs_mbox.mbox) + mempool_free(mbox, phba->mbox_mem_pool); + else + phba->sysfs_mbox.mbox = mbox; + phba->sysfs_mbox.state = SMBOX_WRITING; + } + else { + if (phba->sysfs_mbox.state != SMBOX_WRITING || + phba->sysfs_mbox.offset != off || + phba->sysfs_mbox.mbox == NULL ) { + sysfs_mbox_idle(phba); + spin_unlock_irqrestore(host->host_lock, iflag); + return -EINVAL; + } + } + + memcpy((uint8_t *) & phba->sysfs_mbox.mbox->mb + off, + buf, count); + + phba->sysfs_mbox.offset = off + count; + + spin_unlock_irqrestore(host->host_lock, iflag); + + return count; +} + +static ssize_t +sysfs_mbox_read(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + unsigned long iflag; + struct Scsi_Host *host = + class_to_shost(container_of(kobj, struct class_device, + kobj)); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + int rc; + + if (off > sizeof(MAILBOX_t)) + return -ERANGE; + + if ((count + off) > sizeof(MAILBOX_t)) + count = sizeof(MAILBOX_t) - off; + + if (off % 4 || count % 4 || (unsigned long)buf % 4) + return -EINVAL; + + if (off && count == 0) + return 0; + + spin_lock_irqsave(phba->host->host_lock, iflag); + + if (off == 0 && + phba->sysfs_mbox.state == SMBOX_WRITING && + phba->sysfs_mbox.offset >= 2 * sizeof(uint32_t)) { + + switch (phba->sysfs_mbox.mbox->mb.mbxCommand) { + /* Offline only */ + case MBX_WRITE_NV: + case MBX_INIT_LINK: + case MBX_DOWN_LINK: + case MBX_CONFIG_LINK: + case MBX_CONFIG_RING: + case MBX_RESET_RING: + case MBX_UNREG_LOGIN: + case MBX_CLEAR_LA: + case MBX_DUMP_CONTEXT: + case MBX_RUN_DIAGS: + case MBX_RESTART: + case MBX_FLASH_WR_ULA: + case MBX_SET_MASK: + case MBX_SET_SLIM: + case MBX_SET_DEBUG: + if (!(phba->fc_flag & FC_OFFLINE_MODE)) { + printk(KERN_WARNING "mbox_read:Command 0x%x " + "is illegal in on-line state\n", + phba->sysfs_mbox.mbox->mb.mbxCommand); + sysfs_mbox_idle(phba); + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + return -EPERM; + } + case MBX_LOAD_SM: + case MBX_READ_NV: + case MBX_READ_CONFIG: + case MBX_READ_RCONFIG: + case MBX_READ_STATUS: + case MBX_READ_XRI: + case MBX_READ_REV: + case MBX_READ_LNK_STAT: + case MBX_DUMP_MEMORY: + case MBX_DOWN_LOAD: + case MBX_UPDATE_CFG: + case MBX_LOAD_AREA: + case MBX_LOAD_EXP_ROM: + break; + case MBX_READ_SPARM64: + case MBX_READ_LA: + case MBX_READ_LA64: + case MBX_REG_LOGIN: + case MBX_REG_LOGIN64: + case MBX_CONFIG_PORT: + case MBX_RUN_BIU_DIAG: + printk(KERN_WARNING "mbox_read: Illegal Command 0x%x\n", + phba->sysfs_mbox.mbox->mb.mbxCommand); + sysfs_mbox_idle(phba); + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + return -EPERM; + default: + printk(KERN_WARNING "mbox_read: Unknown Command 0x%x\n", + phba->sysfs_mbox.mbox->mb.mbxCommand); + sysfs_mbox_idle(phba); + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + return -EPERM; + } + + if ((phba->fc_flag & FC_OFFLINE_MODE) || + (!(phba->sli.sliinit.sli_flag & LPFC_SLI2_ACTIVE))){ + spin_unlock_irqrestore(phba->host->host_lock, iflag); + rc = lpfc_sli_issue_mbox (phba, + phba->sysfs_mbox.mbox, + MBX_POLL); + spin_lock_irqsave(phba->host->host_lock, iflag); + } else { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + rc = lpfc_sli_issue_mbox_wait (phba, + phba->sysfs_mbox.mbox, + phba->fc_ratov * 2); + spin_lock_irqsave(phba->host->host_lock, iflag); + } + + if (rc != MBX_SUCCESS) { + sysfs_mbox_idle(phba); + spin_unlock_irqrestore(host->host_lock, iflag); + return -ENODEV; + } + phba->sysfs_mbox.state = SMBOX_READING; + } + else if (phba->sysfs_mbox.offset != off || + phba->sysfs_mbox.state != SMBOX_READING) { + printk(KERN_WARNING "mbox_read: Bad State\n"); + sysfs_mbox_idle(phba); + spin_unlock_irqrestore(host->host_lock, iflag); + return -EINVAL; + } + + memcpy(buf, (uint8_t *) & phba->sysfs_mbox.mbox->mb + off, count); + + phba->sysfs_mbox.offset = off + count; + + if (phba->sysfs_mbox.offset == sizeof(MAILBOX_t)) + sysfs_mbox_idle(phba); + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + + return count; +} + +static struct bin_attribute sysfs_mbox_attr = { + .attr = { + .name = "mbox", + .mode = S_IRUSR | S_IWUSR, + .owner = THIS_MODULE, + }, + .size = sizeof(MAILBOX_t), + .read = sysfs_mbox_read, + .write = sysfs_mbox_write, +}; + + +#ifdef RHEL_FC +/* + * The LPFC driver treats linkdown handling as target loss events so there + * are no sysfs handlers for link_down_tmo. + */ +static void +lpfc_get_starget_port_id(struct scsi_target *starget) +{ + struct lpfc_nodelist *ndlp = NULL; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct lpfc_hba *phba = (struct lpfc_hba *) shost->hostdata[0]; + uint16_t did = 0; + + spin_lock_irq(shost->host_lock); + /* Search the mapped list for this target ID */ + list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) { + if (starget->id == ndlp->nlp_sid) { + did = ndlp->nlp_DID; + break; + } + } + spin_unlock_irq(shost->host_lock); + + fc_starget_port_id(starget) = did; +} + +static void +lpfc_get_starget_node_name(struct scsi_target *starget) +{ + struct lpfc_nodelist *ndlp = NULL; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct lpfc_hba *phba = (struct lpfc_hba *) shost->hostdata[0]; + uint64_t node_name = 0; + + spin_lock_irq(shost->host_lock); + /* Search the mapped list for this target ID */ + list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) { + if (starget->id == ndlp->nlp_sid) { + memcpy(&node_name, &ndlp->nlp_nodename, + sizeof(struct lpfc_name)); + break; + } + } + spin_unlock_irq(shost->host_lock); + + fc_starget_node_name(starget) = be64_to_cpu(node_name); +} + +static void +lpfc_get_starget_port_name(struct scsi_target *starget) +{ + struct lpfc_nodelist *ndlp = NULL; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct lpfc_hba *phba = (struct lpfc_hba *) shost->hostdata[0]; + uint64_t port_name = 0; + + spin_lock_irq(shost->host_lock); + /* Search the mapped list for this target ID */ + list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) { + if (starget->id == ndlp->nlp_sid) { + memcpy(&port_name, &ndlp->nlp_portname, + sizeof(struct lpfc_name)); + break; + } + } + spin_unlock_irq(shost->host_lock); + + fc_starget_port_name(starget) = be64_to_cpu(port_name); +} + +static void +lpfc_get_starget_loss_tmo(struct scsi_target *starget) +{ + /* + * Return the driver's global value for device loss timeout plus + * five seconds to allow the driver's nodev timer to run. + */ + fc_starget_dev_loss_tmo(starget) = lpfc_nodev_tmo + 5; +} + +static void +lpfc_set_starget_loss_tmo(struct scsi_target *starget, uint32_t timeout) +{ + /* + * The driver doesn't have a per-target timeout setting. Set + * this value globally. Keep lpfc_nodev_tmo >= 1. + */ + if (timeout) + lpfc_nodev_tmo = timeout; + else + lpfc_nodev_tmo = 1; +} + +#else /* not RHEL_FC */ + +static void +lpfc_get_port_id(struct scsi_device *sdev) +{ + struct lpfc_target *target = sdev->hostdata; + if (sdev->host->transportt && target->pnode) + fc_port_id(sdev) = target->pnode->nlp_DID; +} + +static void +lpfc_get_node_name(struct scsi_device *sdev) +{ + struct lpfc_target *target = sdev->hostdata; + uint64_t node_name = 0; + if (sdev->host->transportt && target->pnode) + memcpy(&node_name, &target->pnode->nlp_nodename, + sizeof(struct lpfc_name)); + fc_node_name(sdev) = be64_to_cpu(node_name); +} + +static void +lpfc_get_port_name(struct scsi_device *sdev) +{ + struct lpfc_target *target = sdev->hostdata; + uint64_t port_name = 0; + if (sdev->host->transportt && target->pnode) + memcpy(&port_name, &target->pnode->nlp_portname, + sizeof(struct lpfc_name)); + fc_port_name(sdev) = be64_to_cpu(port_name); +} +#endif /* not RHEL_FC */ + +static struct fc_function_template lpfc_transport_functions = { +#ifdef RHEL_FC + .get_starget_port_id = lpfc_get_starget_port_id, + .show_starget_port_id = 1, + + .get_starget_node_name = lpfc_get_starget_node_name, + .show_starget_node_name = 1, + + .get_starget_port_name = lpfc_get_starget_port_name, + .show_starget_port_name = 1, + + .get_starget_dev_loss_tmo = lpfc_get_starget_loss_tmo, + .set_starget_dev_loss_tmo = lpfc_set_starget_loss_tmo, + .show_starget_dev_loss_tmo = 1, + +#else /* not RHEL_FC */ + .get_port_id = lpfc_get_port_id, + .show_port_id = 1, + + .get_node_name = lpfc_get_node_name, + .show_node_name = 1, + + .get_port_name = lpfc_get_port_name, + .show_port_name = 1, +#endif /* not RHEL_FC */ +}; + +static int +lpfc_proc_info(struct Scsi_Host *host, + char *buf, char **start, off_t offset, int count, int rw) +{ + struct lpfc_hba *phba = (struct lpfc_hba *)host->hostdata[0]; + struct lpfc_nodelist *ndlp; + int len = 0; + + /* Sufficient bytes to hold a port or node name. */ + uint8_t name[sizeof (struct lpfc_name)]; + + /* If rw = 0, then read info + * If rw = 1, then write info (NYI) + */ + if (rw) + return -EINVAL; + + list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) { + if (ndlp->nlp_state == NLP_STE_MAPPED_NODE){ + len += snprintf(buf + len, PAGE_SIZE -len, + "lpfc%dt%02x DID %06x WWPN ", + phba->brd_no, + ndlp->nlp_sid, ndlp->nlp_DID); + + memcpy (&name[0], &ndlp->nlp_portname, + sizeof (struct lpfc_name)); + len += snprintf(buf + len, PAGE_SIZE-len, + "%02x:%02x:%02x:%02x:%02x:%02x:" + "%02x:%02x", + name[0], name[1], name[2], + name[3], name[4], name[5], + name[6], name[7]); + len += snprintf(buf + len, PAGE_SIZE-len, " WWNN "); + memcpy (&name[0], &ndlp->nlp_nodename, + sizeof (struct lpfc_name)); + len += snprintf(buf + len, PAGE_SIZE-len, + "%02x:%02x:%02x:%02x:%02x:%02x:" + "%02x:%02x\n", + name[0], name[1], name[2], + name[3], name[4], name[5], + name[6], name[7]); + } + if (PAGE_SIZE - len < 90) + break; + } + if (&ndlp->nlp_listp != &phba->fc_nlpmap_list) + len += snprintf(buf+len, PAGE_SIZE-len, "...\n"); + + return (len); +} + +static int +lpfc_slave_alloc(struct scsi_device *scsi_devs) +{ + struct lpfc_hba *phba; + struct lpfc_target *target; + + /* + * Store the lun pointer in the scsi_device hostdata pointer provided + * the driver has already discovered the target id. + */ + phba = (struct lpfc_hba *) scsi_devs->host->hostdata[0]; + target = lpfc_find_target(phba, scsi_devs->id, NULL); + if (target) { + scsi_devs->hostdata = target; + target->slavecnt++; + return 0; + } + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) + return -ENXIO; +#else + + /* + * The driver does not have a target id matching that in the scsi + * device. Allocate a dummy target initialized to zero so that + * the driver's queuecommand entry correctly fails the call + * forcing the midlayer to call lpfc_slave_destroy. This code + * will be removed in a subsequent kernel patch. + */ + + target = kmalloc(sizeof (struct lpfc_target), GFP_KERNEL); + if (!target) + return 1; + + memset(target, 0, sizeof (struct lpfc_target)); +#ifdef SLES_FC + init_timer(&target->dev_loss_timer); +#endif + scsi_devs->hostdata = target; + target->slavecnt++; + return 0; +#endif +} + +static int +lpfc_slave_configure(struct scsi_device *sdev) +{ + struct lpfc_hba *phba = (struct lpfc_hba *) sdev->host->hostdata[0]; + +#if defined(RHEL_FC) + struct lpfc_target *target = (struct lpfc_target *) sdev->hostdata; +#endif + + if (sdev->tagged_supported) + scsi_activate_tcq(sdev, phba->cfg_lun_queue_depth); + else + scsi_deactivate_tcq(sdev, phba->cfg_lun_queue_depth); + +#ifdef RHEL_FC + if ((target) && (sdev->sdev_target)) { + /* + * Initialize the fc transport attributes for the target + * containing this scsi device. Also note that the driver's + * target pointer is stored in the starget_data for the + * driver's sysfs entry point functions. + */ + target->starget = sdev->sdev_target; + fc_starget_dev_loss_tmo(target->starget) = lpfc_nodev_tmo + 5; + } +#endif /* RHEL_FC */ + + return 0; +} + +static void +lpfc_slave_destroy(struct scsi_device *sdev) +{ + struct lpfc_hba *phba; + struct lpfc_target *target; + int i; + + phba = (struct lpfc_hba *) sdev->host->hostdata[0]; + target = sdev->hostdata; + if (target) { + target->slavecnt--; + + /* Double check for valid lpfc_target */ + for (i = 0; i < MAX_FCP_TARGET; i++) { + if(target == phba->device_queue_hash[i]) { + if ((!target->slavecnt) && !(target->pnode)) { + kfree(target); + phba->device_queue_hash[i] = NULL; + } + sdev->hostdata = NULL; + return; + } + } + /* If we get here, this was a dummy lpfc_target allocated + * in lpfc_slave_alloc. + */ + if (!target->slavecnt) + kfree(target); + } + + /* + * Set this scsi device's hostdata to NULL since it is going + * away. Also, (future) don't set the starget_dev_loss_tmo + * this value is global to all targets managed by this + * host. + */ + sdev->hostdata = NULL; + return; +} + +static struct class_device_attribute *lpfc_host_attrs[] = { + &class_device_attr_info, + &class_device_attr_serialnum, + &class_device_attr_modeldesc, + &class_device_attr_modelname, + &class_device_attr_programtype, + &class_device_attr_portnum, + &class_device_attr_fwrev, + &class_device_attr_hdw, + &class_device_attr_option_rom_version, + &class_device_attr_state, + &class_device_attr_num_discovered_ports, + &class_device_attr_speed, + &class_device_attr_node_name, + &class_device_attr_port_name, + &class_device_attr_portfcid, + &class_device_attr_port_type, + &class_device_attr_fabric_name, + &class_device_attr_events, + &class_device_attr_lpfc_drvr_version, + &class_device_attr_lpfc_log_verbose, + &class_device_attr_lpfc_lun_queue_depth, + &class_device_attr_lpfc_nodev_tmo, + &class_device_attr_lpfc_fcp_class, + &class_device_attr_lpfc_use_adisc, + &class_device_attr_lpfc_ack0, + &class_device_attr_lpfc_topology, + &class_device_attr_lpfc_scan_down, + &class_device_attr_lpfc_link_speed, + &class_device_attr_lpfc_fdmi_on, + &class_device_attr_lpfc_fcp_bind_method, + &class_device_attr_lpfc_max_luns, + &class_device_attr_nport_evt_cnt, + &class_device_attr_management_version, + &class_device_attr_issue_lip, + &class_device_attr_board_online, + &class_device_attr_disc_npr, + &class_device_attr_disc_map, + &class_device_attr_disc_unmap, + &class_device_attr_disc_prli, + &class_device_attr_disc_reglgn, + &class_device_attr_disc_adisc, + &class_device_attr_disc_plogi, + &class_device_attr_disc_unused, + &class_device_attr_outfcpio, + NULL, +}; + +static struct scsi_host_template driver_template = { + .module = THIS_MODULE, + .name = LPFC_DRIVER_NAME, + .info = lpfc_info, + .queuecommand = lpfc_queuecommand, + .eh_abort_handler = lpfc_abort_handler, + .eh_device_reset_handler= lpfc_reset_lun_handler, + .eh_bus_reset_handler = lpfc_reset_bus_handler, + .slave_alloc = lpfc_slave_alloc, + .slave_configure = lpfc_slave_configure, + .slave_destroy = lpfc_slave_destroy, + .proc_info = lpfc_proc_info, + .proc_name = LPFC_DRIVER_NAME, + .this_id = -1, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 30, + .max_sectors = 0xFFFF, + .shost_attrs = lpfc_host_attrs, + .use_clustering = ENABLE_CLUSTERING, +}; + +static int +lpfc_sli_setup(struct lpfc_hba * phba) +{ + int i, totiocb = 0; + struct lpfc_sli *psli = &phba->sli; + LPFC_RING_INIT_t *pring; + + psli->sliinit.num_rings = MAX_CONFIGURED_RINGS; + psli->fcp_ring = LPFC_FCP_RING; + psli->next_ring = LPFC_FCP_NEXT_RING; + psli->ip_ring = LPFC_IP_RING; + + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->sliinit.ringinit[i]; + switch (i) { + case LPFC_FCP_RING: /* ring 0 - FCP */ + /* numCiocb and numRiocb are used in config_port */ + pring->numCiocb = SLI2_IOCB_CMD_R0_ENTRIES; + pring->numRiocb = SLI2_IOCB_RSP_R0_ENTRIES; + pring->numCiocb += SLI2_IOCB_CMD_R1XTRA_ENTRIES; + pring->numRiocb += SLI2_IOCB_RSP_R1XTRA_ENTRIES; + pring->numCiocb += SLI2_IOCB_CMD_R3XTRA_ENTRIES; + pring->numRiocb += SLI2_IOCB_RSP_R3XTRA_ENTRIES; + pring->iotag_ctr = 0; + pring->iotag_max = + (phba->cfg_hba_queue_depth * 2); + pring->fast_iotag = pring->iotag_max; + pring->num_mask = 0; + break; + case LPFC_IP_RING: /* ring 1 - IP */ + /* numCiocb and numRiocb are used in config_port */ + pring->numCiocb = SLI2_IOCB_CMD_R1_ENTRIES; + pring->numRiocb = SLI2_IOCB_RSP_R1_ENTRIES; + pring->num_mask = 0; + break; + case LPFC_ELS_RING: /* ring 2 - ELS / CT */ + /* numCiocb and numRiocb are used in config_port */ + pring->numCiocb = SLI2_IOCB_CMD_R2_ENTRIES; + pring->numRiocb = SLI2_IOCB_RSP_R2_ENTRIES; + pring->fast_iotag = 0; + pring->iotag_ctr = 0; + pring->iotag_max = 4096; + pring->num_mask = 4; + pring->prt[0].profile = 0; /* Mask 0 */ + pring->prt[0].rctl = FC_ELS_REQ; + pring->prt[0].type = FC_ELS_DATA; + pring->prt[0].lpfc_sli_rcv_unsol_event = + lpfc_els_unsol_event; + pring->prt[1].profile = 0; /* Mask 1 */ + pring->prt[1].rctl = FC_ELS_RSP; + pring->prt[1].type = FC_ELS_DATA; + pring->prt[1].lpfc_sli_rcv_unsol_event = + lpfc_els_unsol_event; + pring->prt[2].profile = 0; /* Mask 2 */ + /* NameServer Inquiry */ + pring->prt[2].rctl = FC_UNSOL_CTL; + /* NameServer */ + pring->prt[2].type = FC_COMMON_TRANSPORT_ULP; + pring->prt[2].lpfc_sli_rcv_unsol_event = + lpfc_ct_unsol_event; + pring->prt[3].profile = 0; /* Mask 3 */ + /* NameServer response */ + pring->prt[3].rctl = FC_SOL_CTL; + /* NameServer */ + pring->prt[3].type = FC_COMMON_TRANSPORT_ULP; + pring->prt[3].lpfc_sli_rcv_unsol_event = + lpfc_ct_unsol_event; + break; + } + totiocb += (pring->numCiocb + pring->numRiocb); + } + if (totiocb > MAX_SLI2_IOCB) { + /* Too many cmd / rsp ring entries in SLI2 SLIM */ + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0462 Too many cmd / rsp ring entries in " + "SLI2 SLIM Data: x%x x%x\n", + phba->brd_no, totiocb, MAX_SLI2_IOCB); + } + +#ifdef USE_HGP_HOST_SLIM + psli->sliinit.sli_flag = LPFC_HGP_HOSTSLIM; +#else + psli->sliinit.sli_flag = 0; +#endif + + return (0); +} + +static int +lpfc_set_bind_type(struct lpfc_hba * phba) +{ + int bind_type = phba->cfg_fcp_bind_method; + int ret = LPFC_BIND_WW_NN_PN; + + switch (bind_type) { + case 1: + phba->fcp_mapping = FCP_SEED_WWNN; + break; + + case 2: + phba->fcp_mapping = FCP_SEED_WWPN; + break; + + case 3: + phba->fcp_mapping = FCP_SEED_DID; + ret = LPFC_BIND_DID; + break; + + case 4: + phba->fcp_mapping = FCP_SEED_DID; + ret = LPFC_BIND_DID; + break; + } + + return (ret); +} + +static void +lpfc_get_cfgparam(struct lpfc_hba *phba) +{ + lpfc_log_verbose_set(phba, lpfc_log_verbose); + lpfc_fcp_bind_method_set(phba, lpfc_fcp_bind_method); + lpfc_cr_delay_set(phba, lpfc_cr_delay); + lpfc_cr_count_set(phba, lpfc_cr_count); + lpfc_lun_queue_depth_set(phba, lpfc_lun_queue_depth); + lpfc_fcp_class_set(phba, lpfc_fcp_class); + lpfc_use_adisc_set(phba, lpfc_use_adisc); + lpfc_ack0_set(phba, lpfc_ack0); + lpfc_topology_set(phba, lpfc_topology); + lpfc_scan_down_set(phba, lpfc_scan_down); + lpfc_nodev_tmo_set(phba, lpfc_nodev_tmo); + lpfc_link_speed_set(phba, lpfc_link_speed); + lpfc_fdmi_on_set(phba, lpfc_fdmi_on); + lpfc_discovery_threads_set(phba, lpfc_discovery_threads); + lpfc_max_luns_set(phba, lpfc_max_luns); + phba->cfg_scsi_hotplug = 0; + + switch (phba->pcidev->device) { + case PCI_DEVICE_ID_LP101: + case PCI_DEVICE_ID_BSMB: + case PCI_DEVICE_ID_ZSMB: + phba->cfg_hba_queue_depth = LPFC_LP101_HBA_Q_DEPTH; + break; + case PCI_DEVICE_ID_RFLY: + case PCI_DEVICE_ID_PFLY: + case PCI_DEVICE_ID_BMID: + case PCI_DEVICE_ID_ZMID: + case PCI_DEVICE_ID_TFLY: + phba->cfg_hba_queue_depth = LPFC_LC_HBA_Q_DEPTH; + break; + default: + phba->cfg_hba_queue_depth = LPFC_DFT_HBA_Q_DEPTH; + } + return; +} + +static void +lpfc_consistent_bind_setup(struct lpfc_hba * phba) +{ + INIT_LIST_HEAD(&phba->fc_nlpbind_list); + phba->fc_bind_cnt = 0; +} + +static uint8_t +lpfc_get_brd_no(struct lpfc_hba * phba) +{ + uint8_t brd, found = 1; + + brd = 0; + while(found) { + phba = NULL; + found = 0; + list_for_each_entry(phba, &lpfc_hba_list, hba_list) { + if (phba->brd_no == brd) { + found = 1; + brd++; + break; + } + } + } + return (brd); +} + + +static int __devinit +lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) +{ + struct Scsi_Host *host; + struct lpfc_hba *phba; + struct lpfc_sli *psli; + unsigned long iflag; + unsigned long bar0map_len, bar2map_len; + int error = -ENODEV, retval; + + if (pci_enable_device(pdev)) + goto out; + if (pci_request_regions(pdev, LPFC_DRIVER_NAME)) + goto out_disable_device; + + /* + * Allocate space for adapter info structure + */ + phba = kmalloc(sizeof(*phba), GFP_KERNEL); + if (!phba) + goto out_release_regions; + memset(phba, 0, sizeof (struct lpfc_hba)); + + host = scsi_host_alloc(&driver_template, sizeof (unsigned long)); + if (!host) { + printk (KERN_WARNING "%s: scsi_host_alloc failed.\n", + lpfc_drvr_name); + error = -ENOMEM; + goto out_kfree_phba; + } + + phba->fc_flag |= FC_LOADING; + phba->pcidev = pdev; + phba->host = host; + + INIT_LIST_HEAD(&phba->ctrspbuflist); + INIT_LIST_HEAD(&phba->rnidrspbuflist); + INIT_LIST_HEAD(&phba->freebufList); + + /* Initialize timers used by driver */ + init_timer(&phba->fc_estabtmo); + phba->fc_estabtmo.function = lpfc_establish_link_tmo; + phba->fc_estabtmo.data = (unsigned long)phba; + init_timer(&phba->fc_disctmo); + phba->fc_disctmo.function = lpfc_disc_timeout; + phba->fc_disctmo.data = (unsigned long)phba; + init_timer(&phba->fc_scantmo); + phba->fc_scantmo.function = lpfc_scan_timeout; + phba->fc_scantmo.data = (unsigned long)phba; + + init_timer(&phba->fc_fdmitmo); + phba->fc_fdmitmo.function = lpfc_fdmi_tmo; + phba->fc_fdmitmo.data = (unsigned long)phba; + init_timer(&phba->els_tmofunc); + phba->els_tmofunc.function = lpfc_els_timeout; + phba->els_tmofunc.data = (unsigned long)phba; + psli = &phba->sli; + init_timer(&psli->mbox_tmo); + psli->mbox_tmo.function = lpfc_mbox_timeout; + psli->mbox_tmo.data = (unsigned long)phba; + + /* Assign an unused board number */ + phba->brd_no = lpfc_get_brd_no(phba); + host->unique_id = phba->brd_no; + + /* + * Get all the module params for configuring this host and then + * establish the host parameters. + */ + lpfc_get_cfgparam(phba); + + host->max_id = LPFC_MAX_TARGET; + host->max_lun = phba->cfg_max_luns; + host->this_id = -1; + + if(phba->cfg_scsi_hotplug) { + lpfc_printf_log(phba, KERN_ERR, LOG_FCP, + "%d:0264 HotPlug Support Enabled\n", + phba->brd_no); + } + + /* Add adapter structure to list */ + list_add_tail(&phba->hba_list, &lpfc_hba_list); + + /* Initialize all internally managed lists. */ + INIT_LIST_HEAD(&phba->fc_nlpmap_list); + INIT_LIST_HEAD(&phba->fc_nlpunmap_list); + INIT_LIST_HEAD(&phba->fc_unused_list); + INIT_LIST_HEAD(&phba->fc_plogi_list); + INIT_LIST_HEAD(&phba->fc_adisc_list); + INIT_LIST_HEAD(&phba->fc_reglogin_list); + INIT_LIST_HEAD(&phba->fc_prli_list); + INIT_LIST_HEAD(&phba->fc_npr_list); + lpfc_consistent_bind_setup(phba); + + init_waitqueue_head(&phba->linkevtwq); + init_waitqueue_head(&phba->rscnevtwq); + init_waitqueue_head(&phba->ctevtwq); + + pci_set_master(pdev); + retval = pci_set_mwi(pdev); + if (retval) + dev_printk(KERN_WARNING, &pdev->dev, + "Warning: pci_set_mwi returned %d\n", retval); + + /* Configure DMA attributes. */ + if (dma_set_mask(&phba->pcidev->dev, 0xffffffffffffffffULL) && + dma_set_mask(&phba->pcidev->dev, 0xffffffffULL)) + goto out_list_del; + + /* + * Get the physical address of Bar0 and Bar2 and the number of bytes + * required by each mapping. + */ + phba->pci_bar0_map = pci_resource_start(phba->pcidev, 0); + bar0map_len = pci_resource_len(phba->pcidev, 0); + + phba->pci_bar2_map = pci_resource_start(phba->pcidev, 2); + bar2map_len = pci_resource_len(phba->pcidev, 2); + + /* Map HBA SLIM and Control Registers to a kernel virtual address. */ + phba->slim_memmap_p = ioremap(phba->pci_bar0_map, bar0map_len); + phba->ctrl_regs_memmap_p = ioremap(phba->pci_bar2_map, bar2map_len); + + /* + * Allocate memory for SLI-2 structures + */ + phba->slim2p = dma_alloc_coherent(&phba->pcidev->dev, SLI2_SLIM_SIZE, + &phba->slim2p_mapping, GFP_KERNEL); + if (!phba->slim2p) + goto out_iounmap; + + + lpfc_sli_setup(phba); /* Setup SLI Layer to run over lpfc HBAs */ + lpfc_sli_queue_setup(phba); /* Initialize the SLI Layer */ + + error = lpfc_mem_alloc(phba); + if (error) + goto out_dec_nhbas; + + lpfc_set_bind_type(phba); + + /* Initialize HBA structure */ + phba->fc_edtov = FF_DEF_EDTOV; + phba->fc_ratov = FF_DEF_RATOV; + phba->fc_altov = FF_DEF_ALTOV; + phba->fc_arbtov = FF_DEF_ARBTOV; + + INIT_LIST_HEAD(&phba->dpc_disc); + init_completion(&phba->dpc_startup); + init_completion(&phba->dpc_exiting); + + /* + * Startup the kernel thread for this host adapter + */ + phba->dpc_kill = 0; + phba->dpc_pid = kernel_thread(lpfc_do_dpc, phba, 0); + if (phba->dpc_pid < 0) { + error = phba->dpc_pid; + goto out_free_mem; + } + wait_for_completion(&phba->dpc_startup); + + /* Call SLI to initialize the HBA. */ + error = lpfc_sli_hba_setup(phba); + if (error) + goto out_hba_down; + + /* We can rely on a queue depth attribute only after SLI HBA setup */ + host->can_queue = phba->cfg_hba_queue_depth - 10; + + /* + * Starting with 2.4.0 kernel, Linux can support commands longer + * than 12 bytes. However, scsi_register() always sets it to 12. + * For it to be useful to the midlayer, we have to set it here. + */ + host->max_cmd_len = 16; + + /* + * Queue depths per lun + */ + host->transportt = lpfc_transport_template; + host->hostdata[0] = (unsigned long)phba; + pci_set_drvdata(pdev, host); + error = scsi_add_host(host, &pdev->dev); + if (error) + goto out_hba_down; + + sysfs_create_bin_file(&host->shost_classdev.kobj, &sysfs_ctlreg_attr); + sysfs_create_bin_file(&host->shost_classdev.kobj, &sysfs_mbox_attr); + scsi_scan_host(host); + phba->fc_flag &= ~FC_LOADING; + return 0; + +out_hba_down: + /* Stop any timers that were started during this attach. */ + spin_lock_irqsave(phba->host->host_lock, iflag); + lpfc_sli_hba_down(phba); + lpfc_stop_timer(phba); + phba->work_hba_events = 0; + spin_unlock_irqrestore(phba->host->host_lock, iflag); + + /* Kill the kernel thread for this host */ + if (phba->dpc_pid >= 0) { + phba->dpc_kill = 1; + wmb(); + kill_proc(phba->dpc_pid, SIGHUP, 1); + wait_for_completion(&phba->dpc_exiting); + } + +out_free_mem: + lpfc_mem_free(phba); +out_dec_nhbas: + dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE, + phba->slim2p, phba->slim2p_mapping); +out_iounmap: + iounmap(phba->ctrl_regs_memmap_p); + iounmap(phba->slim_memmap_p); +out_list_del: + list_del_init(&phba->hba_list); + scsi_host_put(host); +out_kfree_phba: + kfree(phba); +out_release_regions: + pci_release_regions(pdev); +out_disable_device: + pci_disable_device(pdev); +out: + return error; +} + +static void __devexit +lpfc_pci_remove_one(struct pci_dev *pdev) +{ + struct Scsi_Host *host = pci_get_drvdata(pdev); + struct lpfc_hba *phba = (struct lpfc_hba *)host->hostdata[0]; + struct lpfc_target *targetp; + int i; + unsigned long iflag; + + sysfs_remove_bin_file(&host->shost_classdev.kobj, &sysfs_mbox_attr); + sysfs_remove_bin_file(&host->shost_classdev.kobj, &sysfs_ctlreg_attr); + + spin_lock_irqsave(phba->host->host_lock, iflag); + + /* Since we are going to scsi_remove_host(), disassociate scsi_dev + * from lpfc_target, and make sure its unblocked. + */ + for (i = 0; i < MAX_FCP_TARGET; i++) { + targetp = phba->device_queue_hash[i]; + if (!targetp) + continue; +#if defined(RHEL_FC) || defined(SLES_FC) + if(targetp->pnode) { + if(targetp->blocked) { + /* If we are blocked, force a nodev_tmo */ + del_timer_sync(&targetp->pnode->nlp_tmofunc); + if (!list_empty(&targetp->pnode-> + nodev_timeout_evt.evt_listp)) + list_del_init(&targetp->pnode-> + nodev_timeout_evt. + evt_listp); + lpfc_process_nodev_timeout(phba, + targetp->pnode); + } + else { + /* If we are unblocked, just remove + * the scsi device. + */ + lpfc_target_remove(phba, targetp); + } + } +#endif /* RHEL_FC or SLES_FC */ +#if defined(RHEL_FC) + targetp->starget = NULL; +#endif /* RHEL_FC */ + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + + list_del(&phba->hba_list); + scsi_remove_host(phba->host); + + /* detach the board */ + + /* Kill the kernel thread for this host */ + if (phba->dpc_pid >= 0) { + phba->dpc_kill = 1; + wmb(); + kill_proc(phba->dpc_pid, SIGHUP, 1); + wait_for_completion(&phba->dpc_exiting); + } + + /* + * Bring down the SLI Layer. This step disable all interrupts, + * clears the rings, discards all mailbox commands, and resets + * the HBA. + */ + spin_lock_irqsave(phba->host->host_lock, iflag); + lpfc_sli_hba_down(phba); + spin_unlock_irqrestore(phba->host->host_lock, iflag); + + /* Release the irq reservation */ + free_irq(phba->pcidev->irq, phba); + + spin_lock_irqsave(phba->host->host_lock, iflag); + lpfc_cleanup(phba, 0); + lpfc_stop_timer(phba); + phba->work_hba_events = 0; + spin_unlock_irqrestore(phba->host->host_lock, iflag); + lpfc_scsi_free(phba); + + lpfc_mem_free(phba); + + /* Free resources associated with SLI2 interface */ + dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE, + phba->slim2p, phba->slim2p_mapping); + + /* unmap adapter SLIM and Control Registers */ + iounmap(phba->ctrl_regs_memmap_p); + iounmap(phba->slim_memmap_p); + + pci_release_regions(phba->pcidev); + pci_disable_device(phba->pcidev); + + scsi_host_put(phba->host); + kfree(phba); + + pci_set_drvdata(pdev, NULL); +} + +static struct pci_device_id lpfc_id_table[] = { + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_VIPER, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_THOR, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PEGASUS, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_CENTAUR, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_DRAGONFLY, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SUPERFLY, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_RFLY, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PFLY, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_HELIOS, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_BMID, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_BSMB, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZEPHYR, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZMID, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZSMB, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_TFLY, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LP101, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LP10000S, + PCI_ANY_ID, PCI_ANY_ID, }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, lpfc_id_table); + + +static struct pci_driver lpfc_driver = { + .name = LPFC_DRIVER_NAME, + .id_table = lpfc_id_table, + .probe = lpfc_pci_probe_one, + .remove = __devexit_p(lpfc_pci_remove_one), +}; + +static int __init +lpfc_init(void) +{ + int rc; + + printk(LPFC_MODULE_DESC "\n"); + printk(LPFC_COPYRIGHT "\n"); + + lpfc_transport_template = + fc_attach_transport(&lpfc_transport_functions); + if (!lpfc_transport_template) + return -ENODEV; + rc = pci_module_init(&lpfc_driver); + return rc; + +} + +static void __exit +lpfc_exit(void) +{ + pci_unregister_driver(&lpfc_driver); + fc_release_transport(lpfc_transport_template); +} +module_init(lpfc_init); +module_exit(lpfc_exit); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(LPFC_MODULE_DESC); +MODULE_AUTHOR("Emulex Corporation - tech.support@emulex.com"); +MODULE_VERSION("0:" LPFC_DRIVER_VERSION); --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_version.h 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_version.h 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,38 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_version.h 1.58.1.8 2005/07/27 18:29:31EDT sf_support Exp $ + */ + +#ifndef _H_LPFC_VERSION +#define _H_LPFC_VERSION + +#define LPFC_DRIVER_VERSION "8.0.16.17" + +#define LPFC_DRIVER_NAME "lpfc" + +#define LPFC_MODULE_DESC "Emulex LightPulse Fibre Channel SCSI driver " \ + LPFC_DRIVER_VERSION +#define LPFC_COPYRIGHT "Copyright(c) 2003-2005 Emulex. All rights reserved." + +#define DFC_API_VERSION "0.0.0" + +#endif --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_mem.h 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_mem.h 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,56 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_mem.h 1.23.1.2 2005/06/13 17:16:36EDT sf_support Exp $ + */ + +#ifndef _H_LPFC_MEM +#define _H_LPFC_MEM + + +struct lpfc_dmabuf { + struct list_head list; + void *virt; /* virtual address ptr */ + dma_addr_t phys; /* mapped address */ +}; +struct lpfc_dmabufext { + struct lpfc_dmabuf dma; + uint32_t size; + uint32_t flag; + struct list_head list; + uint32_t uniqueid; + uint32_t data; +}; +typedef struct lpfc_dmabufext DMABUFEXT_t; + +struct lpfc_dma_pool { + struct lpfc_dmabuf *elements; + uint32_t max_count; + uint32_t current_count; +}; + + +#define MEM_PRI 0x100 /* Priority bit: set to exceed low + water */ +#define LPFC_MBUF_POOL_SIZE 64 /* max elements in MBUF safety pool */ +#define LPFC_MEM_POOL_SIZE 64 /* max elements in non DMA safety + pool */ +#endif /* _H_LPFC_MEM */ --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_init.c 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_init.c 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,1536 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_init.c 1.183.1.2 2005/06/13 17:16:27EDT sf_support Exp $ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" +#include "lpfc_version.h" +#include "lpfc_compat.h" + +static int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *); +static void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *); +static int lpfc_post_rcv_buf(struct lpfc_hba *); +static int lpfc_rdrev_wd30 = 0; + +/************************************************************************/ +/* */ +/* lpfc_config_port_prep */ +/* This routine will do LPFC initialization prior to the */ +/* CONFIG_PORT mailbox command. This will be initialized */ +/* as a SLI layer callback routine. */ +/* This routine returns 0 on success or -ERESTART if it wants */ +/* the SLI layer to reset the HBA and try again. Any */ +/* other return value indicates an error. */ +/* */ +/************************************************************************/ +int +lpfc_config_port_prep(struct lpfc_hba * phba) +{ + lpfc_vpd_t *vp = &phba->vpd; + int i = 0; + LPFC_MBOXQ_t *pmb; + MAILBOX_t *mb; + uint32_t *lpfc_vpd_data = 0; + uint16_t offset = 0; + + /* Get a Mailbox buffer to setup mailbox commands for HBA + initialization */ + pmb = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC); + if (!pmb) { + phba->hba_state = LPFC_HBA_ERROR; + return -ENOMEM; + } + + mb = &pmb->mb; + phba->hba_state = LPFC_INIT_MBX_CMDS; + + /* special handling for LC HBAs */ + if (lpfc_is_LC_HBA(phba->pcidev->device)) { + char licensed[56] = + "key unlock for use with gnu public licensed code only\0"; + uint32_t *ptext = (uint32_t *) licensed; + + for (i = 0; i < 56; i += sizeof (uint32_t), ptext++) + *ptext = cpu_to_be32(*ptext); + + /* Setup and issue mailbox READ NVPARAMS command */ + lpfc_read_nv(phba, pmb); + memset((char*)mb->un.varRDnvp.rsvd3, 0, + sizeof (mb->un.varRDnvp.rsvd3)); + memcpy((char*)mb->un.varRDnvp.rsvd3, licensed, + sizeof (licensed)); + + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + /* Adapter initialization error, mbxCmd + READ_NVPARM, mbxStatus */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_MBOX, + "%d:0324 Config Port initialization " + "error, mbxCmd x%x READ_NVPARM, " + "mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + mempool_free( pmb, phba->mbox_mem_pool); + return -ERESTART; + } + memcpy(phba->wwnn, (char *)mb->un.varRDnvp.nodename, + sizeof (mb->un.varRDnvp.nodename)); + } + + /* Setup and issue mailbox READ REV command */ + lpfc_read_rev(phba, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + /* Adapter failed to init, mbxCmd READ_REV, mbxStatus + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0439 Adapter failed to init, mbxCmd x%x " + "READ_REV, mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + mempool_free( pmb, phba->mbox_mem_pool); + return -ERESTART; + } + + /* The HBA's current state is provided by the ProgType and rr fields. + * Read and check the value of these fields before continuing to config + * this port. + */ + if (mb->un.varRdRev.rr == 0 || mb->un.varRdRev.un.b.ProgType != 2) { + /* Old firmware */ + vp->rev.rBit = 0; + /* Adapter failed to init, mbxCmd READ_REV detected + outdated firmware */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0440 Adapter failed to init, mbxCmd x%x " + "READ_REV detected outdated firmware" + "Data: x%x\n", + phba->brd_no, + mb->mbxCommand, 0); + mempool_free(pmb, phba->mbox_mem_pool); + return -ERESTART; + } else { + vp->rev.rBit = 1; + vp->rev.sli1FwRev = mb->un.varRdRev.sli1FwRev; + memcpy(vp->rev.sli1FwName, + (char*)mb->un.varRdRev.sli1FwName, 16); + vp->rev.sli2FwRev = mb->un.varRdRev.sli2FwRev; + memcpy(vp->rev.sli2FwName, + (char *)mb->un.varRdRev.sli2FwName, 16); + } + + /* Save information as VPD data */ + vp->rev.biuRev = mb->un.varRdRev.biuRev; + vp->rev.smRev = mb->un.varRdRev.smRev; + vp->rev.smFwRev = mb->un.varRdRev.un.smFwRev; + vp->rev.endecRev = mb->un.varRdRev.endecRev; + vp->rev.fcphHigh = mb->un.varRdRev.fcphHigh; + vp->rev.fcphLow = mb->un.varRdRev.fcphLow; + vp->rev.feaLevelHigh = mb->un.varRdRev.feaLevelHigh; + vp->rev.feaLevelLow = mb->un.varRdRev.feaLevelLow; + vp->rev.postKernRev = mb->un.varRdRev.postKernRev; + vp->rev.opFwRev = mb->un.varRdRev.opFwRev; + lpfc_rdrev_wd30 = mb->un.varWords[30]; + + if (lpfc_is_LC_HBA(phba->pcidev->device)) + memcpy(phba->RandomData, (char *)&mb->un.varWords[24], + sizeof (phba->RandomData)); + + /* Get the default values for Model Name and Description */ + lpfc_get_hba_model_desc(phba, phba->ModelName, phba->ModelDesc); + + /* Get adapter VPD information */ + pmb->context2 = kmalloc(DMP_RSP_SIZE, GFP_ATOMIC); + lpfc_vpd_data = kmalloc(DMP_VPD_SIZE, GFP_ATOMIC); + + do { + lpfc_dump_mem(phba, pmb, offset); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + /* Let it go through even if failed. */ + /* Adapter failed to init, mbxCmd DUMP VPD, + mbxStatus */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_INIT, + "%d:0441 VPD not present on adapter, mbxCmd " + "x%x DUMP VPD, mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + kfree(lpfc_vpd_data); + lpfc_vpd_data = 0; + break; + } + + lpfc_sli_pcimem_bcopy((uint32_t *)pmb->context2, + (uint32_t*)((uint8_t*)lpfc_vpd_data + offset), + mb->un.varDmp.word_cnt); + + offset += mb->un.varDmp.word_cnt; + } while (mb->un.varDmp.word_cnt); + + lpfc_parse_vpd(phba, (uint8_t *)lpfc_vpd_data); + + if(pmb->context2) + kfree(pmb->context2); + if (lpfc_vpd_data) + kfree(lpfc_vpd_data); + + pmb->context2 = 0; + mempool_free(pmb, phba->mbox_mem_pool); + return 0; +} + +/************************************************************************/ +/* */ +/* lpfc_config_port_post */ +/* This routine will do LPFC initialization after the */ +/* CONFIG_PORT mailbox command. This will be initialized */ +/* as a SLI layer callback routine. */ +/* This routine returns 0 on success. Any other return value */ +/* indicates an error. */ +/* */ +/************************************************************************/ +int +lpfc_config_port_post(struct lpfc_hba * phba) +{ + LPFC_MBOXQ_t *pmb; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_sli *psli = &phba->sli; + uint32_t status, timeout; + int i, j, flogi_sent; + unsigned long isr_cnt, clk_cnt; + + + /* Get a Mailbox buffer to setup mailbox commands for HBA + initialization */ + pmb = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC); + if (!pmb) { + phba->hba_state = LPFC_HBA_ERROR; + return -ENOMEM; + } + mb = &pmb->mb; + + /* Setup link timers */ + lpfc_config_link(phba, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0447 Adapter failed init, mbxCmd x%x " + "CONFIG_LINK mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -EIO; + } + + /* Get login parameters for NID. */ + lpfc_read_sparam(phba, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0448 Adapter failed init, mbxCmd x%x " + "READ_SPARM mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + phba->hba_state = LPFC_HBA_ERROR; + mp = (struct lpfc_dmabuf *) pmb->context1; + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + return -EIO; + } + + mp = (struct lpfc_dmabuf *) pmb->context1; + + memcpy(&phba->fc_sparam, mp->virt, sizeof (struct serv_parm)); + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + pmb->context1 = NULL; + + memcpy(&phba->fc_nodename, &phba->fc_sparam.nodeName, + sizeof (struct lpfc_name)); + memcpy(&phba->fc_portname, &phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + /* If no serial number in VPD data, use low 6 bytes of WWNN */ + /* This should be consolidated into parse_vpd ? - mr */ + if (phba->SerialNumber[0] == 0) { + uint8_t *outptr; + + outptr = (uint8_t *) & phba->fc_nodename.IEEE[0]; + for (i = 0; i < 12; i++) { + status = *outptr++; + j = ((status & 0xf0) >> 4); + if (j <= 9) + phba->SerialNumber[i] = + (char)((uint8_t) 0x30 + (uint8_t) j); + else + phba->SerialNumber[i] = + (char)((uint8_t) 0x61 + (uint8_t) (j - 10)); + i++; + j = (status & 0xf); + if (j <= 9) + phba->SerialNumber[i] = + (char)((uint8_t) 0x30 + (uint8_t) j); + else + phba->SerialNumber[i] = + (char)((uint8_t) 0x61 + (uint8_t) (j - 10)); + } + } + + /* This should turn on DELAYED ABTS for ELS timeouts */ + lpfc_set_slim(phba, pmb, 0x052198, 0x1); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -EIO; + } + + + lpfc_read_config(phba, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0453 Adapter failed to init, mbxCmd x%x " + "READ_CONFIG, mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -EIO; + } + + /* Reset the DFT_HBA_Q_DEPTH to the max xri */ + if (phba->cfg_hba_queue_depth > (mb->un.varRdConfig.max_xri+1)) + phba->cfg_hba_queue_depth = + mb->un.varRdConfig.max_xri + 1; + + phba->lmt = mb->un.varRdConfig.lmt; + /* HBA is not 4GB capable, or HBA is not 2GB capable, + don't let link speed ask for it */ + if ((((phba->lmt & LMT_4250_10bit) != LMT_4250_10bit) && + (phba->cfg_link_speed > LINK_SPEED_2G)) || + (((phba->lmt & LMT_2125_10bit) != LMT_2125_10bit) && + (phba->cfg_link_speed > LINK_SPEED_1G))) { + /* Reset link speed to auto. 1G/2GB HBA cfg'd for 4G */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_LINK_EVENT, + "%d:1302 Invalid speed for this board: " + "Reset link speed to auto: x%x\n", + phba->brd_no, + phba->cfg_link_speed); + phba->cfg_link_speed = LINK_SPEED_AUTO; + } + + if (!phba->intr_inited) { + /* Add our interrupt routine to kernel's interrupt chain & + enable it */ + + if (request_irq(phba->pcidev->irq, + lpfc_intr_handler, + SA_SHIRQ, + LPFC_DRIVER_NAME, + phba) != 0) { + /* Enable interrupt handler failed */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0451 Enable interrupt handler " + "failed\n", + phba->brd_no); + phba->hba_state = LPFC_HBA_ERROR; + mempool_free(pmb, phba->mbox_mem_pool); + return -EIO; + } + phba->intr_inited = + (HC_MBINT_ENA | HC_ERINT_ENA | HC_LAINT_ENA); + } + + phba->hba_state = LPFC_LINK_DOWN; + + /* Only process IOCBs on ring 0 till hba_state is READY */ + if (psli->ring[psli->ip_ring].cmdringaddr) + psli->ring[psli->ip_ring].flag |= LPFC_STOP_IOCB_EVENT; + if (psli->ring[psli->fcp_ring].cmdringaddr) + psli->ring[psli->fcp_ring].flag |= LPFC_STOP_IOCB_EVENT; + if (psli->ring[psli->next_ring].cmdringaddr) + psli->ring[psli->next_ring].flag |= LPFC_STOP_IOCB_EVENT; + + /* Post receive buffers for desired rings */ + lpfc_post_rcv_buf(phba); + + /* Enable appropriate host interrupts */ + status = readl(phba->HCregaddr); + status |= phba->intr_inited; + if (psli->sliinit.num_rings > 0) + status |= HC_R0INT_ENA; + if (psli->sliinit.num_rings > 1) + status |= HC_R1INT_ENA; + if (psli->sliinit.num_rings > 2) + status |= HC_R2INT_ENA; + if (psli->sliinit.num_rings > 3) + status |= HC_R3INT_ENA; + + writel(status, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + /* Setup and issue mailbox INITIALIZE LINK command */ + lpfc_init_link(phba, pmb, phba->cfg_topology, + phba->cfg_link_speed); + + isr_cnt = psli->slistat.sliIntr; + clk_cnt = jiffies; + + pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl; + if (lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT) != MBX_SUCCESS) { + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0454 Adapter failed to init, mbxCmd x%x " + "INIT_LINK, mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + + /* Clear all interrupt enable conditions */ + writel(0, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + /* Clear all pending interrupts */ + writel(0xffffffff, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + + free_irq(phba->pcidev->irq, phba); + phba->hba_state = LPFC_HBA_ERROR; + mempool_free(pmb, phba->mbox_mem_pool); + return -EIO; + } + /* MBOX buffer will be freed in mbox compl */ + + /* + * Setup the ring 0 (els) timeout handler + */ + timeout = phba->fc_ratov << 1; + + phba->els_tmofunc.expires = jiffies + HZ * timeout; + add_timer(&phba->els_tmofunc); + + phba->fc_prevDID = Mask_DID; + flogi_sent = 0; + i = 0; + while ((phba->hba_state != LPFC_HBA_READY) || + (phba->num_disc_nodes) || (phba->fc_prli_sent) || + ((phba->fc_map_cnt == 0) && (i<2)) || + (psli->sliinit.sli_flag & LPFC_SLI_MBOX_ACTIVE)) { + /* Check every second for 30 retries. */ + i++; + if (i > 30) { + break; + } + if ((i >= 15) && (phba->hba_state <= LPFC_LINK_DOWN)) { + /* The link is down. Set linkdown timeout */ + break; + } + + /* Delay for 1 second to give discovery time to complete. */ + for (j = 0; j < 20; j++) { + /* On some systems, the driver's attach/detect routines + * are uninterruptible. Since the driver cannot predict + * when this is true, just manually call the ISR every + * 50 ms to service any interrupts. + */ + msleep(50); + if (isr_cnt == psli->slistat.sliIntr) { + lpfc_sli_intr(phba); + isr_cnt = psli->slistat.sliIntr; + } + } + isr_cnt = psli->slistat.sliIntr; + + if (clk_cnt == jiffies) { + /* REMOVE: IF THIS HAPPENS, SYSTEM CLOCK IS NOT RUNNING. + * WE HAVE TO MANUALLY CALL OUR TIMEOUT ROUTINES. + */ + clk_cnt = jiffies; + } + } + + /* Since num_disc_nodes keys off of PLOGI, delay a bit to let + * any potential PRLIs to flush thru the SLI sub-system. + */ + msleep(50); + if (isr_cnt == psli->slistat.sliIntr) { + lpfc_sli_intr(phba); + } + + return (0); +} + +/************************************************************************/ +/* */ +/* lpfc_hba_down_prep */ +/* This routine will do LPFC uninitialization before the */ +/* HBA is reset when bringing down the SLI Layer. This will be */ +/* initialized as a SLI layer callback routine. */ +/* This routine returns 0 on success. Any other return value */ +/* indicates an error. */ +/* */ +/************************************************************************/ +int +lpfc_hba_down_prep(struct lpfc_hba * phba) +{ + /* Disable interrupts */ + writel(0, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + /* Cleanup potential discovery resources */ + lpfc_els_flush_rscn(phba); + lpfc_els_flush_cmd(phba); + lpfc_disc_flush_list(phba); + + return (0); +} + +/************************************************************************/ +/* */ +/* lpfc_handle_eratt */ +/* This routine will handle processing a Host Attention */ +/* Error Status event. This will be initialized */ +/* as a SLI layer callback routine. */ +/* */ +/************************************************************************/ +void +lpfc_handle_eratt(struct lpfc_hba * phba, uint32_t status) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *iocb, *next_iocb; + IOCB_t *icmd = NULL, *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + volatile uint32_t status1, status2; + void *from_slim; + unsigned long iflag; + + psli = &phba->sli; + from_slim = ((uint8_t *)phba->MBslimaddr + 0xa8); + status1 = readl( from_slim); + from_slim = ((uint8_t *)phba->MBslimaddr + 0xac); + status2 = readl( from_slim); + + if (status & HS_FFER6) { + /* Re-establishing Link */ + spin_lock_irqsave(phba->host->host_lock, iflag); + lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT, + "%d:1301 Re-establishing Link " + "Data: x%x x%x x%x\n", + phba->brd_no, status, status1, status2); + phba->fc_flag |= FC_ESTABLISH_LINK; + + /* + * Firmware stops when it triggled erratt with HS_FFER6. + * That could cause the I/Os dropped by the firmware. + * Error iocb (I/O) on txcmplq and let the SCSI layer + * retry it after re-establishing link. + */ + pring = &psli->ring[psli->fcp_ring]; + + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, + list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *)(iocb->context1); + if (lpfc_cmd == 0) { + continue; + } + + /* Clear fast_lookup entry */ + if (cmd->ulpIoTag && + (cmd->ulpIoTag < + psli->sliinit.ringinit[pring->ringno].fast_iotag)) + *(pring->fast_lookup + cmd->ulpIoTag) = NULL; + + list_del(&iocb->list); + pring->txcmplq_cnt--; + + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl)(phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + + /* + * There was a firmware error. Take the hba offline and then + * attempt to restart it. + */ + spin_unlock_irqrestore(phba->host->host_lock, iflag); + lpfc_offline(phba); + if (lpfc_online(phba) == 0) { /* Initialize the HBA */ + mod_timer(&phba->fc_estabtmo, jiffies + HZ * 60); + return; + } + } else { + /* The if clause above forces this code path when the status + * failure is a value other than FFER6. Do not call the offline + * twice. This is the adapter hardware error path. + */ + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0457 Adapter Hardware Error " + "Data: x%x x%x x%x\n", + phba->brd_no, status, status1, status2); + + lpfc_offline(phba); + + /* + * Restart all traffic to this host. Since the fc_transport + * block functions (future) were not called in lpfc_offline, + * don't call them here. + */ + scsi_unblock_requests(phba->host); + } + return; +} + +/************************************************************************/ +/* */ +/* lpfc_handle_latt */ +/* This routine will handle processing a Host Attention */ +/* Link Status event. This will be initialized */ +/* as a SLI layer callback routine. */ +/* */ +/************************************************************************/ +void +lpfc_handle_latt(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *pmb; + volatile uint32_t control; + unsigned long iflag; + + + spin_lock_irqsave(phba->host->host_lock, iflag); + + /* called from host_interrupt, to process LATT */ + psli = &phba->sli; + psli->slistat.linkEvent++; + + /* Get a buffer which will be used for mailbox commands */ + if ((pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + if (lpfc_read_la(phba, pmb) == 0) { + pmb->mbox_cmpl = lpfc_mbx_cmpl_read_la; + if (lpfc_sli_issue_mbox + (phba, pmb, (MBX_NOWAIT | MBX_STOP_IOCB)) + != MBX_NOT_FINISHED) { + /* Turn off Link Attention interrupts until + CLEAR_LA done */ + psli->sliinit.sli_flag &= ~LPFC_PROCESS_LA; + control = readl(phba->HCregaddr); + control &= ~HC_LAINT_ENA; + writel(control, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + /* Clear Link Attention in HA REG */ + writel(HA_LATT, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + return; + } else { + mempool_free(pmb, phba->mbox_mem_pool); + } + } else { + mempool_free(pmb, phba->mbox_mem_pool); + } + } + + /* Clear Link Attention in HA REG */ + writel(HA_LATT, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + lpfc_linkdown(phba); + phba->hba_state = LPFC_HBA_ERROR; + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + +/************************************************************************/ +/* */ +/* lpfc_parse_vpd */ +/* This routine will parse the VPD data */ +/* */ +/************************************************************************/ +static int +lpfc_parse_vpd(struct lpfc_hba * phba, uint8_t * vpd) +{ + uint8_t lenlo, lenhi; + uint32_t Length; + int i, j; + int finished = 0; + int index = 0; + + if(!vpd) + return 0; + + /* Vital Product */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_INIT, + "%d:0455 Vital Product Data: x%x x%x x%x x%x\n", + phba->brd_no, + (uint32_t) vpd[0], (uint32_t) vpd[1], (uint32_t) vpd[2], + (uint32_t) vpd[3]); + do { + switch (vpd[index]) { + case 0x82: + index += 1; + lenlo = vpd[index]; + index += 1; + lenhi = vpd[index]; + index += 1; + i = ((((unsigned short)lenhi) << 8) + lenlo); + index += i; + break; + case 0x90: + index += 1; + lenlo = vpd[index]; + index += 1; + lenhi = vpd[index]; + index += 1; + Length = ((((unsigned short)lenhi) << 8) + lenlo); + + while (Length > 0) { + /* Look for Serial Number */ + if ((vpd[index] == 'S') && (vpd[index+1] == 'N')) { + index += 2; + i = vpd[index]; + index += 1; + j = 0; + Length -= (3+i); + while(i--) { + phba->SerialNumber[j++] = vpd[index++]; + if(j == 31) + break; + } + phba->SerialNumber[j] = 0; + continue; + } + else if ((vpd[index] == 'V') && (vpd[index+1] == '1')) { + phba->vpd_flag |= VPD_MODEL_DESC; + index += 2; + i = vpd[index]; + index += 1; + j = 0; + Length -= (3+i); + while(i--) { + phba->ModelDesc[j++] = vpd[index++]; + if(j == 255) + break; + } + phba->ModelDesc[j] = 0; + continue; + } + else if ((vpd[index] == 'V') && (vpd[index+1] == '2')) { + phba->vpd_flag |= VPD_MODEL_NAME; + index += 2; + i = vpd[index]; + index += 1; + j = 0; + Length -= (3+i); + while(i--) { + phba->ModelName[j++] = vpd[index++]; + if(j == 79) + break; + } + phba->ModelName[j] = 0; + continue; + } + else if ((vpd[index] == 'V') && (vpd[index+1] == '3')) { + phba->vpd_flag |= VPD_PROGRAM_TYPE; + index += 2; + i = vpd[index]; + index += 1; + j = 0; + Length -= (3+i); + while(i--) { + phba->ProgramType[j++] = vpd[index++]; + if(j == 255) + break; + } + phba->ProgramType[j] = 0; + continue; + } + else if ((vpd[index] == 'V') && (vpd[index+1] == '4')) { + phba->vpd_flag |= VPD_PORT; + index += 2; + i = vpd[index]; + index += 1; + j = 0; + Length -= (3+i); + while(i--) { + phba->Port[j++] = vpd[index++]; + if(j == 19) + break; + } + phba->Port[j] = 0; + continue; + } + else { + index += 2; + i = vpd[index]; + index += 1; + index += i; + Length -= (3 + i); + } + } + finished = 0; + break; + case 0x78: + finished = 1; + break; + default: + index ++; + break; + } + } while (!finished && (index < 108)); + + return(1); +} + +static void +lpfc_get_hba_model_desc(struct lpfc_hba * phba, uint8_t * mdp, uint8_t * descp) +{ + lpfc_vpd_t *vp; + uint32_t id; + uint8_t hdrtype; + char str[16]; + + vp = &phba->vpd; + pci_read_config_dword(phba->pcidev, PCI_VENDOR_ID, &id); + pci_read_config_byte(phba->pcidev, PCI_HEADER_TYPE, &hdrtype); + + switch ((id >> 16) & 0xffff) { + case PCI_DEVICE_ID_SUPERFLY: + if (vp->rev.biuRev >= 1 && vp->rev.biuRev <= 3) + strcpy(str, "LP7000 1"); + else + strcpy(str, "LP7000E 1"); + break; + case PCI_DEVICE_ID_DRAGONFLY: + strcpy(str, "LP8000 1"); + break; + case PCI_DEVICE_ID_CENTAUR: + if (FC_JEDEC_ID(vp->rev.biuRev) == CENTAUR_2G_JEDEC_ID) + strcpy(str, "LP9002 2"); + else + strcpy(str, "LP9000 1"); + break; + case PCI_DEVICE_ID_RFLY: + strcpy(str, "LP952 2"); + break; + case PCI_DEVICE_ID_PEGASUS: + strcpy(str, "LP9802 2"); + break; + case PCI_DEVICE_ID_THOR: + if (hdrtype == 0x80) + strcpy(str, "LP10000DC 2"); + else + strcpy(str, "LP10000 2"); + break; + case PCI_DEVICE_ID_VIPER: + strcpy(str, "LPX1000 10"); + break; + case PCI_DEVICE_ID_PFLY: + strcpy(str, "LP982 2"); + break; + case PCI_DEVICE_ID_TFLY: + if (hdrtype == 0x80) + strcpy(str, "LP1050DC 2"); + else + strcpy(str, "LP1050 2"); + break; + case PCI_DEVICE_ID_HELIOS: + if (hdrtype == 0x80) + strcpy(str, "LP11002 4"); + else + strcpy(str, "LP11000 4"); + break; + case PCI_DEVICE_ID_BMID: + strcpy(str, "LP1150 4"); + break; + case PCI_DEVICE_ID_BSMB: + strcpy(str, "LP111 4"); + break; + case PCI_DEVICE_ID_ZEPHYR: + if (hdrtype == 0x80) + strcpy(str, "LPe11002 4"); + else + strcpy(str, "LPe11000 4"); + break; + case PCI_DEVICE_ID_ZMID: + strcpy(str, "LPe1150 4"); + break; + case PCI_DEVICE_ID_ZSMB: + strcpy(str, "LPe111 4"); + break; + case PCI_DEVICE_ID_LP101: + strcpy(str, "LP101 2"); + break; + case PCI_DEVICE_ID_LP10000S: + strcpy(str, "LP10000-S 2"); + break; + } + if (mdp) + sscanf(str, "%s", mdp); + if (descp) + sprintf(descp, "Emulex LightPulse %s Gigabit PCI Fibre " + "Channel Adapter", str); +} + +/**************************************************/ +/* lpfc_post_buffer */ +/* */ +/* This routine will post count buffers to the */ +/* ring with the QUE_RING_BUF_CN command. This */ +/* allows 3 buffers / command to be posted. */ +/* Returns the number of buffers NOT posted. */ +/**************************************************/ +int +lpfc_post_buffer(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, int cnt, + int type) +{ + IOCB_t *icmd; + struct lpfc_iocbq *iocb; + struct lpfc_dmabuf *mp1, *mp2; + + cnt += pring->missbufcnt; + + /* While there are buffers to post */ + while (cnt > 0) { + /* Allocate buffer for command iocb */ + if ((iocb = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC)) + == 0) { + pring->missbufcnt = cnt; + return (cnt); + } + memset(iocb, 0, sizeof (struct lpfc_iocbq)); + icmd = &iocb->iocb; + + /* 2 buffers can be posted per command */ + /* Allocate buffer to post */ + mp1 = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (mp1) + mp1->virt = lpfc_mbuf_alloc(phba, MEM_PRI, + &mp1->phys); + if (mp1 == 0 || mp1->virt == 0) { + if (mp1) + kfree(mp1); + + mempool_free( iocb, phba->iocb_mem_pool); + pring->missbufcnt = cnt; + return (cnt); + } + + INIT_LIST_HEAD(&mp1->list); + /* Allocate buffer to post */ + if (cnt > 1) { + mp2 = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (mp2) + mp2->virt = lpfc_mbuf_alloc(phba, MEM_PRI, + &mp2->phys); + if (mp2 == 0 || mp2->virt == 0) { + if (mp2) + kfree(mp2); + lpfc_mbuf_free(phba, mp1->virt, mp1->phys); + kfree(mp1); + mempool_free( iocb, phba->iocb_mem_pool); + pring->missbufcnt = cnt; + return (cnt); + } + + INIT_LIST_HEAD(&mp2->list); + } else { + mp2 = NULL; + } + + icmd->un.cont64[0].addrHigh = putPaddrHigh(mp1->phys); + icmd->un.cont64[0].addrLow = putPaddrLow(mp1->phys); + icmd->un.cont64[0].tus.f.bdeSize = FCELSSIZE; + icmd->ulpBdeCount = 1; + cnt--; + if (mp2) { + icmd->un.cont64[1].addrHigh = putPaddrHigh(mp2->phys); + icmd->un.cont64[1].addrLow = putPaddrLow(mp2->phys); + icmd->un.cont64[1].tus.f.bdeSize = FCELSSIZE; + cnt--; + icmd->ulpBdeCount = 2; + } + + icmd->ulpCommand = CMD_QUE_RING_BUF64_CN; + icmd->ulpIoTag = lpfc_sli_next_iotag(phba, pring); + icmd->ulpLe = 1; + + if (lpfc_sli_issue_iocb(phba, pring, iocb, 0) == IOCB_ERROR) { + lpfc_mbuf_free(phba, mp1->virt, mp1->phys); + kfree(mp1); + cnt++; + if (mp2) { + lpfc_mbuf_free(phba, mp2->virt, mp2->phys); + kfree(mp2); + cnt++; + } + mempool_free( iocb, phba->iocb_mem_pool); + pring->missbufcnt = cnt; + return (cnt); + } + lpfc_sli_ringpostbuf_put(phba, pring, mp1); + if (mp2) { + lpfc_sli_ringpostbuf_put(phba, pring, mp2); + } + } + pring->missbufcnt = 0; + return (0); +} + +/************************************************************************/ +/* */ +/* lpfc_post_rcv_buf */ +/* This routine post initial rcv buffers to the configured rings */ +/* */ +/************************************************************************/ +static int +lpfc_post_rcv_buf(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli = &phba->sli; + + /* Ring 0, ELS / CT buffers */ + lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], LPFC_BUF_RING0, 1); + /* Ring 2 - FCP no buffers needed */ + + return 0; +} + +#define S(N,V) (((V)<<(N))|((V)>>(32-(N)))) + +/************************************************************************/ +/* */ +/* lpfc_sha_init */ +/* */ +/************************************************************************/ +static void +lpfc_sha_init(uint32_t * HashResultPointer) +{ + HashResultPointer[0] = 0x67452301; + HashResultPointer[1] = 0xEFCDAB89; + HashResultPointer[2] = 0x98BADCFE; + HashResultPointer[3] = 0x10325476; + HashResultPointer[4] = 0xC3D2E1F0; +} + +/************************************************************************/ +/* */ +/* lpfc_sha_iterate */ +/* */ +/************************************************************************/ +static void +lpfc_sha_iterate(uint32_t * HashResultPointer, uint32_t * HashWorkingPointer) +{ + int t; + uint32_t TEMP; + uint32_t A, B, C, D, E; + t = 16; + do { + HashWorkingPointer[t] = + S(1, + HashWorkingPointer[t - 3] ^ HashWorkingPointer[t - + 8] ^ + HashWorkingPointer[t - 14] ^ HashWorkingPointer[t - 16]); + } while (++t <= 79); + t = 0; + A = HashResultPointer[0]; + B = HashResultPointer[1]; + C = HashResultPointer[2]; + D = HashResultPointer[3]; + E = HashResultPointer[4]; + + do { + if (t < 20) { + TEMP = ((B & C) | ((~B) & D)) + 0x5A827999; + } else if (t < 40) { + TEMP = (B ^ C ^ D) + 0x6ED9EBA1; + } else if (t < 60) { + TEMP = ((B & C) | (B & D) | (C & D)) + 0x8F1BBCDC; + } else { + TEMP = (B ^ C ^ D) + 0xCA62C1D6; + } + TEMP += S(5, A) + E + HashWorkingPointer[t]; + E = D; + D = C; + C = S(30, B); + B = A; + A = TEMP; + } while (++t <= 79); + + HashResultPointer[0] += A; + HashResultPointer[1] += B; + HashResultPointer[2] += C; + HashResultPointer[3] += D; + HashResultPointer[4] += E; + +} + +/************************************************************************/ +/* */ +/* lpfc_challenge_key */ +/* */ +/************************************************************************/ +static void +lpfc_challenge_key(uint32_t * RandomChallenge, uint32_t * HashWorking) +{ + *HashWorking = (*RandomChallenge ^ *HashWorking); +} + +/************************************************************************/ +/* */ +/* lpfc_hba_init */ +/* */ +/************************************************************************/ +void +lpfc_hba_init(struct lpfc_hba *phba, uint32_t *hbainit) +{ + int t; + uint32_t *HashWorking; + uint32_t *pwwnn = phba->wwnn; + + HashWorking = kmalloc(80 * sizeof(uint32_t), GFP_ATOMIC); + if (!HashWorking) + return; + + memset(HashWorking, 0, (80 * sizeof(uint32_t))); + HashWorking[0] = HashWorking[78] = *pwwnn++; + HashWorking[1] = HashWorking[79] = *pwwnn; + + for (t = 0; t < 7; t++) + lpfc_challenge_key(phba->RandomData + t, HashWorking + t); + + lpfc_sha_init(hbainit); + lpfc_sha_iterate(hbainit, HashWorking); + kfree(HashWorking); +} + +static void +lpfc_consistent_bind_cleanup(struct lpfc_hba * phba) +{ + struct lpfc_bindlist *bdlp, *next_bdlp; + + list_for_each_entry_safe(bdlp, next_bdlp, + &phba->fc_nlpbind_list, nlp_listp) { + list_del(&bdlp->nlp_listp); + mempool_free( bdlp, phba->bind_mem_pool); + } + phba->fc_bind_cnt = 0; +} + +void +lpfc_cleanup(struct lpfc_hba * phba, uint32_t save_bind) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + + /* clean up phba - lpfc specific */ + lpfc_can_disctmo(phba); + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpunmap_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_unused_list, + nlp_listp) { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_plogi_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_reglogin_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_prli_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + if (save_bind == 0) { + lpfc_consistent_bind_cleanup(phba); + } + + INIT_LIST_HEAD(&phba->fc_nlpmap_list); + INIT_LIST_HEAD(&phba->fc_nlpunmap_list); + INIT_LIST_HEAD(&phba->fc_unused_list); + INIT_LIST_HEAD(&phba->fc_plogi_list); + INIT_LIST_HEAD(&phba->fc_adisc_list); + INIT_LIST_HEAD(&phba->fc_reglogin_list); + INIT_LIST_HEAD(&phba->fc_prli_list); + INIT_LIST_HEAD(&phba->fc_npr_list); + + phba->fc_map_cnt = 0; + phba->fc_unmap_cnt = 0; + phba->fc_plogi_cnt = 0; + phba->fc_adisc_cnt = 0; + phba->fc_reglogin_cnt = 0; + phba->fc_prli_cnt = 0; + phba->fc_npr_cnt = 0; + phba->fc_unused_cnt= 0; + return; +} + +void +lpfc_establish_link_tmo(unsigned long ptr) +{ + struct lpfc_hba *phba = (struct lpfc_hba *)ptr; + unsigned long iflag; + + spin_lock_irqsave(phba->host->host_lock, iflag); + + /* Re-establishing Link, timer expired */ + lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, + "%d:1300 Re-establishing Link, timer expired " + "Data: x%x x%x\n", + phba->brd_no, phba->fc_flag, phba->hba_state); + phba->fc_flag &= ~FC_ESTABLISH_LINK; + spin_unlock_irqrestore(phba->host->host_lock, iflag); +} + +int +lpfc_online(struct lpfc_hba * phba) +{ + if (!phba) + return 0; + + if (!(phba->fc_flag & FC_OFFLINE_MODE)) + return 0; + + lpfc_printf_log(phba, + KERN_WARNING, + LOG_INIT, + "%d:0458 Bring Adapter online\n", + phba->brd_no); + + if (!lpfc_sli_queue_setup(phba)) + return 1; + + if (lpfc_sli_hba_setup(phba)) /* Initialize the HBA */ + return 1; + + phba->fc_flag &= ~FC_OFFLINE_MODE; + + /* + * Restart all traffic to this host. Since the fc_transport block + * functions (future) were not called in lpfc_offline, don't call them + * here. + */ + scsi_unblock_requests(phba->host); + return 0; +} + +int +lpfc_offline(struct lpfc_hba * phba) +{ + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + unsigned long iflag; + int i = 0; + + if (!phba) + return 0; + + if (phba->fc_flag & FC_OFFLINE_MODE) + return 0; + + /* + * Don't call the fc_transport block api (future). The device is + * going offline and causing a timer to fire in the midlayer is + * unproductive. Just block all new requests until the driver + * comes back online. + */ + scsi_block_requests(phba->host); + psli = &phba->sli; + pring = &psli->ring[psli->fcp_ring]; + + spin_lock_irqsave(phba->host->host_lock, iflag); + lpfc_linkdown(phba); + spin_unlock_irqrestore(phba->host->host_lock, iflag); + + /* The linkdown event takes 30 seconds to timeout. */ + while (pring->txcmplq_cnt) { + mdelay(10); + if (i++ > 3000) + break; + } + + /* stop all timers associated with this hba */ + spin_lock_irqsave(phba->host->host_lock, iflag); + lpfc_stop_timer(phba); + phba->work_hba_events = 0; + spin_unlock_irqrestore(phba->host->host_lock, iflag); + + lpfc_printf_log(phba, + KERN_WARNING, + LOG_INIT, + "%d:0460 Bring Adapter offline\n", + phba->brd_no); + + /* Bring down the SLI Layer and cleanup. The HBA is offline + now. */ + spin_lock_irqsave(phba->host->host_lock, iflag); + lpfc_sli_hba_down(phba); + lpfc_cleanup(phba, 1); + phba->fc_flag |= FC_OFFLINE_MODE; + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return 0; +} + +/****************************************************************************** +* Function name : lpfc_scsi_free +* +* Description : Called from fc_detach to free scsi tgt / lun resources +* +******************************************************************************/ +int +lpfc_scsi_free(struct lpfc_hba * phba) +{ + struct lpfc_target *targetp; + int i; + + for (i = 0; i < MAX_FCP_TARGET; i++) { + targetp = phba->device_queue_hash[i]; + if (targetp) { + kfree(targetp); + phba->device_queue_hash[i] = NULL; + } + } + return 0; +} + +static void +lpfc_wakeup_event(struct lpfc_hba * phba, fcEVTHDR_t * ep) +{ + ep->e_mode &= ~E_SLEEPING_MODE; + switch (ep->e_mask) { + case FC_REG_LINK_EVENT: + wake_up_interruptible(&phba->linkevtwq); + break; + case FC_REG_RSCN_EVENT: + wake_up_interruptible(&phba->rscnevtwq); + break; + case FC_REG_CT_EVENT: + wake_up_interruptible(&phba->ctevtwq); + break; + } + return; +} + +int +lpfc_put_event(struct lpfc_hba * phba, uint32_t evcode, uint32_t evdata0, + void * evdata1, uint32_t evdata2, uint32_t evdata3) +{ + fcEVT_t *ep; + fcEVTHDR_t *ehp = phba->fc_evt_head; + int found = 0; + void *fstype = NULL; + struct lpfc_dmabuf *mp; + struct lpfc_sli_ct_request *ctp; + struct lpfc_hba_event *rec; + uint32_t evtype; + + switch (evcode) { + case HBA_EVENT_RSCN: + evtype = FC_REG_RSCN_EVENT; + break; + case HBA_EVENT_LINK_DOWN: + case HBA_EVENT_LINK_UP: + evtype = FC_REG_LINK_EVENT; + break; + default: + evtype = FC_REG_CT_EVENT; + } + + if (evtype == FC_REG_RSCN_EVENT || evtype == FC_REG_LINK_EVENT) { + rec = &phba->hbaevt[phba->hba_event_put]; + rec->fc_eventcode = evcode; + rec->fc_evdata1 = evdata0; + rec->fc_evdata2 = (uint32_t)(unsigned long)evdata1; + rec->fc_evdata3 = evdata2; + rec->fc_evdata4 = evdata3; + + phba->hba_event_put++; + if (phba->hba_event_put >= MAX_HBAEVT) + phba->hba_event_put = 0; + + if (phba->hba_event_put == phba->hba_event_get) { + phba->hba_event_missed++; + phba->hba_event_get++; + if (phba->hba_event_get >= MAX_HBAEVT) + phba->hba_event_get = 0; + } + } + + if (evtype == FC_REG_CT_EVENT) { + mp = (struct lpfc_dmabuf *) evdata1; + ctp = (struct lpfc_sli_ct_request *) mp->virt; + fstype = (void *)(ulong) (ctp->FsType); + } + + while (ehp && ((ehp->e_mask != evtype) || (ehp->e_type != fstype))) + ehp = (fcEVTHDR_t *) ehp->e_next_header; + + if (!ehp) + return (0); + + ep = ehp->e_head; + + while (ep && !(found)) { + if (ep->evt_sleep) { + switch (evtype) { + case FC_REG_CT_EVENT: + if ((ep->evt_type == + (void *)(ulong) FC_FSTYPE_ALL) + || (ep->evt_type == fstype)) { + found++; + ep->evt_data0 = evdata0; /* tag */ + ep->evt_data1 = evdata1; /* buffer + ptr */ + ep->evt_data2 = evdata2; /* count */ + ep->evt_sleep = 0; + if (ehp->e_mode & E_SLEEPING_MODE) { + ehp->e_flag |= + E_GET_EVENT_ACTIVE; + lpfc_wakeup_event(phba, ehp); + } + /* For FC_REG_CT_EVENT just give it to + first one found */ + } + break; + default: + found++; + ep->evt_data0 = evdata0; + ep->evt_data1 = evdata1; + ep->evt_data2 = evdata2; + ep->evt_sleep = 0; + if ((ehp->e_mode & E_SLEEPING_MODE) + && !(ehp->e_flag & E_GET_EVENT_ACTIVE)) { + ehp->e_flag |= E_GET_EVENT_ACTIVE; + lpfc_wakeup_event(phba, ehp); + } + /* For all other events, give it to every one + waiting */ + break; + } + } + ep = ep->evt_next; + } + if (evtype == FC_REG_LINK_EVENT) + phba->nport_event_cnt++; + + return (found); +} + +int +lpfc_stop_timer(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli = &phba->sli; + + /* Instead of a timer, this has been converted to a + * deferred procedding list. + */ + while (!list_empty(&phba->freebufList)) { + struct lpfc_dmabuf *mp; + + mp = (struct lpfc_dmabuf *)(phba->freebufList.next); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + list_del(&mp->list); + kfree(mp); + } + } + + del_timer_sync(&phba->fc_estabtmo); + del_timer_sync(&phba->fc_disctmo); + del_timer_sync(&phba->fc_scantmo); + del_timer_sync(&phba->fc_fdmitmo); + del_timer_sync(&phba->els_tmofunc); + psli = &phba->sli; + del_timer_sync(&psli->mbox_tmo); + return(1); +} --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_scsiport.c 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_scsiport.c 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,1374 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_scsiport.c 1.231.2.8 2005/07/25 12:56:08EDT sf_support Exp $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lpfc_hw.h" +#include "lpfc_sli.h" +#include "lpfc_mem.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_logmsg.h" +#include "lpfc_fcp.h" +#include "lpfc_crtn.h" + +/* This routine allocates a scsi buffer, which contains all the necessary + * information needed to initiate a SCSI I/O. The non-DMAable region of + * the buffer contains the area to build the IOCB. The DMAable region contains + * the memory for the FCP CMND, FCP RSP, and the inital BPL. + * In addition to allocating memeory, the FCP CMND and FCP RSP BDEs are setup + * in the BPL and the BPL BDE is setup in the IOCB. + */ +struct lpfc_scsi_buf * +lpfc_get_scsi_buf(struct lpfc_hba * phba, int gfp_flags) +{ + struct lpfc_scsi_buf *psb; + struct ulp_bde64 *bpl; + IOCB_t *cmd; + uint8_t *ptr; + dma_addr_t pdma_phys; + + psb = mempool_alloc(phba->scsibuf_mem_pool, gfp_flags); + if (!psb) + return NULL; + + memset(psb, 0, sizeof (struct lpfc_scsi_buf)); + + /* Get a SCSI DMA extention for an I/O */ + /* + * The DMA buffer for struct fcp_cmnd, struct fcp_rsp and BPL use + * lpfc_scsi_dma_ext_pool with size LPFC_SCSI_DMA_EXT_SIZE + * + * + * The size of struct fcp_cmnd = 32 bytes. + * The size of struct fcp_rsp = 160 bytes. + * The size of struct ulp_bde64 = 12 bytes and driver can only + * support LPFC_SCSI_INITIAL_BPL_SIZE (3) S/G segments for scsi data. + * One struct ulp_bde64 is used for each of the struct fcp_cmnd and + * struct fcp_rsp + * + * Total usage for each I/O use 32 + 160 + (2 * 12) + + * (4 * 12) = 264 bytes. + */ + + INIT_LIST_HEAD(&psb->dma_ext.list); + + psb->dma_ext.virt = pci_pool_alloc(phba->lpfc_scsi_dma_ext_pool, + GFP_ATOMIC, &psb->dma_ext.phys); + if (!psb->dma_ext.virt) { + mempool_free(psb, phba->scsibuf_mem_pool); + return NULL; + } + + /* Save virtual ptrs to FCP Command, Response, and BPL */ + ptr = (uint8_t *) psb->dma_ext.virt; + + memset(ptr, 0, LPFC_SCSI_DMA_EXT_SIZE); + psb->fcp_cmnd = (struct fcp_cmnd *) ptr; + ptr += sizeof (struct fcp_cmnd); + psb->fcp_rsp = (struct fcp_rsp *) ptr; + ptr += (sizeof (struct fcp_rsp)); + psb->fcp_bpl = (struct ulp_bde64 *) ptr; + psb->scsi_hba = phba; + + /* Since this is for a FCP cmd, the first 2 BDEs in the BPL are always + * the FCP CMND and FCP RSP, so lets just set it up right here. + */ + bpl = psb->fcp_bpl; + /* ptr points to physical address of FCP CMD */ + pdma_phys = psb->dma_ext.phys; + bpl->addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys)); + bpl->addrLow = le32_to_cpu(putPaddrLow(pdma_phys)); + bpl->tus.f.bdeSize = sizeof (struct fcp_cmnd); + bpl->tus.f.bdeFlags = BUFF_USE_CMND; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + bpl++; + + /* Setup FCP RSP */ + pdma_phys += sizeof (struct fcp_cmnd); + bpl->addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys)); + bpl->addrLow = le32_to_cpu(putPaddrLow(pdma_phys)); + bpl->tus.f.bdeSize = sizeof (struct fcp_rsp); + bpl->tus.f.bdeFlags = (BUFF_USE_CMND | BUFF_USE_RCV); + bpl->tus.w = le32_to_cpu(bpl->tus.w); + bpl++; + + /* Since the IOCB for the FCP I/O is built into the struct + * lpfc_scsi_buf, lets setup what we can right here. + */ + pdma_phys += (sizeof (struct fcp_rsp)); + cmd = &psb->cur_iocbq.iocb; + cmd->un.fcpi64.bdl.ulpIoTag32 = 0; + cmd->un.fcpi64.bdl.addrHigh = putPaddrHigh(pdma_phys); + cmd->un.fcpi64.bdl.addrLow = putPaddrLow(pdma_phys); + cmd->un.fcpi64.bdl.bdeSize = (2 * sizeof (struct ulp_bde64)); + cmd->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDL; + cmd->ulpBdeCount = 1; + cmd->ulpClass = CLASS3; + + return (psb); +} + +void +lpfc_free_scsi_buf(struct lpfc_scsi_buf * psb) +{ + struct lpfc_hba *phba = psb->scsi_hba; + struct lpfc_dmabuf *pbpl, *next_bpl; + + /* + * There are only two special cases to consider. (1) the scsi command + * requested scatter-gather usage or (2) the scsi command allocated + * a request buffer, but did not request use_sg. There is a third + * case, but it does not require resource deallocation. + */ + + if ((psb->seg_cnt > 0) && (psb->pCmd->use_sg)) { + /* + * Since the segment count is nonzero, the scsi command + * requested scatter-gather usage and the driver allocated + * addition memory buffers to chain BPLs. Traverse this list + * and release those resource before freeing the parent + * structure. + */ + dma_unmap_sg(&phba->pcidev->dev, psb->pCmd->request_buffer, + psb->seg_cnt, psb->pCmd->sc_data_direction); + + list_for_each_entry_safe(pbpl, next_bpl, + &psb->dma_ext.list, list) { + lpfc_mbuf_free(phba, pbpl->virt, pbpl->phys); + list_del(&pbpl->list); + kfree(pbpl); + } + } else { + if ((psb->nonsg_phys) && (psb->pCmd->request_bufflen)) { + /* + * Since either the segment count or the use_sg + * value is zero, the scsi command did not request + * scatter-gather usage and no additional buffers were + * required. Just unmap the dma single resource. + */ + dma_unmap_single(&phba->pcidev->dev, psb->nonsg_phys, + psb->pCmd->request_bufflen, + psb->pCmd->sc_data_direction); + } + } + + /* + * Release the pci pool resource and clean up the scsi buffer. Neither + * are required now that the IO has completed. + */ + pci_pool_free(phba->lpfc_scsi_dma_ext_pool, psb->dma_ext.virt, + psb->dma_ext.phys); + mempool_free(psb, phba->scsibuf_mem_pool); +} + +static int +lpfc_os_prep_io(struct lpfc_hba * phba, struct lpfc_scsi_buf * lpfc_cmd) +{ + struct fcp_cmnd *fcp_cmnd; + struct ulp_bde64 *topbpl = NULL; + struct ulp_bde64 *bpl; + struct lpfc_dmabuf *bmp; + struct lpfc_dmabuf *head_bmp; + IOCB_t *cmd; + struct scsi_cmnd *cmnd; + struct scatterlist *sgel = NULL; + struct scatterlist *sgel_begin = NULL; + dma_addr_t physaddr; + uint32_t i; + uint32_t num_bmps = 1, num_bde = 0, max_bde; + uint16_t use_sg; + int datadir; + int dma_error; + + bpl = lpfc_cmd->fcp_bpl; + fcp_cmnd = lpfc_cmd->fcp_cmnd; + + bpl += 2; /* Bump past FCP CMND and FCP RSP */ + max_bde = LPFC_SCSI_INITIAL_BPL_SIZE - 1; + + cmnd = lpfc_cmd->pCmd; + cmd = &lpfc_cmd->cur_iocbq.iocb; + + /* These are needed if we chain BPLs */ + head_bmp = &(lpfc_cmd->dma_ext); + use_sg = cmnd->use_sg; + + /* + * Fill in the FCP CMND + */ + memcpy(&fcp_cmnd->fcpCdb[0], cmnd->cmnd, 16); + + if (cmnd->device->tagged_supported) { + switch (cmnd->tag) { + case HEAD_OF_QUEUE_TAG: + fcp_cmnd->fcpCntl1 = HEAD_OF_Q; + break; + case ORDERED_QUEUE_TAG: + fcp_cmnd->fcpCntl1 = ORDERED_Q; + break; + default: + fcp_cmnd->fcpCntl1 = SIMPLE_Q; + break; + } + } else { + fcp_cmnd->fcpCntl1 = 0; + } + + datadir = cmnd->sc_data_direction; + + if (use_sg) { + /* + * Get a local pointer to the scatter-gather list. The + * scatter-gather list head must be preserved since + * sgel is incremented in the loop. The driver must store + * the segment count returned from pci_map_sg for calls to + * pci_unmap_sg later on because the use_sg field in the + * scsi_cmd is a count of physical memory pages, whereas the + * seg_cnt is a count of dma-mappings used by the MMIO to + * map the use_sg pages. They are not the same in most + * cases for those architectures that implement an MMIO. + */ + sgel = (struct scatterlist *)cmnd->request_buffer; + sgel_begin = sgel; + lpfc_cmd->seg_cnt = dma_map_sg(&phba->pcidev->dev, sgel, + use_sg, datadir); + + /* return error if we cannot map sg list */ + if (lpfc_cmd->seg_cnt == 0) + return 1; + + /* scatter-gather list case */ + for (i = 0; i < lpfc_cmd->seg_cnt; i++) { + /* Check to see if current BPL is full of BDEs */ + /* If this is last BDE and there is one left in */ + /* current BPL, use it. */ + if (num_bde == max_bde) { + bmp = kmalloc(sizeof (struct lpfc_dmabuf), + GFP_ATOMIC); + if (bmp == 0) { + goto error_out; + } + memset(bmp, 0, sizeof (struct lpfc_dmabuf)); + bmp->virt = + lpfc_mbuf_alloc(phba, 0, &bmp->phys); + if (!bmp->virt) { + kfree(bmp); + goto error_out; + } + max_bde = ((1024 / sizeof(struct ulp_bde64))-3); + /* Fill in continuation entry to next bpl */ + bpl->addrHigh = + le32_to_cpu(putPaddrHigh(bmp->phys)); + bpl->addrLow = + le32_to_cpu(putPaddrLow(bmp->phys)); + bpl->tus.f.bdeFlags = BPL64_SIZE_WORD; + num_bde++; + if (num_bmps == 1) { + cmd->un.fcpi64.bdl.bdeSize += (num_bde * + sizeof (struct ulp_bde64)); + } else { + topbpl->tus.f.bdeSize = (num_bde * + sizeof (struct ulp_bde64)); + topbpl->tus.w = + le32_to_cpu(topbpl->tus.w); + } + topbpl = bpl; + bpl = (struct ulp_bde64 *) bmp->virt; + list_add(&bmp->list, &head_bmp->list); + num_bde = 0; + num_bmps++; + } + + physaddr = sg_dma_address(sgel); + + bpl->addrLow = le32_to_cpu(putPaddrLow(physaddr)); + bpl->addrHigh = le32_to_cpu(putPaddrHigh(physaddr)); + bpl->tus.f.bdeSize = sg_dma_len(sgel); + if (datadir == DMA_TO_DEVICE) + bpl->tus.f.bdeFlags = 0; + else + bpl->tus.f.bdeFlags = BUFF_USE_RCV; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + bpl++; + sgel++; + num_bde++; + } /* end for loop */ + + if (datadir == DMA_TO_DEVICE) { + cmd->ulpCommand = CMD_FCP_IWRITE64_CR; + fcp_cmnd->fcpCntl3 = WRITE_DATA; + + phba->fc4OutputRequests++; + } else { + cmd->ulpCommand = CMD_FCP_IREAD64_CR; + cmd->ulpPU = PARM_READ_CHECK; + cmd->un.fcpi.fcpi_parm = cmnd->request_bufflen; + fcp_cmnd->fcpCntl3 = READ_DATA; + + phba->fc4InputRequests++; + } + } else if (cmnd->request_buffer && cmnd->request_bufflen) { + physaddr = dma_map_single(&phba->pcidev->dev, + cmnd->request_buffer, + cmnd->request_bufflen, + datadir); + dma_error = dma_mapping_error(physaddr); + if (dma_error){ + lpfc_printf_log(phba, KERN_ERR, LOG_FCP, + "%d:0718 Unable to dma_map_single " + "request_buffer: x%x\n", + phba->brd_no, dma_error); + return 1; + } + + /* no scatter-gather list case */ + lpfc_cmd->nonsg_phys = physaddr; + bpl->addrLow = le32_to_cpu(putPaddrLow(physaddr)); + bpl->addrHigh = le32_to_cpu(putPaddrHigh(physaddr)); + bpl->tus.f.bdeSize = cmnd->request_bufflen; + if (datadir == DMA_TO_DEVICE) { + cmd->ulpCommand = CMD_FCP_IWRITE64_CR; + fcp_cmnd->fcpCntl3 = WRITE_DATA; + bpl->tus.f.bdeFlags = 0; + + phba->fc4OutputRequests++; + } else { + cmd->ulpCommand = CMD_FCP_IREAD64_CR; + cmd->ulpPU = PARM_READ_CHECK; + cmd->un.fcpi.fcpi_parm = cmnd->request_bufflen; + fcp_cmnd->fcpCntl3 = READ_DATA; + bpl->tus.f.bdeFlags = BUFF_USE_RCV; + + phba->fc4InputRequests++; + } + bpl->tus.w = le32_to_cpu(bpl->tus.w); + num_bde = 1; + bpl++; + } else { + cmd->ulpCommand = CMD_FCP_ICMND64_CR; + cmd->un.fcpi.fcpi_parm = 0; + fcp_cmnd->fcpCntl3 = 0; + + phba->fc4ControlRequests++; + } + + bpl->addrHigh = 0; + bpl->addrLow = 0; + bpl->tus.w = 0; + if (num_bmps == 1) { + cmd->un.fcpi64.bdl.bdeSize += + (num_bde * sizeof (struct ulp_bde64)); + } else { + topbpl->tus.f.bdeSize = (num_bde * sizeof (struct ulp_bde64)); + topbpl->tus.w = le32_to_cpu(topbpl->tus.w); + } + cmd->ulpBdeCount = 1; + cmd->ulpLe = 1; /* Set the LE bit in the iocb */ + + /* set the Data Length field in the FCP CMND accordingly */ + fcp_cmnd->fcpDl = be32_to_cpu(cmnd->request_bufflen); + + return 0; + +error_out: + /* + * Allocation of a chained BPL failed, unmap the sg list and return + * error. This will ultimately cause lpfc_free_scsi_buf to be called + * which will handle the rest of the cleanup. Set seg_cnt back to zero + * to avoid double unmaps of the sg resources. + */ + dma_unmap_sg(&phba->pcidev->dev, sgel_begin, lpfc_cmd->seg_cnt, + datadir); + lpfc_cmd->seg_cnt = 0; + return 1; +} + +static void +lpfc_handle_fcp_err(struct lpfc_scsi_buf *lpfc_cmd) +{ + struct scsi_cmnd *cmnd = lpfc_cmd->pCmd; + struct fcp_cmnd *fcpcmd = lpfc_cmd->fcp_cmnd; + struct fcp_rsp *fcprsp = lpfc_cmd->fcp_rsp; + struct lpfc_hba *phba = lpfc_cmd->scsi_hba; + uint32_t fcpi_parm = lpfc_cmd->cur_iocbq.iocb.un.fcpi.fcpi_parm; + uint32_t resp_info = fcprsp->rspStatus2; + uint32_t scsi_status = fcprsp->rspStatus3; + uint32_t host_status = DID_OK; + uint32_t rsplen = 0; + + /* + * If this is a task management command, there is no + * scsi packet associated with this lpfc_cmd. The driver + * consumes it. + */ + if (fcpcmd->fcpCntl2) { + scsi_status = 0; + goto out; + } + + lpfc_printf_log(phba, KERN_WARNING, LOG_FCP, + "%d:0730 FCP command failed: RSP " + "Data: x%x x%x x%x x%x x%x x%x\n", + phba->brd_no, resp_info, scsi_status, + be32_to_cpu(fcprsp->rspResId), + be32_to_cpu(fcprsp->rspSnsLen), + be32_to_cpu(fcprsp->rspRspLen), + fcprsp->rspInfo3); + + if (resp_info & RSP_LEN_VALID) { + rsplen = be32_to_cpu(fcprsp->rspRspLen); + if ((rsplen != 0 && rsplen != 4 && rsplen != 8) || + (fcprsp->rspInfo3 != RSP_NO_FAILURE)) { + host_status = DID_ERROR; + goto out; + } + } + + if ((resp_info & SNS_LEN_VALID) && fcprsp->rspSnsLen) { + uint32_t snslen = be32_to_cpu(fcprsp->rspSnsLen); + if (snslen > SCSI_SENSE_BUFFERSIZE) + snslen = SCSI_SENSE_BUFFERSIZE; + + memcpy(cmnd->sense_buffer, &fcprsp->rspInfo0 + rsplen, snslen); + } + + cmnd->resid = 0; + if (resp_info & RESID_UNDER) { + cmnd->resid = be32_to_cpu(fcprsp->rspResId); + + lpfc_printf_log(phba, KERN_INFO, LOG_FCP, + "%d:0716 FCP Read Underrun, expected %d, " + "residual %d Data: x%x x%x x%x\n", phba->brd_no, + be32_to_cpu(fcpcmd->fcpDl), cmnd->resid, + fcpi_parm, cmnd->cmnd[0], cmnd->underflow); + + /* + * The cmnd->underflow is the minimum number of bytes that must + * be transfered for this command. Provided a sense condition is + * not present, make sure the actual amount transferred is at + * least the underflow value or fail. + */ + if (!(resp_info & SNS_LEN_VALID) && + (scsi_status == SAM_STAT_GOOD) && + (cmnd->request_bufflen - cmnd->resid) < cmnd->underflow) { + lpfc_printf_log(phba, KERN_INFO, LOG_FCP, + "%d:0717 FCP command x%x residual " + "underrun converted to error " + "Data: x%x x%x x%x\n", phba->brd_no, + cmnd->cmnd[0], cmnd->request_bufflen, + cmnd->resid, cmnd->underflow); + + host_status = DID_ERROR; + } + } else if (resp_info & RESID_OVER) { + lpfc_printf_log(phba, KERN_WARNING, LOG_FCP, + "%d:0720 FCP command x%x residual " + "overrun error. Data: x%x x%x \n", + phba->brd_no, cmnd->cmnd[0], + cmnd->request_bufflen, cmnd->resid); + host_status = DID_ERROR; + + /* + * Check SLI validation that all the transfer was actually done + * (fcpi_parm should be zero). Apply check only to reads. + */ + } else if ((scsi_status == SAM_STAT_GOOD) && fcpi_parm && + (cmnd->sc_data_direction == DMA_FROM_DEVICE)) { + lpfc_printf_log(phba, KERN_WARNING, LOG_FCP, + "%d:0734 FCP Read Check Error Data: " + "x%x x%x x%x x%x\n", phba->brd_no, + be32_to_cpu(fcpcmd->fcpDl), + be32_to_cpu(fcprsp->rspResId), + fcpi_parm, cmnd->cmnd[0]); + host_status = DID_ERROR; + cmnd->resid = cmnd->request_bufflen; + } + + out: + cmnd->result = ScsiResult(host_status, scsi_status); +} + +void +lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, + struct lpfc_iocbq *pIocbOut) +{ + int depth, pend_cnt; + struct lpfc_scsi_buf *lpfc_cmd = + (struct lpfc_scsi_buf *) pIocbIn->context1; + struct lpfc_target *target = lpfc_cmd->target; + struct scsi_cmnd *cmd = lpfc_cmd->pCmd; + struct scsi_device *sdev; + int result; + + lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4]; + lpfc_cmd->status = pIocbOut->iocb.ulpStatus; + + target->iodonecnt++; + + if (lpfc_cmd->status) { + target->errorcnt++; + + if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT && + (lpfc_cmd->result & IOERR_DRVR_MASK)) + lpfc_cmd->status = IOSTAT_DRIVER_REJECT; + else if (lpfc_cmd->status >= IOSTAT_CNT) + lpfc_cmd->status = IOSTAT_DEFAULT; + + lpfc_printf_log(phba, KERN_WARNING, LOG_FCP, + "%d:0729 FCP cmd x%x failed <%d/%d> status: " + "x%x result: x%x Data: x%x x%x\n", + phba->brd_no, cmd->cmnd[0], cmd->device->id, + cmd->device->lun, lpfc_cmd->status, + lpfc_cmd->result, pIocbOut->iocb.ulpContext, + lpfc_cmd->cur_iocbq.iocb.ulpIoTag); + + switch (lpfc_cmd->status) { + case IOSTAT_FCP_RSP_ERROR: + /* Call FCP RSP handler to determine result */ + lpfc_handle_fcp_err(lpfc_cmd); + break; + case IOSTAT_NPORT_BSY: + case IOSTAT_FABRIC_BSY: + cmd->result = ScsiResult(DID_BUS_BUSY, 0); + break; + case IOSTAT_LOCAL_REJECT: + if (lpfc_cmd->result == IOERR_LOOP_OPEN_FAILURE) + lpfc_discq_post_event(phba, target->pnode, + NULL, + LPFC_EVT_OPEN_LOOP); + cmd->result = ScsiResult(DID_ERROR, 0); + break; + default: + cmd->result = ScsiResult(DID_ERROR, 0); + break; + } + + if (target->pnode) { + if(target->pnode->nlp_state != NLP_STE_MAPPED_NODE) + cmd->result = ScsiResult(DID_BUS_BUSY, + SAM_STAT_BUSY); + } + else { + cmd->result = ScsiResult(DID_NO_CONNECT, 0); + } + } else { + cmd->result = ScsiResult(DID_OK, 0); + } + + if (cmd->result || lpfc_cmd->fcp_rsp->rspSnsLen) { + uint32_t *lp = (uint32_t *)cmd->sense_buffer; + + lpfc_printf_log(phba, KERN_INFO, LOG_FCP, + "%d:0710 Iodone <%d/%d> cmd %p, error x%x " + "SNS x%x x%x Data: x%x x%x\n", + phba->brd_no, cmd->device->id, + cmd->device->lun, cmd, cmd->result, + *lp, *(lp + 3), cmd->retries, cmd->resid); + } + + result = cmd->result; + sdev = cmd->device; + + lpfc_free_scsi_buf(lpfc_cmd); + cmd->host_scribble = NULL; + cmd->scsi_done(cmd); + + /* + * Check for queue full. If the lun is reporting queue full, then + * back off the lun queue depth to prevent target overloads. + */ + if (result == SAM_STAT_TASK_SET_FULL) { + pend_cnt = lpfc_sli_sum_iocb_lun(phba, + &phba->sli.ring[phba->sli.fcp_ring], + sdev->id, sdev->lun); + + spin_unlock_irq(phba->host->host_lock); + depth = scsi_track_queue_full(sdev, pend_cnt); + spin_lock_irq(phba->host->host_lock); + + if (depth) { + lpfc_printf_log(phba, KERN_WARNING, LOG_FCP, + "%d:0711 detected queue full - lun queue depth " + " adjusted to %d.\n", phba->brd_no, depth); + } + } +} + +static int +lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_hba *phba, + struct lpfc_scsi_buf *lpfc_cmd, + uint8_t task_mgmt_cmd) +{ + + struct lpfc_sli *psli; + struct lpfc_iocbq *piocbq; + IOCB_t *piocb; + struct fcp_cmnd *fcp_cmnd; + struct lpfc_nodelist *ndlp = lpfc_cmd->target->pnode; + + if ((ndlp == 0) || (ndlp->nlp_state != NLP_STE_MAPPED_NODE)) { + return 0; + } + + /* allocate an iocb command */ + psli = &phba->sli; + piocbq = &(lpfc_cmd->cur_iocbq); + piocb = &piocbq->iocb; + + + fcp_cmnd = lpfc_cmd->fcp_cmnd; + putLunHigh(fcp_cmnd->fcpLunMsl, lpfc_cmd->lun); + putLunLow(fcp_cmnd->fcpLunLsl, lpfc_cmd->lun) + fcp_cmnd->fcpCntl2 = task_mgmt_cmd; + fcp_cmnd->fcpCntl3 = 0; + + piocb->ulpCommand = CMD_FCP_ICMND64_CR; + + piocb->ulpContext = ndlp->nlp_rpi; + if (ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) { + piocb->ulpFCP2Rcvy = 1; + } + piocb->ulpClass = (ndlp->nlp_fcp_info & 0x0f); + + /* ulpTimeout is only one byte */ + if (lpfc_cmd->timeout > 0xff) { + /* + * Do not timeout the command at the firmware level. + * The driver will provide the timeout mechanism. + */ + piocb->ulpTimeout = 0; + } else { + piocb->ulpTimeout = lpfc_cmd->timeout; + } + + switch (task_mgmt_cmd) { + case FCP_LUN_RESET: + /* Issue LUN Reset to TGT LUN */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_FCP, + "%d:0703 Issue LUN Reset to TGT %d LUN %d " + "Data: x%x x%x\n", + phba->brd_no, + lpfc_cmd->target->scsi_id, lpfc_cmd->lun, + ndlp->nlp_rpi, ndlp->nlp_flag); + + break; + case FCP_ABORT_TASK_SET: + /* Issue Abort Task Set to TGT LUN */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_FCP, + "%d:0701 Issue Abort Task Set to TGT %d LUN %d " + "Data: x%x x%x\n", + phba->brd_no, + lpfc_cmd->target->scsi_id, lpfc_cmd->lun, + ndlp->nlp_rpi, ndlp->nlp_flag); + + break; + case FCP_TARGET_RESET: + /* Issue Target Reset to TGT */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_FCP, + "%d:0702 Issue Target Reset to TGT %d " + "Data: x%x x%x\n", + phba->brd_no, + lpfc_cmd->target->scsi_id, ndlp->nlp_rpi, + ndlp->nlp_flag); + break; + } + + return (1); +} + +static int +lpfc_scsi_tgt_reset(struct lpfc_target * target, int id, struct lpfc_hba * phba) +{ + struct lpfc_iocbq *piocbq, *piocbqrsp; + struct lpfc_scsi_buf * lpfc_cmd; + struct lpfc_sli *psli = &phba->sli; + int ret, retval = FAILED; + + lpfc_cmd = lpfc_get_scsi_buf(phba, GFP_ATOMIC); + if (!lpfc_cmd) + goto out; + + /* + * The driver cannot count on any meaningful timeout value in the scsi + * command. The timeout is chosen to be twice the ratov plus a window. + */ + lpfc_cmd->timeout = (2 * phba->fc_ratov) + 3; + lpfc_cmd->target = target; + lpfc_cmd->lun = 0; + + ret = lpfc_scsi_prep_task_mgmt_cmd(phba, lpfc_cmd, FCP_TARGET_RESET); + if (!ret) + goto out_free_scsi_buf; + + piocbq = &lpfc_cmd->cur_iocbq; + piocbq->context1 = lpfc_cmd; + + piocbqrsp = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC); + if (!piocbqrsp) + goto out_free_scsi_buf; + + /* First flush all outstanding commands on the txq for the target */ + lpfc_sli_abort_iocb_tgt(phba, &phba->sli.ring[phba->sli.fcp_ring], + lpfc_cmd->target->scsi_id, LPFC_ABORT_TXQ); + + memset(piocbqrsp, 0, sizeof (struct lpfc_iocbq)); + + piocbq->iocb_flag |= LPFC_IO_POLL; + + ret = lpfc_sli_issue_iocb_wait_high_priority(phba, + &phba->sli.ring[psli->fcp_ring], + piocbq, SLI_IOCB_HIGH_PRIORITY, + piocbqrsp); + if (ret != IOCB_SUCCESS) { + lpfc_cmd->status = IOSTAT_DRIVER_REJECT; + retval = FAILED; + } else { + lpfc_cmd->result = piocbqrsp->iocb.un.ulpWord[4]; + lpfc_cmd->status = piocbqrsp->iocb.ulpStatus; + if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT && + (lpfc_cmd->result & IOERR_DRVR_MASK)) + lpfc_cmd->status = IOSTAT_DRIVER_REJECT; + retval = SUCCESS; + } + + /* At this point in time, target reset completion, all outstanding + * txcmplq I/Os should have been aborted by the target. + * Unfortunately, all targets do not abide by this so we need + * to help it out a bit. + */ + lpfc_sli_abort_iocb_tgt(phba, &phba->sli.ring[phba->sli.fcp_ring], + lpfc_cmd->target->scsi_id, LPFC_ABORT_ALLQ); + + /* + * If the IOCB failed then free the memory resources. Otherwise, + * the resources will be freed up by the completion handler. + */ + if (ret == IOCB_TIMEDOUT) + goto out; + + mempool_free(piocbqrsp, phba->iocb_mem_pool); + +out_free_scsi_buf: + lpfc_free_scsi_buf(lpfc_cmd); +out: + return retval; +} + + +#define LPFC_RESET_WAIT 2 +int +lpfc_reset_bus_handler(struct scsi_cmnd *cmnd) +{ + struct Scsi_Host *shost = cmnd->device->host; + struct lpfc_hba *phba = (struct lpfc_hba *)shost->hostdata[0]; + int ret = FAILED, i, err_count = 0; + struct lpfc_target *target; + int cnt, loopcnt; + + /* + * Since the driver manages a single bus device, reset all + * targets known to the driver. Should any target reset + * fail, this routine returns failure to the midlayer. + */ + for (i = 0; i < MAX_FCP_TARGET; i++) { + target = phba->device_queue_hash[i]; + if (!target) + continue; + + ret = lpfc_scsi_tgt_reset(target, i, phba); + if (ret != SUCCESS) { + lpfc_printf_log(phba, KERN_INFO, LOG_FCP, + "%d:0712 Bus Reset on target %d failed\n", + phba->brd_no, i); + err_count++; + } + } + + loopcnt = 0; + while((cnt = lpfc_sli_sum_iocb_host(phba, + &phba->sli.ring[phba->sli.fcp_ring]))) { + spin_unlock_irq(phba->host->host_lock); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(LPFC_RESET_WAIT*HZ); + spin_lock_irq(phba->host->host_lock); + + if (++loopcnt + > (2 * phba->cfg_nodev_tmo)/LPFC_RESET_WAIT) + break; + } + + if (cnt) { + /* flush all outstanding commands on the host */ + i = lpfc_sli_abort_iocb_host(phba, + &phba->sli.ring[phba->sli.fcp_ring], + LPFC_ABORT_ALLQ); + + lpfc_printf_log(phba, KERN_INFO, LOG_FCP, + "%d:0715 Bus Reset I/O flush failure: cnt x%x left x%x\n", + phba->brd_no, cnt, i); + } + + if (!err_count) + ret = SUCCESS; + else + ret = FAILED; + + lpfc_printf_log(phba, + KERN_ERR, + LOG_FCP, + "%d:0714 SCSI layer issued Bus Reset Data: x%x\n", + phba->brd_no, ret); + + return ret; +} + + +int +lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) +{ + struct lpfc_hba *phba = + (struct lpfc_hba *) cmnd->device->host->hostdata[0]; + struct lpfc_sli *psli = &phba->sli; + struct lpfc_target *targetp = cmnd->device->hostdata; + struct lpfc_nodelist *ndlp; + struct lpfc_iocbq *piocbq; + struct lpfc_scsi_buf *lpfc_cmd; + IOCB_t *piocb; + int err = 0; + uint16_t nlp_state; + + targetp->qcmdcnt++; + + /* + * The target pointer is guaranteed not to be NULL because the driver + * only clears the device->hostdata field in lpfc_slave_destroy. This + * approach guarantees no further IO calls on this target. + */ + ndlp = targetp->pnode; + if (!ndlp) { + cmnd->result = ScsiResult(DID_NO_CONNECT, 0); + goto out_fail_command; + } + + nlp_state = ndlp->nlp_state; + + /* + * A Fibre Channel is present and functioning only when the node state + * is MAPPED. Any other state is a failure. + */ + if (nlp_state != NLP_STE_MAPPED_NODE) { + if ((nlp_state == NLP_STE_UNMAPPED_NODE) || + (nlp_state == NLP_STE_UNUSED_NODE)) { + cmnd->result = ScsiResult(DID_NO_CONNECT, 0); + goto out_fail_command; + } + /* + * The device is most likely recovered and the driver + * needs a bit more time to finish. Ask the midlayer + * to retry. + */ + goto out_host_busy; + } + + lpfc_cmd = lpfc_get_scsi_buf(phba, GFP_ATOMIC); + if (!lpfc_cmd) + goto out_host_busy; + + /* + * Store the midlayer's command structure for the completion phase + * and complete the command initialization. + */ + cmnd->scsi_done = done; + cmnd->host_scribble = (unsigned char *)lpfc_cmd; + + lpfc_cmd->target = targetp; + lpfc_cmd->lun = cmnd->device->lun; + lpfc_cmd->timeout = 0; + lpfc_cmd->pCmd = cmnd; + putLunHigh(lpfc_cmd->fcp_cmnd->fcpLunMsl, lpfc_cmd->lun); + putLunLow(lpfc_cmd->fcp_cmnd->fcpLunLsl, lpfc_cmd->lun); + + err = lpfc_os_prep_io(phba, lpfc_cmd); + if (err) + goto out_host_busy_free_buf; + + piocbq = &(lpfc_cmd->cur_iocbq); + piocb = &piocbq->iocb; + piocb->ulpTimeout = lpfc_cmd->timeout; + piocbq->context1 = lpfc_cmd; + piocbq->iocb_cmpl = lpfc_scsi_cmd_iocb_cmpl; + + piocbq->iocb.ulpContext = ndlp->nlp_rpi; + if (ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) { + piocbq->iocb.ulpFCP2Rcvy = 1; + } + + piocbq->iocb.ulpClass = (ndlp->nlp_fcp_info & 0x0f); + + err = lpfc_sli_issue_iocb(phba, &phba->sli.ring[psli->fcp_ring], piocbq, + SLI_IOCB_RET_IOCB); + if (err) + goto out_host_busy_free_buf; + return 0; + + out_host_busy_free_buf: + lpfc_free_scsi_buf(lpfc_cmd); + cmnd->host_scribble = NULL; + out_host_busy: + targetp->iodonecnt++; + targetp->errorcnt++; + return SCSI_MLQUEUE_HOST_BUSY; + + out_fail_command: + targetp->iodonecnt++; + targetp->errorcnt++; + done(cmnd); + return 0; +} + +int +lpfc_reset_lun_handler(struct scsi_cmnd *cmnd) +{ + struct Scsi_Host *shost = cmnd->device->host; + struct lpfc_hba *phba = (struct lpfc_hba *)shost->hostdata[0]; + struct lpfc_sli *psli = &phba->sli; + struct lpfc_scsi_buf *lpfc_cmd; + struct lpfc_iocbq *piocbq, *piocbqrsp = NULL; + struct lpfc_target *target = cmnd->device->hostdata; + int ret, retval = FAILED; + int cnt, loopcnt; + + /* + * If target is not in a MAPPED state, delay the reset till + * target is rediscovered or nodev timeout is fired. + */ + while ( 1 ) { + if (!target->pnode) + break; + + if (target->pnode->nlp_state != NLP_STE_MAPPED_NODE) { + spin_unlock_irq(phba->host->host_lock); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout( HZ/2); + spin_lock_irq(phba->host->host_lock); + } + if ((target->pnode) && + (target->pnode->nlp_state == NLP_STE_MAPPED_NODE)) + break; + } + + lpfc_cmd = lpfc_get_scsi_buf(phba, GFP_ATOMIC); + if (!lpfc_cmd) + goto out; + + lpfc_cmd->timeout = 60; /* set command timeout to 60 seconds */ + lpfc_cmd->scsi_hba = phba; + lpfc_cmd->target = target; + lpfc_cmd->lun = cmnd->device->lun; + + ret = lpfc_scsi_prep_task_mgmt_cmd(phba, lpfc_cmd, FCP_LUN_RESET); + if (!ret) + goto out_free_scsi_buf; + + piocbq = &lpfc_cmd->cur_iocbq; + piocbq->context1 = lpfc_cmd; + + /* get a buffer for this IOCB command response */ + piocbqrsp = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC); + if(!piocbqrsp) + goto out_free_scsi_buf; + + /* First flush all outstanding commands on the txq for the lun */ + lpfc_sli_abort_iocb_lun(phba, + &phba->sli.ring[phba->sli.fcp_ring], + cmnd->device->id, + cmnd->device->lun, LPFC_ABORT_TXQ); + + memset(piocbqrsp, 0, sizeof (struct lpfc_iocbq)); + + piocbq->iocb_flag |= LPFC_IO_POLL; + + ret = lpfc_sli_issue_iocb_wait_high_priority(phba, + &phba->sli.ring[psli->fcp_ring], + piocbq, 0, + piocbqrsp); + if (ret == IOCB_SUCCESS) + retval = SUCCESS; + + lpfc_cmd->result = piocbqrsp->iocb.un.ulpWord[4]; + lpfc_cmd->status = piocbqrsp->iocb.ulpStatus; + if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT) + if (lpfc_cmd->result & IOERR_DRVR_MASK) + lpfc_cmd->status = IOSTAT_DRIVER_REJECT; + + /* At this point in time, lun reset completion, all outstanding + * txcmplq I/Os should have been aborted by the target. + * Unfortunately, all targets do not abide by this so we need + * to help it out a bit. + */ + lpfc_sli_abort_iocb_lun(phba, + &phba->sli.ring[phba->sli.fcp_ring], + cmnd->device->id, + cmnd->device->lun, LPFC_ABORT_ALLQ); + + loopcnt = 0; + while((cnt = lpfc_sli_sum_iocb_lun(phba, + &phba->sli.ring[phba->sli.fcp_ring], + cmnd->device->id, + cmnd->device->lun))) { + spin_unlock_irq(phba->host->host_lock); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(LPFC_RESET_WAIT*HZ); + spin_lock_irq(phba->host->host_lock); + + if (++loopcnt + > (2 * phba->cfg_nodev_tmo)/LPFC_RESET_WAIT) + break; + } + + if(cnt) { + lpfc_printf_log(phba, KERN_INFO, LOG_FCP, + "%d:0719 LUN Reset I/O flush failure: cnt x%x\n", + phba->brd_no, cnt); + } + + lpfc_printf_log(phba, KERN_ERR, LOG_FCP, + "%d:0713 SCSI layer issued LUN reset (%d, %d) " + "Data: x%x x%x x%x\n", + phba->brd_no, lpfc_cmd->target->scsi_id, + lpfc_cmd->lun, ret, lpfc_cmd->status, + lpfc_cmd->result); + + if (ret == IOCB_TIMEDOUT) + goto out; + + mempool_free(piocbqrsp, phba->iocb_mem_pool); + +out_free_scsi_buf: + lpfc_free_scsi_buf(lpfc_cmd); +out: + return retval; +} + +static void +lpfc_scsi_cmd_iocb_cleanup (struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, + struct lpfc_iocbq *pIocbOut) +{ + struct lpfc_scsi_buf *lpfc_cmd = + (struct lpfc_scsi_buf *) pIocbIn->context1; + struct scsi_cmnd *ml_cmd = + ((struct lpfc_scsi_buf *) pIocbIn->context1)->pCmd; + struct lpfc_target *targetp = ml_cmd->device->hostdata; + + if (targetp) { + targetp->iodonecnt++; + targetp->errorcnt++; + } + lpfc_free_scsi_buf(lpfc_cmd); +} + +static void +lpfc_scsi_cmd_iocb_cmpl_aborted (struct lpfc_hba *phba, + struct lpfc_iocbq *pIocbIn, + struct lpfc_iocbq *pIocbOut) +{ + struct scsi_cmnd *ml_cmd = + ((struct lpfc_scsi_buf *) pIocbIn->context1)->pCmd; + + lpfc_scsi_cmd_iocb_cleanup (phba, pIocbIn, pIocbOut); + ml_cmd->host_scribble = NULL; +} + +#define LPFC_ABORT_WAIT 2 +int +lpfc_abort_handler(struct scsi_cmnd *cmnd) +{ + struct lpfc_hba *phba = + (struct lpfc_hba *)cmnd->device->host->hostdata[0]; + struct lpfc_sli_ring *pring = &phba->sli.ring[phba->sli.fcp_ring]; + struct lpfc_iocbq *iocb, *next_iocb, *abtsiocbp; + struct lpfc_scsi_buf *lpfc_cmd; + IOCB_t *cmd, *icmd; + unsigned long snum; + unsigned int id, lun; + unsigned int loop_count = 0; + int ret = IOCB_SUCCESS; + + /* + * If the host_scribble data area is NULL, then the driver has already + * completed this command, but the midlayer did not see the completion + * before the eh fired. Just return SUCCESS. + */ + lpfc_cmd = (struct lpfc_scsi_buf *)cmnd->host_scribble; + if (!lpfc_cmd) + return SUCCESS; + + /* save these now since lpfc_cmd can be freed */ + id = lpfc_cmd->target->scsi_id; + lun = lpfc_cmd->lun; + snum = cmnd->serial_number; + + /* Search the txq first. */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + if (iocb->context1 != lpfc_cmd) + continue; + + list_del_init(&iocb->list); + pring->txq_cnt--; + if (!iocb->iocb_cmpl) { + mempool_free(iocb, phba->iocb_mem_pool); + } + else { + cmd->ulpStatus = IOSTAT_LOCAL_REJECT; + cmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + lpfc_scsi_cmd_iocb_cmpl_aborted(phba, iocb, iocb); + } + goto out; + } + + abtsiocbp = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC); + if (!abtsiocbp) + goto out; + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + + /* + * The scsi command was not in the txq. Check the txcmplq and if it is + * found, send an abort to the FW. + */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + if (iocb->context1 != lpfc_cmd) + continue; + + iocb->iocb_cmpl = lpfc_scsi_cmd_iocb_cmpl_aborted; + cmd = &iocb->iocb; + icmd = &abtsiocbp->iocb; + icmd->un.acxri.abortType = ABORT_TYPE_ABTS; + icmd->un.acxri.abortContextTag = cmd->ulpContext; + icmd->un.acxri.abortIoTag = cmd->ulpIoTag; + + icmd->ulpLe = 1; + icmd->ulpClass = cmd->ulpClass; + abtsiocbp->iocb_cmpl = lpfc_sli_abort_fcp_cmpl; + if (phba->hba_state >= LPFC_LINK_UP) + icmd->ulpCommand = CMD_ABORT_XRI_CN; + else + icmd->ulpCommand = CMD_CLOSE_XRI_CN; + + if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == + IOCB_ERROR) { + mempool_free(abtsiocbp, phba->iocb_mem_pool); + ret = IOCB_ERROR; + break; + } + + /* Wait for abort to complete */ + while (cmnd->host_scribble) + { + spin_unlock_irq(phba->host->host_lock); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(LPFC_ABORT_WAIT*HZ); + spin_lock_irq(phba->host->host_lock); + if (++loop_count + > (2 * phba->cfg_nodev_tmo)/LPFC_ABORT_WAIT) + break; + } + + if (cmnd->host_scribble) { + lpfc_printf_log(phba, KERN_ERR, LOG_FCP, + "%d:0748 abort handler timed " + "out waiting for abort to " + "complete. Data: " + "x%x x%x x%x x%lx\n", + phba->brd_no, ret, id, lun, snum); + cmnd->host_scribble = NULL; + iocb->iocb_cmpl = lpfc_scsi_cmd_iocb_cleanup; + ret = IOCB_ERROR; + } + + break; + } + + out: + lpfc_printf_log(phba, KERN_WARNING, LOG_FCP, + "%d:0749 SCSI layer issued abort device " + "Data: x%x x%x x%x x%lx\n", + phba->brd_no, ret, id, lun, snum); + + return (ret == IOCB_SUCCESS ? SUCCESS : FAILED); +} + +#if defined(RHEL_FC) || defined(SLES_FC) +void +lpfc_target_unblock(struct lpfc_hba *phba, struct lpfc_target *targetp) +{ +#if defined(RHEL_FC) + /* + * This code to be removed once block/unblock and the new + * dicovery state machine are fully debugged. + */ + if (!targetp || !targetp->starget) { +#else /* not RHEL_FC -> is SLES_FC */ + if (!targetp) { +#endif + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY | LOG_FCP, + "%d:0262 Cannot unblock scsi target\n", phba->brd_no); + + return; + } + + /* Unblock IO to target scsi id to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY | LOG_FCP, + "%d:0258 Unblocking IO to Target scsi id x%x " + "NPort pointer x%p\n", + phba->brd_no, targetp->scsi_id, targetp->pnode); + + spin_unlock_irq(phba->host->host_lock); + +#if defined(RHEL_FC) + fc_target_unblock(targetp->starget); +#else /* not RHEL_FC -> is SLES_FC */ + fc_target_unblock(phba->host, targetp->scsi_id, + &targetp->dev_loss_timer); +#endif + spin_lock_irq(phba->host->host_lock); + targetp->blocked--; +} + +void +lpfc_target_block(struct lpfc_hba *phba, struct lpfc_target *targetp) +{ +#if defined(RHEL_FC) + /* + * This code to be removed once block/unblock and the new + * dicovery state machine are fully debugged. + */ + if (!targetp || !targetp->starget) { +#else /* not RHEL_FC -> is SLES_FC */ + if (!targetp) { +#endif + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY | LOG_FCP, + "%d:0263 Cannot block scsi target." + " target ptr x%p\n", + phba->brd_no, targetp); + return; + } + + /* Block all IO to target scsi id to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY | LOG_FCP, + "%d:0259 Blocking IO to Target scsi id x%x" + " NPort pointer x%p\n", + phba->brd_no, targetp->scsi_id, targetp->pnode); + + spin_unlock_irq(phba->host->host_lock); +#if defined(RHEL_FC) + fc_target_block(targetp->starget); +#else /* not RHEL_FC -> is SLES_FC */ + fc_target_block(phba->host, targetp->scsi_id, &targetp->dev_loss_timer, + phba->cfg_nodev_tmo); + + /* + * Kill the midlayer unblock timer, but leave the target blocked. + * The driver will unblock with the nodev_tmo callback function. + */ + del_timer_sync(&targetp->dev_loss_timer); +#endif + spin_lock_irq(phba->host->host_lock); + targetp->blocked++; +} + +int +lpfc_target_remove(struct lpfc_hba *phba, struct lpfc_target *targetp) +{ + struct scsi_device *sdev; + struct Scsi_Host *shost = phba->host; + + /* This is only called if scsi target (targetp->starget) is valid */ + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY | LOG_FCP, + "%d:0260 Remove Target scsi id x%x\n", + phba->brd_no, targetp->scsi_id); + + /* If this target is blocked, we must unblock it first */ + if (targetp->blocked) + lpfc_target_unblock(phba, targetp); + + /* Remove all associated devices for this target */ + if (phba->cfg_scsi_hotplug) { +top: + list_for_each_entry(sdev, &shost->__devices, siblings) { + if (sdev->channel == 0 + && sdev->id == targetp->scsi_id) { + spin_unlock_irq(shost->host_lock); + scsi_device_get(sdev); + scsi_remove_device(sdev); + scsi_device_put(sdev); + spin_lock_irq(shost->host_lock); + goto top; + } + } + } + + return 0; +} + +int +lpfc_target_add(struct lpfc_hba *phba, struct lpfc_target *targetp) +{ + /* If the driver is not supporting scsi hotplug, just exit. */ + if(!phba->cfg_scsi_hotplug) + return 1; + + /* This is only called if scsi target (targetp->starget) is valid */ + + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY | LOG_FCP, + "%d:0261 Adding Target scsi id x%x\n", + phba->brd_no, targetp->scsi_id); + + /* + * The driver discovered a new target. Call the midlayer and get this + * target's luns added into the device list. + * Since we are going to scan the entire host, kick off a timer to + * do this so we can possibly consolidate multiple target scans into + * one scsi host scan. + */ + mod_timer(&phba->fc_scantmo, jiffies + HZ); + phba->fc_flag |= FC_SCSI_SCAN_TMO; + return 0; +} +#endif /* RHEL_FC or SLES_FC */ --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_compat.h 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_compat.h 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,109 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_compat.h 1.31.1.2 2005/06/13 17:16:04EDT sf_support Exp $ + * + * This file provides macros to aid compilation in the Linux 2.4 kernel + * over various platform architectures. + */ + +#ifndef _H_LPFC_COMPAT +#define _H_LPFC_COMPAT + + +/******************************************************************* +Note: HBA's SLI memory contains little-endian LW. +Thus to access it from a little-endian host, +memcpy_toio() and memcpy_fromio() can be used. +However on a big-endian host, copy 4 bytes at a time, +using writel() and readl(). + *******************************************************************/ + +#if __BIG_ENDIAN + +static inline void +lpfc_memcpy_to_slim( void *dest, void *src, unsigned int bytes) +{ + uint32_t *dest32; + uint32_t *src32; + unsigned int four_bytes; + + + dest32 = (uint32_t *) dest; + src32 = (uint32_t *) src; + + /* write input bytes, 4 bytes at a time */ + for (four_bytes = bytes /4; four_bytes > 0; four_bytes--) { + writel( *src32, dest32); + readl(dest32); /* flush */ + dest32++; + src32++; + } + + return; +} + +static inline void +lpfc_memcpy_from_slim( void *dest, void *src, unsigned int bytes) +{ + uint32_t *dest32; + uint32_t *src32; + unsigned int four_bytes; + + + dest32 = (uint32_t *) dest; + src32 = (uint32_t *) src; + + /* read input bytes, 4 bytes at a time */ + for (four_bytes = bytes /4; four_bytes > 0; four_bytes--) { + *dest32 = readl( src32); + dest32++; + src32++; + } + + return; +} + +#else + +static inline void +lpfc_memcpy_to_slim( void *dest, void *src, unsigned int bytes) +{ + /* actually returns 1 byte past dest */ + memcpy_toio( dest, src, bytes); +} + +static inline void +lpfc_memcpy_from_slim( void *dest, void *src, unsigned int bytes) +{ + /* actually returns 1 byte past dest */ + memcpy_fromio( dest, src, bytes); +} + +#endif /* __BIG_ENDIAN */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,6) +#define msleep(x) do { \ + set_current_state(TASK_UNINTERRUPTIBLE); \ + schedule_timeout((x)); \ + } while (0); +#endif +#endif /* _H_LPFC_COMPAT */ --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_hbadisc.c 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_hbadisc.c 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,2906 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_hbadisc.c 1.225.1.3 2005/07/08 19:33:24EDT sf_support Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_fcp.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" + +/* AlpaArray for assignment of scsid for scan-down and bind_method */ +uint8_t lpfcAlpaArray[] = { + 0xEF, 0xE8, 0xE4, 0xE2, 0xE1, 0xE0, 0xDC, 0xDA, 0xD9, 0xD6, + 0xD5, 0xD4, 0xD3, 0xD2, 0xD1, 0xCE, 0xCD, 0xCC, 0xCB, 0xCA, + 0xC9, 0xC7, 0xC6, 0xC5, 0xC3, 0xBC, 0xBA, 0xB9, 0xB6, 0xB5, + 0xB4, 0xB3, 0xB2, 0xB1, 0xAE, 0xAD, 0xAC, 0xAB, 0xAA, 0xA9, + 0xA7, 0xA6, 0xA5, 0xA3, 0x9F, 0x9E, 0x9D, 0x9B, 0x98, 0x97, + 0x90, 0x8F, 0x88, 0x84, 0x82, 0x81, 0x80, 0x7C, 0x7A, 0x79, + 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x6E, 0x6D, 0x6C, 0x6B, + 0x6A, 0x69, 0x67, 0x66, 0x65, 0x63, 0x5C, 0x5A, 0x59, 0x56, + 0x55, 0x54, 0x53, 0x52, 0x51, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, + 0x49, 0x47, 0x46, 0x45, 0x43, 0x3C, 0x3A, 0x39, 0x36, 0x35, + 0x34, 0x33, 0x32, 0x31, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, + 0x27, 0x26, 0x25, 0x23, 0x1F, 0x1E, 0x1D, 0x1B, 0x18, 0x17, + 0x10, 0x0F, 0x08, 0x04, 0x02, 0x01 +}; + +static void lpfc_disc_timeout_handler(struct lpfc_hba *); + +void +lpfc_evt_iocb_free(struct lpfc_hba * phba, struct lpfc_iocbq * saveq) +{ + struct lpfc_iocbq *rspiocbp, *tmpiocbp; + + /* Free up iocb buffer chain for cmd just processed */ + list_for_each_entry_safe(rspiocbp, tmpiocbp, + &saveq->list, list) { + list_del(&rspiocbp->list); + mempool_free( rspiocbp, phba->iocb_mem_pool); + } + mempool_free( saveq, phba->iocb_mem_pool); +} + +void +lpfc_process_nodev_timeout(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) +{ + struct lpfc_target *targetp; + int scsid, warn_user = 0; + + /* If the nodev_timeout is cancelled do nothing */ + if (!(ndlp->nlp_flag & NLP_NODEV_TMO)) + return; + + ndlp->nlp_flag &= ~NLP_NODEV_TMO; + + for(scsid=0;scsiddevice_queue_hash[scsid]; + /* First see if the SCSI ID has an allocated struct + lpfc_target */ + if (targetp) { + if (targetp->pnode == ndlp) { + /* flush the target */ + lpfc_sli_abort_iocb_tgt(phba, + &phba->sli.ring[phba->sli.fcp_ring], + scsid, LPFC_ABORT_ALLQ); + warn_user = 1; + break; + } + } + } + + if (warn_user) { + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "%d:0203 Nodev timeout on NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag, + ndlp->nlp_state, ndlp->nlp_rpi); + } else { + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0206 Nodev timeout on NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag, + ndlp->nlp_state, ndlp->nlp_rpi); + } + + lpfc_disc_state_machine(phba, ndlp, NULL, NLP_EVT_DEVICE_RM); + return; +} + +static void +lpfc_disc_done(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli = &phba->sli; + LPFC_DISC_EVT_t *evtp; + LPFC_MBOXQ_t *pmb; + struct lpfc_iocbq *cmdiocbp, *saveq; + struct lpfc_nodelist *ndlp; + LPFC_RING_MASK_t *func; + struct Scsi_Host *shost; + struct lpfc_dmabuf *mp; + uint32_t work_hba_events; + int free_evt; + + work_hba_events=phba->work_hba_events; + spin_unlock_irq(phba->host->host_lock); + + if (work_hba_events & WORKER_DISC_TMO) + lpfc_disc_timeout_handler(phba); + + if (work_hba_events & WORKER_ELS_TMO) + lpfc_els_timeout_handler(phba); + + if (work_hba_events & WORKER_MBOX_TMO) + lpfc_mbox_timeout_handler(phba); + + if (work_hba_events & WORKER_FDMI_TMO) + lpfc_fdmi_tmo_handler(phba); + + spin_lock_irq(phba->host->host_lock); + phba->work_hba_events &= ~work_hba_events; + + /* check discovery event list */ + while(!list_empty(&phba->dpc_disc)) { + evtp = list_entry(phba->dpc_disc.next, + typeof(*evtp), evt_listp); + list_del_init(&evtp->evt_listp); + free_evt =1; + switch(evtp->evt) { + case LPFC_EVT_MBOX: + pmb = (LPFC_MBOXQ_t *)(evtp->evt_arg1); + if ( pmb->mbox_cmpl ) + (pmb->mbox_cmpl) (phba, pmb); + else { + mp = (struct lpfc_dmabuf *) (pmb->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + mempool_free( pmb, phba->mbox_mem_pool); + } + break; + case LPFC_EVT_SOL_IOCB: + cmdiocbp = (struct lpfc_iocbq *)(evtp->evt_arg1); + saveq = (struct lpfc_iocbq *)(evtp->evt_arg2); + (cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq); + lpfc_evt_iocb_free(phba, saveq); + break; + case LPFC_EVT_UNSOL_IOCB: + func = (LPFC_RING_MASK_t *)(evtp->evt_arg1); + saveq = (struct lpfc_iocbq *)(evtp->evt_arg2); + (func->lpfc_sli_rcv_unsol_event) (phba, + &psli->ring[LPFC_ELS_RING], saveq); + lpfc_evt_iocb_free(phba, saveq); + break; + case LPFC_EVT_NODEV_TMO: + free_evt = 0; + ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1); + lpfc_process_nodev_timeout(phba, ndlp); + break; + case LPFC_EVT_ELS_RETRY: + ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1); + spin_unlock_irq(phba->host->host_lock); + lpfc_els_retry_delay_handler(ndlp); + spin_lock_irq(phba->host->host_lock); + free_evt = 0; + break; + case LPFC_EVT_SCAN: + shost = phba->host; + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY | LOG_FCP, + "%d:0252 Rescanning scsi host\n", phba->brd_no); + spin_unlock_irq(shost->host_lock); + scsi_scan_host(shost); + spin_lock_irq(shost->host_lock); + break; + case LPFC_EVT_ERR_ATTN: + spin_unlock_irq(phba->host->host_lock); + lpfc_handle_eratt(phba, (unsigned long) evtp->evt_arg1); + spin_lock_irq(phba->host->host_lock); + break; + case LPFC_EVT_OPEN_LOOP: + ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1); + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_ADISC; + break; + } + if (free_evt) + kfree(evtp); + } +} + +int +lpfc_do_dpc(void *p) +{ + unsigned long flags; + DECLARE_MUTEX_LOCKED(sem); + struct lpfc_hba *phba = (struct lpfc_hba *)p; + + lock_kernel(); + + daemonize("lpfc_dpc_%d", phba->brd_no); + allow_signal(SIGHUP); + + phba->dpc_wait = &sem; + set_user_nice(current, -20); + + unlock_kernel(); + + complete(&phba->dpc_startup); + + while (1) { + if (down_interruptible(&sem)) + break; + + if (signal_pending(current)) + break; + + if (phba->dpc_kill) + break; + + spin_lock_irqsave(phba->host->host_lock, flags); + lpfc_disc_done(phba); + spin_unlock_irqrestore(phba->host->host_lock, flags); + } + + /* Zero out semaphore we were waiting on. */ + phba->dpc_wait = NULL; + complete_and_exit(&phba->dpc_exiting, 0); + return(0); +} + +/* + * This is only called to handle FC discovery events. Since this a rare + * occurance, we allocate an LPFC_DISC_EVT_t structure here instead of + * embedding it in the IOCB. + */ +int +lpfc_discq_post_event(struct lpfc_hba * phba, void *arg1, void *arg2, + uint32_t evt) +{ + LPFC_DISC_EVT_t *evtp; + + /* All Mailbox completions and LPFC_ELS_RING rcv ring IOCB events + * will be queued to DPC for processing + */ + evtp = (LPFC_DISC_EVT_t *) kmalloc(sizeof(LPFC_DISC_EVT_t), GFP_ATOMIC); + if (!evtp) + return 0; + + evtp->evt_arg1 = arg1; + evtp->evt_arg2 = arg2; + evtp->evt = evt; + evtp->evt_listp.next = NULL; + evtp->evt_listp.prev = NULL; + + /* Queue the event to the DPC to be processed later */ + list_add_tail(&evtp->evt_listp, &phba->dpc_disc); + if (phba->dpc_wait) + up(phba->dpc_wait); + + return 1; +} + +int +lpfc_linkdown(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + struct list_head *node_list[7]; + LPFC_MBOXQ_t *mb; + int rc, i; + + psli = &phba->sli; + phba->hba_state = LPFC_LINK_DOWN; + +#if !defined(RHEL_FC) && !defined(SLES_FC) + /* Stop all requests to the driver from the midlayer. */ + scsi_block_requests(phba->host); +#endif + + lpfc_put_event(phba, HBA_EVENT_LINK_DOWN, phba->fc_myDID, NULL, 0, 0); + + /* Clean up any firmware default rpi's */ + if ((mb = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + lpfc_unreg_did(phba, 0xffffffff, mb); + mb->mbox_cmpl=lpfc_sli_def_mbox_cmpl; + if (lpfc_sli_issue_mbox(phba, mb, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mb, phba->mbox_mem_pool); + } + } + + /* Cleanup any outstanding RSCN activity */ + lpfc_els_flush_rscn(phba); + + /* Cleanup any outstanding ELS commands */ + lpfc_els_flush_cmd(phba); + + /* + * If this function was called by the lpfc_do_dpc, don't recurse into + * the routine again. If not, just process any outstanding + * discovery events. + */ + if ((!list_empty(&phba->dpc_disc)) || + (phba->work_hba_events)){ + lpfc_disc_done(phba); + } + + /* Issue a LINK DOWN event to all nodes */ + node_list[0] = &phba->fc_npr_list; /* MUST do this list first */ + node_list[1] = &phba->fc_nlpmap_list; + node_list[2] = &phba->fc_nlpunmap_list; + node_list[3] = &phba->fc_prli_list; + node_list[4] = &phba->fc_reglogin_list; + node_list[5] = &phba->fc_adisc_list; + node_list[6] = &phba->fc_plogi_list; + for (i = 0; i < 7; i++) { + listp = node_list[i]; + if (list_empty(listp)) + continue; + + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + /* Fabric nodes are not handled thru state machine for + link down */ + if (ndlp->nlp_type & NLP_FABRIC) { + /* Remove ALL Fabric nodes except Fabric_DID */ + if (ndlp->nlp_DID != Fabric_DID) { + /* Take it off current list and free */ + lpfc_nlp_list(phba, ndlp, + NLP_NO_LIST); + } + } + else { + lpfc_set_failmask(phba, ndlp, + LPFC_DEV_LINK_DOWN, + LPFC_SET_BITMASK); + + rc = lpfc_disc_state_machine(phba, ndlp, NULL, + NLP_EVT_DEVICE_RECOVERY); + + /* Check config parameter use-adisc or FCP-2 */ + if ((rc != NLP_STE_FREED_NODE) && + (phba->cfg_use_adisc == 0) && + !(ndlp->nlp_fcp_info & + NLP_FCP_2_DEVICE)) { + /* We know we will have to relogin, so + * unreglogin the rpi right now to fail + * any outstanding I/Os quickly. + */ + lpfc_unreg_rpi(phba, ndlp); + } + } + } + } + + /* free any ndlp's on unused list */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_unused_list, + nlp_listp) { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + } + + /* Setup myDID for link up if we are in pt2pt mode */ + if (phba->fc_flag & FC_PT2PT) { + phba->fc_myDID = 0; + if ((mb = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + lpfc_config_link(phba, mb); + mb->mbox_cmpl=lpfc_sli_def_mbox_cmpl; + if (lpfc_sli_issue_mbox + (phba, mb, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mb, phba->mbox_mem_pool); + } + } + phba->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI); + } + phba->fc_flag &= ~FC_LBIT; + + /* Turn off discovery timer if its running */ + lpfc_can_disctmo(phba); + + /* Must process IOCBs on all rings to handle ABORTed I/Os */ + return (0); +} + +static int +lpfc_linkup(struct lpfc_hba * phba) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + struct list_head *node_list[7]; + int i; + + phba->hba_state = LPFC_LINK_UP; + phba->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI | FC_ABORT_DISCOVERY | + FC_RSCN_MODE | FC_NLP_MORE | FC_RSCN_DISCOVERY); + phba->fc_flag |= FC_NDISC_ACTIVE; + phba->fc_ns_retry = 0; + + + lpfc_put_event(phba, HBA_EVENT_LINK_UP, phba->fc_myDID, + (void *)(unsigned long)(phba->fc_topology), + 0, phba->fc_linkspeed); + + /* + * Clean up old Fabric NLP_FABRIC logins. + */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpunmap_list, + nlp_listp) { + if (ndlp->nlp_DID == Fabric_DID) { + /* Take it off current list and free */ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + } + } + + /* free any ndlp's on unused list */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_unused_list, + nlp_listp) { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + } + + /* Mark all nodes for LINK UP */ + node_list[0] = &phba->fc_plogi_list; + node_list[1] = &phba->fc_adisc_list; + node_list[2] = &phba->fc_reglogin_list; + node_list[3] = &phba->fc_prli_list; + node_list[4] = &phba->fc_nlpunmap_list; + node_list[5] = &phba->fc_nlpmap_list; + node_list[6] = &phba->fc_npr_list; + for (i = 0; i < 7; i++) { + listp = node_list[i]; + if (list_empty(listp)) + continue; + + list_for_each_entry(ndlp, listp, nlp_listp) { + lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCOVERY_INP, + LPFC_SET_BITMASK); + lpfc_set_failmask(phba, ndlp, LPFC_DEV_LINK_DOWN, + LPFC_CLR_BITMASK); + } + } + +#if !defined(RHEL_FC) && !defined(SLES_FC) + spin_unlock_irq(phba->host->host_lock); + scsi_unblock_requests(phba->host); + spin_lock_irq(phba->host->host_lock); +#endif + return 0; +} + +/* + * This routine handles processing a CLEAR_LA mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_clear_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + uint32_t control; + + psli = &phba->sli; + mb = &pmb->mb; + /* Since we don't do discovery right now, turn these off here */ + psli->ring[psli->ip_ring].flag &= ~LPFC_STOP_IOCB_EVENT; + psli->ring[psli->fcp_ring].flag &= ~LPFC_STOP_IOCB_EVENT; + psli->ring[psli->next_ring].flag &= ~LPFC_STOP_IOCB_EVENT; + /* Check for error */ + if ((mb->mbxStatus) && (mb->mbxStatus != 0x1601)) { + /* CLEAR_LA mbox error state */ + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, + "%d:0320 CLEAR_LA mbxStatus error x%x hba " + "state x%x\n", + phba->brd_no, mb->mbxStatus, phba->hba_state); + + phba->hba_state = LPFC_HBA_ERROR; + goto out; + } + + if(phba->fc_flag & FC_ABORT_DISCOVERY) + goto out; + + phba->num_disc_nodes = 0; + /* go thru NPR list and issue ELS PLOGIs */ + if (phba->fc_npr_cnt) { + lpfc_els_disc_plogi(phba); + } + + if(!phba->num_disc_nodes) { + phba->fc_flag &= ~FC_NDISC_ACTIVE; + } + + phba->hba_state = LPFC_HBA_READY; + +out: + phba->fc_flag &= ~FC_ABORT_DISCOVERY; + /* Device Discovery completes */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0225 Device Discovery completes\n", + phba->brd_no); + + mempool_free( pmb, phba->mbox_mem_pool); + if (phba->fc_flag & FC_ESTABLISH_LINK) { + phba->fc_flag &= ~FC_ESTABLISH_LINK; + } + spin_unlock_irq(phba->host->host_lock); + del_timer_sync(&phba->fc_estabtmo); + spin_lock_irq(phba->host->host_lock); + lpfc_can_disctmo(phba); + + /* turn on Link Attention interrupts */ + psli->sliinit.sli_flag |= LPFC_PROCESS_LA; + control = readl(phba->HCregaddr); + control |= HC_LAINT_ENA; + writel(control, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + return; +} + +static void +lpfc_mbx_cmpl_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + + psli = &phba->sli; + mb = &pmb->mb; + /* Check for error */ + if (mb->mbxStatus) { + /* CONFIG_LINK mbox error state */ + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, + "%d:0306 CONFIG_LINK mbxStatus error x%x " + "HBA state x%x\n", + phba->brd_no, mb->mbxStatus, phba->hba_state); + + lpfc_linkdown(phba); + phba->hba_state = LPFC_HBA_ERROR; + goto out; + } + + if (phba->hba_state == LPFC_LOCAL_CFG_LINK) { + /* Start discovery by sending a FLOGI hba_state is identically + * LPFC_FLOGI while waiting for FLOGI cmpl (same on FAN) + */ + phba->hba_state = LPFC_FLOGI; + lpfc_set_disctmo(phba); + lpfc_initial_flogi(phba); + mempool_free( pmb, phba->mbox_mem_pool); + return; + } + if (phba->hba_state == LPFC_FABRIC_CFG_LINK) { + mempool_free( pmb, phba->mbox_mem_pool); + return; + } + +out: + /* CONFIG_LINK bad hba state */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0200 CONFIG_LINK bad hba state x%x\n", + phba->brd_no, phba->hba_state); + + if (phba->hba_state != LPFC_CLEAR_LA) { + lpfc_clear_la(phba, pmb); + pmb->mbox_cmpl = lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox(phba, pmb, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( pmb, phba->mbox_mem_pool); + lpfc_disc_flush_list(phba); + psli->ring[(psli->ip_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->fcp_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->next_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + phba->hba_state = LPFC_HBA_READY; + } + } else { + mempool_free( pmb, phba->mbox_mem_pool); + } + return; +} + +static void +lpfc_mbx_cmpl_read_sparam(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli = &phba->sli; + MAILBOX_t *mb = &pmb->mb; + struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) pmb->context1; + + + /* Check for error */ + if (mb->mbxStatus) { + /* READ_SPARAM mbox error state */ + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, + "%d:0319 READ_SPARAM mbxStatus error x%x " + "hba state x%x>\n", + phba->brd_no, mb->mbxStatus, phba->hba_state); + + lpfc_linkdown(phba); + phba->hba_state = LPFC_HBA_ERROR; + goto out; + } + + memcpy((uint8_t *) & phba->fc_sparam, (uint8_t *) mp->virt, + sizeof (struct serv_parm)); + memcpy((uint8_t *) & phba->fc_nodename, + (uint8_t *) & phba->fc_sparam.nodeName, + sizeof (struct lpfc_name)); + memcpy((uint8_t *) & phba->fc_portname, + (uint8_t *) & phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + return; + +out: + pmb->context1 = NULL; + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + if (phba->hba_state != LPFC_CLEAR_LA) { + lpfc_clear_la(phba, pmb); + pmb->mbox_cmpl = lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox(phba, pmb, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( pmb, phba->mbox_mem_pool); + lpfc_disc_flush_list(phba); + psli->ring[(psli->ip_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->fcp_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->next_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + phba->hba_state = LPFC_HBA_READY; + } + } else { + mempool_free( pmb, phba->mbox_mem_pool); + } + return; +} + +/* + * This routine handles processing a READ_LA mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_read_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli = &phba->sli; + READ_LA_VAR *la; + LPFC_MBOXQ_t *mbox; + MAILBOX_t *mb = &pmb->mb; + struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1); + uint32_t control; + int i; + + /* Check for error */ + if (mb->mbxStatus) { + /* READ_LA mbox error state */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_LINK_EVENT, + "%d:1307 READ_LA mbox error x%x state x%x\n", + phba->brd_no, + mb->mbxStatus, phba->hba_state); + pmb->context1 = NULL; + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + + lpfc_linkdown(phba); + phba->hba_state = LPFC_HBA_ERROR; + + /* turn on Link Attention interrupts */ + psli->sliinit.sli_flag |= LPFC_PROCESS_LA; + control = readl(phba->HCregaddr); + control |= HC_LAINT_ENA; + writel(control, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + return; + } + la = (READ_LA_VAR *) & pmb->mb.un.varReadLA; + + /* Get Loop Map information */ + if (mp) { + memcpy(&phba->alpa_map[0], mp->virt, 128); + } else { + memset(&phba->alpa_map[0], 0, 128); + } + + if (((phba->fc_eventTag + 1) < la->eventTag) || + (phba->fc_eventTag == la->eventTag)) { + phba->fc_stat.LinkMultiEvent++; + if (la->attType == AT_LINK_UP) { + if (phba->fc_eventTag != 0) { + + lpfc_linkdown(phba); + } + } + } + + phba->fc_eventTag = la->eventTag; + + if (la->attType == AT_LINK_UP) { + phba->fc_stat.LinkUp++; + /* Link Up Event received */ + lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, + "%d:1303 Link Up Event x%x received " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, la->eventTag, phba->fc_eventTag, + la->granted_AL_PA, la->UlnkSpeed, + phba->alpa_map[0]); + + switch(la->UlnkSpeed) { + case LA_1GHZ_LINK: + phba->fc_linkspeed = LA_1GHZ_LINK; + break; + case LA_2GHZ_LINK: + phba->fc_linkspeed = LA_2GHZ_LINK; + break; + case LA_4GHZ_LINK: + phba->fc_linkspeed = LA_4GHZ_LINK; + break; + default: + phba->fc_linkspeed = LA_UNKNW_LINK; + break; + } + + if ((phba->fc_topology = la->topology) == TOPOLOGY_LOOP) { + + if (la->il) { + phba->fc_flag |= FC_LBIT; + } + + phba->fc_myDID = la->granted_AL_PA; + + i = la->un.lilpBde64.tus.f.bdeSize; + if (i == 0) { + phba->alpa_map[0] = 0; + } else { + if (phba->cfg_log_verbose + & LOG_LINK_EVENT) { + int numalpa, j, k; + union { + uint8_t pamap[16]; + struct { + uint32_t wd1; + uint32_t wd2; + uint32_t wd3; + uint32_t wd4; + } pa; + } un; + + numalpa = phba->alpa_map[0]; + j = 0; + while (j < numalpa) { + memset(un.pamap, 0, 16); + for (k = 1; j < numalpa; k++) { + un.pamap[k - 1] = + phba->alpa_map[j + + 1]; + j++; + if (k == 16) + break; + } + /* Link Up Event ALPA map */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_LINK_EVENT, + "%d:1304 Link Up Event " + "ALPA map Data: x%x " + "x%x x%x x%x\n", + phba->brd_no, + un.pa.wd1, un.pa.wd2, + un.pa.wd3, un.pa.wd4); + } + } + } + } else { + phba->fc_myDID = phba->fc_pref_DID; + phba->fc_flag |= FC_LBIT; + } + + lpfc_linkup(phba); + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + lpfc_read_sparam(phba, mbox); + mbox->mbox_cmpl = lpfc_mbx_cmpl_read_sparam; + lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)); + } + + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + phba->hba_state = LPFC_LOCAL_CFG_LINK; + lpfc_config_link(phba, mbox); + mbox->mbox_cmpl = lpfc_mbx_cmpl_config_link; + lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)); + } + } else { + phba->fc_stat.LinkDown++; + /* Link Down Event received */ + lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, + "%d:1305 Link Down Event x%x received " + "Data: x%x x%x x%x\n", + phba->brd_no, la->eventTag, phba->fc_eventTag, + phba->hba_state, phba->fc_flag); + + lpfc_linkdown(phba); + + /* turn on Link Attention interrupts - no CLEAR_LA needed */ + psli->sliinit.sli_flag |= LPFC_PROCESS_LA; + control = readl(phba->HCregaddr); + control |= HC_LAINT_ENA; + writel(control, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + } + + pmb->context1 = NULL; + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + return; +} + +/* + * This routine handles processing a REG_LOGIN mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + mb = &pmb->mb; + + ndlp = (struct lpfc_nodelist *) pmb->context2; + mp = (struct lpfc_dmabuf *) (pmb->context1); + + pmb->context1 = NULL; + + /* Good status, call state machine */ + lpfc_disc_state_machine(phba, ndlp, pmb, NLP_EVT_CMPL_REG_LOGIN); + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + + return; +} + +/* + * This routine handles processing a Fabric REG_LOGIN mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_nodelist *ndlp; + struct lpfc_nodelist *ndlp_fdmi; + + + psli = &phba->sli; + mb = &pmb->mb; + + ndlp = (struct lpfc_nodelist *) pmb->context2; + mp = (struct lpfc_dmabuf *) (pmb->context1); + + if (mb->mbxStatus) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + mempool_free( ndlp, phba->nlp_mem_pool); + + /* FLOGI failed, so just use loop map to make discovery list */ + lpfc_disc_list_loopmap(phba); + + /* Start discovery */ + lpfc_disc_start(phba); + return; + } + + pmb->context1 = NULL; + + if (ndlp->nlp_rpi != 0) + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + ndlp->nlp_rpi = mb->un.varWords[0]; + lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi); + ndlp->nlp_type |= NLP_FABRIC; + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + + if (phba->hba_state == LPFC_FABRIC_CFG_LINK) { + /* This NPort has been assigned an NPort_ID by the fabric as a + * result of the completed fabric login. Issue a State Change + * Registration (SCR) ELS request to the fabric controller + * (SCR_DID) so that this NPort gets RSCN events from the + * fabric. + */ + lpfc_issue_els_scr(phba, SCR_DID, 0); + + /* Allocate a new node instance. If the pool is empty, just + * start the discovery process and skip the Nameserver login + * process. This is attempted again later on. Otherwise, issue + * a Port Login (PLOGI) to the NameServer + */ + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) + == 0) { + lpfc_disc_start(phba); + } else { + lpfc_nlp_init(phba, ndlp, NameServer_DID); + ndlp->nlp_type |= NLP_FABRIC; + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + if (phba->cfg_fdmi_on) { + if ((ndlp_fdmi = mempool_alloc( + phba->nlp_mem_pool, + GFP_ATOMIC))) { + lpfc_nlp_init(phba, ndlp_fdmi, + FDMI_DID); + ndlp_fdmi->nlp_type |= NLP_FABRIC; + ndlp_fdmi->nlp_state = + NLP_STE_PLOGI_ISSUE; + lpfc_issue_els_plogi(phba, ndlp_fdmi, + 0); + } + } + } + } + + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + + return; +} + +/* + * This routine handles processing a NameServer REG_LOGIN mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + mb = &pmb->mb; + + ndlp = (struct lpfc_nodelist *) pmb->context2; + mp = (struct lpfc_dmabuf *) (pmb->context1); + + if (mb->mbxStatus) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + + /* RegLogin failed, so just use loop map to make discovery + list */ + lpfc_disc_list_loopmap(phba); + + /* Start discovery */ + lpfc_disc_start(phba); + return; + } + + pmb->context1 = NULL; + + if (ndlp->nlp_rpi != 0) + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + ndlp->nlp_rpi = mb->un.varWords[0]; + lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi); + ndlp->nlp_type |= NLP_FABRIC; + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + + if (phba->hba_state < LPFC_HBA_READY) { + /* Link up discovery requires Fabrib registration. */ + lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RNN_ID); + lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RSNN_NN); + lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RFT_ID); + } + + phba->fc_ns_retry = 0; + /* Good status, issue CT Request to NameServer */ + if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT)) { + /* Cannot issue NameServer Query, so finish up discovery */ + lpfc_disc_start(phba); + } + + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + + return; +} + +/* Put blp on the bind list */ +int +lpfc_consistent_bind_save(struct lpfc_hba * phba, struct lpfc_bindlist * blp) +{ + /* Put it at the end of the bind list */ + list_add_tail(&blp->nlp_listp, &phba->fc_nlpbind_list); + phba->fc_bind_cnt++; + + /* Add scsiid to BIND list */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0903 Add scsiid %d to BIND list " + "Data: x%x x%x x%x x%p\n", + phba->brd_no, blp->nlp_sid, phba->fc_bind_cnt, + blp->nlp_DID, blp->nlp_bind_type, blp); + + return (0); +} + +int +lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) +{ + struct lpfc_bindlist *blp; + struct lpfc_target *targetp; + struct lpfc_sli *psli; + psli = &phba->sli; + + /* Sanity check to ensure we are not moving to / from the same list */ + if((nlp->nlp_flag & NLP_LIST_MASK) == list) { + if(list != NLP_NO_LIST) + return(0); + } + + blp = nlp->nlp_listp_bind; + + switch(nlp->nlp_flag & NLP_LIST_MASK) { + case NLP_NO_LIST: /* Not on any list */ + break; + case NLP_UNUSED_LIST: + phba->fc_unused_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + break; + case NLP_PLOGI_LIST: + phba->fc_plogi_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + break; + case NLP_ADISC_LIST: + phba->fc_adisc_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + break; + case NLP_REGLOGIN_LIST: + phba->fc_reglogin_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + break; + case NLP_PRLI_LIST: + phba->fc_prli_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + break; + case NLP_UNMAPPED_LIST: + phba->fc_unmap_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + nlp->nlp_flag &= ~NLP_TGT_NO_SCSIID; + nlp->nlp_type &= ~NLP_FC_NODE; + phba->nport_event_cnt++; + break; + case NLP_MAPPED_LIST: + phba->fc_map_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + phba->nport_event_cnt++; + lpfc_set_failmask(phba, nlp, LPFC_DEV_DISAPPEARED, + LPFC_SET_BITMASK); + nlp->nlp_type &= ~NLP_FCP_TARGET; + targetp = nlp->nlp_Target; + if (targetp && (list != NLP_MAPPED_LIST)) { + nlp->nlp_Target = NULL; +#if defined(RHEL_FC) || defined(SLES_FC) + /* + * Do not block the target if the driver has just reset + * its interface to the hardware. + */ + if (phba->hba_state != LPFC_INIT_START) + lpfc_target_block(phba, targetp); +#endif + } + + break; + case NLP_NPR_LIST: + phba->fc_npr_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + /* Stop delay tmo if taking node off NPR list */ + if ((nlp->nlp_flag & NLP_DELAY_TMO) && + (list != NLP_NPR_LIST)) { + nlp->nlp_flag &= ~NLP_DELAY_TMO; + spin_unlock_irq(phba->host->host_lock); + del_timer_sync(&nlp->nlp_delayfunc); + spin_lock_irq(phba->host->host_lock); + if (!list_empty(&nlp->els_retry_evt.evt_listp)) + list_del_init(&nlp->els_retry_evt. + evt_listp); + if (nlp->nlp_flag & NLP_NPR_2B_DISC) { + nlp->nlp_flag &= ~NLP_NPR_2B_DISC; + if (phba->num_disc_nodes) { + /* Check to see if there are more + * PLOGIs to be sent + */ + lpfc_more_plogi(phba); + } + + + if (phba->num_disc_nodes == 0) { + phba->fc_flag &= ~FC_NDISC_ACTIVE; + lpfc_can_disctmo(phba); + + if (phba->fc_flag & FC_RSCN_MODE) { + /* Check to see if more RSCNs + * came in while we were + * processing this one. + */ + if((phba->fc_rscn_id_cnt==0) && + (!(phba->fc_flag & + FC_RSCN_DISCOVERY))) { + phba->fc_flag &= + ~FC_RSCN_MODE; + } + else { + lpfc_els_handle_rscn( + phba); + } + } + } + } + } + break; + } + + /* Add NPort to list */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_NODE, + "%d:0904 Add NPort x%x to %d list Data: x%x x%p\n", + phba->brd_no, + nlp->nlp_DID, list, nlp->nlp_flag, blp); + + nlp->nlp_listp_bind = NULL; + + switch(list) { + case NLP_NO_LIST: /* No list, just remove it */ +#if defined(SLES_FC) + targetp = NULL; + if (((nlp->nlp_DID & Fabric_DID_MASK) != Fabric_DID_MASK) && + (nlp->nlp_sid != NLP_NO_SID)) { + targetp = phba->device_queue_hash[nlp->nlp_sid]; + } +#endif + lpfc_nlp_remove(phba, nlp); + +#if defined(SLES_FC) + if (targetp && targetp->blocked) { + lpfc_target_unblock(phba, targetp); + } +#endif + + break; + case NLP_UNUSED_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the unused list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_unused_list); + phba->fc_unused_cnt++; + break; + case NLP_PLOGI_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the plogi list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_plogi_list); + phba->fc_plogi_cnt++; + break; + case NLP_ADISC_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the adisc list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_adisc_list); + phba->fc_adisc_cnt++; + break; + case NLP_REGLOGIN_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the reglogin list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_reglogin_list); + phba->fc_reglogin_cnt++; + break; + case NLP_PRLI_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the prli list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_prli_list); + phba->fc_prli_cnt++; + break; + case NLP_UNMAPPED_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the unmap list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_nlpunmap_list); + phba->fc_unmap_cnt++; + phba->nport_event_cnt++; + /* stop nodev tmo if running */ + if (nlp->nlp_flag & NLP_NODEV_TMO) { + nlp->nlp_flag &= ~NLP_NODEV_TMO; + spin_unlock_irq(phba->host->host_lock); + del_timer_sync(&nlp->nlp_tmofunc); + spin_lock_irq(phba->host->host_lock); + if (!list_empty(&nlp->nodev_timeout_evt. + evt_listp)) + list_del_init(&nlp->nodev_timeout_evt. + evt_listp); + } + nlp->nlp_type |= NLP_FC_NODE; + lpfc_set_failmask(phba, nlp, LPFC_DEV_DISCOVERY_INP, + LPFC_CLR_BITMASK); + break; + case NLP_MAPPED_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the map list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_nlpmap_list); + phba->fc_map_cnt++; + phba->nport_event_cnt++; + /* stop nodev tmo if running */ + if (nlp->nlp_flag & NLP_NODEV_TMO) { + nlp->nlp_flag &= ~NLP_NODEV_TMO; + spin_unlock_irq(phba->host->host_lock); + del_timer_sync(&nlp->nlp_tmofunc); + spin_lock_irq(phba->host->host_lock); + if (!list_empty(&nlp->nodev_timeout_evt. + evt_listp)) + list_del_init(&nlp->nodev_timeout_evt. + evt_listp); + } + nlp->nlp_type |= NLP_FCP_TARGET; + lpfc_set_failmask(phba, nlp, LPFC_DEV_DISAPPEARED, + LPFC_CLR_BITMASK); + lpfc_set_failmask(phba, nlp, LPFC_DEV_DISCOVERY_INP, + LPFC_CLR_BITMASK); + + targetp = NULL; + if (nlp->nlp_sid != NLP_NO_SID) + targetp = phba->device_queue_hash[nlp->nlp_sid]; + + if (targetp && targetp->pnode) { + nlp->nlp_Target = targetp; +#if defined(RHEL_FC) || defined(SLES_FC) + /* Unblock I/Os on target */ + if(targetp->blocked) + lpfc_target_unblock(phba, targetp); +#endif + } + break; + case NLP_NPR_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the npr list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_npr_list); + phba->fc_npr_cnt++; + + /* + * Sanity check for Fabric entity. + * Set nodev_tmo for NPR state, for Fabric use 1 sec. + */ + if (nlp->nlp_type & NLP_FABRIC) { + mod_timer(&nlp->nlp_tmofunc, jiffies + HZ); + } + else { + mod_timer(&nlp->nlp_tmofunc, + jiffies + HZ * phba->cfg_nodev_tmo); + } + nlp->nlp_flag |= NLP_NODEV_TMO; + nlp->nlp_flag &= ~NLP_RCV_PLOGI; + break; + case NLP_JUST_DQ: + break; + } + + if (blp) { + nlp->nlp_flag &= ~NLP_SEED_MASK; + nlp->nlp_Target = NULL; + lpfc_consistent_bind_save(phba, blp); + } + return (0); +} + +/* + * Start / ReStart rescue timer for Discovery / RSCN handling + */ +void +lpfc_set_disctmo(struct lpfc_hba * phba) +{ + uint32_t tmo; + + tmo = ((phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT + 3); + + mod_timer(&phba->fc_disctmo, jiffies + HZ * tmo); + phba->fc_flag |= FC_DISC_TMO; + + /* Start Discovery Timer state */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0247 Start Discovery Timer state x%x " + "Data: x%x x%lx x%x x%x\n", + phba->brd_no, + phba->hba_state, tmo, (unsigned long)&phba->fc_disctmo, + phba->fc_plogi_cnt, phba->fc_adisc_cnt); + + return; +} + +/* + * Cancel rescue timer for Discovery / RSCN handling + */ +int +lpfc_can_disctmo(struct lpfc_hba * phba) +{ + /* Turn off discovery timer if its running */ + if(phba->fc_flag & FC_DISC_TMO) { + phba->fc_flag &= ~FC_DISC_TMO; + spin_unlock_irq(phba->host->host_lock); + del_timer_sync(&phba->fc_disctmo); + spin_lock_irq(phba->host->host_lock); + phba->work_hba_events &= ~WORKER_DISC_TMO; + } + + /* Cancel Discovery Timer state */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0248 Cancel Discovery Timer state x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->hba_state, phba->fc_flag, + phba->fc_plogi_cnt, phba->fc_adisc_cnt); + + return (0); +} + +/* + * Check specified ring for outstanding IOCB on the SLI queue + * Return true if iocb matches the specified nport + */ +int +lpfc_check_sli_ndlp(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * iocb, struct lpfc_nodelist * ndlp) +{ + struct lpfc_sli *psli; + IOCB_t *icmd; + + psli = &phba->sli; + icmd = &iocb->iocb; + if (pring->ringno == LPFC_ELS_RING) { + switch (icmd->ulpCommand) { + case CMD_GEN_REQUEST64_CR: + if (icmd->ulpContext == (volatile ushort)ndlp->nlp_rpi) + return (1); + case CMD_ELS_REQUEST64_CR: + case CMD_XMIT_ELS_RSP64_CX: + if (iocb->context1 == (uint8_t *) ndlp) + return (1); + } + } else if (pring->ringno == psli->ip_ring) { + + } else if (pring->ringno == psli->fcp_ring) { + /* Skip match check if waiting to relogin to FCP target */ + if ((ndlp->nlp_type & NLP_FCP_TARGET) && + (ndlp->nlp_flag & NLP_DELAY_TMO)) { + return (0); + } + if (icmd->ulpContext == (volatile ushort)ndlp->nlp_rpi) { + return (1); + } + } else if (pring->ringno == psli->next_ring) { + + } + return (0); +} + +/* + * Free resources / clean up outstanding I/Os + * associated with nlp_rpi in the LPFC_NODELIST entry. + */ +static int +lpfc_no_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *iocb, *next_iocb; + IOCB_t *icmd; + uint32_t rpi, i; + + psli = &phba->sli; + rpi = ndlp->nlp_rpi; + if (rpi) { + /* Now process each ring */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, + list) { + /* + * Check to see if iocb matches the nport we are + * looking for + */ + if ((lpfc_check_sli_ndlp + (phba, pring, iocb, ndlp))) { + /* It matches, so deque and call compl + with an error */ + list_del(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = + IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = + IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, + iocb, iocb); + } else { + mempool_free(iocb, + phba->iocb_mem_pool); + } + } + } + /* Everything that matches on txcmplq will be returned + * by firmware with a no rpi error. + */ + } + } + return (0); +} + +/* + * Free rpi associated with LPFC_NODELIST entry. + * This routine is called from lpfc_freenode(), when we are removing + * a LPFC_NODELIST entry. It is also called if the driver initiates a + * LOGO that completes successfully, and we are waiting to PLOGI back + * to the remote NPort. In addition, it is called after we receive + * and unsolicated ELS cmd, send back a rsp, the rsp completes and + * we are waiting to PLOGI back to the remote NPort. + */ +int +lpfc_unreg_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + LPFC_MBOXQ_t *mbox; + + if (ndlp->nlp_rpi) { + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + lpfc_unreg_login(phba, ndlp->nlp_rpi, mbox); + mbox->mbox_cmpl=lpfc_sli_def_mbox_cmpl; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mbox, phba->mbox_mem_pool); + } + } + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + lpfc_no_rpi(phba, ndlp); + ndlp->nlp_rpi = 0; + lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCONNECTED, + LPFC_SET_BITMASK); + return 1; + } + return 0; +} + +/* + * Free resources associated with LPFC_NODELIST entry + * so it can be freed. + */ +static int +lpfc_freenode(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + struct lpfc_target *targetp; + LPFC_MBOXQ_t *mb, *nextmb; + LPFC_DISC_EVT_t *evtp, *next_evtp; + struct lpfc_dmabuf *mp; + struct lpfc_sli *psli; + int scsid; + + /* The psli variable gets rid of the long pointer deference. */ + psli = &phba->sli; + + /* Cleanup node for NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0900 Cleanup node for NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag, + ndlp->nlp_state, ndlp->nlp_rpi); + + lpfc_nlp_list(phba, ndlp, NLP_JUST_DQ); + + /* cleanup any ndlp on mbox q waiting for reglogin cmpl */ + if ((mb = psli->mbox_active)) { + if ((mb->mb.mbxCommand == MBX_REG_LOGIN64) && + (ndlp == (struct lpfc_nodelist *) mb->context2)) { + mb->context2 = NULL; + mb->mbox_cmpl = lpfc_sli_def_mbox_cmpl; + } + } + list_for_each_entry_safe(mb, nextmb, &psli->mboxq, list) { + if ((mb->mb.mbxCommand == MBX_REG_LOGIN64) && + (ndlp == (struct lpfc_nodelist *) mb->context2)) { + mp = (struct lpfc_dmabuf *) (mb->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + list_del(&mb->list); + mempool_free(mb, phba->mbox_mem_pool); + } + } + /* cleanup any ndlp on disc event q waiting for reglogin cmpl */ + list_for_each_entry_safe(evtp, next_evtp, &phba->dpc_disc, evt_listp) { + mb = (LPFC_MBOXQ_t *)(evtp->evt_arg1); + if ((evtp->evt == LPFC_EVT_MBOX) && + (mb->mb.mbxCommand == MBX_REG_LOGIN64) && + (ndlp == (struct lpfc_nodelist *) mb->context2)) { + mp = (struct lpfc_dmabuf *) (mb->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + mempool_free(mb, phba->mbox_mem_pool); + list_del_init(&evtp->evt_listp); + kfree(evtp); + } + } + + lpfc_els_abort(phba,ndlp,0); + if(ndlp->nlp_flag & NLP_NODEV_TMO) { + ndlp->nlp_flag &= ~NLP_NODEV_TMO; + spin_unlock_irq(phba->host->host_lock); + del_timer_sync(&ndlp->nlp_tmofunc); + spin_lock_irq(phba->host->host_lock); + if (!list_empty(&ndlp->nodev_timeout_evt. + evt_listp)) + list_del_init(&ndlp->nodev_timeout_evt. + evt_listp); + } + + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + spin_unlock_irq(phba->host->host_lock); + del_timer_sync(&ndlp->nlp_delayfunc); + spin_lock_irq(phba->host->host_lock); + if (!list_empty(&ndlp->els_retry_evt. + evt_listp)) + list_del_init(&ndlp->els_retry_evt. + evt_listp); + } + + lpfc_unreg_rpi(phba, ndlp); + + for(scsid=0;scsiddevice_queue_hash[scsid]; + /* First see if the SCSI ID has an allocated struct + lpfc_target */ + if (targetp) { + if (targetp->pnode == ndlp) { + targetp->pnode = NULL; + ndlp->nlp_Target = NULL; +#ifdef RHEL_FC + /* + * This code does not apply to SLES9 since there + * is no starget defined in the midlayer. + * Additionally, dynamic target discovery to the + * midlayer is not supported yet. + */ + if (targetp->starget) { + /* Remove SCSI target / SCSI Hotplug */ + lpfc_target_remove(phba, targetp); + } +#endif /* RHEL_FC */ + break; + } + } + } + return (0); +} + +/* + * Check to see if we can free the nlp back to the freelist. + * If we are in the middle of using the nlp in the discovery state + * machine, defer the free till we reach the end of the state machine. + */ +int +lpfc_nlp_remove(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + + if(ndlp->nlp_flag & NLP_NODEV_TMO) { + ndlp->nlp_flag &= ~NLP_NODEV_TMO; + spin_unlock_irq(phba->host->host_lock); + del_timer_sync(&ndlp->nlp_tmofunc); + spin_lock_irq(phba->host->host_lock); + if (!list_empty(&ndlp->nodev_timeout_evt. + evt_listp)) + list_del_init(&ndlp->nodev_timeout_evt. + evt_listp); + } + + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + spin_unlock_irq(phba->host->host_lock); + del_timer_sync(&ndlp->nlp_delayfunc); + spin_lock_irq(phba->host->host_lock); + if (!list_empty(&ndlp->els_retry_evt. + evt_listp)) + list_del_init(&ndlp->els_retry_evt. + evt_listp); + } + + if (ndlp->nlp_disc_refcnt) { + ndlp->nlp_flag |= NLP_DELAY_REMOVE; + } + else { + lpfc_freenode(phba, ndlp); + mempool_free( ndlp, phba->nlp_mem_pool); + } + return(0); +} + +static int +lpfc_matchdid(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, uint32_t did) +{ + D_ID mydid; + D_ID ndlpdid; + D_ID matchdid; + + if (did == Bcast_DID) + return (0); + + if (ndlp->nlp_DID == 0) { + return (0); + } + + /* First check for Direct match */ + if (ndlp->nlp_DID == did) + return (1); + + /* Next check for area/domain identically equals 0 match */ + mydid.un.word = phba->fc_myDID; + if ((mydid.un.b.domain == 0) && (mydid.un.b.area == 0)) { + return (0); + } + + matchdid.un.word = did; + ndlpdid.un.word = ndlp->nlp_DID; + if (matchdid.un.b.id == ndlpdid.un.b.id) { + if ((mydid.un.b.domain == matchdid.un.b.domain) && + (mydid.un.b.area == matchdid.un.b.area)) { + if ((ndlpdid.un.b.domain == 0) && + (ndlpdid.un.b.area == 0)) { + if (ndlpdid.un.b.id) + return (1); + } + return (0); + } + + matchdid.un.word = ndlp->nlp_DID; + if ((mydid.un.b.domain == ndlpdid.un.b.domain) && + (mydid.un.b.area == ndlpdid.un.b.area)) { + if ((matchdid.un.b.domain == 0) && + (matchdid.un.b.area == 0)) { + if (matchdid.un.b.id) + return (1); + } + } + } + return (0); +} + +/* Search for a nodelist entry on a specific list */ +struct lpfc_nodelist * +lpfc_findnode_wwpn(struct lpfc_hba * phba, uint32_t order, + struct lpfc_name * wwpn) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + uint32_t data1; + + if (order & NLP_SEARCH_UNMAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, + &phba->fc_nlpunmap_list, nlp_listp) { + if (memcmp(&ndlp->nlp_portname, wwpn, + sizeof(struct lpfc_name)) == 0) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node DID unmapped */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_NODE, + "%d:0911 FIND node DID unmapped" + " Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_MAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list, + nlp_listp) { + if (memcmp(&ndlp->nlp_portname, wwpn, + sizeof(struct lpfc_name)) == 0) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node DID mapped */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0901 FIND node DID mapped " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + /* no match found */ + return ((struct lpfc_nodelist *) 0); +} +/* Search for a nodelist entry on a specific list */ +struct lpfc_nodelist * +lpfc_findnode_wwnn(struct lpfc_hba * phba, uint32_t order, + struct lpfc_name * wwnn) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + uint32_t data1; + + if (order & NLP_SEARCH_UNMAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, + &phba->fc_nlpunmap_list, nlp_listp) { + if (memcmp(&ndlp->nlp_nodename, wwnn, + sizeof(struct lpfc_name)) == 0) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node DID unmapped */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0910 FIND node DID unmapped" + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_MAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list, + nlp_listp) { + if (memcmp(&ndlp->nlp_nodename, wwnn, + sizeof(struct lpfc_name)) == 0) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node did mapped */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0902 FIND node DID mapped " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + /* no match found */ + return ((struct lpfc_nodelist *) 0); +} +/* Search for a nodelist entry on a specific list */ +struct lpfc_nodelist * +lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order, uint32_t did) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + uint32_t data1; + + if (order & NLP_SEARCH_UNMAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, + &phba->fc_nlpunmap_list, nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node DID unmapped */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0929 FIND node DID unmapped" + " Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_MAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node DID mapped */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0930 FIND node DID mapped " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_PLOGI) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_plogi_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to PLOGI */ + /* FIND node DID plogi */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0908 FIND node DID plogi " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_ADISC) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to ADISC */ + /* FIND node DID adisc */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0931 FIND node DID adisc " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_REGLOGIN) { + list_for_each_entry_safe(ndlp, next_ndlp, + &phba->fc_reglogin_list, nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to REGLOGIN */ + /* FIND node DID reglogin */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0933 FIND node DID reglogin" + " Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_PRLI) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_prli_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to PRLI */ + /* FIND node DID prli */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0934 FIND node DID prli " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_NPR) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to NPR */ + /* FIND node DID npr */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0935 FIND node DID npr " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_UNUSED) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to UNUSED */ + /* FIND node DID unused */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0936 FIND node DID unused " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + /* FIND node did NOT FOUND */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_NODE, + "%d:0932 FIND node did x%x NOT FOUND Data: x%x\n", + phba->brd_no, did, order); + + /* no match found */ + return ((struct lpfc_nodelist *) 0); +} + +struct lpfc_nodelist * +lpfc_setup_disc_node(struct lpfc_hba * phba, uint32_t did) +{ + struct lpfc_nodelist *ndlp; + uint32_t flg; + + if((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, did)) == 0) { + if ((phba->hba_state == LPFC_HBA_READY) && + ((lpfc_rscn_payload_check(phba, did) == 0))) + return NULL; + ndlp = (struct lpfc_nodelist *) + mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC); + if (!ndlp) + return NULL; + lpfc_nlp_init(phba, ndlp, did); + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + return ndlp; + } + if ((phba->hba_state == LPFC_HBA_READY) && + (phba->fc_flag & FC_RSCN_MODE)) { + if(lpfc_rscn_payload_check(phba, did)) { + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + } + else { + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + ndlp = NULL; + } + } + else { + flg = ndlp->nlp_flag & NLP_LIST_MASK; + if ((flg == NLP_ADISC_LIST) || + (flg == NLP_PLOGI_LIST)) { + return NULL; + } + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + } + return ndlp; +} + +/* Build a list of nodes to discover based on the loopmap */ +void +lpfc_disc_list_loopmap(struct lpfc_hba * phba) +{ + int j; + uint32_t alpa, index; + + if (phba->hba_state <= LPFC_LINK_DOWN) { + return; + } + if (phba->fc_topology != TOPOLOGY_LOOP) { + return; + } + + /* Check for loop map present or not */ + if (phba->alpa_map[0]) { + for (j = 1; j <= phba->alpa_map[0]; j++) { + alpa = phba->alpa_map[j]; + + if (((phba->fc_myDID & 0xff) == alpa) || (alpa == 0)) { + continue; + } + lpfc_setup_disc_node(phba, alpa); + } + } else { + /* No alpamap, so try all alpa's */ + for (j = 0; j < FC_MAXLOOP; j++) { + /* If cfg_scan_down is set, start from highest + * ALPA (0xef) to lowest (0x1). + */ + if (phba->cfg_scan_down) + index = j; + else + index = FC_MAXLOOP - j - 1; + alpa = lpfcAlpaArray[index]; + if ((phba->fc_myDID & 0xff) == alpa) { + continue; + } + + lpfc_setup_disc_node(phba, alpa); + } + } + return; +} + +/* Start Link up / RSCN discovery on NPR list */ +void +lpfc_disc_start(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *mbox; + struct lpfc_nodelist *ndlp, *next_ndlp; + uint32_t did_changed, num_sent; + uint32_t clear_la_pending; + + psli = &phba->sli; + + if (phba->hba_state <= LPFC_LINK_DOWN) { + return; + } + if (phba->hba_state == LPFC_CLEAR_LA) + clear_la_pending = 1; + else + clear_la_pending = 0; + + if (phba->hba_state < LPFC_HBA_READY) { + phba->hba_state = LPFC_DISC_AUTH; + } + lpfc_set_disctmo(phba); + + if (phba->fc_prevDID == phba->fc_myDID) { + did_changed = 0; + } else { + did_changed = 1; + } + phba->fc_prevDID = phba->fc_myDID; + phba->num_disc_nodes = 0; + + /* Start Discovery state */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0202 Start Discovery hba state x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->hba_state, phba->fc_flag, + phba->fc_plogi_cnt, phba->fc_adisc_cnt); + + /* If our did changed, we MUST do PLOGI */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, + nlp_listp) { + if(ndlp->nlp_flag & NLP_NPR_2B_DISC) { + if(did_changed) + ndlp->nlp_flag &= ~NLP_NPR_ADISC; + } + } + + /* First do ADISCs - if any */ + num_sent = lpfc_els_disc_adisc(phba); + + if(num_sent) + return; + + if ((phba->hba_state < LPFC_HBA_READY) && (!clear_la_pending)) { + /* If we get here, there is nothing to ADISC */ + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + phba->hba_state = LPFC_CLEAR_LA; + lpfc_clear_la(phba, mbox); + mbox->mbox_cmpl = lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mbox, phba->mbox_mem_pool); + lpfc_disc_flush_list(phba); + psli->ring[(psli->ip_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->fcp_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->next_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + phba->hba_state = LPFC_HBA_READY; + } + } + } else { + /* Next do PLOGIs - if any */ + num_sent = lpfc_els_disc_plogi(phba); + + if(num_sent) + return; + + if (phba->fc_flag & FC_RSCN_MODE) { + /* Check to see if more RSCNs came in while we + * were processing this one. + */ + if ((phba->fc_rscn_id_cnt == 0) && + (!(phba->fc_flag & FC_RSCN_DISCOVERY))) { + phba->fc_flag &= ~FC_RSCN_MODE; + } else { + lpfc_els_handle_rscn(phba); + } + } + } + return; +} + +/* + * Ignore completion for all IOCBs on tx and txcmpl queue for ELS + * ring the match the sppecified nodelist. + */ +static void +lpfc_free_tx(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + struct lpfc_sli *psli; + IOCB_t *icmd; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_sli_ring *pring; + struct lpfc_dmabuf *mp; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + if (iocb->context1 != ndlp) { + continue; + } + icmd = &iocb->iocb; + if ((icmd->ulpCommand == CMD_ELS_REQUEST64_CR) || + (icmd->ulpCommand == CMD_XMIT_ELS_RSP64_CX)) { + + list_del(&iocb->list); + pring->txq_cnt--; + lpfc_els_free_iocb(phba, iocb); + } + } + + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + if (iocb->context1 != ndlp) { + continue; + } + icmd = &iocb->iocb; + if ((icmd->ulpCommand == CMD_ELS_REQUEST64_CR) || + (icmd->ulpCommand == CMD_XMIT_ELS_RSP64_CX)) { + + iocb->iocb_cmpl = NULL; + /* context2 = cmd, context2->next = rsp, context3 = + bpl */ + if (iocb->context2) { + /* Free the response IOCB before handling the + command. */ + + mp = (struct lpfc_dmabuf *) + (((struct lpfc_dmabuf *) (iocb->context2)) + ->list.next); + if (mp) { + /* Delay before releasing rsp buffer to + * give UNREG mbox a chance to take + * effect. + */ + list_add(&mp->list, + &phba->freebufList); + } + lpfc_mbuf_free(phba, + ((struct lpfc_dmabuf *) + iocb->context2)->virt, + ((struct lpfc_dmabuf *) + iocb->context2)->phys); + kfree(iocb->context2); + } + + if (iocb->context3) { + lpfc_mbuf_free(phba, + ((struct lpfc_dmabuf *) + iocb->context3)->virt, + ((struct lpfc_dmabuf *) + iocb->context3)->phys); + kfree(iocb->context3); + } + } + } + + return; +} + +void +lpfc_disc_flush_list(struct lpfc_hba * phba) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + + if (phba->fc_plogi_cnt) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_plogi_list, + nlp_listp) { + lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCONNECTED, + LPFC_SET_BITMASK); + lpfc_free_tx(phba, ndlp); + lpfc_nlp_remove(phba, ndlp); + } + } + if (phba->fc_adisc_cnt) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list, + nlp_listp) { + lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCONNECTED, + LPFC_SET_BITMASK); + lpfc_free_tx(phba, ndlp); + lpfc_nlp_remove(phba, ndlp); + } + } + return; +} + +/*****************************************************************************/ +/* + * NAME: lpfc_disc_timeout + * + * FUNCTION: Fibre Channel driver discovery timeout routine. + * + * EXECUTION ENVIRONMENT: interrupt only + * + * CALLED FROM: + * Timer function + * + * RETURNS: + * none + */ +/*****************************************************************************/ +void +lpfc_disc_timeout(unsigned long ptr) +{ + struct lpfc_hba *phba = (struct lpfc_hba *)ptr; + unsigned long flags = 0; + + if (unlikely(!phba)) + return; + + spin_lock_irqsave(phba->host->host_lock, flags); + if (!(phba->work_hba_events & WORKER_DISC_TMO)) { + phba->work_hba_events |= WORKER_DISC_TMO; + if (phba->dpc_wait) + up(phba->dpc_wait); + } + spin_unlock_irqrestore(phba->host->host_lock, flags); + return; +} + +static void +lpfc_disc_timeout_handler(struct lpfc_hba *phba) +{ + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + LPFC_MBOXQ_t *mbox; + + if (!phba) { + return; + } + if (!(phba->fc_flag & FC_DISC_TMO)) + return; + + psli = &phba->sli; + spin_lock_irq(phba->host->host_lock); + + phba->fc_flag &= ~FC_DISC_TMO; + + /* hba_state is identically LPFC_LOCAL_CFG_LINK while waiting for FAN */ + if (phba->hba_state == LPFC_LOCAL_CFG_LINK) { + /* FAN timeout */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_DISCOVERY, + "%d:0221 FAN timeout\n", + phba->brd_no); + + /* Forget about FAN, Start discovery by sending a FLOGI + * hba_state is identically LPFC_FLOGI while waiting for FLOGI + * cmpl + */ + phba->hba_state = LPFC_FLOGI; + lpfc_set_disctmo(phba); + lpfc_initial_flogi(phba); + goto out; + } + + /* hba_state is identically LPFC_FLOGI while waiting for FLOGI cmpl */ + if (phba->hba_state == LPFC_FLOGI) { + /* Initial FLOGI timeout */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0222 Initial FLOGI timeout\n", + phba->brd_no); + + /* Assume no Fabric and go on with discovery. + * Check for outstanding ELS FLOGI to abort. + */ + + /* FLOGI failed, so just use loop map to make discovery list */ + lpfc_disc_list_loopmap(phba); + + /* Start discovery */ + lpfc_disc_start(phba); + goto out; + } + + /* hba_state is identically LPFC_FABRIC_CFG_LINK while waiting for + NameServer login */ + if (phba->hba_state == LPFC_FABRIC_CFG_LINK) { + /* Timeout while waiting for NameServer login */ + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "%d:0223 Timeout while waiting for NameServer " + "login\n", phba->brd_no); + + /* Next look for NameServer ndlp */ + if ((ndlp = lpfc_findnode_did(phba, + NLP_SEARCH_ALL, NameServer_DID))) { + lpfc_nlp_remove(phba, ndlp); + } + /* Start discovery */ + lpfc_disc_start(phba); + goto out; + } + + /* Check for wait for NameServer Rsp timeout */ + if (phba->hba_state == LPFC_NS_QRY) { + /* NameServer Query timeout */ + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "%d:0224 NameServer Query timeout " + "Data: x%x x%x\n", + phba->brd_no, + phba->fc_ns_retry, LPFC_MAX_NS_RETRY); + + if ((ndlp = + lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED, + NameServer_DID))) { + if (phba->fc_ns_retry < LPFC_MAX_NS_RETRY) { + /* Try it one more time */ + if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT) == + 0) { + goto out; + } + } + phba->fc_ns_retry = 0; + } + + /* Nothing to authenticate, so CLEAR_LA right now */ + if (phba->hba_state != LPFC_CLEAR_LA) { + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + phba->hba_state = LPFC_CLEAR_LA; + lpfc_clear_la(phba, mbox); + mbox->mbox_cmpl = lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, phba->mbox_mem_pool); + goto clrlaerr; + } + } else { + /* Device Discovery completion error */ + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "%d:0226 Device Discovery " + "completion error\n", + phba->brd_no); + phba->hba_state = LPFC_HBA_ERROR; + } + } + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + /* Setup and issue mailbox INITIALIZE LINK command */ + lpfc_linkdown(phba); + lpfc_init_link(phba, mbox, + phba->cfg_topology, + phba->cfg_link_speed); + mbox->mb.un.varInitLnk.lipsr_AL_PA = 0; + mbox->mbox_cmpl=lpfc_sli_def_mbox_cmpl; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mbox, phba->mbox_mem_pool); + } + } + goto out; + } + + if (phba->hba_state == LPFC_DISC_AUTH) { + /* Node Authentication timeout */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0227 Node Authentication timeout\n", + phba->brd_no); + lpfc_disc_flush_list(phba); + if (phba->hba_state != LPFC_CLEAR_LA) { + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + phba->hba_state = LPFC_CLEAR_LA; + lpfc_clear_la(phba, mbox); + mbox->mbox_cmpl = lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, phba->mbox_mem_pool); + goto clrlaerr; + } + } + } + goto out; + } + + if (phba->hba_state == LPFC_CLEAR_LA) { + /* CLEAR LA timeout */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0228 CLEAR LA timeout\n", + phba->brd_no); +clrlaerr: + lpfc_disc_flush_list(phba); + psli->ring[(psli->ip_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->fcp_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->next_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; + phba->hba_state = LPFC_HBA_READY; + goto out; + } + + if ((phba->hba_state == LPFC_HBA_READY) && + (phba->fc_flag & FC_RSCN_MODE)) { + /* RSCN timeout */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0231 RSCN timeout Data: x%x x%x\n", + phba->brd_no, + phba->fc_ns_retry, LPFC_MAX_NS_RETRY); + + /* Cleanup any outstanding ELS commands */ + lpfc_els_flush_cmd(phba); + + lpfc_els_flush_rscn(phba); + lpfc_disc_flush_list(phba); + goto out; + } + +out: + spin_unlock_irq(phba->host->host_lock); + return; +} + +/*****************************************************************************/ +/* + * NAME: lpfc_scan_timeout + * + * FUNCTION: Fibre Channel driver scsi_scan_host timeout routine. + * + * EXECUTION ENVIRONMENT: interrupt only + * + * CALLED FROM: + * Timer function + * + * RETURNS: + * none + */ +/*****************************************************************************/ +void +lpfc_scan_timeout(unsigned long ptr) +{ + struct lpfc_hba *phba; + unsigned long iflag; + + phba = (struct lpfc_hba *)ptr; + if (!phba) { + return; + } + spin_lock_irqsave(phba->host->host_lock, iflag); + phba->fc_flag &= ~FC_SCSI_SCAN_TMO; + lpfc_discq_post_event(phba, NULL, NULL, LPFC_EVT_SCAN); + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + +static void +lpfc_nodev_timeout(unsigned long ptr) +{ + struct lpfc_hba *phba; + struct lpfc_nodelist *ndlp; + unsigned long iflag; + LPFC_DISC_EVT_t *evtp; + + ndlp = (struct lpfc_nodelist *)ptr; + phba = ndlp->nlp_phba; + evtp = &ndlp->nodev_timeout_evt; + spin_lock_irqsave(phba->host->host_lock, iflag); + + if (!list_empty(&evtp->evt_listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; + } + evtp->evt_arg1 = ndlp; + evtp->evt = LPFC_EVT_NODEV_TMO; + list_add_tail(&evtp->evt_listp, &phba->dpc_disc); + if (phba->dpc_wait) + up(phba->dpc_wait); + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + + +/*****************************************************************************/ +/* + * NAME: lpfc_find_target + * + * FUNCTION: Fibre Channel bus/target/LUN to struct lpfc_target lookup + * + * EXECUTION ENVIRONMENT: + * + * RETURNS: + * ptr to desired struct lpfc_target + */ +/*****************************************************************************/ +struct lpfc_target * +lpfc_find_target(struct lpfc_hba * phba, uint32_t tgt, + struct lpfc_nodelist *nlp) +{ + struct lpfc_target *targetp = NULL; + int found = 0, i; + struct list_head *listp; + struct list_head *node_list[6]; + + if (tgt == NLP_NO_SID) + return NULL; + + if(!nlp) { + /* Search over all lists other than fc_nlpunmap_list */ + node_list[0] = &phba->fc_npr_list; + node_list[1] = &phba->fc_nlpmap_list; /* Skip fc_nlpunmap */ + node_list[2] = &phba->fc_prli_list; + node_list[3] = &phba->fc_reglogin_list; + node_list[4] = &phba->fc_adisc_list; + node_list[5] = &phba->fc_plogi_list; + + for (i=0; i < 6 && !found; i++) { + listp = node_list[i]; + if (list_empty(listp)) + continue; + list_for_each_entry(nlp, listp, nlp_listp) { + if (tgt == nlp->nlp_sid) { + found = 1; + break; + } + } + } + + if (!found) + return NULL; + } + + targetp = phba->device_queue_hash[tgt]; + + /* First see if the SCSI ID has an allocated struct lpfc_target */ + if (!targetp) { + targetp = kmalloc(sizeof (struct lpfc_target), GFP_ATOMIC); + if (!targetp) + return NULL; + + memset(targetp, 0, sizeof (struct lpfc_target)); +#ifdef SLES_FC + init_timer(&targetp->dev_loss_timer); +#endif + phba->device_queue_hash[tgt] = targetp; + targetp->scsi_id = tgt; + + /* Create SCSI Target */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY | LOG_FCP, + "%d:0204 Create SCSI Target %d\n", + phba->brd_no, tgt); + } + + if (targetp->pnode == NULL) { + targetp->pnode = nlp; + nlp->nlp_Target = targetp; +#ifdef RHEL_FC + /* + * This code does not apply to SLES9 since there is no + * starget defined in the midlayer. Additionally, + * dynamic target discovery to the midlayer is not + * supported yet. + */ + if(!(phba->fc_flag & FC_LOADING)) { + /* Add SCSI target / SCSI Hotplug if called + * after initial driver load. + */ + lpfc_target_add(phba, targetp); + } +#endif /* RHEL_FC */ + } + else { + if(targetp->pnode != nlp) { + /* + * The scsi-id exists but the nodepointer is different. + * We are reassigning the scsi-id. Attach the nodelist + * pointer to the correct target. This is common + * with a target side cable swap. + */ + if (targetp->pnode->nlp_Target != targetp) + targetp->pnode = nlp; + } + } + nlp->nlp_Target = targetp; + return (targetp); +} + +/* + * lpfc_set_failmask + * Set, or clear, failMask bits in struct lpfc_nodelist + */ +void +lpfc_set_failmask(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, uint32_t bitmask, uint32_t flag) +{ + uint32_t oldmask; + uint32_t changed; + + /* Failmask change on NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0208 Failmask change on NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, + ndlp->nlp_DID, ndlp->nlp_failMask, bitmask, flag); + + if (flag == LPFC_SET_BITMASK) { + oldmask = ndlp->nlp_failMask; + /* Set failMask event */ + ndlp->nlp_failMask |= bitmask; + if (oldmask != ndlp->nlp_failMask) { + changed = 1; + } else { + changed = 0; + } + + } else { + /* Clear failMask event */ + ndlp->nlp_failMask &= ~bitmask; + changed = 1; + } + return; +} + +/* + * This routine handles processing a NameServer REG_LOGIN mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + mb = &pmb->mb; + + ndlp = (struct lpfc_nodelist *) pmb->context2; + mp = (struct lpfc_dmabuf *) (pmb->context1); + + pmb->context1 = NULL; + + if (ndlp->nlp_rpi != 0) + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + ndlp->nlp_rpi = mb->un.varWords[0]; + lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi); + ndlp->nlp_type |= NLP_FABRIC; + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + + /* Start issuing Fabric-Device Management Interface (FDMI) + * command to 0xfffffa (FDMI well known port) + */ + if (phba->cfg_fdmi_on == 1) { + lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_DHBA); + } else { + /* + * Delay issuing FDMI command if fdmi-on=2 + * (supporting RPA/hostnmae) + */ + mod_timer(&phba->fc_fdmitmo, jiffies + HZ * 60); + } + + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + + return; +} + +/* + * This routine looks up the ndlp hash + * table for the given RPI. If rpi found + * it return the node list pointer + * else return 0. + */ +struct lpfc_nodelist * +lpfc_findnode_rpi(struct lpfc_hba * phba, uint16_t rpi) +{ + struct lpfc_nodelist *ret; + + ret = phba->fc_nlplookup[LPFC_RPI_HASH_FUNC(rpi)]; + while ((ret != 0) && (ret->nlp_rpi != rpi)) { + ret = ret->nlp_rpi_hash_next; + } + return ret; +} + +/* + * This routine looks up the ndlp hash table for the + * given RPI. If rpi found it return the node list + * pointer else return 0 after deleting the entry + * from hash table. + */ +struct lpfc_nodelist * +lpfc_findnode_remove_rpi(struct lpfc_hba * phba, uint16_t rpi) +{ + struct lpfc_nodelist *ret, *temp;; + + ret = phba->fc_nlplookup[LPFC_RPI_HASH_FUNC(rpi)]; + if (ret == 0) + return NULL; + + if (ret->nlp_rpi == rpi) { + phba->fc_nlplookup[LPFC_RPI_HASH_FUNC(rpi)] = + ret->nlp_rpi_hash_next; + ret->nlp_rpi_hash_next = NULL; + return ret; + } + + while ((ret->nlp_rpi_hash_next != 0) && + (ret->nlp_rpi_hash_next->nlp_rpi != rpi)) { + ret = ret->nlp_rpi_hash_next; + } + + if (ret->nlp_rpi_hash_next != 0) { + temp = ret->nlp_rpi_hash_next; + ret->nlp_rpi_hash_next = temp->nlp_rpi_hash_next; + temp->nlp_rpi_hash_next = NULL; + return temp; + } else { + return NULL; + } +} + +/* + * This routine adds the node list entry to the + * ndlp hash table. + */ +void +lpfc_addnode_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint16_t rpi) +{ + + uint32_t index; + + index = LPFC_RPI_HASH_FUNC(rpi); + ndlp->nlp_rpi_hash_next = phba->fc_nlplookup[index]; + phba->fc_nlplookup[index] = ndlp; + return; +} + +void +lpfc_nlp_init(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint32_t did) +{ + memset(ndlp, 0, sizeof (struct lpfc_nodelist)); + INIT_LIST_HEAD(&ndlp->nodev_timeout_evt.evt_listp); + INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp); + init_timer(&ndlp->nlp_tmofunc); + ndlp->nlp_tmofunc.function = lpfc_nodev_timeout; + ndlp->nlp_tmofunc.data = (unsigned long)ndlp; + init_timer(&ndlp->nlp_delayfunc); + ndlp->nlp_delayfunc.function = lpfc_els_retry_delay; + ndlp->nlp_delayfunc.data = (unsigned long)ndlp; + ndlp->nlp_DID = did; + ndlp->nlp_phba = phba; + ndlp->nlp_sid = NLP_NO_SID; + return; +} + --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_nportdisc.c 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_nportdisc.c 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,2038 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_nportdisc.c 1.160.1.2 2005/06/13 17:16:39EDT sf_support Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" + +extern uint8_t lpfcAlpaArray[]; + + +/* Called to verify a rcv'ed ADISC was intended for us. */ +static int +lpfc_check_adisc(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + struct lpfc_name * nn, struct lpfc_name * pn) +{ + /* Compare the ADISC rsp WWNN / WWPN matches our internal node + * table entry for that node. + */ + if (memcmp(nn, &ndlp->nlp_nodename, sizeof (struct lpfc_name)) != 0) + return (0); + + if (memcmp(pn, &ndlp->nlp_portname, sizeof (struct lpfc_name)) != 0) + return (0); + + /* we match, return success */ + return (1); +} + + +int +lpfc_check_sparm(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, struct serv_parm * sp, + uint32_t class) +{ + volatile struct serv_parm *hsp = &phba->fc_sparam; + /* First check for supported version */ + + /* Next check for class validity */ + if (sp->cls1.classValid) { + + if (sp->cls1.rcvDataSizeMsb > hsp->cls1.rcvDataSizeMsb) + sp->cls1.rcvDataSizeMsb = hsp->cls1.rcvDataSizeMsb; + if (sp->cls1.rcvDataSizeLsb > hsp->cls1.rcvDataSizeLsb) + sp->cls1.rcvDataSizeLsb = hsp->cls1.rcvDataSizeLsb; + } else if (class == CLASS1) { + return (0); + } + + if (sp->cls2.classValid) { + + if (sp->cls2.rcvDataSizeMsb > hsp->cls2.rcvDataSizeMsb) + sp->cls2.rcvDataSizeMsb = hsp->cls2.rcvDataSizeMsb; + if (sp->cls2.rcvDataSizeLsb > hsp->cls2.rcvDataSizeLsb) + sp->cls2.rcvDataSizeLsb = hsp->cls2.rcvDataSizeLsb; + } else if (class == CLASS2) { + return (0); + } + + if (sp->cls3.classValid) { + + if (sp->cls3.rcvDataSizeMsb > hsp->cls3.rcvDataSizeMsb) + sp->cls3.rcvDataSizeMsb = hsp->cls3.rcvDataSizeMsb; + if (sp->cls3.rcvDataSizeLsb > hsp->cls3.rcvDataSizeLsb) + sp->cls3.rcvDataSizeLsb = hsp->cls3.rcvDataSizeLsb; + } else if (class == CLASS3) { + return (0); + } + + if (sp->cmn.bbRcvSizeMsb > hsp->cmn.bbRcvSizeMsb) + sp->cmn.bbRcvSizeMsb = hsp->cmn.bbRcvSizeMsb; + if (sp->cmn.bbRcvSizeLsb > hsp->cmn.bbRcvSizeLsb) + sp->cmn.bbRcvSizeLsb = hsp->cmn.bbRcvSizeLsb; + + /* If check is good, copy wwpn wwnn into ndlp */ + memcpy(&ndlp->nlp_nodename, &sp->nodeName, sizeof (struct lpfc_name)); + memcpy(&ndlp->nlp_portname, &sp->portName, sizeof (struct lpfc_name)); + return (1); +} + +static void * +lpfc_check_elscmpl_iocb(struct lpfc_hba * phba, + struct lpfc_iocbq *cmdiocb, + struct lpfc_iocbq *rspiocb) +{ + struct lpfc_dmabuf *pcmd, *prsp; + uint32_t *lp; + void *ptr; + IOCB_t *irsp; + + irsp = &rspiocb->iocb; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + /* For lpfc_els_abort, context2 could be zero'ed to delay + * freeing associated memory till after ABTS completes. + */ + if (pcmd) { + prsp = (struct lpfc_dmabuf *) pcmd->list.next; + lp = (uint32_t *) prsp->virt; + + ptr = (void *)((uint8_t *)lp + sizeof(uint32_t)); + } + else { + /* Force ulpStatus error since we are returning NULL ptr */ + if (!(irsp->ulpStatus)) { + irsp->ulpStatus = IOSTAT_LOCAL_REJECT; + irsp->un.ulpWord[4] = IOERR_SLI_ABORTED; + } + ptr = NULL; + } + return (ptr); +} + + +/* + * Free resources / clean up outstanding I/Os + * associated with a LPFC_NODELIST entry. This + * routine effectively results in a "software abort". + */ +int +lpfc_els_abort(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + int send_abts) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *iocb, *next_iocb, *saveq; + IOCB_t *icmd; + int found = 0; + LPFC_DISC_EVT_t *evtp, *next_evtp; + + /* Abort outstanding I/O on NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0205 Abort outstanding I/O on NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag, + ndlp->nlp_state, ndlp->nlp_rpi); + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + /* Abort all the ELS iocbs in the dpc thread. */ + list_for_each_entry_safe(evtp, next_evtp, &phba->dpc_disc,evt_listp) { + if (evtp->evt != LPFC_EVT_SOL_IOCB) + continue; + + iocb = (struct lpfc_iocbq *)(evtp->evt_arg1); + saveq = (struct lpfc_iocbq *)(evtp->evt_arg2); + + if (lpfc_check_sli_ndlp(phba, pring, iocb, ndlp) == 0) + continue; + + list_del_init(&evtp->evt_listp); + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, saveq); + lpfc_evt_iocb_free(phba, saveq); + kfree(evtp); + } + + /* First check the txq */ + do { + found = 0; + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + /* Check to see if iocb matches the nport we are looking for */ + if ((lpfc_check_sli_ndlp(phba, pring, iocb, ndlp))) { + found = 1; + /* It matches, so deque and call compl with an error */ + list_del(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free(iocb, phba->iocb_mem_pool); + } + break; + } + } + + } while (found); + + /* Everything on txcmplq will be returned by firmware + * with a no rpi / linkdown / abort error. For ring 0, + * ELS discovery, we want to get rid of it right here. + */ + /* Next check the txcmplq */ + do { + found = 0; + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + /* Check to see if iocb matches the nport we are looking for */ + if ((lpfc_check_sli_ndlp (phba, pring, iocb, ndlp))) { + found = 1; + /* It matches, so deque and call compl with an error */ + list_del(&iocb->list); + pring->txcmplq_cnt--; + + icmd = &iocb->iocb; + /* If the driver is completing an ELS + * command early, flush it out of the firmware. + */ + if (send_abts && + (icmd->ulpCommand == CMD_ELS_REQUEST64_CR) && + (icmd->un.elsreq64.bdl.ulpIoTag32)) { + lpfc_sli_issue_abort_iotag32(phba, pring, iocb); + } + if (iocb->iocb_cmpl) { + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free(iocb, phba->iocb_mem_pool); + } + break; + } + } + } while (found); + + + /* If we are delaying issuing an ELS command, cancel it */ + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + spin_unlock_irq(phba->host->host_lock); + del_timer_sync(&ndlp->nlp_delayfunc); + spin_lock_irq(phba->host->host_lock); + if (!list_empty(&ndlp->els_retry_evt. + evt_listp)) + list_del_init(&ndlp->els_retry_evt. + evt_listp); + } + return (0); +} + +static int +lpfc_rcv_plogi(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, + struct lpfc_iocbq *cmdiocb) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + struct serv_parm *sp; + LPFC_MBOXQ_t *mbox; + struct ls_rjt stat; + + memset(&stat, 0, sizeof (struct ls_rjt)); + if (phba->hba_state <= LPFC_FLOGI) { + /* Before responding to PLOGI, check for pt2pt mode. + * If we are pt2pt, with an outstanding FLOGI, abort + * the FLOGI and resend it first. + */ + if (phba->fc_flag & FC_PT2PT) { + lpfc_els_abort_flogi(phba); + if(!(phba->fc_flag & FC_PT2PT_PLOGI)) { + /* If the other side is supposed to initiate + * the PLOGI anyway, just ACC it now and + * move on with discovery. + */ + phba->fc_edtov = FF_DEF_EDTOV; + phba->fc_ratov = FF_DEF_RATOV; + /* Start discovery - this should just do + CLEAR_LA */ + lpfc_disc_start(phba); + } + else { + lpfc_initial_flogi(phba); + } + } + else { + stat.un.b.lsRjtRsnCode = LSRJT_LOGICAL_BSY; + stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE; + goto out; + } + } + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t)); + if ((lpfc_check_sparm(phba, ndlp, sp, CLASS3) == 0)) { + /* Reject this request because invalid parameters */ + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS; + goto out; + } + icmd = &cmdiocb->iocb; + + /* PLOGI chkparm OK */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_ELS, + "%d:0114 PLOGI chkparm OK Data: x%x x%x x%x x%x\n", + phba->brd_no, + ndlp->nlp_DID, ndlp->nlp_state, ndlp->nlp_flag, + ndlp->nlp_rpi); + + if ((phba->cfg_fcp_class == 2) && + (sp->cls2.classValid)) { + ndlp->nlp_fcp_info |= CLASS2; + } else { + ndlp->nlp_fcp_info |= CLASS3; + } + + /* no need to reg_login if we are already in one of these states */ + switch(ndlp->nlp_state) { + case NLP_STE_NPR_NODE: + if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) + break; + case NLP_STE_REG_LOGIN_ISSUE: + case NLP_STE_PRLI_ISSUE: + case NLP_STE_UNMAPPED_NODE: + case NLP_STE_MAPPED_NODE: + lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL, 0); + return (1); + } + + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC)) == 0) { + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE; + goto out; + } + + if ((phba->fc_flag & FC_PT2PT) + && !(phba->fc_flag & FC_PT2PT_PLOGI)) { + /* rcv'ed PLOGI decides what our NPortId will be */ + phba->fc_myDID = icmd->un.rcvels.parmRo; + lpfc_config_link(phba, mbox); + mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mbox, phba->mbox_mem_pool); + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE; + goto out; + } + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE; + goto out; + } + lpfc_can_disctmo(phba); + } + + if(lpfc_reg_login(phba, icmd->un.rcvels.remoteID, + (uint8_t *) sp, mbox, 0)) { + mempool_free( mbox, phba->mbox_mem_pool); + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE; +out: + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + return (0); + } + + /* ACC PLOGI rsp command needs to execute first, + * queue this mbox command to be processed later. + */ + mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login; + mbox->context2 = ndlp; + ndlp->nlp_flag |= NLP_ACC_REGLOGIN; + + /* If there is an outstanding PLOGI issued, abort it before + * sending ACC rsp to PLOGI recieved. + */ + if(ndlp->nlp_state == NLP_STE_PLOGI_ISSUE) { + /* software abort outstanding PLOGI */ + lpfc_els_abort(phba, ndlp, 1); + } + ndlp->nlp_flag |= NLP_RCV_PLOGI; + lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox, 0); + return (1); +} + +static int +lpfc_rcv_padisc(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, + struct lpfc_iocbq *cmdiocb) +{ + struct lpfc_dmabuf *pcmd; + struct serv_parm *sp; + struct lpfc_name *pnn, *ppn; + struct ls_rjt stat; + ADISC *ap; + IOCB_t *icmd; + uint32_t *lp; + uint32_t cmd; + + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + cmd = *lp++; + if (cmd == ELS_CMD_ADISC) { + ap = (ADISC *) lp; + pnn = (struct lpfc_name *) & ap->nodeName; + ppn = (struct lpfc_name *) & ap->portName; + } else { + sp = (struct serv_parm *) lp; + pnn = (struct lpfc_name *) & sp->nodeName; + ppn = (struct lpfc_name *) & sp->portName; + } + + icmd = &cmdiocb->iocb; + if ((icmd->ulpStatus == 0) && + (lpfc_check_adisc(phba, ndlp, pnn, ppn))) { + if (cmd == ELS_CMD_ADISC) { + lpfc_els_rsp_adisc_acc(phba, cmdiocb, ndlp); + } + else { + lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, + NULL, 0); + } + return (1); + } + /* Reject this request because invalid parameters */ + stat.un.b.lsRjtRsvd0 = 0; + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS; + stat.un.b.vendorUnique = 0; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + + ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI; + /* 1 sec timeout */ + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ); + + ndlp->nlp_flag |= NLP_DELAY_TMO; + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + return (0); +} + +static int +lpfc_rcv_logo(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, + struct lpfc_iocbq *cmdiocb) +{ + /* Put ndlp on NPR list with 1 sec timeout for plogi, ACC logo */ + /* Only call LOGO ACC for first LOGO, this avoids sending unnecessary + * PLOGIs during LOGO storms from a device. + */ + ndlp->nlp_flag |= NLP_LOGO_ACC; + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + + if (!(ndlp->nlp_type & NLP_FABRIC)) { + /* Only try to re-login if this is NOT a Fabric Node */ + ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI; + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1); + ndlp->nlp_flag |= NLP_DELAY_TMO; + } + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + + ndlp->nlp_flag &= ~NLP_NPR_ADISC; + /* The driver has to wait until the ACC completes before it continues + * processing the LOGO. The action will resume in + * lpfc_cmpl_els_logo_acc routine. Since part of processing includes an + * unreg_login, the driver waits so the ACC does not get aborted. + */ + return (0); +} + +static int +lpfc_binding_found(struct lpfc_bindlist * blp, struct lpfc_nodelist * ndlp) +{ + uint16_t bindtype = blp->nlp_bind_type; + + if ((bindtype & FCP_SEED_DID) && + (ndlp->nlp_DID == be32_to_cpu(blp->nlp_DID))) { + return (1); + } else if ((bindtype & FCP_SEED_WWPN) && + (memcmp(&ndlp->nlp_portname, &blp->nlp_portname, + sizeof (struct lpfc_name)) == 0)) { + return (1); + } else if ((bindtype & FCP_SEED_WWNN) && + (memcmp(&ndlp->nlp_nodename, &blp->nlp_nodename, + sizeof (struct lpfc_name)) == 0)) { + return (1); + } + return (0); +} + +static int +lpfc_binding_useid(struct lpfc_hba * phba, uint32_t sid) +{ + struct lpfc_bindlist *blp; + + list_for_each_entry(blp, &phba->fc_nlpbind_list, nlp_listp) { + if (blp->nlp_sid == sid) { + return (1); + } + } + return (0); +} + +static int +lpfc_mapping_useid(struct lpfc_hba * phba, uint32_t sid) +{ + struct lpfc_nodelist *mapnode; + struct lpfc_bindlist *blp; + + list_for_each_entry(mapnode, &phba->fc_nlpmap_list, nlp_listp) { + blp = mapnode->nlp_listp_bind; + if (blp->nlp_sid == sid) { + return (1); + } + } + return (0); +} + +static struct lpfc_bindlist * +lpfc_create_binding(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, uint16_t index, + uint16_t bindtype) +{ + struct lpfc_bindlist *blp; + + if ((blp = mempool_alloc(phba->bind_mem_pool, GFP_ATOMIC))) { + memset(blp, 0, sizeof (struct lpfc_bindlist)); + switch (bindtype) { + case FCP_SEED_WWPN: + blp->nlp_bind_type = FCP_SEED_WWPN; + break; + case FCP_SEED_WWNN: + blp->nlp_bind_type = FCP_SEED_WWNN; + break; + case FCP_SEED_DID: + blp->nlp_bind_type = FCP_SEED_DID; + break; + } + blp->nlp_sid = index; + blp->nlp_DID = ndlp->nlp_DID; + memcpy(&blp->nlp_nodename, &ndlp->nlp_nodename, + sizeof (struct lpfc_name)); + memcpy(&blp->nlp_portname, &ndlp->nlp_portname, + sizeof (struct lpfc_name)); + + return (blp); + } + return NULL; +} + + +static struct lpfc_bindlist * +lpfc_consistent_bind_get(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + struct lpfc_bindlist *blp, *next_blp; + + /* check binding list */ + list_for_each_entry_safe(blp, next_blp, &phba->fc_nlpbind_list, + nlp_listp) { + if (lpfc_binding_found(blp, ndlp)) { + + /* take it off the binding list */ + phba->fc_bind_cnt--; + list_del_init(&blp->nlp_listp); + + /* Reassign scsi id to NPort */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY | LOG_FCP, + "%d:0213 Reassign scsi id x%x to " + "NPort x%x Data: x%x x%x x%x x%x\n", + phba->brd_no, + blp->nlp_sid, ndlp->nlp_DID, + blp->nlp_bind_type, ndlp->nlp_flag, + ndlp->nlp_state, ndlp->nlp_rpi); + + return (blp); + } + } + return NULL; +} + + +static struct lpfc_bindlist * +lpfc_consistent_bind_create(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + + struct lpfc_bindlist *blp; + uint16_t index; + + + /* NOTE: if scan-down = 2 and we have private loop, then we use + * AlpaArray to determine sid. + */ + if ((phba->cfg_fcp_bind_method == 4) && + ((phba->fc_flag & (FC_PUBLIC_LOOP | FC_FABRIC)) || + (phba->fc_topology != TOPOLOGY_LOOP))) { + /* Log message: ALPA based binding used on a non loop + topology */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_DISCOVERY, + "%d:0245 ALPA based bind method used on an HBA " + "which is in a nonloop topology Data: x%x\n", + phba->brd_no, + phba->fc_topology); + } + + if ((phba->cfg_fcp_bind_method == 4) && + !(phba->fc_flag & (FC_PUBLIC_LOOP | FC_FABRIC)) && + (phba->fc_topology == TOPOLOGY_LOOP)) { + for (index = 0; index < FC_MAXLOOP; index++) { + if (ndlp->nlp_DID == (uint32_t) lpfcAlpaArray[index]) { + if ((blp = + lpfc_create_binding(phba, ndlp, index, + FCP_SEED_DID))) { + return (blp); + } + goto errid; + } + } + } + + while (1) { + if ((lpfc_binding_useid(phba, phba->sid_cnt)) + || (lpfc_mapping_useid (phba, phba->sid_cnt))) { + + phba->sid_cnt++; + } else { + if ((blp = + lpfc_create_binding(phba, ndlp, + phba->sid_cnt, + phba->fcp_mapping))) { + blp->nlp_bind_type |= FCP_SEED_AUTO; + + phba->sid_cnt++; + return (blp); + } + goto errid; + } + } +errid: + /* Cannot assign scsi id on NPort */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY | LOG_FCP, + "%d:0230 Cannot assign scsi ID on NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, + ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, + ndlp->nlp_rpi); + + return NULL; +} + +static uint32_t +lpfc_assign_binding(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, struct lpfc_bindlist *blp) +{ + struct lpfc_target *targetp; + + targetp = lpfc_find_target(phba, blp->nlp_sid, ndlp); + if(!targetp) { + /* Cannot assign scsi id to NPort */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY | LOG_FCP, + "%d:0229 Cannot assign scsi id x%x to NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, blp->nlp_sid, + ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, + ndlp->nlp_rpi); + return(0); + } + ndlp->nlp_sid = blp->nlp_sid; + ndlp->nlp_flag &= ~NLP_SEED_MASK; + switch ((blp->nlp_bind_type & FCP_SEED_MASK)) { + case FCP_SEED_WWPN: + ndlp->nlp_flag |= NLP_SEED_WWPN; + break; + case FCP_SEED_WWNN: + ndlp->nlp_flag |= NLP_SEED_WWNN; + break; + case FCP_SEED_DID: + ndlp->nlp_flag |= NLP_SEED_DID; + break; + } + if (blp->nlp_bind_type & FCP_SEED_AUTO) { + ndlp->nlp_flag |= NLP_AUTOMAP; + } + /* Assign scsi id to NPort */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY | LOG_FCP, + "%d:0216 Assign scsi " + "id x%x to NPort x%x " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + ndlp->nlp_sid, ndlp->nlp_DID, + blp->nlp_bind_type, + ndlp->nlp_flag, ndlp->nlp_state, + ndlp->nlp_rpi); + return(1); +} + +static uint32_t +lpfc_disc_set_adisc(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp) +{ + /* Check config parameter use-adisc or FCP-2 */ + if ((phba->cfg_use_adisc == 0) && + !(phba->fc_flag & FC_RSCN_MODE)) { + if (!(ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE)) + return (0); + } + ndlp->nlp_flag |= NLP_NPR_ADISC; + return (1); +} + +static uint32_t +lpfc_disc_noop(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + /* This routine does nothing, just return the current state */ + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_disc_illegal(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0253 Illegal State Transition: node x%x event x%x, " + "state x%x Data: x%x x%x\n", + phba->brd_no, + ndlp->nlp_DID, evt, ndlp->nlp_state, ndlp->nlp_rpi, + ndlp->nlp_flag); + return (ndlp->nlp_state); +} + +/* Start of Discovery State Machine routines */ + +static uint32_t +lpfc_rcv_plogi_unused_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + if(lpfc_rcv_plogi(phba, ndlp, cmdiocb)) { + ndlp->nlp_state = NLP_STE_UNUSED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNUSED_LIST); + return (ndlp->nlp_state); + } + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_rcv_els_unused_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + lpfc_issue_els_logo(phba, ndlp, 0); + lpfc_nlp_list(phba, ndlp, NLP_UNUSED_LIST); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_unused_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + ndlp->nlp_flag |= NLP_LOGO_ACC; + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + lpfc_nlp_list(phba, ndlp, NLP_UNUSED_LIST); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_logo_unused_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_rm_unused_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_rcv_plogi_plogi_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, + struct lpfc_iocbq *cmdiocb, uint32_t evt) +{ + struct lpfc_dmabuf *pcmd; + struct serv_parm *sp; + uint32_t *lp; + struct ls_rjt stat; + int port_cmp; + + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t)); + + memset(&stat, 0, sizeof (struct ls_rjt)); + + /* For a PLOGI, we only accept if our portname is less + * than the remote portname. + */ + phba->fc_stat.elsLogiCol++; + port_cmp = memcmp(&phba->fc_portname, &sp->portName, + sizeof (struct lpfc_name)); + + if (port_cmp >= 0) { + /* Reject this request because the remote node will accept + ours */ + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_CMD_IN_PROGRESS; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + } + else { + lpfc_rcv_plogi(phba, ndlp, cmdiocb); + } /* if our portname was less */ + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_els_plogi_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + /* software abort outstanding PLOGI */ + lpfc_els_abort(phba, ndlp, 1); + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1); + ndlp->nlp_flag |= NLP_DELAY_TMO; + + if(evt == NLP_EVT_RCV_LOGO) { + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + } + else { + lpfc_issue_els_logo(phba, ndlp, 0); + } + + /* Put ndlp in npr list set plogi timer for 1 sec */ + ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI; + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_plogi_plogi_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb, *rspiocb; + struct lpfc_dmabuf *pcmd, *prsp; + uint32_t *lp; + IOCB_t *irsp; + struct serv_parm *sp; + LPFC_MBOXQ_t *mbox; + + cmdiocb = (struct lpfc_iocbq *) arg; + rspiocb = cmdiocb->context_un.rsp_iocb; + + if (ndlp->nlp_flag & NLP_ACC_REGLOGIN) { + return (ndlp->nlp_state); + } + + irsp = &rspiocb->iocb; + + if (irsp->ulpStatus == 0) { + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + prsp = (struct lpfc_dmabuf *) pcmd->list.next; + lp = (uint32_t *) prsp->virt; + + sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t)); + if ((lpfc_check_sparm(phba, ndlp, sp, CLASS3))) { + /* PLOGI chkparm OK */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_ELS, + "%d:0121 PLOGI chkparm OK " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + ndlp->nlp_DID, ndlp->nlp_state, + ndlp->nlp_flag, ndlp->nlp_rpi); + + if ((phba->cfg_fcp_class == 2) && + (sp->cls2.classValid)) { + ndlp->nlp_fcp_info |= CLASS2; + } else { + ndlp->nlp_fcp_info |= CLASS3; + } + + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + lpfc_unreg_rpi(phba, ndlp); + if (lpfc_reg_login + (phba, irsp->un.elsreq64.remoteID, + (uint8_t *) sp, mbox, 0) == 0) { + /* set_slim mailbox command needs to + * execute first, queue this command to + * be processed later. + */ + switch(ndlp->nlp_DID) { + case NameServer_DID: + mbox->mbox_cmpl = + lpfc_mbx_cmpl_ns_reg_login; + break; + case FDMI_DID: + mbox->mbox_cmpl = + lpfc_mbx_cmpl_fdmi_reg_login; + break; + default: + mbox->mbox_cmpl = + lpfc_mbx_cmpl_reg_login; + } + mbox->context2 = ndlp; + if (lpfc_sli_issue_mbox(phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + != MBX_NOT_FINISHED) { + ndlp->nlp_state = + NLP_STE_REG_LOGIN_ISSUE; + lpfc_nlp_list(phba, ndlp, + NLP_REGLOGIN_LIST); + return (ndlp->nlp_state); + } + mempool_free(mbox, phba->mbox_mem_pool); + } else { + mempool_free(mbox, phba->mbox_mem_pool); + } + } + } + } + + /* Free this node since the driver cannot login or has the wrong + sparm */ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_rm_plogi_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + /* software abort outstanding PLOGI */ + lpfc_els_abort(phba, ndlp, 1); + + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_recov_plogi_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + /* software abort outstanding PLOGI */ + lpfc_els_abort(phba, ndlp, 1); + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + /* software abort outstanding ADISC */ + lpfc_els_abort(phba, ndlp, 1); + + cmdiocb = (struct lpfc_iocbq *) arg; + + if(lpfc_rcv_plogi(phba, ndlp, cmdiocb)) { + return (ndlp->nlp_state); + } + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + /* software abort outstanding ADISC */ + lpfc_els_abort(phba, ndlp, 0); + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prlo_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + /* Treat like rcv logo */ + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_adisc_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb, *rspiocb; + struct lpfc_bindlist *blp; + IOCB_t *irsp; + ADISC *ap; + + cmdiocb = (struct lpfc_iocbq *) arg; + rspiocb = cmdiocb->context_un.rsp_iocb; + + ap = (ADISC *)lpfc_check_elscmpl_iocb(phba, cmdiocb, rspiocb); + irsp = &rspiocb->iocb; + + if ((irsp->ulpStatus) || + (!lpfc_check_adisc(phba, ndlp, &ap->nodeName, &ap->portName))) { + ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI; + /* 1 sec timeout */ + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ); + ndlp->nlp_flag |= NLP_DELAY_TMO; + + memset(&ndlp->nlp_nodename, 0, sizeof (struct lpfc_name)); + memset(&ndlp->nlp_portname, 0, sizeof (struct lpfc_name)); + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + lpfc_unreg_rpi(phba, ndlp); + return (ndlp->nlp_state); + } + /* move to mapped / unmapped list accordingly */ + /* Can we assign a SCSI Id to this NPort */ + if ((blp = lpfc_consistent_bind_get(phba, ndlp))) { + /* Next 4 lines MUST be in this order */ + if(lpfc_assign_binding(phba, ndlp, blp)) { + ndlp->nlp_state = NLP_STE_MAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_MAPPED_LIST); + ndlp->nlp_listp_bind = blp; + + lpfc_set_failmask(phba, ndlp, + (LPFC_DEV_DISCOVERY_INP|LPFC_DEV_DISCONNECTED), + LPFC_CLR_BITMASK); + + return (ndlp->nlp_state); + } + } + ndlp->nlp_flag |= NLP_TGT_NO_SCSIID; + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + + lpfc_set_failmask(phba, ndlp, + (LPFC_DEV_DISCOVERY_INP | LPFC_DEV_DISCONNECTED), + LPFC_CLR_BITMASK); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_device_rm_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + /* software abort outstanding ADISC */ + lpfc_els_abort(phba, ndlp, 1); + + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_recov_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + /* software abort outstanding ADISC */ + lpfc_els_abort(phba, ndlp, 1); + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + + lpfc_disc_set_adisc(phba, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_rcv_plogi(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prlo_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, + void *arg, uint32_t evt) +{ + LPFC_MBOXQ_t *pmb; + MAILBOX_t *mb; + uint32_t did; + + pmb = (LPFC_MBOXQ_t *) arg; + mb = &pmb->mb; + did = mb->un.varWords[1]; + if (mb->mbxStatus) { + /* RegLogin failed */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0246 RegLogin failed Data: x%x x%x x%x\n", + phba->brd_no, + did, mb->mbxStatus, phba->hba_state); + + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1); + ndlp->nlp_flag |= NLP_DELAY_TMO; + + lpfc_issue_els_logo(phba, ndlp, 0); + /* Put ndlp in npr list set plogi timer for 1 sec */ + ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI; + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + return (ndlp->nlp_state); + } + + if (ndlp->nlp_rpi != 0) + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + + ndlp->nlp_rpi = mb->un.varWords[0]; + lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi); + + /* Only if we are not a fabric nport do we issue PRLI */ + if (!(ndlp->nlp_type & NLP_FABRIC)) { + ndlp->nlp_state = NLP_STE_PRLI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST); + lpfc_issue_els_prli(phba, ndlp, 0); + } else { + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + } + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_device_rm_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_recov_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_rcv_plogi(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + /* Software abort outstanding PRLI before sending acc */ + lpfc_els_abort(phba, ndlp, 1); + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +/* This routine is envoked when we rcv a PRLO request from a nport + * we are logged into. We should send back a PRLO rsp setting the + * appropriate bits. + * NEXT STATE = PRLI_ISSUE + */ +static uint32_t +lpfc_rcv_prlo_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_prli_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb, *rspiocb; + IOCB_t *irsp; + PRLI *npr; + struct lpfc_bindlist *blp; + + cmdiocb = (struct lpfc_iocbq *) arg; + rspiocb = cmdiocb->context_un.rsp_iocb; + npr = (PRLI *)lpfc_check_elscmpl_iocb(phba, cmdiocb, rspiocb); + + irsp = &rspiocb->iocb; + if (irsp->ulpStatus) { + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCOVERY_INP, + LPFC_CLR_BITMASK); + return (ndlp->nlp_state); + } + + /* Check out PRLI rsp */ + if ((npr->acceptRspCode != PRLI_REQ_EXECUTED) || + (npr->prliType != PRLI_FCP_TYPE) || (npr->targetFunc != 1)) { + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + lpfc_set_failmask(phba, ndlp, + (LPFC_DEV_DISCOVERY_INP | LPFC_DEV_DISCONNECTED), + LPFC_CLR_BITMASK); + return (ndlp->nlp_state); + } + if (npr->Retry == 1) { + ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE; + } + + /* Can we assign a SCSI Id to this NPort */ + blp = lpfc_consistent_bind_get(phba, ndlp); + if (!blp) + blp = lpfc_consistent_bind_create(phba, ndlp); + if (blp) { + /* Next 4 lines MUST be in this order */ + if(lpfc_assign_binding(phba, ndlp, blp)) { + ndlp->nlp_state = NLP_STE_MAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_MAPPED_LIST); + ndlp->nlp_listp_bind = blp; + + lpfc_set_failmask(phba, ndlp, + (LPFC_DEV_DISCOVERY_INP|LPFC_DEV_DISCONNECTED), + LPFC_CLR_BITMASK); + return (ndlp->nlp_state); + } + } + ndlp->nlp_flag |= NLP_TGT_NO_SCSIID; + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + + lpfc_set_failmask(phba, ndlp, + (LPFC_DEV_DISCOVERY_INP | LPFC_DEV_DISCONNECTED), + LPFC_CLR_BITMASK); + return (ndlp->nlp_state); +} + +/*! lpfc_device_rm_prli_issue + * + * \pre + * \post + * \param phba + * \param ndlp + * \param arg + * \param evt + * \return uint32_t + * + * \b Description: + * This routine is envoked when we a request to remove a nport we are in the + * process of PRLIing. We should software abort outstanding prli, unreg + * login, send a logout. We will change node state to UNUSED_NODE, put it + * on plogi list so it can be freed when LOGO completes. + * + */ +static uint32_t +lpfc_device_rm_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + /* software abort outstanding PRLI */ + lpfc_els_abort(phba, ndlp, 1); + + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + + +/*! lpfc_device_recov_prli_issue + * + * \pre + * \post + * \param phba + * \param ndlp + * \param arg + * \param evt + * \return uint32_t + * + * \b Description: + * The routine is envoked when the state of a device is unknown, like + * during a link down. We should remove the nodelist entry from the + * unmapped list, issue a UNREG_LOGIN, do a software abort of the + * outstanding PRLI command, then free the node entry. + */ +static uint32_t +lpfc_device_recov_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + /* software abort outstanding PRLI */ + lpfc_els_abort(phba, ndlp, 1); + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_rcv_plogi(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prlo_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + /* Treat like rcv logo */ + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_device_recov_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + lpfc_disc_set_adisc(phba, ndlp); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_rcv_plogi(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prlo_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + /* flush the target */ + lpfc_sli_abort_iocb_tgt(phba, + &phba->sli.ring[phba->sli.fcp_ring], + ndlp->nlp_sid, LPFC_ABORT_ALLQ); + + /* Treat like rcv logo */ + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_device_recov_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + lpfc_disc_set_adisc(phba, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + /* Ignore PLOGI if we have an outstanding LOGO */ + if (ndlp->nlp_flag & NLP_LOGO_SND) { + return (ndlp->nlp_state); + } + + if(lpfc_rcv_plogi(phba, ndlp, cmdiocb)) { + ndlp->nlp_flag &= ~(NLP_NPR_ADISC | NLP_NPR_2B_DISC); + return (ndlp->nlp_state); + } + + /* send PLOGI immediately, move to PLOGI issue state */ + if(!(ndlp->nlp_flag & NLP_DELAY_TMO)) { + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + } + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp); + + if(!(ndlp->nlp_flag & NLP_DELAY_TMO)) { + if (ndlp->nlp_flag & NLP_NPR_ADISC) { + ndlp->nlp_state = NLP_STE_ADISC_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST); + lpfc_issue_els_adisc(phba, ndlp, 0); + } else { + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + } + } + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + + if(!(ndlp->nlp_flag & NLP_DELAY_TMO)) { + if (ndlp->nlp_flag & NLP_NPR_ADISC) { + ndlp->nlp_state = NLP_STE_ADISC_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST); + lpfc_issue_els_adisc(phba, ndlp, 0); + } else { + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + } + } + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prlo_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + if (ndlp->nlp_last_elscmd == (unsigned long)ELS_CMD_PLOGI) { + return (ndlp->nlp_state); + } else { + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + spin_unlock_irq(phba->host->host_lock); + del_timer_sync(&ndlp->nlp_delayfunc); + spin_lock_irq(phba->host->host_lock); + if (!list_empty(&ndlp->els_retry_evt. + evt_listp)) + list_del_init(&ndlp->els_retry_evt. + evt_listp); + } + } + + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_logo_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + lpfc_unreg_rpi(phba, ndlp); + /* This routine does nothing, just return the current state */ + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_reglogin_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + LPFC_MBOXQ_t *pmb; + MAILBOX_t *mb; + + pmb = (LPFC_MBOXQ_t *) arg; + mb = &pmb->mb; + + /* save rpi */ + if (ndlp->nlp_rpi != 0) + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + + ndlp->nlp_rpi = mb->un.varWords[0]; + lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_device_rm_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_recov_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + return (ndlp->nlp_state); +} + + +/* This next section defines the NPort Discovery State Machine */ + +/* There are 4 different double linked lists nodelist entries can reside on. + * The plogi list and adisc list are used when Link Up discovery or RSCN + * processing is needed. Each list holds the nodes that we will send PLOGI + * or ADISC on. These lists will keep track of what nodes will be effected + * by an RSCN, or a Link Up (Typically, all nodes are effected on Link Up). + * The unmapped_list will contain all nodes that we have successfully logged + * into at the Fibre Channel level. The mapped_list will contain all nodes + * that are mapped FCP targets. + */ +/* + * The bind list is a list of undiscovered (potentially non-existent) nodes + * that we have saved binding information on. This information is used when + * nodes transition from the unmapped to the mapped list. + */ +/* For UNUSED_NODE state, the node has just been allocated . + * For PLOGI_ISSUE and REG_LOGIN_ISSUE, the node is on + * the PLOGI list. For REG_LOGIN_COMPL, the node is taken off the PLOGI list + * and put on the unmapped list. For ADISC processing, the node is taken off + * the ADISC list and placed on either the mapped or unmapped list (depending + * on its previous state). Once on the unmapped list, a PRLI is issued and the + * state changed to PRLI_ISSUE. When the PRLI completion occurs, the state is + * changed to UNMAPPED_NODE. If the completion indicates a mapped + * node, the node is taken off the unmapped list. The binding list is checked + * for a valid binding, or a binding is automatically assigned. If binding + * assignment is unsuccessful, the node is left on the unmapped list. If + * binding assignment is successful, the associated binding list entry (if + * any) is removed, and the node is placed on the mapped list. + */ +/* + * For a Link Down, all nodes on the ADISC, PLOGI, unmapped or mapped + * lists will receive a DEVICE_RECOVERY event. If the linkdown or nodev timers + * expire, all effected nodes will receive a DEVICE_RM event. + */ +/* + * For a Link Up or RSCN, all nodes will move from the mapped / unmapped lists + * to either the ADISC or PLOGI list. After a Nameserver query or ALPA loopmap + * check, additional nodes may be added or removed (via DEVICE_RM) to / from + * the PLOGI or ADISC lists. Once the PLOGI and ADISC lists are populated, + * we will first process the ADISC list. 32 entries are processed initially and + * ADISC is initited for each one. Completions / Events for each node are + * funnelled thru the state machine. As each node finishes ADISC processing, it + * starts ADISC for any nodes waiting for ADISC processing. If no nodes are + * waiting, and the ADISC list count is identically 0, then we are done. For + * Link Up discovery, since all nodes on the PLOGI list are UNREG_LOGIN'ed, we + * can issue a CLEAR_LA and reenable Link Events. Next we will process the PLOGI + * list. 32 entries are processed initially and PLOGI is initited for each one. + * Completions / Events for each node are funnelled thru the state machine. As + * each node finishes PLOGI processing, it starts PLOGI for any nodes waiting + * for PLOGI processing. If no nodes are waiting, and the PLOGI list count is + * indentically 0, then we are done. We have now completed discovery / RSCN + * handling. Upon completion, ALL nodes should be on either the mapped or + * unmapped lists. + */ + +static void *lpfc_disc_action[NLP_STE_MAX_STATE * NLP_EVT_MAX_EVENT] = { + /* Action routine Event Current State */ + (void *)lpfc_rcv_plogi_unused_node, /* RCV_PLOGI UNUSED_NODE */ + (void *)lpfc_rcv_els_unused_node, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_unused_node, /* RCV_LOGO */ + (void *)lpfc_rcv_els_unused_node, /* RCV_ADISC */ + (void *)lpfc_rcv_els_unused_node, /* RCV_PDISC */ + (void *)lpfc_rcv_els_unused_node, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_cmpl_logo_unused_node, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_unused_node, /* DEVICE_RM */ + (void *)lpfc_disc_illegal, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_plogi_issue, /* RCV_PLOGI PLOGI_ISSUE */ + (void *)lpfc_rcv_els_plogi_issue, /* RCV_PRLI */ + (void *)lpfc_rcv_els_plogi_issue, /* RCV_LOGO */ + (void *)lpfc_rcv_els_plogi_issue, /* RCV_ADISC */ + (void *)lpfc_rcv_els_plogi_issue, /* RCV_PDISC */ + (void *)lpfc_rcv_els_plogi_issue, /* RCV_PRLO */ + (void *)lpfc_cmpl_plogi_plogi_issue, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_plogi_issue, /* DEVICE_RM */ + (void *)lpfc_device_recov_plogi_issue, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_adisc_issue, /* RCV_PLOGI ADISC_ISSUE */ + (void *)lpfc_rcv_prli_adisc_issue, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_adisc_issue, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_adisc_issue, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_adisc_issue, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_adisc_issue, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_cmpl_adisc_adisc_issue, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_adisc_issue, /* DEVICE_RM */ + (void *)lpfc_device_recov_adisc_issue, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_reglogin_issue, /* RCV_PLOGI REG_LOGIN_ISSUE */ + (void *)lpfc_rcv_prli_reglogin_issue, /* RCV_PLOGI */ + (void *)lpfc_rcv_logo_reglogin_issue, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_reglogin_issue, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_reglogin_issue, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_reglogin_issue, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_cmpl_reglogin_reglogin_issue,/* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_reglogin_issue, /* DEVICE_RM */ + (void *)lpfc_device_recov_reglogin_issue,/* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_prli_issue, /* RCV_PLOGI PRLI_ISSUE */ + (void *)lpfc_rcv_prli_prli_issue, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_prli_issue, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_prli_issue, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_prli_issue, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_prli_issue, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_cmpl_prli_prli_issue, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_prli_issue, /* DEVICE_RM */ + (void *)lpfc_device_recov_prli_issue, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_unmap_node, /* RCV_PLOGI UNMAPPED_NODE */ + (void *)lpfc_rcv_prli_unmap_node, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_unmap_node, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_unmap_node, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_unmap_node, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_unmap_node, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_disc_illegal, /* DEVICE_RM */ + (void *)lpfc_device_recov_unmap_node, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_mapped_node, /* RCV_PLOGI MAPPED_NODE */ + (void *)lpfc_rcv_prli_mapped_node, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_mapped_node, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_mapped_node, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_mapped_node, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_mapped_node, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_disc_illegal, /* DEVICE_RM */ + (void *)lpfc_device_recov_mapped_node, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_npr_node, /* RCV_PLOGI NPR_NODE */ + (void *)lpfc_rcv_prli_npr_node, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_npr_node, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_npr_node, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_npr_node, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_npr_node, /* RCV_PRLO */ + (void *)lpfc_disc_noop, /* CMPL_PLOGI */ + (void *)lpfc_disc_noop, /* CMPL_PRLI */ + (void *)lpfc_cmpl_logo_npr_node, /* CMPL_LOGO */ + (void *)lpfc_disc_noop, /* CMPL_ADISC */ + (void *)lpfc_cmpl_reglogin_npr_node, /* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_npr_node, /* DEVICE_RM */ + (void *)lpfc_device_recov_npr_node, /* DEVICE_RECOVERY */ +}; + +int +lpfc_disc_state_machine(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + uint32_t cur_state, rc; + uint32_t(*func) (struct lpfc_hba *, struct lpfc_nodelist *, void *, + uint32_t); + + ndlp->nlp_disc_refcnt++; + cur_state = ndlp->nlp_state; + + /* DSM in event on NPort in state */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0211 DSM in event x%x on NPort x%x in state %d " + "Data: x%x\n", + phba->brd_no, + evt, ndlp->nlp_DID, cur_state, ndlp->nlp_flag); + + func = (uint32_t(*)(struct lpfc_hba *, struct lpfc_nodelist *, void *, + uint32_t)) + lpfc_disc_action[(cur_state * NLP_EVT_MAX_EVENT) + evt]; + rc = (func) (phba, ndlp, arg, evt); + + /* DSM out state on NPort */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0212 DSM out state %d on NPort x%x Data: x%x\n", + phba->brd_no, + rc, ndlp->nlp_DID, ndlp->nlp_flag); + + ndlp->nlp_disc_refcnt--; + + /* Check to see if ndlp removal is deferred */ + if ((ndlp->nlp_disc_refcnt == 0) + && (ndlp->nlp_flag & NLP_DELAY_REMOVE)) { + + ndlp->nlp_flag &= ~NLP_DELAY_REMOVE; + lpfc_nlp_remove(phba, ndlp); + return (NLP_STE_FREED_NODE); + } + if (rc == NLP_STE_FREED_NODE) + return (NLP_STE_FREED_NODE); + ndlp->nlp_state = rc; + return (rc); +} --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/Makefile 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/Makefile 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,7 @@ +# Driver for Emulex LightPulse fibre channel host bus adapters. +EXTRA_CFLAGS += -DRHEL_FC +obj-$(CONFIG_SCSI_LPFC) := lpfc.o + +lpfc-objs := lpfc_mem.o lpfc_sli.o lpfc_ct.o lpfc_els.o \ +lpfc_hbadisc.o lpfc_init.o lpfc_mbox.o lpfc_nportdisc.o lpfc_scsiport.o \ +lpfc_fcp.o --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc.h 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc.h 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,464 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc.h 1.143.2.2 2005/06/13 17:16:00EDT sf_support Exp $ + */ + +#ifndef _H_LPFC +#define _H_LPFC + +struct lpfc_sli2_slim; + +#define LPFC_MAX_TARGET 256 /* max nunber of targets + supported */ +#define LPFC_MAX_DISC_THREADS 64 /* max outstanding discovery els + requests */ +#define LPFC_MAX_NS_RETRY 3 /* Try to get to the NameServer + 3 times and then give up. */ +#define LPFC_DFT_HBA_Q_DEPTH 2048 /* max cmds per hba */ +#define LPFC_LC_HBA_Q_DEPTH 1024 /* max cmds per low cost hba */ +#define LPFC_LP101_HBA_Q_DEPTH 128 /* max cmds per low cost hba */ + +/* Define the SLIM2 page size. */ +#define LPFC_SLIM2_PAGE_AREA 8192 + +/* Define macros for 64 bit support */ +#define putPaddrLow(addr) ((uint32_t) (0xffffffff & (u64)(addr))) +#define putPaddrHigh(addr) ((uint32_t) (0xffffffff & (((u64)(addr))>>32))) +#define getPaddr(high, low) ((dma_addr_t)( \ + (( (u64)(high)<<16 ) << 16)|( (u64)(low)))) +/* Provide maximum configuration definitions. */ +#define LPFC_DRVR_TIMEOUT 16 /* driver iocb timeout value in sec */ +#define MAX_FCP_TARGET 256 /* max num of FCP targets supported */ +#define FC_MAX_ADPTMSG 64 + +#define MAX_HBAEVT 32 + +#if __LITTLE_ENDIAN + +#define putLunLow(lunlow, lun) \ + { \ + lunlow = 0; \ + } + +#define putLunHigh(lunhigh, lun) \ + { \ + lunhigh = swab16(lun); \ + } + +#else /* BIG_ENDIAN_HOST */ + +#define putLunLow(lunlow, lun) \ + { \ + lunlow = 0; \ + } + +#define putLunHigh(lunhigh, lun) \ + { \ + lunhigh = (uint32_t)(lun << 16); \ + } +#endif + +/****************************************************************************/ +/* Device VPD save area */ +/****************************************************************************/ +typedef struct lpfc_vpd { + uint32_t status; /* vpd status value */ + uint32_t length; /* number of bytes actually returned */ + struct { + uint32_t rsvd1; /* Revision numbers */ + uint32_t biuRev; + uint32_t smRev; + uint32_t smFwRev; + uint32_t endecRev; + uint16_t rBit; + uint8_t fcphHigh; + uint8_t fcphLow; + uint8_t feaLevelHigh; + uint8_t feaLevelLow; + uint32_t postKernRev; + uint32_t opFwRev; + uint8_t opFwName[16]; + uint32_t sli1FwRev; + uint8_t sli1FwName[16]; + uint32_t sli2FwRev; + uint8_t sli2FwName[16]; + } rev; +} lpfc_vpd_t; + +struct lpfc_scsi_buf; + +struct lpfc_hba_event { + uint32_t fc_eventcode; + uint32_t fc_evdata1; + uint32_t fc_evdata2; + uint32_t fc_evdata3; + uint32_t fc_evdata4; +}; + +/* + * lpfc stat counters + */ +struct lpfc_stats { + /* Statistics for ELS commands */ + uint32_t elsLogiCol; + uint32_t elsRetryExceeded; + uint32_t elsXmitRetry; + uint32_t elsDelayRetry; + uint32_t elsRcvDrop; + uint32_t elsRcvFrame; + uint32_t elsRcvRSCN; + uint32_t elsRcvRNID; + uint32_t elsRcvFARP; + uint32_t elsRcvFARPR; + uint32_t elsRcvFLOGI; + uint32_t elsRcvPLOGI; + uint32_t elsRcvADISC; + uint32_t elsRcvPDISC; + uint32_t elsRcvFAN; + uint32_t elsRcvLOGO; + uint32_t elsRcvPRLO; + uint32_t elsRcvPRLI; + uint32_t elsRcvRRQ; + uint32_t elsXmitFLOGI; + uint32_t elsXmitPLOGI; + uint32_t elsXmitPRLI; + uint32_t elsXmitADISC; + uint32_t elsXmitLOGO; + uint32_t elsXmitSCR; + uint32_t elsXmitRNID; + uint32_t elsXmitFARP; + uint32_t elsXmitFARPR; + uint32_t elsXmitACC; + uint32_t elsXmitLSRJT; + + uint32_t frameRcvBcast; + uint32_t frameRcvMulti; + uint32_t strayXmitCmpl; + uint32_t frameXmitDelay; + uint32_t xriCmdCmpl; + uint32_t xriStatErr; + uint32_t LinkUp; + uint32_t LinkDown; + uint32_t LinkMultiEvent; + uint32_t NoRcvBuf; + uint32_t fcpCmd; + uint32_t fcpCmpl; + uint32_t fcpRspErr; + uint32_t fcpRemoteStop; + uint32_t fcpPortRjt; + uint32_t fcpPortBusy; + uint32_t fcpError; + uint32_t fcpLocalErr; +}; + +enum sysfs_mbox_state { + SMBOX_IDLE, + SMBOX_WRITING, + SMBOX_READING +}; + +struct lpfc_sysfs_mbox { + enum sysfs_mbox_state state; + size_t offset; + struct lpfcMboxq * mbox; +}; + +struct lpfc_hba { + uint32_t intr_inited; /* flag for interrupt registration */ + struct list_head hba_list; /* List of hbas/ports */ + struct lpfc_sli sli; + struct lpfc_sli2_slim *slim2p; + dma_addr_t slim2p_mapping; + + uint32_t hba_state; + +#define LPFC_INIT_START 1 /* Initial state after board reset */ +#define LPFC_INIT_MBX_CMDS 2 /* Initialize HBA with mbox commands */ +#define LPFC_LINK_DOWN 3 /* HBA initialized, link is down */ +#define LPFC_LINK_UP 4 /* Link is up - issue READ_LA */ +#define LPFC_LOCAL_CFG_LINK 5 /* local NPORT Id configured */ +#define LPFC_FLOGI 6 /* FLOGI sent to Fabric */ +#define LPFC_FABRIC_CFG_LINK 7 /* Fabric assigned NPORT Id + configured */ +#define LPFC_NS_REG 8 /* Register with NameServer */ +#define LPFC_NS_QRY 9 /* Query NameServer for NPort ID list */ +#define LPFC_BUILD_DISC_LIST 10 /* Build ADISC and PLOGI lists for + * device authentication / discovery */ +#define LPFC_DISC_AUTH 11 /* Processing ADISC list */ +#define LPFC_CLEAR_LA 12 /* authentication cmplt - issue + CLEAR_LA */ +#define LPFC_HBA_READY 32 +#define LPFC_HBA_ERROR 0xff + + uint8_t fc_linkspeed; /* Link speed after last READ_LA */ + + uint32_t fc_eventTag; /* event tag for link attention */ + uint32_t fc_prli_sent; /* cntr for outstanding PRLIs */ + + uint32_t num_disc_nodes; /*in addition to hba_state */ + + uint8_t fcp_mapping; /* Map FCP devices based on WWNN WWPN or DID */ +#define FCP_SEED_WWNN 0x1 +#define FCP_SEED_WWPN 0x2 +#define FCP_SEED_DID 0x4 +#define FCP_SEED_MASK 0x7 +#define FCP_SEED_AUTO 0x8 /* binding was created by auto mapping */ + + struct timer_list fc_estabtmo; /* link establishment timer */ + struct timer_list fc_disctmo; /* Discovery rescue timer */ + struct timer_list fc_fdmitmo; /* fdmi timer */ + struct timer_list fc_scantmo; /* scsi scan host timer */ + + + void *fc_evt_head; /* waiting for event queue */ + void *fc_evt_tail; /* waiting for event queue */ + + uint16_t hba_event_put; /* hbaevent event put word anchor */ + uint16_t hba_event_get; /* hbaevent event get word anchor */ + uint32_t hba_event_missed; /* hbaevent missed event word anchor */ + uint32_t sid_cnt; /* SCSI ID counter */ + + struct lpfc_hba_event hbaevt[MAX_HBAEVT]; + + /* These fields used to be binfo */ + struct lpfc_name fc_nodename; /* fc nodename */ + struct lpfc_name fc_portname; /* fc portname */ + uint32_t fc_pref_DID; /* preferred D_ID */ + uint8_t fc_pref_ALPA; /* preferred AL_PA */ + uint32_t fc_edtov; /* E_D_TOV timer value */ + uint32_t fc_arbtov; /* ARB_TOV timer value */ + uint32_t fc_ratov; /* R_A_TOV timer value */ + uint32_t fc_rttov; /* R_T_TOV timer value */ + uint32_t fc_altov; /* AL_TOV timer value */ + uint32_t fc_crtov; /* C_R_TOV timer value */ + uint32_t fc_citov; /* C_I_TOV timer value */ + uint32_t fc_myDID; /* fibre channel S_ID */ + uint32_t fc_prevDID; /* previous fibre channel S_ID */ + + struct serv_parm fc_sparam; /* buffer for our service parameters */ + struct serv_parm fc_fabparam; /* fabric service parameters buffer */ + uint8_t alpa_map[128]; /* AL_PA map from READ_LA */ + + uint8_t fc_ns_retry; /* retries for fabric nameserver */ + uint32_t fc_nlp_cnt; /* outstanding NODELIST requests */ + uint32_t fc_rscn_id_cnt; /* count of RSCNs payloads in list */ + struct lpfc_dmabuf *fc_rscn_id_list[FC_MAX_HOLD_RSCN]; + uint32_t lmt; + uint32_t fc_flag; /* FC flags */ +#define FC_PT2PT 0x1 /* pt2pt with no fabric */ +#define FC_PT2PT_PLOGI 0x2 /* pt2pt initiate PLOGI */ +#define FC_DISC_TMO 0x4 /* Discovery timer running */ +#define FC_PUBLIC_LOOP 0x8 /* Public loop */ +#define FC_LBIT 0x10 /* LOGIN bit in loopinit set */ +#define FC_RSCN_MODE 0x20 /* RSCN cmd rcv'ed */ +#define FC_NLP_MORE 0x40 /* More node to process in node tbl */ +#define FC_OFFLINE_MODE 0x80 /* Interface is offline for diag */ +#define FC_FABRIC 0x100 /* We are fabric attached */ +#define FC_ESTABLISH_LINK 0x200 /* Reestablish Link */ +#define FC_RSCN_DISCOVERY 0x400 /* Authenticate all devices after RSCN*/ +#define FC_LOADING 0x1000 /* HBA in process of loading drvr */ +#define FC_SCSI_SCAN_TMO 0x4000 /* scsi scan timer running */ +#define FC_ABORT_DISCOVERY 0x8000 /* we want to abort discovery */ +#define FC_NDISC_ACTIVE 0x10000 /* NPort discovery active */ + + uint32_t fc_topology; /* link topology, from LINK INIT */ + + struct lpfc_stats fc_stat; + + /* These are the head/tail pointers for the bind, plogi, adisc, unmap, + * and map lists. Their counters are immediately following. + */ + struct list_head fc_nlpbind_list; + struct list_head fc_plogi_list; + struct list_head fc_adisc_list; + struct list_head fc_reglogin_list; + struct list_head fc_prli_list; + struct list_head fc_nlpunmap_list; + struct list_head fc_nlpmap_list; + struct list_head fc_npr_list; + struct list_head fc_unused_list; + + /* Keep counters for the number of entries in each list. */ + uint16_t fc_bind_cnt; + uint16_t fc_plogi_cnt; + uint16_t fc_adisc_cnt; + uint16_t fc_reglogin_cnt; + uint16_t fc_prli_cnt; + uint16_t fc_unmap_cnt; + uint16_t fc_map_cnt; + uint16_t fc_npr_cnt; + uint16_t fc_unused_cnt; + struct lpfc_nodelist fc_fcpnodev; /* nodelist entry for no device */ + uint32_t nport_event_cnt; /* timestamp for nlplist entry */ + + struct lpfc_target *device_queue_hash[MAX_FCP_TARGET]; +#define LPFC_RPI_HASH_SIZE 64 +#define LPFC_RPI_HASH_FUNC(x) ((x) & (0x3f)) + /* ptr to active D_ID / RPIs */ + struct lpfc_nodelist *fc_nlplookup[LPFC_RPI_HASH_SIZE]; + uint32_t wwnn[2]; + uint32_t RandomData[7]; + + uint32_t cfg_log_verbose; + uint32_t cfg_lun_queue_depth; + uint32_t cfg_nodev_tmo; + uint32_t cfg_hba_queue_depth; + uint32_t cfg_fcp_class; + uint32_t cfg_use_adisc; + uint32_t cfg_ack0; + uint32_t cfg_topology; + uint32_t cfg_scan_down; + uint32_t cfg_link_speed; + uint32_t cfg_cr_delay; + uint32_t cfg_cr_count; + uint32_t cfg_fdmi_on; + uint32_t cfg_fcp_bind_method; + uint32_t cfg_discovery_threads; + uint32_t cfg_max_luns; + uint32_t cfg_scsi_hotplug; + + lpfc_vpd_t vpd; /* vital product data */ + +#if defined(SLES_FC) + /* + * Provide a per-HBA timer for 2.6.5 kernels patched with the + * block/unblock FC transport patch. + */ + struct timer_list dev_loss_timer; +#endif + + struct Scsi_Host *host; + struct pci_dev *pcidev; + struct list_head dpc_disc; + + pid_t dpc_pid; + int dpc_kill; + struct completion dpc_startup; + struct completion dpc_exiting; + struct semaphore *dpc_wait; + uint32_t work_hba_events; /* Timeout to be handled */ +#define WORKER_DISC_TMO 0x1 /* Discovery timeout */ +#define WORKER_ELS_TMO 0x2 /* ELS timeout */ +#define WORKER_MBOX_TMO 0x4 /* MBOX timeout */ +#define WORKER_FDMI_TMO 0x8 /* FDMI timeout */ + + unsigned long pci_bar0_map; /* Physical address for PCI BAR0 */ + unsigned long pci_bar2_map; /* Physical address for PCI BAR2 */ + void *slim_memmap_p; /* Kernel memory mapped address for PCI + BAR0 */ + void *ctrl_regs_memmap_p; /* Kernel memory mapped address for PCI + BAR2 */ + + void *MBslimaddr; /* virtual address for mbox cmds */ + void *HAregaddr; /* virtual address for host attn reg */ + void *CAregaddr; /* virtual address for chip attn reg */ + void *HSregaddr; /* virtual address for host status reg */ + void *HCregaddr; /* virtual address for host ctl reg */ + wait_queue_head_t linkevtwq; + wait_queue_head_t rscnevtwq; + wait_queue_head_t ctevtwq; + + uint8_t brd_no; /* FC board number */ + + char SerialNumber[32]; /* adapter Serial Number */ + char OptionROMVersion[32]; /* adapter BIOS / Fcode version */ + char ModelDesc[256]; /* Model Description */ + char ModelName[80]; /* Model Name */ + char ProgramType[256]; /* Program Type */ + char Port[20]; /* Port No */ + uint8_t vpd_flag; /* VPD data flag */ + +#define VPD_MODEL_DESC 0x1 /* valid vpd model description */ +#define VPD_MODEL_NAME 0x2 /* valid vpd model name */ +#define VPD_PROGRAM_TYPE 0x4 /* valid vpd program type */ +#define VPD_PORT 0x8 /* valid vpd port data */ +#define VPD_MASK 0xf /* mask for any vpd data */ + + struct timer_list els_tmofunc; + + void *link_stats; + + /* + * stat counters + */ + uint64_t fc4InputRequests; + uint64_t fc4OutputRequests; + uint64_t fc4ControlRequests; + + struct lpfc_sysfs_mbox sysfs_mbox; +; + /* pci_mem_pools */ + struct pci_pool *lpfc_scsi_dma_ext_pool; + struct pci_pool *lpfc_mbuf_pool; + struct lpfc_dma_pool lpfc_mbuf_safety_pool; + mempool_t *scsibuf_mem_pool; + + mempool_t *iocb_mem_pool; + mempool_t *mbox_mem_pool; + mempool_t *nlp_mem_pool; + mempool_t *bind_mem_pool; + struct list_head freebufList; + struct list_head ctrspbuflist; + struct list_head rnidrspbuflist; +}; + +/* event mask definitions */ +#define FC_REG_LINK_EVENT 0x1 /* Register for link up / down events */ +#define FC_REG_RSCN_EVENT 0x2 /* Register for RSCN events */ +#define FC_REG_CT_EVENT 0x4 /* Register for CT request events */ + +#define FC_FSTYPE_ALL 0xffff /* match on all fsTypes */ + +typedef struct fcEVT { /* Kernel level Event structure */ + uint32_t evt_handle; + uint32_t evt_mask; + uint32_t evt_data0; + uint16_t evt_sleep; + uint16_t evt_flags; + void *evt_type; + void *evt_next; + void *evt_data1; + uint32_t evt_data2; +} fcEVT_t; + +typedef struct fcEVTHDR { /* Kernel level Event Header */ + uint32_t e_handle; + uint32_t e_mask; + uint16_t e_mode; +#define E_SLEEPING_MODE 0x0001 + uint16_t e_refcnt; + uint16_t e_flag; +#define E_GET_EVENT_ACTIVE 0x0001 + fcEVT_t *e_head; + fcEVT_t *e_tail; + void *e_next_header; + void *e_type; +} fcEVTHDR_t; + +struct rnidrsp { + void *buf; + uint32_t uniqueid; + struct list_head list; + uint32_t data; +}; + +#endif /* _H_LPFC */ --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_mbox.c 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_mbox.c 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,665 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_mbox.c 1.77.2.2 2005/06/13 17:16:32EDT sf_support Exp $ + */ +#include +#include +#include +#include +#include +#include +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" +#include "lpfc_compat.h" + +/**********************************************/ + +/* mailbox command */ +/**********************************************/ +void +lpfc_dump_mem(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, uint16_t offset) +{ + MAILBOX_t *mb; + void *ctx; + + mb = &pmb->mb; + ctx = pmb->context2; + + /* Setup to dump VPD region */ + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + mb->mbxCommand = MBX_DUMP_MEMORY; + mb->un.varDmp.cv = 1; + mb->un.varDmp.type = DMP_NV_PARAMS; + mb->un.varDmp.entry_index = offset; + mb->un.varDmp.region_id = DMP_REGION_VPD; + mb->un.varDmp.word_cnt = (DMP_RSP_SIZE / sizeof (uint32_t)); + mb->un.varDmp.co = 0; + mb->un.varDmp.resp_offset = 0; + pmb->context2 = ctx; + mb->mbxOwner = OWN_HOST; + return; +} + +/**********************************************/ +/* lpfc_read_nv Issue a READ NVPARAM */ +/* mailbox command */ +/**********************************************/ +void +lpfc_read_nv(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + mb->mbxCommand = MBX_READ_NV; + mb->mbxOwner = OWN_HOST; + return; +} + +/**********************************************/ +/* lpfc_read_la Issue a READ LA */ +/* mailbox command */ +/**********************************************/ +int +lpfc_read_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_sli *psli; + + psli = &phba->sli; + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + /* Get a buffer to hold the loop map */ + if (((mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC)) == 0) || + ((mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys))) == 0)) { + if (mp) + kfree(mp); + mb->mbxCommand = MBX_READ_LA64; + /* READ_LA: no buffers */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_MBOX, + "%d:0300 READ_LA: no buffers\n", + phba->brd_no); + return (1); + } + INIT_LIST_HEAD(&mp->list); + mb->mbxCommand = MBX_READ_LA64; + mb->un.varReadLA.un.lilpBde64.tus.f.bdeSize = 128; + mb->un.varReadLA.un.lilpBde64.addrHigh = putPaddrHigh(mp->phys); + mb->un.varReadLA.un.lilpBde64.addrLow = putPaddrLow(mp->phys); + + /* Save address for later completion and set the owner to host so that + * the FW knows this mailbox is available for processing. + */ + pmb->context1 = (uint8_t *) mp; + mb->mbxOwner = OWN_HOST; + return (0); +} + +/**********************************************/ +/* lpfc_clear_la Issue a CLEAR LA */ +/* mailbox command */ +/**********************************************/ +void +lpfc_clear_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->un.varClearLA.eventTag = phba->fc_eventTag; + mb->mbxCommand = MBX_CLEAR_LA; + mb->mbxOwner = OWN_HOST; + return; +} + +/**************************************************/ +/* lpfc_config_link Issue a CONFIG LINK */ +/* mailbox command */ +/**************************************************/ +void +lpfc_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + /* NEW_FEATURE + * SLI-2, Coalescing Response Feature. + */ + if (phba->cfg_cr_delay) { + mb->un.varCfgLnk.cr = 1; + mb->un.varCfgLnk.ci = 1; + mb->un.varCfgLnk.cr_delay = phba->cfg_cr_delay; + mb->un.varCfgLnk.cr_count = phba->cfg_cr_count; + } + + mb->un.varCfgLnk.myId = phba->fc_myDID; + mb->un.varCfgLnk.edtov = phba->fc_edtov; + mb->un.varCfgLnk.arbtov = phba->fc_arbtov; + mb->un.varCfgLnk.ratov = phba->fc_ratov; + mb->un.varCfgLnk.rttov = phba->fc_rttov; + mb->un.varCfgLnk.altov = phba->fc_altov; + mb->un.varCfgLnk.crtov = phba->fc_crtov; + mb->un.varCfgLnk.citov = phba->fc_citov; + + if (phba->cfg_ack0) + mb->un.varCfgLnk.ack0_enable = 1; + + mb->mbxCommand = MBX_CONFIG_LINK; + mb->mbxOwner = OWN_HOST; + return; +} + +/**********************************************/ +/* lpfc_init_link Issue an INIT LINK */ +/* mailbox command */ +/**********************************************/ +void +lpfc_init_link(struct lpfc_hba * phba, + LPFC_MBOXQ_t * pmb, uint32_t topology, uint32_t linkspeed) +{ + lpfc_vpd_t *vpd; + struct lpfc_sli *psli; + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + psli = &phba->sli; + switch (topology) { + case FLAGS_TOPOLOGY_MODE_LOOP_PT: + mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_LOOP; + mb->un.varInitLnk.link_flags |= FLAGS_TOPOLOGY_FAILOVER; + break; + case FLAGS_TOPOLOGY_MODE_PT_PT: + mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_PT_PT; + break; + case FLAGS_TOPOLOGY_MODE_LOOP: + mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_LOOP; + break; + case FLAGS_TOPOLOGY_MODE_PT_LOOP: + mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_PT_PT; + mb->un.varInitLnk.link_flags |= FLAGS_TOPOLOGY_FAILOVER; + break; + } + + /* NEW_FEATURE + * Setting up the link speed + */ + vpd = &phba->vpd; + if (vpd->rev.feaLevelHigh >= 0x02){ + switch(linkspeed){ + case LINK_SPEED_1G: + case LINK_SPEED_2G: + case LINK_SPEED_4G: + mb->un.varInitLnk.link_flags |= + FLAGS_LINK_SPEED; + mb->un.varInitLnk.link_speed = linkspeed; + break; + case LINK_SPEED_AUTO: + default: + mb->un.varInitLnk.link_speed = + LINK_SPEED_AUTO; + break; + } + + } + else + mb->un.varInitLnk.link_speed = LINK_SPEED_AUTO; + + mb->mbxCommand = (volatile uint8_t)MBX_INIT_LINK; + mb->mbxOwner = OWN_HOST; + mb->un.varInitLnk.fabric_AL_PA = phba->fc_pref_ALPA; + return; +} + +/**********************************************/ +/* lpfc_read_sparam Issue a READ SPARAM */ +/* mailbox command */ +/**********************************************/ +int +lpfc_read_sparam(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_dmabuf *mp; + MAILBOX_t *mb; + struct lpfc_sli *psli; + + psli = &phba->sli; + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->mbxOwner = OWN_HOST; + + /* Get a buffer to hold the HBAs Service Parameters */ + + if (((mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC)) == 0) || + ((mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys))) == 0)) { + if (mp) + kfree(mp); + mb->mbxCommand = MBX_READ_SPARM64; + /* READ_SPARAM: no buffers */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_MBOX, + "%d:0301 READ_SPARAM: no buffers\n", + phba->brd_no); + return (1); + } + INIT_LIST_HEAD(&mp->list); + mb->mbxCommand = MBX_READ_SPARM64; + mb->un.varRdSparm.un.sp64.tus.f.bdeSize = sizeof (struct serv_parm); + mb->un.varRdSparm.un.sp64.addrHigh = putPaddrHigh(mp->phys); + mb->un.varRdSparm.un.sp64.addrLow = putPaddrLow(mp->phys); + + /* save address for completion */ + pmb->context1 = mp; + + return (0); +} + +/********************************************/ +/* lpfc_unreg_did Issue a UNREG_DID */ +/* mailbox command */ +/********************************************/ +void +lpfc_unreg_did(struct lpfc_hba * phba, uint32_t did, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->un.varUnregDID.did = did; + + mb->mbxCommand = MBX_UNREG_D_ID; + mb->mbxOwner = OWN_HOST; + return; +} + +/***********************************************/ + +/* command to write slim */ +/***********************************************/ +void +lpfc_set_slim(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, uint32_t addr, + uint32_t value) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + /* addr = 0x090597 is AUTO ABTS disable for ELS commands */ + /* addr = 0x052198 is DELAYED ABTS enable for ELS commands */ + + /* + * Always turn on DELAYED ABTS for ELS timeouts + */ + if ((addr == 0x052198) && (value == 0)) + value = 1; + + mb->un.varWords[0] = addr; + mb->un.varWords[1] = value; + + mb->mbxCommand = MBX_SET_SLIM; + mb->mbxOwner = OWN_HOST; + return; +} + +/**********************************************/ +/* lpfc_read_config Issue a READ CONFIG */ +/* mailbox command */ +/**********************************************/ +void +lpfc_read_config(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->mbxCommand = MBX_READ_CONFIG; + mb->mbxOwner = OWN_HOST; + return; +} + +/********************************************/ +/* lpfc_reg_login Issue a REG_LOGIN */ +/* mailbox command */ +/********************************************/ +int +lpfc_reg_login(struct lpfc_hba * phba, + uint32_t did, uint8_t * param, LPFC_MBOXQ_t * pmb, uint32_t flag) +{ + uint8_t *sparam; + struct lpfc_dmabuf *mp; + MAILBOX_t *mb; + struct lpfc_sli *psli; + + psli = &phba->sli; + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->un.varRegLogin.rpi = 0; + mb->un.varRegLogin.did = did; + mb->un.varWords[30] = flag; /* Set flag to issue action on cmpl */ + + mb->mbxOwner = OWN_HOST; + + /* Get a buffer to hold NPorts Service Parameters */ + if (((mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC)) == 0) || + ((mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys))) == 0)) { + if (mp) + kfree(mp); + + mb->mbxCommand = MBX_REG_LOGIN64; + /* REG_LOGIN: no buffers */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_MBOX, + "%d:0302 REG_LOGIN: no buffers Data x%x x%x\n", + phba->brd_no, + (uint32_t) did, (uint32_t) flag); + return (1); + } + INIT_LIST_HEAD(&mp->list); + sparam = mp->virt; + + /* Copy param's into a new buffer */ + memcpy(sparam, param, sizeof (struct serv_parm)); + + /* save address for completion */ + pmb->context1 = (uint8_t *) mp; + + mb->mbxCommand = MBX_REG_LOGIN64; + mb->un.varRegLogin.un.sp64.tus.f.bdeSize = sizeof (struct serv_parm); + mb->un.varRegLogin.un.sp64.addrHigh = putPaddrHigh(mp->phys); + mb->un.varRegLogin.un.sp64.addrLow = putPaddrLow(mp->phys); + + return (0); +} + +/**********************************************/ +/* lpfc_unreg_login Issue a UNREG_LOGIN */ +/* mailbox command */ +/**********************************************/ +void +lpfc_unreg_login(struct lpfc_hba * phba, uint32_t rpi, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->un.varUnregLogin.rpi = (uint16_t) rpi; + mb->un.varUnregLogin.rsvd1 = 0; + + mb->mbxCommand = MBX_UNREG_LOGIN; + mb->mbxOwner = OWN_HOST; + return; +} + +static void +lpfc_config_pcb_setup(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli = &phba->sli; + struct lpfc_sli_ring *pring; + PCB_t *pcbp = &phba->slim2p->pcb; + LPFC_RING_INIT_t *pringinit; + dma_addr_t pdma_addr; + uint32_t offset; + uint32_t iocbCnt; + int i; + + psli->MBhostaddr = (uint32_t *)&phba->slim2p->mbx; + pcbp->maxRing = (psli->sliinit.num_rings - 1); + + iocbCnt = 0; + for (i = 0; i < psli->sliinit.num_rings; i++) { + pringinit = &psli->sliinit.ringinit[i]; + pring = &psli->ring[i]; + /* A ring MUST have both cmd and rsp entries defined to be + valid */ + if ((pringinit->numCiocb == 0) || (pringinit->numRiocb == 0)) { + pcbp->rdsc[i].cmdEntries = 0; + pcbp->rdsc[i].rspEntries = 0; + pcbp->rdsc[i].cmdAddrHigh = 0; + pcbp->rdsc[i].rspAddrHigh = 0; + pcbp->rdsc[i].cmdAddrLow = 0; + pcbp->rdsc[i].rspAddrLow = 0; + pring->cmdringaddr = NULL; + pring->rspringaddr = NULL; + continue; + } + /* Command ring setup for ring */ + pring->cmdringaddr = + (void *)&phba->slim2p->IOCBs[iocbCnt]; + pcbp->rdsc[i].cmdEntries = pringinit->numCiocb; + + offset = (uint8_t *)&phba->slim2p->IOCBs[iocbCnt] - + (uint8_t *)phba->slim2p; + pdma_addr = phba->slim2p_mapping + offset; + pcbp->rdsc[i].cmdAddrHigh = putPaddrHigh(pdma_addr); + pcbp->rdsc[i].cmdAddrLow = putPaddrLow(pdma_addr); + iocbCnt += pringinit->numCiocb; + + /* Response ring setup for ring */ + pring->rspringaddr = + (void *)&phba->slim2p->IOCBs[iocbCnt]; + + pcbp->rdsc[i].rspEntries = pringinit->numRiocb; + offset = (uint8_t *)&phba->slim2p->IOCBs[iocbCnt] - + (uint8_t *)phba->slim2p; + pdma_addr = phba->slim2p_mapping + offset; + pcbp->rdsc[i].rspAddrHigh = putPaddrHigh(pdma_addr); + pcbp->rdsc[i].rspAddrLow = putPaddrLow(pdma_addr); + iocbCnt += pringinit->numRiocb; + } +} + +void +lpfc_read_rev(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + mb->un.varRdRev.cv = 1; + mb->mbxCommand = MBX_READ_REV; + mb->mbxOwner = OWN_HOST; + return; +} + +void +lpfc_config_ring(struct lpfc_hba * phba, int ring, LPFC_MBOXQ_t * pmb) +{ + int i; + MAILBOX_t *mb = &pmb->mb; + struct lpfc_sli *psli; + LPFC_RING_INIT_t *pring; + + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->un.varCfgRing.ring = ring; + mb->un.varCfgRing.maxOrigXchg = 0; + mb->un.varCfgRing.maxRespXchg = 0; + mb->un.varCfgRing.recvNotify = 1; + + psli = &phba->sli; + pring = &psli->sliinit.ringinit[ring]; + mb->un.varCfgRing.numMask = pring->num_mask; + mb->mbxCommand = MBX_CONFIG_RING; + mb->mbxOwner = OWN_HOST; + + /* Is this ring configured for a specific profile */ + if (pring->prt[0].profile) { + mb->un.varCfgRing.profile = pring->prt[0].profile; + return; + } + + /* Otherwise we setup specific rctl / type masks for this ring */ + for (i = 0; i < pring->num_mask; i++) { + mb->un.varCfgRing.rrRegs[i].rval = pring->prt[i].rctl; + if (mb->un.varCfgRing.rrRegs[i].rval != FC_ELS_REQ) + mb->un.varCfgRing.rrRegs[i].rmask = 0xff; + else + mb->un.varCfgRing.rrRegs[i].rmask = 0xfe; + mb->un.varCfgRing.rrRegs[i].tval = pring->prt[i].type; + mb->un.varCfgRing.rrRegs[i].tmask = 0xff; + } + + return; +} + +void +lpfc_config_port(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb = &pmb->mb; + dma_addr_t pdma_addr; + uint32_t bar_low, bar_high; + size_t offset; + HGP hgp; + void *to_slim; + + memset(pmb, 0, sizeof(LPFC_MBOXQ_t)); + mb->mbxCommand = MBX_CONFIG_PORT; + mb->mbxOwner = OWN_HOST; + + mb->un.varCfgPort.pcbLen = sizeof(PCB_t); + offset = (uint8_t *)&phba->slim2p->pcb - (uint8_t *)phba->slim2p; + pdma_addr = phba->slim2p_mapping + offset; + mb->un.varCfgPort.pcbLow = putPaddrLow(pdma_addr); + mb->un.varCfgPort.pcbHigh = putPaddrHigh(pdma_addr); + + /* Now setup pcb */ + phba->slim2p->pcb.type = TYPE_NATIVE_SLI2; + phba->slim2p->pcb.feature = FEATURE_INITIAL_SLI2; + + /* Setup Mailbox pointers */ + phba->slim2p->pcb.mailBoxSize = sizeof(MAILBOX_t); + offset = (uint8_t *)&phba->slim2p->mbx - (uint8_t *)phba->slim2p; + pdma_addr = phba->slim2p_mapping + offset; + phba->slim2p->pcb.mbAddrHigh = putPaddrHigh(pdma_addr); + phba->slim2p->pcb.mbAddrLow = putPaddrLow(pdma_addr); + + /* + * Setup Host Group ring pointer. + * + * For efficiency reasons, the ring get/put pointers can be + * placed in adapter memory (SLIM) rather than in host memory. + * This allows firmware to avoid PCI reads/writes when updating + * and checking pointers. + * + * The firmware recognizes the use of SLIM memory by comparing + * the address of the get/put pointers structure with that of + * the SLIM BAR (BAR0). + * + * Caution: be sure to use the PCI config space value of BAR0/BAR1 + * (the hardware's view of the base address), not the OS's + * value of pci_resource_start() as the OS value may be a cookie + * for ioremap/iomap. + */ + + + pci_read_config_dword(phba->pcidev, PCI_BASE_ADDRESS_0, &bar_low); + pci_read_config_dword(phba->pcidev, PCI_BASE_ADDRESS_1, &bar_high); + + + /* mask off BAR0's flag bits 0 - 3 */ + phba->slim2p->pcb.hgpAddrLow = (bar_low & PCI_BASE_ADDRESS_MEM_MASK) + + (SLIMOFF*sizeof(uint32_t)); + if (bar_low & PCI_BASE_ADDRESS_MEM_TYPE_64) + phba->slim2p->pcb.hgpAddrHigh = bar_high; + else + phba->slim2p->pcb.hgpAddrHigh = 0; + /* write HGP data to SLIM at the required longword offset */ + memset(&hgp, 0, sizeof(HGP)); + to_slim = (uint8_t *)phba->MBslimaddr + (SLIMOFF*sizeof (uint32_t)); + lpfc_memcpy_to_slim(to_slim, &hgp, sizeof (HGP)); + + /* Setup Port Group ring pointer */ + offset = (uint8_t *)&phba->slim2p->mbx.us.s2.port - + (uint8_t *)phba->slim2p; + pdma_addr = phba->slim2p_mapping + offset; + phba->slim2p->pcb.pgpAddrHigh = putPaddrHigh(pdma_addr); + phba->slim2p->pcb.pgpAddrLow = putPaddrLow(pdma_addr); + + /* Use callback routine to setp rings in the pcb */ + lpfc_config_pcb_setup(phba); + + /* special handling for LC HBAs */ + if (lpfc_is_LC_HBA(phba->pcidev->device)) { + uint32_t hbainit[5]; + + lpfc_hba_init(phba, hbainit); + + memcpy(&mb->un.varCfgPort.hbainit, hbainit, 20); + } + + /* Swap PCB if needed */ + lpfc_sli_pcimem_bcopy((uint32_t *)&phba->slim2p->pcb, + (uint32_t *)&phba->slim2p->pcb, + sizeof (PCB_t)); + + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "%d:0405 Service Level Interface (SLI) 2 selected\n", + phba->brd_no); +} + +void +lpfc_mbox_put(struct lpfc_hba * phba, LPFC_MBOXQ_t * mbq) +{ + struct lpfc_sli *psli; + + psli = &phba->sli; + + list_add_tail(&mbq->list, &psli->mboxq); + + psli->mboxq_cnt++; + + return; +} + +LPFC_MBOXQ_t * +lpfc_mbox_get(struct lpfc_hba * phba) +{ + LPFC_MBOXQ_t *mbq = NULL; + struct lpfc_sli *psli = &phba->sli; + + if (!list_empty(&psli->mboxq)) { + mbq = list_entry(psli->mboxq.next, LPFC_MBOXQ_t, list); + list_del_init(&mbq->list); + psli->mboxq_cnt--; + } + + return mbq; +} --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_sli.c 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_sli.c 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,3447 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_sli.c 1.200.1.8 2005/07/27 17:00:59EDT sf_support Exp $ + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" +#include "lpfc_compat.h" +#include "lpfc_fcp.h" + +static int lpfc_sli_reset_on_init = 1; +extern void +lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *, struct lpfc_iocbq *, struct lpfc_iocbq *); +/* + * Define macro to log: Mailbox command x%x cannot issue Data + * This allows multiple uses of lpfc_msgBlk0311 + * w/o perturbing log msg utility. +*/ +#define LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag) \ + lpfc_printf_log(phba, \ + KERN_INFO, \ + LOG_MBOX | LOG_SLI, \ + "%d:0311 Mailbox command x%x cannot issue " \ + "Data: x%x x%x x%x\n", \ + phba->brd_no, \ + mb->mbxCommand, \ + phba->hba_state, \ + psli->sliinit.sli_flag, \ + flag); + + +/* This will save a huge switch to determine if the IOCB cmd + * is unsolicited or solicited. + */ +#define LPFC_UNKNOWN_IOCB 0 +#define LPFC_UNSOL_IOCB 1 +#define LPFC_SOL_IOCB 2 +#define LPFC_ABORT_IOCB 3 +static uint8_t lpfc_sli_iocb_cmd_type[CMD_MAX_IOCB_CMD] = { + LPFC_UNKNOWN_IOCB, /* 0x00 */ + LPFC_UNSOL_IOCB, /* CMD_RCV_SEQUENCE_CX 0x01 */ + LPFC_SOL_IOCB, /* CMD_XMIT_SEQUENCE_CR 0x02 */ + LPFC_SOL_IOCB, /* CMD_XMIT_SEQUENCE_CX 0x03 */ + LPFC_SOL_IOCB, /* CMD_XMIT_BCAST_CN 0x04 */ + LPFC_SOL_IOCB, /* CMD_XMIT_BCAST_CX 0x05 */ + LPFC_UNKNOWN_IOCB, /* CMD_QUE_RING_BUF_CN 0x06 */ + LPFC_UNKNOWN_IOCB, /* CMD_QUE_XRI_BUF_CX 0x07 */ + LPFC_UNKNOWN_IOCB, /* CMD_IOCB_CONTINUE_CN 0x08 */ + LPFC_UNKNOWN_IOCB, /* CMD_RET_XRI_BUF_CX 0x09 */ + LPFC_SOL_IOCB, /* CMD_ELS_REQUEST_CR 0x0A */ + LPFC_SOL_IOCB, /* CMD_ELS_REQUEST_CX 0x0B */ + LPFC_UNKNOWN_IOCB, /* 0x0C */ + LPFC_UNSOL_IOCB, /* CMD_RCV_ELS_REQ_CX 0x0D */ + LPFC_ABORT_IOCB, /* CMD_ABORT_XRI_CN 0x0E */ + LPFC_ABORT_IOCB, /* CMD_ABORT_XRI_CX 0x0F */ + LPFC_ABORT_IOCB, /* CMD_CLOSE_XRI_CR 0x10 */ + LPFC_ABORT_IOCB, /* CMD_CLOSE_XRI_CX 0x11 */ + LPFC_SOL_IOCB, /* CMD_CREATE_XRI_CR 0x12 */ + LPFC_SOL_IOCB, /* CMD_CREATE_XRI_CX 0x13 */ + LPFC_SOL_IOCB, /* CMD_GET_RPI_CN 0x14 */ + LPFC_SOL_IOCB, /* CMD_XMIT_ELS_RSP_CX 0x15 */ + LPFC_SOL_IOCB, /* CMD_GET_RPI_CR 0x16 */ + LPFC_ABORT_IOCB, /* CMD_XRI_ABORTED_CX 0x17 */ + LPFC_SOL_IOCB, /* CMD_FCP_IWRITE_CR 0x18 */ + LPFC_SOL_IOCB, /* CMD_FCP_IWRITE_CX 0x19 */ + LPFC_SOL_IOCB, /* CMD_FCP_IREAD_CR 0x1A */ + LPFC_SOL_IOCB, /* CMD_FCP_IREAD_CX 0x1B */ + LPFC_SOL_IOCB, /* CMD_FCP_ICMND_CR 0x1C */ + LPFC_SOL_IOCB, /* CMD_FCP_ICMND_CX 0x1D */ + LPFC_UNKNOWN_IOCB, /* 0x1E */ + LPFC_SOL_IOCB, /* CMD_FCP_TSEND_CX 0x1F */ + LPFC_SOL_IOCB, /* CMD_ADAPTER_MSG 0x20 */ + LPFC_SOL_IOCB, /* CMD_FCP_TRECEIVE_CX 0x21 */ + LPFC_SOL_IOCB, /* CMD_ADAPTER_DUMP 0x22 */ + LPFC_SOL_IOCB, /* CMD_FCP_TRSP_CX 0x23 */ + /* 0x24 - 0x80 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + /* 0x30 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + /* 0x40 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + /* 0x50 */ + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + LPFC_UNKNOWN_IOCB, + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + LPFC_UNSOL_IOCB, + LPFC_UNSOL_IOCB, + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + /* 0x60 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + /* 0x70 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + /* 0x80 */ + LPFC_UNKNOWN_IOCB, + LPFC_UNSOL_IOCB, /* CMD_RCV_SEQUENCE64_CX 0x81 */ + LPFC_SOL_IOCB, /* CMD_XMIT_SEQUENCE64_CR 0x82 */ + LPFC_SOL_IOCB, /* CMD_XMIT_SEQUENCE64_CX 0x83 */ + LPFC_SOL_IOCB, /* CMD_XMIT_BCAST64_CN 0x84 */ + LPFC_SOL_IOCB, /* CMD_XMIT_BCAST64_CX 0x85 */ + LPFC_UNKNOWN_IOCB, /* CMD_QUE_RING_BUF64_CN 0x86 */ + LPFC_UNKNOWN_IOCB, /* CMD_QUE_XRI_BUF64_CX 0x87 */ + LPFC_UNKNOWN_IOCB, /* CMD_IOCB_CONTINUE64_CN 0x88 */ + LPFC_UNKNOWN_IOCB, /* CMD_RET_XRI_BUF64_CX 0x89 */ + LPFC_SOL_IOCB, /* CMD_ELS_REQUEST64_CR 0x8A */ + LPFC_SOL_IOCB, /* CMD_ELS_REQUEST64_CX 0x8B */ + LPFC_ABORT_IOCB, /* CMD_ABORT_MXRI64_CN 0x8C */ + LPFC_UNSOL_IOCB, /* CMD_RCV_ELS_REQ64_CX 0x8D */ + /* 0x8E - 0x94 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + LPFC_SOL_IOCB, /* CMD_XMIT_ELS_RSP64_CX 0x95 */ + LPFC_UNKNOWN_IOCB, /* 0x96 */ + LPFC_UNKNOWN_IOCB, /* 0x97 */ + LPFC_SOL_IOCB, /* CMD_FCP_IWRITE64_CR 0x98 */ + LPFC_SOL_IOCB, /* CMD_FCP_IWRITE64_CX 0x99 */ + LPFC_SOL_IOCB, /* CMD_FCP_IREAD64_CR 0x9A */ + LPFC_SOL_IOCB, /* CMD_FCP_IREAD64_CX 0x9B */ + LPFC_SOL_IOCB, /* CMD_FCP_ICMND64_CR 0x9C */ + LPFC_SOL_IOCB, /* CMD_FCP_ICMND64_CX 0x9D */ + LPFC_UNKNOWN_IOCB, /* 0x9E */ + LPFC_SOL_IOCB, /* CMD_FCP_TSEND64_CX 0x9F */ + LPFC_UNKNOWN_IOCB, /* 0xA0 */ + LPFC_SOL_IOCB, /* CMD_FCP_TRECEIVE64_CX 0xA1 */ + LPFC_UNKNOWN_IOCB, /* 0xA2 */ + LPFC_SOL_IOCB, /* CMD_FCP_TRSP64_CX 0xA3 */ + /* 0xA4 - 0xC1 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_SOL_IOCB, /* CMD_GEN_REQUEST64_CR 0xC2 */ + LPFC_SOL_IOCB, /* CMD_GEN_REQUEST64_CX 0xC3 */ + /* 0xC4 - 0xCF */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, /* CMD_SENDTEXT_CR 0xD1 */ + LPFC_SOL_IOCB, /* CMD_SENDTEXT_CX 0xD2 */ + LPFC_SOL_IOCB, /* CMD_RCV_LOGIN 0xD3 */ + LPFC_SOL_IOCB, /* CMD_ACCEPT_LOGIN 0xD4 */ + LPFC_SOL_IOCB, /* CMD_REJECT_LOGIN 0xD5 */ + LPFC_UNSOL_IOCB, + /* 0xD7 - 0xDF */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + /* 0xE0 */ + LPFC_UNSOL_IOCB, + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + LPFC_UNSOL_IOCB +}; + +static void +lpfc_sli_wake_mbox_wait(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq) +{ + wait_queue_head_t *pdone_q; + + /* + * If pdone_q is empty, the driver thread gave up waiting and + * continued running. + */ + pdone_q = (wait_queue_head_t *) pmboxq->context1; + if (pdone_q) + wake_up_interruptible(pdone_q); + return; +} + + + +static int +lpfc_sli_ring_map(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *pmb; + MAILBOX_t *pmbox; + int i; + + psli = &phba->sli; + + /* Get a Mailbox buffer to setup mailbox commands for HBA + initialization */ + if ((pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + phba->hba_state = LPFC_HBA_ERROR; + return -ENOMEM; + } + pmbox = &pmb->mb; + + /* Initialize the struct lpfc_sli_ring structure for each ring */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + /* Issue a CONFIG_RING mailbox command for each ring */ + phba->hba_state = LPFC_INIT_MBX_CMDS; + lpfc_config_ring(phba, i, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + /* Adapter failed to init, mbxCmd CFG_RING, + mbxStatus , ring */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0446 Adapter failed to init, " + "mbxCmd x%x CFG_RING, mbxStatus x%x, " + "ring %d\n", + phba->brd_no, + pmbox->mbxCommand, + pmbox->mbxStatus, + i); + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -ENXIO; + } + } + mempool_free( pmb, phba->mbox_mem_pool); + return 0; +} + +static int +lpfc_sli_ringtxcmpl_put(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, struct lpfc_iocbq * piocb) +{ + uint16_t iotag; + + list_add_tail(&piocb->list, &pring->txcmplq); + pring->txcmplq_cnt++; + if (unlikely(pring->ringno == LPFC_ELS_RING)) + mod_timer(&phba->els_tmofunc, + jiffies + HZ * (phba->fc_ratov << 1)); + + if (pring->fast_lookup) { + /* Setup fast lookup based on iotag for completion */ + iotag = piocb->iocb.ulpIoTag; + if (iotag && (iotag + < phba->sli.sliinit.ringinit[pring->ringno].fast_iotag)) + *(pring->fast_lookup + iotag) = piocb; + else { + + /* Cmd ring put: iotag greater then + configured max wd0 */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_SLI, + "%d:0316 Cmd ring %d put: iotag x%x " + "greater then configured max x%x " + "wd0 x%x\n", + phba->brd_no, + pring->ringno, iotag, phba->sli.sliinit + .ringinit[pring->ringno].fast_iotag, + *(((uint32_t *)(&piocb->iocb)) + 7)); + } + } + return (0); +} + +static int +lpfc_sli_ringtx_put(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, + struct lpfc_iocbq * piocb) +{ + /* Insert the caller's iocb in the txq tail for later processing. */ + list_add_tail(&piocb->list, &pring->txq); + pring->txq_cnt++; + return (0); +} + +static struct lpfc_iocbq * +lpfc_sli_ringtx_get(struct lpfc_hba * phba, struct lpfc_sli_ring * pring) +{ + struct list_head *dlp; + struct lpfc_iocbq *cmd_iocb; + struct lpfc_iocbq *next_iocb; + + dlp = &pring->txq; + cmd_iocb = NULL; + next_iocb = (struct lpfc_iocbq *) pring->txq.next; + if (next_iocb != (struct lpfc_iocbq *) & pring->txq) { + /* If the first ptr is not equal to the list header, + * deque the IOCBQ_t and return it. + */ + cmd_iocb = next_iocb; + list_del(&cmd_iocb->list); + pring->txq_cnt--; + } + return (cmd_iocb); +} + +static IOCB_t * +lpfc_sli_next_iocb_slot (struct lpfc_hba *phba, struct lpfc_sli_ring *pring) +{ + MAILBOX_t *mbox = (MAILBOX_t *)phba->sli.MBhostaddr; + PGP *pgp = (PGP *)&mbox->us.s2.port[pring->ringno]; + uint32_t max_cmd_idx = + phba->sli.sliinit.ringinit[pring->ringno].numCiocb; + IOCB_t *iocb = NULL; + + if((pring->next_cmdidx == pring->cmdidx) && + (++pring->next_cmdidx >= max_cmd_idx)) + pring->next_cmdidx = 0; + + if (unlikely(pring->local_getidx == pring->next_cmdidx)) { + + pring->local_getidx = le32_to_cpu(pgp->cmdGetInx); + + if (unlikely(pring->local_getidx >= max_cmd_idx)) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "%d:0315 Ring %d issue: portCmdGet %d " + "is bigger then cmd ring %d\n", + phba->brd_no, pring->ringno, + pring->local_getidx, max_cmd_idx); + + phba->hba_state = LPFC_HBA_ERROR; + /* + All error attention handlers are posted to + discovery tasklet + */ + lpfc_discq_post_event(phba, (void *)HS_FFER3, NULL, + LPFC_EVT_ERR_ATTN); + + return NULL; + } + + if (pring->local_getidx == pring->next_cmdidx) + return NULL; + } + + iocb = IOCB_ENTRY(pring->cmdringaddr, pring->cmdidx); + + return iocb; +} + +static int +lpfc_sli_submit_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + IOCB_t *iocb, struct lpfc_iocbq *nextiocb) +{ + struct lpfc_sli *psli = &phba->sli; + int ringno = pring->ringno; + + /* + * Alloocate and set up an iotag + */ + if ((nextiocb->iocb.ulpIoTag = + lpfc_sli_next_iotag(phba, &psli->ring[psli->fcp_ring])) == 0) + return (1); + + /* + * Issue iocb command to adapter + */ + lpfc_sli_pcimem_bcopy((uint32_t *)&nextiocb->iocb, + (uint32_t *)(iocb), sizeof (IOCB_t)); + wmb(); + psli->slistat.iocbCmd[ringno]++; + + /* + * If there is no completion routine to call, we can release the + * IOCB buffer back right now. For IOCBs, like QUE_RING_BUF, + * that have no rsp ring completion, iocb_cmpl MUST be NULL. + */ + if (nextiocb->iocb_cmpl) + lpfc_sli_ringtxcmpl_put(phba, pring, nextiocb); + else + mempool_free(nextiocb, phba->iocb_mem_pool); + + /* + * Let the HBA know what IOCB slot will be the next one the + * driver will put a command into. + */ + pring->cmdidx = pring->next_cmdidx; + writeb(pring->cmdidx, + (u8 *)phba->MBslimaddr + (SLIMOFF + (ringno * 2)) * 4); + + return (0); +} + +static void +lpfc_sli_update_full_ring(struct lpfc_hba * phba, + struct lpfc_sli_ring *pring) +{ + int ringno = pring->ringno; + + pring->flag |= LPFC_CALL_RING_AVAILABLE; + + wmb(); + + /* + * Set ring 'ringno' to SET R0CE_REQ in Chip Att register. + * The HBA will tell us when an IOCB entry is available. + */ + writel((CA_R0ATT|CA_R0CE_REQ) << (ringno*4), phba->CAregaddr); + readl(phba->CAregaddr); /* flush */ + + phba->sli.slistat.iocbCmdFull[ringno]++; +} + +static void +lpfc_sli_update_ring(struct lpfc_hba * phba, + struct lpfc_sli_ring *pring) +{ + int ringno = pring->ringno; + + /* + * Tell the HBA that there is work to do in this ring. + */ + wmb(); + writel(CA_R0ATT << (ringno * 4), phba->CAregaddr); + readl(phba->CAregaddr); /* flush */ +} + +static void +lpfc_sli_resume_iocb(struct lpfc_hba * phba, struct lpfc_sli_ring * pring) +{ + struct lpfc_sli *psli = &phba->sli; + IOCB_t *iocb; + struct lpfc_iocbq *nextiocb; + + /* + * Check to see if: + * (a) there is anything on the txq to send + * (b) link is up + * (c) link attention events can be processed (fcp ring only) + * (d) IOCB processing is not blocked by the outstanding mbox command. + */ + if (pring->txq_cnt && + (phba->hba_state > LPFC_LINK_DOWN) && + (pring->ringno != psli->fcp_ring || + psli->sliinit.sli_flag & LPFC_PROCESS_LA) && + !(pring->flag & LPFC_STOP_IOCB_MBX)) { + + while ((iocb = lpfc_sli_next_iocb_slot(phba, pring)) && + (nextiocb = lpfc_sli_ringtx_get(phba, pring))) + if (lpfc_sli_submit_iocb(phba, pring, iocb, nextiocb)) { + lpfc_sli_ringtx_put(phba, pring, nextiocb); + break; + } + + if (iocb) + lpfc_sli_update_ring(phba, pring); + else + lpfc_sli_update_full_ring(phba, pring); + } + + return; +} + +/* lpfc_sli_turn_on_ring is only called by lpfc_sli_handle_mb_event below */ +static void +lpfc_sli_turn_on_ring(struct lpfc_hba * phba, int ringno) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + PGP *pgp; + + psli = &phba->sli; + pring = &psli->ring[ringno]; + pgp = (PGP *) & (((MAILBOX_t *)psli->MBhostaddr)->us.s2.port[ringno]); + + /* If the ring is active, flag it */ + if (psli->ring[ringno].cmdringaddr) { + if (psli->ring[ringno].flag & LPFC_STOP_IOCB_MBX) { + psli->ring[ringno].flag &= ~LPFC_STOP_IOCB_MBX; + /* + * Force update of the local copy of cmdGetInx + */ + pring->local_getidx = le32_to_cpu(pgp->cmdGetInx); + lpfc_sli_resume_iocb(phba, pring); + } + } +} + +static int +lpfc_sli_chk_mbx_command(uint8_t mbxCommand) +{ + uint8_t ret; + + switch (mbxCommand) { + case MBX_LOAD_SM: + case MBX_READ_NV: + case MBX_WRITE_NV: + case MBX_RUN_BIU_DIAG: + case MBX_INIT_LINK: + case MBX_DOWN_LINK: + case MBX_CONFIG_LINK: + case MBX_CONFIG_RING: + case MBX_RESET_RING: + case MBX_READ_CONFIG: + case MBX_READ_RCONFIG: + case MBX_READ_SPARM: + case MBX_READ_STATUS: + case MBX_READ_RPI: + case MBX_READ_XRI: + case MBX_READ_REV: + case MBX_READ_LNK_STAT: + case MBX_REG_LOGIN: + case MBX_UNREG_LOGIN: + case MBX_READ_LA: + case MBX_CLEAR_LA: + case MBX_DUMP_MEMORY: + case MBX_DUMP_CONTEXT: + case MBX_RUN_DIAGS: + case MBX_RESTART: + case MBX_UPDATE_CFG: + case MBX_DOWN_LOAD: + case MBX_DEL_LD_ENTRY: + case MBX_RUN_PROGRAM: + case MBX_SET_MASK: + case MBX_SET_SLIM: + case MBX_UNREG_D_ID: + case MBX_CONFIG_FARP: + case MBX_LOAD_AREA: + case MBX_RUN_BIU_DIAG64: + case MBX_CONFIG_PORT: + case MBX_READ_SPARM64: + case MBX_READ_RPI64: + case MBX_REG_LOGIN64: + case MBX_READ_LA64: + case MBX_FLASH_WR_ULA: + case MBX_SET_DEBUG: + case MBX_LOAD_EXP_ROM: + ret = mbxCommand; + break; + default: + ret = MBX_SHUTDOWN; + break; + } + return (ret); +} + +void +lpfc_sli_def_mbox_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_dmabuf *mp; + mp = (struct lpfc_dmabuf *) (pmb->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + mempool_free( pmb, phba->mbox_mem_pool); + return; +} + +static int +lpfc_sli_handle_mb_event(struct lpfc_hba * phba) +{ + MAILBOX_t *mbox; + MAILBOX_t *pmbox; + LPFC_MBOXQ_t *pmb; + struct lpfc_sli *psli; + int i; + unsigned long iflag; + uint32_t process_next; + + + psli = &phba->sli; + /* We should only get here if we are in SLI2 mode */ + if (!(psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE)) { + return (1); + } + + spin_lock_irqsave(phba->host->host_lock, iflag); + + psli->slistat.mboxEvent++; + + /* Get a Mailbox buffer to setup mailbox commands for callback */ + if ((pmb = psli->mbox_active)) { + pmbox = &pmb->mb; + mbox = (MAILBOX_t *) psli->MBhostaddr; + + /* First check out the status word */ + lpfc_sli_pcimem_bcopy((uint32_t *) mbox, (uint32_t *) pmbox, + sizeof (uint32_t)); + + /* Sanity check to ensure the host owns the mailbox */ + if (pmbox->mbxOwner != OWN_HOST) { + /* Lets try for a while */ + for (i = 0; i < 10240; i++) { + /* First copy command data */ + lpfc_sli_pcimem_bcopy((uint32_t *) mbox, + (uint32_t *) pmbox, + sizeof (uint32_t)); + if (pmbox->mbxOwner == OWN_HOST) + goto mbout; + } + /* Stray Mailbox Interrupt, mbxCommand mbxStatus + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_MBOX | LOG_SLI, + "%d:0304 Stray Mailbox Interrupt " + "mbxCommand x%x mbxStatus x%x\n", + phba->brd_no, + pmbox->mbxCommand, + pmbox->mbxStatus); + + psli->sliinit.sli_flag |= LPFC_SLI_MBOX_ACTIVE; + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return (1); + } + + mbout: + del_timer_sync(&psli->mbox_tmo); + phba->work_hba_events &= ~WORKER_MBOX_TMO; + + /* + * It is a fatal error if unknown mbox command completion. + */ + if (lpfc_sli_chk_mbx_command(pmbox->mbxCommand) == + MBX_SHUTDOWN) { + + /* Unknow mailbox command compl */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_MBOX | LOG_SLI, + "%d:0323 Unknown Mailbox command %x Cmpl\n", + phba->brd_no, + pmbox->mbxCommand); + phba->hba_state = LPFC_HBA_ERROR; + + /* + All error attention handlers are posted to + discovery tasklet + */ + lpfc_discq_post_event(phba, (void *)HS_FFER3, NULL, + LPFC_EVT_ERR_ATTN); + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return (0); + } + + psli->mbox_active = NULL; + if (pmbox->mbxStatus) { + psli->slistat.mboxStatErr++; + if (pmbox->mbxStatus == MBXERR_NO_RESOURCES) { + /* Mbox cmd cmpl error - RETRYing */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_MBOX | LOG_SLI, + "%d:0305 Mbox cmd cmpl error - " + "RETRYing Data: x%x x%x x%x x%x\n", + phba->brd_no, + pmbox->mbxCommand, + pmbox->mbxStatus, + pmbox->un.varWords[0], + phba->hba_state); + pmbox->mbxStatus = 0; + pmbox->mbxOwner = OWN_HOST; + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + if (lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT) + == MBX_SUCCESS) { + spin_unlock_irqrestore( + phba->host->host_lock, + iflag); + return (0); + } + } + } + + /* Mailbox cmd Cmpl */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_MBOX | LOG_SLI, + "%d:0307 Mailbox cmd x%x Cmpl x%p " + "Data: x%x x%x x%x x%x x%x x%x x%x x%x x%x\n", + phba->brd_no, + pmbox->mbxCommand, + pmb->mbox_cmpl, + *((uint32_t *) pmbox), + mbox->un.varWords[0], + mbox->un.varWords[1], + mbox->un.varWords[2], + mbox->un.varWords[3], + mbox->un.varWords[4], + mbox->un.varWords[5], + mbox->un.varWords[6], + mbox->un.varWords[7]); + + if (pmb->mbox_cmpl) { + /* Copy entire mbox completion over buffer */ + lpfc_sli_pcimem_bcopy((uint32_t *) mbox, + (uint32_t *) pmbox, + (sizeof (uint32_t) * + (MAILBOX_CMD_WSIZE))); + /* All mbox cmpls are posted to discovery tasklet */ + lpfc_discq_post_event(phba, pmb, NULL, + LPFC_EVT_MBOX); + + } + } + + + do { + process_next = 0; /* by default don't loop */ + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + + /* Process next mailbox command if there is one */ + if ((pmb = lpfc_mbox_get(phba))) { + if (lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT) == + MBX_NOT_FINISHED) { + pmb->mb.mbxStatus = MBX_NOT_FINISHED; + /* All mbox cmpls are posted to discovery tasklet */ + lpfc_discq_post_event(phba, pmb, NULL, + LPFC_EVT_MBOX); + process_next = 1; + continue; /* loop back */ + } + } else { + /* Turn on IOCB processing */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + lpfc_sli_turn_on_ring(phba, i); + } + + /* Free any lpfc_dmabuf's waiting for mbox cmd cmpls */ + while (!list_empty(&phba->freebufList)) { + struct lpfc_dmabuf *mp; + + mp = (struct lpfc_dmabuf *) + (phba->freebufList.next); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, + mp->phys); + list_del(&mp->list); + kfree(mp); + } + } + } + + } while (process_next); + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return (0); +} +static int +lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + struct lpfc_iocbq *saveq) +{ + struct lpfc_sli * psli; + IOCB_t * irsp; + LPFC_RING_INIT_t * pringinit; + WORD5 * w5p; + uint32_t Rctl, Type; + uint32_t match, ringno, i; + unsigned long iflag; + + psli = &phba->sli; + match = 0; + ringno = pring->ringno; + irsp = &(saveq->iocb); + if ((irsp->ulpCommand == CMD_RCV_ELS_REQ64_CX) + || (irsp->ulpCommand == CMD_RCV_ELS_REQ_CX)) { + Rctl = FC_ELS_REQ; + Type = FC_ELS_DATA; + } else { + w5p = + (WORD5 *) & (saveq->iocb.un. + ulpWord[5]); + Rctl = w5p->hcsw.Rctl; + Type = w5p->hcsw.Type; + + /* Firmware Workaround */ + if ((Rctl == 0) && (pring->ringno == LPFC_ELS_RING) && + (irsp->ulpCommand == CMD_RCV_SEQUENCE64_CX)) { + Rctl = FC_ELS_REQ; + Type = FC_ELS_DATA; + w5p->hcsw.Rctl = Rctl; + w5p->hcsw.Type = Type; + } + } + /* unSolicited Responses */ + pringinit = &psli->sliinit.ringinit[ringno]; + if (pringinit->prt[0].profile) { + /* If this ring has a profile set, just + send it to prt[0] */ + /* All unsol iocbs for LPFC_ELS_RING + * are posted to discovery tasklet. + */ + if (ringno == LPFC_ELS_RING) { + spin_lock_irqsave(phba->host->host_lock, iflag); + lpfc_discq_post_event(phba, (void *)&pringinit->prt[0], + (void *)saveq, LPFC_EVT_UNSOL_IOCB); + spin_unlock_irqrestore(phba->host->host_lock, iflag); + } + else { + (pringinit->prt[0]. + lpfc_sli_rcv_unsol_event) (phba, pring, saveq); + } + match = 1; + } else { + /* We must search, based on rctl / type + for the right routine */ + for (i = 0; i < pringinit->num_mask; + i++) { + if ((pringinit->prt[i].rctl == + Rctl) + && (pringinit->prt[i]. + type == Type)) { + /* All unsol iocbs for LPFC_ELS_RING + * are posted to discovery tasklet. + */ + if (ringno == LPFC_ELS_RING) { + spin_lock_irqsave(phba->host->host_lock, iflag); + lpfc_discq_post_event(phba, + (void *)&pringinit->prt[i], + (void *)saveq, LPFC_EVT_UNSOL_IOCB); + spin_unlock_irqrestore(phba->host->host_lock, iflag); + } + else { + (pringinit->prt[i]. + lpfc_sli_rcv_unsol_event) + (phba, pring, saveq); + } + match = 1; + break; + } + } + } + if (match == 0) { + /* Unexpected Rctl / Type received */ + /* Ring handler: unexpected + Rctl Type received */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_SLI, + "%d:0313 Ring %d handler: unexpected Rctl x%x " + "Type x%x received \n", + phba->brd_no, + ringno, + Rctl, + Type); + } + return(1); +} +static struct lpfc_iocbq * +lpfc_search_txcmpl(struct lpfc_sli_ring * pring, struct lpfc_iocbq * prspiocb) +{ + IOCB_t *icmd = NULL; + IOCB_t *irsp = NULL; + struct lpfc_iocbq *cmd_iocb; + struct lpfc_iocbq *iocb, *next_iocb; + uint16_t iotag; + + irsp = &prspiocb->iocb; + iotag = irsp->ulpIoTag; + cmd_iocb = NULL; + + /* Search through txcmpl from the begining */ + list_for_each_entry_safe(iocb, next_iocb, &(pring->txcmplq), list) { + icmd = &iocb->iocb; + if (iotag == icmd->ulpIoTag) { + /* Found a match. */ + cmd_iocb = iocb; + list_del(&iocb->list); + pring->txcmplq_cnt--; + break; + } + } + + return (cmd_iocb); +} +static struct lpfc_iocbq * +lpfc_sli_ringtxcmpl_get(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * prspiocb, uint32_t srch) +{ + struct list_head *dlp; + IOCB_t *irsp = NULL; + struct lpfc_iocbq *cmd_iocb; + struct lpfc_sli *psli; + uint16_t iotag; + + + dlp = &pring->txcmplq; + + if (pring->fast_lookup && (srch == 0)) { + /* + * Use fast lookup based on iotag for completion + */ + psli = &phba->sli; + irsp = &prspiocb->iocb; + iotag = irsp->ulpIoTag; + if (iotag < psli->sliinit.ringinit[pring->ringno].fast_iotag) { + cmd_iocb = *(pring->fast_lookup + iotag); + *(pring->fast_lookup + iotag) = NULL; + if (cmd_iocb) { + list_del(&cmd_iocb->list); + pring->txcmplq_cnt--; + return cmd_iocb; + } + } else { + /* + * Rsp ring get: iotag greater then + * configured max wd0 + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_SLI, + "%d:0317 Rsp ring %d get: iotag x%x " + "greater then configured max x%x " + "wd0 x%x\n", + phba->brd_no, + pring->ringno, iotag, + psli->sliinit.ringinit[pring->ringno] + .fast_iotag, + *(((uint32_t *) irsp) + 7)); + } + } + + cmd_iocb = lpfc_search_txcmpl(pring, prspiocb); + + return cmd_iocb; +} + +static int +lpfc_sli_process_sol_iocb(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, + struct lpfc_iocbq *saveq) +{ + struct lpfc_iocbq * cmdiocbp; + int ringno, rc; + unsigned long iflag; + + rc = 1; + ringno = pring->ringno; + /* Solicited Responses */ + /* Based on the iotag field, get the cmd IOCB + from the txcmplq */ + spin_lock_irqsave(phba->host->host_lock, iflag); + if ((cmdiocbp = + lpfc_sli_ringtxcmpl_get(phba, pring, saveq, + 0))) { + /* Call the specified completion + routine */ + if (cmdiocbp->iocb_cmpl) { + /* All iocb cmpls for LPFC_ELS_RING + * are posted to discovery tasklet. + */ + if (ringno == LPFC_ELS_RING) { + lpfc_discq_post_event(phba, (void *)cmdiocbp, + (void *)saveq, LPFC_EVT_SOL_IOCB); + } + else { + if (cmdiocbp->iocb_flag & LPFC_IO_POLL) { + rc = 0; + } + + if (cmdiocbp->iocb_cmpl == lpfc_scsi_cmd_iocb_cmpl) + (cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq); + else { + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + (cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq); + spin_lock_irqsave(phba->host->host_lock, iflag); + } + } + } else { + mempool_free( cmdiocbp, phba->iocb_mem_pool); + } + } else { + /* Could not find the initiating command + * based of the response iotag. + * This is expected on ELS ring because of lpfc_els_abort(). + */ + if (ringno != LPFC_ELS_RING) { + /* Ring handler: unexpected + completion IoTag */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_SLI, + "%d:0322 Ring %d handler: unexpected " + "completion IoTag x%x Data: x%x x%x x%x x%x\n", + phba->brd_no, + ringno, + saveq->iocb.ulpIoTag, + saveq->iocb.ulpStatus, + saveq->iocb.un.ulpWord[4], + saveq->iocb.ulpCommand, + saveq->iocb.ulpContext); + } + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return(rc); +} +static int +lpfc_sli_handle_ring_event(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, uint32_t mask) +{ + struct lpfc_sli * psli; + IOCB_t * entry; + IOCB_t * irsp; + struct lpfc_iocbq * rspiocbp, *next_iocb; + struct lpfc_iocbq * cmdiocbp; + struct lpfc_iocbq * saveq; + HGP * hgp; + PGP * pgp; + MAILBOX_t * mbox; + uint32_t status, free_saveq; + uint32_t portRspPut, portRspMax; + int ringno, loopcnt, rc; + uint8_t type; + unsigned long iflag; + void *to_slim; + + psli = &phba->sli; + ringno = pring->ringno; + irsp = NULL; + rc = 1; + + spin_lock_irqsave(phba->host->host_lock, iflag); + psli->slistat.iocbEvent[ringno]++; + + /* At this point we assume SLI-2 */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + pgp = (PGP *) & mbox->us.s2.port[ringno]; + hgp = (HGP *) & mbox->us.s2.host[ringno]; + + /* portRspMax is the number of rsp ring entries for this specific + ring. */ + portRspMax = psli->sliinit.ringinit[ringno].numRiocb; + + rspiocbp = NULL; + loopcnt = 0; + + /* Gather iocb entries off response ring. + * rspidx is the IOCB index of the next IOCB that the driver + * is going to process. + */ + entry = IOCB_ENTRY(pring->rspringaddr, pring->rspidx); + portRspPut = le32_to_cpu(pgp->rspPutInx); + + if (portRspPut >= portRspMax) { + + /* Ring handler: portRspPut is bigger then + rsp ring */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_SLI, + "%d:0312 Ring %d handler: portRspPut %d " + "is bigger then rsp ring %d\n", + phba->brd_no, + ringno, portRspPut, portRspMax); + /* + * Treat it as adapter hardware error. + */ + phba->hba_state = LPFC_HBA_ERROR; + /* + All error attention handlers are posted to + discovery tasklet + */ + lpfc_discq_post_event(phba, (void *)HS_FFER3, NULL, + LPFC_EVT_ERR_ATTN); + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return (1); + } + + rmb(); + + /* Get the next available response iocb. + * rspidx is the IOCB index of the next IOCB that the driver + * is going to process. + */ + while (pring->rspidx != portRspPut) { + /* get an iocb buffer to copy entry into */ + if ((rspiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + break; + } + + lpfc_sli_pcimem_bcopy((uint32_t *) entry, + (uint32_t *) & rspiocbp->iocb, + sizeof (IOCB_t)); + irsp = &rspiocbp->iocb; + + /* bump iocb available response index */ + if (++pring->rspidx >= portRspMax) { + pring->rspidx = 0; + } + + /* Let the HBA know what IOCB slot will be the next one the + * driver will read a response from. + */ + to_slim = (uint8_t *) phba->MBslimaddr + + (SLIMOFF + (ringno * 2) + 1) * 4; + writeb( pring->rspidx, to_slim); + + /* chain all iocb entries until LE is set */ + if (list_empty(&(pring->iocb_continueq))) { + list_add(&rspiocbp->list, &(pring->iocb_continueq)); + } else { + list_add_tail(&rspiocbp->list, + &(pring->iocb_continueq)); + } + pring->iocb_continueq_cnt++; + + /* + * When the ulpLe field is set, the entire Command has been + * received. Start by getting a pointer to the first iocb entry + * in the chain. + */ + if (irsp->ulpLe) { + /* + * By default, the driver expects to free all resources + * associated with this iocb completion. + */ + free_saveq = 1; + saveq = list_entry(pring->iocb_continueq.next, + struct lpfc_iocbq, list); + irsp = &(saveq->iocb); + list_del_init(&pring->iocb_continueq); + pring->iocb_continueq_cnt = 0; + + psli->slistat.iocbRsp[ringno]++; + + if(irsp->ulpStatus) { + /* Rsp ring error: IOCB */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_SLI, + "%d:0326 Rsp Ring %d error: IOCB Data: " + "x%x x%x x%x x%x x%x x%x x%x x%x\n", + phba->brd_no, + ringno, + irsp->un.ulpWord[0], + irsp->un.ulpWord[1], + irsp->un.ulpWord[2], + irsp->un.ulpWord[3], + irsp->un.ulpWord[4], + irsp->un.ulpWord[5], + *(((uint32_t *) irsp) + 6), + *(((uint32_t *) irsp) + 7)); + } + + /* Determine if IOCB command is a solicited or + unsolicited event */ + type = + lpfc_sli_iocb_cmd_type[(irsp-> + ulpCommand & + CMD_IOCB_MASK)]; + if (type == LPFC_SOL_IOCB) { + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + rc = lpfc_sli_process_sol_iocb(phba, pring, + saveq); + spin_lock_irqsave(phba->host->host_lock, iflag); + /* + * If this solicted completion is an ELS + * command, don't free the resources now because + * the discoverytasklet does later. + */ + if (pring->ringno == LPFC_ELS_RING) + free_saveq = 0; + else + free_saveq = 1; + + } else if (type == LPFC_UNSOL_IOCB) { + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + rc = lpfc_sli_process_unsol_iocb(phba, pring, + saveq); + spin_lock_irqsave(phba->host->host_lock, iflag); + + /* + * If this unsolicted completion is an ELS + * command, don't free the resources now because + * the discoverytasklet does later. + */ + if (pring->ringno == LPFC_ELS_RING) + free_saveq = 0; + else + free_saveq = 1; + + } else if (type == LPFC_ABORT_IOCB) { + /* Solicited ABORT Responses */ + /* Based on the iotag field, get the cmd IOCB + from the txcmplq */ + if ((irsp->ulpCommand != CMD_XRI_ABORTED_CX) && + ((cmdiocbp = + lpfc_sli_ringtxcmpl_get(phba, pring, + saveq, 0)))) { + /* Call the specified completion + routine */ + if (cmdiocbp->iocb_cmpl) { + spin_unlock_irqrestore( + phba->host->host_lock, + iflag); + (cmdiocbp->iocb_cmpl) (phba, + cmdiocbp, saveq); + spin_lock_irqsave( + phba->host->host_lock, + iflag); + } else { + mempool_free(cmdiocbp, + phba->iocb_mem_pool); + } + } + } else if (type == LPFC_UNKNOWN_IOCB) { + if (irsp->ulpCommand == CMD_ADAPTER_MSG) { + + char adaptermsg[LPFC_MAX_ADPTMSG]; + + memset(adaptermsg, 0, + LPFC_MAX_ADPTMSG); + memcpy(&adaptermsg[0], (uint8_t *) irsp, + MAX_MSG_DATA); + dev_warn(&((phba->pcidev)->dev), + "lpfc%d: %s", + phba->brd_no, adaptermsg); + } else { + /* Unknown IOCB command */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_SLI, + "%d:0321 Unknown IOCB command " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + irsp->ulpCommand, + irsp->ulpStatus, + irsp->ulpIoTag, + irsp->ulpContext); + } + } + + if (free_saveq) { + /* + * Free up iocb buffer chain for command just + * processed + */ + if (!list_empty(&pring->iocb_continueq)) { + list_for_each_entry_safe(rspiocbp, + next_iocb, + &pring->iocb_continueq, list) { + list_del_init(&rspiocbp->list); + mempool_free(rspiocbp, + phba->iocb_mem_pool); + } + } + mempool_free( saveq, phba->iocb_mem_pool); + } + } + + /* Entire Command has been received */ + entry = IOCB_ENTRY(pring->rspringaddr, pring->rspidx); + + /* If the port response put pointer has not been updated, sync + * the pgp->rspPutInx in the MAILBOX_tand fetch the new port + * response put pointer. + */ + if (pring->rspidx == portRspPut) { + portRspPut = le32_to_cpu(pgp->rspPutInx); + } + } /* while (pring->rspidx != portRspPut) */ + + if ((rspiocbp != 0) && (mask & HA_R0RE_REQ)) { + /* At least one response entry has been freed */ + psli->slistat.iocbRspFull[ringno]++; + /* SET RxRE_RSP in Chip Att register */ + status = ((CA_R0ATT | CA_R0RE_RSP) << (ringno * 4)); + writel(status, phba->CAregaddr); + readl(phba->CAregaddr); /* flush */ + } + if ((mask & HA_R0CE_RSP) && (pring->flag & LPFC_CALL_RING_AVAILABLE)) { + pring->flag &= ~LPFC_CALL_RING_AVAILABLE; + psli->slistat.iocbCmdEmpty[ringno]++; + /* + * Force update of the local copy of cmdGetInx + */ + pring->local_getidx = le32_to_cpu(pgp->cmdGetInx); + lpfc_sli_resume_iocb(phba, pring); + + if ((psli->sliinit.ringinit[ringno].lpfc_sli_cmd_available)) + (psli->sliinit.ringinit[ringno]. + lpfc_sli_cmd_available) (phba, pring); + + } + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return (rc); +} + +static uint32_t +lpfc_intr_prep(struct lpfc_hba * phba) +{ + uint32_t ha_copy; + + /* Ignore all interrupts during initialization. */ + if (phba->hba_state < LPFC_LINK_DOWN) + return (0); + + /* Read host attention register to determine interrupt source */ + ha_copy = readl(phba->HAregaddr); + + /* Clear Attention Sources, except ERATT (to preserve status) & LATT + * (ha_copy & ~(HA_ERATT | HA_LATT)); + */ + writel((ha_copy & ~(HA_LATT | HA_ERATT)), phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + return (ha_copy); +} /* lpfc_intr_prep */ + +int +lpfc_sli_intr(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + uint32_t ha_copy; + unsigned long status; + int i; + unsigned long iflag; + + psli = &phba->sli; + psli->slistat.sliIntr++; + + /* + * Call the HBA to see if it is interrupting. If not, don't claim + * the interrupt + */ + spin_lock_irqsave(phba->host->host_lock, iflag); + ha_copy = lpfc_intr_prep(phba); + if (!ha_copy) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return (1); + } + + if (ha_copy & HA_ERATT) { + /* + * There was a link/board error. Read the status register to + * retrieve the error event and process it. + */ + psli->slistat.errAttnEvent++; + status = readl(phba->HSregaddr); + /* Clear Chip error bit */ + writel(HA_ERATT, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + /* + All error attention handlers are posted to + discovery tasklet + */ + + lpfc_discq_post_event(phba, (void *)status, NULL, + LPFC_EVT_ERR_ATTN); + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return (0); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + + if (ha_copy & HA_MBATT) { + /* There was a Mailbox event. */ + lpfc_sli_handle_mb_event(phba); + } + + if (ha_copy & HA_LATT) { + /* + * There was a link attention event. Provided the driver is in + * a state to handle link events, handle this event. + */ + if (psli->sliinit.sli_flag & LPFC_PROCESS_LA) { + lpfc_handle_latt(phba); + } + } + + /* Process all events on each ring */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + if ((ha_copy & HA_RXATT) + || (pring->flag & LPFC_DEFERRED_RING_EVENT)) { + if (pring->flag & LPFC_STOP_IOCB_MASK) { + pring->flag |= LPFC_DEFERRED_RING_EVENT; + } else { + lpfc_sli_handle_ring_event(phba, pring, + (ha_copy & + HA_RXMASK)); + pring->flag &= ~LPFC_DEFERRED_RING_EVENT; + } + } + ha_copy = (ha_copy >> 4); + } + + return (0); +} + +static int +lpfc_sli_abort_iocb_ring(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, + uint32_t flag) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL, *cmd = NULL; + int errcnt; + uint16_t iotag; + + psli = &phba->sli; + errcnt = 0; + + /* Error everything on txq and txcmplq + * First do the txq. + */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + list_del_init(&iocb->list); + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + + pring->txq_cnt = 0; + INIT_LIST_HEAD(&(pring->txq)); + + /* Next issue ABTS for everything on the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + cmd = &iocb->iocb; + + if (flag == LPFC_SLI_ABORT_IMED) { + /* + * Imediate abort of IOCB, clear fast_lookup entry, + * if any, deque and call compl + */ + iotag = cmd->ulpIoTag; + if (pring->fast_lookup && + iotag && + (iotag < + psli->sliinit.ringinit[pring->ringno].fast_iotag)) + *(pring->fast_lookup + iotag) = NULL; + + list_del_init(&iocb->list); + pring->txcmplq_cnt--; + + if (iocb->iocb_cmpl) { + cmd->ulpStatus = IOSTAT_LOCAL_REJECT; + cmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + continue; + } + + /* issue ABTS for this IOCB based on iotag */ + + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + errcnt++; + continue; + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + icmd = &abtsiocbp->iocb; + + icmd->un.acxri.abortType = ABORT_TYPE_ABTS; + icmd->un.acxri.abortContextTag = cmd->ulpContext; + icmd->un.acxri.abortIoTag = cmd->ulpIoTag; + + icmd->ulpLe = 1; + icmd->ulpClass = cmd->ulpClass; + if (phba->hba_state >= LPFC_LINK_UP) { + icmd->ulpCommand = CMD_ABORT_XRI_CN; + } else { + icmd->ulpCommand = CMD_CLOSE_XRI_CN; + + } + + if (lpfc_sli_issue_iocb + (phba, pring, abtsiocbp, 0) == IOCB_ERROR) { + mempool_free(abtsiocbp, phba->iocb_mem_pool); + errcnt++; + continue; + } + /* The rsp ring completion will remove IOCB from txcmplq when + * abort is read by HBA. + */ + } + + if (flag == LPFC_SLI_ABORT_IMED) { + INIT_LIST_HEAD(&(pring->txcmplq)); + pring->txcmplq_cnt = 0; + } + + return (errcnt); +} + +int +lpfc_sli_brdreset(struct lpfc_hba * phba) +{ + MAILBOX_t *swpmb; + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + uint16_t cfg_value, skip_post; + volatile uint32_t word0; + int i; + void *to_slim; + struct lpfc_dmabuf *mp, *next_mp; + + psli = &phba->sli; + + /* A board reset must use REAL SLIM. */ + psli->sliinit.sli_flag &= ~LPFC_SLI2_ACTIVE; + + word0 = 0; + swpmb = (MAILBOX_t *) & word0; + swpmb->mbxCommand = MBX_RESTART; + swpmb->mbxHc = 1; + + to_slim = phba->MBslimaddr; + writel(*(uint32_t *) swpmb, to_slim); + readl(to_slim); /* flush */ + + /* Only skip post after fc_ffinit is completed */ + if (phba->hba_state) { + skip_post = 1; + word0 = 1; /* This is really setting up word1 */ + } else { + skip_post = 0; + word0 = 0; /* This is really setting up word1 */ + } + to_slim = (uint8_t *) phba->MBslimaddr + sizeof (uint32_t); + writel(*(uint32_t *) swpmb, to_slim); + readl(to_slim); /* flush */ + + /* Reset HBA */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_SLI, + "%d:0325 Reset HBA Data: x%x x%x\n", + phba->brd_no, + phba->hba_state, + psli->sliinit.sli_flag); + + /* Turn off SERR, PERR in PCI cmd register */ + phba->hba_state = LPFC_INIT_START; + + /* perform board reset */ + phba->fc_eventTag = 0; + phba->fc_myDID = 0; + phba->fc_prevDID = 0; + + /* Turn off parity checking and serr during the physical reset */ + pci_read_config_word(phba->pcidev, PCI_COMMAND, &cfg_value); + pci_write_config_word(phba->pcidev, PCI_COMMAND, + (cfg_value & + ~(PCI_COMMAND_PARITY | PCI_COMMAND_SERR))); + + /* Now toggle INITFF bit in the Host Control Register */ + writel(HC_INITFF, phba->HCregaddr); + mdelay(1); + readl(phba->HCregaddr); /* flush */ + writel(0, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + /* Restore PCI cmd register */ + + pci_write_config_word(phba->pcidev, PCI_COMMAND, cfg_value); + phba->hba_state = LPFC_INIT_START; + + /* Initialize relevant SLI info */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + pring->flag = 0; + pring->rspidx = 0; + pring->next_cmdidx = 0; + pring->local_getidx = 0; + pring->cmdidx = 0; + pring->missbufcnt = 0; + } + + if (skip_post) { + mdelay(100); + } else { + mdelay(2000); + } + + /* Cleanup preposted buffers on the ELS ring */ + pring = &psli->ring[LPFC_ELS_RING]; + list_for_each_entry_safe(mp, next_mp, &pring->postbufq, list) { + list_del(&mp->list); + pring->postbufq_cnt--; + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + lpfc_sli_abort_iocb_ring(phba, pring, LPFC_SLI_ABORT_IMED); + } + + return (0); +} + +static void +lpfc_setup_slim_access(struct lpfc_hba *phba) +{ + phba->MBslimaddr = phba->slim_memmap_p; + phba->HAregaddr = (uint32_t *) (phba->ctrl_regs_memmap_p) + + HA_REG_OFFSET; + phba->HCregaddr = (uint32_t *) (phba->ctrl_regs_memmap_p) + + HC_REG_OFFSET; + phba->CAregaddr = (uint32_t *) (phba->ctrl_regs_memmap_p) + + CA_REG_OFFSET; + phba->HSregaddr = (uint32_t *) (phba->ctrl_regs_memmap_p) + + HS_REG_OFFSET; + return; +} + +int +lpfc_sli_hba_setup(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *pmb; + int read_rev_reset, i, rc; + uint32_t status; + + psli = &phba->sli; + + /* Setep SLI interface for HBA register and HBA SLIM access */ + lpfc_setup_slim_access(phba); + + /* Set board state to initialization started */ + phba->hba_state = LPFC_INIT_START; + read_rev_reset = 0; + + /* On some platforms/OS's, the driver can't rely on the state the + * adapter may be in. For this reason, the driver is allowed to reset + * the HBA before initialization. + */ + if (lpfc_sli_reset_on_init) { + phba->hba_state = 0; /* Don't skip post */ + lpfc_sli_brdreset(phba); + phba->hba_state = LPFC_INIT_START; + + /* Sleep for 2.5 sec */ + msleep(2500); + } + +top: + /* Read the HBA Host Status Register */ + status = readl(phba->HSregaddr); + + /* Check status register to see what current state is */ + i = 0; + while ((status & (HS_FFRDY | HS_MBRDY)) != (HS_FFRDY | HS_MBRDY)) { + + /* Check every 100ms for 5 retries, then every 500ms for 5, then + * every 2.5 sec for 5, then reset board and every 2.5 sec for + * 4. + */ + if (i++ >= 20) { + /* Adapter failed to init, timeout, status reg + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0436 Adapter failed to init, " + "timeout, status reg x%x\n", + phba->brd_no, + status); + phba->hba_state = LPFC_HBA_ERROR; + return -ETIMEDOUT; + } + + /* Check to see if any errors occurred during init */ + if (status & HS_FFERM) { + /* ERROR: During chipset initialization */ + /* Adapter failed to init, chipset, status reg + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0437 Adapter failed to init, " + "chipset, status reg x%x\n", + phba->brd_no, + status); + phba->hba_state = LPFC_HBA_ERROR; + return -EIO; + } + + if (i <= 5) { + msleep(10); + } else if (i <= 10) { + msleep(500); + } else { + msleep(2500); + } + + if (i == 15) { + phba->hba_state = 0; /* Don't skip post */ + lpfc_sli_brdreset(phba); + phba->hba_state = LPFC_INIT_START; + } + /* Read the HBA Host Status Register */ + status = readl(phba->HSregaddr); + } + + /* Check to see if any errors occurred during init */ + if (status & HS_FFERM) { + /* ERROR: During chipset initialization */ + /* Adapter failed to init, chipset, status reg */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0438 Adapter failed to init, chipset, " + "status reg x%x\n", + phba->brd_no, + status); + phba->hba_state = LPFC_HBA_ERROR; + return -EIO; + } + + /* Clear all interrupt enable conditions */ + writel(0, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + /* setup host attn register */ + writel(0xffffffff, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + + /* Get a Mailbox buffer to setup mailbox commands for HBA + initialization */ + if ((pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + phba->hba_state = LPFC_HBA_ERROR; + return -ENOMEM; + } + + /* Call pre CONFIG_PORT mailbox command initialization. A value of 0 + * means the call was successful. Any other nonzero value is a failure, + * but if ERESTART is returned, the driver may reset the HBA and try + * again. + */ + if ((rc = lpfc_config_port_prep(phba))) { + if ((rc == -ERESTART) && (read_rev_reset == 0)) { + mempool_free( pmb, phba->mbox_mem_pool); + phba->hba_state = 0; /* Don't skip post */ + lpfc_sli_brdreset(phba); + phba->hba_state = LPFC_INIT_START; + msleep(500); + read_rev_reset = 1; + goto top; + } + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -ENXIO; + } + + /* Setup and issue mailbox CONFIG_PORT command */ + phba->hba_state = LPFC_INIT_MBX_CMDS; + lpfc_config_port(phba, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + /* Adapter failed to init, mbxCmd CONFIG_PORT, + mbxStatus */ + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0442 Adapter failed to init, mbxCmd x%x " + "CONFIG_PORT, mbxStatus x%x Data: x%x\n", + phba->brd_no, pmb->mb.mbxCommand, + pmb->mb.mbxStatus, 0); + + /* This clause gives the config_port call is given multiple + chances to succeed. */ + if (read_rev_reset == 0) { + mempool_free( pmb, phba->mbox_mem_pool); + phba->hba_state = 0; /* Don't skip post */ + lpfc_sli_brdreset(phba); + phba->hba_state = LPFC_INIT_START; + msleep(2500); + read_rev_reset = 1; + goto top; + } + + psli->sliinit.sli_flag &= ~LPFC_SLI2_ACTIVE; + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -ENXIO; + } + + if ((rc = lpfc_sli_ring_map(phba))) { + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -ENXIO; + } + psli->sliinit.sli_flag |= LPFC_PROCESS_LA; + + /* Call post CONFIG_PORT mailbox command initialization. */ + if ((rc = lpfc_config_port_post(phba))) { + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -ENXIO; + } + mempool_free( pmb, phba->mbox_mem_pool); + return 0; +} + + +static void +lpfc_mbox_abort(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *pmbox; + MAILBOX_t *mb; + + psli = &phba->sli; + + if (psli->mbox_active) { + del_timer_sync(&psli->mbox_tmo); + phba->work_hba_events &= ~WORKER_MBOX_TMO; + pmbox = psli->mbox_active; + mb = &pmbox->mb; + psli->mbox_active = NULL; + if (pmbox->mbox_cmpl) { + mb->mbxStatus = MBX_NOT_FINISHED; + (pmbox->mbox_cmpl) (phba, pmbox); + } + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + } + + /* Abort all the non active mailbox commands. */ + pmbox = lpfc_mbox_get(phba); + while (pmbox) { + mb = &pmbox->mb; + if (pmbox->mbox_cmpl) { + mb->mbxStatus = MBX_NOT_FINISHED; + (pmbox->mbox_cmpl) (phba, pmbox); + } + pmbox = lpfc_mbox_get(phba); + } + return; +} +/*! lpfc_mbox_timeout + * + * \pre + * \post + * \param hba Pointer to per struct lpfc_hba structure + * \param l1 Pointer to the driver's mailbox queue. + * \return + * void + * + * \b Description: + * + * This routine handles mailbox timeout events at timer interrupt context. + */ +void +lpfc_mbox_timeout(unsigned long ptr) +{ + struct lpfc_hba *phba; + unsigned long iflag; + + phba = (struct lpfc_hba *)ptr; + spin_lock_irqsave(phba->host->host_lock, iflag); + if (!(phba->work_hba_events & WORKER_MBOX_TMO)) { + phba->work_hba_events |= WORKER_MBOX_TMO; + if (phba->dpc_wait) + up(phba->dpc_wait); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); +} + +void +lpfc_mbox_timeout_handler(struct lpfc_hba *phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *pmbox; + MAILBOX_t *mb; + + psli = &phba->sli; + spin_lock_irq(phba->host->host_lock); + if (!(phba->work_hba_events & WORKER_MBOX_TMO)) { + spin_unlock_irq(phba->host->host_lock); + return; + } + + phba->work_hba_events &= ~WORKER_MBOX_TMO; + + pmbox = psli->mbox_active; + mb = &pmbox->mb; + + /* Mbox cmd timeout */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_MBOX | LOG_SLI, + "%d:0310 Mailbox command x%x timeout Data: x%x x%x x%p\n", + phba->brd_no, + mb->mbxCommand, + phba->hba_state, + psli->sliinit.sli_flag, + psli->mbox_active); + + if (psli->mbox_active == pmbox) { + psli->mbox_active = NULL; + if (pmbox->mbox_cmpl) { + mb->mbxStatus = MBX_NOT_FINISHED; + (pmbox->mbox_cmpl) (phba, pmbox); + } + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + } + + lpfc_mbox_abort(phba); + spin_unlock_irq(phba->host->host_lock); + return; +} + + +int +lpfc_sli_issue_mbox(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmbox, uint32_t flag) +{ + MAILBOX_t *mbox; + MAILBOX_t *mb; + struct lpfc_sli *psli; + uint32_t status, evtctr; + uint32_t ha_copy; + int i; + unsigned long drvr_flag = 0; + volatile uint32_t word0, ldata; + void *to_slim; + + psli = &phba->sli; + if (flag & MBX_POLL) { + spin_lock_irqsave(phba->host->host_lock, drvr_flag); + } + + mb = &pmbox->mb; + status = MBX_SUCCESS; + + if (psli->sliinit.sli_flag & LPFC_SLI_MBOX_ACTIVE) { + /* Polling for a mbox command when another one is already active + * is not allowed in SLI. Also, the driver must have established + * SLI2 mode to queue and process multiple mbox commands. + */ + + if (flag & MBX_POLL) { + spin_unlock_irqrestore(phba->host->host_lock, + drvr_flag); + + /* Mbox command cannot issue */ + LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag) + return (MBX_NOT_FINISHED); + } + + if (!(psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE)) { + + /* Mbox command cannot issue */ + LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag) + return (MBX_NOT_FINISHED); + } + + /* Handle STOP IOCB processing flag. This is only meaningful + * if we are not polling for mbox completion. + */ + if (flag & MBX_STOP_IOCB) { + flag &= ~MBX_STOP_IOCB; + /* Now flag each ring */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + /* If the ring is active, flag it */ + if (psli->ring[i].cmdringaddr) { + psli->ring[i].flag |= + LPFC_STOP_IOCB_MBX; + } + } + } + + /* Another mailbox command is still being processed, queue this + * command to be processed later. + */ + lpfc_mbox_put(phba, pmbox); + + /* Mbox cmd issue - BUSY */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_MBOX | LOG_SLI, + "%d:0308 Mbox cmd issue - BUSY Data: x%x x%x x%x x%x\n", + phba->brd_no, + mb->mbxCommand, + phba->hba_state, + psli->sliinit.sli_flag, + flag); + + psli->slistat.mboxBusy++; + if (flag == MBX_POLL) { + spin_unlock_irqrestore(phba->host->host_lock, + drvr_flag); + } + return (MBX_BUSY); + } + + /* Handle STOP IOCB processing flag. This is only meaningful + * if we are not polling for mbox completion. + */ + if (flag & MBX_STOP_IOCB) { + flag &= ~MBX_STOP_IOCB; + if (flag == MBX_NOWAIT) { + /* Now flag each ring */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + /* If the ring is active, flag it */ + if (psli->ring[i].cmdringaddr) { + psli->ring[i].flag |= + LPFC_STOP_IOCB_MBX; + } + } + } + } + + psli->sliinit.sli_flag |= LPFC_SLI_MBOX_ACTIVE; + + /* If we are not polling, we MUST be in SLI2 mode */ + if (flag != MBX_POLL) { + if (!(psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE)) { + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + + /* Mbox command cannot issue */ + LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag); + return (MBX_NOT_FINISHED); + } + /* timeout active mbox command */ + mod_timer(&psli->mbox_tmo, jiffies + HZ * LPFC_MBOX_TMO); + } + + /* Mailbox cmd issue */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_MBOX | LOG_SLI, + "%d:0309 Mailbox cmd x%x issue Data: x%x x%x x%x\n", + phba->brd_no, + mb->mbxCommand, + phba->hba_state, + psli->sliinit.sli_flag, + flag); + + psli->slistat.mboxCmd++; + evtctr = psli->slistat.mboxEvent; + + /* next set own bit for the adapter and copy over command word */ + mb->mbxOwner = OWN_CHIP; + + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) { + + /* First copy command data to host SLIM area */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + lpfc_sli_pcimem_bcopy((uint32_t *) mb, (uint32_t *) mbox, + (sizeof (uint32_t) * + (MAILBOX_CMD_WSIZE))); + + } else { + if (mb->mbxCommand == MBX_CONFIG_PORT) { + /* copy command data into host mbox for cmpl */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + lpfc_sli_pcimem_bcopy((uint32_t *) mb, + (uint32_t *) mbox, + (sizeof (uint32_t) * + (MAILBOX_CMD_WSIZE))); + } + + /* First copy mbox command data to HBA SLIM, skip past first + word */ + to_slim = (uint8_t *) phba->MBslimaddr + sizeof (uint32_t); + lpfc_memcpy_to_slim(to_slim, (void *)&mb->un.varWords[0], + (MAILBOX_CMD_WSIZE - 1) * sizeof (uint32_t)); + + /* Next copy over first word, with mbxOwner set */ + ldata = *((volatile uint32_t *)mb); + to_slim = phba->MBslimaddr; + writel(ldata, to_slim); + readl(to_slim); /* flush */ + + if (mb->mbxCommand == MBX_CONFIG_PORT) { + /* switch over to host mailbox */ + psli->sliinit.sli_flag |= LPFC_SLI2_ACTIVE; + } + } + + wmb(); + /* interrupt board to doit right away */ + writel(CA_MBATT, phba->CAregaddr); + readl(phba->CAregaddr); /* flush */ + + switch (flag) { + case MBX_NOWAIT: + /* Don't wait for it to finish, just return */ + psli->mbox_active = pmbox; + break; + + case MBX_POLL: + i = 0; + psli->mbox_active = NULL; + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) { + /* First read mbox status word */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + word0 = *((volatile uint32_t *)mbox); + word0 = le32_to_cpu(word0); + } else { + /* First read mbox status word */ + word0 = readl(phba->MBslimaddr); + } + + /* Read the HBA Host Attention Register */ + ha_copy = readl(phba->HAregaddr); + + /* Wait for command to complete */ + while (((word0 & OWN_CHIP) == OWN_CHIP) + || !(ha_copy & HA_MBATT)) { + if (i++ >= 5000) { + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + spin_unlock_irqrestore(phba->host->host_lock, + drvr_flag); + return (MBX_NOT_FINISHED); + } + + /* Check if we took a mbox interrupt while we were + polling */ + if (((word0 & OWN_CHIP) != OWN_CHIP) + && (evtctr != psli->slistat.mboxEvent)) + break; + + /* Can be in interrupt context, do not sleep */ + /* (or might be called with interrupts disabled) */ + udelay(1000); + + + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) { + /* First copy command data */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + word0 = *((volatile uint32_t *)mbox); + word0 = le32_to_cpu(word0); + if (mb->mbxCommand == MBX_CONFIG_PORT) { + MAILBOX_t *slimmb; + volatile uint32_t slimword0; + /* Check real SLIM for any errors */ + slimword0 = readl(phba->MBslimaddr); + slimmb = (MAILBOX_t *) & slimword0; + if (((slimword0 & OWN_CHIP) != OWN_CHIP) + && slimmb->mbxStatus) { + psli->sliinit.sli_flag &= + ~LPFC_SLI2_ACTIVE; + word0 = slimword0; + } + } + } else { + /* First copy command data */ + word0 = readl(phba->MBslimaddr); + } + /* Read the HBA Host Attention Register */ + ha_copy = readl(phba->HAregaddr); + } + + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) { + /* First copy command data */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + /* copy results back to user */ + lpfc_sli_pcimem_bcopy((uint32_t *) mbox, + (uint32_t *) mb, + (sizeof (uint32_t) * + MAILBOX_CMD_WSIZE)); + } else { + /* First copy command data */ + lpfc_memcpy_from_slim((void *)mb, + phba->MBslimaddr, + sizeof (uint32_t) * (MAILBOX_CMD_WSIZE)); + if ((mb->mbxCommand == MBX_DUMP_MEMORY) && + pmbox->context2) { + lpfc_memcpy_from_slim((void *)pmbox->context2, + phba->MBslimaddr + DMP_RSP_OFFSET, + mb->un.varDmp.word_cnt); + } + } + + writel(HA_MBATT, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + status = mb->mbxStatus; + } + + if (flag == MBX_POLL) { + spin_unlock_irqrestore(phba->host->host_lock, drvr_flag); + } + return (status); +} + +static struct lpfc_iocbq * +lpfc_sli_next_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + struct lpfc_iocbq ** piocb) +{ + struct lpfc_iocbq * nextiocb; + + nextiocb = lpfc_sli_ringtx_get(phba, pring); + if (!nextiocb) { + nextiocb = *piocb; + *piocb = NULL; + } + + return nextiocb; +} + +int +lpfc_sli_issue_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + struct lpfc_iocbq *piocb, uint32_t flag) +{ + struct lpfc_sli *psli = &phba->sli; + int ringno = pring->ringno; + struct lpfc_iocbq *nextiocb; + IOCB_t *iocb; + + /* + * We should never get an IOCB if we are in a < LINK_DOWN state + */ + if (unlikely(phba->hba_state < LPFC_LINK_DOWN)) + return IOCB_ERROR; + + /* + * Check to see if we are blocking IOCB processing because of a + * outstanding mbox command. + */ + if (unlikely(pring->flag & LPFC_STOP_IOCB_MBX)) + goto iocb_busy; + + if (unlikely(phba->hba_state == LPFC_LINK_DOWN)) { + /* + * Only CREATE_XRI, CLOSE_XRI, ABORT_XRI, and QUE_RING_BUF + * can be issued if the link is not up. + */ + switch (piocb->iocb.ulpCommand) { + case CMD_QUE_RING_BUF_CN: + case CMD_QUE_RING_BUF64_CN: + /* + * For IOCBs, like QUE_RING_BUF, that have no rsp ring + * completion, iocb_cmpl MUST be 0. + */ + if (piocb->iocb_cmpl) + piocb->iocb_cmpl = NULL; + /*FALLTHROUGH*/ + case CMD_CREATE_XRI_CR: + break; + default: + goto iocb_busy; + } + + /* + * For FCP commands, we must be in a state where we can process link + * attention events. + */ + } else if (unlikely(pring->ringno == psli->fcp_ring && + !(psli->sliinit.sli_flag & LPFC_PROCESS_LA))) + goto iocb_busy; + + /* + * Check to see if this is a high priority command. + * If so bypass tx queue processing. + */ + if (unlikely((flag & SLI_IOCB_HIGH_PRIORITY) && + (iocb = lpfc_sli_next_iocb_slot(phba, pring)))) { + if (lpfc_sli_submit_iocb(phba, pring, iocb, piocb)) + goto iocb_busy; + piocb = NULL; + } + + while ((iocb = lpfc_sli_next_iocb_slot(phba, pring)) && + (nextiocb = lpfc_sli_next_iocb(phba, pring, &piocb))) + if (lpfc_sli_submit_iocb(phba, pring, iocb, nextiocb)) + break; + + if (iocb) + lpfc_sli_update_ring(phba, pring); + else + lpfc_sli_update_full_ring(phba, pring); + + if (!piocb) + return IOCB_SUCCESS; + + goto out_busy; + + iocb_busy: + psli->slistat.iocbCmdDelay[ringno]++; + + out_busy: + + if (!(flag & SLI_IOCB_RET_IOCB)) { + lpfc_sli_ringtx_put(phba, pring, piocb); + return IOCB_SUCCESS; + } + + return IOCB_BUSY; +} + +int +lpfc_sli_queue_setup(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + int i, cnt; + + psli = &phba->sli; + INIT_LIST_HEAD(&psli->mboxq); + /* Initialize list headers for txq and txcmplq as double linked lists */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + pring->ringno = i; + pring->next_cmdidx = 0; + pring->local_getidx = 0; + pring->cmdidx = 0; + INIT_LIST_HEAD(&pring->txq); + INIT_LIST_HEAD(&pring->txcmplq); + INIT_LIST_HEAD(&pring->iocb_continueq); + INIT_LIST_HEAD(&pring->postbufq); + cnt = psli->sliinit.ringinit[i].fast_iotag; + if (cnt) { + pring->fast_lookup = + kmalloc(cnt * sizeof (struct lpfc_iocbq *), + GFP_KERNEL); + if (pring->fast_lookup == 0) { + return (0); + } + memset((char *)pring->fast_lookup, 0, + cnt * sizeof (struct lpfc_iocbq *)); + } + } + return (1); +} + +int +lpfc_sli_hba_down(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + LPFC_MBOXQ_t *pmb; + struct lpfc_iocbq *iocb, *next_iocb; + IOCB_t *icmd = NULL; + int i; + + psli = &phba->sli; + lpfc_hba_down_prep(phba); + + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + pring->flag |= LPFC_DEFERRED_RING_EVENT; + + /* + * Error everything on the txq since these iocbs have not been + * given to the FW yet. + */ + pring->txq_cnt = 0; + + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + list_del_init(&iocb->list); + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_DOWN; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + + INIT_LIST_HEAD(&(pring->txq)); + + if (pring->fast_lookup) { + kfree(pring->fast_lookup); + pring->fast_lookup = NULL; + } + + } + + /* Return any active mbox cmds */ + del_timer_sync(&psli->mbox_tmo); + phba->work_hba_events &= ~WORKER_MBOX_TMO; + if ((psli->mbox_active)) { + pmb = psli->mbox_active; + pmb->mb.mbxStatus = MBX_NOT_FINISHED; + if (pmb->mbox_cmpl) + pmb->mbox_cmpl(phba,pmb); + } + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + psli->mbox_active = NULL; + + /* Return any pending mbox cmds */ + while ((pmb = lpfc_mbox_get(phba)) != NULL) { + pmb->mb.mbxStatus = MBX_NOT_FINISHED; + if (pmb->mbox_cmpl) + pmb->mbox_cmpl(phba,pmb); + } + + INIT_LIST_HEAD(&psli->mboxq); + + /* + * Provided the hba is not in an error state, reset it. It is not + * capable of IO anymore. + */ + if (phba->hba_state != LPFC_HBA_ERROR) { + phba->hba_state = LPFC_INIT_START; + lpfc_sli_brdreset(phba); + } + + return 1; +} + +void +lpfc_sli_pcimem_bcopy(uint32_t * src, uint32_t * dest, uint32_t cnt) +{ + uint32_t ldata; + int i; + + for (i = 0; i < (int)cnt; i += sizeof (uint32_t)) { + ldata = *src++; + ldata = le32_to_cpu(ldata); + *dest++ = ldata; + } +} + +int +lpfc_sli_ringpostbuf_put(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, + struct lpfc_dmabuf * mp) +{ + /* Stick struct lpfc_dmabuf at end of postbufq so driver can look it up + later */ + list_add_tail(&mp->list, &pring->postbufq); + + pring->postbufq_cnt++; + return 0; +} + + +struct lpfc_dmabuf * +lpfc_sli_ringpostbuf_get(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + dma_addr_t phys) +{ + struct lpfc_dmabuf *mp, *next_mp; + struct list_head *slp = &pring->postbufq; + + /* Search postbufq, from the begining, looking for a match on phys */ + list_for_each_entry_safe(mp, next_mp, &pring->postbufq, list) { + if (mp->phys == phys) { + list_del_init(&mp->list); + pring->postbufq_cnt--; + return mp; + } + } + + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0410 Cannot find virtual addr for mapped buf on " + "ring %d Data x%llx x%p x%p x%x\n", + phba->brd_no, pring->ringno, (unsigned long long)phys, + slp->next, slp->prev, pring->postbufq_cnt); + return NULL; +} + +uint32_t +lpfc_sli_next_iotag(struct lpfc_hba * phba, struct lpfc_sli_ring * pring) +{ + LPFC_RING_INIT_t *pringinit; + struct lpfc_sli *psli; + uint32_t search_start; + + psli = &phba->sli; + pringinit = &psli->sliinit.ringinit[pring->ringno]; + + if (pring->fast_lookup == NULL) { + pringinit->iotag_ctr++; + if (pringinit->iotag_ctr >= pringinit->iotag_max) + pringinit->iotag_ctr = 1; + return pringinit->iotag_ctr; + } + + search_start = pringinit->iotag_ctr; + + do { + pringinit->iotag_ctr++; + if (pringinit->iotag_ctr >= pringinit->fast_iotag) + pringinit->iotag_ctr = 1; + + if(*(pring->fast_lookup + pringinit->iotag_ctr) == NULL) + return pringinit->iotag_ctr; + + } while (pringinit->iotag_ctr != search_start); + + /* + * Outstanding I/O count for ring is at max + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_SLI, + "%d:0318 Outstanding I/O count for ring %d is at max x%x\n", + phba->brd_no, + pring->ringno, + psli->sliinit.ringinit[pring->ringno].fast_iotag); + return (0); +} + +static void +lpfc_sli_abort_elsreq_cmpl(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + struct lpfc_dmabuf *buf_ptr, *buf_ptr1; + /* Free the resources associated with the ELS_REQUEST64 IOCB the driver + * just aborted. + * In this case, context2 = cmd, context2->next = rsp, context3 = bpl + */ + if (cmdiocb->context2) { + buf_ptr1 = (struct lpfc_dmabuf *) cmdiocb->context2; + + /* Free the response IOCB before completing the abort + command. */ + if (!list_empty(&buf_ptr1->list)) { + + buf_ptr = list_entry(buf_ptr1->list.next, + struct lpfc_dmabuf, list); + + list_del(&buf_ptr->list); + lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys); + kfree(buf_ptr); + } + lpfc_mbuf_free(phba, buf_ptr1->virt, buf_ptr1->phys); + kfree(buf_ptr1); + } + + if (cmdiocb->context3) { + buf_ptr = (struct lpfc_dmabuf *) cmdiocb->context3; + lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys); + kfree(buf_ptr); + } + mempool_free( cmdiocb, phba->iocb_mem_pool); + return; +} + +int +lpfc_sli_issue_abort_iotag32(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * cmdiocb) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL; + IOCB_t *iabt = NULL; + uint32_t iotag32; + + psli = &phba->sli; + + /* issue ABTS for this IOCB based on iotag */ + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC)) == 0) { + return (0); + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + iabt = &abtsiocbp->iocb; + + icmd = &cmdiocb->iocb; + switch (icmd->ulpCommand) { + case CMD_ELS_REQUEST64_CR: + iotag32 = icmd->un.elsreq64.bdl.ulpIoTag32; + /* Even though we abort the ELS command, the firmware may access + * the BPL or other resources before it processes our + * ABORT_MXRI64. Thus we must delay reusing the cmdiocb + * resources till the actual abort request completes. + */ + abtsiocbp->context1 = (void *)((unsigned long)icmd->ulpCommand); + abtsiocbp->context2 = cmdiocb->context2; + abtsiocbp->context3 = cmdiocb->context3; + cmdiocb->context2 = NULL; + cmdiocb->context3 = NULL; + abtsiocbp->iocb_cmpl = lpfc_sli_abort_elsreq_cmpl; + break; + default: + mempool_free( abtsiocbp, phba->iocb_mem_pool); + return (0); + } + + iabt->un.amxri.abortType = ABORT_TYPE_ABTS; + iabt->un.amxri.iotag32 = iotag32; + + iabt->ulpLe = 1; + iabt->ulpClass = CLASS3; + iabt->ulpCommand = CMD_ABORT_MXRI64_CN; + + if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == IOCB_ERROR) { + mempool_free( abtsiocbp, phba->iocb_mem_pool); + return (0); + } + + return (1); +} + +void +lpfc_sli_abort_fcp_cmpl(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + /* + * Just free the iocbq resources back to the memory pool. This was an + * abort command and has no other outstanding resources associated with + * it. + */ + mempool_free(cmdiocb, phba->iocb_mem_pool); +} + + +int +lpfc_sli_abort_iocb_ctx(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, + uint32_t ctx) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL, *cmd = NULL; + int errcnt; + + psli = &phba->sli; + errcnt = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + if (cmd->ulpContext != ctx) { + continue; + } + + list_del_init(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + cmd = &iocb->iocb; + if (cmd->ulpContext != ctx) { + continue; + } + + /* issue ABTS for this IOCB based on iotag */ + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + errcnt++; + continue; + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + icmd = &abtsiocbp->iocb; + + icmd->un.acxri.abortType = ABORT_TYPE_ABTS; + icmd->un.acxri.abortContextTag = cmd->ulpContext; + icmd->un.acxri.abortIoTag = cmd->ulpIoTag; + + icmd->ulpLe = 1; + icmd->ulpClass = cmd->ulpClass; + abtsiocbp->iocb_cmpl = lpfc_sli_abort_fcp_cmpl; + if (phba->hba_state >= LPFC_LINK_UP) { + icmd->ulpCommand = CMD_ABORT_XRI_CN; + } else { + icmd->ulpCommand = CMD_CLOSE_XRI_CN; + } + + if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == + IOCB_ERROR) { + mempool_free( abtsiocbp, phba->iocb_mem_pool); + errcnt++; + continue; + } + /* The rsp ring completion will remove IOCB from txcmplq when + * abort is read by HBA. + */ + } + return (errcnt); +} + +int +lpfc_sli_sum_iocb_host(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + IOCB_t *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + int sum; + + psli = &phba->sli; + sum = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if (lpfc_cmd == 0) { + continue; + } + sum++; + } + + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if (lpfc_cmd == 0) { + continue; + } + sum++; + } + return (sum); +} + +int +lpfc_sli_abort_iocb_host(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, int flag) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL, *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + int errcnt; + + psli = &phba->sli; + errcnt = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + if(flag & LPFC_ABORT_TXQ) { + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if (lpfc_cmd == 0) { + continue; + } + + list_del_init(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + } + + if(flag & LPFC_ABORT_TXCMPLQ) { + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, + list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if (lpfc_cmd == 0) { + continue; + } + + /* issue ABTS for this IOCB based on iotag */ + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + errcnt++; + continue; + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + icmd = &abtsiocbp->iocb; + + icmd->un.acxri.abortType = ABORT_TYPE_ABTS; + icmd->un.acxri.abortContextTag = cmd->ulpContext; + icmd->un.acxri.abortIoTag = cmd->ulpIoTag; + + icmd->ulpLe = 1; + icmd->ulpClass = cmd->ulpClass; + abtsiocbp->iocb_cmpl = lpfc_sli_abort_fcp_cmpl; + if (phba->hba_state >= LPFC_LINK_UP) { + icmd->ulpCommand = CMD_ABORT_XRI_CN; + } else { + icmd->ulpCommand = CMD_CLOSE_XRI_CN; + } + + if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == + IOCB_ERROR) { + mempool_free( abtsiocbp, phba->iocb_mem_pool); + errcnt++; + continue; + } + /* The rsp ring completion will remove IOCB from + * tacmplq when abort is read by HBA. + */ + } + } + return (errcnt); +} + +int +lpfc_sli_sum_iocb_lun(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + uint16_t scsi_target, uint64_t scsi_lun) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + IOCB_t *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + int sum; + + psli = &phba->sli; + sum = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) || + (lpfc_cmd->target == 0) || + (lpfc_cmd->target->scsi_id != scsi_target) || + (lpfc_cmd->lun != scsi_lun)) { + continue; + } + sum++; + } + + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) || + (lpfc_cmd->target == 0) || + (lpfc_cmd->target->scsi_id != scsi_target) || + (lpfc_cmd->lun != scsi_lun)) { + continue; + } + + sum++; + } + return (sum); +} + +int +lpfc_sli_abort_iocb_lun(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + uint16_t scsi_target, uint64_t scsi_lun, int flag) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL, *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + int errcnt; + + psli = &phba->sli; + errcnt = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + if(flag & LPFC_ABORT_TXQ) { + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) || + (lpfc_cmd->target == 0) || + (lpfc_cmd->target->scsi_id != scsi_target) || + (lpfc_cmd->lun != scsi_lun)) { + continue; + } + + list_del_init(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + } + + if(flag & LPFC_ABORT_TXCMPLQ) { + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, + list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) || + (lpfc_cmd->target == 0) || + (lpfc_cmd->target->scsi_id != scsi_target) || + (lpfc_cmd->lun != scsi_lun)) { + continue; + } + + /* issue ABTS for this IOCB based on iotag */ + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + errcnt++; + continue; + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + icmd = &abtsiocbp->iocb; + + icmd->un.acxri.abortType = ABORT_TYPE_ABTS; + icmd->un.acxri.abortContextTag = cmd->ulpContext; + icmd->un.acxri.abortIoTag = cmd->ulpIoTag; + + icmd->ulpLe = 1; + icmd->ulpClass = cmd->ulpClass; + abtsiocbp->iocb_cmpl = lpfc_sli_abort_fcp_cmpl; + if (phba->hba_state >= LPFC_LINK_UP) { + icmd->ulpCommand = CMD_ABORT_XRI_CN; + } else { + icmd->ulpCommand = CMD_CLOSE_XRI_CN; + } + + if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == + IOCB_ERROR) { + mempool_free( abtsiocbp, phba->iocb_mem_pool); + errcnt++; + continue; + } + /* The rsp ring completion will remove IOCB from + * tacmplq when abort is read by HBA. + */ + } + } + return (errcnt); +} + +int +lpfc_sli_abort_iocb_tgt(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + uint16_t scsi_target, int flag) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL, *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + int errcnt; + + psli = &phba->sli; + errcnt = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + if(flag & LPFC_ABORT_TXQ) { + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) || + (lpfc_cmd->target == 0) || + (lpfc_cmd->target->scsi_id != scsi_target)) { + continue; + } + + list_del_init(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + } + + if(flag & LPFC_ABORT_TXCMPLQ) { + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, + list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) || + (lpfc_cmd->target == 0) || + (lpfc_cmd->target->scsi_id != scsi_target)) { + continue; + } + + /* issue ABTS for this IOCB based on iotag */ + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + errcnt++; + continue; + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + icmd = &abtsiocbp->iocb; + + icmd->un.acxri.abortType = ABORT_TYPE_ABTS; + icmd->un.acxri.abortContextTag = cmd->ulpContext; + icmd->un.acxri.abortIoTag = cmd->ulpIoTag; + + icmd->ulpLe = 1; + icmd->ulpClass = cmd->ulpClass; + abtsiocbp->iocb_cmpl = lpfc_sli_abort_fcp_cmpl; + if (phba->hba_state >= LPFC_LINK_UP) { + icmd->ulpCommand = CMD_ABORT_XRI_CN; + } else { + icmd->ulpCommand = CMD_CLOSE_XRI_CN; + } + + if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == + IOCB_ERROR) { + mempool_free( abtsiocbp, phba->iocb_mem_pool); + errcnt++; + continue; + } + /* The rsp ring completion will remove IOCB from + * txcmplq when abort is read by HBA. + */ + } + } + return (errcnt); +} + + + +void +lpfc_sli_wake_iocb_high_priority(struct lpfc_hba * phba, + struct lpfc_iocbq * queue1, + struct lpfc_iocbq * queue2) +{ + if (queue1->context2 && queue2) + memcpy(queue1->context2, queue2, sizeof (struct lpfc_iocbq)); + + /* The waiter is looking for LPFC_IO_HIPRI bit to be set + as a signal to wake up */ + queue1->iocb_flag |= LPFC_IO_HIPRI; + return; +} + +static void +lpfc_sli_wake_iocb_high_priority_cleanup(struct lpfc_hba * phba, + struct lpfc_iocbq * queue1, + struct lpfc_iocbq * queue2) +{ + struct lpfc_scsi_buf *lpfc_cmd = queue1->context1; + + /* + * Just free the iocbq back to the mempool. The driver + * has stopped polling and this routine will execute as + * a result of the subsequent abort. + */ + mempool_free(queue1->context2, phba->iocb_mem_pool); + lpfc_free_scsi_buf(lpfc_cmd); + return; +} + +int +lpfc_sli_issue_iocb_wait_high_priority(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * piocb, + uint32_t flag, + struct lpfc_iocbq * prspiocbq) +{ + int wait_time = 0, retval = IOCB_ERROR; + + /* The caller must left context1 empty. */ + if (piocb->context_un.hipri_wait_queue != 0) { + return IOCB_ERROR; + } + + /* + * If the caller has provided a response iocbq buffer, context2 must + * be NULL or its an error. + */ + if (prspiocbq && piocb->context2) { + return IOCB_ERROR; + } + + piocb->context2 = prspiocbq; + + /* Setup callback routine and issue the command. */ + piocb->iocb_cmpl = lpfc_sli_wake_iocb_high_priority; + retval = lpfc_sli_issue_iocb(phba, pring, piocb, + flag | SLI_IOCB_HIGH_PRIORITY); + if (retval != IOCB_SUCCESS) { + piocb->context2 = NULL; + return IOCB_ERROR; + } + + /* + * This high-priority iocb was sent out-of-band. Poll for its + * completion rather than wait for a signal. Note that the host_lock + * is held by the midlayer and must be released here to allow the + * interrupt handlers to complete the IO and signal this routine via + * the iocb_flag. + * The driver waits a maximum of 600 seconds to give the FW ample time + * to complete the target reset ABTS. The race is not waiting long + * enough and then having the FW complete the request before the driver + * can issue the second abort. Since a solicited completion is required + * by the FW, this wait period should be enough time for the FW to + * complete the abts successfully or give up. + */ + + retval = IOCB_TIMEDOUT; + spin_unlock_irq(phba->host->host_lock); + while (wait_time <= 600000) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,6) + mdelay(100); +#else + msleep(100); +#endif + if (piocb->iocb_flag & LPFC_IO_HIPRI) { + piocb->iocb_flag &= ~LPFC_IO_HIPRI; + retval = IOCB_SUCCESS; + break; + } + wait_time += 100; + } + + spin_lock_irq(phba->host->host_lock); + + /* + * If the polling attempt failed to get a completion from the HBA, + * then substitute the initial completion function with one that + * releases the piocb back to the mempool. Failure to do this + * results in a memory leak. Also note the small timing race that + * exists between the driver giving up and a completion coming in. + */ + if ((retval == IOCB_TIMEDOUT) && !(piocb->iocb_flag & LPFC_IO_HIPRI)) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "%d:0327 waited %d mSecs for high priority " + "IOCB %p - giving up\n", + phba->brd_no, wait_time, piocb); + piocb->iocb_cmpl = lpfc_sli_wake_iocb_high_priority_cleanup; + } + + piocb->context2 = NULL; + + return retval; +} + +int +lpfc_sli_issue_mbox_wait(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq, + uint32_t timeout) +{ + DECLARE_WAIT_QUEUE_HEAD(done_q); + DECLARE_WAITQUEUE(wq_entry, current); + uint32_t timeleft = 0; + int retval; + + /* The caller must leave context1 empty. */ + if (pmboxq->context1 != 0) { + return (MBX_NOT_FINISHED); + } + + /* setup wake call as IOCB callback */ + pmboxq->mbox_cmpl = lpfc_sli_wake_mbox_wait; + /* setup context field to pass wait_queue pointer to wake function */ + pmboxq->context1 = &done_q; + + /* start to sleep before we wait, to avoid races */ + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&done_q, &wq_entry); + + /* now issue the command */ + spin_lock_irq(phba->host->host_lock); + retval = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT); + spin_unlock_irq(phba->host->host_lock); + + if (retval == MBX_BUSY || retval == MBX_SUCCESS) { + timeleft = schedule_timeout(timeout * HZ); + pmboxq->context1 = NULL; + /* if schedule_timeout returns 0, we timed out and were not + woken up */ + if (timeleft == 0) { + retval = MBX_TIMEOUT; + } else { + retval = MBX_SUCCESS; + } + } + + + set_current_state(TASK_RUNNING); + remove_wait_queue(&done_q, &wq_entry); + return retval; +} + +static void +lpfc_sli_wake_iocb_wait(struct lpfc_hba * phba, + struct lpfc_iocbq * queue1, struct lpfc_iocbq * queue2) +{ + wait_queue_head_t *pdone_q; + + queue1->iocb_flag |= LPFC_IO_WAIT; + if (queue1->context2 && queue2) + memcpy(queue1->context2, queue2, sizeof (struct lpfc_iocbq)); + + /* + * If pdone_q is empty, the waiter gave up and returned and this + * call has nothing to do. + */ + pdone_q = queue1->context_un.hipri_wait_queue; + if (pdone_q) { + wake_up(pdone_q); + } + + return; +} + +int +lpfc_sli_issue_iocb_wait(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * piocb, + struct lpfc_iocbq * prspiocbq, uint32_t timeout) +{ + DECLARE_WAIT_QUEUE_HEAD(done_q); + DECLARE_WAITQUEUE(wq_entry, current); + uint32_t timeleft = 0; + int retval; + + /* The caller must leave context1 empty for the driver. */ + if (piocb->context_un.hipri_wait_queue != 0) + return (IOCB_ERROR); + + /* If the caller has provided a response iocbq buffer, then context2 + * is NULL or its an error. + */ + if (prspiocbq) { + if (piocb->context2) + return (IOCB_ERROR); + piocb->context2 = prspiocbq; + } + + /* setup wake call as IOCB callback */ + piocb->iocb_cmpl = lpfc_sli_wake_iocb_wait; + /* setup context field to pass wait_queue pointer to wake function */ + piocb->context_un.hipri_wait_queue = &done_q; + + /* start to sleep before we wait, to avoid races */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&done_q, &wq_entry); + + /* now issue the command */ + retval = lpfc_sli_issue_iocb(phba, pring, piocb, 0); + if (retval == IOCB_SUCCESS) { + /* Give up thread time and wait for the iocb to complete or for + * the alloted time to expire. + */ + spin_unlock_irq(phba->host->host_lock); + timeleft = schedule_timeout(timeout * HZ); + spin_lock_irq(phba->host->host_lock); + + piocb->context_un.hipri_wait_queue = NULL; + piocb->iocb_cmpl = NULL; + if (piocb->context2 == prspiocbq) + piocb->context2 = NULL; + + /* + * Catch the error cases. A timeleft of zero is an error since + * the iocb should have completed. The iocb_flag not have value + * LPFC_IO_WAIT is also an error since the wakeup callback sets + * this flag when it runs. Handle each. + */ + if (!(piocb->iocb_flag & LPFC_IO_WAIT)) { + printk(KERN_ERR "%s: Timeleft is %d, iocb_flags is 0x%x ring_no %d ulpCommand 0x%x`\n ", + __FUNCTION__, timeleft, piocb->iocb_flag, + pring->ringno, piocb->iocb.ulpCommand); + retval = IOCB_TIMEDOUT; + } + } + + remove_wait_queue(&done_q, &wq_entry); + set_current_state(TASK_RUNNING); + piocb->context2 = NULL; + return retval; +} + +irqreturn_t +lpfc_intr_handler(int irq, void *dev_id, struct pt_regs * regs) +{ + struct lpfc_hba *phba; + int intr_status; + + /* + * Get the driver's phba structure from the dev_id and + * assume the HBA is not interrupting. + */ + phba = (struct lpfc_hba *) dev_id; + + if (phba) { + /* Call SLI to handle the interrupt event. */ + intr_status = lpfc_sli_intr(phba); + if (intr_status == 0) + return IRQ_HANDLED; + } + + return IRQ_NONE; + +} /* lpfc_intr_handler */ --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_disc.h 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_disc.h 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,278 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_disc.h 1.51.1.2 2005/06/13 17:16:12EDT sf_support Exp $ + */ + +#ifndef _H_LPFC_DISC +#define _H_LPFC_DISC + +#include "lpfc_hw.h" + +struct lpfc_target; + +#define FC_MAX_HOLD_RSCN 32 /* max number of deferred RSCNs */ +#define FC_MAX_NS_RSP 65536 /* max size NameServer rsp */ +#define FC_MAXLOOP 126 /* max devices supported on a fc loop */ +#define LPFC_DISC_FLOGI_TMO 10 /* Discovery FLOGI ratov */ + +/* Defines for failMask bitmask + * These are reasons that the device is not currently available + * for I/O to be sent. + */ +#define LPFC_DEV_LINK_DOWN 0x1 /* Link is down */ +#define LPFC_DEV_DISAPPEARED 0x2 /* Device disappeared from mapped + list */ +#define LPFC_DEV_DISCOVERY_INP 0x4 /* Device to go through discovery */ +#define LPFC_DEV_DISCONNECTED 0x8 /* noactive connection to remote dev */ + +/* These defines are used for set failMask routines */ +#define LPFC_SET_BITMASK 1 +#define LPFC_CLR_BITMASK 2 + +/* Provide an enumeration for the Types of addresses a FARP can resolve. */ +typedef enum lpfc_farp_addr_type { + LPFC_FARP_BY_IEEE, + LPFC_FARP_BY_WWPN, + LPFC_FARP_BY_WWNN, +} LPFC_FARP_ADDR_TYPE; + +/* This is the protocol dependent definition for a Node List Entry. + * This is used by Fibre Channel protocol to support FCP. + */ + +struct lpfc_bindlist { + struct list_head nlp_listp; + struct lpfc_target *nlp_Target; /* ptr to the tgt structure */ + struct lpfc_name nlp_portname; /* port name */ + struct lpfc_name nlp_nodename; /* node name */ + uint16_t nlp_bind_type; + uint16_t nlp_sid; /* scsi id */ + uint32_t nlp_DID; /* FibreChannel D_ID of entry */ +}; + +/* structure used to queue event to the discovery tasklet */ +struct lpfc_disc_evt { + struct list_head evt_listp; + void * evt_arg1; + void * evt_arg2; + uint32_t evt; +}; +typedef struct lpfc_disc_evt LPFC_DISC_EVT_t; + +#define LPFC_EVT_MBOX 0x1 +#define LPFC_EVT_SOL_IOCB 0x2 +#define LPFC_EVT_UNSOL_IOCB 0x3 +#define LPFC_EVT_NODEV_TMO 0x4 +#define LPFC_EVT_SCAN 0x5 +#define LPFC_EVT_ERR_ATTN 0x6 +#define LPFC_EVT_ELS_RETRY 0x7 +#define LPFC_EVT_OPEN_LOOP 0x8 + +struct lpfc_nodelist { + struct list_head nlp_listp; + struct lpfc_name nlp_portname; /* port name */ + struct lpfc_name nlp_nodename; /* node name */ + uint32_t nlp_failMask; /* failure mask for device */ + uint32_t nlp_flag; /* entry flags */ + uint32_t nlp_DID; /* FC D_ID of entry */ + uint32_t nlp_last_elscmd; /* Last ELS cmd sent */ + uint16_t nlp_type; +#define NLP_FC_NODE 0x1 /* entry is an FC node */ +#define NLP_FABRIC 0x4 /* entry rep a Fabric entity */ +#define NLP_FCP_TARGET 0x8 /* entry is an FCP target */ + + uint16_t nlp_rpi; + uint16_t nlp_state; /* state transition indicator */ + uint16_t nlp_xri; /* output exchange id for RPI */ + uint16_t nlp_sid; /* scsi id */ +#define NLP_NO_SID 0xffff + + uint8_t nlp_retry; /* used for ELS retries */ + uint8_t nlp_disc_refcnt; /* used for DSM */ + uint8_t nlp_fcp_info; /* class info, bits 0-3 */ +#define NLP_FCP_2_DEVICE 0x10 /* FCP-2 device */ + + struct timer_list nlp_delayfunc; /* Used for delayed ELS cmds */ + struct timer_list nlp_tmofunc; /* Used for nodev tmo */ + struct lpfc_target *nlp_Target; /* Pointer to the target + structure */ + + struct lpfc_bindlist *nlp_listp_bind; /* Linked list bounded remote + ports */ + struct lpfc_nodelist *nlp_rpi_hash_next; + struct lpfc_hba *nlp_phba; + LPFC_DISC_EVT_t nodev_timeout_evt; + LPFC_DISC_EVT_t els_retry_evt; +}; + +/*++ + * lpfc_node_farp_list: + * This data structure defines the attributes associated with + * an outstanding FARP REQ to a remote node. + * + * listentry - head of this list of pending farp requests. + * rnode_addr - The address of the remote node. Either the IEEE, WWPN, or + * WWNN. Used in the FARP request. + * + --*/ +struct lpfc_node_farp_pend { + struct list_head listentry; + struct lpfc_name rnode_addr; +}; + +/* Defines for nlp_flag (uint32) */ +#define NLP_NO_LIST 0x0 /* Indicates immediately free node */ +#define NLP_UNUSED_LIST 0x1 /* Flg to indicate node will be freed */ +#define NLP_PLOGI_LIST 0x2 /* Flg to indicate sent PLOGI */ +#define NLP_ADISC_LIST 0x3 /* Flg to indicate sent ADISC */ +#define NLP_REGLOGIN_LIST 0x4 /* Flg to indicate sent REG_LOGIN */ +#define NLP_PRLI_LIST 0x5 /* Flg to indicate sent PRLI */ +#define NLP_UNMAPPED_LIST 0x6 /* Node is now unmapped */ +#define NLP_MAPPED_LIST 0x7 /* Node is now mapped */ +#define NLP_NPR_LIST 0x8 /* Node is in NPort Recovery state */ +#define NLP_JUST_DQ 0x9 /* just deque ndlp in lpfc_nlp_list */ +#define NLP_LIST_MASK 0xf /* mask to see what list node is on */ +#define NLP_PLOGI_SND 0x20 /* sent PLOGI request for this entry */ +#define NLP_PRLI_SND 0x40 /* sent PRLI request for this entry */ +#define NLP_ADISC_SND 0x80 /* sent ADISC request for this entry */ +#define NLP_LOGO_SND 0x100 /* sent LOGO request for this entry */ +#define NLP_RNID_SND 0x400 /* sent RNID request for this entry */ +#define NLP_ELS_SND_MASK 0x7e0 /* sent ELS request for this entry */ +#define NLP_AUTOMAP 0x800 /* Entry was automap'ed */ +#define NLP_SEED_WWPN 0x1000 /* Entry scsi id is seeded for WWPN */ +#define NLP_SEED_WWNN 0x2000 /* Entry scsi id is seeded for WWNN */ +#define NLP_SEED_DID 0x4000 /* Entry scsi id is seeded for DID */ +#define NLP_SEED_MASK 0x807000 /* mask for seeded flags */ +#define NLP_NS_NODE 0x8000 /* Authenticated entry by NameServer */ +#define NLP_NODEV_TMO 0x10000 /* nodev timeout is running for node */ +#define NLP_DELAY_TMO 0x20000 /* delay timeout is running for node */ +#define NLP_NPR_2B_DISC 0x40000 /* node is included in num_disc_nodes */ +#define NLP_RCV_PLOGI 0x80000 /* Rcv'ed PLOGI from remote system */ +#define NLP_LOGO_ACC 0x100000 /* Process LOGO after ACC completes */ +#define NLP_TGT_NO_SCSIID 0x200000 /* good PRLI but no binding for scsid */ +#define NLP_SEED_ALPA 0x800000 /* SCSI id is derived from alpa array */ +#define NLP_ACC_REGLOGIN 0x1000000 /* Issue Reg Login after successful + ACC */ +#define NLP_NPR_ADISC 0x2000000 /* Issue ADISC when dq'ed from + NPR list */ +#define NLP_DELAY_REMOVE 0x4000000 /* Defer removal till end of DSM */ + +/* Defines for list searchs */ +#define NLP_SEARCH_MAPPED 0x1 /* search mapped */ +#define NLP_SEARCH_UNMAPPED 0x2 /* search unmapped */ +#define NLP_SEARCH_PLOGI 0x4 /* search plogi */ +#define NLP_SEARCH_ADISC 0x8 /* search adisc */ +#define NLP_SEARCH_REGLOGIN 0x10 /* search reglogin */ +#define NLP_SEARCH_PRLI 0x20 /* search prli */ +#define NLP_SEARCH_NPR 0x40 /* search npr */ +#define NLP_SEARCH_UNUSED 0x80 /* search mapped */ +#define NLP_SEARCH_ALL 0xff /* search all lists */ + +/* There are 4 different double linked lists nodelist entries can reside on. + * The Port Login (PLOGI) list and Address Discovery (ADISC) list are used + * when Link Up discovery or Registered State Change Notification (RSCN) + * processing is needed. Each list holds the nodes that require a PLOGI or + * ADISC Extended Link Service (ELS) request. These lists keep track of the + * nodes affected by an RSCN, or a Link Up (Typically, all nodes are effected + * by Link Up) event. The unmapped_list contains all nodes that have + * successfully logged into at the Fibre Channel level. The + * mapped_list will contain all nodes that are mapped FCP targets. + * + * The bind list is a list of undiscovered (potentially non-existent) nodes + * that we have saved binding information on. This information is used when + * nodes transition from the unmapped to the mapped list. + */ + +/* Defines for nlp_state */ +#define NLP_STE_UNUSED_NODE 0x0 /* node is just allocated */ +#define NLP_STE_PLOGI_ISSUE 0x1 /* PLOGI was sent to NL_PORT */ +#define NLP_STE_ADISC_ISSUE 0x2 /* ADISC was sent to NL_PORT */ +#define NLP_STE_REG_LOGIN_ISSUE 0x3 /* REG_LOGIN was issued for NL_PORT */ +#define NLP_STE_PRLI_ISSUE 0x4 /* PRLI was sent to NL_PORT */ +#define NLP_STE_UNMAPPED_NODE 0x5 /* PRLI completed from NL_PORT */ +#define NLP_STE_MAPPED_NODE 0x6 /* Identified as a FCP Target */ +#define NLP_STE_NPR_NODE 0x7 /* NPort disappeared */ +#define NLP_STE_MAX_STATE 0x8 +#define NLP_STE_FREED_NODE 0xff /* node entry was freed to MEM_NLP */ + +/* For UNUSED_NODE state, the node has just been allocated. + * For PLOGI_ISSUE and REG_LOGIN_ISSUE, the node is on + * the PLOGI list. For REG_LOGIN_COMPL, the node is taken off the PLOGI list + * and put on the unmapped list. For ADISC processing, the node is taken off + * the ADISC list and placed on either the mapped or unmapped list (depending + * on its previous state). Once on the unmapped list, a PRLI is issued and the + * state changed to PRLI_ISSUE. When the PRLI completion occurs, the state is + * changed to PRLI_COMPL. If the completion indicates a mapped + * node, the node is taken off the unmapped list. The binding list is checked + * for a valid binding, or a binding is automatically assigned. If binding + * assignment is unsuccessful, the node is left on the unmapped list. If + * binding assignment is successful, the associated binding list entry (if + * any) is removed, and the node is placed on the mapped list. + */ +/* + * For a Link Down, all nodes on the ADISC, PLOGI, unmapped or mapped + * lists will receive a DEVICE_RECOVERY event. If the linkdown or nodev timers + * expire, all effected nodes will receive a DEVICE_RM event. + */ +/* + * For a Link Up or RSCN, all nodes will move from the mapped / unmapped lists + * to either the ADISC or PLOGI list. After a Nameserver query or ALPA loopmap + * check, additional nodes may be added (DEVICE_ADD) or removed (DEVICE_RM) to / + * from the PLOGI or ADISC lists. Once the PLOGI and ADISC lists are populated, + * we will first process the ADISC list. 32 entries are processed initially and + * ADISC is initited for each one. Completions / Events for each node are + * funnelled thru the state machine. As each node finishes ADISC processing, it + * starts ADISC for any nodes waiting for ADISC processing. If no nodes are + * waiting, and the ADISC list count is identically 0, then we are done. For + * Link Up discovery, since all nodes on the PLOGI list are UNREG_LOGIN'ed, we + * can issue a CLEAR_LA and reenable Link Events. Next we will process the PLOGI + * list. 32 entries are processed initially and PLOGI is initited for each one. + * Completions / Events for each node are funnelled thru the state machine. As + * each node finishes PLOGI processing, it starts PLOGI for any nodes waiting + * for PLOGI processing. If no nodes are waiting, and the PLOGI list count is + * identically 0, then we are done. We have now completed discovery / RSCN + * handling. Upon completion, ALL nodes should be on either the mapped or + * unmapped lists. + */ + +/* Defines for Node List Entry Events that could happen */ +#define NLP_EVT_RCV_PLOGI 0x0 /* Rcv'd an ELS PLOGI command */ +#define NLP_EVT_RCV_PRLI 0x1 /* Rcv'd an ELS PRLI command */ +#define NLP_EVT_RCV_LOGO 0x2 /* Rcv'd an ELS LOGO command */ +#define NLP_EVT_RCV_ADISC 0x3 /* Rcv'd an ELS ADISC command */ +#define NLP_EVT_RCV_PDISC 0x4 /* Rcv'd an ELS PDISC command */ +#define NLP_EVT_RCV_PRLO 0x5 /* Rcv'd an ELS PRLO command */ +#define NLP_EVT_CMPL_PLOGI 0x6 /* Sent an ELS PLOGI command */ +#define NLP_EVT_CMPL_PRLI 0x7 /* Sent an ELS PRLI command */ +#define NLP_EVT_CMPL_LOGO 0x8 /* Sent an ELS LOGO command */ +#define NLP_EVT_CMPL_ADISC 0x9 /* Sent an ELS ADISC command */ +#define NLP_EVT_CMPL_REG_LOGIN 0xa /* REG_LOGIN mbox cmd completed */ +#define NLP_EVT_DEVICE_RM 0xb /* Device not found in NS / ALPAmap */ +#define NLP_EVT_DEVICE_RECOVERY 0xc /* Device existence unknown */ +#define NLP_EVT_MAX_EVENT 0xd + + +/* Definitions for Binding Entry Type for lpfc_parse_binding_entry() */ +#define LPFC_BIND_WW_NN_PN 0 +#define LPFC_BIND_DID 1 + +#endif /* _H_LPFC_DISC */ --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_scsi.h 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_scsi.h 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,93 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_scsi.h 1.71.1.3 2005/06/21 15:48:51EDT sf_support Exp $ + */ + +#ifndef _H_LPFC_SCSI +#define _H_LPFC_SCSI + +#include "lpfc_disc.h" +#include "lpfc_mem.h" +#include "lpfc_sli.h" + +struct lpfc_hba; + + +struct lpfc_target { + struct lpfc_nodelist *pnode; /* Pointer to the node structure. */ + uint16_t scsi_id; + uint32_t qcmdcnt; + uint32_t iodonecnt; + uint32_t errorcnt; + uint32_t slavecnt; +#if defined(RHEL_FC) || defined(SLES_FC) + uint16_t blocked; +#endif +#ifdef RHEL_FC + struct scsi_target *starget; /* Pointer to midlayer target + structure. */ +#endif +#ifdef SLES_FC + struct timer_list dev_loss_timer; +#endif +}; + +struct lpfc_scsi_buf { + struct scsi_cmnd *pCmd; + struct lpfc_hba *scsi_hba; + struct lpfc_target *target; + uint32_t lun; + + uint32_t timeout; + + uint16_t status; /* From IOCB Word 7- ulpStatus */ + uint32_t result; /* From IOCB Word 4. */ + + uint32_t seg_cnt; /* Number of scatter-gather segments returned by + * dma_map_sg. The driver needs this for calls + * to dma_unmap_sg. */ + dma_addr_t nonsg_phys; /* Non scatter-gather physical address. */ + + /* dma_ext has both virt, phys to dma-able buffer + * which contains fcp_cmd, fcp_rsp and scatter gather list fro upto + * 68 (LPFC_SCSI_BPL_SIZE) BDE entries, + * xfer length, cdb, data direction.... + */ + struct lpfc_dmabuf dma_ext; + struct fcp_cmnd *fcp_cmnd; + struct fcp_rsp *fcp_rsp; + struct ulp_bde64 *fcp_bpl; + + /* cur_iocbq has phys of the dma-able buffer. + * Iotag is in here + */ + struct lpfc_iocbq cur_iocbq; +}; + +#define LPFC_SCSI_INITIAL_BPL_SIZE 4 /* Number of scsi buf BDEs in fcp_bpl */ + +#define LPFC_SCSI_DMA_EXT_SIZE 264 +#define LPFC_BPL_SIZE 1024 + +#define MDAC_DIRECT_CMD 0x22 + +#endif /* _H_LPFC_SCSI */ --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_fcp.h 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_fcp.h 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,108 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_fcp.h 1.10.1.2 2005/06/13 17:16:19EDT sf_support Exp $ + */ + +#ifndef H_LPFC_DFC +#define H_LPFC_DFC + +#define MAX_LPFC_SNS 128 + +struct fcp_rsp { + uint32_t rspRsvd1; /* FC Word 0, byte 0:3 */ + uint32_t rspRsvd2; /* FC Word 1, byte 0:3 */ + + uint8_t rspStatus0; /* FCP_STATUS byte 0 (reserved) */ + uint8_t rspStatus1; /* FCP_STATUS byte 1 (reserved) */ + uint8_t rspStatus2; /* FCP_STATUS byte 2 field validity */ +#define RSP_LEN_VALID 0x01 /* bit 0 */ +#define SNS_LEN_VALID 0x02 /* bit 1 */ +#define RESID_OVER 0x04 /* bit 2 */ +#define RESID_UNDER 0x08 /* bit 3 */ + uint8_t rspStatus3; /* FCP_STATUS byte 3 SCSI status byte */ + + uint32_t rspResId; /* Residual xfer if residual count field set in + fcpStatus2 */ + /* Received in Big Endian format */ + uint32_t rspSnsLen; /* Length of sense data in fcpSnsInfo */ + /* Received in Big Endian format */ + uint32_t rspRspLen; /* Length of FCP response data in fcpRspInfo */ + /* Received in Big Endian format */ + + uint8_t rspInfo0; /* FCP_RSP_INFO byte 0 (reserved) */ + uint8_t rspInfo1; /* FCP_RSP_INFO byte 1 (reserved) */ + uint8_t rspInfo2; /* FCP_RSP_INFO byte 2 (reserved) */ + uint8_t rspInfo3; /* FCP_RSP_INFO RSP_CODE byte 3 */ + +#define RSP_NO_FAILURE 0x00 +#define RSP_DATA_BURST_ERR 0x01 +#define RSP_CMD_FIELD_ERR 0x02 +#define RSP_RO_MISMATCH_ERR 0x03 +#define RSP_TM_NOT_SUPPORTED 0x04 /* Task mgmt function not supported */ +#define RSP_TM_NOT_COMPLETED 0x05 /* Task mgmt function not performed */ + + uint32_t rspInfoRsvd; /* FCP_RSP_INFO bytes 4-7 (reserved) */ + + uint8_t rspSnsInfo[MAX_LPFC_SNS]; +#define SNS_ILLEGAL_REQ 0x05 /* sense key is byte 3 ([2]) */ +#define SNSCOD_BADCMD 0x20 /* sense code is byte 13 ([12]) */ +}; + +struct fcp_cmnd { + uint32_t fcpLunMsl; /* most significant lun word (32 bits) */ + uint32_t fcpLunLsl; /* least significant lun word (32 bits) */ + /* # of bits to shift lun id to end up in right + * payload word, little endian = 8, big = 16. + */ +#if __BIG_ENDIAN +#define FC_LUN_SHIFT 16 +#define FC_ADDR_MODE_SHIFT 24 +#else /* __LITTLE_ENDIAN */ +#define FC_LUN_SHIFT 8 +#define FC_ADDR_MODE_SHIFT 0 +#endif + + uint8_t fcpCntl0; /* FCP_CNTL byte 0 (reserved) */ + uint8_t fcpCntl1; /* FCP_CNTL byte 1 task codes */ +#define SIMPLE_Q 0x00 +#define HEAD_OF_Q 0x01 +#define ORDERED_Q 0x02 +#define ACA_Q 0x04 +#define UNTAGGED 0x05 + uint8_t fcpCntl2; /* FCP_CTL byte 2 task management codes */ +#define FCP_ABORT_TASK_SET 0x02 /* Bit 1 */ +#define FCP_CLEAR_TASK_SET 0x04 /* bit 2 */ +#define FCP_BUS_RESET 0x08 /* bit 3 */ +#define FCP_LUN_RESET 0x10 /* bit 4 */ +#define FCP_TARGET_RESET 0x20 /* bit 5 */ +#define FCP_CLEAR_ACA 0x40 /* bit 6 */ +#define FCP_TERMINATE_TASK 0x80 /* bit 7 */ + uint8_t fcpCntl3; +#define WRITE_DATA 0x01 /* Bit 0 */ +#define READ_DATA 0x02 /* Bit 1 */ + + uint8_t fcpCdb[16]; /* SRB cdb field is copied here */ + uint32_t fcpDl; /* Total transfer length */ + +}; + +#endif --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_crtn.h 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_crtn.h 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,273 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_crtn.h 1.149.1.4 2005/07/13 17:04:12EDT sf_support Exp $ + */ + +#ifndef _H_LPFC_CRTN +#define _H_LPFC_CRTN + +#include +#include +#include + +#include "lpfc_disc.h" +#include "lpfc_logmsg.h" +#include "lpfc_scsi.h" +#include "lpfc_sli.h" + + +void lpfc_dump_mem(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t); +void lpfc_read_nv(struct lpfc_hba *, LPFC_MBOXQ_t *); +int lpfc_read_la(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_clear_la(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_config_link(struct lpfc_hba *, LPFC_MBOXQ_t *); +int lpfc_read_sparam(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_read_config(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_set_slim(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t); +int lpfc_reg_login(struct lpfc_hba *, uint32_t, uint8_t *, LPFC_MBOXQ_t *, + uint32_t); +void lpfc_unreg_login(struct lpfc_hba *, uint32_t, LPFC_MBOXQ_t *); +void lpfc_unreg_did(struct lpfc_hba *, uint32_t, LPFC_MBOXQ_t *); +void lpfc_init_link(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t); + + +int lpfc_linkdown(struct lpfc_hba *); +void lpfc_mbx_cmpl_read_la(struct lpfc_hba *, LPFC_MBOXQ_t *); + +void lpfc_mbx_cmpl_clear_la(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_mbx_cmpl_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *); +int lpfc_consistent_bind_save(struct lpfc_hba *, struct lpfc_bindlist *); +int lpfc_nlp_plogi(struct lpfc_hba *, struct lpfc_nodelist *); +int lpfc_nlp_adisc(struct lpfc_hba *, struct lpfc_nodelist *); +int lpfc_nlp_unmapped(struct lpfc_hba *, struct lpfc_nodelist *); +int lpfc_nlp_mapped(struct lpfc_hba *, struct lpfc_nodelist *, + struct lpfc_bindlist *); +int lpfc_nlp_list(struct lpfc_hba *, struct lpfc_nodelist *, int); +void lpfc_set_disctmo(struct lpfc_hba *); +int lpfc_can_disctmo(struct lpfc_hba *); +int lpfc_unreg_rpi(struct lpfc_hba *, struct lpfc_nodelist *); +int lpfc_check_sli_ndlp(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_iocbq *, struct lpfc_nodelist *); +int lpfc_nlp_remove(struct lpfc_hba *, struct lpfc_nodelist *); +void lpfc_nlp_init(struct lpfc_hba *, struct lpfc_nodelist *, uint32_t); +struct lpfc_nodelist *lpfc_setup_disc_node(struct lpfc_hba *, uint32_t); +struct lpfc_nodelist *lpfc_setup_rscn_node(struct lpfc_hba *, uint32_t); +void lpfc_disc_list_loopmap(struct lpfc_hba *); +void lpfc_disc_start(struct lpfc_hba *); +void lpfc_disc_flush_list(struct lpfc_hba *); +void lpfc_establish_link_tmo(unsigned long); +void lpfc_disc_timeout(unsigned long); +void lpfc_scan_timeout(unsigned long); +struct lpfc_target *lpfc_find_target(struct lpfc_hba *, uint32_t, + struct lpfc_nodelist *); +void lpfc_set_failmask(struct lpfc_hba *, struct lpfc_nodelist *, uint32_t, + uint32_t); +void lpfc_process_nodev_timeout(struct lpfc_hba *, struct lpfc_nodelist *); + +struct lpfc_nodelist *lpfc_findnode_rpi(struct lpfc_hba * phba, uint16_t rpi); +struct lpfc_nodelist *lpfc_findnode_remove_rpi(struct lpfc_hba * phba, + uint16_t rpi); +void lpfc_addnode_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint16_t rpi); + +int lpfc_discq_post_event(struct lpfc_hba *, void *, void *, uint32_t); +int lpfc_do_dpc(void *); +void lpfc_evt_iocb_free(struct lpfc_hba *, struct lpfc_iocbq *); +int lpfc_disc_state_machine(struct lpfc_hba *, struct lpfc_nodelist *, void *, + uint32_t); + +uint32_t lpfc_cmpl_prli_reglogin_issue(struct lpfc_hba *, + struct lpfc_nodelist *, void *, + uint32_t); +uint32_t lpfc_cmpl_plogi_prli_issue(struct lpfc_hba *, struct lpfc_nodelist *, + void *, uint32_t); + +int lpfc_check_sparm(struct lpfc_hba *, struct lpfc_nodelist *, + struct serv_parm *, uint32_t); +int lpfc_els_abort(struct lpfc_hba *, struct lpfc_nodelist * ndlp, + int); +int lpfc_els_abort_flogi(struct lpfc_hba *); +int lpfc_initial_flogi(struct lpfc_hba *); +void lpfc_more_plogi(struct lpfc_hba *); +int lpfc_issue_els_plogi(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t); +int lpfc_issue_els_prli(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t); +int lpfc_issue_els_adisc(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t); +int lpfc_issue_els_logo(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t); +int lpfc_issue_els_scr(struct lpfc_hba *, uint32_t, uint8_t); +int lpfc_els_free_iocb(struct lpfc_hba *, struct lpfc_iocbq *); +int lpfc_els_rsp_acc(struct lpfc_hba *, uint32_t, struct lpfc_iocbq *, + struct lpfc_nodelist *, LPFC_MBOXQ_t *, uint8_t); +int lpfc_els_rsp_reject(struct lpfc_hba *, uint32_t, struct lpfc_iocbq *, + struct lpfc_nodelist *); +int lpfc_els_rsp_adisc_acc(struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_nodelist *); +int lpfc_els_rsp_prli_acc(struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_nodelist *); +void lpfc_els_retry_delay(unsigned long); +void lpfc_els_retry_delay_handler(struct lpfc_nodelist *); +void lpfc_els_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_iocbq *); +int lpfc_els_handle_rscn(struct lpfc_hba *); +int lpfc_els_flush_rscn(struct lpfc_hba *); +int lpfc_rscn_payload_check(struct lpfc_hba *, uint32_t); +void lpfc_els_flush_cmd(struct lpfc_hba *); +int lpfc_els_disc_adisc(struct lpfc_hba *); +int lpfc_els_disc_plogi(struct lpfc_hba *); +void lpfc_els_timeout(unsigned long); +void lpfc_els_timeout_handler(struct lpfc_hba *); + +void lpfc_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_iocbq *); +int lpfc_ns_cmd(struct lpfc_hba *, struct lpfc_nodelist *, int); +int lpfc_fdmi_cmd(struct lpfc_hba *, struct lpfc_nodelist *, int); +void lpfc_fdmi_tmo(unsigned long); +void lpfc_fdmi_tmo_handler(struct lpfc_hba *); + +int lpfc_config_port_prep(struct lpfc_hba *); +int lpfc_config_port_post(struct lpfc_hba *); +int lpfc_hba_down_prep(struct lpfc_hba *); +void lpfc_handle_eratt(struct lpfc_hba *, uint32_t); +void lpfc_handle_latt(struct lpfc_hba *); +void lpfc_hba_init(struct lpfc_hba *, uint32_t *); +int lpfc_post_buffer(struct lpfc_hba *, struct lpfc_sli_ring *, int, int); +void lpfc_cleanup(struct lpfc_hba *, uint32_t); +int lpfc_scsi_free(struct lpfc_hba *); +void lpfc_decode_firmware_rev(struct lpfc_hba *, char *, int); +uint8_t *lpfc_get_lpfchba_info(struct lpfc_hba *, uint8_t *); +int lpfc_fcp_abort(struct lpfc_hba *, int, int, int); +int lpfc_put_event(struct lpfc_hba *, uint32_t, uint32_t, void *, + uint32_t, uint32_t); +int lpfc_online(struct lpfc_hba *); +int lpfc_offline(struct lpfc_hba *); + + + +int lpfc_sli_queue_setup(struct lpfc_hba *); +void lpfc_slim_access(struct lpfc_hba *); + +void lpfc_handle_eratt(struct lpfc_hba *, uint32_t); +void lpfc_handle_latt(struct lpfc_hba *); +irqreturn_t lpfc_intr_handler(int, void *, struct pt_regs *); + +void lpfc_read_rev(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_config_ring(struct lpfc_hba *, int, LPFC_MBOXQ_t *); +void lpfc_config_port(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_mbox_put(struct lpfc_hba *, LPFC_MBOXQ_t *); +LPFC_MBOXQ_t *lpfc_mbox_get(struct lpfc_hba *); + +int lpfc_mem_alloc(struct lpfc_hba *); +void lpfc_mem_free(struct lpfc_hba *); + +struct lpfc_iocbq * +lpfc_prep_els_iocb(struct lpfc_hba * phba, + uint8_t expectRsp, + uint16_t cmdSize, + uint8_t retry, struct lpfc_nodelist * ndlp, uint32_t elscmd); + +int lpfc_sli_hba_setup(struct lpfc_hba *); +int lpfc_sli_hba_down(struct lpfc_hba *); +int lpfc_sli_intr(struct lpfc_hba *); +int lpfc_sli_issue_mbox(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t); +void lpfc_sli_def_mbox_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *); +int lpfc_sli_issue_iocb(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_iocbq *, uint32_t); +void lpfc_sli_pcimem_bcopy(uint32_t *, uint32_t *, uint32_t); +int lpfc_sli_ringpostbuf_put(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_dmabuf *); +struct lpfc_dmabuf *lpfc_sli_ringpostbuf_get(struct lpfc_hba *, + struct lpfc_sli_ring *, + dma_addr_t); +uint32_t lpfc_sli_next_iotag(struct lpfc_hba *, struct lpfc_sli_ring *); +int lpfc_sli_issue_abort_iotag32(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_iocbq *); +int lpfc_sli_abort_iocb_ctx(struct lpfc_hba *, struct lpfc_sli_ring *, + uint32_t); +int lpfc_sli_sum_iocb_host(struct lpfc_hba *, struct lpfc_sli_ring *); +int lpfc_sli_abort_iocb_host(struct lpfc_hba *, struct lpfc_sli_ring *, int); +int lpfc_sli_sum_iocb_lun(struct lpfc_hba *, struct lpfc_sli_ring *, uint16_t, + uint64_t); +int lpfc_sli_abort_iocb_lun(struct lpfc_hba *, struct lpfc_sli_ring *, uint16_t, + uint64_t, int); +int lpfc_sli_abort_iocb_tgt(struct lpfc_hba *, struct lpfc_sli_ring *, + uint16_t, int); +void lpfc_mbox_timeout(unsigned long); +void lpfc_mbox_timeout_handler(struct lpfc_hba *); +void lpfc_map_fcp_cmnd_to_bpl(struct lpfc_hba *, struct lpfc_scsi_buf *); +void lpfc_free_scsi_cmd(struct lpfc_scsi_buf *); +uint32_t lpfc_os_timeout_transform(struct lpfc_hba *, uint32_t); + +struct lpfc_nodelist * +lpfc_findnode_wwpn(struct lpfc_hba * phba, uint32_t order, + struct lpfc_name * wwpn); +struct lpfc_nodelist * +lpfc_findnode_wwnn(struct lpfc_hba * phba, uint32_t order, + struct lpfc_name * wwnn); +struct lpfc_nodelist *lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order, + uint32_t did); + +int lpfc_sli_issue_mbox_wait(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq, + uint32_t timeout); + +int +lpfc_sli_issue_iocb_wait(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * piocb, + struct lpfc_iocbq * prspiocbq, uint32_t timeout); +int lpfc_sli_issue_iocb_wait_high_priority(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * piocb, + uint32_t flag, + struct lpfc_iocbq * prspiocbq); +void lpfc_sli_wake_iocb_high_priority(struct lpfc_hba * phba, + struct lpfc_iocbq * queue1, + struct lpfc_iocbq * queue2); +void lpfc_sli_abort_fcp_cmpl(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb); +void *lpfc_mbuf_alloc(struct lpfc_hba *, int, dma_addr_t *); +void lpfc_mbuf_free(struct lpfc_hba *, void *, dma_addr_t); + +int lpfc_stop_timer(struct lpfc_hba *); + + +/* Function prototypes. */ +int lpfc_queuecommand(struct scsi_cmnd *, void (*done) (struct scsi_cmnd *)); +int lpfc_abort_handler(struct scsi_cmnd *); +int lpfc_reset_bus_handler(struct scsi_cmnd *); +int lpfc_reset_lun_handler(struct scsi_cmnd *); +void lpfc_free_scsi_buf(struct lpfc_scsi_buf *); + +#if defined(RHEL_FC) || defined(SLES_FC) +void lpfc_target_unblock(struct lpfc_hba *, struct lpfc_target *); +void lpfc_target_block(struct lpfc_hba *, struct lpfc_target *); +int lpfc_target_remove(struct lpfc_hba *, struct lpfc_target *); +int lpfc_target_add(struct lpfc_hba *, struct lpfc_target *); +#endif + +#define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code) +#define HBA_EVENT_RSCN 5 +#define HBA_EVENT_LINK_UP 2 +#define HBA_EVENT_LINK_DOWN 3 +#endif /* _H_LPFC_CRTN */ --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_els.c 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_els.c 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,3152 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_els.c 1.165.2.3 2005/07/08 19:33:28EDT sf_support Exp $ + */ +#include +#include +#include +#include +#include +#include +#include +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" + + +static int lpfc_els_retry(struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *); +static int lpfc_max_els_tries = 3; + +static int +lpfc_els_chk_latt(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *mbox; + uint32_t ha_copy; + + psli = &phba->sli; + + if ((phba->hba_state < LPFC_HBA_READY) && + (phba->hba_state != LPFC_LINK_DOWN)) { + + /* Read the HBA Host Attention Register */ + ha_copy = readl(phba->HAregaddr); + + if (ha_copy & HA_LATT) { /* Link Attention interrupt */ + + /* Pending Link Event during Discovery */ + lpfc_printf_log(phba, KERN_WARNING, LOG_DISCOVERY, + "%d:0237 Pending Link Event during " + "Discovery: State x%x\n", + phba->brd_no, phba->hba_state); + + /* CLEAR_LA should re-enable link attention events and + * we should then imediately take a LATT event. The + * LATT processing should call lpfc_linkdown() which + * will cleanup any left over in-progress discovery + * events. + */ + phba->fc_flag |= FC_ABORT_DISCOVERY; + + if (phba->hba_state != LPFC_CLEAR_LA) { + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + phba->hba_state = LPFC_CLEAR_LA; + lpfc_clear_la(phba, mbox); + mbox->mbox_cmpl = + lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox + (phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, + phba->mbox_mem_pool); + phba->hba_state = + LPFC_HBA_ERROR; + } + } + } + return (1); + } + } + + return (0); +} + +struct lpfc_iocbq * +lpfc_prep_els_iocb(struct lpfc_hba * phba, + uint8_t expectRsp, + uint16_t cmdSize, + uint8_t retry, struct lpfc_nodelist * ndlp, uint32_t elscmd) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *elsiocb; + struct lpfc_dmabuf *pcmd, *prsp, *pbuflist; + struct ulp_bde64 *bpl; + IOCB_t *icmd; + uint32_t tag; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + if (phba->hba_state < LPFC_LINK_UP) + return NULL; + + + /* Allocate buffer for command iocb */ + elsiocb = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC); + if (!elsiocb) + return NULL; + + memset(elsiocb, 0, sizeof (struct lpfc_iocbq)); + icmd = &elsiocb->iocb; + + /* fill in BDEs for command */ + /* Allocate buffer for command payload */ + if (((pcmd = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC)) == 0) || + ((pcmd->virt = lpfc_mbuf_alloc(phba, + MEM_PRI, &(pcmd->phys))) == 0)) { + if (pcmd) + kfree(pcmd); + mempool_free( elsiocb, phba->iocb_mem_pool); + return NULL; + } + + INIT_LIST_HEAD(&pcmd->list); + + /* Allocate buffer for response payload */ + if (expectRsp) { + prsp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (prsp) + prsp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, + &prsp->phys); + if (prsp == 0 || prsp->virt == 0) { + if (prsp) + kfree(prsp); + lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys); + kfree(pcmd); + mempool_free( elsiocb, phba->iocb_mem_pool); + return NULL; + } + INIT_LIST_HEAD(&prsp->list); + } else { + prsp = NULL; + } + + /* Allocate buffer for Buffer ptr list */ + pbuflist = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (pbuflist) + pbuflist->virt = lpfc_mbuf_alloc(phba, MEM_PRI, + &pbuflist->phys); + if (pbuflist == 0 || pbuflist->virt == 0) { + mempool_free( elsiocb, phba->iocb_mem_pool); + lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys); + lpfc_mbuf_free(phba, prsp->virt, prsp->phys); + kfree(pcmd); + kfree(prsp); + if (pbuflist) + kfree(pbuflist); + return NULL; + } + + INIT_LIST_HEAD(&pbuflist->list); + + icmd->un.elsreq64.bdl.addrHigh = putPaddrHigh(pbuflist->phys); + icmd->un.elsreq64.bdl.addrLow = putPaddrLow(pbuflist->phys); + icmd->un.elsreq64.bdl.bdeFlags = BUFF_TYPE_BDL; + if (expectRsp) { + icmd->un.elsreq64.bdl.bdeSize = (2 * sizeof (struct ulp_bde64)); + icmd->un.elsreq64.remoteID = ndlp->nlp_DID; /* DID */ + icmd->ulpCommand = CMD_ELS_REQUEST64_CR; + } else { + icmd->un.elsreq64.bdl.bdeSize = sizeof (struct ulp_bde64); + icmd->ulpCommand = CMD_XMIT_ELS_RSP64_CX; + } + + /* NOTE: we don't use ulpIoTag0 because it is a t2 structure */ + tag = lpfc_sli_next_iotag(phba, pring); + icmd->ulpIoTag = (uint16_t)(tag & 0xffff); + icmd->un.elsreq64.bdl.ulpIoTag32 = tag; + icmd->ulpBdeCount = 1; + icmd->ulpLe = 1; + icmd->ulpClass = CLASS3; + + bpl = (struct ulp_bde64 *) pbuflist->virt; + bpl->addrLow = le32_to_cpu(putPaddrLow(pcmd->phys)); + bpl->addrHigh = le32_to_cpu(putPaddrHigh(pcmd->phys)); + bpl->tus.f.bdeSize = cmdSize; + bpl->tus.f.bdeFlags = 0; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + + if (expectRsp) { + bpl++; + bpl->addrLow = le32_to_cpu(putPaddrLow(prsp->phys)); + bpl->addrHigh = le32_to_cpu(putPaddrHigh(prsp->phys)); + bpl->tus.f.bdeSize = FCELSSIZE; + bpl->tus.f.bdeFlags = BUFF_USE_RCV; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + } + + /* Save for completion so we can release these resources */ + elsiocb->context1 = (uint8_t *) ndlp; + elsiocb->context2 = (uint8_t *) pcmd; + elsiocb->context3 = (uint8_t *) pbuflist; + elsiocb->retry = retry; + elsiocb->drvrTimeout = (phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT; + + if (prsp) { + list_add(&prsp->list, &pcmd->list); + } + + if (expectRsp) { + /* Xmit ELS command to remote NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0116 Xmit ELS command x%x to remote " + "NPORT x%x Data: x%x x%x\n", + phba->brd_no, elscmd, + ndlp->nlp_DID, icmd->ulpIoTag, phba->hba_state); + } else { + /* Xmit ELS response to remote NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0117 Xmit ELS response x%x to remote " + "NPORT x%x Data: x%x x%x\n", + phba->brd_no, elscmd, + ndlp->nlp_DID, icmd->ulpIoTag, cmdSize); + } + + return (elsiocb); +} + +static void +lpfc_cmpl_els_flogi(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_dmabuf *pcmd, *prsp; + struct serv_parm *sp; + uint32_t *lp; + LPFC_MBOXQ_t *mbox; + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + int rc; + + psli = &phba->sli; + irsp = &(rspiocb->iocb); + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) { + lpfc_nlp_remove(phba, ndlp); + goto out; + } + + if (irsp->ulpStatus) { + /* FLOGI failure */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_ELS, + "%d:0100 FLOGI failure Data: x%x x%x\n", + phba->brd_no, + irsp->ulpStatus, irsp->un.ulpWord[4]); + + /* Check for retry */ + if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { + /* ELS command is being retried */ + goto out; + } + /* FLOGI failed, so there is no fabric */ + phba->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP); + + /* If private loop, then allow max outstandting els to be + * LPFC_MAX_DISC_THREADS (32). Scanning in the case of no + * alpa map would take too long otherwise. + */ + if (phba->alpa_map[0] == 0) { + phba->cfg_discovery_threads = + LPFC_MAX_DISC_THREADS; + } + + } else { + /* The FLogI succeeded. Sync the data for the CPU before + * accessing it. + */ + prsp = (struct lpfc_dmabuf *) pcmd->list.next; + lp = (uint32_t *) prsp->virt; + + sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t)); + + /* FLOGI completes successfully */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0101 FLOGI completes sucessfully " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + irsp->un.ulpWord[4], sp->cmn.e_d_tov, + sp->cmn.w2.r_a_tov, sp->cmn.edtovResolution); + + if (phba->hba_state == LPFC_FLOGI) { + /* If Common Service Parameters indicate Nport + * we are point to point, if Fport we are Fabric. + */ + if (sp->cmn.fPort) { + phba->fc_flag |= FC_FABRIC; + if (sp->cmn.edtovResolution) { + /* E_D_TOV ticks are in nanoseconds */ + phba->fc_edtov = + (be32_to_cpu(sp->cmn.e_d_tov) + + 999999) / 1000000; + } else { + /* E_D_TOV ticks are in milliseconds */ + phba->fc_edtov = + be32_to_cpu(sp->cmn.e_d_tov); + } + phba->fc_ratov = + (be32_to_cpu(sp->cmn.w2.r_a_tov) + + 999) / 1000; + + if (phba->fc_topology == TOPOLOGY_LOOP) { + phba->fc_flag |= FC_PUBLIC_LOOP; + } else { + /* If we are a N-port connected to a + * Fabric, fixup sparam's so logins to + * devices on remote loops work. + */ + phba->fc_sparam.cmn.altBbCredit = 1; + } + + phba->fc_myDID = irsp->un.ulpWord[4] & Mask_DID; + + memcpy(&ndlp->nlp_portname, &sp->portName, + sizeof (struct lpfc_name)); + memcpy(&ndlp->nlp_nodename, &sp->nodeName, + sizeof (struct lpfc_name)); + memcpy(&phba->fc_fabparam, sp, + sizeof (struct serv_parm)); + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + goto flogifail; + } + phba->hba_state = LPFC_FABRIC_CFG_LINK; + lpfc_config_link(phba, mbox); + mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, phba->mbox_mem_pool); + goto flogifail; + } + + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + goto flogifail; + } + if (lpfc_reg_login(phba, Fabric_DID, + (uint8_t *) sp, mbox, + 0) == 0) { + /* set_slim mailbox command needs to + * execute first, queue this command to + * be processed later. + */ + mbox->mbox_cmpl = + lpfc_mbx_cmpl_fabric_reg_login; + mbox->context2 = ndlp; + if (lpfc_sli_issue_mbox + (phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, + phba->mbox_mem_pool); + goto flogifail; + } + } else { + mempool_free(mbox, phba->mbox_mem_pool); + goto flogifail; + } + } else { + /* We FLOGIed into an NPort, initiate pt2pt + protocol */ + phba->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP); + phba->fc_edtov = FF_DEF_EDTOV; + phba->fc_ratov = FF_DEF_RATOV; + rc = memcmp(&phba->fc_portname, &sp->portName, + sizeof(struct lpfc_name)); + if (rc >= 0) { + /* This side will initiate the PLOGI */ + phba->fc_flag |= FC_PT2PT_PLOGI; + + /* N_Port ID cannot be 0, set our to + * LocalID the other side will be + * RemoteID. + */ + + /* not equal */ + if (rc) + phba->fc_myDID = PT2PT_LocalID; + + if ((mbox = + mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) + == 0) { + goto flogifail; + } + lpfc_config_link(phba, mbox); + mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl; + if (lpfc_sli_issue_mbox + (phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, + phba->mbox_mem_pool); + goto flogifail; + } + mempool_free( ndlp, phba->nlp_mem_pool); + + if ((ndlp = + lpfc_findnode_did(phba, + NLP_SEARCH_ALL, + PT2PT_RemoteID)) + == 0) { + /* Cannot find existing Fabric + ndlp, so allocate a new + one */ + if ((ndlp = + mempool_alloc( + phba->nlp_mem_pool, + GFP_ATOMIC)) == 0) { + goto flogifail; + } + lpfc_nlp_init(phba, ndlp, + PT2PT_RemoteID); + } + memcpy(&ndlp->nlp_portname, + &sp->portName, + sizeof (struct lpfc_name)); + memcpy(&ndlp->nlp_nodename, + &sp->nodeName, + sizeof (struct lpfc_name)); + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + } + else { + /* This side will wait for the PLOGI */ + mempool_free( ndlp, phba->nlp_mem_pool); + } + + phba->fc_flag |= FC_PT2PT; + + /* Start discovery - this should just do + CLEAR_LA */ + lpfc_disc_start(phba); + } + goto out; + } + } + +flogifail: + lpfc_nlp_remove(phba, ndlp); + + if((irsp->ulpStatus != IOSTAT_LOCAL_REJECT) || + ((irsp->un.ulpWord[4] != IOERR_SLI_ABORTED) && + (irsp->un.ulpWord[4] != IOERR_SLI_DOWN))) { + + /* FLOGI failed, so just use loop map to make discovery list */ + lpfc_disc_list_loopmap(phba); + + /* Start discovery */ + lpfc_disc_start(phba); + } + +out: + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +static int +lpfc_issue_els_flogi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint8_t retry) +{ + struct serv_parm *sp; + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + uint8_t *pcmd; + uint16_t cmdsize; + uint32_t tmo; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = (sizeof (uint32_t) + sizeof (struct serv_parm)); + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_FLOGI)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + /* For FLOGI request, remainder of payload is service parameters */ + *((uint32_t *) (pcmd)) = ELS_CMD_FLOGI; + pcmd += sizeof (uint32_t); + memcpy(pcmd, &phba->fc_sparam, sizeof (struct serv_parm)); + sp = (struct serv_parm *) pcmd; + + /* Setup CSPs accordingly for Fabric */ + sp->cmn.e_d_tov = 0; + sp->cmn.w2.r_a_tov = 0; + sp->cls1.classValid = 0; + sp->cls2.seqDelivery = 1; + sp->cls3.seqDelivery = 1; + if (sp->cmn.fcphLow < FC_PH3) + sp->cmn.fcphLow = FC_PH3; + if (sp->cmn.fcphHigh < FC_PH3) + sp->cmn.fcphHigh = FC_PH3; + + tmo = phba->fc_ratov; + phba->fc_ratov = LPFC_DISC_FLOGI_TMO; + lpfc_set_disctmo(phba); + phba->fc_ratov = tmo; + + phba->fc_stat.elsXmitFLOGI++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_flogi; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +int +lpfc_els_abort_flogi(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_nodelist *ndlp; + IOCB_t *icmd; + struct list_head *curr, *next; + + /* Abort outstanding I/O on NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0201 Abort outstanding I/O on NPort x%x\n", + phba->brd_no, Fabric_DID); + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + /* check the txcmplq */ + list_for_each_safe(curr, next, &pring->txcmplq) { + next_iocb = list_entry(curr, struct lpfc_iocbq, list); + iocb = next_iocb; + /* Check to see if iocb matches the nport we are + looking for */ + icmd = &iocb->iocb; + if (icmd->ulpCommand == CMD_ELS_REQUEST64_CR) { + ndlp = (struct lpfc_nodelist *)(iocb->context1); + if(ndlp && (ndlp->nlp_DID == Fabric_DID)) { + /* It matches, so deque and call compl + with an error */ + list_del(&iocb->list); + pring->txcmplq_cnt--; + + if ((icmd->un.elsreq64.bdl.ulpIoTag32)) { + lpfc_sli_issue_abort_iotag32 + (phba, pring, iocb); + } + if (iocb->iocb_cmpl) { + icmd->ulpStatus = + IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = + IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free(iocb, phba->iocb_mem_pool); + } + } + } + } + return (0); +} + +int +lpfc_initial_flogi(struct lpfc_hba * phba) +{ + struct lpfc_nodelist *ndlp; + + /* First look for Fabric ndlp on the unmapped list */ + + if ((ndlp = + lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED, + Fabric_DID)) == 0) { + /* Cannot find existing Fabric ndlp, so allocate a new one */ + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) + == 0) { + return (0); + } + lpfc_nlp_init(phba, ndlp, Fabric_DID); + } + else { + phba->fc_unmap_cnt--; + list_del(&ndlp->nlp_listp); + ndlp->nlp_flag &= ~NLP_LIST_MASK; + } + if (lpfc_issue_els_flogi(phba, ndlp, 0)) { + mempool_free( ndlp, phba->nlp_mem_pool); + } + return (1); +} + +void +lpfc_more_plogi(struct lpfc_hba * phba) +{ + int sentplogi; + + if (phba->num_disc_nodes) + phba->num_disc_nodes--; + + /* Continue discovery with PLOGIs to go */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0232 Continue discovery with %d PLOGIs to go " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->num_disc_nodes, phba->fc_plogi_cnt, + phba->fc_flag, phba->hba_state); + + /* Check to see if there are more PLOGIs to be sent */ + if (phba->fc_flag & FC_NLP_MORE) { + /* go thru NPR list and issue any remaining ELS PLOGIs */ + sentplogi = lpfc_els_disc_plogi(phba); + } + return; +} + +static void +lpfc_cmpl_els_plogi(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + int disc, rc, did, type; + struct lpfc_nodelist *curr_ndlp, *next_ndlp; + int valid_ndlp = 0; + + psli = &phba->sli; + + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + irsp = &rspiocb->iocb; + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + + list_for_each_entry_safe(curr_ndlp, next_ndlp, &phba->fc_plogi_list, + nlp_listp) { + if (curr_ndlp == ndlp ) { + valid_ndlp =1; + break; + } + } + if (!valid_ndlp) + goto out; + + ndlp->nlp_flag &= ~NLP_PLOGI_SND; + + /* Since ndlp can be freed in the disc state machine, note if this node + * is being used during discovery. + */ + disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); + rc = 0; + + /* PLOGI completes to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0102 PLOGI completes to NPort x%x " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus, + irsp->un.ulpWord[4], disc, phba->num_disc_nodes); + + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) { + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + goto out; + } + + /* ndlp could be freed in DSM, save these values now */ + type = ndlp->nlp_type; + did = ndlp->nlp_DID; + + if (irsp->ulpStatus) { + /* Check for retry */ + if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { + /* ELS command is being retried */ + if (disc) { + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + } + goto out; + } + + /* PLOGI failed */ + /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ + if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) || + (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) { + disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); + } + else { + rc = lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_PLOGI); + } + } else { + /* Good status, call state machine */ + rc = lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_PLOGI); + } + + if(type & NLP_FABRIC) { + /* If we cannot login to Nameserver, kick off discovery now */ + if ((did == NameServer_DID) && (rc == NLP_STE_FREED_NODE)) { + lpfc_disc_start(phba); + } + goto out; + } + + if (disc && phba->num_disc_nodes) { + /* Check to see if there are more PLOGIs to be sent */ + lpfc_more_plogi(phba); + } + + if (rc != NLP_STE_FREED_NODE) + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + + if (phba->num_disc_nodes == 0) { + if(disc) { + phba->fc_flag &= ~FC_NDISC_ACTIVE; + } + lpfc_can_disctmo(phba); + if (phba->fc_flag & FC_RSCN_MODE) { + /* Check to see if more RSCNs came in while we were + * processing this one. + */ + if ((phba->fc_rscn_id_cnt == 0) && + (!(phba->fc_flag & FC_RSCN_DISCOVERY))) { + phba->fc_flag &= ~FC_RSCN_MODE; + } else { + lpfc_els_handle_rscn(phba); + } + } + } + +out: + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_issue_els_plogi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint8_t retry) +{ + struct serv_parm *sp; + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = (sizeof (uint32_t) + sizeof (struct serv_parm)); + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_PLOGI)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + /* For PLOGI request, remainder of payload is service parameters */ + *((uint32_t *) (pcmd)) = ELS_CMD_PLOGI; + pcmd += sizeof (uint32_t); + memcpy(pcmd, &phba->fc_sparam, sizeof (struct serv_parm)); + sp = (struct serv_parm *) pcmd; + + if (sp->cmn.fcphLow < FC_PH_4_3) + sp->cmn.fcphLow = FC_PH_4_3; + + if (sp->cmn.fcphHigh < FC_PH3) + sp->cmn.fcphHigh = FC_PH3; + + phba->fc_stat.elsXmitPLOGI++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_plogi; + ndlp->nlp_flag |= NLP_PLOGI_SND; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + ndlp->nlp_flag &= ~NLP_PLOGI_SND; + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +static void +lpfc_cmpl_els_prli(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp, *curr_ndlp, *next_ndlp; + int valid_ndlp = 0; + + psli = &phba->sli; + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + irsp = &(rspiocb->iocb); + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + phba->fc_prli_sent--; + list_for_each_entry_safe(curr_ndlp, next_ndlp, &phba->fc_prli_list, + nlp_listp) { + if (curr_ndlp == ndlp ) { + valid_ndlp =1; + break; + } + } + + if (!valid_ndlp) + goto out; + + ndlp->nlp_flag &= ~NLP_PRLI_SND; + + /* PRLI completes to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0103 PRLI completes to NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus, + irsp->un.ulpWord[4], phba->num_disc_nodes); + + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) + goto out; + + if (irsp->ulpStatus) { + /* Check for retry */ + if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { + /* ELS command is being retried */ + goto out; + } + /* PRLI failed */ + /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ + if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) || + (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) { + goto out; + } + else { + lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_PRLI); + } + } else { + /* Good status, call state machine */ + lpfc_disc_state_machine(phba, ndlp, cmdiocb, NLP_EVT_CMPL_PRLI); + } + +out: + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_issue_els_prli(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint8_t retry) +{ + PRLI *npr; + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = (sizeof (uint32_t) + sizeof (PRLI)); + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_PRLI)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + /* For PRLI request, remainder of payload is service parameters */ + memset(pcmd, 0, (sizeof (PRLI) + sizeof (uint32_t))); + *((uint32_t *) (pcmd)) = ELS_CMD_PRLI; + pcmd += sizeof (uint32_t); + + /* For PRLI, remainder of payload is PRLI parameter page */ + npr = (PRLI *) pcmd; + /* + * If our firmware version is 3.20 or later, + * set the following bits for FC-TAPE support. + */ + if (phba->vpd.rev.feaLevelHigh >= 0x02) { + npr->ConfmComplAllowed = 1; + npr->Retry = 1; + npr->TaskRetryIdReq = 1; + } + npr->estabImagePair = 1; + npr->readXferRdyDis = 1; + + /* For FCP support */ + npr->prliType = PRLI_FCP_TYPE; + npr->initiatorFunc = 1; + + phba->fc_stat.elsXmitPRLI++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_prli; + ndlp->nlp_flag |= NLP_PRLI_SND; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + ndlp->nlp_flag &= ~NLP_PRLI_SND; + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + phba->fc_prli_sent++; + return (0); +} + +static void +lpfc_more_adisc(struct lpfc_hba * phba) +{ + int sentadisc; + + if (phba->num_disc_nodes) + phba->num_disc_nodes--; + + /* Continue discovery with ADISCs to go */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0210 Continue discovery with %d ADISCs to go " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->num_disc_nodes, phba->fc_adisc_cnt, + phba->fc_flag, phba->hba_state); + + /* Check to see if there are more ADISCs to be sent */ + if (phba->fc_flag & FC_NLP_MORE) { + lpfc_set_disctmo(phba); + + /* go thru NPR list and issue any remaining ELS ADISCs */ + sentadisc = lpfc_els_disc_adisc(phba); + } + return; +} + +static void +lpfc_rscn_disc(struct lpfc_hba * phba) +{ + /* RSCN discovery */ + /* go thru NPR list and issue ELS PLOGIs */ + if (phba->fc_npr_cnt) { + if (lpfc_els_disc_plogi(phba)) + return; + } + if (phba->fc_flag & FC_RSCN_MODE) { + /* Check to see if more RSCNs came in while we were + * processing this one. + */ + if ((phba->fc_rscn_id_cnt == 0) && + (!(phba->fc_flag & FC_RSCN_DISCOVERY))) { + phba->fc_flag &= ~FC_RSCN_MODE; + } else { + lpfc_els_handle_rscn(phba); + } + } +} + +static void +lpfc_cmpl_els_adisc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + LPFC_MBOXQ_t *mbox; + int disc; + + psli = &phba->sli; + + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + irsp = &(rspiocb->iocb); + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + ndlp->nlp_flag &= ~NLP_ADISC_SND; + + /* Since ndlp can be freed in the disc state machine, note if this node + * is being used during discovery. + */ + disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); + + /* ADISC completes to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0104 ADISC completes to NPort x%x " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus, + irsp->un.ulpWord[4], disc, phba->num_disc_nodes); + + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) { + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + goto out; + } + + if (irsp->ulpStatus) { + /* Check for retry */ + if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { + /* ELS command is being retried */ + if (disc) { + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + } + goto out; + } + /* ADISC failed */ + /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ + if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) || + (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) { + disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); + } + else { + lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_ADISC); + } + } else { + /* Good status, call state machine */ + lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_ADISC); + } + + if (disc && phba->num_disc_nodes) { + /* Check to see if there are more ADISCs to be sent */ + lpfc_more_adisc(phba); + + /* Check to see if we are done with ADISC authentication */ + if (phba->num_disc_nodes == 0) { + lpfc_can_disctmo(phba); + /* If we get here, there is nothing left to wait for */ + if ((phba->hba_state < LPFC_HBA_READY) && + (phba->hba_state != LPFC_CLEAR_LA)) { + /* Link up discovery */ + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + phba->hba_state = LPFC_CLEAR_LA; + lpfc_clear_la(phba, mbox); + mbox->mbox_cmpl = + lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox + (phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, + phba->mbox_mem_pool); + lpfc_disc_flush_list(phba); + psli->ring[(psli->ip_ring)]. + flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->fcp_ring)]. + flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->next_ring)]. + flag &= + ~LPFC_STOP_IOCB_EVENT; + phba->hba_state = + LPFC_HBA_READY; + } + } + } else { + lpfc_rscn_disc(phba); + } + } + } + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; +out: + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_issue_els_adisc(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint8_t retry) +{ + ADISC *ap; + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = (sizeof (uint32_t) + sizeof (ADISC)); + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_ADISC)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + /* For ADISC request, remainder of payload is service parameters */ + *((uint32_t *) (pcmd)) = ELS_CMD_ADISC; + pcmd += sizeof (uint32_t); + + /* Fill in ADISC payload */ + ap = (ADISC *) pcmd; + ap->hardAL_PA = phba->fc_pref_ALPA; + memcpy(&ap->portName, &phba->fc_portname, sizeof (struct lpfc_name)); + memcpy(&ap->nodeName, &phba->fc_nodename, sizeof (struct lpfc_name)); + ap->DID = be32_to_cpu(phba->fc_myDID); + + phba->fc_stat.elsXmitADISC++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_adisc; + ndlp->nlp_flag |= NLP_ADISC_SND; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + ndlp->nlp_flag &= ~NLP_ADISC_SND; + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +static void +lpfc_cmpl_els_logo(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + irsp = &(rspiocb->iocb); + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + ndlp->nlp_flag &= ~NLP_LOGO_SND; + + /* LOGO completes to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0105 LOGO completes to NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus, + irsp->un.ulpWord[4], phba->num_disc_nodes); + + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) + goto out; + + if (irsp->ulpStatus) { + /* Check for retry */ + if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { + /* ELS command is being retried */ + goto out; + } + /* LOGO failed */ + /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ + if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) || + (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) { + goto out; + } + else { + lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_LOGO); + } + } else { + /* Good status, call state machine */ + lpfc_disc_state_machine(phba, ndlp, cmdiocb, NLP_EVT_CMPL_LOGO); + + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + lpfc_unreg_rpi(phba, ndlp); + } + } + +out: + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_issue_els_logo(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint8_t retry) +{ + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + cmdsize = 2 * (sizeof (uint32_t) + sizeof (struct lpfc_name)); + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_LOGO)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + *((uint32_t *) (pcmd)) = ELS_CMD_LOGO; + pcmd += sizeof (uint32_t); + + /* Fill in LOGO payload */ + *((uint32_t *) (pcmd)) = be32_to_cpu(phba->fc_myDID); + pcmd += sizeof (uint32_t); + memcpy(pcmd, &phba->fc_portname, sizeof (struct lpfc_name)); + + phba->fc_stat.elsXmitLOGO++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_logo; + ndlp->nlp_flag |= NLP_LOGO_SND; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + ndlp->nlp_flag &= ~NLP_LOGO_SND; + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +static void +lpfc_cmpl_els_cmd(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + + irsp = &rspiocb->iocb; + + /* ELS cmd tag completes */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_ELS, + "%d:0106 ELS cmd tag x%x completes Data: x%x x%x\n", + phba->brd_no, + irsp->ulpIoTag, irsp->ulpStatus, irsp->un.ulpWord[4]); + + /* Check to see if link went down during discovery */ + lpfc_els_chk_latt(phba); + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_issue_els_scr(struct lpfc_hba * phba, uint32_t nportid, uint8_t retry) +{ + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + uint8_t *pcmd; + uint16_t cmdsize; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + cmdsize = (sizeof (uint32_t) + sizeof (SCR)); + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) == 0) { + return (1); + } + + lpfc_nlp_init(phba, ndlp, nportid); + + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_SCR)) == 0) { + mempool_free( ndlp, phba->nlp_mem_pool); + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = ELS_CMD_SCR; + pcmd += sizeof (uint32_t); + + /* For SCR, remainder of payload is SCR parameter page */ + memset(pcmd, 0, sizeof (SCR)); + ((SCR *) pcmd)->Function = SCR_FUNC_FULL; + + phba->fc_stat.elsXmitSCR++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + mempool_free( ndlp, phba->nlp_mem_pool); + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + mempool_free( ndlp, phba->nlp_mem_pool); + return (0); +} + +static int +lpfc_issue_els_farpr(struct lpfc_hba * phba, uint32_t nportid, uint8_t retry) +{ + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + FARP *fp; + uint8_t *pcmd; + uint32_t *lp; + uint16_t cmdsize; + struct lpfc_nodelist *ondlp; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + cmdsize = (sizeof (uint32_t) + sizeof (FARP)); + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) == 0) { + return (1); + } + lpfc_nlp_init(phba, ndlp, nportid); + + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_RNID)) == 0) { + mempool_free( ndlp, phba->nlp_mem_pool); + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = ELS_CMD_FARPR; + pcmd += sizeof (uint32_t); + + /* Fill in FARPR payload */ + fp = (FARP *) (pcmd); + memset(fp, 0, sizeof (FARP)); + lp = (uint32_t *) pcmd; + *lp++ = be32_to_cpu(nportid); + *lp++ = be32_to_cpu(phba->fc_myDID); + fp->Rflags = 0; + fp->Mflags = (FARP_MATCH_PORT | FARP_MATCH_NODE); + + memcpy(&fp->RportName, &phba->fc_portname, sizeof (struct lpfc_name)); + memcpy(&fp->RnodeName, &phba->fc_nodename, sizeof (struct lpfc_name)); + if ((ondlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, nportid))) { + memcpy(&fp->OportName, &ondlp->nlp_portname, + sizeof (struct lpfc_name)); + memcpy(&fp->OnodeName, &ondlp->nlp_nodename, + sizeof (struct lpfc_name)); + } + + phba->fc_stat.elsXmitFARPR++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + mempool_free( ndlp, phba->nlp_mem_pool); + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + mempool_free( ndlp, phba->nlp_mem_pool); + return (0); +} + +void +lpfc_els_retry_delay(unsigned long ptr) +{ + struct lpfc_nodelist *ndlp; + struct lpfc_hba *phba; + unsigned long iflag; + LPFC_DISC_EVT_t *evtp; + + ndlp = (struct lpfc_nodelist *)ptr; + phba = ndlp->nlp_phba; + evtp = &ndlp->els_retry_evt; + + spin_lock_irqsave(phba->host->host_lock, iflag); + if (!list_empty(&evtp->evt_listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; + } + + evtp->evt_arg1 = ndlp; + evtp->evt = LPFC_EVT_ELS_RETRY; + list_add_tail(&evtp->evt_listp, &phba->dpc_disc); + if (phba->dpc_wait) + up(phba->dpc_wait); + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + +void +lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp) +{ + struct lpfc_hba *phba; + uint32_t cmd; + uint32_t did; + uint8_t retry; + + phba = ndlp->nlp_phba; + spin_lock_irq(phba->host->host_lock); + did = (uint32_t) (ndlp->nlp_DID); + cmd = (uint32_t) (ndlp->nlp_last_elscmd); + + if (!(ndlp->nlp_flag & NLP_DELAY_TMO)) { + spin_unlock_irq(phba->host->host_lock); + return; + } + + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + retry = ndlp->nlp_retry; + + switch (cmd) { + case ELS_CMD_FLOGI: + lpfc_issue_els_flogi(phba, ndlp, retry); + break; + case ELS_CMD_PLOGI: + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, retry); + break; + case ELS_CMD_ADISC: + ndlp->nlp_state = NLP_STE_ADISC_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST); + lpfc_issue_els_adisc(phba, ndlp, retry); + break; + case ELS_CMD_PRLI: + ndlp->nlp_state = NLP_STE_PRLI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST); + lpfc_issue_els_prli(phba, ndlp, retry); + break; + case ELS_CMD_LOGO: + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + lpfc_issue_els_logo(phba, ndlp, retry); + break; + } + spin_unlock_irq(phba->host->host_lock); + return; +} + +static int +lpfc_els_retry(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_dmabuf *pcmd; + struct lpfc_nodelist *ndlp; + uint32_t *elscmd; + struct ls_rjt stat; + int retry, maxretry; + int delay; + uint32_t cmd; + + retry = 0; + delay = 0; + maxretry = lpfc_max_els_tries; + irsp = &rspiocb->iocb; + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + cmd = 0; + /* Note: context2 may be 0 for internal driver abort + * of delays ELS command. + */ + + if (pcmd && pcmd->virt) { + elscmd = (uint32_t *) (pcmd->virt); + cmd = *elscmd++; + } + + switch (irsp->ulpStatus) { + case IOSTAT_FCP_RSP_ERROR: + case IOSTAT_REMOTE_STOP: + break; + + case IOSTAT_LOCAL_REJECT: + switch ((irsp->un.ulpWord[4] & 0xff)) { + case IOERR_LOOP_OPEN_FAILURE: + if (cmd == ELS_CMD_PLOGI) { + if (cmdiocb->retry == 0) { + delay = 1; + } + } + retry = 1; + break; + + case IOERR_SEQUENCE_TIMEOUT: + retry = 1; + if ((cmd == ELS_CMD_FLOGI) + && (phba->fc_topology != TOPOLOGY_LOOP)) { + maxretry = 48; + } + break; + + case IOERR_NO_RESOURCES: + if (cmd == ELS_CMD_PLOGI) { + delay = 1; + } + retry = 1; + break; + + case IOERR_INVALID_RPI: + retry = 1; + break; + } + break; + + case IOSTAT_NPORT_RJT: + case IOSTAT_FABRIC_RJT: + if (irsp->un.ulpWord[4] & RJT_UNAVAIL_TEMP) { + retry = 1; + break; + } + break; + + case IOSTAT_NPORT_BSY: + case IOSTAT_FABRIC_BSY: + retry = 1; + break; + + case IOSTAT_LS_RJT: + stat.un.lsRjtError = be32_to_cpu(irsp->un.ulpWord[4]); + /* Added for Vendor specifc support + * Just keep retrying for these Rsn / Exp codes + */ + switch (stat.un.b.lsRjtRsnCode) { + case LSRJT_UNABLE_TPC: + if (stat.un.b.lsRjtRsnCodeExp == + LSEXP_CMD_IN_PROGRESS) { + if (cmd == ELS_CMD_PLOGI) { + delay = 1; + maxretry = 48; + } + retry = 1; + break; + } + if (cmd == ELS_CMD_PLOGI) { + delay = 1; + retry = 1; + break; + } + break; + + case LSRJT_LOGICAL_BSY: + if (cmd == ELS_CMD_PLOGI) { + delay = 1; + maxretry = 48; + } + retry = 1; + break; + } + break; + + case IOSTAT_INTERMED_RSP: + case IOSTAT_BA_RJT: + break; + + default: + break; + } + + if (ndlp->nlp_DID == FDMI_DID) { + retry = 1; + } + + if ((++cmdiocb->retry) >= maxretry) { + phba->fc_stat.elsRetryExceeded++; + retry = 0; + } + + if (retry) { + + /* Retry ELS command to remote NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0107 Retry ELS command x%x to remote " + "NPORT x%x Data: x%x x%x\n", + phba->brd_no, + cmd, ndlp->nlp_DID, cmdiocb->retry, delay); + + if ((cmd == ELS_CMD_PLOGI) || (cmd == ELS_CMD_ADISC)) { + /* If discovery / RSCN timer is running, reset it */ + if (timer_pending(&phba->fc_disctmo) || + (phba->fc_flag & FC_RSCN_MODE)) { + lpfc_set_disctmo(phba); + } + } + + phba->fc_stat.elsXmitRetry++; + if (delay) { + phba->fc_stat.elsDelayRetry++; + ndlp->nlp_retry = cmdiocb->retry; + + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ); + ndlp->nlp_flag |= NLP_DELAY_TMO; + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_last_elscmd = cmd; + + return (1); + } + switch (cmd) { + case ELS_CMD_FLOGI: + lpfc_issue_els_flogi(phba, ndlp, cmdiocb->retry); + return (1); + case ELS_CMD_PLOGI: + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, cmdiocb->retry); + return (1); + case ELS_CMD_ADISC: + ndlp->nlp_state = NLP_STE_ADISC_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST); + lpfc_issue_els_adisc(phba, ndlp, cmdiocb->retry); + return (1); + case ELS_CMD_PRLI: + ndlp->nlp_state = NLP_STE_PRLI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST); + lpfc_issue_els_prli(phba, ndlp, cmdiocb->retry); + return (1); + case ELS_CMD_LOGO: + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + lpfc_issue_els_logo(phba, ndlp, cmdiocb->retry); + return (1); + } + } + + /* No retry ELS command to remote NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0108 No retry ELS command x%x to remote NPORT x%x " + "Data: x%x x%x\n", + phba->brd_no, + cmd, ndlp->nlp_DID, cmdiocb->retry, ndlp->nlp_flag); + + return (0); +} + +int +lpfc_els_free_iocb(struct lpfc_hba * phba, struct lpfc_iocbq * elsiocb) +{ + struct lpfc_dmabuf *buf_ptr, *buf_ptr1; + + /* context2 = cmd, context2->next = rsp, context3 = bpl */ + if (elsiocb->context2) { + buf_ptr1 = (struct lpfc_dmabuf *) elsiocb->context2; + /* Free the response before processing the command. */ + if (!list_empty(&buf_ptr1->list)) { + buf_ptr = list_entry(buf_ptr1->list.next, + struct lpfc_dmabuf, list); + lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys); + kfree(buf_ptr); + } + lpfc_mbuf_free(phba, buf_ptr1->virt, buf_ptr1->phys); + kfree(buf_ptr1); + } + + if (elsiocb->context3) { + buf_ptr = (struct lpfc_dmabuf *) elsiocb->context3; + lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys); + kfree(buf_ptr); + } + + mempool_free( elsiocb, phba->iocb_mem_pool); + return 0; +} + +static void +lpfc_cmpl_els_logo_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + struct lpfc_nodelist *ndlp; + + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + + /* ACC to LOGO completes to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0109 ACC to LOGO completes to NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag, + ndlp->nlp_state, ndlp->nlp_rpi); + + ndlp->nlp_flag &= ~NLP_LOGO_ACC; + + switch (ndlp->nlp_state) { + case NLP_STE_UNUSED_NODE: /* node is just allocated */ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + break; + case NLP_STE_NPR_NODE: /* NPort Recovery mode */ + lpfc_unreg_rpi(phba, ndlp); + break; + default: + break; + } + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +static void +lpfc_cmpl_els_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + struct lpfc_nodelist *ndlp; + LPFC_MBOXQ_t *mbox = NULL; + + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + if (cmdiocb->context_un.mbox) + mbox = cmdiocb->context_un.mbox; + + + /* Check to see if link went down during discovery */ + if ((lpfc_els_chk_latt(phba)) || !ndlp) { + if (mbox) { + mempool_free( mbox, phba->mbox_mem_pool); + } + goto out; + } + + /* ELS response tag completes */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0110 ELS response tag x%x completes " + "Data: x%x x%x x%x x%x x%x x%x\n", + phba->brd_no, + cmdiocb->iocb.ulpIoTag, rspiocb->iocb.ulpStatus, + rspiocb->iocb.un.ulpWord[4], ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + + if (mbox) { + if ((rspiocb->iocb.ulpStatus == 0) + && (ndlp->nlp_flag & NLP_ACC_REGLOGIN)) { + /* set_slim mailbox command needs to execute first, + * queue this command to be processed later. + */ + lpfc_unreg_rpi(phba, ndlp); + mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login; + mbox->context2 = ndlp; + ndlp->nlp_state = NLP_STE_REG_LOGIN_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_REGLOGIN_LIST); + if (lpfc_sli_issue_mbox(phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + != MBX_NOT_FINISHED) { + goto out; + } + /* NOTE: we should have messages for unsuccessful + reglogin */ + mempool_free( mbox, phba->mbox_mem_pool); + } else { + mempool_free( mbox, phba->mbox_mem_pool); + if (ndlp->nlp_flag & NLP_ACC_REGLOGIN) { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + } + } + } +out: + if(ndlp) + ndlp->nlp_flag &= ~NLP_ACC_REGLOGIN; + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_els_rsp_acc(struct lpfc_hba * phba, uint32_t flag, + struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp, + LPFC_MBOXQ_t * mbox, uint8_t newnode) +{ + IOCB_t *icmd; + IOCB_t *oldcmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + oldcmd = &oldiocb->iocb; + + switch (flag) { + case ELS_CMD_ACC: + cmdsize = sizeof (uint32_t); + if ((elsiocb = + lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ELS_CMD_ACC)) == 0) { + return (1); + } + icmd = &elsiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + *((uint32_t *) (pcmd)) = ELS_CMD_ACC; + pcmd += sizeof (uint32_t); + break; + case ELS_CMD_PLOGI: + cmdsize = (sizeof (struct serv_parm) + sizeof (uint32_t)); + if ((elsiocb = + lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ELS_CMD_ACC)) == 0) { + return (1); + } + icmd = &elsiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + if (mbox) + elsiocb->context_un.mbox = mbox; + + *((uint32_t *) (pcmd)) = ELS_CMD_ACC; + pcmd += sizeof (uint32_t); + memcpy(pcmd, &phba->fc_sparam, sizeof (struct serv_parm)); + break; + default: + return (1); + } + + if (newnode) + elsiocb->context1 = NULL; + + /* Xmit ELS ACC response tag */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0128 Xmit ELS ACC response tag x%x " + "Data: x%x x%x x%x x%x x%x\n", + phba->brd_no, + elsiocb->iocb.ulpIoTag, + elsiocb->iocb.ulpContext, ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + + if (ndlp->nlp_flag & NLP_LOGO_ACC) { + elsiocb->iocb_cmpl = lpfc_cmpl_els_logo_acc; + } else { + elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; + } + + phba->fc_stat.elsXmitACC++; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +int +lpfc_els_rsp_reject(struct lpfc_hba * phba, uint32_t rejectError, + struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp) +{ + IOCB_t *icmd; + IOCB_t *oldcmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = 2 * sizeof (uint32_t); + if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ELS_CMD_LS_RJT)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + oldcmd = &oldiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = ELS_CMD_LS_RJT; + pcmd += sizeof (uint32_t); + *((uint32_t *) (pcmd)) = rejectError; + + /* Xmit ELS RJT response tag */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0129 Xmit ELS RJT x%x response tag x%x " + "Data: x%x x%x x%x x%x x%x\n", + phba->brd_no, + rejectError, elsiocb->iocb.ulpIoTag, + elsiocb->iocb.ulpContext, ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + + phba->fc_stat.elsXmitLSRJT++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; + + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +int +lpfc_els_rsp_adisc_acc(struct lpfc_hba * phba, + struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp) +{ + ADISC *ap; + IOCB_t *icmd; + IOCB_t *oldcmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = sizeof (uint32_t) + sizeof (ADISC); + if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ELS_CMD_ACC)) == 0) { + return (1); + } + + /* Xmit ADISC ACC response tag */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0130 Xmit ADISC ACC response tag x%x " + "Data: x%x x%x x%x x%x x%x\n", + phba->brd_no, + elsiocb->iocb.ulpIoTag, + elsiocb->iocb.ulpContext, ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + + icmd = &elsiocb->iocb; + oldcmd = &oldiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = ELS_CMD_ACC; + pcmd += sizeof (uint32_t); + + ap = (ADISC *) (pcmd); + ap->hardAL_PA = phba->fc_pref_ALPA; + memcpy(&ap->portName, &phba->fc_portname, sizeof (struct lpfc_name)); + memcpy(&ap->nodeName, &phba->fc_nodename, sizeof (struct lpfc_name)); + ap->DID = be32_to_cpu(phba->fc_myDID); + + phba->fc_stat.elsXmitACC++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; + + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +int +lpfc_els_rsp_prli_acc(struct lpfc_hba * phba, + struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp) +{ + PRLI *npr; + lpfc_vpd_t *vpd; + IOCB_t *icmd; + IOCB_t *oldcmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = sizeof (uint32_t) + sizeof (PRLI); + if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, + (ELS_CMD_ACC | + (ELS_CMD_PRLI & ~ELS_RSP_MASK)))) == + 0) { + return (1); + } + + /* Xmit PRLI ACC response tag */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0131 Xmit PRLI ACC response tag x%x " + "Data: x%x x%x x%x x%x x%x\n", + phba->brd_no, + elsiocb->iocb.ulpIoTag, + elsiocb->iocb.ulpContext, ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + + icmd = &elsiocb->iocb; + oldcmd = &oldiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = (ELS_CMD_ACC | (ELS_CMD_PRLI & ~ELS_RSP_MASK)); + pcmd += sizeof (uint32_t); + + /* For PRLI, remainder of payload is PRLI parameter page */ + memset(pcmd, 0, sizeof (PRLI)); + + npr = (PRLI *) pcmd; + vpd = &phba->vpd; + /* + * If our firmware version is 3.20 or later, + * set the following bits for FC-TAPE support. + */ + if (vpd->rev.feaLevelHigh >= 0x02) { + npr->ConfmComplAllowed = 1; + npr->Retry = 1; + npr->TaskRetryIdReq = 1; + } + + npr->acceptRspCode = PRLI_REQ_EXECUTED; + npr->estabImagePair = 1; + npr->readXferRdyDis = 1; + npr->ConfmComplAllowed = 1; + + npr->prliType = PRLI_FCP_TYPE; + npr->initiatorFunc = 1; + + phba->fc_stat.elsXmitACC++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; + + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +static int +lpfc_els_rsp_rnid_acc(struct lpfc_hba * phba, + uint8_t format, + struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp) +{ + RNID *rn; + IOCB_t *icmd; + IOCB_t *oldcmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + cmdsize = sizeof (uint32_t) + sizeof (uint32_t) + + (2 * sizeof (struct lpfc_name)); + if (format) + cmdsize += sizeof (RNID_TOP_DISC); + + if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ELS_CMD_ACC)) == 0) { + return (1); + } + + /* Xmit RNID ACC response tag */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0132 Xmit RNID ACC response tag x%x " + "Data: x%x\n", + phba->brd_no, + elsiocb->iocb.ulpIoTag, + elsiocb->iocb.ulpContext); + + icmd = &elsiocb->iocb; + oldcmd = &oldiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = ELS_CMD_ACC; + pcmd += sizeof (uint32_t); + + memset(pcmd, 0, sizeof (RNID)); + rn = (RNID *) (pcmd); + rn->Format = format; + rn->CommonLen = (2 * sizeof (struct lpfc_name)); + memcpy(&rn->portName, &phba->fc_portname, sizeof (struct lpfc_name)); + memcpy(&rn->nodeName, &phba->fc_nodename, sizeof (struct lpfc_name)); + switch (format) { + case 0: + rn->SpecificLen = 0; + break; + case RNID_TOPOLOGY_DISC: + rn->SpecificLen = sizeof (RNID_TOP_DISC); + memcpy(&rn->un.topologyDisc.portName, + &phba->fc_portname, sizeof (struct lpfc_name)); + rn->un.topologyDisc.unitType = RNID_HBA; + rn->un.topologyDisc.physPort = 0; + rn->un.topologyDisc.attachedNodes = 0; + break; + default: + rn->CommonLen = 0; + rn->SpecificLen = 0; + break; + } + + phba->fc_stat.elsXmitACC++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; + elsiocb->context1 = NULL; /* Don't need ndlp for cmpl, + * it could be freed */ + + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +int +lpfc_els_disc_adisc(struct lpfc_hba * phba) +{ + int sentadisc; + struct lpfc_nodelist *ndlp, *next_ndlp; + + sentadisc = 0; + /* go thru NPR list and issue any remaining ELS ADISCs */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, + nlp_listp) { + if(ndlp->nlp_flag & NLP_NPR_2B_DISC) { + if(ndlp->nlp_flag & NLP_NPR_ADISC) { + ndlp->nlp_flag &= ~NLP_NPR_ADISC; + ndlp->nlp_state = NLP_STE_ADISC_ISSUE; + lpfc_nlp_list(phba, ndlp, + NLP_ADISC_LIST); + lpfc_issue_els_adisc(phba, ndlp, 0); + sentadisc++; + phba->num_disc_nodes++; + if (phba->num_disc_nodes >= + phba->cfg_discovery_threads) { + phba->fc_flag |= FC_NLP_MORE; + break; + } + } + } + } + if (sentadisc == 0) { + phba->fc_flag &= ~FC_NLP_MORE; + } + return(sentadisc); +} + +int +lpfc_els_disc_plogi(struct lpfc_hba * phba) +{ + int sentplogi; + struct lpfc_nodelist *ndlp, *next_ndlp; + + sentplogi = 0; + /* go thru NPR list and issue any remaining ELS PLOGIs */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, + nlp_listp) { + if((ndlp->nlp_flag & NLP_NPR_2B_DISC) && + (!(ndlp->nlp_flag & NLP_DELAY_TMO))) { + if(!(ndlp->nlp_flag & NLP_NPR_ADISC)) { + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + sentplogi++; + phba->num_disc_nodes++; + if (phba->num_disc_nodes >= + phba->cfg_discovery_threads) { + phba->fc_flag |= FC_NLP_MORE; + break; + } + } + } + } + if (sentplogi == 0) { + phba->fc_flag &= ~FC_NLP_MORE; + } + return(sentplogi); +} + +int +lpfc_els_flush_rscn(struct lpfc_hba * phba) +{ + struct lpfc_dmabuf *mp; + int i; + + for (i = 0; i < phba->fc_rscn_id_cnt; i++) { + mp = phba->fc_rscn_id_list[i]; + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + phba->fc_rscn_id_list[i] = NULL; + } + phba->fc_rscn_id_cnt = 0; + phba->fc_flag &= ~(FC_RSCN_MODE | FC_RSCN_DISCOVERY); + lpfc_can_disctmo(phba); + return (0); +} + +int +lpfc_rscn_payload_check(struct lpfc_hba * phba, uint32_t did) +{ + D_ID ns_did; + D_ID rscn_did; + struct lpfc_dmabuf *mp; + uint32_t *lp; + uint32_t payload_len, cmd, i, match; + + ns_did.un.word = did; + match = 0; + + /* Never match fabric nodes for RSCNs */ + if ((did & Fabric_DID_MASK) == Fabric_DID_MASK) + return(0); + + /* If we are doing a FULL RSCN rediscovery, match everything */ + if (phba->fc_flag & FC_RSCN_DISCOVERY) { + return (did); + } + + for (i = 0; i < phba->fc_rscn_id_cnt; i++) { + mp = phba->fc_rscn_id_list[i]; + lp = (uint32_t *) mp->virt; + cmd = *lp++; + payload_len = be32_to_cpu(cmd) & 0xffff; /* payload length */ + payload_len -= sizeof (uint32_t); /* take off word 0 */ + while (payload_len) { + rscn_did.un.word = *lp++; + rscn_did.un.word = be32_to_cpu(rscn_did.un.word); + payload_len -= sizeof (uint32_t); + switch (rscn_did.un.b.resv) { + case 0: /* Single N_Port ID effected */ + if (ns_did.un.word == rscn_did.un.word) { + match = did; + } + break; + case 1: /* Whole N_Port Area effected */ + if ((ns_did.un.b.domain == rscn_did.un.b.domain) + && (ns_did.un.b.area == rscn_did.un.b.area)) + { + match = did; + } + break; + case 2: /* Whole N_Port Domain effected */ + if (ns_did.un.b.domain == rscn_did.un.b.domain) + { + match = did; + } + break; + case 3: /* Whole Fabric effected */ + match = did; + break; + default: + /* Unknown Identifier in RSCN list */ + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "%d:0217 Unknown Identifier in " + "RSCN payload Data: x%x\n", + phba->brd_no, rscn_did.un.word); + break; + } + if (match) { + break; + } + } + } + return (match); +} + +static int +lpfc_rscn_recovery_check(struct lpfc_hba * phba) +{ + struct lpfc_nodelist *ndlp = NULL, *next_ndlp; + struct list_head *listp; + struct list_head *node_list[7]; + int i; + + /* Look at all nodes effected by pending RSCNs and move + * them to NPR list. + */ + node_list[0] = &phba->fc_npr_list; /* MUST do this list first */ + node_list[1] = &phba->fc_nlpmap_list; + node_list[2] = &phba->fc_nlpunmap_list; + node_list[3] = &phba->fc_prli_list; + node_list[4] = &phba->fc_reglogin_list; + node_list[5] = &phba->fc_adisc_list; + node_list[6] = &phba->fc_plogi_list; + for (i = 0; i < 7; i++) { + listp = node_list[i]; + if (list_empty(listp)) + continue; + + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + if((lpfc_rscn_payload_check(phba, ndlp->nlp_DID))) { + /* part of RSCN, process this entry */ + lpfc_set_failmask(phba, ndlp, + LPFC_DEV_DISCOVERY_INP, + LPFC_SET_BITMASK); + + lpfc_disc_state_machine(phba, ndlp, NULL, + NLP_EVT_DEVICE_RECOVERY); + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + del_timer_sync(&ndlp->nlp_delayfunc); + + if (!list_empty(&ndlp-> + els_retry_evt.evt_listp)) + list_del_init(&ndlp-> + els_retry_evt. + evt_listp); + } + } + } + } + return (0); +} + +static int +lpfc_els_rcv_rscn(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, + struct lpfc_nodelist * ndlp, uint8_t newnode) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + uint32_t payload_len, cmd; + + icmd = &cmdiocb->iocb; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + cmd = *lp++; + payload_len = be32_to_cpu(cmd) & 0xffff; /* payload length */ + payload_len -= sizeof (uint32_t); /* take off word 0 */ + cmd &= ELS_CMD_MASK; + + /* RSCN received */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0214 RSCN received Data: x%x x%x x%x x%x\n", + phba->brd_no, + phba->fc_flag, payload_len, *lp, phba->fc_rscn_id_cnt); + + /* If we are about to begin discovery, just ACC the RSCN. + * Discovery processing will satisfy it. + */ + if (phba->hba_state < LPFC_NS_QRY) { + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, + newnode); + return (0); + } + + /* If we are already processing an RSCN, save the received + * RSCN payload buffer, cmdiocb->context2 to process later. + */ + if (phba->fc_flag & (FC_RSCN_MODE | FC_NDISC_ACTIVE)) { + if ((phba->fc_rscn_id_cnt < FC_MAX_HOLD_RSCN) && + !(phba->fc_flag & FC_RSCN_DISCOVERY)) { + phba->fc_flag |= FC_RSCN_MODE; + phba->fc_rscn_id_list[phba->fc_rscn_id_cnt++] = pcmd; + + /* If we zero, cmdiocb->context2, the calling + * routine will not try to free it. + */ + cmdiocb->context2 = NULL; + + /* Deferred RSCN */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0235 Deferred RSCN " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->fc_rscn_id_cnt, + phba->fc_flag, phba->hba_state); + } else { + phba->fc_flag |= FC_RSCN_DISCOVERY; + /* ReDiscovery RSCN */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0234 ReDiscovery RSCN " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->fc_rscn_id_cnt, + phba->fc_flag, phba->hba_state); + } + /* Send back ACC */ + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, + newnode); + + /* send RECOVERY event for ALL nodes that match RSCN payload */ + lpfc_rscn_recovery_check(phba); + return (0); + } + + phba->fc_flag |= FC_RSCN_MODE; + phba->fc_rscn_id_list[phba->fc_rscn_id_cnt++] = pcmd; + /* + * If we zero, cmdiocb->context2, the calling routine will + * not try to free it. + */ + cmdiocb->context2 = NULL; + + lpfc_set_disctmo(phba); + + /* Send back ACC */ + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, newnode); + + /* send RECOVERY event for ALL nodes that match RSCN payload */ + lpfc_rscn_recovery_check(phba); + + return (lpfc_els_handle_rscn(phba)); +} + +int +lpfc_els_handle_rscn(struct lpfc_hba * phba) +{ + struct lpfc_nodelist *ndlp; + + lpfc_put_event(phba, HBA_EVENT_RSCN, phba->fc_myDID, + (void *)(unsigned long)(phba->fc_myDID), 0, 0); + + /* Start timer for RSCN processing */ + lpfc_set_disctmo(phba); + + /* RSCN processed */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0215 RSCN processed Data: x%x x%x x%x x%x\n", + phba->brd_no, + phba->fc_flag, 0, phba->fc_rscn_id_cnt, + phba->hba_state); + + /* To process RSCN, first compare RSCN data with NameServer */ + phba->fc_ns_retry = 0; + if ((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED, + NameServer_DID))) { + /* Good ndlp, issue CT Request to NameServer */ + if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT) == 0) { + /* Wait for NameServer query cmpl before we can + continue */ + return (1); + } + } else { + /* If login to NameServer does not exist, issue one */ + /* Good status, issue PLOGI to NameServer */ + if ((ndlp = + lpfc_findnode_did(phba, NLP_SEARCH_ALL, NameServer_DID))) { + /* Wait for NameServer login cmpl before we can + continue */ + return (1); + } + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) + == 0) { + lpfc_els_flush_rscn(phba); + return (0); + } else { + lpfc_nlp_init(phba, ndlp, NameServer_DID); + ndlp->nlp_type |= NLP_FABRIC; + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_issue_els_plogi(phba, ndlp, 0); + /* Wait for NameServer login cmpl before we can + continue */ + return (1); + } + } + + lpfc_els_flush_rscn(phba); + return (0); +} + +static int +lpfc_els_rcv_flogi(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, + struct lpfc_nodelist * ndlp, uint8_t newnode) +{ + struct lpfc_dmabuf *pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + uint32_t *lp = (uint32_t *) pcmd->virt; + IOCB_t *icmd = &cmdiocb->iocb; + struct serv_parm *sp; + LPFC_MBOXQ_t *mbox; + struct ls_rjt stat; + uint32_t cmd, did; + + + cmd = *lp++; + sp = (struct serv_parm *) lp; + + /* FLOGI received */ + + lpfc_set_disctmo(phba); + + if (phba->fc_topology == TOPOLOGY_LOOP) { + /* We should never receive a FLOGI in loop mode, ignore it */ + did = icmd->un.elsreq64.remoteID; + + /* An FLOGI ELS command was received from DID in + Loop Mode */ + lpfc_printf_log(phba, KERN_ERR, LOG_ELS, + "%d:0113 An FLOGI ELS command x%x was received " + "from DID x%x in Loop Mode\n", + phba->brd_no, cmd, did); + return (1); + } + + did = Fabric_DID; + + if ((lpfc_check_sparm(phba, ndlp, sp, CLASS3))) { + /* For a FLOGI we accept, then if our portname is greater + * then the remote portname we initiate Nport login. + */ + int rc; + + rc = memcmp(&phba->fc_portname, &sp->portName, + sizeof (struct lpfc_name)); + + if (!rc) { + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + return (1); + } + lpfc_linkdown(phba); + lpfc_init_link(phba, mbox, + phba->cfg_topology, + phba->cfg_link_speed); + mbox->mb.un.varInitLnk.lipsr_AL_PA = 0; + mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mbox, phba->mbox_mem_pool); + } + return (1); + } + + else if (rc > 0) { /* greater than */ + phba->fc_flag |= FC_PT2PT_PLOGI; + } + phba->fc_flag |= FC_PT2PT; + phba->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP); + } else { + /* Reject this request because invalid parameters */ + stat.un.b.lsRjtRsvd0 = 0; + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS; + stat.un.b.vendorUnique = 0; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + return (1); + } + + /* Send back ACC */ + lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL, newnode); + + return (0); +} + +static int +lpfc_els_rcv_rnid(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, struct lpfc_nodelist * ndlp) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + RNID *rn; + struct ls_rjt stat; + uint32_t cmd, did; + + icmd = &cmdiocb->iocb; + did = icmd->un.elsreq64.remoteID; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + cmd = *lp++; + rn = (RNID *) lp; + + /* RNID received */ + + switch (rn->Format) { + case 0: + case RNID_TOPOLOGY_DISC: + /* Send back ACC */ + lpfc_els_rsp_rnid_acc(phba, rn->Format, cmdiocb, ndlp); + break; + default: + /* Reject this request because format not supported */ + stat.un.b.lsRjtRsvd0 = 0; + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_CANT_GIVE_DATA; + stat.un.b.vendorUnique = 0; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + } + return (0); +} + +static int +lpfc_els_rcv_rrq(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_nodelist * ndlp) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + RRQ *rrq; + uint32_t cmd, did; + + psli = &phba->sli; + pring = &psli->ring[LPFC_FCP_RING]; + icmd = &cmdiocb->iocb; + did = icmd->un.elsreq64.remoteID; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + cmd = *lp++; + rrq = (RRQ *) lp; + + /* RRQ received */ + /* Get oxid / rxid from payload and abort it */ + if ((rrq->SID == be32_to_cpu(phba->fc_myDID))) { + lpfc_sli_abort_iocb_ctx(phba, pring, rrq->Oxid); + } else { + lpfc_sli_abort_iocb_ctx(phba, pring, rrq->Rxid); + } + /* ACCEPT the rrq request */ + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + + return 0; +} + +static int +lpfc_els_rcv_farp(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, struct lpfc_nodelist * ndlp) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + FARP *fp; + uint32_t cmd, cnt, did; + + icmd = &cmdiocb->iocb; + did = icmd->un.elsreq64.remoteID; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + cmd = *lp++; + fp = (FARP *) lp; + + /* FARP-REQ received from DID */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_ELS, + "%d:0134 FARP-REQ received from DID x%x\n", + phba->brd_no, did); + + /* We will only support match on WWPN or WWNN */ + if (fp->Mflags & ~(FARP_MATCH_NODE | FARP_MATCH_PORT)) { + return (0); + } + + cnt = 0; + /* If this FARP command is searching for my portname */ + if (fp->Mflags & FARP_MATCH_PORT) { + if (memcmp(&fp->RportName, &phba->fc_portname, + sizeof (struct lpfc_name)) == 0) + cnt = 1; + } + + /* If this FARP command is searching for my nodename */ + if (fp->Mflags & FARP_MATCH_NODE) { + if (memcmp(&fp->RnodeName, &phba->fc_nodename, + sizeof (struct lpfc_name)) == 0) + cnt = 1; + } + + if (cnt) { + if((ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) || + (ndlp->nlp_state == NLP_STE_MAPPED_NODE)) { + /* Log back into the node before sending the FARP. */ + if (fp->Rflags & FARP_REQUEST_PLOGI) { + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + } + + /* Send a FARP response to that node */ + if (fp->Rflags & FARP_REQUEST_FARPR) { + lpfc_issue_els_farpr(phba, did, 0); + } + } + } + return (0); +} + +static int +lpfc_els_rcv_farpr(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, struct lpfc_nodelist * ndlp) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + uint32_t cmd, did; + + icmd = &cmdiocb->iocb; + did = icmd->un.elsreq64.remoteID; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + cmd = *lp++; + /* FARP-RSP received from DID */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_ELS, + "%d:0133 FARP-RSP received from DID x%x\n", + phba->brd_no, did); + + /* ACCEPT the Farp resp request */ + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + + return 0; +} + +static int +lpfc_els_rcv_fan(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_nodelist * ndlp) +{ + /* FAN received */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_ELS, + "%d:265 FAN received\n", + phba->brd_no); + + return (0); +} + +void +lpfc_els_timeout(unsigned long ptr) +{ + struct lpfc_hba *phba; + unsigned long iflag; + + phba = (struct lpfc_hba *)ptr; + if (phba == 0) + return; + spin_lock_irqsave(phba->host->host_lock, iflag); + if (!(phba->work_hba_events & WORKER_ELS_TMO)) { + phba->work_hba_events |= WORKER_ELS_TMO; + if (phba->dpc_wait) + up(phba->dpc_wait); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + +void +lpfc_els_timeout_handler(struct lpfc_hba *phba) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *tmp_iocb, *piocb; + IOCB_t *cmd = NULL; + struct lpfc_dmabuf *pcmd; + struct list_head *dlp; + uint32_t *elscmd; + uint32_t els_command; + uint32_t timeout; + uint32_t remote_ID; + + if(phba == 0) + return; + spin_lock_irq(phba->host->host_lock); + /* If the timer is already canceled do nothing */ + if (!(phba->work_hba_events & WORKER_ELS_TMO)) { + spin_unlock_irq(phba->host->host_lock); + return; + } + + timeout = (uint32_t)(phba->fc_ratov << 1); + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + dlp = &pring->txcmplq; + + list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) { + cmd = &piocb->iocb; + + if (piocb->iocb_flag & LPFC_IO_LIBDFC) { + continue; + } + pcmd = (struct lpfc_dmabuf *) piocb->context2; + elscmd = (uint32_t *) (pcmd->virt); + els_command = *elscmd; + + if ((els_command == ELS_CMD_FARP) + || (els_command == ELS_CMD_FARPR)) { + continue; + } + + if (piocb->drvrTimeout > 0) { + if (piocb->drvrTimeout >= timeout) { + piocb->drvrTimeout -= timeout; + } else { + piocb->drvrTimeout = 0; + } + continue; + } + + list_del(&piocb->list); + pring->txcmplq_cnt--; + + if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) { + struct lpfc_nodelist *ndlp; + + ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext); + remote_ID = ndlp->nlp_DID; + if (cmd->un.elsreq64.bdl.ulpIoTag32) { + lpfc_sli_issue_abort_iotag32(phba, + pring, piocb); + } + } else { + remote_ID = cmd->un.elsreq64.remoteID; + } + + lpfc_printf_log(phba, + KERN_ERR, + LOG_ELS, + "%d:0127 ELS timeout Data: x%x x%x x%x x%x\n", + phba->brd_no, els_command, + remote_ID, cmd->ulpCommand, cmd->ulpIoTag); + + /* + * The iocb has timed out; abort it. + */ + if (piocb->iocb_cmpl) { + cmd->ulpStatus = IOSTAT_LOCAL_REJECT; + cmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (piocb->iocb_cmpl) (phba, piocb, piocb); + } else { + mempool_free(piocb, phba->iocb_mem_pool); + } + } + + if (phba->sli.ring[LPFC_ELS_RING].txcmplq_cnt) { + phba->els_tmofunc.expires = jiffies + HZ * timeout; + add_timer(&phba->els_tmofunc); + } + spin_unlock_irq(phba->host->host_lock); +} + +void +lpfc_els_flush_cmd(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *tmp_iocb, *piocb; + IOCB_t *cmd = NULL; + struct lpfc_dmabuf *pcmd; + uint32_t *elscmd; + uint32_t els_command; + uint32_t remote_ID; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + list_for_each_entry_safe(piocb, tmp_iocb, &pring->txq, list) { + cmd = &piocb->iocb; + + if (piocb->iocb_flag & LPFC_IO_LIBDFC) { + continue; + } + + /* Do not flush out the QUE_RING and ABORT/CLOSE iocbs */ + if ((cmd->ulpCommand == CMD_QUE_RING_BUF_CN) || + (cmd->ulpCommand == CMD_QUE_RING_BUF64_CN) || + (cmd->ulpCommand == CMD_CLOSE_XRI_CN) || + (cmd->ulpCommand == CMD_ABORT_XRI_CN)) { + continue; + } + + pcmd = (struct lpfc_dmabuf *) piocb->context2; + elscmd = (uint32_t *) (pcmd->virt); + els_command = *elscmd; + + if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) { + struct lpfc_nodelist *ndlp; + + ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext); + remote_ID = ndlp->nlp_DID; + if (phba->hba_state == LPFC_HBA_READY) { + continue; + } + } else { + remote_ID = cmd->un.elsreq64.remoteID; + } + + list_del(&piocb->list); + pring->txcmplq_cnt--; + + cmd->ulpStatus = IOSTAT_LOCAL_REJECT; + cmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + + if (piocb->iocb_cmpl) { + (piocb->iocb_cmpl) (phba, piocb, piocb); + } else { + mempool_free( piocb, phba->iocb_mem_pool); + } + } + + list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) { + cmd = &piocb->iocb; + + if (piocb->iocb_flag & LPFC_IO_LIBDFC) { + continue; + } + pcmd = (struct lpfc_dmabuf *) piocb->context2; + elscmd = (uint32_t *) (pcmd->virt); + els_command = *elscmd; + + if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) { + struct lpfc_nodelist *ndlp; + + ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext); + remote_ID = ndlp->nlp_DID; + if (phba->hba_state == LPFC_HBA_READY) { + continue; + } + } else { + remote_ID = cmd->un.elsreq64.remoteID; + } + + list_del(&piocb->list); + pring->txcmplq_cnt--; + + cmd->ulpStatus = IOSTAT_LOCAL_REJECT; + cmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + + if (piocb->iocb_cmpl) { + (piocb->iocb_cmpl) (phba, piocb, piocb); + } else { + mempool_free( piocb, phba->iocb_mem_pool); + } + } + return; +} + +void +lpfc_els_unsol_event(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, struct lpfc_iocbq * elsiocb) +{ + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + struct lpfc_dmabuf *mp; + uint32_t *lp; + IOCB_t *icmd; + struct ls_rjt stat; + uint32_t cmd; + uint32_t did; + uint32_t newnode; + uint32_t drop_cmd = 0; /* by default do NOT drop received cmd */ + uint32_t rjt_err = 0; + + psli = &phba->sli; + icmd = &elsiocb->iocb; + + if ((icmd->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((icmd->un.ulpWord[4] & 0xff) == IOERR_RCV_BUFFER_WAITING)) { + /* Not enough posted buffers; Try posting more buffers */ + phba->fc_stat.NoRcvBuf++; + lpfc_post_buffer(phba, pring, 0, 1); + return; + } + + /* If there are no BDEs associated with this IOCB, + * there is nothing to do. + */ + if (icmd->ulpBdeCount == 0) + return; + + /* type of ELS cmd is first 32bit word in packet */ + mp = lpfc_sli_ringpostbuf_get(phba, pring, getPaddr(icmd->un. + cont64[0]. + addrHigh, + icmd->un. + cont64[0].addrLow)); + if (mp == 0) { + drop_cmd = 1; + goto dropit; + } + + newnode = 0; + lp = (uint32_t *) mp->virt; + cmd = *lp++; + lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], 1, 1); + + if (icmd->ulpStatus) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + drop_cmd = 1; + goto dropit; + } + + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + drop_cmd = 1; + goto dropit; + } + + did = icmd->un.rcvels.remoteID; + if ((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, did)) == 0) { + /* Cannot find existing Fabric ndlp, so allocate a new one */ + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) + == 0) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + drop_cmd = 1; + goto dropit; + } + + lpfc_nlp_init(phba, ndlp, did); + newnode = 1; + if ((did & Fabric_DID_MASK) == Fabric_DID_MASK) { + ndlp->nlp_type |= NLP_FABRIC; + } + } + + phba->fc_stat.elsRcvFrame++; + elsiocb->context1 = ndlp; + elsiocb->context2 = mp; + + if ((cmd & ELS_CMD_MASK) == ELS_CMD_RSCN) { + cmd &= ELS_CMD_MASK; + } + /* ELS command received from NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0112 ELS command x%x received from NPORT x%x " + "Data: x%x\n", phba->brd_no, cmd, did, phba->hba_state); + + switch (cmd) { + case ELS_CMD_PLOGI: + phba->fc_stat.elsRcvPLOGI++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PLOGI); + break; + case ELS_CMD_FLOGI: + phba->fc_stat.elsRcvFLOGI++; + lpfc_els_rcv_flogi(phba, elsiocb, ndlp, newnode); + if (newnode) { + mempool_free( ndlp, phba->nlp_mem_pool); + } + break; + case ELS_CMD_LOGO: + phba->fc_stat.elsRcvLOGO++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_LOGO); + break; + case ELS_CMD_PRLO: + phba->fc_stat.elsRcvPRLO++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PRLO); + break; + case ELS_CMD_RSCN: + phba->fc_stat.elsRcvRSCN++; + lpfc_els_rcv_rscn(phba, elsiocb, ndlp, newnode); + if (newnode) { + mempool_free( ndlp, phba->nlp_mem_pool); + } + break; + case ELS_CMD_ADISC: + phba->fc_stat.elsRcvADISC++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_ADISC); + break; + case ELS_CMD_PDISC: + phba->fc_stat.elsRcvPDISC++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PDISC); + break; + case ELS_CMD_FARPR: + phba->fc_stat.elsRcvFARPR++; + lpfc_els_rcv_farpr(phba, elsiocb, ndlp); + break; + case ELS_CMD_FARP: + phba->fc_stat.elsRcvFARP++; + lpfc_els_rcv_farp(phba, elsiocb, ndlp); + break; + case ELS_CMD_FAN: + phba->fc_stat.elsRcvFAN++; + lpfc_els_rcv_fan(phba, elsiocb, ndlp); + break; + case ELS_CMD_RRQ: + phba->fc_stat.elsRcvRRQ++; + lpfc_els_rcv_rrq(phba, elsiocb, ndlp); + break; + case ELS_CMD_PRLI: + phba->fc_stat.elsRcvPRLI++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PRLI); + break; + case ELS_CMD_RNID: + phba->fc_stat.elsRcvRNID++; + lpfc_els_rcv_rnid(phba, elsiocb, ndlp); + break; + default: + /* Unsupported ELS command, reject */ + rjt_err = LSEXP_NOTHING_MORE; + + /* Unknown ELS command received from NPORT */ + lpfc_printf_log(phba, KERN_ERR, LOG_ELS, + "%d:0115 Unknown ELS command x%x received from " + "NPORT x%x\n", phba->brd_no, cmd, did); + if (newnode) { + mempool_free( ndlp, phba->nlp_mem_pool); + } + break; + } + + /* check if need to LS_RJT received ELS cmd */ + if (rjt_err) { + stat.un.b.lsRjtRsvd0 = 0; + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = rjt_err; + stat.un.b.vendorUnique = 0; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, elsiocb, ndlp); + } + + if (elsiocb->context2) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } +dropit: + /* check if need to drop received ELS cmd */ + if (drop_cmd == 1) { + lpfc_printf_log(phba, KERN_ERR, LOG_ELS, + "%d:0111 Dropping received ELS cmd " + "Data: x%x x%x\n", phba->brd_no, + icmd->ulpStatus, icmd->un.ulpWord[4]); + phba->fc_stat.elsRcvDrop++; + } + return; +} --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_ct.c 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_ct.c 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,1235 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_ct.c 1.150.2.2 2005/06/13 17:16:09EDT sf_support Exp $ + * + * Fibre Channel SCSI LAN Device Driver CT support + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" +#include "lpfc_version.h" + + +#define HBA_PORTSPEED_UNKNOWN 0 /* Unknown - transceiver + * incapable of reporting */ +#define HBA_PORTSPEED_1GBIT 1 /* 1 GBit/sec */ +#define HBA_PORTSPEED_2GBIT 2 /* 2 GBit/sec */ +#define HBA_PORTSPEED_4GBIT 8 /* 4 GBit/sec */ +#define HBA_PORTSPEED_8GBIT 16 /* 8 GBit/sec */ +#define HBA_PORTSPEED_10GBIT 4 /* 10 GBit/sec */ +#define HBA_PORTSPEED_NOT_NEGOTIATED 5 /* Speed not established */ + +#define FOURBYTES 4 + + +static char *lpfc_release_version = LPFC_DRIVER_VERSION; + +/* + * lpfc_ct_unsol_event + */ +void +lpfc_ct_unsol_event(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, struct lpfc_iocbq * piocbq) +{ + + struct lpfc_iocbq *next_piocbq; + struct lpfc_dmabuf *pmbuf = NULL; + struct lpfc_dmabuf *matp, *next_matp; + uint32_t ctx = 0, size = 0, cnt = 0; + IOCB_t *icmd = &piocbq->iocb; + IOCB_t *save_icmd = icmd; + int i, status, go_exit = 0; + struct list_head head; + + if ((icmd->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((icmd->un.ulpWord[4] & 0xff) == IOERR_RCV_BUFFER_WAITING)) { + /* Not enough posted buffers; Try posting more buffers */ + phba->fc_stat.NoRcvBuf++; + lpfc_post_buffer(phba, pring, 0, 1); + return; + } + + /* If there are no BDEs associated with this IOCB, + * there is nothing to do. + */ + if (icmd->ulpBdeCount == 0) + return; + + INIT_LIST_HEAD(&head); + list_add_tail(&head, &piocbq->list); + list_for_each_entry_safe(piocbq, next_piocbq, &head, list) { + icmd = &piocbq->iocb; + if (ctx == 0) + ctx = (uint32_t) (icmd->ulpContext); + if (icmd->ulpBdeCount == 0) + continue; + + for (i = 0; i < icmd->ulpBdeCount; i++) { + matp = lpfc_sli_ringpostbuf_get(phba, pring, + getPaddr(icmd->un. + cont64[i]. + addrHigh, + icmd->un. + cont64[i]. + addrLow)); + if (!matp) { + /* Insert lpfc log message here */ + lpfc_post_buffer(phba, pring, cnt, 1); + go_exit = 1; + goto ct_unsol_event_exit_piocbq; + } + + /* Typically for Unsolicited CT requests */ + if (!pmbuf) { + pmbuf = matp; + INIT_LIST_HEAD(&pmbuf->list); + } else + list_add_tail(&matp->list, &pmbuf->list); + + size += icmd->un.cont64[i].tus.f.bdeSize; + cnt++; + } + + icmd->ulpBdeCount = 0; + } + + lpfc_post_buffer(phba, pring, cnt, 1); + if (save_icmd->ulpStatus) { + go_exit = 1; + } +ct_unsol_event_exit_piocbq: + list_del(&head); + /* + * if not early-exiting and there is pmbuf, + * then do FC_REG_CT_EVENT for libdfc + */ + if (!go_exit && pmbuf) { + status = lpfc_put_event(phba, FC_REG_CT_EVENT, ctx, + (void *)pmbuf, size, 0); + if (status) + return; + } + if (pmbuf) { + list_for_each_entry_safe(matp, next_matp, &pmbuf->list, list) { + lpfc_mbuf_free(phba, matp->virt, matp->phys); + list_del(&matp->list); + kfree(matp); + } + lpfc_mbuf_free(phba, pmbuf->virt, pmbuf->phys); + kfree(pmbuf); + } + return; +} + +static void +lpfc_free_ct_rsp(struct lpfc_hba * phba, struct lpfc_dmabuf * mlist) +{ + struct lpfc_dmabuf *mlast, *next_mlast; + + list_for_each_entry_safe(mlast, next_mlast, &mlist->list, list) { + lpfc_mbuf_free(phba, mlast->virt, mlast->phys); + list_del(&mlast->list); + kfree(mlast); + } + lpfc_mbuf_free(phba, mlist->virt, mlist->phys); + kfree(mlist); + return; +} + +static struct lpfc_dmabuf * +lpfc_alloc_ct_rsp(struct lpfc_hba * phba, int cmdcode, struct ulp_bde64 * bpl, + uint32_t size, int *entries) +{ + struct lpfc_dmabuf *mlist = NULL; + struct lpfc_dmabuf *mp; + int cnt, i = 0; + + /* We get chucks of FCELSSIZE */ + cnt = size > FCELSSIZE ? FCELSSIZE: size; + + while (size) { + /* Allocate buffer for rsp payload */ + mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_ATOMIC); + if (!mp) { + if (mlist) + lpfc_free_ct_rsp(phba, mlist); + return NULL; + } + + INIT_LIST_HEAD(&mp->list); + + if (cmdcode == be16_to_cpu(SLI_CTNS_GID_FT)) + mp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(mp->phys)); + else + mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys)); + + if (!mp->virt) { + kfree(mp); + lpfc_free_ct_rsp(phba, mlist); + return NULL; + } + + /* Queue it to a linked list */ + if (!mlist) + mlist = mp; + else + list_add_tail(&mp->list, &mlist->list); + + bpl->tus.f.bdeFlags = BUFF_USE_RCV; + /* build buffer ptr list for IOCB */ + bpl->addrLow = le32_to_cpu( putPaddrLow(mp->phys) ); + bpl->addrHigh = le32_to_cpu( putPaddrHigh(mp->phys) ); + bpl->tus.f.bdeSize = (uint16_t) cnt; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + bpl++; + + i++; + size -= cnt; + } + + *entries = i; + return mlist; +} + +static int +lpfc_gen_req(struct lpfc_hba *phba, struct lpfc_dmabuf *bmp, + struct lpfc_dmabuf *inp, struct lpfc_dmabuf *outp, + void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *), + struct lpfc_nodelist *ndlp, uint32_t usr_flg, uint32_t num_entry, + uint32_t tmo) +{ + + struct lpfc_sli *psli = &phba->sli; + struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING]; + IOCB_t *icmd; + struct lpfc_iocbq *geniocb; + + /* Allocate buffer for command iocb */ + geniocb = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC); + if (!geniocb) { + return 1; + } + memset(geniocb, 0, sizeof (struct lpfc_iocbq)); + icmd = &geniocb->iocb; + + icmd->un.genreq64.bdl.ulpIoTag32 = 0; + icmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys); + icmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys); + icmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BDL; + icmd->un.genreq64.bdl.bdeSize = (num_entry * sizeof (struct ulp_bde64)); + + if (usr_flg) + geniocb->context3 = NULL; + else + geniocb->context3 = (uint8_t *) bmp; + + /* Save for completion so we can release these resources */ + geniocb->context1 = (uint8_t *) inp; + geniocb->context2 = (uint8_t *) outp; + + /* Fill in payload, bp points to frame payload */ + icmd->ulpCommand = CMD_GEN_REQUEST64_CR; + + icmd->ulpIoTag = lpfc_sli_next_iotag(phba, pring); + + /* Fill in rest of iocb */ + icmd->un.genreq64.w5.hcsw.Fctl = (SI | LA); + icmd->un.genreq64.w5.hcsw.Dfctl = 0; + icmd->un.genreq64.w5.hcsw.Rctl = FC_UNSOL_CTL; + icmd->un.genreq64.w5.hcsw.Type = FC_COMMON_TRANSPORT_ULP; + + if (!tmo) + tmo = (2 * phba->fc_ratov) + 1; + icmd->ulpTimeout = tmo; + icmd->ulpBdeCount = 1; + icmd->ulpLe = 1; + icmd->ulpClass = CLASS3; + icmd->ulpContext = ndlp->nlp_rpi; + + /* Issue GEN REQ IOCB for NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0119 Issue GEN REQ IOCB for NPORT x%x " + "Data: x%x x%x\n", phba->brd_no, icmd->un.ulpWord[5], + icmd->ulpIoTag, phba->hba_state); + geniocb->iocb_cmpl = cmpl; + geniocb->drvrTimeout = icmd->ulpTimeout + LPFC_DRVR_TIMEOUT; + if (lpfc_sli_issue_iocb(phba, pring, geniocb, 0) == IOCB_ERROR) { + mempool_free( geniocb, phba->iocb_mem_pool); + return 1; + } + + return 0; +} + +static int +lpfc_ct_cmd(struct lpfc_hba *phba, struct lpfc_dmabuf *inmp, + struct lpfc_dmabuf *bmp, struct lpfc_nodelist *ndlp, + void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *), + uint32_t rsp_size) +{ + struct ulp_bde64 *bpl = (struct ulp_bde64 *) bmp->virt; + struct lpfc_dmabuf *outmp; + int cnt = 0, status; + int cmdcode = ((struct lpfc_sli_ct_request *) inmp->virt)-> + CommandResponse.bits.CmdRsp; + + bpl++; /* Skip past ct request */ + + /* Put buffer(s) for ct rsp in bpl */ + outmp = lpfc_alloc_ct_rsp(phba, cmdcode, bpl, rsp_size, &cnt); + if (!outmp) + return -ENOMEM; + + status = lpfc_gen_req(phba, bmp, inmp, outmp, cmpl, ndlp, 0, + cnt+1, 0); + if (status) { + lpfc_free_ct_rsp(phba, outmp); + return -ENOMEM; + } + return 0; +} + +static int +lpfc_ns_rsp(struct lpfc_hba * phba, struct lpfc_dmabuf * mp, uint32_t Size) +{ + struct lpfc_sli_ct_request *Response = + (struct lpfc_sli_ct_request *) mp->virt; + struct lpfc_nodelist *ndlp = NULL; + struct lpfc_dmabuf *mlast, *next_mp; + uint32_t *ctptr = (uint32_t *) & Response->un.gid.PortType; + uint32_t Did; + uint32_t CTentry; + int Cnt; + struct list_head head; + + lpfc_set_disctmo(phba); + + Cnt = Size > FCELSSIZE ? FCELSSIZE : Size; + + list_add_tail(&head, &mp->list); + list_for_each_entry_safe(mp, next_mp, &head, list) { + mlast = mp; + Size -= Cnt; + + if (!ctptr) + ctptr = (uint32_t *) mlast->virt; + else + Cnt -= 16; /* subtract length of CT header */ + + /* Loop through entire NameServer list of DIDs */ + while (Cnt) { + + /* Get next DID from NameServer List */ + CTentry = *ctptr++; + Did = ((be32_to_cpu(CTentry)) & Mask_DID); + + ndlp = NULL; + if (Did != phba->fc_myDID) { + /* Check for rscn processing or not */ + ndlp = lpfc_setup_disc_node(phba, Did); + } + /* Mark all node table entries that are in the + Nameserver */ + if (ndlp) { + /* NameServer Rsp */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0238 Process x%x NameServer" + " Rsp Data: x%x x%x x%x\n", + phba->brd_no, + Did, ndlp->nlp_flag, + phba->fc_flag, + phba->fc_rscn_id_cnt); + } else { + /* NameServer Rsp */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0239 Skip x%x NameServer " + "Rsp Data: x%x x%x x%x\n", + phba->brd_no, + Did, Size, phba->fc_flag, + phba->fc_rscn_id_cnt); + } + + if (CTentry & (be32_to_cpu(SLI_CT_LAST_ENTRY))) + goto nsout1; + Cnt -= sizeof (uint32_t); + } + ctptr = NULL; + + } + +nsout1: + list_del(&head); + + /* Here we are finished in the case RSCN */ + if (phba->hba_state == LPFC_HBA_READY) { + lpfc_els_flush_rscn(phba); + phba->fc_flag |= FC_RSCN_MODE; /* we are still in RSCN mode */ + } + return 0; +} + + + + +static void +lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + struct lpfc_dmabuf *inp; + struct lpfc_dmabuf *outp; + struct lpfc_nodelist *ndlp; + struct lpfc_sli_ct_request *CTrsp; + + psli = &phba->sli; + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + inp = (struct lpfc_dmabuf *) cmdiocb->context1; + outp = (struct lpfc_dmabuf *) cmdiocb->context2; + bmp = (struct lpfc_dmabuf *) cmdiocb->context3; + + irsp = &rspiocb->iocb; + if (irsp->ulpStatus) { + if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((irsp->un.ulpWord[4] == IOERR_SLI_DOWN) || + (irsp->un.ulpWord[4] == IOERR_SLI_ABORTED))) { + goto out; + } + + /* Check for retry */ + if (phba->fc_ns_retry < LPFC_MAX_NS_RETRY) { + phba->fc_ns_retry++; + /* CT command is being retried */ + ndlp = + lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED, + NameServer_DID); + if (ndlp) { + if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT) == + 0) { + goto out; + } + } + } + } else { + /* Good status, continue checking */ + CTrsp = (struct lpfc_sli_ct_request *) outp->virt; + if (CTrsp->CommandResponse.bits.CmdRsp == + be16_to_cpu(SLI_CT_RESPONSE_FS_ACC)) { + lpfc_ns_rsp(phba, outp, + (uint32_t) (irsp->un.genreq64.bdl.bdeSize)); + } else if (CTrsp->CommandResponse.bits.CmdRsp == + be16_to_cpu(SLI_CT_RESPONSE_FS_RJT)) { + /* NameServer Rsp Error */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0240 NameServer Rsp Error " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + CTrsp->CommandResponse.bits.CmdRsp, + (uint32_t) CTrsp->ReasonCode, + (uint32_t) CTrsp->Explanation, + phba->fc_flag); + } else { + /* NameServer Rsp Error */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0241 NameServer Rsp Error " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + CTrsp->CommandResponse.bits.CmdRsp, + (uint32_t) CTrsp->ReasonCode, + (uint32_t) CTrsp->Explanation, + phba->fc_flag); + } + } + /* Link up / RSCN discovery */ + lpfc_disc_start(phba); +out: + lpfc_free_ct_rsp(phba, outp); + lpfc_mbuf_free(phba, inp->virt, inp->phys); + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); + kfree(inp); + kfree(bmp); + mempool_free( cmdiocb, phba->iocb_mem_pool); + return; +} + +static void +lpfc_cmpl_ct_cmd_rft_id(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + struct lpfc_dmabuf *inp; + struct lpfc_dmabuf *outp; + IOCB_t *irsp; + struct lpfc_sli_ct_request *CTrsp; + + psli = &phba->sli; + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + inp = (struct lpfc_dmabuf *) cmdiocb->context1; + outp = (struct lpfc_dmabuf *) cmdiocb->context2; + bmp = (struct lpfc_dmabuf *) cmdiocb->context3; + irsp = &rspiocb->iocb; + + CTrsp = (struct lpfc_sli_ct_request *) outp->virt; + + /* RFT request completes status CmdRsp */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0209 RFT request completes ulpStatus x%x " + "CmdRsp x%x\n", phba->brd_no, irsp->ulpStatus, + CTrsp->CommandResponse.bits.CmdRsp); + + lpfc_free_ct_rsp(phba, outp); + lpfc_mbuf_free(phba, inp->virt, inp->phys); + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); + kfree(inp); + kfree(bmp); + mempool_free( cmdiocb, phba->iocb_mem_pool); + return; +} + +static void +lpfc_cmpl_ct_cmd_rnn_id(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + lpfc_cmpl_ct_cmd_rft_id(phba, cmdiocb, rspiocb); + return; +} + +static void +lpfc_cmpl_ct_cmd_rsnn_nn(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + lpfc_cmpl_ct_cmd_rft_id(phba, cmdiocb, rspiocb); + return; +} + +static void +lpfc_get_hba_sym_node_name(struct lpfc_hba * phba, uint8_t * symbp) +{ + char fwrev[16]; + + lpfc_decode_firmware_rev(phba, fwrev, 0); + + if (phba->Port[0]) { + sprintf(symbp, "Emulex %s Port %s FV%s DV%s", phba->ModelName, + phba->Port, fwrev, lpfc_release_version); + } else { + sprintf(symbp, "Emulex %s FV%s DV%s", phba->ModelName, + fwrev, lpfc_release_version); + } +} + +/* + * lpfc_ns_cmd + * Description: + * Issue Cmd to NameServer + * SLI_CTNS_GID_FT + * LI_CTNS_RFT_ID + */ +int +lpfc_ns_cmd(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, int cmdcode) +{ + struct lpfc_dmabuf *mp, *bmp; + struct lpfc_sli_ct_request *CtReq; + struct ulp_bde64 *bpl; + void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *) = NULL; + uint32_t rsp_size = 1024; + + /* fill in BDEs for command */ + /* Allocate buffer for command payload */ + mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (!mp) + goto ns_cmd_exit; + + INIT_LIST_HEAD(&mp->list); + mp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(mp->phys)); + if (!mp->virt) + goto ns_cmd_free_mp; + + /* Allocate buffer for Buffer ptr list */ + bmp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (!bmp) + goto ns_cmd_free_mpvirt; + + INIT_LIST_HEAD(&bmp->list); + bmp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(bmp->phys)); + if (!bmp->virt) + goto ns_cmd_free_bmp; + + /* NameServer Req */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0236 NameServer Req Data: x%x x%x x%x\n", + phba->brd_no, cmdcode, phba->fc_flag, + phba->fc_rscn_id_cnt); + + bpl = (struct ulp_bde64 *) bmp->virt; + memset(bpl, 0, sizeof(struct ulp_bde64)); + bpl->addrHigh = le32_to_cpu( putPaddrHigh(mp->phys) ); + bpl->addrLow = le32_to_cpu( putPaddrLow(mp->phys) ); + bpl->tus.f.bdeFlags = 0; + if (cmdcode == SLI_CTNS_GID_FT) + bpl->tus.f.bdeSize = GID_REQUEST_SZ; + else if (cmdcode == SLI_CTNS_RFT_ID) + bpl->tus.f.bdeSize = RFT_REQUEST_SZ; + else if (cmdcode == SLI_CTNS_RNN_ID) + bpl->tus.f.bdeSize = RNN_REQUEST_SZ; + else if (cmdcode == SLI_CTNS_RSNN_NN) + bpl->tus.f.bdeSize = RSNN_REQUEST_SZ; + else + bpl->tus.f.bdeSize = 0; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + + CtReq = (struct lpfc_sli_ct_request *) mp->virt; + memset(CtReq, 0, sizeof (struct lpfc_sli_ct_request)); + CtReq->RevisionId.bits.Revision = SLI_CT_REVISION; + CtReq->RevisionId.bits.InId = 0; + CtReq->FsType = SLI_CT_DIRECTORY_SERVICE; + CtReq->FsSubType = SLI_CT_DIRECTORY_NAME_SERVER; + CtReq->CommandResponse.bits.Size = 0; + switch (cmdcode) { + case SLI_CTNS_GID_FT: + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_CTNS_GID_FT); + CtReq->un.gid.Fc4Type = SLI_CTPT_FCP; + if (phba->hba_state < LPFC_HBA_READY) + phba->hba_state = LPFC_NS_QRY; + lpfc_set_disctmo(phba); + cmpl = lpfc_cmpl_ct_cmd_gid_ft; + rsp_size = FC_MAX_NS_RSP; + break; + + case SLI_CTNS_RFT_ID: + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_CTNS_RFT_ID); + CtReq->un.rft.PortId = be32_to_cpu(phba->fc_myDID); + CtReq->un.rft.fcpReg = 1; + cmpl = lpfc_cmpl_ct_cmd_rft_id; + break; + + case SLI_CTNS_RNN_ID: + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_CTNS_RNN_ID); + CtReq->un.rnn.PortId = be32_to_cpu(phba->fc_myDID); + memcpy(CtReq->un.rnn.wwnn, &phba->fc_nodename, + sizeof (struct lpfc_name)); + cmpl = lpfc_cmpl_ct_cmd_rnn_id; + break; + + case SLI_CTNS_RSNN_NN: + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_CTNS_RSNN_NN); + memcpy(CtReq->un.rsnn.wwnn, &phba->fc_nodename, + sizeof (struct lpfc_name)); + lpfc_get_hba_sym_node_name(phba, CtReq->un.rsnn.symbname); + CtReq->un.rsnn.len = strlen(CtReq->un.rsnn.symbname); + cmpl = lpfc_cmpl_ct_cmd_rsnn_nn; + break; + } + + if (!lpfc_ct_cmd(phba, mp, bmp, ndlp, cmpl, rsp_size)) + /* On success, The cmpl function will free the buffers */ + return 0; + + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); +ns_cmd_free_bmp: + kfree(bmp); +ns_cmd_free_mpvirt: + lpfc_mbuf_free(phba, mp->virt, mp->phys); +ns_cmd_free_mp: + kfree(mp); +ns_cmd_exit: + return 1; +} + +static void +lpfc_cmpl_ct_cmd_fdmi(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, struct lpfc_iocbq * rspiocb) +{ + struct lpfc_dmabuf *bmp = cmdiocb->context3; + struct lpfc_dmabuf *inp = cmdiocb->context1; + struct lpfc_dmabuf *outp = cmdiocb->context2; + struct lpfc_sli_ct_request *CTrsp = outp->virt; + struct lpfc_sli_ct_request *CTcmd = inp->virt; + struct lpfc_nodelist *ndlp; + uint16_t fdmi_cmd = CTcmd->CommandResponse.bits.CmdRsp; + uint16_t fdmi_rsp = CTrsp->CommandResponse.bits.CmdRsp; + + ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, FDMI_DID); + if (fdmi_rsp == be16_to_cpu(SLI_CT_RESPONSE_FS_RJT)) { + /* FDMI rsp failed */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0220 FDMI rsp failed Data: x%x\n", + phba->brd_no, + be16_to_cpu(fdmi_cmd)); + } + + switch (be16_to_cpu(fdmi_cmd)) { + case SLI_MGMT_RHBA: + lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_RPA); + break; + + case SLI_MGMT_RPA: + break; + + case SLI_MGMT_DHBA: + lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_DPRT); + break; + + case SLI_MGMT_DPRT: + lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_RHBA); + break; + } + + lpfc_free_ct_rsp(phba, outp); + lpfc_mbuf_free(phba, inp->virt, inp->phys); + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); + kfree(inp); + kfree(bmp); + mempool_free(cmdiocb, phba->iocb_mem_pool); + return; +} +int +lpfc_fdmi_cmd(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, int cmdcode) +{ + struct lpfc_dmabuf *mp, *bmp; + struct lpfc_sli_ct_request *CtReq; + struct ulp_bde64 *bpl; + uint32_t size; + REG_HBA *rh; + PORT_ENTRY *pe; + REG_PORT_ATTRIBUTE *pab; + ATTRIBUTE_BLOCK *ab; + ATTRIBUTE_ENTRY *ae; + void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *); + + + /* fill in BDEs for command */ + /* Allocate buffer for command payload */ + mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (!mp) + goto fdmi_cmd_exit; + + mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys)); + if (!mp->virt) + goto fdmi_cmd_free_mp; + + /* Allocate buffer for Buffer ptr list */ + bmp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (!bmp) + goto fdmi_cmd_free_mpvirt; + + bmp->virt = lpfc_mbuf_alloc(phba, 0, &(bmp->phys)); + if (!bmp->virt) + goto fdmi_cmd_free_bmp; + + INIT_LIST_HEAD(&mp->list); + INIT_LIST_HEAD(&bmp->list); + + /* FDMI request */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0218 FDMI Request Data: x%x x%x x%x\n", + phba->brd_no, + phba->fc_flag, phba->hba_state, cmdcode); + + CtReq = (struct lpfc_sli_ct_request *) mp->virt; + + memset(CtReq, 0, sizeof(struct lpfc_sli_ct_request)); + CtReq->RevisionId.bits.Revision = SLI_CT_REVISION; + CtReq->RevisionId.bits.InId = 0; + + CtReq->FsType = SLI_CT_MANAGEMENT_SERVICE; + CtReq->FsSubType = SLI_CT_FDMI_Subtypes; + size = 0; + + switch (cmdcode) { + case SLI_MGMT_RHBA: + { + lpfc_vpd_t *vp = &phba->vpd; + uint32_t i, j, incr; + int len; + + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_MGMT_RHBA); + CtReq->CommandResponse.bits.Size = 0; + rh = (REG_HBA *) & CtReq->un.PortID; + memcpy(&rh->hi.PortName, &phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + /* One entry (port) per adapter */ + rh->rpl.EntryCnt = be32_to_cpu(1); + memcpy(&rh->rpl.pe, &phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + + /* point to the HBA attribute block */ + size = 2 * sizeof (struct lpfc_name) + FOURBYTES; + ab = (ATTRIBUTE_BLOCK *) ((uint8_t *) rh + size); + ab->EntryCnt = 0; + + /* Point to the beginning of the first HBA attribute + entry */ + /* #1 HBA attribute entry */ + size += FOURBYTES; + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(NODE_NAME); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + + sizeof (struct lpfc_name)); + memcpy(&ae->un.NodeName, &phba->fc_sparam.nodeName, + sizeof (struct lpfc_name)); + ab->EntryCnt++; + size += FOURBYTES + sizeof (struct lpfc_name); + + /* #2 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(MANUFACTURER); + strcpy(ae->un.Manufacturer, "Emulex Corporation"); + len = strlen(ae->un.Manufacturer); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #3 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(SERIAL_NUMBER); + strcpy(ae->un.SerialNumber, phba->SerialNumber); + len = strlen(ae->un.SerialNumber); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #4 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(MODEL); + strcpy(ae->un.Model, phba->ModelName); + len = strlen(ae->un.Model); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #5 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(MODEL_DESCRIPTION); + strcpy(ae->un.ModelDescription, phba->ModelDesc); + len = strlen(ae->un.ModelDescription); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #6 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(HARDWARE_VERSION); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 8); + /* Convert JEDEC ID to ascii for hardware version */ + incr = vp->rev.biuRev; + for (i = 0; i < 8; i++) { + j = (incr & 0xf); + if (j <= 9) + ae->un.HardwareVersion[7 - i] = + (char)((uint8_t) 0x30 + + (uint8_t) j); + else + ae->un.HardwareVersion[7 - i] = + (char)((uint8_t) 0x61 + + (uint8_t) (j - 10)); + incr = (incr >> 4); + } + ab->EntryCnt++; + size += FOURBYTES + 8; + + /* #7 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(DRIVER_VERSION); + strcpy(ae->un.DriverVersion, lpfc_release_version); + len = strlen(ae->un.DriverVersion); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #8 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(OPTION_ROM_VERSION); + strcpy(ae->un.OptionROMVersion, phba->OptionROMVersion); + len = strlen(ae->un.OptionROMVersion); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #9 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(FIRMWARE_VERSION); + lpfc_decode_firmware_rev(phba, ae->un.FirmwareVersion, + 1); + len = strlen(ae->un.FirmwareVersion); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #10 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(OS_NAME_VERSION); + sprintf(ae->un.OsNameVersion, "%s %s %s", + system_utsname.sysname, system_utsname.release, + system_utsname.version); + len = strlen(ae->un.OsNameVersion); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #11 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(MAX_CT_PAYLOAD_LEN); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4); + ae->un.MaxCTPayloadLen = (65 * 4096); + ab->EntryCnt++; + size += FOURBYTES + 4; + + ab->EntryCnt = be32_to_cpu(ab->EntryCnt); + /* Total size */ + size = GID_REQUEST_SZ - 4 + size; + } + break; + + case SLI_MGMT_RPA: + { + lpfc_vpd_t *vp; + struct serv_parm *hsp; + int len; + + vp = &phba->vpd; + + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_MGMT_RPA); + CtReq->CommandResponse.bits.Size = 0; + pab = (REG_PORT_ATTRIBUTE *) & CtReq->un.PortID; + size = sizeof (struct lpfc_name) + FOURBYTES; + memcpy((uint8_t *) & pab->PortName, + (uint8_t *) & phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + pab->ab.EntryCnt = 0; + + /* #1 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size); + ae->ad.bits.AttrType = be16_to_cpu(SUPPORTED_FC4_TYPES); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 32); + ae->un.SupportFC4Types[2] = 1; + ae->un.SupportFC4Types[7] = 1; + pab->ab.EntryCnt++; + size += FOURBYTES + 32; + + /* #2 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size); + ae->ad.bits.AttrType = be16_to_cpu(SUPPORTED_SPEED); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4); + if (FC_JEDEC_ID(vp->rev.biuRev) == VIPER_JEDEC_ID) + ae->un.SupportSpeed = HBA_PORTSPEED_10GBIT; + else if (FC_JEDEC_ID(vp->rev.biuRev) == HELIOS_JEDEC_ID) + ae->un.SupportSpeed = HBA_PORTSPEED_4GBIT; + else if ((FC_JEDEC_ID(vp->rev.biuRev) == + CENTAUR_2G_JEDEC_ID) + || (FC_JEDEC_ID(vp->rev.biuRev) == + PEGASUS_JEDEC_ID) + || (FC_JEDEC_ID(vp->rev.biuRev) == + THOR_JEDEC_ID)) + ae->un.SupportSpeed = HBA_PORTSPEED_2GBIT; + else + ae->un.SupportSpeed = HBA_PORTSPEED_1GBIT; + pab->ab.EntryCnt++; + size += FOURBYTES + 4; + + /* #3 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size); + ae->ad.bits.AttrType = be16_to_cpu(PORT_SPEED); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4); + switch(phba->fc_linkspeed) { + case LA_1GHZ_LINK: + ae->un.PortSpeed = HBA_PORTSPEED_1GBIT; + break; + case LA_2GHZ_LINK: + ae->un.PortSpeed = HBA_PORTSPEED_2GBIT; + break; + case LA_4GHZ_LINK: + ae->un.PortSpeed = HBA_PORTSPEED_4GBIT; + break; + default: + ae->un.PortSpeed = + HBA_PORTSPEED_UNKNOWN; + break; + } + pab->ab.EntryCnt++; + size += FOURBYTES + 4; + + /* #4 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size); + ae->ad.bits.AttrType = be16_to_cpu(MAX_FRAME_SIZE); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4); + hsp = (struct serv_parm *) & phba->fc_sparam; + ae->un.MaxFrameSize = + (((uint32_t) hsp->cmn. + bbRcvSizeMsb) << 8) | (uint32_t) hsp->cmn. + bbRcvSizeLsb; + pab->ab.EntryCnt++; + size += FOURBYTES + 4; + + /* #5 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size); + ae->ad.bits.AttrType = be16_to_cpu(OS_DEVICE_NAME); + strcpy((char *)ae->un.OsDeviceName, LPFC_DRIVER_NAME); + len = strlen((char *)ae->un.OsDeviceName); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + pab->ab.EntryCnt++; + size += FOURBYTES + len; + + if (phba->cfg_fdmi_on == 2) { + /* #6 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + + size); + ae->ad.bits.AttrType = be16_to_cpu(HOST_NAME); + sprintf(ae->un.HostName, "%s", + system_utsname.nodename); + len = strlen(ae->un.HostName); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = + be16_to_cpu(FOURBYTES + len); + pab->ab.EntryCnt++; + size += FOURBYTES + len; + } + + pab->ab.EntryCnt = be32_to_cpu(pab->ab.EntryCnt); + /* Total size */ + size = GID_REQUEST_SZ - 4 + size; + } + break; + + case SLI_MGMT_DHBA: + CtReq->CommandResponse.bits.CmdRsp = be16_to_cpu(SLI_MGMT_DHBA); + CtReq->CommandResponse.bits.Size = 0; + pe = (PORT_ENTRY *) & CtReq->un.PortID; + memcpy((uint8_t *) & pe->PortName, + (uint8_t *) & phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + size = GID_REQUEST_SZ - 4 + sizeof (struct lpfc_name); + break; + + case SLI_MGMT_DPRT: + CtReq->CommandResponse.bits.CmdRsp = be16_to_cpu(SLI_MGMT_DPRT); + CtReq->CommandResponse.bits.Size = 0; + pe = (PORT_ENTRY *) & CtReq->un.PortID; + memcpy((uint8_t *) & pe->PortName, + (uint8_t *) & phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + size = GID_REQUEST_SZ - 4 + sizeof (struct lpfc_name); + break; + } + + bpl = (struct ulp_bde64 *) bmp->virt; + bpl->addrHigh = le32_to_cpu( putPaddrHigh(mp->phys) ); + bpl->addrLow = le32_to_cpu( putPaddrLow(mp->phys) ); + bpl->tus.f.bdeFlags = 0; + bpl->tus.f.bdeSize = size; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + + cmpl = lpfc_cmpl_ct_cmd_fdmi; + + if (!lpfc_ct_cmd(phba, mp, bmp, ndlp, cmpl, FC_MAX_NS_RSP)) + return 0; + + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); +fdmi_cmd_free_bmp: + kfree(bmp); +fdmi_cmd_free_mpvirt: + lpfc_mbuf_free(phba, mp->virt, mp->phys); +fdmi_cmd_free_mp: + kfree(mp); +fdmi_cmd_exit: + /* Issue FDMI request failed */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0244 Issue FDMI request failed Data: x%x\n", + phba->brd_no, + cmdcode); + return 1; +} + +void +lpfc_fdmi_tmo(unsigned long ptr) +{ + struct lpfc_hba *phba = (struct lpfc_hba *)ptr; + unsigned long iflag; + + spin_lock_irqsave(phba->host->host_lock, iflag); + if (!(phba->work_hba_events & WORKER_FDMI_TMO)) { + phba->work_hba_events |= WORKER_FDMI_TMO; + if (phba->dpc_wait) + up(phba->dpc_wait); + } + spin_unlock_irqrestore(phba->host->host_lock,iflag); +} + +void +lpfc_fdmi_tmo_handler(struct lpfc_hba *phba) +{ + struct lpfc_nodelist *ndlp; + + spin_lock_irq(phba->host->host_lock); + if (!(phba->work_hba_events & WORKER_FDMI_TMO)) { + spin_unlock_irq(phba->host->host_lock); + return; + } + ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, FDMI_DID); + if (ndlp) { + if (system_utsname.nodename[0] != '\0') { + lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_DHBA); + } else { + mod_timer(&phba->fc_fdmitmo, jiffies + HZ * 60); + } + } + spin_unlock_irq(phba->host->host_lock); + return; +} + + +void +lpfc_decode_firmware_rev(struct lpfc_hba * phba, char *fwrevision, int flag) +{ + struct lpfc_sli *psli = &phba->sli; + lpfc_vpd_t *vp = &phba->vpd; + uint32_t b1, b2, b3, b4, i, rev; + char c; + uint32_t *ptr, str[4]; + uint8_t *fwname; + + if (vp->rev.rBit) { + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) + rev = vp->rev.sli2FwRev; + else + rev = vp->rev.sli1FwRev; + + b1 = (rev & 0x0000f000) >> 12; + b2 = (rev & 0x00000f00) >> 8; + b3 = (rev & 0x000000c0) >> 6; + b4 = (rev & 0x00000030) >> 4; + + switch (b4) { + case 0: + c = 'N'; + break; + case 1: + c = 'A'; + break; + case 2: + c = 'B'; + break; + default: + c = 0; + break; + } + b4 = (rev & 0x0000000f); + + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) + fwname = vp->rev.sli2FwName; + else + fwname = vp->rev.sli1FwName; + + for (i = 0; i < 16; i++) + if(fwname[i] == 0x20) + fwname[i] = 0; + + ptr = (uint32_t*)fwname; + + for (i = 0; i < 3; i++) + str[i] = be32_to_cpu(*ptr++); + + if (c == 0) { + if (flag) + sprintf(fwrevision, "%d.%d%d (%s)", + b1, b2, b3, (char *)str); + else + sprintf(fwrevision, "%d.%d%d", b1, + b2, b3); + } else { + if (flag) + sprintf(fwrevision, "%d.%d%d%c%d (%s)", + b1, b2, b3, c, + b4, (char *)str); + else + sprintf(fwrevision, "%d.%d%d%c%d", + b1, b2, b3, c, b4); + } + } else { + rev = vp->rev.smFwRev; + + b1 = (rev & 0xff000000) >> 24; + b2 = (rev & 0x00f00000) >> 20; + b3 = (rev & 0x000f0000) >> 16; + c = (rev & 0x0000ff00) >> 8; + b4 = (rev & 0x000000ff); + + if (flag) + sprintf(fwrevision, "%d.%d%d%c%d ", b1, + b2, b3, c, b4); + else + sprintf(fwrevision, "%d.%d%d%c%d ", b1, + b2, b3, c, b4); + } + return; +} --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_sli.h 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_sli.h 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,218 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_sli.h 1.38.2.2 2005/06/13 17:16:49EDT sf_support Exp $ + */ + +#ifndef _H_LPFC_SLI +#define _H_LPFC_SLI + +#include "lpfc_hw.h" + +/* forward declaration for LPFC_IOCB_t's use */ +struct lpfc_hba; + +/* This structure is used to handle IOCB requests / responses */ +struct lpfc_iocbq { + /* lpfc_iocbqs are used in double linked lists */ + struct list_head list; + IOCB_t iocb; /* IOCB cmd */ + uint8_t retry; /* retry counter for IOCB cmd - if needed */ + uint8_t iocb_flag; +#define LPFC_IO_POLL 1 /* Polling mode iocb */ +#define LPFC_IO_LIBDFC 2 /* libdfc iocb */ +#define LPFC_IO_WAIT 4 +#define LPFC_IO_HIPRI 8 /* High Priority Queue signal flag */ + + uint8_t abort_count; + uint8_t rsvd2; + uint32_t drvrTimeout; /* driver timeout in seconds */ + void *context1; /* caller context information */ + void *context2; /* caller context information */ + void *context3; /* caller context information */ + union { + wait_queue_head_t *hipri_wait_queue; /* High Priority Queue wait + queue */ + struct lpfc_iocbq *rsp_iocb; + struct lpfcMboxq *mbox; + } context_un; + + void (*iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *); + +}; + +#define SLI_IOCB_RET_IOCB 1 /* Return IOCB if cmd ring full */ +#define SLI_IOCB_HIGH_PRIORITY 2 /* High priority command */ + +#define IOCB_SUCCESS 0 +#define IOCB_BUSY 1 +#define IOCB_ERROR 2 +#define IOCB_TIMEDOUT 3 + +typedef struct lpfcMboxq { + /* MBOXQs are used in single linked lists */ + struct list_head list; /* ptr to next mailbox command */ + MAILBOX_t mb; /* Mailbox cmd */ + void *context1; /* caller context information */ + void *context2; /* caller context information */ + + void (*mbox_cmpl) (struct lpfc_hba *, struct lpfcMboxq *); + +} LPFC_MBOXQ_t; + +#define MBX_POLL 1 /* poll mailbox till command done, then + return */ +#define MBX_NOWAIT 2 /* issue command then return immediately */ +#define MBX_STOP_IOCB 4 /* Stop iocb processing till mbox cmds + complete */ + +#define LPFC_MAX_RING_MASK 4 /* max num of rctl/type masks allowed per + ring */ +#define LPFC_MAX_RING 4 /* max num of SLI rings used by driver */ + +/* Structure used to hold SLI ring information */ +struct lpfc_sli_ring { + uint16_t flag; /* ring flags */ +#define LPFC_DEFERRED_RING_EVENT 0x001 /* Deferred processing a ring event */ +#define LPFC_CALL_RING_AVAILABLE 0x002 /* indicates cmd was full */ +#define LPFC_STOP_IOCB_MBX 0x010 /* Stop processing IOCB cmds mbox */ +#define LPFC_STOP_IOCB_EVENT 0x020 /* Stop processing IOCB cmds event */ +#define LPFC_STOP_IOCB_MASK 0x030 /* Stop processing IOCB cmds mask */ + uint16_t abtsiotag; /* tracks next iotag to use for ABTS */ + + uint32_t local_getidx; /* last available cmd index (from cmdGetInx) */ + uint32_t next_cmdidx; /* next_cmd index */ + uint8_t rsvd; + uint8_t ringno; /* ring number */ + uint8_t rspidx; /* current index in response ring */ + uint8_t cmdidx; /* current index in command ring */ + struct lpfc_iocbq ** fast_lookup; /* array of IOCB ptrs indexed by + iotag */ + struct list_head txq; + uint16_t txq_cnt; /* current length of queue */ + uint16_t txq_max; /* max length */ + struct list_head txcmplq; + uint16_t txcmplq_cnt; /* current length of queue */ + uint16_t txcmplq_max; /* max length */ + volatile uint32_t *cmdringaddr; /* virtual address for cmd rings */ + volatile uint32_t *rspringaddr; /* virtual address for rsp rings */ + uint32_t missbufcnt; /* keep track of buffers to post */ + struct list_head postbufq; + uint16_t postbufq_cnt; /* current length of queue */ + uint16_t postbufq_max; /* max length */ + struct list_head iocb_continueq; + uint16_t iocb_continueq_cnt; /* current length of queue */ + uint16_t iocb_continueq_max; /* max length */ +}; + +typedef struct { + uint8_t profile; /* profile associated with ring */ + uint8_t rctl; /* rctl / type pair configured for ring */ + uint8_t type; /* rctl / type pair configured for ring */ + uint8_t rsvd; + /* rcv'd unsol event */ + void (*lpfc_sli_rcv_unsol_event) (struct lpfc_hba *, + struct lpfc_sli_ring *, + struct lpfc_iocbq *); +} LPFC_RING_MASK_t; + +/* Structure used for configuring rings to a specific profile or rctl / type */ +typedef struct { + LPFC_RING_MASK_t prt[LPFC_MAX_RING_MASK]; + uint32_t num_mask; /* number of mask entries in prt array */ + uint32_t iotag_ctr; /* keeps track of the next iotag to use */ + uint32_t iotag_max; /* max iotag value to use */ + uint32_t fast_iotag; /* max fastlookup based iotag */ + uint16_t numCiocb; /* number of command iocb's per ring */ + uint16_t numRiocb; /* number of rsp iocb's per ring */ + /* cmd ring available */ + void (*lpfc_sli_cmd_available) (struct lpfc_hba *, + struct lpfc_sli_ring *); +} LPFC_RING_INIT_t; + +typedef struct { + LPFC_RING_INIT_t ringinit[LPFC_MAX_RING]; /* ring initialization info */ + uint32_t num_rings; + uint32_t sli_flag; +} LPFC_SLI_INIT_t; + +/* Structure used to hold SLI statistical counters and info */ +typedef struct { + uint64_t iocbEvent[LPFC_MAX_RING]; /* IOCB event counters */ + uint64_t iocbCmd[LPFC_MAX_RING]; /* IOCB cmd issued */ + uint64_t iocbRsp[LPFC_MAX_RING]; /* IOCB rsp received */ + uint64_t iocbCmdDelay[LPFC_MAX_RING]; /* IOCB cmd ring delay */ + uint64_t iocbCmdFull[LPFC_MAX_RING]; /* IOCB cmd ring full */ + uint64_t iocbCmdEmpty[LPFC_MAX_RING]; /* IOCB cmd ring is now empty */ + uint64_t iocbRspFull[LPFC_MAX_RING]; /* IOCB rsp ring full */ + uint64_t mboxStatErr; /* Mbox cmds completed status error */ + uint64_t mboxCmd; /* Mailbox commands issued */ + uint64_t sliIntr; /* Count of Host Attention interrupts */ + uint32_t errAttnEvent; /* Error Attn event counters */ + uint32_t linkEvent; /* Link event counters */ + uint32_t mboxEvent; /* Mailbox event counters */ + uint32_t mboxBusy; /* Mailbox cmd busy */ +} LPFC_SLI_STAT_t; + +/* Structure used to hold SLI information */ +struct lpfc_sli { + LPFC_SLI_INIT_t sliinit; /* initialization info */ + /* Additional sli_flags */ +#define LPFC_SLI_MBOX_ACTIVE 0x100 /* HBA mailbox is currently active */ +#define LPFC_SLI2_ACTIVE 0x200 /* SLI2 overlay in firmware is active */ +#define LPFC_PROCESS_LA 0x400 /* Able to process link attention */ + + struct lpfc_sli_ring ring[LPFC_MAX_RING]; + int fcp_ring; /* ring used for FCP initiator commands */ + int next_ring; + + int ip_ring; /* ring used for IP network drv cmds */ + + LPFC_SLI_STAT_t slistat; /* SLI statistical info */ + struct list_head mboxq; + uint16_t mboxq_cnt; /* current length of queue */ + uint16_t mboxq_max; /* max length */ + LPFC_MBOXQ_t *mbox_active; /* active mboxq information */ + + struct timer_list mbox_tmo; /* Hold clk to timeout active mbox + cmd */ + + volatile uint32_t *MBhostaddr; /* virtual address for mbox cmds */ +}; + +/* Given a pointer to the start of the ring, and the slot number of + * the desired iocb entry, calc a pointer to that entry. + * (assume iocb entry size is 32 bytes, or 8 words) + */ +#define IOCB_ENTRY(ring,slot) ((IOCB_t *)(((char *)(ring)) + ((slot) * 32))) + +#define LPFC_SLI_ABORT_IMED 0 /* Immediate abort of IOCB, deque and + call compl routine immediately. */ +#define LPFC_MBOX_TMO 30 /* Sec tmo for outstanding mbox + command */ + +/* Flags for aborting I/Os on tx and txcmpl queues */ +#define LPFC_ABORT_TXQ 1 /* Abort I/Os on txq */ +#define LPFC_ABORT_TXCMPLQ 2 /* Abort I/Os on txcmplq */ +#define LPFC_ABORT_ALLQ 3 /* Abort I/Os both txq and txcmplq */ + +#endif /* _H_LPFC_SLI */ --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_mem.c 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_mem.c 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,204 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_mem.c 1.72.1.2 2005/06/13 17:16:34EDT sf_support Exp $ + */ + +#include +#include +#include +#include + +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_mem.h" + +static void * +lpfc_pool_kmalloc(int gfp_flags, void *data) +{ + return kmalloc((unsigned long)data, gfp_flags); +} + +static void +lpfc_pool_kfree(void *obj, void *data) +{ + kfree(obj); +} + +int +lpfc_mem_alloc(struct lpfc_hba * phba) +{ + struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool; + int i; + + phba->lpfc_scsi_dma_ext_pool = pci_pool_create("lpfc_scsi_dma_ext_pool", + phba->pcidev, LPFC_SCSI_DMA_EXT_SIZE, 8, 0); + if (!phba->lpfc_scsi_dma_ext_pool) + goto fail; + + phba->lpfc_mbuf_pool = pci_pool_create("lpfc_mbuf_pool", phba->pcidev, + LPFC_BPL_SIZE, 8,0); + if (!phba->lpfc_mbuf_pool) + goto fail_free_dma_ext_pool; + + pool->elements = kmalloc(sizeof(struct lpfc_dmabuf) * + LPFC_MBUF_POOL_SIZE, GFP_KERNEL); + pool->max_count = 0; + pool->current_count = 0; + for ( i = 0; i < LPFC_MBUF_POOL_SIZE; i++) { + pool->elements[i].virt = pci_pool_alloc(phba->lpfc_mbuf_pool, + GFP_KERNEL, &pool->elements[i].phys); + if (!pool->elements[i].virt) + goto fail_free_mbuf_pool; + pool->max_count++; + pool->current_count++; + } + + phba->iocb_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE, + lpfc_pool_kmalloc, lpfc_pool_kfree, + (void *)(unsigned long)sizeof(struct lpfc_iocbq)); + if (!phba->iocb_mem_pool) + goto fail_free_mbuf_pool; + + phba->scsibuf_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE, + lpfc_pool_kmalloc, lpfc_pool_kfree, + (void *)(unsigned long)sizeof(struct lpfc_scsi_buf)); + if (!phba->scsibuf_mem_pool) + goto fail_free_iocb_pool; + + phba->mbox_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE, + lpfc_pool_kmalloc, lpfc_pool_kfree, + (void *)(unsigned long)sizeof(LPFC_MBOXQ_t)); + if (!phba->mbox_mem_pool) + goto fail_free_scsibuf_pool; + + phba->nlp_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE, + lpfc_pool_kmalloc, lpfc_pool_kfree, + (void *)(unsigned long)sizeof(struct lpfc_nodelist)); + if (!phba->nlp_mem_pool) + goto fail_free_mbox_pool; + + phba->bind_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE, + lpfc_pool_kmalloc, lpfc_pool_kfree, + (void *)(unsigned long)sizeof(struct lpfc_bindlist)); + if (!phba->bind_mem_pool) + goto fail_free_nlp_pool; + + return 0; + + fail_free_nlp_pool: + mempool_destroy(phba->nlp_mem_pool); + fail_free_mbox_pool: + mempool_destroy(phba->mbox_mem_pool); + fail_free_scsibuf_pool: + mempool_destroy(phba->scsibuf_mem_pool); + fail_free_iocb_pool: + mempool_destroy(phba->iocb_mem_pool); + fail_free_mbuf_pool: + while (--i) + pci_pool_free(phba->lpfc_mbuf_pool, pool->elements[i].virt, + pool->elements[i].phys); + kfree(pool->elements); + pci_pool_destroy(phba->lpfc_mbuf_pool); + fail_free_dma_ext_pool: + pci_pool_destroy(phba->lpfc_scsi_dma_ext_pool); + fail: + return -ENOMEM; +} + +void +lpfc_mem_free(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli = &phba->sli; + struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool; + LPFC_MBOXQ_t *mbox, *next_mbox; + struct lpfc_dmabuf *mp; + int i; + + list_for_each_entry_safe(mbox, next_mbox, &psli->mboxq, list) { + mp = (struct lpfc_dmabuf *) (mbox->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + list_del(&mbox->list); + mempool_free(mbox, phba->mbox_mem_pool); + } + + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + if (psli->mbox_active) { + mbox = psli->mbox_active; + mp = (struct lpfc_dmabuf *) (mbox->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + mempool_free(mbox, phba->mbox_mem_pool); + psli->mbox_active = NULL; + } + + for (i = 0; i < pool->current_count; i++) + pci_pool_free(phba->lpfc_mbuf_pool, pool->elements[i].virt, + pool->elements[i].phys); + kfree(pool->elements); + mempool_destroy(phba->bind_mem_pool); + mempool_destroy(phba->nlp_mem_pool); + mempool_destroy(phba->mbox_mem_pool); + mempool_destroy(phba->scsibuf_mem_pool); + mempool_destroy(phba->iocb_mem_pool); + + pci_pool_destroy(phba->lpfc_scsi_dma_ext_pool); + pci_pool_destroy(phba->lpfc_mbuf_pool); +} + +void * +lpfc_mbuf_alloc(struct lpfc_hba *phba, int mem_flags, dma_addr_t *handle) +{ + struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool; + void *ret; + + ret = pci_pool_alloc(phba->lpfc_mbuf_pool, GFP_ATOMIC, handle); + + if (!ret && ( mem_flags & MEM_PRI) && pool->current_count) { + pool->current_count--; + ret = pool->elements[pool->current_count].virt; + *handle = pool->elements[pool->current_count].phys; + } + return ret; +} + +void +lpfc_mbuf_free(struct lpfc_hba * phba, void *virt, dma_addr_t dma) +{ + struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool; + + if (pool->current_count < pool->max_count) { + pool->elements[pool->current_count].virt = virt; + pool->elements[pool->current_count].phys = dma; + pool->current_count++; + } else { + pci_pool_free(phba->lpfc_mbuf_pool, virt, dma); + } + return; +} --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_hw.h 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_hw.h 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,2691 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_hw.h 1.34.2.2 2005/06/13 17:16:25EDT sf_support Exp $ + */ + +#ifndef _H_LPFC_HW +#define _H_LPFC_HW + +#define FDMI_DID ((uint32_t)0xfffffa) +#define NameServer_DID ((uint32_t)0xfffffc) +#define SCR_DID ((uint32_t)0xfffffd) +#define Fabric_DID ((uint32_t)0xfffffe) +#define Bcast_DID ((uint32_t)0xffffff) +#define Mask_DID ((uint32_t)0xffffff) +#define CT_DID_MASK ((uint32_t)0xffff00) +#define Fabric_DID_MASK ((uint32_t)0xfff000) +#define WELL_KNOWN_DID_MASK ((uint32_t)0xfffff0) + +#define PT2PT_LocalID ((uint32_t)1) +#define PT2PT_RemoteID ((uint32_t)2) + +#define FF_DEF_EDTOV 2000 /* Default E_D_TOV (2000ms) */ +#define FF_DEF_ALTOV 15 /* Default AL_TIME (15ms) */ +#define FF_DEF_RATOV 2 /* Default RA_TOV (2s) */ +#define FF_DEF_ARBTOV 1900 /* Default ARB_TOV (1900ms) */ + +#define LPFC_BUF_RING0 64 /* Number of buffers to post to RING + 0 */ + +#define FCELSSIZE 1024 /* maximum ELS transfer size */ + +#define LPFC_FCP_RING 0 /* ring 2 for FCP initiator commands */ +#define LPFC_IP_RING 1 /* ring 1 for IP commands */ +#define LPFC_ELS_RING 2 /* ring 0 for ELS commands */ +#define LPFC_FCP_NEXT_RING 3 + +#define SLI2_IOCB_CMD_R0_ENTRIES 172 /* SLI-2 FCP command ring entries */ +#define SLI2_IOCB_RSP_R0_ENTRIES 134 /* SLI-2 FCP response ring entries */ +#define SLI2_IOCB_CMD_R1_ENTRIES 4 /* SLI-2 IP command ring entries */ +#define SLI2_IOCB_RSP_R1_ENTRIES 4 /* SLI-2 IP response ring entries */ +#define SLI2_IOCB_CMD_R1XTRA_ENTRIES 36 /* SLI-2 extra FCP cmd ring entries */ +#define SLI2_IOCB_RSP_R1XTRA_ENTRIES 52 /* SLI-2 extra FCP rsp ring entries */ +#define SLI2_IOCB_CMD_R2_ENTRIES 20 /* SLI-2 ELS command ring entries */ +#define SLI2_IOCB_RSP_R2_ENTRIES 20 /* SLI-2 ELS response ring entries */ +#define SLI2_IOCB_CMD_R3_ENTRIES 0 +#define SLI2_IOCB_RSP_R3_ENTRIES 0 +#define SLI2_IOCB_CMD_R3XTRA_ENTRIES 24 +#define SLI2_IOCB_RSP_R3XTRA_ENTRIES 32 + +/* Common Transport structures and definitions */ + +union CtRevisionId { + /* Structure is in Big Endian format */ + struct { + uint32_t Revision:8; + uint32_t InId:24; + } bits; + uint32_t word; +}; + +union CtCommandResponse { + /* Structure is in Big Endian format */ + struct { + uint32_t CmdRsp:16; + uint32_t Size:16; + } bits; + uint32_t word; +}; + +struct lpfc_sli_ct_request { + /* Structure is in Big Endian format */ + union CtRevisionId RevisionId; + uint8_t FsType; + uint8_t FsSubType; + uint8_t Options; + uint8_t Rsrvd1; + union CtCommandResponse CommandResponse; + uint8_t Rsrvd2; + uint8_t ReasonCode; + uint8_t Explanation; + uint8_t VendorUnique; + + union { + uint32_t PortID; + struct gid { + uint8_t PortType; /* for GID_PT requests */ + uint8_t DomainScope; + uint8_t AreaScope; + uint8_t Fc4Type; /* for GID_FT requests */ + } gid; + struct rft { + uint32_t PortId; /* For RFT_ID requests */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd0:16; + uint32_t rsvd1:7; + uint32_t fcpReg:1; /* Type 8 */ + uint32_t rsvd2:2; + uint32_t ipReg:1; /* Type 5 */ + uint32_t rsvd3:5; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t rsvd0:16; + uint32_t fcpReg:1; /* Type 8 */ + uint32_t rsvd1:7; + uint32_t rsvd3:5; + uint32_t ipReg:1; /* Type 5 */ + uint32_t rsvd2:2; +#endif + + uint32_t rsvd[7]; + } rft; + struct rnn { + uint32_t PortId; /* For RNN_ID requests */ + uint8_t wwnn[8]; + } rnn; + struct rsnn { /* For RSNN_ID requests */ + uint8_t wwnn[8]; + uint8_t len; + uint8_t symbname[255]; + } rsnn; + } un; +}; + +#define SLI_CT_REVISION 1 +#define GID_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request) - 260) +#define RFT_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request) - 228) +#define RNN_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request) - 252) +#define RSNN_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request)) + +/* + * FsType Definitions + */ + +#define SLI_CT_MANAGEMENT_SERVICE 0xFA +#define SLI_CT_TIME_SERVICE 0xFB +#define SLI_CT_DIRECTORY_SERVICE 0xFC +#define SLI_CT_FABRIC_CONTROLLER_SERVICE 0xFD + +/* + * Directory Service Subtypes + */ + +#define SLI_CT_DIRECTORY_NAME_SERVER 0x02 + +/* + * Response Codes + */ + +#define SLI_CT_RESPONSE_FS_RJT 0x8001 +#define SLI_CT_RESPONSE_FS_ACC 0x8002 + +/* + * Reason Codes + */ + +#define SLI_CT_NO_ADDITIONAL_EXPL 0x0 +#define SLI_CT_INVALID_COMMAND 0x01 +#define SLI_CT_INVALID_VERSION 0x02 +#define SLI_CT_LOGICAL_ERROR 0x03 +#define SLI_CT_INVALID_IU_SIZE 0x04 +#define SLI_CT_LOGICAL_BUSY 0x05 +#define SLI_CT_PROTOCOL_ERROR 0x07 +#define SLI_CT_UNABLE_TO_PERFORM_REQ 0x09 +#define SLI_CT_REQ_NOT_SUPPORTED 0x0b +#define SLI_CT_HBA_INFO_NOT_REGISTERED 0x10 +#define SLI_CT_MULTIPLE_HBA_ATTR_OF_SAME_TYPE 0x11 +#define SLI_CT_INVALID_HBA_ATTR_BLOCK_LEN 0x12 +#define SLI_CT_HBA_ATTR_NOT_PRESENT 0x13 +#define SLI_CT_PORT_INFO_NOT_REGISTERED 0x20 +#define SLI_CT_MULTIPLE_PORT_ATTR_OF_SAME_TYPE 0x21 +#define SLI_CT_INVALID_PORT_ATTR_BLOCK_LEN 0x22 +#define SLI_CT_VENDOR_UNIQUE 0xff + +/* + * Name Server SLI_CT_UNABLE_TO_PERFORM_REQ Explanations + */ + +#define SLI_CT_NO_PORT_ID 0x01 +#define SLI_CT_NO_PORT_NAME 0x02 +#define SLI_CT_NO_NODE_NAME 0x03 +#define SLI_CT_NO_CLASS_OF_SERVICE 0x04 +#define SLI_CT_NO_IP_ADDRESS 0x05 +#define SLI_CT_NO_IPA 0x06 +#define SLI_CT_NO_FC4_TYPES 0x07 +#define SLI_CT_NO_SYMBOLIC_PORT_NAME 0x08 +#define SLI_CT_NO_SYMBOLIC_NODE_NAME 0x09 +#define SLI_CT_NO_PORT_TYPE 0x0A +#define SLI_CT_ACCESS_DENIED 0x10 +#define SLI_CT_INVALID_PORT_ID 0x11 +#define SLI_CT_DATABASE_EMPTY 0x12 + +/* + * Name Server Command Codes + */ + +#define SLI_CTNS_GA_NXT 0x0100 +#define SLI_CTNS_GPN_ID 0x0112 +#define SLI_CTNS_GNN_ID 0x0113 +#define SLI_CTNS_GCS_ID 0x0114 +#define SLI_CTNS_GFT_ID 0x0117 +#define SLI_CTNS_GSPN_ID 0x0118 +#define SLI_CTNS_GPT_ID 0x011A +#define SLI_CTNS_GID_PN 0x0121 +#define SLI_CTNS_GID_NN 0x0131 +#define SLI_CTNS_GIP_NN 0x0135 +#define SLI_CTNS_GIPA_NN 0x0136 +#define SLI_CTNS_GSNN_NN 0x0139 +#define SLI_CTNS_GNN_IP 0x0153 +#define SLI_CTNS_GIPA_IP 0x0156 +#define SLI_CTNS_GID_FT 0x0171 +#define SLI_CTNS_GID_PT 0x01A1 +#define SLI_CTNS_RPN_ID 0x0212 +#define SLI_CTNS_RNN_ID 0x0213 +#define SLI_CTNS_RCS_ID 0x0214 +#define SLI_CTNS_RFT_ID 0x0217 +#define SLI_CTNS_RSPN_ID 0x0218 +#define SLI_CTNS_RPT_ID 0x021A +#define SLI_CTNS_RIP_NN 0x0235 +#define SLI_CTNS_RIPA_NN 0x0236 +#define SLI_CTNS_RSNN_NN 0x0239 +#define SLI_CTNS_DA_ID 0x0300 + +/* + * Port Types + */ + +#define SLI_CTPT_N_PORT 0x01 +#define SLI_CTPT_NL_PORT 0x02 +#define SLI_CTPT_FNL_PORT 0x03 +#define SLI_CTPT_IP 0x04 +#define SLI_CTPT_FCP 0x08 +#define SLI_CTPT_NX_PORT 0x7F +#define SLI_CTPT_F_PORT 0x81 +#define SLI_CTPT_FL_PORT 0x82 +#define SLI_CTPT_E_PORT 0x84 + +#define SLI_CT_LAST_ENTRY 0x80000000 + +/* Fibre Channel Service Parameter definitions */ + +#define FC_PH_4_0 6 /* FC-PH version 4.0 */ +#define FC_PH_4_1 7 /* FC-PH version 4.1 */ +#define FC_PH_4_2 8 /* FC-PH version 4.2 */ +#define FC_PH_4_3 9 /* FC-PH version 4.3 */ + +#define FC_PH_LOW 8 /* Lowest supported FC-PH version */ +#define FC_PH_HIGH 9 /* Highest supported FC-PH version */ +#define FC_PH3 0x20 /* FC-PH-3 version */ + +#define FF_FRAME_SIZE 2048 + +struct lpfc_name { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t nameType:4; /* FC Word 0, bit 28:31 */ + uint8_t IEEEextMsn:4; /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t IEEEextMsn:4; /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */ + uint8_t nameType:4; /* FC Word 0, bit 28:31 */ +#endif + +#define NAME_IEEE 0x1 /* IEEE name - nameType */ +#define NAME_IEEE_EXT 0x2 /* IEEE extended name */ +#define NAME_FC_TYPE 0x3 /* FC native name type */ +#define NAME_IP_TYPE 0x4 /* IP address */ +#define NAME_CCITT_TYPE 0xC +#define NAME_CCITT_GR_TYPE 0xE + uint8_t IEEEextLsb; /* FC Word 0, bit 16:23, IEEE extended Lsb */ + uint8_t IEEE[6]; /* FC IEEE address */ +}; + +struct csp { + uint8_t fcphHigh; /* FC Word 0, byte 0 */ + uint8_t fcphLow; + uint8_t bbCreditMsb; + uint8_t bbCreditlsb; /* FC Word 0, byte 3 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t increasingOffset:1; /* FC Word 1, bit 31 */ + uint16_t randomOffset:1; /* FC Word 1, bit 30 */ + uint16_t word1Reserved2:1; /* FC Word 1, bit 29 */ + uint16_t fPort:1; /* FC Word 1, bit 28 */ + uint16_t altBbCredit:1; /* FC Word 1, bit 27 */ + uint16_t edtovResolution:1; /* FC Word 1, bit 26 */ + uint16_t multicast:1; /* FC Word 1, bit 25 */ + uint16_t broadcast:1; /* FC Word 1, bit 24 */ + + uint16_t huntgroup:1; /* FC Word 1, bit 23 */ + uint16_t simplex:1; /* FC Word 1, bit 22 */ + uint16_t word1Reserved1:3; /* FC Word 1, bit 21:19 */ + uint16_t dhd:1; /* FC Word 1, bit 18 */ + uint16_t contIncSeqCnt:1; /* FC Word 1, bit 17 */ + uint16_t payloadlength:1; /* FC Word 1, bit 16 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t broadcast:1; /* FC Word 1, bit 24 */ + uint16_t multicast:1; /* FC Word 1, bit 25 */ + uint16_t edtovResolution:1; /* FC Word 1, bit 26 */ + uint16_t altBbCredit:1; /* FC Word 1, bit 27 */ + uint16_t fPort:1; /* FC Word 1, bit 28 */ + uint16_t word1Reserved2:1; /* FC Word 1, bit 29 */ + uint16_t randomOffset:1; /* FC Word 1, bit 30 */ + uint16_t increasingOffset:1; /* FC Word 1, bit 31 */ + + uint16_t payloadlength:1; /* FC Word 1, bit 16 */ + uint16_t contIncSeqCnt:1; /* FC Word 1, bit 17 */ + uint16_t dhd:1; /* FC Word 1, bit 18 */ + uint16_t word1Reserved1:3; /* FC Word 1, bit 21:19 */ + uint16_t simplex:1; /* FC Word 1, bit 22 */ + uint16_t huntgroup:1; /* FC Word 1, bit 23 */ +#endif + + uint8_t bbRcvSizeMsb; /* Upper nibble is reserved */ + uint8_t bbRcvSizeLsb; /* FC Word 1, byte 3 */ + union { + struct { + uint8_t word2Reserved1; /* FC Word 2 byte 0 */ + + uint8_t totalConcurrSeq; /* FC Word 2 byte 1 */ + uint8_t roByCategoryMsb; /* FC Word 2 byte 2 */ + + uint8_t roByCategoryLsb; /* FC Word 2 byte 3 */ + } nPort; + uint32_t r_a_tov; /* R_A_TOV must be in B.E. format */ + } w2; + + uint32_t e_d_tov; /* E_D_TOV must be in B.E. format */ +}; + +struct class_parms { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t classValid:1; /* FC Word 0, bit 31 */ + uint8_t intermix:1; /* FC Word 0, bit 30 */ + uint8_t stackedXparent:1; /* FC Word 0, bit 29 */ + uint8_t stackedLockDown:1; /* FC Word 0, bit 28 */ + uint8_t seqDelivery:1; /* FC Word 0, bit 27 */ + uint8_t word0Reserved1:3; /* FC Word 0, bit 24:26 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t word0Reserved1:3; /* FC Word 0, bit 24:26 */ + uint8_t seqDelivery:1; /* FC Word 0, bit 27 */ + uint8_t stackedLockDown:1; /* FC Word 0, bit 28 */ + uint8_t stackedXparent:1; /* FC Word 0, bit 29 */ + uint8_t intermix:1; /* FC Word 0, bit 30 */ + uint8_t classValid:1; /* FC Word 0, bit 31 */ + +#endif + + uint8_t word0Reserved2; /* FC Word 0, bit 16:23 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t iCtlXidReAssgn:2; /* FC Word 0, Bit 14:15 */ + uint8_t iCtlInitialPa:2; /* FC Word 0, bit 12:13 */ + uint8_t iCtlAck0capable:1; /* FC Word 0, bit 11 */ + uint8_t iCtlAckNcapable:1; /* FC Word 0, bit 10 */ + uint8_t word0Reserved3:2; /* FC Word 0, bit 8: 9 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t word0Reserved3:2; /* FC Word 0, bit 8: 9 */ + uint8_t iCtlAckNcapable:1; /* FC Word 0, bit 10 */ + uint8_t iCtlAck0capable:1; /* FC Word 0, bit 11 */ + uint8_t iCtlInitialPa:2; /* FC Word 0, bit 12:13 */ + uint8_t iCtlXidReAssgn:2; /* FC Word 0, Bit 14:15 */ +#endif + + uint8_t word0Reserved4; /* FC Word 0, bit 0: 7 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t rCtlAck0capable:1; /* FC Word 1, bit 31 */ + uint8_t rCtlAckNcapable:1; /* FC Word 1, bit 30 */ + uint8_t rCtlXidInterlck:1; /* FC Word 1, bit 29 */ + uint8_t rCtlErrorPolicy:2; /* FC Word 1, bit 27:28 */ + uint8_t word1Reserved1:1; /* FC Word 1, bit 26 */ + uint8_t rCtlCatPerSeq:2; /* FC Word 1, bit 24:25 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t rCtlCatPerSeq:2; /* FC Word 1, bit 24:25 */ + uint8_t word1Reserved1:1; /* FC Word 1, bit 26 */ + uint8_t rCtlErrorPolicy:2; /* FC Word 1, bit 27:28 */ + uint8_t rCtlXidInterlck:1; /* FC Word 1, bit 29 */ + uint8_t rCtlAckNcapable:1; /* FC Word 1, bit 30 */ + uint8_t rCtlAck0capable:1; /* FC Word 1, bit 31 */ +#endif + + uint8_t word1Reserved2; /* FC Word 1, bit 16:23 */ + uint8_t rcvDataSizeMsb; /* FC Word 1, bit 8:15 */ + uint8_t rcvDataSizeLsb; /* FC Word 1, bit 0: 7 */ + + uint8_t concurrentSeqMsb; /* FC Word 2, bit 24:31 */ + uint8_t concurrentSeqLsb; /* FC Word 2, bit 16:23 */ + uint8_t EeCreditSeqMsb; /* FC Word 2, bit 8:15 */ + uint8_t EeCreditSeqLsb; /* FC Word 2, bit 0: 7 */ + + uint8_t openSeqPerXchgMsb; /* FC Word 3, bit 24:31 */ + uint8_t openSeqPerXchgLsb; /* FC Word 3, bit 16:23 */ + uint8_t word3Reserved1; /* Fc Word 3, bit 8:15 */ + uint8_t word3Reserved2; /* Fc Word 3, bit 0: 7 */ +}; + +struct serv_parm { /* Structure is in Big Endian format */ + struct csp cmn; + struct lpfc_name portName; + struct lpfc_name nodeName; + struct class_parms cls1; + struct class_parms cls2; + struct class_parms cls3; + struct class_parms cls4; + uint8_t vendorVersion[16]; +}; + +/* + * Extended Link Service LS_COMMAND codes (Payload Word 0) + */ +#ifdef __BIG_ENDIAN_BITFIELD +#define ELS_CMD_MASK 0xffff0000 +#define ELS_RSP_MASK 0xff000000 +#define ELS_CMD_LS_RJT 0x01000000 +#define ELS_CMD_ACC 0x02000000 +#define ELS_CMD_PLOGI 0x03000000 +#define ELS_CMD_FLOGI 0x04000000 +#define ELS_CMD_LOGO 0x05000000 +#define ELS_CMD_ABTX 0x06000000 +#define ELS_CMD_RCS 0x07000000 +#define ELS_CMD_RES 0x08000000 +#define ELS_CMD_RSS 0x09000000 +#define ELS_CMD_RSI 0x0A000000 +#define ELS_CMD_ESTS 0x0B000000 +#define ELS_CMD_ESTC 0x0C000000 +#define ELS_CMD_ADVC 0x0D000000 +#define ELS_CMD_RTV 0x0E000000 +#define ELS_CMD_RLS 0x0F000000 +#define ELS_CMD_ECHO 0x10000000 +#define ELS_CMD_TEST 0x11000000 +#define ELS_CMD_RRQ 0x12000000 +#define ELS_CMD_PRLI 0x20100014 +#define ELS_CMD_PRLO 0x21100014 +#define ELS_CMD_PDISC 0x50000000 +#define ELS_CMD_FDISC 0x51000000 +#define ELS_CMD_ADISC 0x52000000 +#define ELS_CMD_FARP 0x54000000 +#define ELS_CMD_FARPR 0x55000000 +#define ELS_CMD_FAN 0x60000000 +#define ELS_CMD_RSCN 0x61040000 +#define ELS_CMD_SCR 0x62000000 +#define ELS_CMD_RNID 0x78000000 +#else /* __LITTLE_ENDIAN_BITFIELD */ +#define ELS_CMD_MASK 0xffff +#define ELS_RSP_MASK 0xff +#define ELS_CMD_LS_RJT 0x01 +#define ELS_CMD_ACC 0x02 +#define ELS_CMD_PLOGI 0x03 +#define ELS_CMD_FLOGI 0x04 +#define ELS_CMD_LOGO 0x05 +#define ELS_CMD_ABTX 0x06 +#define ELS_CMD_RCS 0x07 +#define ELS_CMD_RES 0x08 +#define ELS_CMD_RSS 0x09 +#define ELS_CMD_RSI 0x0A +#define ELS_CMD_ESTS 0x0B +#define ELS_CMD_ESTC 0x0C +#define ELS_CMD_ADVC 0x0D +#define ELS_CMD_RTV 0x0E +#define ELS_CMD_RLS 0x0F +#define ELS_CMD_ECHO 0x10 +#define ELS_CMD_TEST 0x11 +#define ELS_CMD_RRQ 0x12 +#define ELS_CMD_PRLI 0x14001020 +#define ELS_CMD_PRLO 0x14001021 +#define ELS_CMD_PDISC 0x50 +#define ELS_CMD_FDISC 0x51 +#define ELS_CMD_ADISC 0x52 +#define ELS_CMD_FARP 0x54 +#define ELS_CMD_FARPR 0x55 +#define ELS_CMD_FAN 0x60 +#define ELS_CMD_RSCN 0x0461 +#define ELS_CMD_SCR 0x62 +#define ELS_CMD_RNID 0x78 +#endif + +/* + * LS_RJT Payload Definition + */ + +struct ls_rjt { /* Structure is in Big Endian format */ + union { + uint32_t lsRjtError; + struct { + uint8_t lsRjtRsvd0; /* FC Word 0, bit 24:31 */ + + uint8_t lsRjtRsnCode; /* FC Word 0, bit 16:23 */ + /* LS_RJT reason codes */ +#define LSRJT_INVALID_CMD 0x01 +#define LSRJT_LOGICAL_ERR 0x03 +#define LSRJT_LOGICAL_BSY 0x05 +#define LSRJT_PROTOCOL_ERR 0x07 +#define LSRJT_UNABLE_TPC 0x09 /* Unable to perform command */ +#define LSRJT_CMD_UNSUPPORTED 0x0B +#define LSRJT_VENDOR_UNIQUE 0xFF /* See Byte 3 */ + + uint8_t lsRjtRsnCodeExp; /* FC Word 0, bit 8:15 */ + /* LS_RJT reason explanation */ +#define LSEXP_NOTHING_MORE 0x00 +#define LSEXP_SPARM_OPTIONS 0x01 +#define LSEXP_SPARM_ICTL 0x03 +#define LSEXP_SPARM_RCTL 0x05 +#define LSEXP_SPARM_RCV_SIZE 0x07 +#define LSEXP_SPARM_CONCUR_SEQ 0x09 +#define LSEXP_SPARM_CREDIT 0x0B +#define LSEXP_INVALID_PNAME 0x0D +#define LSEXP_INVALID_NNAME 0x0E +#define LSEXP_INVALID_CSP 0x0F +#define LSEXP_INVALID_ASSOC_HDR 0x11 +#define LSEXP_ASSOC_HDR_REQ 0x13 +#define LSEXP_INVALID_O_SID 0x15 +#define LSEXP_INVALID_OX_RX 0x17 +#define LSEXP_CMD_IN_PROGRESS 0x19 +#define LSEXP_INVALID_NPORT_ID 0x1F +#define LSEXP_INVALID_SEQ_ID 0x21 +#define LSEXP_INVALID_XCHG 0x23 +#define LSEXP_INACTIVE_XCHG 0x25 +#define LSEXP_RQ_REQUIRED 0x27 +#define LSEXP_OUT_OF_RESOURCE 0x29 +#define LSEXP_CANT_GIVE_DATA 0x2A +#define LSEXP_REQ_UNSUPPORTED 0x2C + uint8_t vendorUnique; /* FC Word 0, bit 0: 7 */ + } b; + } un; +}; + +/* + * N_Port Login (FLOGO/PLOGO Request) Payload Definition + */ + +typedef struct _LOGO { /* Structure is in Big Endian format */ + union { + uint32_t nPortId32; /* Access nPortId as a word */ + struct { + uint8_t word1Reserved1; /* FC Word 1, bit 31:24 */ + uint8_t nPortIdByte0; /* N_port ID bit 16:23 */ + uint8_t nPortIdByte1; /* N_port ID bit 8:15 */ + uint8_t nPortIdByte2; /* N_port ID bit 0: 7 */ + } b; + } un; + struct lpfc_name portName; /* N_port name field */ +} LOGO; + +/* + * FCP Login (PRLI Request / ACC) Payload Definition + */ + +#define PRLX_PAGE_LEN 0x10 +#define TPRLO_PAGE_LEN 0x14 + +typedef struct _PRLI { /* Structure is in Big Endian format */ + uint8_t prliType; /* FC Parm Word 0, bit 24:31 */ + +#define PRLI_FCP_TYPE 0x08 + uint8_t word0Reserved1; /* FC Parm Word 0, bit 16:23 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t origProcAssocV:1; /* FC Parm Word 0, bit 15 */ + uint8_t respProcAssocV:1; /* FC Parm Word 0, bit 14 */ + uint8_t estabImagePair:1; /* FC Parm Word 0, bit 13 */ + + /* ACC = imagePairEstablished */ + uint8_t word0Reserved2:1; /* FC Parm Word 0, bit 12 */ + uint8_t acceptRspCode:4; /* FC Parm Word 0, bit 8:11, ACC ONLY */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t acceptRspCode:4; /* FC Parm Word 0, bit 8:11, ACC ONLY */ + uint8_t word0Reserved2:1; /* FC Parm Word 0, bit 12 */ + uint8_t estabImagePair:1; /* FC Parm Word 0, bit 13 */ + uint8_t respProcAssocV:1; /* FC Parm Word 0, bit 14 */ + uint8_t origProcAssocV:1; /* FC Parm Word 0, bit 15 */ + /* ACC = imagePairEstablished */ +#endif + +#define PRLI_REQ_EXECUTED 0x1 /* acceptRspCode */ +#define PRLI_NO_RESOURCES 0x2 +#define PRLI_INIT_INCOMPLETE 0x3 +#define PRLI_NO_SUCH_PA 0x4 +#define PRLI_PREDEF_CONFIG 0x5 +#define PRLI_PARTIAL_SUCCESS 0x6 +#define PRLI_INVALID_PAGE_CNT 0x7 + uint8_t word0Reserved3; /* FC Parm Word 0, bit 0:7 */ + + uint32_t origProcAssoc; /* FC Parm Word 1, bit 0:31 */ + + uint32_t respProcAssoc; /* FC Parm Word 2, bit 0:31 */ + + uint8_t word3Reserved1; /* FC Parm Word 3, bit 24:31 */ + uint8_t word3Reserved2; /* FC Parm Word 3, bit 16:23 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t Word3bit15Resved:1; /* FC Parm Word 3, bit 15 */ + uint16_t Word3bit14Resved:1; /* FC Parm Word 3, bit 14 */ + uint16_t Word3bit13Resved:1; /* FC Parm Word 3, bit 13 */ + uint16_t Word3bit12Resved:1; /* FC Parm Word 3, bit 12 */ + uint16_t Word3bit11Resved:1; /* FC Parm Word 3, bit 11 */ + uint16_t Word3bit10Resved:1; /* FC Parm Word 3, bit 10 */ + uint16_t TaskRetryIdReq:1; /* FC Parm Word 3, bit 9 */ + uint16_t Retry:1; /* FC Parm Word 3, bit 8 */ + uint16_t ConfmComplAllowed:1; /* FC Parm Word 3, bit 7 */ + uint16_t dataOverLay:1; /* FC Parm Word 3, bit 6 */ + uint16_t initiatorFunc:1; /* FC Parm Word 3, bit 5 */ + uint16_t targetFunc:1; /* FC Parm Word 3, bit 4 */ + uint16_t cmdDataMixEna:1; /* FC Parm Word 3, bit 3 */ + uint16_t dataRspMixEna:1; /* FC Parm Word 3, bit 2 */ + uint16_t readXferRdyDis:1; /* FC Parm Word 3, bit 1 */ + uint16_t writeXferRdyDis:1; /* FC Parm Word 3, bit 0 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t Retry:1; /* FC Parm Word 3, bit 8 */ + uint16_t TaskRetryIdReq:1; /* FC Parm Word 3, bit 9 */ + uint16_t Word3bit10Resved:1; /* FC Parm Word 3, bit 10 */ + uint16_t Word3bit11Resved:1; /* FC Parm Word 3, bit 11 */ + uint16_t Word3bit12Resved:1; /* FC Parm Word 3, bit 12 */ + uint16_t Word3bit13Resved:1; /* FC Parm Word 3, bit 13 */ + uint16_t Word3bit14Resved:1; /* FC Parm Word 3, bit 14 */ + uint16_t Word3bit15Resved:1; /* FC Parm Word 3, bit 15 */ + uint16_t writeXferRdyDis:1; /* FC Parm Word 3, bit 0 */ + uint16_t readXferRdyDis:1; /* FC Parm Word 3, bit 1 */ + uint16_t dataRspMixEna:1; /* FC Parm Word 3, bit 2 */ + uint16_t cmdDataMixEna:1; /* FC Parm Word 3, bit 3 */ + uint16_t targetFunc:1; /* FC Parm Word 3, bit 4 */ + uint16_t initiatorFunc:1; /* FC Parm Word 3, bit 5 */ + uint16_t dataOverLay:1; /* FC Parm Word 3, bit 6 */ + uint16_t ConfmComplAllowed:1; /* FC Parm Word 3, bit 7 */ +#endif +} PRLI; + +/* + * FCP Logout (PRLO Request / ACC) Payload Definition + */ + +typedef struct _PRLO { /* Structure is in Big Endian format */ + uint8_t prloType; /* FC Parm Word 0, bit 24:31 */ + +#define PRLO_FCP_TYPE 0x08 + uint8_t word0Reserved1; /* FC Parm Word 0, bit 16:23 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t origProcAssocV:1; /* FC Parm Word 0, bit 15 */ + uint8_t respProcAssocV:1; /* FC Parm Word 0, bit 14 */ + uint8_t word0Reserved2:2; /* FC Parm Word 0, bit 12:13 */ + uint8_t acceptRspCode:4; /* FC Parm Word 0, bit 8:11, ACC ONLY */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t acceptRspCode:4; /* FC Parm Word 0, bit 8:11, ACC ONLY */ + uint8_t word0Reserved2:2; /* FC Parm Word 0, bit 12:13 */ + uint8_t respProcAssocV:1; /* FC Parm Word 0, bit 14 */ + uint8_t origProcAssocV:1; /* FC Parm Word 0, bit 15 */ +#endif + +#define PRLO_REQ_EXECUTED 0x1 /* acceptRspCode */ +#define PRLO_NO_SUCH_IMAGE 0x4 +#define PRLO_INVALID_PAGE_CNT 0x7 + + uint8_t word0Reserved3; /* FC Parm Word 0, bit 0:7 */ + + uint32_t origProcAssoc; /* FC Parm Word 1, bit 0:31 */ + + uint32_t respProcAssoc; /* FC Parm Word 2, bit 0:31 */ + + uint32_t word3Reserved1; /* FC Parm Word 3, bit 0:31 */ +} PRLO; + +typedef struct _ADISC { /* Structure is in Big Endian format */ + uint32_t hardAL_PA; + struct lpfc_name portName; + struct lpfc_name nodeName; + uint32_t DID; +} ADISC; + +typedef struct _FARP { /* Structure is in Big Endian format */ + uint32_t Mflags:8; + uint32_t Odid:24; +#define FARP_NO_ACTION 0 /* FARP information enclosed, no + action */ +#define FARP_MATCH_PORT 0x1 /* Match on Responder Port Name */ +#define FARP_MATCH_NODE 0x2 /* Match on Responder Node Name */ +#define FARP_MATCH_IP 0x4 /* Match on IP address, not supported */ +#define FARP_MATCH_IPV4 0x5 /* Match on IPV4 address, not + supported */ +#define FARP_MATCH_IPV6 0x6 /* Match on IPV6 address, not + supported */ + uint32_t Rflags:8; + uint32_t Rdid:24; +#define FARP_REQUEST_PLOGI 0x1 /* Request for PLOGI */ +#define FARP_REQUEST_FARPR 0x2 /* Request for FARP Response */ + struct lpfc_name OportName; + struct lpfc_name OnodeName; + struct lpfc_name RportName; + struct lpfc_name RnodeName; + uint8_t Oipaddr[16]; + uint8_t Ripaddr[16]; +} FARP; + +typedef struct _FAN { /* Structure is in Big Endian format */ + uint32_t Fdid; + struct lpfc_name FportName; + struct lpfc_name FnodeName; +} FAN; + +typedef struct _SCR { /* Structure is in Big Endian format */ + uint8_t resvd1; + uint8_t resvd2; + uint8_t resvd3; + uint8_t Function; +#define SCR_FUNC_FABRIC 0x01 +#define SCR_FUNC_NPORT 0x02 +#define SCR_FUNC_FULL 0x03 +#define SCR_CLEAR 0xff +} SCR; + +typedef struct _RNID_TOP_DISC { + struct lpfc_name portName; + uint8_t resvd[8]; + uint32_t unitType; +#define RNID_HBA 0x7 +#define RNID_HOST 0xa +#define RNID_DRIVER 0xd + uint32_t physPort; + uint32_t attachedNodes; + uint16_t ipVersion; +#define RNID_IPV4 0x1 +#define RNID_IPV6 0x2 + uint16_t UDPport; + uint8_t ipAddr[16]; + uint16_t resvd1; + uint16_t flags; +#define RNID_TD_SUPPORT 0x1 +#define RNID_LP_VALID 0x2 +} RNID_TOP_DISC; + +typedef struct _RNID { /* Structure is in Big Endian format */ + uint8_t Format; +#define RNID_TOPOLOGY_DISC 0xdf + uint8_t CommonLen; + uint8_t resvd1; + uint8_t SpecificLen; + struct lpfc_name portName; + struct lpfc_name nodeName; + union { + RNID_TOP_DISC topologyDisc; /* topology disc (0xdf) */ + } un; +} RNID; + +typedef struct _RRQ { /* Structure is in Big Endian format */ + uint32_t SID; + uint16_t Oxid; + uint16_t Rxid; + uint8_t resv[32]; /* optional association hdr */ +} RRQ; + +/* This is used for RSCN command */ +typedef struct _D_ID { /* Structure is in Big Endian format */ + union { + uint32_t word; + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t resv; + uint8_t domain; + uint8_t area; + uint8_t id; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t id; + uint8_t area; + uint8_t domain; + uint8_t resv; +#endif + } b; + } un; +} D_ID; + +/* + * Structure to define all ELS Payload types + */ + +typedef struct _ELS_PKT { /* Structure is in Big Endian format */ + uint8_t elsCode; /* FC Word 0, bit 24:31 */ + uint8_t elsByte1; + uint8_t elsByte2; + uint8_t elsByte3; + union { + struct ls_rjt lsRjt; /* Payload for LS_RJT ELS response */ + struct serv_parm logi; /* Payload for PLOGI/FLOGI/PDISC/ACC */ + LOGO logo; /* Payload for PLOGO/FLOGO/ACC */ + PRLI prli; /* Payload for PRLI/ACC */ + PRLO prlo; /* Payload for PRLO/ACC */ + ADISC adisc; /* Payload for ADISC/ACC */ + FARP farp; /* Payload for FARP/ACC */ + FAN fan; /* Payload for FAN */ + SCR scr; /* Payload for SCR/ACC */ + RRQ rrq; /* Payload for RRQ */ + RNID rnid; /* Payload for RNID */ + uint8_t pad[128 - 4]; /* Pad out to payload of 128 bytes */ + } un; +} ELS_PKT; + +/* + * FDMI + * HBA MAnagement Operations Command Codes + */ +#define SLI_MGMT_GRHL 0x100 /* Get registered HBA list */ +#define SLI_MGMT_GHAT 0x101 /* Get HBA attributes */ +#define SLI_MGMT_GRPL 0x102 /* Get registered Port list */ +#define SLI_MGMT_GPAT 0x110 /* Get Port attributes */ +#define SLI_MGMT_RHBA 0x200 /* Register HBA */ +#define SLI_MGMT_RHAT 0x201 /* Register HBA atttributes */ +#define SLI_MGMT_RPRT 0x210 /* Register Port */ +#define SLI_MGMT_RPA 0x211 /* Register Port attributes */ +#define SLI_MGMT_DHBA 0x300 /* De-register HBA */ +#define SLI_MGMT_DPRT 0x310 /* De-register Port */ + +/* + * Management Service Subtypes + */ +#define SLI_CT_FDMI_Subtypes 0x10 + +/* + * HBA Management Service Reject Code + */ +#define REJECT_CODE 0x9 /* Unable to perform command request */ + +/* + * HBA Management Service Reject Reason Code + * Please refer to the Reason Codes above + */ + +/* + * HBA Attribute Types + */ +#define NODE_NAME 0x1 +#define MANUFACTURER 0x2 +#define SERIAL_NUMBER 0x3 +#define MODEL 0x4 +#define MODEL_DESCRIPTION 0x5 +#define HARDWARE_VERSION 0x6 +#define DRIVER_VERSION 0x7 +#define OPTION_ROM_VERSION 0x8 +#define FIRMWARE_VERSION 0x9 +#define OS_NAME_VERSION 0xa +#define MAX_CT_PAYLOAD_LEN 0xb + +/* + * Port Attrubute Types + */ +#define SUPPORTED_FC4_TYPES 0x1 +#define SUPPORTED_SPEED 0x2 +#define PORT_SPEED 0x3 +#define MAX_FRAME_SIZE 0x4 +#define OS_DEVICE_NAME 0x5 +#define HOST_NAME 0x6 + +union AttributesDef { + /* Structure is in Big Endian format */ + struct { + uint32_t AttrType:16; + uint32_t AttrLen:16; + } bits; + uint32_t word; +}; + + +/* + * HBA Attribute Entry (8 - 260 bytes) + */ +typedef struct { + union AttributesDef ad; + union { + uint32_t VendorSpecific; + uint8_t Manufacturer[64]; + uint8_t SerialNumber[64]; + uint8_t Model[256]; + uint8_t ModelDescription[256]; + uint8_t HardwareVersion[256]; + uint8_t DriverVersion[256]; + uint8_t OptionROMVersion[256]; + uint8_t FirmwareVersion[256]; + struct lpfc_name NodeName; + uint8_t SupportFC4Types[32]; + uint32_t SupportSpeed; + uint32_t PortSpeed; + uint32_t MaxFrameSize; + uint8_t OsDeviceName[256]; + uint8_t OsNameVersion[256]; + uint32_t MaxCTPayloadLen; + uint8_t HostName[256]; + } un; +} ATTRIBUTE_ENTRY; + +/* + * HBA Attribute Block + */ +typedef struct { + uint32_t EntryCnt; /* Number of HBA attribute entries */ + ATTRIBUTE_ENTRY Entry; /* Variable-length array */ +} ATTRIBUTE_BLOCK; + +/* + * Port Entry + */ +typedef struct { + struct lpfc_name PortName; +} PORT_ENTRY; + +/* + * HBA Identifier + */ +typedef struct { + struct lpfc_name PortName; +} HBA_IDENTIFIER; + +/* + * Registered Port List Format + */ +typedef struct { + uint32_t EntryCnt; + PORT_ENTRY pe; /* Variable-length array */ +} REG_PORT_LIST; + +/* + * Register HBA(RHBA) + */ +typedef struct { + HBA_IDENTIFIER hi; + REG_PORT_LIST rpl; /* variable-length array */ +/* ATTRIBUTE_BLOCK ab; */ +} REG_HBA; + +/* + * Register HBA Attributes (RHAT) + */ +typedef struct { + struct lpfc_name HBA_PortName; + ATTRIBUTE_BLOCK ab; +} REG_HBA_ATTRIBUTE; + +/* + * Register Port Attributes (RPA) + */ +typedef struct { + struct lpfc_name PortName; + ATTRIBUTE_BLOCK ab; +} REG_PORT_ATTRIBUTE; + +/* + * Get Registered HBA List (GRHL) Accept Payload Format + */ +typedef struct { + uint32_t HBA__Entry_Cnt; /* Number of Registered HBA Identifiers */ + struct lpfc_name HBA_PortName; /* Variable-length array */ +} GRHL_ACC_PAYLOAD; + +/* + * Get Registered Port List (GRPL) Accept Payload Format + */ +typedef struct { + uint32_t RPL_Entry_Cnt; /* Number of Registered Port Entries */ + PORT_ENTRY Reg_Port_Entry[1]; /* Variable-length array */ +} GRPL_ACC_PAYLOAD; + +/* + * Get Port Attributes (GPAT) Accept Payload Format + */ + +typedef struct { + ATTRIBUTE_BLOCK pab; +} GPAT_ACC_PAYLOAD; + + +/* + * Begin HBA configuration parameters. + * The PCI configuration register BAR assignments are: + * BAR0, offset 0x10 - SLIM base memory address + * BAR1, offset 0x14 - SLIM base memory high address + * BAR2, offset 0x18 - REGISTER base memory address + * BAR3, offset 0x1c - REGISTER base memory high address + * BAR4, offset 0x20 - BIU I/O registers + * BAR5, offset 0x24 - REGISTER base io high address + */ + +/* Number of rings currently used and available. */ +#define MAX_CONFIGURED_RINGS 3 +#define MAX_RINGS 4 + +/* IOCB / Mailbox is owned by FireFly */ +#define OWN_CHIP 1 + +/* IOCB / Mailbox is owned by Host */ +#define OWN_HOST 0 + +/* Number of 4-byte words in an IOCB. */ +#define IOCB_WORD_SZ 8 + +/* defines for type field in fc header */ +#define FC_ELS_DATA 0x1 +#define FC_LLC_SNAP 0x5 +#define FC_FCP_DATA 0x8 +#define FC_COMMON_TRANSPORT_ULP 0x20 + +/* defines for rctl field in fc header */ +#define FC_DEV_DATA 0x0 +#define FC_UNSOL_CTL 0x2 +#define FC_SOL_CTL 0x3 +#define FC_UNSOL_DATA 0x4 +#define FC_FCP_CMND 0x6 +#define FC_ELS_REQ 0x22 +#define FC_ELS_RSP 0x23 + +/* network headers for Dfctl field */ +#define FC_NET_HDR 0x20 + +/* Start FireFly Register definitions */ +#define PCI_VENDOR_ID_EMULEX 0x10df +#define PCI_DEVICE_ID_FIREFLY 0x1ae5 +#define PCI_DEVICE_ID_SUPERFLY 0xf700 +#define PCI_DEVICE_ID_DRAGONFLY 0xf800 +#define PCI_DEVICE_ID_RFLY 0xf095 +#define PCI_DEVICE_ID_PFLY 0xf098 +#define PCI_DEVICE_ID_TFLY 0xf0a5 +#define PCI_DEVICE_ID_CENTAUR 0xf900 +#define PCI_DEVICE_ID_PEGASUS 0xf980 +#define PCI_DEVICE_ID_THOR 0xfa00 +#define PCI_DEVICE_ID_VIPER 0xfb00 +#define PCI_DEVICE_ID_HELIOS 0xfd00 +#define PCI_DEVICE_ID_BMID 0xf0d5 +#define PCI_DEVICE_ID_BSMB 0xf0d1 +#define PCI_DEVICE_ID_ZEPHYR 0xfe00 +#define PCI_DEVICE_ID_ZMID 0xf0e5 +#define PCI_DEVICE_ID_ZSMB 0xf0e1 +#define PCI_DEVICE_ID_LP101 0xf0a1 +#define PCI_DEVICE_ID_LP10000S 0xfc00 + +#define JEDEC_ID_ADDRESS 0x0080001c +#define FIREFLY_JEDEC_ID 0x1ACC +#define SUPERFLY_JEDEC_ID 0x0020 +#define DRAGONFLY_JEDEC_ID 0x0021 +#define DRAGONFLY_V2_JEDEC_ID 0x0025 +#define CENTAUR_2G_JEDEC_ID 0x0026 +#define CENTAUR_1G_JEDEC_ID 0x0028 +#define PEGASUS_ORION_JEDEC_ID 0x0036 +#define PEGASUS_JEDEC_ID 0x0038 +#define THOR_JEDEC_ID 0x0012 +#define HELIOS_JEDEC_ID 0x0364 +#define ZEPHYR_JEDEC_ID 0x0577 +#define VIPER_JEDEC_ID 0x4838 + +#define JEDEC_ID_MASK 0x0FFFF000 +#define JEDEC_ID_SHIFT 12 +#define FC_JEDEC_ID(id) ((id & JEDEC_ID_MASK) >> JEDEC_ID_SHIFT) + +typedef struct { /* FireFly BIU registers */ + uint32_t hostAtt; /* See definitions for Host Attention + register */ + uint32_t chipAtt; /* See definitions for Chip Attention + register */ + uint32_t hostStatus; /* See definitions for Host Status register */ + uint32_t hostControl; /* See definitions for Host Control register */ + uint32_t buiConfig; /* See definitions for BIU configuration + register */ +} FF_REGS; + +/* IO Register size in bytes */ +#define FF_REG_AREA_SIZE 256 + +/* Host Attention Register */ + +#define HA_REG_OFFSET 0 /* Word offset from register base address */ + +#define HA_R0RE_REQ 0x00000001 /* Bit 0 */ +#define HA_R0CE_RSP 0x00000002 /* Bit 1 */ +#define HA_R0ATT 0x00000008 /* Bit 3 */ +#define HA_R1RE_REQ 0x00000010 /* Bit 4 */ +#define HA_R1CE_RSP 0x00000020 /* Bit 5 */ +#define HA_R1ATT 0x00000080 /* Bit 7 */ +#define HA_R2RE_REQ 0x00000100 /* Bit 8 */ +#define HA_R2CE_RSP 0x00000200 /* Bit 9 */ +#define HA_R2ATT 0x00000800 /* Bit 11 */ +#define HA_R3RE_REQ 0x00001000 /* Bit 12 */ +#define HA_R3CE_RSP 0x00002000 /* Bit 13 */ +#define HA_R3ATT 0x00008000 /* Bit 15 */ +#define HA_LATT 0x20000000 /* Bit 29 */ +#define HA_MBATT 0x40000000 /* Bit 30 */ +#define HA_ERATT 0x80000000 /* Bit 31 */ + +#define HA_RXRE_REQ 0x00000001 /* Bit 0 */ +#define HA_RXCE_RSP 0x00000002 /* Bit 1 */ +#define HA_RXATT 0x00000008 /* Bit 3 */ +#define HA_RXMASK 0x0000000f + +/* Chip Attention Register */ + +#define CA_REG_OFFSET 1 /* Word offset from register base address */ + +#define CA_R0CE_REQ 0x00000001 /* Bit 0 */ +#define CA_R0RE_RSP 0x00000002 /* Bit 1 */ +#define CA_R0ATT 0x00000008 /* Bit 3 */ +#define CA_R1CE_REQ 0x00000010 /* Bit 4 */ +#define CA_R1RE_RSP 0x00000020 /* Bit 5 */ +#define CA_R1ATT 0x00000080 /* Bit 7 */ +#define CA_R2CE_REQ 0x00000100 /* Bit 8 */ +#define CA_R2RE_RSP 0x00000200 /* Bit 9 */ +#define CA_R2ATT 0x00000800 /* Bit 11 */ +#define CA_R3CE_REQ 0x00001000 /* Bit 12 */ +#define CA_R3RE_RSP 0x00002000 /* Bit 13 */ +#define CA_R3ATT 0x00008000 /* Bit 15 */ +#define CA_MBATT 0x40000000 /* Bit 30 */ + +/* Host Status Register */ + +#define HS_REG_OFFSET 2 /* Word offset from register base address */ + +#define HS_MBRDY 0x00400000 /* Bit 22 */ +#define HS_FFRDY 0x00800000 /* Bit 23 */ +#define HS_FFER8 0x01000000 /* Bit 24 */ +#define HS_FFER7 0x02000000 /* Bit 25 */ +#define HS_FFER6 0x04000000 /* Bit 26 */ +#define HS_FFER5 0x08000000 /* Bit 27 */ +#define HS_FFER4 0x10000000 /* Bit 28 */ +#define HS_FFER3 0x20000000 /* Bit 29 */ +#define HS_FFER2 0x40000000 /* Bit 30 */ +#define HS_FFER1 0x80000000 /* Bit 31 */ +#define HS_FFERM 0xFF000000 /* Mask for error bits 31:24 */ + +/* Host Control Register */ + +#define HC_REG_OFFSET 3 /* Word offset from register base address */ + +#define HC_MBINT_ENA 0x00000001 /* Bit 0 */ +#define HC_R0INT_ENA 0x00000002 /* Bit 1 */ +#define HC_R1INT_ENA 0x00000004 /* Bit 2 */ +#define HC_R2INT_ENA 0x00000008 /* Bit 3 */ +#define HC_R3INT_ENA 0x00000010 /* Bit 4 */ +#define HC_INITHBI 0x02000000 /* Bit 25 */ +#define HC_INITMB 0x04000000 /* Bit 26 */ +#define HC_INITFF 0x08000000 /* Bit 27 */ +#define HC_LAINT_ENA 0x20000000 /* Bit 29 */ +#define HC_ERINT_ENA 0x80000000 /* Bit 31 */ + +/* Mailbox Commands */ +#define MBX_SHUTDOWN 0x00 /* terminate testing */ +#define MBX_LOAD_SM 0x01 +#define MBX_READ_NV 0x02 +#define MBX_WRITE_NV 0x03 +#define MBX_RUN_BIU_DIAG 0x04 +#define MBX_INIT_LINK 0x05 +#define MBX_DOWN_LINK 0x06 +#define MBX_CONFIG_LINK 0x07 +#define MBX_CONFIG_RING 0x09 +#define MBX_RESET_RING 0x0A +#define MBX_READ_CONFIG 0x0B +#define MBX_READ_RCONFIG 0x0C +#define MBX_READ_SPARM 0x0D +#define MBX_READ_STATUS 0x0E +#define MBX_READ_RPI 0x0F +#define MBX_READ_XRI 0x10 +#define MBX_READ_REV 0x11 +#define MBX_READ_LNK_STAT 0x12 +#define MBX_REG_LOGIN 0x13 +#define MBX_UNREG_LOGIN 0x14 +#define MBX_READ_LA 0x15 +#define MBX_CLEAR_LA 0x16 +#define MBX_DUMP_MEMORY 0x17 +#define MBX_DUMP_CONTEXT 0x18 +#define MBX_RUN_DIAGS 0x19 +#define MBX_RESTART 0x1A +#define MBX_UPDATE_CFG 0x1B +#define MBX_DOWN_LOAD 0x1C +#define MBX_DEL_LD_ENTRY 0x1D +#define MBX_RUN_PROGRAM 0x1E +#define MBX_SET_MASK 0x20 +#define MBX_SET_SLIM 0x21 +#define MBX_UNREG_D_ID 0x23 +#define MBX_CONFIG_FARP 0x25 + +#define MBX_LOAD_AREA 0x81 +#define MBX_RUN_BIU_DIAG64 0x84 +#define MBX_CONFIG_PORT 0x88 +#define MBX_READ_SPARM64 0x8D +#define MBX_READ_RPI64 0x8F +#define MBX_REG_LOGIN64 0x93 +#define MBX_READ_LA64 0x95 + +#define MBX_FLASH_WR_ULA 0x98 +#define MBX_SET_DEBUG 0x99 +#define MBX_LOAD_EXP_ROM 0x9C + +#define MBX_MAX_CMDS 0x9D +#define MBX_SLI2_CMD_MASK 0x80 + +/* IOCB Commands */ + +#define CMD_RCV_SEQUENCE_CX 0x01 +#define CMD_XMIT_SEQUENCE_CR 0x02 +#define CMD_XMIT_SEQUENCE_CX 0x03 +#define CMD_XMIT_BCAST_CN 0x04 +#define CMD_XMIT_BCAST_CX 0x05 +#define CMD_QUE_RING_BUF_CN 0x06 +#define CMD_QUE_XRI_BUF_CX 0x07 +#define CMD_IOCB_CONTINUE_CN 0x08 +#define CMD_RET_XRI_BUF_CX 0x09 +#define CMD_ELS_REQUEST_CR 0x0A +#define CMD_ELS_REQUEST_CX 0x0B +#define CMD_RCV_ELS_REQ_CX 0x0D +#define CMD_ABORT_XRI_CN 0x0E +#define CMD_ABORT_XRI_CX 0x0F +#define CMD_CLOSE_XRI_CN 0x10 +#define CMD_CLOSE_XRI_CX 0x11 +#define CMD_CREATE_XRI_CR 0x12 +#define CMD_CREATE_XRI_CX 0x13 +#define CMD_GET_RPI_CN 0x14 +#define CMD_XMIT_ELS_RSP_CX 0x15 +#define CMD_GET_RPI_CR 0x16 +#define CMD_XRI_ABORTED_CX 0x17 +#define CMD_FCP_IWRITE_CR 0x18 +#define CMD_FCP_IWRITE_CX 0x19 +#define CMD_FCP_IREAD_CR 0x1A +#define CMD_FCP_IREAD_CX 0x1B +#define CMD_FCP_ICMND_CR 0x1C +#define CMD_FCP_ICMND_CX 0x1D + +#define CMD_ADAPTER_MSG 0x20 +#define CMD_ADAPTER_DUMP 0x22 + +/* SLI_2 IOCB Command Set */ + +#define CMD_RCV_SEQUENCE64_CX 0x81 +#define CMD_XMIT_SEQUENCE64_CR 0x82 +#define CMD_XMIT_SEQUENCE64_CX 0x83 +#define CMD_XMIT_BCAST64_CN 0x84 +#define CMD_XMIT_BCAST64_CX 0x85 +#define CMD_QUE_RING_BUF64_CN 0x86 +#define CMD_QUE_XRI_BUF64_CX 0x87 +#define CMD_IOCB_CONTINUE64_CN 0x88 +#define CMD_RET_XRI_BUF64_CX 0x89 +#define CMD_ELS_REQUEST64_CR 0x8A +#define CMD_ELS_REQUEST64_CX 0x8B +#define CMD_ABORT_MXRI64_CN 0x8C +#define CMD_RCV_ELS_REQ64_CX 0x8D +#define CMD_XMIT_ELS_RSP64_CX 0x95 +#define CMD_FCP_IWRITE64_CR 0x98 +#define CMD_FCP_IWRITE64_CX 0x99 +#define CMD_FCP_IREAD64_CR 0x9A +#define CMD_FCP_IREAD64_CX 0x9B +#define CMD_FCP_ICMND64_CR 0x9C +#define CMD_FCP_ICMND64_CX 0x9D + +#define CMD_GEN_REQUEST64_CR 0xC2 +#define CMD_GEN_REQUEST64_CX 0xC3 + +#define CMD_MAX_IOCB_CMD 0xE6 +#define CMD_IOCB_MASK 0xff + +#define MAX_MSG_DATA 28 /* max msg data in CMD_ADAPTER_MSG + iocb */ +#define LPFC_MAX_ADPTMSG 32 /* max msg data */ +/* + * Define Status + */ +#define MBX_SUCCESS 0 +#define MBXERR_NUM_RINGS 1 +#define MBXERR_NUM_IOCBS 2 +#define MBXERR_IOCBS_EXCEEDED 3 +#define MBXERR_BAD_RING_NUMBER 4 +#define MBXERR_MASK_ENTRIES_RANGE 5 +#define MBXERR_MASKS_EXCEEDED 6 +#define MBXERR_BAD_PROFILE 7 +#define MBXERR_BAD_DEF_CLASS 8 +#define MBXERR_BAD_MAX_RESPONDER 9 +#define MBXERR_BAD_MAX_ORIGINATOR 10 +#define MBXERR_RPI_REGISTERED 11 +#define MBXERR_RPI_FULL 12 +#define MBXERR_NO_RESOURCES 13 +#define MBXERR_BAD_RCV_LENGTH 14 +#define MBXERR_DMA_ERROR 15 +#define MBXERR_ERROR 16 +#define MBX_NOT_FINISHED 255 + +#define MBX_BUSY 0xffffff /* Attempted cmd to busy Mailbox */ +#define MBX_TIMEOUT 0xfffffe /* time-out expired waiting for */ + +/* + * Begin Structure Definitions for Mailbox Commands + */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t tval; + uint8_t tmask; + uint8_t rval; + uint8_t rmask; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t rmask; + uint8_t rval; + uint8_t tmask; + uint8_t tval; +#endif +} RR_REG; + +struct ulp_bde { + uint32_t bdeAddress; +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t bdeReserved:4; + uint32_t bdeAddrHigh:4; + uint32_t bdeSize:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t bdeSize:24; + uint32_t bdeAddrHigh:4; + uint32_t bdeReserved:4; +#endif +}; + +struct ulp_bde64 { /* SLI-2 */ + union ULP_BDE_TUS { + uint32_t w; + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t bdeFlags:8; /* BDE Flags 0 IS A SUPPORTED + VALUE !! */ + uint32_t bdeSize:24; /* Size of buffer (in bytes) */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t bdeSize:24; /* Size of buffer (in bytes) */ + uint32_t bdeFlags:8; /* BDE Flags 0 IS A SUPPORTED + VALUE !! */ +#endif + +#define BUFF_USE_RSVD 0x01 /* bdeFlags */ +#define BUFF_USE_INTRPT 0x02 /* Not Implemented with LP6000 */ +#define BUFF_USE_CMND 0x04 /* Optional, 1=cmd/rsp 0=data buffer */ +#define BUFF_USE_RCV 0x08 /* "" "", 1=rcv buffer, 0=xmit + buffer */ +#define BUFF_TYPE_32BIT 0x10 /* "" "", 1=32 bit addr 0=64 bit + addr */ +#define BUFF_TYPE_SPECIAL 0x20 /* Not Implemented with LP6000 */ +#define BUFF_TYPE_BDL 0x40 /* Optional, may be set in BDL */ +#define BUFF_TYPE_INVALID 0x80 /* "" "" */ + } f; + } tus; + uint32_t addrLow; + uint32_t addrHigh; +}; +#define BDE64_SIZE_WORD 0 +#define BPL64_SIZE_WORD 0x40 + +typedef struct ULP_BDL { /* SLI-2 */ +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t bdeFlags:8; /* BDL Flags */ + uint32_t bdeSize:24; /* Size of BDL array in host memory (bytes) */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t bdeSize:24; /* Size of BDL array in host memory (bytes) */ + uint32_t bdeFlags:8; /* BDL Flags */ +#endif + + uint32_t addrLow; /* Address 0:31 */ + uint32_t addrHigh; /* Address 32:63 */ + uint32_t ulpIoTag32; /* Can be used for 32 bit I/O Tag */ +} ULP_BDL; + +/* Structure for MB Command LOAD_SM and DOWN_LOAD */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd2:25; + uint32_t acknowledgment:1; + uint32_t version:1; + uint32_t erase_or_prog:1; + uint32_t update_flash:1; + uint32_t update_ram:1; + uint32_t method:1; + uint32_t load_cmplt:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t load_cmplt:1; + uint32_t method:1; + uint32_t update_ram:1; + uint32_t update_flash:1; + uint32_t erase_or_prog:1; + uint32_t version:1; + uint32_t acknowledgment:1; + uint32_t rsvd2:25; +#endif + + uint32_t dl_to_adr_low; + uint32_t dl_to_adr_high; + uint32_t dl_len; + union { + uint32_t dl_from_mbx_offset; + struct ulp_bde dl_from_bde; + struct ulp_bde64 dl_from_bde64; + } un; + +} LOAD_SM_VAR; + +/* Structure for MB Command READ_NVPARM (02) */ + +typedef struct { + uint32_t rsvd1[3]; /* Read as all one's */ + uint32_t rsvd2; /* Read as all zero's */ + uint32_t portname[2]; /* N_PORT name */ + uint32_t nodename[2]; /* NODE name */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t pref_DID:24; + uint32_t hardAL_PA:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t hardAL_PA:8; + uint32_t pref_DID:24; +#endif + + uint32_t rsvd3[21]; /* Read as all one's */ +} READ_NV_VAR; + +/* Structure for MB Command WRITE_NVPARMS (03) */ + +typedef struct { + uint32_t rsvd1[3]; /* Must be all one's */ + uint32_t rsvd2; /* Must be all zero's */ + uint32_t portname[2]; /* N_PORT name */ + uint32_t nodename[2]; /* NODE name */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t pref_DID:24; + uint32_t hardAL_PA:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t hardAL_PA:8; + uint32_t pref_DID:24; +#endif + + uint32_t rsvd3[21]; /* Must be all one's */ +} WRITE_NV_VAR; + +/* Structure for MB Command RUN_BIU_DIAG (04) */ +/* Structure for MB Command RUN_BIU_DIAG64 (0x84) */ + +typedef struct { + uint32_t rsvd1; + union { + struct { + struct ulp_bde xmit_bde; + struct ulp_bde rcv_bde; + } s1; + struct { + struct ulp_bde64 xmit_bde64; + struct ulp_bde64 rcv_bde64; + } s2; + } un; +} BIU_DIAG_VAR; + +/* Structure for MB Command INIT_LINK (05) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd1:24; + uint32_t lipsr_AL_PA:8; /* AL_PA to issue Lip Selective Reset to */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t lipsr_AL_PA:8; /* AL_PA to issue Lip Selective Reset to */ + uint32_t rsvd1:24; +#endif + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t fabric_AL_PA; /* If using a Fabric Assigned AL_PA */ + uint8_t rsvd2; + uint16_t link_flags; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t link_flags; + uint8_t rsvd2; + uint8_t fabric_AL_PA; /* If using a Fabric Assigned AL_PA */ +#endif + +#define FLAGS_LOCAL_LB 0x01 /* link_flags (=1) ENDEC loopback */ +#define FLAGS_TOPOLOGY_MODE_LOOP_PT 0x00 /* Attempt loop then pt-pt */ +#define FLAGS_TOPOLOGY_MODE_PT_PT 0x02 /* Attempt pt-pt only */ +#define FLAGS_TOPOLOGY_MODE_LOOP 0x04 /* Attempt loop only */ +#define FLAGS_TOPOLOGY_MODE_PT_LOOP 0x06 /* Attempt pt-pt then loop */ +#define FLAGS_LIRP_LILP 0x80 /* LIRP / LILP is disabled */ + +#define FLAGS_TOPOLOGY_FAILOVER 0x0400 /* Bit 10 */ +#define FLAGS_LINK_SPEED 0x0800 /* Bit 11 */ + + uint32_t link_speed; +#define LINK_SPEED_AUTO 0 /* Auto selection */ +#define LINK_SPEED_1G 1 /* 1 Gigabaud */ +#define LINK_SPEED_2G 2 /* 2 Gigabaud */ +#define LINK_SPEED_4G 4 /* 4 Gigabaud */ +#define LINK_SPEED_8G 8 /* 4 Gigabaud */ +#define LINK_SPEED_10G 16 /* 10 Gigabaud */ + +} INIT_LINK_VAR; + +/* Structure for MB Command DOWN_LINK (06) */ + +typedef struct { + uint32_t rsvd1; +} DOWN_LINK_VAR; + +/* Structure for MB Command CONFIG_LINK (07) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t cr:1; + uint32_t ci:1; + uint32_t cr_delay:6; + uint32_t cr_count:8; + uint32_t rsvd1:8; + uint32_t MaxBBC:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t MaxBBC:8; + uint32_t rsvd1:8; + uint32_t cr_count:8; + uint32_t cr_delay:6; + uint32_t ci:1; + uint32_t cr:1; +#endif + + uint32_t myId; + uint32_t rsvd2; + uint32_t edtov; + uint32_t arbtov; + uint32_t ratov; + uint32_t rttov; + uint32_t altov; + uint32_t crtov; + uint32_t citov; +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rrq_enable:1; + uint32_t rrq_immed:1; + uint32_t rsvd4:29; + uint32_t ack0_enable:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t ack0_enable:1; + uint32_t rsvd4:29; + uint32_t rrq_immed:1; + uint32_t rrq_enable:1; +#endif +} CONFIG_LINK; + +/* Structure for MB Command PART_SLIM (08) + * will be removed since SLI1 is no longer supported! + */ +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t offCiocb; + uint16_t numCiocb; + uint16_t offRiocb; + uint16_t numRiocb; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t numCiocb; + uint16_t offCiocb; + uint16_t numRiocb; + uint16_t offRiocb; +#endif +} RING_DEF; + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t unused1:24; + uint32_t numRing:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t numRing:8; + uint32_t unused1:24; +#endif + + RING_DEF ringdef[4]; + uint32_t hbainit; +} PART_SLIM_VAR; + +/* Structure for MB Command CONFIG_RING (09) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t unused2:6; + uint32_t recvSeq:1; + uint32_t recvNotify:1; + uint32_t numMask:8; + uint32_t profile:8; + uint32_t unused1:4; + uint32_t ring:4; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t ring:4; + uint32_t unused1:4; + uint32_t profile:8; + uint32_t numMask:8; + uint32_t recvNotify:1; + uint32_t recvSeq:1; + uint32_t unused2:6; +#endif + +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t maxRespXchg; + uint16_t maxOrigXchg; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t maxOrigXchg; + uint16_t maxRespXchg; +#endif + + RR_REG rrRegs[6]; +} CONFIG_RING_VAR; + +/* Structure for MB Command RESET_RING (10) */ + +typedef struct { + uint32_t ring_no; +} RESET_RING_VAR; + +/* Structure for MB Command READ_CONFIG (11) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t cr:1; + uint32_t ci:1; + uint32_t cr_delay:6; + uint32_t cr_count:8; + uint32_t InitBBC:8; + uint32_t MaxBBC:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t MaxBBC:8; + uint32_t InitBBC:8; + uint32_t cr_count:8; + uint32_t cr_delay:6; + uint32_t ci:1; + uint32_t cr:1; +#endif + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t topology:8; + uint32_t myDid:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t myDid:24; + uint32_t topology:8; +#endif + + /* Defines for topology (defined previously) */ +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t AR:1; + uint32_t IR:1; + uint32_t rsvd1:29; + uint32_t ack0:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t ack0:1; + uint32_t rsvd1:29; + uint32_t IR:1; + uint32_t AR:1; +#endif + + uint32_t edtov; + uint32_t arbtov; + uint32_t ratov; + uint32_t rttov; + uint32_t altov; + uint32_t lmt; +#define LMT_RESERVED 0x0 /* Not used */ +#define LMT_266_10bit 0x1 /* 265.625 Mbaud 10 bit iface */ +#define LMT_532_10bit 0x2 /* 531.25 Mbaud 10 bit iface */ +#define LMT_1063_20bit 0x3 /* 1062.5 Mbaud 20 bit iface */ +#define LMT_1063_10bit 0x4 /* 1062.5 Mbaud 10 bit iface */ +#define LMT_2125_10bit 0x8 /* 2125 Mbaud 10 bit iface */ +#define LMT_4250_10bit 0x40 /* 4250 Mbaud 10 bit iface */ + + uint32_t rsvd2; + uint32_t rsvd3; + uint32_t max_xri; + uint32_t max_iocb; + uint32_t max_rpi; + uint32_t avail_xri; + uint32_t avail_iocb; + uint32_t avail_rpi; + uint32_t default_rpi; +} READ_CONFIG_VAR; + +/* Structure for MB Command READ_RCONFIG (12) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd2:7; + uint32_t recvNotify:1; + uint32_t numMask:8; + uint32_t profile:8; + uint32_t rsvd1:4; + uint32_t ring:4; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t ring:4; + uint32_t rsvd1:4; + uint32_t profile:8; + uint32_t numMask:8; + uint32_t recvNotify:1; + uint32_t rsvd2:7; +#endif + +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t maxResp; + uint16_t maxOrig; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t maxOrig; + uint16_t maxResp; +#endif + + RR_REG rrRegs[6]; + +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t cmdRingOffset; + uint16_t cmdEntryCnt; + uint16_t rspRingOffset; + uint16_t rspEntryCnt; + uint16_t nextCmdOffset; + uint16_t rsvd3; + uint16_t nextRspOffset; + uint16_t rsvd4; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t cmdEntryCnt; + uint16_t cmdRingOffset; + uint16_t rspEntryCnt; + uint16_t rspRingOffset; + uint16_t rsvd3; + uint16_t nextCmdOffset; + uint16_t rsvd4; + uint16_t nextRspOffset; +#endif +} READ_RCONF_VAR; + +/* Structure for MB Command READ_SPARM (13) */ +/* Structure for MB Command READ_SPARM64 (0x8D) */ + +typedef struct { + uint32_t rsvd1; + uint32_t rsvd2; + union { + struct ulp_bde sp; /* This BDE points to struct serv_parm + structure */ + struct ulp_bde64 sp64; + } un; +} READ_SPARM_VAR; + +/* Structure for MB Command READ_STATUS (14) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd1:31; + uint32_t clrCounters:1; + uint16_t activeXriCnt; + uint16_t activeRpiCnt; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t clrCounters:1; + uint32_t rsvd1:31; + uint16_t activeRpiCnt; + uint16_t activeXriCnt; +#endif + + uint32_t xmitByteCnt; + uint32_t rcvByteCnt; + uint32_t xmitFrameCnt; + uint32_t rcvFrameCnt; + uint32_t xmitSeqCnt; + uint32_t rcvSeqCnt; + uint32_t totalOrigExchanges; + uint32_t totalRespExchanges; + uint32_t rcvPbsyCnt; + uint32_t rcvFbsyCnt; +} READ_STATUS_VAR; + +/* Structure for MB Command READ_RPI (15) */ +/* Structure for MB Command READ_RPI64 (0x8F) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t nextRpi; + uint16_t reqRpi; + uint32_t rsvd2:8; + uint32_t DID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t reqRpi; + uint16_t nextRpi; + uint32_t DID:24; + uint32_t rsvd2:8; +#endif + + union { + struct ulp_bde sp; + struct ulp_bde64 sp64; + } un; + +} READ_RPI_VAR; + +/* Structure for MB Command READ_XRI (16) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t nextXri; + uint16_t reqXri; + uint16_t rsvd1; + uint16_t rpi; + uint32_t rsvd2:8; + uint32_t DID:24; + uint32_t rsvd3:8; + uint32_t SID:24; + uint32_t rsvd4; + uint8_t seqId; + uint8_t rsvd5; + uint16_t seqCount; + uint16_t oxId; + uint16_t rxId; + uint32_t rsvd6:30; + uint32_t si:1; + uint32_t exchOrig:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t reqXri; + uint16_t nextXri; + uint16_t rpi; + uint16_t rsvd1; + uint32_t DID:24; + uint32_t rsvd2:8; + uint32_t SID:24; + uint32_t rsvd3:8; + uint32_t rsvd4; + uint16_t seqCount; + uint8_t rsvd5; + uint8_t seqId; + uint16_t rxId; + uint16_t oxId; + uint32_t exchOrig:1; + uint32_t si:1; + uint32_t rsvd6:30; +#endif +} READ_XRI_VAR; + +/* Structure for MB Command READ_REV (17) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t cv:1; + uint32_t rr:1; + uint32_t rsvd1:29; + uint32_t rv:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t rv:1; + uint32_t rsvd1:29; + uint32_t rr:1; + uint32_t cv:1; +#endif + + uint32_t biuRev; + uint32_t smRev; + union { + uint32_t smFwRev; + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t ProgType; + uint8_t ProgId; + uint16_t ProgVer:4; + uint16_t ProgRev:4; + uint16_t ProgFixLvl:2; + uint16_t ProgDistType:2; + uint16_t DistCnt:4; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t DistCnt:4; + uint16_t ProgDistType:2; + uint16_t ProgFixLvl:2; + uint16_t ProgRev:4; + uint16_t ProgVer:4; + uint8_t ProgId; + uint8_t ProgType; +#endif + + } b; + } un; + uint32_t endecRev; +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t feaLevelHigh; + uint8_t feaLevelLow; + uint8_t fcphHigh; + uint8_t fcphLow; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t fcphLow; + uint8_t fcphHigh; + uint8_t feaLevelLow; + uint8_t feaLevelHigh; +#endif + + uint32_t postKernRev; + uint32_t opFwRev; + uint8_t opFwName[16]; + uint32_t sli1FwRev; + uint8_t sli1FwName[16]; + uint32_t sli2FwRev; + uint8_t sli2FwName[16]; + uint32_t rsvd2; + uint32_t RandomData[7]; +} READ_REV_VAR; + +/* Structure for MB Command READ_LINK_STAT (18) */ + +typedef struct { + uint32_t rsvd1; + uint32_t linkFailureCnt; + uint32_t lossSyncCnt; + + uint32_t lossSignalCnt; + uint32_t primSeqErrCnt; + uint32_t invalidXmitWord; + uint32_t crcCnt; + uint32_t primSeqTimeout; + uint32_t elasticOverrun; + uint32_t arbTimeout; +} READ_LNK_VAR; + +/* Structure for MB Command REG_LOGIN (19) */ +/* Structure for MB Command REG_LOGIN64 (0x93) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t rsvd1; + uint16_t rpi; + uint32_t rsvd2:8; + uint32_t did:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t rpi; + uint16_t rsvd1; + uint32_t did:24; + uint32_t rsvd2:8; +#endif + + union { + struct ulp_bde sp; + struct ulp_bde64 sp64; + } un; + +} REG_LOGIN_VAR; + +/* Word 30 contents for REG_LOGIN */ +typedef union { + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t rsvd1:12; + uint16_t wd30_class:4; + uint16_t xri; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t xri; + uint16_t wd30_class:4; + uint16_t rsvd1:12; +#endif + } f; + uint32_t word; +} REG_WD30; + +/* Structure for MB Command UNREG_LOGIN (20) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t rsvd1; + uint16_t rpi; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t rpi; + uint16_t rsvd1; +#endif +} UNREG_LOGIN_VAR; + +/* Structure for MB Command UNREG_D_ID (0x23) */ + +typedef struct { + uint32_t did; +} UNREG_D_ID_VAR; + +/* Structure for MB Command READ_LA (21) */ +/* Structure for MB Command READ_LA64 (0x95) */ + +typedef struct { + uint32_t eventTag; /* Event tag */ +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd1:22; + uint32_t pb:1; + uint32_t il:1; + uint32_t attType:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t attType:8; + uint32_t il:1; + uint32_t pb:1; + uint32_t rsvd1:22; +#endif + +#define AT_RESERVED 0x00 /* Reserved - attType */ +#define AT_LINK_UP 0x01 /* Link is up */ +#define AT_LINK_DOWN 0x02 /* Link is down */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t granted_AL_PA; + uint8_t lipAlPs; + uint8_t lipType; + uint8_t topology; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t topology; + uint8_t lipType; + uint8_t lipAlPs; + uint8_t granted_AL_PA; +#endif + +#define TOPOLOGY_PT_PT 0x01 /* Topology is pt-pt / pt-fabric */ +#define TOPOLOGY_LOOP 0x02 /* Topology is FC-AL */ + + union { + struct ulp_bde lilpBde; /* This BDE points to a 128 byte buffer + to */ + /* store the LILP AL_PA position map into */ + struct ulp_bde64 lilpBde64; + } un; + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t Dlu:1; + uint32_t Dtf:1; + uint32_t Drsvd2:14; + uint32_t DlnkSpeed:8; + uint32_t DnlPort:4; + uint32_t Dtx:2; + uint32_t Drx:2; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t Drx:2; + uint32_t Dtx:2; + uint32_t DnlPort:4; + uint32_t DlnkSpeed:8; + uint32_t Drsvd2:14; + uint32_t Dtf:1; + uint32_t Dlu:1; +#endif + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t Ulu:1; + uint32_t Utf:1; + uint32_t Ursvd2:14; + uint32_t UlnkSpeed:8; + uint32_t UnlPort:4; + uint32_t Utx:2; + uint32_t Urx:2; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t Urx:2; + uint32_t Utx:2; + uint32_t UnlPort:4; + uint32_t UlnkSpeed:8; + uint32_t Ursvd2:14; + uint32_t Utf:1; + uint32_t Ulu:1; +#endif + +#define LA_UNKNW_LINK 0x0 /* lnkSpeed */ +#define LA_1GHZ_LINK 0x04 /* lnkSpeed */ +#define LA_2GHZ_LINK 0x08 /* lnkSpeed */ +#define LA_4GHZ_LINK 0x10 /* lnkSpeed */ +#define LA_8GHZ_LINK 0x20 /* lnkSpeed */ +#define LA_10GHZ_LINK 0x40 /* lnkSpeed */ + +} READ_LA_VAR; + +/* Structure for MB Command CLEAR_LA (22) */ + +typedef struct { + uint32_t eventTag; /* Event tag */ + uint32_t rsvd1; +} CLEAR_LA_VAR; + +/* Structure for MB Command DUMP */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd:25; + uint32_t ra:1; + uint32_t co:1; + uint32_t cv:1; + uint32_t type:4; + uint32_t entry_index:16; + uint32_t region_id:16; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t type:4; + uint32_t cv:1; + uint32_t co:1; + uint32_t ra:1; + uint32_t rsvd:25; + uint32_t region_id:16; + uint32_t entry_index:16; +#endif + + uint32_t rsvd1; + uint32_t word_cnt; + uint32_t resp_offset; +} DUMP_VAR; + +#define DMP_MEM_REG 0x1 +#define DMP_NV_PARAMS 0x2 + +#define DMP_REGION_VPD 0xe +#define DMP_VPD_SIZE 0x400 /* maximum amount of VPD */ +#define DMP_RSP_OFFSET 0x14 /* word 5 contains first word of rsp */ +#define DMP_RSP_SIZE 0x6C /* maximum of 27 words of rsp data */ + +/* Structure for MB Command CONFIG_PORT (0x88) */ + +typedef struct { + uint32_t pcbLen; + uint32_t pcbLow; /* bit 31:0 of memory based port config block */ + uint32_t pcbHigh; /* bit 63:32 of memory based port config block */ + uint32_t hbainit[5]; +} CONFIG_PORT_VAR; + +/* SLI-2 Port Control Block */ + +/* SLIM POINTER */ +#define SLIMOFF 0x30 /* WORD */ + +typedef struct _SLI2_RDSC { + uint32_t cmdEntries; + uint32_t cmdAddrLow; + uint32_t cmdAddrHigh; + + uint32_t rspEntries; + uint32_t rspAddrLow; + uint32_t rspAddrHigh; +} SLI2_RDSC; + +typedef struct _PCB { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t type:8; +#define TYPE_NATIVE_SLI2 0x01; + uint32_t feature:8; +#define FEATURE_INITIAL_SLI2 0x01; + uint32_t rsvd:12; + uint32_t maxRing:4; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t maxRing:4; + uint32_t rsvd:12; + uint32_t feature:8; +#define FEATURE_INITIAL_SLI2 0x01; + uint32_t type:8; +#define TYPE_NATIVE_SLI2 0x01; +#endif + + uint32_t mailBoxSize; + uint32_t mbAddrLow; + uint32_t mbAddrHigh; + + uint32_t hgpAddrLow; + uint32_t hgpAddrHigh; + + uint32_t pgpAddrLow; + uint32_t pgpAddrHigh; + SLI2_RDSC rdsc[MAX_RINGS]; +} PCB_t; + +/* NEW_FEATURE */ +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd0:27; + uint32_t discardFarp:1; + uint32_t IPEnable:1; + uint32_t nodeName:1; + uint32_t portName:1; + uint32_t filterEnable:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t filterEnable:1; + uint32_t portName:1; + uint32_t nodeName:1; + uint32_t IPEnable:1; + uint32_t discardFarp:1; + uint32_t rsvd:27; +#endif + + uint8_t portname[8]; /* Used to be struct lpfc_name */ + uint8_t nodename[8]; + uint32_t rsvd1; + uint32_t rsvd2; + uint32_t rsvd3; + uint32_t IPAddress; +} CONFIG_FARP_VAR; + +/* Union of all Mailbox Command types */ +#define MAILBOX_CMD_WSIZE 32 + +typedef union { + uint32_t varWords[MAILBOX_CMD_WSIZE - 1]; + LOAD_SM_VAR varLdSM; /* cmd = 1 (LOAD_SM) */ + READ_NV_VAR varRDnvp; /* cmd = 2 (READ_NVPARMS) */ + WRITE_NV_VAR varWTnvp; /* cmd = 3 (WRITE_NVPARMS) */ + BIU_DIAG_VAR varBIUdiag; /* cmd = 4 (RUN_BIU_DIAG) */ + INIT_LINK_VAR varInitLnk; /* cmd = 5 (INIT_LINK) */ + DOWN_LINK_VAR varDwnLnk; /* cmd = 6 (DOWN_LINK) */ + CONFIG_LINK varCfgLnk; /* cmd = 7 (CONFIG_LINK) */ + PART_SLIM_VAR varSlim; /* cmd = 8 (PART_SLIM) */ + CONFIG_RING_VAR varCfgRing; /* cmd = 9 (CONFIG_RING) */ + RESET_RING_VAR varRstRing; /* cmd = 10 (RESET_RING) */ + READ_CONFIG_VAR varRdConfig; /* cmd = 11 (READ_CONFIG) */ + READ_RCONF_VAR varRdRConfig; /* cmd = 12 (READ_RCONFIG) */ + READ_SPARM_VAR varRdSparm; /* cmd = 13 (READ_SPARM(64)) */ + READ_STATUS_VAR varRdStatus; /* cmd = 14 (READ_STATUS) */ + READ_RPI_VAR varRdRPI; /* cmd = 15 (READ_RPI(64)) */ + READ_XRI_VAR varRdXRI; /* cmd = 16 (READ_XRI) */ + READ_REV_VAR varRdRev; /* cmd = 17 (READ_REV) */ + READ_LNK_VAR varRdLnk; /* cmd = 18 (READ_LNK_STAT) */ + REG_LOGIN_VAR varRegLogin; /* cmd = 19 (REG_LOGIN(64)) */ + UNREG_LOGIN_VAR varUnregLogin; /* cmd = 20 (UNREG_LOGIN) */ + READ_LA_VAR varReadLA; /* cmd = 21 (READ_LA(64)) */ + CLEAR_LA_VAR varClearLA; /* cmd = 22 (CLEAR_LA) */ + DUMP_VAR varDmp; /* Warm Start DUMP mbx cmd */ + UNREG_D_ID_VAR varUnregDID; /* cmd = 0x23 (UNREG_D_ID) */ + CONFIG_FARP_VAR varCfgFarp; /* cmd = 0x25 (CONFIG_FARP) NEW_FEATURE */ + CONFIG_PORT_VAR varCfgPort; /* cmd = 0x88 (CONFIG_PORT) */ +} MAILVARIANTS; + +/* + * SLI-2 specific structures + */ + +typedef struct { + uint32_t cmdPutInx; + uint32_t rspGetInx; +} HGP; + +typedef struct { + uint32_t cmdGetInx; + uint32_t rspPutInx; +} PGP; + +typedef struct _SLI2_DESC { + HGP host[MAX_RINGS]; + uint32_t unused1[16]; + PGP port[MAX_RINGS]; +} SLI2_DESC; + +typedef union { + SLI2_DESC s2; +} SLI_VAR; + +typedef volatile struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t mbxStatus; + uint8_t mbxCommand; + uint8_t mbxReserved:6; + uint8_t mbxHc:1; + uint8_t mbxOwner:1; /* Low order bit first word */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t mbxOwner:1; /* Low order bit first word */ + uint8_t mbxHc:1; + uint8_t mbxReserved:6; + uint8_t mbxCommand; + uint16_t mbxStatus; +#endif + + MAILVARIANTS un; + SLI_VAR us; +} MAILBOX_t; + +/* + * Begin Structure Definitions for IOCB Commands + */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t statAction; + uint8_t statRsn; + uint8_t statBaExp; + uint8_t statLocalError; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t statLocalError; + uint8_t statBaExp; + uint8_t statRsn; + uint8_t statAction; +#endif + /* statRsn P/F_RJT reason codes */ +#define RJT_BAD_D_ID 0x01 /* Invalid D_ID field */ +#define RJT_BAD_S_ID 0x02 /* Invalid S_ID field */ +#define RJT_UNAVAIL_TEMP 0x03 /* N_Port unavailable temp. */ +#define RJT_UNAVAIL_PERM 0x04 /* N_Port unavailable perm. */ +#define RJT_UNSUP_CLASS 0x05 /* Class not supported */ +#define RJT_DELIM_ERR 0x06 /* Delimiter usage error */ +#define RJT_UNSUP_TYPE 0x07 /* Type not supported */ +#define RJT_BAD_CONTROL 0x08 /* Invalid link conrtol */ +#define RJT_BAD_RCTL 0x09 /* R_CTL invalid */ +#define RJT_BAD_FCTL 0x0A /* F_CTL invalid */ +#define RJT_BAD_OXID 0x0B /* OX_ID invalid */ +#define RJT_BAD_RXID 0x0C /* RX_ID invalid */ +#define RJT_BAD_SEQID 0x0D /* SEQ_ID invalid */ +#define RJT_BAD_DFCTL 0x0E /* DF_CTL invalid */ +#define RJT_BAD_SEQCNT 0x0F /* SEQ_CNT invalid */ +#define RJT_BAD_PARM 0x10 /* Param. field invalid */ +#define RJT_XCHG_ERR 0x11 /* Exchange error */ +#define RJT_PROT_ERR 0x12 /* Protocol error */ +#define RJT_BAD_LENGTH 0x13 /* Invalid Length */ +#define RJT_UNEXPECTED_ACK 0x14 /* Unexpected ACK */ +#define RJT_LOGIN_REQUIRED 0x16 /* Login required */ +#define RJT_TOO_MANY_SEQ 0x17 /* Excessive sequences */ +#define RJT_XCHG_NOT_STRT 0x18 /* Exchange not started */ +#define RJT_UNSUP_SEC_HDR 0x19 /* Security hdr not supported */ +#define RJT_UNAVAIL_PATH 0x1A /* Fabric Path not available */ +#define RJT_VENDOR_UNIQUE 0xFF /* Vendor unique error */ + +#define IOERR_SUCCESS 0x00 /* statLocalError */ +#define IOERR_MISSING_CONTINUE 0x01 +#define IOERR_SEQUENCE_TIMEOUT 0x02 +#define IOERR_INTERNAL_ERROR 0x03 +#define IOERR_INVALID_RPI 0x04 +#define IOERR_NO_XRI 0x05 +#define IOERR_ILLEGAL_COMMAND 0x06 +#define IOERR_XCHG_DROPPED 0x07 +#define IOERR_ILLEGAL_FIELD 0x08 +#define IOERR_BAD_CONTINUE 0x09 +#define IOERR_TOO_MANY_BUFFERS 0x0A +#define IOERR_RCV_BUFFER_WAITING 0x0B +#define IOERR_NO_CONNECTION 0x0C +#define IOERR_TX_DMA_FAILED 0x0D +#define IOERR_RX_DMA_FAILED 0x0E +#define IOERR_ILLEGAL_FRAME 0x0F +#define IOERR_EXTRA_DATA 0x10 +#define IOERR_NO_RESOURCES 0x11 +#define IOERR_RESERVED 0x12 +#define IOERR_ILLEGAL_LENGTH 0x13 +#define IOERR_UNSUPPORTED_FEATURE 0x14 +#define IOERR_ABORT_IN_PROGRESS 0x15 +#define IOERR_ABORT_REQUESTED 0x16 +#define IOERR_RECEIVE_BUFFER_TIMEOUT 0x17 +#define IOERR_LOOP_OPEN_FAILURE 0x18 +#define IOERR_RING_RESET 0x19 +#define IOERR_LINK_DOWN 0x1A +#define IOERR_CORRUPTED_DATA 0x1B +#define IOERR_CORRUPTED_RPI 0x1C +#define IOERR_OUT_OF_ORDER_DATA 0x1D +#define IOERR_OUT_OF_ORDER_ACK 0x1E +#define IOERR_DUP_FRAME 0x1F +#define IOERR_LINK_CONTROL_FRAME 0x20 /* ACK_N received */ +#define IOERR_BAD_HOST_ADDRESS 0x21 +#define IOERR_RCV_HDRBUF_WAITING 0x22 +#define IOERR_MISSING_HDR_BUFFER 0x23 +#define IOERR_MSEQ_CHAIN_CORRUPTED 0x24 +#define IOERR_ABORTMULT_REQUESTED 0x25 +#define IOERR_BUFFER_SHORTAGE 0x28 +#define IOERR_DEFAULT 0x29 +#define IOERR_CNT 0x2A + +#define IOERR_DRVR_MASK 0x100 +#define IOERR_SLI_DOWN 0x101 /* ulpStatus - Driver defined */ +#define IOERR_SLI_BRESET 0x102 +#define IOERR_SLI_ABORTED 0x103 +} PARM_ERR; + +typedef union { + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t Rctl; /* R_CTL field */ + uint8_t Type; /* TYPE field */ + uint8_t Dfctl; /* DF_CTL field */ + uint8_t Fctl; /* Bits 0-7 of IOCB word 5 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t Fctl; /* Bits 0-7 of IOCB word 5 */ + uint8_t Dfctl; /* DF_CTL field */ + uint8_t Type; /* TYPE field */ + uint8_t Rctl; /* R_CTL field */ +#endif + +#define BC 0x02 /* Broadcast Received - Fctl */ +#define SI 0x04 /* Sequence Initiative */ +#define LA 0x08 /* Ignore Link Attention state */ +#define LS 0x80 /* Last Sequence */ + } hcsw; + uint32_t reserved; +} WORD5; + +/* IOCB Command template for a generic response */ +typedef struct { + uint32_t reserved[4]; + PARM_ERR perr; +} GENERIC_RSP; + +/* IOCB Command template for XMIT / XMIT_BCAST / RCV_SEQUENCE / XMIT_ELS */ +typedef struct { + struct ulp_bde xrsqbde[2]; + uint32_t xrsqRo; /* Starting Relative Offset */ + WORD5 w5; /* Header control/status word */ +} XR_SEQ_FIELDS; + +/* IOCB Command template for ELS_REQUEST */ +typedef struct { + struct ulp_bde elsReq; + struct ulp_bde elsRsp; + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t word4Rsvd:7; + uint32_t fl:1; + uint32_t myID:24; + uint32_t word5Rsvd:8; + uint32_t remoteID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t myID:24; + uint32_t fl:1; + uint32_t word4Rsvd:7; + uint32_t remoteID:24; + uint32_t word5Rsvd:8; +#endif +} ELS_REQUEST; + +/* IOCB Command template for RCV_ELS_REQ */ +typedef struct { + struct ulp_bde elsReq[2]; + uint32_t parmRo; + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t word5Rsvd:8; + uint32_t remoteID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t remoteID:24; + uint32_t word5Rsvd:8; +#endif +} RCV_ELS_REQ; + +/* IOCB Command template for ABORT / CLOSE_XRI */ +typedef struct { + uint32_t rsvd[3]; + uint32_t abortType; +#define ABORT_TYPE_ABTX 0x00000000 +#define ABORT_TYPE_ABTS 0x00000001 + uint32_t parm; +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t abortContextTag; /* ulpContext from command to abort/close */ + uint16_t abortIoTag; /* ulpIoTag from command to abort/close */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t abortIoTag; /* ulpIoTag from command to abort/close */ + uint16_t abortContextTag; /* ulpContext from command to abort/close */ +#endif +} AC_XRI; + +/* IOCB Command template for ABORT_MXRI64 */ +typedef struct { + uint32_t rsvd[3]; + uint32_t abortType; + uint32_t parm; + uint32_t iotag32; +} A_MXRI64; + +/* IOCB Command template for GET_RPI */ +typedef struct { + uint32_t rsvd[4]; + uint32_t parmRo; +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t word5Rsvd:8; + uint32_t remoteID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t remoteID:24; + uint32_t word5Rsvd:8; +#endif +} GET_RPI; + +/* IOCB Command template for all FCP Initiator commands */ +typedef struct { + struct ulp_bde fcpi_cmnd; /* FCP_CMND payload descriptor */ + struct ulp_bde fcpi_rsp; /* Rcv buffer */ + uint32_t fcpi_parm; + uint32_t fcpi_XRdy; /* transfer ready for IWRITE */ +} FCPI_FIELDS; + +/* IOCB Command template for all FCP Target commands */ +typedef struct { + struct ulp_bde fcpt_Buffer[2]; /* FCP_CMND payload descriptor */ + uint32_t fcpt_Offset; + uint32_t fcpt_Length; /* transfer ready for IWRITE */ +} FCPT_FIELDS; + +/* SLI-2 IOCB structure definitions */ + +/* IOCB Command template for 64 bit XMIT / XMIT_BCAST / XMIT_ELS */ +typedef struct { + ULP_BDL bdl; + uint32_t xrsqRo; /* Starting Relative Offset */ + WORD5 w5; /* Header control/status word */ +} XMT_SEQ_FIELDS64; + +/* IOCB Command template for 64 bit RCV_SEQUENCE64 */ +typedef struct { + struct ulp_bde64 rcvBde; + uint32_t rsvd1; + uint32_t xrsqRo; /* Starting Relative Offset */ + WORD5 w5; /* Header control/status word */ +} RCV_SEQ_FIELDS64; + +/* IOCB Command template for ELS_REQUEST64 */ +typedef struct { + ULP_BDL bdl; +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t word4Rsvd:7; + uint32_t fl:1; + uint32_t myID:24; + uint32_t word5Rsvd:8; + uint32_t remoteID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t myID:24; + uint32_t fl:1; + uint32_t word4Rsvd:7; + uint32_t remoteID:24; + uint32_t word5Rsvd:8; +#endif +} ELS_REQUEST64; + +/* IOCB Command template for GEN_REQUEST64 */ +typedef struct { + ULP_BDL bdl; + uint32_t xrsqRo; /* Starting Relative Offset */ + WORD5 w5; /* Header control/status word */ +} GEN_REQUEST64; + +/* IOCB Command template for RCV_ELS_REQ64 */ +typedef struct { + struct ulp_bde64 elsReq; + uint32_t rcvd1; + uint32_t parmRo; + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t word5Rsvd:8; + uint32_t remoteID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t remoteID:24; + uint32_t word5Rsvd:8; +#endif +} RCV_ELS_REQ64; + +/* IOCB Command template for all 64 bit FCP Initiator commands */ +typedef struct { + ULP_BDL bdl; + uint32_t fcpi_parm; + uint32_t fcpi_XRdy; /* transfer ready for IWRITE */ +} FCPI_FIELDS64; + +/* IOCB Command template for all 64 bit FCP Target commands */ +typedef struct { + ULP_BDL bdl; + uint32_t fcpt_Offset; + uint32_t fcpt_Length; /* transfer ready for IWRITE */ +} FCPT_FIELDS64; + +typedef volatile struct _IOCB { /* IOCB structure */ + union { + GENERIC_RSP grsp; /* Generic response */ + XR_SEQ_FIELDS xrseq; /* XMIT / BCAST / RCV_SEQUENCE cmd */ + struct ulp_bde cont[3]; /* up to 3 continuation bdes */ + RCV_ELS_REQ rcvels; /* RCV_ELS_REQ template */ + AC_XRI acxri; /* ABORT / CLOSE_XRI template */ + A_MXRI64 amxri; /* abort multiple xri command overlay */ + GET_RPI getrpi; /* GET_RPI template */ + FCPI_FIELDS fcpi; /* FCP Initiator template */ + FCPT_FIELDS fcpt; /* FCP target template */ + + /* SLI-2 structures */ + + struct ulp_bde64 cont64[2]; /* up to 2 64 bit continuation + bde_64s */ + ELS_REQUEST64 elsreq64; /* ELS_REQUEST template */ + GEN_REQUEST64 genreq64; /* GEN_REQUEST template */ + RCV_ELS_REQ64 rcvels64; /* RCV_ELS_REQ template */ + XMT_SEQ_FIELDS64 xseq64; /* XMIT / BCAST cmd */ + FCPI_FIELDS64 fcpi64; /* FCP 64 bit Initiator template */ + FCPT_FIELDS64 fcpt64; /* FCP 64 bit target template */ + + uint32_t ulpWord[IOCB_WORD_SZ - 2]; /* generic 6 'words' */ + } un; + union { + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t ulpContext; /* High order bits word 6 */ + uint16_t ulpIoTag; /* Low order bits word 6 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t ulpIoTag; /* Low order bits word 6 */ + uint16_t ulpContext; /* High order bits word 6 */ +#endif + } t1; + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t ulpContext; /* High order bits word 6 */ + uint16_t ulpIoTag1:2; /* Low order bits word 6 */ + uint16_t ulpIoTag0:14; /* Low order bits word 6 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t ulpIoTag0:14; /* Low order bits word 6 */ + uint16_t ulpIoTag1:2; /* Low order bits word 6 */ + uint16_t ulpContext; /* High order bits word 6 */ +#endif + } t2; + } un1; +#define ulpContext un1.t1.ulpContext +#define ulpIoTag un1.t1.ulpIoTag +#define ulpIoTag0 un1.t2.ulpIoTag0 + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t ulpTimeout:8; + uint32_t ulpXS:1; + uint32_t ulpFCP2Rcvy:1; + uint32_t ulpPU:2; + uint32_t ulpIr:1; + uint32_t ulpClass:3; + uint32_t ulpCommand:8; + uint32_t ulpStatus:4; + uint32_t ulpBdeCount:2; + uint32_t ulpLe:1; + uint32_t ulpOwner:1; /* Low order bit word 7 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t ulpOwner:1; /* Low order bit word 7 */ + uint32_t ulpLe:1; + uint32_t ulpBdeCount:2; + uint32_t ulpStatus:4; + uint32_t ulpCommand:8; + uint32_t ulpClass:3; + uint32_t ulpIr:1; + uint32_t ulpPU:2; + uint32_t ulpFCP2Rcvy:1; + uint32_t ulpXS:1; + uint32_t ulpTimeout:8; +#endif + +#define PARM_UNUSED 0 /* PU field (Word 4) not used */ +#define PARM_REL_OFF 1 /* PU field (Word 4) = R. O. */ +#define PARM_READ_CHECK 2 /* PU field (Word 4) = Data Transfer Length */ +#define CLASS1 0 /* Class 1 */ +#define CLASS2 1 /* Class 2 */ +#define CLASS3 2 /* Class 3 */ +#define CLASS_FCP_INTERMIX 7 /* FCP Data->Cls 1, all else->Cls 2 */ + +#define IOSTAT_SUCCESS 0x0 /* ulpStatus - HBA defined */ +#define IOSTAT_FCP_RSP_ERROR 0x1 +#define IOSTAT_REMOTE_STOP 0x2 +#define IOSTAT_LOCAL_REJECT 0x3 +#define IOSTAT_NPORT_RJT 0x4 +#define IOSTAT_FABRIC_RJT 0x5 +#define IOSTAT_NPORT_BSY 0x6 +#define IOSTAT_FABRIC_BSY 0x7 +#define IOSTAT_INTERMED_RSP 0x8 +#define IOSTAT_LS_RJT 0x9 +#define IOSTAT_BA_RJT 0xA +#define IOSTAT_RSVD1 0xB +#define IOSTAT_RSVD2 0xC +#define IOSTAT_RSVD3 0xD +#define IOSTAT_RSVD4 0xE +#define IOSTAT_RSVD5 0xF +#define IOSTAT_DRIVER_REJECT 0x10 /* ulpStatus - Driver defined */ +#define IOSTAT_DEFAULT 0xF /* Same as rsvd5 for now */ +#define IOSTAT_CNT 0x11 + +} IOCB_t; + + +#define SLI1_SLIM_SIZE (4 * 1024) + +/* Up to 498 IOCBs will fit into 16k + * 256 (MAILBOX_t) + 140 (PCB_t) + ( 32 (IOCB_t) * 498 ) = < 16384 + */ +#define SLI2_SLIM_SIZE (16 * 1024) + +/* Maximum IOCBs that will fit in SLI2 slim */ +#define MAX_SLI2_IOCB 498 + +struct lpfc_sli2_slim { + MAILBOX_t mbx; + PCB_t pcb; + IOCB_t IOCBs[MAX_SLI2_IOCB]; +}; + +/******************************************************************* +This macro check PCI device to allow special handling for LC HBAs. + +Parameters: +device : struct pci_dev 's device field + +return 1 => TRUE + 0 => FALSE + *******************************************************************/ +static inline int +lpfc_is_LC_HBA(unsigned short device) +{ + if ((device == PCI_DEVICE_ID_TFLY) || + (device == PCI_DEVICE_ID_PFLY) || + (device == PCI_DEVICE_ID_LP101) || + (device == PCI_DEVICE_ID_BMID) || + (device == PCI_DEVICE_ID_BSMB) || + (device == PCI_DEVICE_ID_ZMID) || + (device == PCI_DEVICE_ID_ZSMB) || + (device == PCI_DEVICE_ID_RFLY)) + return 1; + else + return 0; +} + +#endif /* _H_LPFC_HW */ --- linux-2.6.8.1-t044-driver-update//drivers/scsi/lpfc/lpfc_logmsg.h 1970-01-01 03:00:00.000000000 +0300 +++ rhel4u2//drivers/scsi/lpfc/lpfc_logmsg.h 2005-10-19 11:47:17.000000000 +0400 @@ -0,0 +1,46 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2003-2005 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_logmsg.h 1.33.1.2 2005/06/13 17:16:30EDT sf_support Exp $ + */ + +#ifndef _H_LPFC_LOGMSG +#define _H_LPFC_LOGMSG + +#define LOG_ELS 0x1 /* ELS events */ +#define LOG_DISCOVERY 0x2 /* Link discovery events */ +#define LOG_MBOX 0x4 /* Mailbox events */ +#define LOG_INIT 0x8 /* Initialization events */ +#define LOG_LINK_EVENT 0x10 /* Link events */ +#define LOG_IP 0x20 /* IP traffic history */ +#define LOG_FCP 0x40 /* FCP traffic history */ +#define LOG_NODE 0x80 /* Node table events */ +#define LOG_MISC 0x400 /* Miscellaneous events */ +#define LOG_SLI 0x800 /* SLI events */ +#define LOG_CHK_COND 0x1000 /* FCP Check condition flag */ +#define LOG_LIBDFC 0x2000 /* Libdfc events */ +#define LOG_ALL_MSG 0xffff /* LOG all messages */ + +#define lpfc_printf_log(phba, level, mask, fmt, arg...) \ + { if (((mask) &(phba)->cfg_log_verbose) || (level[1] <= '3')) \ + dev_printk(level, &((phba)->pcidev)->dev, fmt, ##arg); } +#endif + --- linux-2.6.8.1-t044-driver-update//drivers/scsi/Makefile 2005-10-25 14:49:22.457611512 +0400 +++ rhel4u2//drivers/scsi/Makefile 2005-10-19 11:47:17.000000000 +0400 @@ -129,6 +132,7 @@ obj-$(CONFIG_SCSI_SATA_VITESSE) += libat obj-$(CONFIG_SCSI_SATA_SIS) += libata.o sata_sis.o obj-$(CONFIG_SCSI_SATA_SX4) += libata.o sata_sx4.o obj-$(CONFIG_SCSI_SATA_NV) += libata.o sata_nv.o +obj-$(CONFIG_SCSI_LPFC) += lpfc/ obj-$(CONFIG_ARM) += arm/ --- linux-2.6.8.1-t044-driver-update//drivers/scsi/Kconfig 2005-10-25 13:18:59.017099792 +0400 +++ rhel4u2//drivers/scsi/Kconfig 2005-10-19 11:47:17.000000000 +0400 @@ -599,6 +621,13 @@ config SCSI_EATA_PIO To compile this driver as a module, choose M here: the module will be called eata_pio. +config SCSI_LPFC + tristate "Emulex LightPulse Fibre Channel Support" + depends on PCI && SCSI + help + This lpfc driver supports the Emulex LightPulse + family of Fibre Channel PCI host adapters. + config SCSI_FUTURE_DOMAIN tristate "Future Domain 16xx SCSI/AHA-2920A support" depends on (ISA || PCI) && SCSI