Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 3565

kernel-2.6.18-194.11.1.el5.src.rpm

From: Marcus Barrow <mbarrow@redhat.com>
Date: Wed, 20 Aug 2008 17:09:49 -0400
Subject: [scsi] qla2xxx - mgmt. API for FCoE, NetLink
Message-id: 20080820210944.12317.94494.sendpatchset@file.bos.redhat.com
O-Subject: [rhel 5.3 patch] [V3] qla2xxx - mgmt. API for FCoE, NetLink
Bugzilla: 456900
RH-Acked-by: Mike Christie <mchristi@redhat.com>

BZ 456900    qla2xxx- Netlink, FCoE management API

This is version 3 of this patch. It now addresses a problem found by
Dave Jones where the arguments for 2 uses of memset(0 where in the
wrong order. In addition, during further testing, I found a debug
printf() which executed a couple of times per hour.

Support the NetLink interface for FCoE Management API.

This code is currently submitted upstream.

diff --git a/drivers/scsi/qla2xxx/Makefile b/drivers/scsi/qla2xxx/Makefile
index 90562c5..bcc2dfc 100644
--- a/drivers/scsi/qla2xxx/Makefile
+++ b/drivers/scsi/qla2xxx/Makefile
@@ -1,5 +1,6 @@
+EXTRA_CFLAGS += -DNETLINK_FCTRANSPORT=20
 qla2xxx-y := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \
-		qla_dbg.o qla_sup.o qla_attr.o qla_mid.o ql2100_fw.o \
+		qla_dbg.o qla_sup.o qla_attr.o qla_mid.o qla_nlnk.o ql2100_fw.o \
 		ql2200_fw.o ql2300_fw.o ql2322_fw.o ql2400_fw.o ql2500_fw.o
 
 obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx.o
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index c3257d3..e446f0f 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -2233,6 +2233,8 @@ struct qla_statistics {
 	uint32_t total_isp_aborts;
 };
 
+#include "qla_nlnk.h"
+
 /*
  * Linux Host Adapter structure
  */
@@ -2669,6 +2671,7 @@ typedef struct scsi_qla_host {
 	int	pass_thru_cmd_in_process;
 
 	struct qla_chip_state_84xx *cs84xx;
+	struct qlfc_aen_log aen_log;
 	struct qla_statistics qla_stats;
 } scsi_qla_host_t;
 
@@ -2736,4 +2739,23 @@ typedef struct scsi_qla_host {
 #define CMD_ACTUAL_SNSLEN(Cmnd)	((Cmnd)->SCp.Message)
 #define CMD_ENTRY_STATUS(Cmnd)	((Cmnd)->SCp.have_data_in)
 
+/*
+ * qla2xxx_log_aen : Logs AENs. hardware_lock is assumed to be held.
+ */
+static inline void
+qla2xxx_log_aen(scsi_qla_host_t *ha, uint16_t code, uint16_t p1, uint16_t p2,
+	uint16_t p3)
+{
+	struct qlfc_aen_entry *aen;
+
+	if (ha->aen_log.num_events < QLFC_MAX_AEN) {
+		aen = &ha->aen_log.aen[ha->aen_log.num_events];
+		aen->event_code = code;
+		aen->payload[0] = p1;
+		aen->payload[1] = p2;
+		aen->payload[2] = p3;
+		ha->aen_log.num_events++;
+	}
+}
+
 #endif
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 3106455..1da372d 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -379,4 +379,10 @@ extern void qla2x00_init_host_attr(scsi_qla_host_t *);
 extern void qla2x00_alloc_sysfs_attr(scsi_qla_host_t *);
 extern void qla2x00_free_sysfs_attr(scsi_qla_host_t *);
 
+/*
+ * Global functions in qla_nlk.c
+ */
+extern int ql_nl_register(void);
+extern void ql_nl_unregister(void);
+
 #endif /* _QLA_GBL_H */
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index cef0cc1..08ad762 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -260,6 +260,8 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
 	uint8_t		rscn_queue_index;
 	unsigned long	flags;
 
+	qla2xxx_log_aen(ha, mb[0], mb[1], mb[2], mb[3]);
+
 	/* Setup to process RIO completion. */
 	handle_cnt = 0;
 	switch (mb[0]) {
diff --git a/drivers/scsi/qla2xxx/qla_nlnk.c b/drivers/scsi/qla2xxx/qla_nlnk.c
new file mode 100644
index 0000000..f65a7f0
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_nlnk.c
@@ -0,0 +1,413 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c)  2003-2005 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+
+#include "qla_def.h"
+#include <net/sock.h>
+#include <net/netlink.h>
+#include "qla_nlnk.h"
+
+static struct sock *ql_fc_nl_sock = NULL;
+static int ql_fc_nl_event(struct notifier_block *this,
+			unsigned long event, void *ptr);
+
+static struct notifier_block ql_fc_nl_notifier = {
+	.notifier_call = ql_fc_nl_event,
+};
+
+static struct qlfc_aen_log aen_log;
+
+/*
+ * local functions
+ */
+static void ql_fc_nl_rcv_msg(struct sk_buff *skb);
+static int ql_fc_proc_nl_rcv_msg(struct sk_buff *skb,
+		struct nlmsghdr *nlh, int rcvlen);
+static int ql_fc_nl_rsp(uint32_t pid, uint32_t seq, uint32_t type,
+		void *hdr, int hdr_len, void *payload, int size);
+
+static int qla84xx_update_fw(struct scsi_qla_host *ha,
+		uint32_t diag_fw, uint32_t len, uint8_t *fw_bytes)
+{
+	void *fw_buffer;
+	struct verify_chip_entry_84xx *mn;
+	dma_addr_t fw_dma, mn_dma;
+	int ret = 0;
+	uint32_t fw_ver;
+	uint16_t options;
+
+	fw_ver = le32_to_cpu(*((uint32_t *)fw_bytes));
+	if (!fw_ver) {
+		printk(KERN_ERR "%s(%lu): invalid fw revision 0x%x\n",
+			__FUNCTION__, ha->host_no, fw_ver);
+		return (-EINVAL);
+	}
+
+	fw_buffer = dma_alloc_coherent(&ha->pdev->dev, len, &fw_dma, GFP_KERNEL);
+	if (fw_buffer == NULL) {
+		printk(KERN_ERR "%s: dma alloc for fw buffer failed%lu\n",
+			__FUNCTION__, ha->host_no);
+		return (-ENOMEM);
+	}
+
+	mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma);
+	if (mn == NULL) {
+		printk(KERN_ERR "%s: dma alloc for fw buffer failed%lu\n",
+			__FUNCTION__, ha->host_no);
+		ret = -ENOMEM;
+		goto exit_updfw;
+	}
+
+	/* Copy the firmware into DMA Buffer */
+	memcpy(fw_buffer, fw_bytes, len);
+
+	/* Create iocb and issue it */
+	memset(mn, 0, sizeof(*mn));
+	
+	mn->entry_type = VERIFY_CHIP_IOCB_TYPE;
+	mn->entry_count = 1;
+
+	options = VCO_FORCE_UPDATE | VCO_END_OF_DATA;
+	if (diag_fw)
+		options |= VCO_DIAG_FW;
+	mn->options = cpu_to_le16(options);
+ 
+	mn->fw_ver = cpu_to_le32(fw_ver);
+	mn->fw_size = cpu_to_le32(len);
+	mn->fw_seq_size = cpu_to_le32(len);
+
+	mn->dseg_address[0] = cpu_to_le32(LSD(fw_dma));
+	mn->dseg_address[1] = cpu_to_le32(MSD(fw_dma));
+	mn->dseg_length = cpu_to_le16(1);
+	
+	ret = qla2x00_issue_iocb(ha, mn, mn_dma, 0);
+
+	if (ret != QLA_SUCCESS) {
+		printk(KERN_ERR "%s(%lu): failed\n", __func__, ha->host_no);
+	}
+
+	dma_pool_free(ha->s_dma_pool, mn, mn_dma);
+exit_updfw:
+	dma_free_coherent(&ha->pdev->dev, len, fw_buffer, fw_dma);
+	return ret;
+}
+
+static int
+qla84xx_mgmt_cmd(scsi_qla_host_t *ha, struct qla_fc_msg *cmd,
+	uint32_t pid, uint32_t seq, uint32_t type)
+{
+	struct access_chip_84xx *mn;
+	dma_addr_t mn_dma, mgmt_dma;
+	void *mgmt_b = NULL;
+	int ret = 0;
+	int rsp_hdr_len, len = 0;
+	struct qla84_msg_mgmt *ql84_mgmt;
+
+	ql84_mgmt = &cmd->u.utok.mgmt;
+	rsp_hdr_len = offsetof(struct qla_fc_msg, u) + 
+			offsetof(struct qla84_msg_mgmt, payload);
+
+	mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma);
+	if (mn == NULL) {
+		printk(KERN_ERR "%s: dma alloc for fw buffer failed%lu\n",
+			__FUNCTION__, ha->host_no);
+		return (-ENOMEM);
+	}
+
+	memset(mn, 0, sizeof (*mn));
+
+	mn->entry_type = ACCESS_CHIP_IOCB_TYPE;
+	mn->entry_count = 1;
+
+	switch (ql84_mgmt->cmd) {
+	case QLA84_MGMT_READ_MEM:
+		mn->options = cpu_to_le16(ACO_DUMP_MEMORY);
+		mn->parameter1 = cpu_to_le32(ql84_mgmt->mgmtp.u.mem.start_addr);
+		break;
+	case QLA84_MGMT_WRITE_MEM:
+		mn->options = cpu_to_le16(ACO_LOAD_MEMORY);
+		mn->parameter1 = cpu_to_le32(ql84_mgmt->mgmtp.u.mem.start_addr);
+		break;
+	case QLA84_MGMT_CHNG_CONFIG:
+		mn->options = cpu_to_le16(ACO_CHANGE_CONFIG_PARAM);
+		mn->parameter1 = cpu_to_le32(ql84_mgmt->mgmtp.u.config.id);
+		mn->parameter2 = cpu_to_le32(ql84_mgmt->mgmtp.u.config.param0);
+		mn->parameter3 = cpu_to_le32(ql84_mgmt->mgmtp.u.config.param1);
+		break;
+	case QLA84_MGMT_GET_INFO:
+		mn->options = cpu_to_le16(ACO_REQUEST_INFO);
+		mn->parameter1 = cpu_to_le32(ql84_mgmt->mgmtp.u.info.type);
+		mn->parameter2 = cpu_to_le32(ql84_mgmt->mgmtp.u.info.context);
+		break;
+	default:
+		ret = -EIO;
+		goto exit_mgmt0;
+	}
+
+	if ((len = ql84_mgmt->len) && ql84_mgmt->cmd != QLA84_MGMT_CHNG_CONFIG) {
+		mgmt_b = dma_alloc_coherent(&ha->pdev->dev, len,
+				&mgmt_dma, GFP_KERNEL);
+		if (mgmt_b == NULL) {
+			printk(KERN_ERR "%s: dma alloc mgmt_b failed%lu\n",
+				__func__, ha->host_no);
+			ret = -ENOMEM;
+			goto exit_mgmt0;
+		}
+		mn->total_byte_cnt = cpu_to_le32(ql84_mgmt->len);
+		mn->dseg_count = cpu_to_le16(1);
+		mn->dseg_address[0] = cpu_to_le32(LSD(mgmt_dma));
+		mn->dseg_address[1] = cpu_to_le32(MSD(mgmt_dma));
+		mn->dseg_length = cpu_to_le32(len);
+
+		if (ql84_mgmt->cmd == QLA84_MGMT_WRITE_MEM) {
+			memcpy(mgmt_b, ql84_mgmt->payload, len);
+		}
+	}
+
+	ret = qla2x00_issue_iocb(ha, mn, mn_dma, 0);
+	cmd->error = ret;
+
+	if (ret != QLA_SUCCESS) {
+		printk(KERN_ERR "%s(%lu): failed\n", __func__, ha->host_no);
+		ret = ql_fc_nl_rsp(pid, seq, type, cmd, rsp_hdr_len, NULL, 0);
+	} else if ((ql84_mgmt->cmd == QLA84_MGMT_READ_MEM)||
+			(ql84_mgmt->cmd == QLA84_MGMT_GET_INFO)) {
+		ret = ql_fc_nl_rsp(pid, seq, type, cmd, rsp_hdr_len, mgmt_b, len);
+	}
+
+	if (mgmt_b)
+		dma_free_coherent(&ha->pdev->dev, len, mgmt_b, mgmt_dma);
+
+exit_mgmt0:
+	dma_pool_free(ha->s_dma_pool, mn, mn_dma);
+	return ret;
+}
+
+static void
+ql_fc_get_aen(scsi_qla_host_t *ha)
+{
+	unsigned long flags;
+
+	memset(&aen_log, 0, sizeof (struct qlfc_aen_log));
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+
+	memcpy(&aen_log, &ha->aen_log, sizeof(struct qlfc_aen_log));
+	ha->aen_log.num_events = 0;
+
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+/*
+ * Netlink Interface Related Functions
+ */
+
+void
+ql_fc_nl_rcv(struct sock *sk, int len)
+{
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
+		ql_fc_nl_rcv_msg(skb);
+		kfree_skb(skb);
+	}
+}
+
+static void
+ql_fc_nl_rcv_msg(struct sk_buff *skb)
+{
+	struct nlmsghdr *nlh;
+	struct scsi_nl_hdr *snlh;
+	uint32_t rlen;
+	int err;
+
+	while (skb->len >= NLMSG_SPACE(0)) {
+		err = 0;
+
+		nlh = (struct nlmsghdr *) skb->data;
+
+		if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*snlh))) ||
+		    (skb->len < nlh->nlmsg_len)) {
+			printk(KERN_WARNING "%s: discarding partial skb\n",
+			       __FUNCTION__);
+			break;
+		}
+
+		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+		if (rlen > skb->len) {
+			printk(KERN_WARNING "%s: rlen > skb->len\n",
+				 __FUNCTION__);
+			rlen = skb->len;
+		}
+
+		if (nlh->nlmsg_type != FC_TRANSPORT_MSG) {
+			printk(KERN_WARNING "%s: Not FC_TRANSPORT_MSG\n",
+			       __FUNCTION__);
+			err = -EBADMSG;
+			goto next_msg;
+		}
+
+		snlh = NLMSG_DATA(nlh);
+		if ((snlh->version != SCSI_NL_VERSION) ||
+		    (snlh->magic != SCSI_NL_MAGIC)) {
+			printk(KERN_WARNING "%s: Bad Version or Magic number\n",
+			       __FUNCTION__);
+			err = -EPROTOTYPE;
+			goto next_msg;
+		}
+		err = ql_fc_proc_nl_rcv_msg(skb, nlh, rlen);
+next_msg:
+		if (err)
+			netlink_ack(skb, nlh, err);
+		skb_pull(skb, rlen);
+	}
+}
+
+static int
+ql_fc_proc_nl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int rcvlen)
+{
+	struct scsi_nl_hdr *snlh;
+	struct qla_fc_msg  *ql_cmd;
+	struct Scsi_Host *shost;
+	struct scsi_qla_host *ha;
+	int err = 0;
+	int rsp_hdr_len;
+	
+	snlh = NLMSG_DATA(nlh);
+
+	/* Only vendor specific commands are supported */
+	if (!(snlh->msgtype & FC_NL_VNDR_SPECIFIC))
+		return -EBADMSG;
+	
+	ql_cmd = (struct qla_fc_msg *)((char *)snlh + sizeof (struct scsi_nl_hdr));
+
+	if (ql_cmd->magic != QL_FC_NL_MAGIC)
+		return -EBADMSG;
+
+	shost = scsi_host_lookup(ql_cmd->host_no);
+	if (IS_ERR(shost)) {
+		printk(KERN_ERR "%s: could not find host no %u\n", __FUNCTION__,
+			ql_cmd->host_no);
+		err = -ENODEV;
+		goto exit_proc_nl_rcv_msg;
+	}
+
+	ha = (struct scsi_qla_host *)shost->hostdata;
+	
+	if (!ha || !IS_QLA84XX(ha)) {
+		printk(KERN_ERR "%s: invalid host ha = %p dtype = 0x%x\n",
+			__FUNCTION__, ha, (ha ? DT_MASK(ha): ~0));
+		err = -ENODEV;
+		goto exit_proc_nl_rcv_msg;
+	}
+
+	switch (ql_cmd->cmd) {
+
+	case QLA84_RESET:
+	
+		rsp_hdr_len = offsetof(struct qla_fc_msg, u);
+		err = qla84xx_reset(ha, ql_cmd->u.utok.qla84_reset.diag_fw);
+		ql_cmd->error = err;
+
+		err = ql_fc_nl_rsp(NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq,
+			(uint32_t)nlh->nlmsg_type, ql_cmd, rsp_hdr_len, NULL, 0);
+		break;
+
+	case QLA84_UPDATE_FW:
+		rsp_hdr_len = offsetof(struct qla_fc_msg, u);
+		err = qla84xx_update_fw(ha,
+			ql_cmd->u.utok.qla84_update_fw.diag_fw,
+			ql_cmd->u.utok.qla84_update_fw.len,
+			ql_cmd->u.utok.qla84_update_fw.fw_bytes);
+		ql_cmd->error = err;
+
+		err = ql_fc_nl_rsp(NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq,
+			(uint32_t)nlh->nlmsg_type, ql_cmd, rsp_hdr_len, NULL, 0);
+		break;
+
+	case QLA84_MGMT_CMD:
+		err = qla84xx_mgmt_cmd(ha, ql_cmd, NETLINK_CREDS(skb)->pid,
+				nlh->nlmsg_seq, (uint32_t)nlh->nlmsg_type);
+		break;
+
+	case QLFC_GET_AEN:
+		rsp_hdr_len = offsetof(struct qla_fc_msg, u);
+		ql_cmd->error = 0;
+		ql_fc_get_aen(ha);
+		err = ql_fc_nl_rsp(NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq,
+			(uint32_t)nlh->nlmsg_type, ql_cmd, rsp_hdr_len,
+			&aen_log, sizeof(struct qlfc_aen_log));
+		break;
+	default:
+		err = -EBADMSG;
+	}
+
+exit_proc_nl_rcv_msg:
+	return err;
+}
+
+static int
+ql_fc_nl_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+	DEBUG16(printk(KERN_WARNING "%s: event 0x%lx ptr = %p\n", __func__, event, ptr));
+	return NOTIFY_DONE;
+}
+
+static int
+ql_fc_nl_rsp(uint32_t pid, uint32_t seq, uint32_t type, void *hdr, int hdr_len,
+	void *payload, int size)
+{
+	struct sk_buff *skb;
+	struct nlmsghdr *nlh;
+	int rc;
+	int len = NLMSG_SPACE(size + hdr_len);
+	
+	skb = alloc_skb(len, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_ERR "%s: Could not alloc skb\n", __func__);
+		return -ENOMEM;
+	}
+	nlh = __nlmsg_put(skb, pid, seq, type, (len - sizeof(*nlh)), 0);
+	nlh->nlmsg_flags = 0;
+	memcpy(NLMSG_DATA(nlh), hdr, hdr_len);
+	
+	if (payload)
+		memcpy((void *)((char *)(NLMSG_DATA(nlh)) + hdr_len), payload, size);
+	
+	rc = netlink_unicast(ql_fc_nl_sock, skb, pid, MSG_DONTWAIT);
+	if (rc < 0) {
+		printk(KERN_ERR "%s: netlink_unicast failed\n", __func__);
+		return rc;
+	}
+	return 0;
+}
+
+int
+ql_nl_register(void)
+{
+	int error = 0;
+
+	error = netlink_register_notifier(&ql_fc_nl_notifier);
+	if (!error) {
+		ql_fc_nl_sock = netlink_kernel_create(NETLINK_FCTRANSPORT,
+					QL_FC_NL_GROUP_CNT, ql_fc_nl_rcv,
+					THIS_MODULE);
+		if (!ql_fc_nl_sock) {
+			netlink_unregister_notifier(&ql_fc_nl_notifier);
+			error = -ENODEV;
+		}
+	}
+	return (error);
+}
+
+void
+ql_nl_unregister()
+{
+	if (ql_fc_nl_sock) {
+		sock_release(ql_fc_nl_sock->sk_socket);
+		netlink_unregister_notifier(&ql_fc_nl_notifier);
+	}
+}
diff --git a/drivers/scsi/qla2xxx/qla_nlnk.h b/drivers/scsi/qla2xxx/qla_nlnk.h
new file mode 100644
index 0000000..930ab14
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_nlnk.h
@@ -0,0 +1,158 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c)  2003-2005 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#ifndef _QLA_NLNK_H_
+#define _QLA_NLNK_H_
+
+#ifndef NETLINK_FCTRANSPORT
+#define NETLINK_FCTRANSPORT 20
+#endif
+#define QL_FC_NL_GROUP_CNT	0
+
+#define FC_TRANSPORT_MSG		NLMSG_MIN_TYPE + 1
+
+/* 
+ * Transport Message Types
+ */
+#define FC_NL_VNDR_SPECIFIC	0x8000
+
+/*
+ * Structures
+ */
+
+struct qla84_mgmt_param {
+	union {
+		struct {
+			uint32_t start_addr;
+		} mem; /* for QLA84_MGMT_READ/WRITE_MEM */ 
+		struct {
+			uint32_t id;
+#define QLA84_MGMT_CONFIG_ID_UIF	1
+#define QLA84_MGMT_CONFIG_ID_FCOE_COS	2
+#define QLA84_MGMT_CONFIG_ID_PAUSE	3
+#define QLA84_MGMT_CONFIG_ID_TIMEOUTS	4
+
+			uint32_t param0;
+			uint32_t param1;
+		} config; /* for QLA84_MGMT_CHNG_CONFIG */
+
+		struct {
+			uint32_t type;
+#define QLA84_MGMT_INFO_CONFIG_LOG_DATA		1 /* Get Config Log Data */
+#define QLA84_MGMT_INFO_LOG_DATA		2 /* Get Log Data */
+#define QLA84_MGMT_INFO_PORT_STAT		3 /* Get Port Statistics */
+#define QLA84_MGMT_INFO_LIF_STAT		4 /* Get LIF Statistics  */
+#define QLA84_MGMT_INFO_ASIC_STAT		5 /* Get ASIC Statistics */
+#define QLA84_MGMT_INFO_CONFIG_PARAMS		6 /* Get Config Parameters */
+#define QLA84_MGMT_INFO_PANIC_LOG		7 /* Get Panic Log */
+
+			uint32_t context;
+/*
+ * context definitions for QLA84_MGMT_INFO_CONFIG_LOG_DATA
+ */
+#define IC_LOG_DATA_LOG_ID_DEBUG_LOG			0
+#define IC_LOG_DATA_LOG_ID_LEARN_LOG			1
+#define IC_LOG_DATA_LOG_ID_FC_ACL_INGRESS_LOG		2
+#define IC_LOG_DATA_LOG_ID_FC_ACL_EGRESS_LOG		3
+#define IC_LOG_DATA_LOG_ID_ETHERNET_ACL_INGRESS_LOG	4
+#define IC_LOG_DATA_LOG_ID_ETHERNET_ACL_EGRESS_LOG	5
+#define IC_LOG_DATA_LOG_ID_MESSAGE_TRANSMIT_LOG		6
+#define IC_LOG_DATA_LOG_ID_MESSAGE_RECEIVE_LOG		7
+#define IC_LOG_DATA_LOG_ID_LINK_EVENT_LOG		8
+#define IC_LOG_DATA_LOG_ID_DCX_LOG			9
+
+/*
+ * context definitions for QLA84_MGMT_INFO_PORT_STAT
+ */
+#define IC_PORT_STATISTICS_PORT_NUMBER_ETHERNET_PORT0	0
+#define IC_PORT_STATISTICS_PORT_NUMBER_ETHERNET_PORT1	1
+#define IC_PORT_STATISTICS_PORT_NUMBER_NSL_PORT0	2
+#define IC_PORT_STATISTICS_PORT_NUMBER_NSL_PORT1	3
+#define IC_PORT_STATISTICS_PORT_NUMBER_FC_PORT0		4
+#define IC_PORT_STATISTICS_PORT_NUMBER_FC_PORT1		5
+
+
+/*
+ * context definitions for QLA84_MGMT_INFO_LIF_STAT
+ */
+#define IC_LIF_STATISTICS_LIF_NUMBER_ETHERNET_PORT0	0
+#define IC_LIF_STATISTICS_LIF_NUMBER_ETHERNET_PORT1	1
+#define IC_LIF_STATISTICS_LIF_NUMBER_FC_PORT0		2
+#define IC_LIF_STATISTICS_LIF_NUMBER_FC_PORT1		3
+#define IC_LIF_STATISTICS_LIF_NUMBER_CPU		6
+
+		} info; /* for QLA84_MGMT_GET_INFO */
+	} u;
+};
+
+#define QLFC_MAX_AEN	256
+struct qlfc_aen_entry {
+	uint16_t event_code;
+	uint16_t payload[3];
+};
+
+struct qlfc_aen_log {
+	uint32_t num_events;
+	struct qlfc_aen_entry aen[QLFC_MAX_AEN];
+};
+
+struct qla84_msg_mgmt {
+	uint16_t cmd;
+#define QLA84_MGMT_READ_MEM	0x00
+#define QLA84_MGMT_WRITE_MEM	0x01
+#define QLA84_MGMT_CHNG_CONFIG	0x02
+#define QLA84_MGMT_GET_INFO	0x03
+	uint16_t rsrvd;
+	struct qla84_mgmt_param mgmtp;/* parameters for cmd */
+	uint32_t len; /* bytes in payload following this struct */
+	uint8_t payload[0]; /* payload for cmd */
+};
+
+struct qla_fc_msg {
+
+	uint32_t magic;
+#define QL_FC_NL_MAGIC	0xFCAB1FC1
+
+	uint32_t cmd;
+#define QLA84_RESET	0x01
+#define QLA84_UPDATE_FW	0x02
+#define QLA84_MGMT_CMD	0x03
+#define QLFC_GET_AEN	0x04
+
+	uint32_t error; /* interface or resource error holder*/
+	uint32_t host_no;
+
+	union {
+		union {
+			struct msg_reset {
+				/*
+				 * diag_fw = 0  for operational fw 
+				 * otherwise diagnostic fw 
+				 */
+				uint32_t diag_fw; 
+			} qla84_reset;
+
+			struct msg_update_fw {
+				/*
+				 * diag_fw = 0  for operational fw 
+				 * otherwise diagnostic fw 
+				 */
+				uint32_t diag_fw; 
+				uint32_t len; /* size of fw in bytes */
+				uint8_t fw_bytes[0];
+			} qla84_update_fw;
+
+			struct qla84_msg_mgmt mgmt;
+		} utok;
+	
+		union {
+			struct qla84_msg_mgmt mgmt;
+			struct qlfc_aen_log aen_log;
+		} ktou;
+	} u;
+} __attribute__ ((aligned (sizeof(uint64_t))));
+	
+#endif /* _QLA_NLNK_H_ */
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 83386fe..807b80e 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -2977,10 +2977,17 @@ qla2x00_module_init(void)
 		kmem_cache_destroy(srb_cachep);
 		return -ENODEV;
 	}
+	if (ql_nl_register()) {
+		kmem_cache_destroy(srb_cachep);
+		fc_release_transport(qla2xxx_transport_template);
+		return -ENODEV;
+	}
+
 
 	printk(KERN_INFO "QLogic Fibre Channel HBA Driver\n");
 	ret = pci_register_driver(&qla2xxx_pci_driver);
 	if (ret) {
+		ql_nl_unregister();
 		kmem_cache_destroy(srb_cachep);
 		fc_release_transport(qla2xxx_transport_template);
 	}
@@ -2995,6 +3002,7 @@ qla2x00_module_exit(void)
 {
 	pci_unregister_driver(&qla2xxx_pci_driver);
 	/* qla2x00_release_firmware(); */
+	ql_nl_unregister();
 	kmem_cache_destroy(srb_cachep);
 	fc_release_transport(qla2xxx_transport_template);
 }