Commit 61d8658b authored by Dupuis, Chad's avatar Dupuis, Chad Committed by Martin K. Petersen

scsi: qedf: Add QLogic FastLinQ offload FCoE driver framework.

The QLogic FastLinQ Driver for FCoE (qedf) is the FCoE specific module
for 41000 Series Converged Network Adapters by QLogic. This patch
consists of following changes:

- MAINTAINERS Makefile and Kconfig changes for qedf
- PCI driver registration
- libfc/fcoe host level initialization
- SCSI host template initialization and callbacks
- Debugfs and log level infrastructure
- Link handling
- Firmware interface structures
- QED core module initialization
- Light L2 interface callbacks
- I/O request initialization
- Firmware I/O completion handling
- Firmware ELS request/response handling
- FIP request/response handled by the driver itself
Signed-off-by: default avatarNilesh Javali <nilesh.javali@cavium.com>
Signed-off-by: default avatarManish Rangankar <manish.rangankar@cavium.com>
Signed-off-by: default avatarSaurav Kashyap <saurav.kashyap@cavium.com>
Signed-off-by: default avatarArun Easi <arun.easi@cavium.com>
Signed-off-by: default avatarChad Dupuis <chad.dupuis@cavium.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 67f2db87
...@@ -10242,6 +10242,12 @@ L: linux-scsi@vger.kernel.org ...@@ -10242,6 +10242,12 @@ L: linux-scsi@vger.kernel.org
S: Supported S: Supported
F: drivers/scsi/qedi/ F: drivers/scsi/qedi/
QLOGIC QL41xxx FCOE DRIVER
M: QLogic-Storage-Upstream@cavium.com
L: linux-scsi@vger.kernel.org
S: Supported
F: drivers/scsi/qedf/
QNX4 FILESYSTEM QNX4 FILESYSTEM
M: Anders Larsen <al@alarsen.net> M: Anders Larsen <al@alarsen.net>
W: http://www.alarsen.net/linux/qnx4fs/ W: http://www.alarsen.net/linux/qnx4fs/
......
...@@ -1235,6 +1235,7 @@ config SCSI_QLOGICPTI ...@@ -1235,6 +1235,7 @@ config SCSI_QLOGICPTI
source "drivers/scsi/qla2xxx/Kconfig" source "drivers/scsi/qla2xxx/Kconfig"
source "drivers/scsi/qla4xxx/Kconfig" source "drivers/scsi/qla4xxx/Kconfig"
source "drivers/scsi/qedi/Kconfig" source "drivers/scsi/qedi/Kconfig"
source "drivers/scsi/qedf/Kconfig"
config SCSI_LPFC config SCSI_LPFC
tristate "Emulex LightPulse Fibre Channel Support" tristate "Emulex LightPulse Fibre Channel Support"
......
...@@ -41,6 +41,7 @@ obj-$(CONFIG_FCOE) += fcoe/ ...@@ -41,6 +41,7 @@ obj-$(CONFIG_FCOE) += fcoe/
obj-$(CONFIG_FCOE_FNIC) += fnic/ obj-$(CONFIG_FCOE_FNIC) += fnic/
obj-$(CONFIG_SCSI_SNIC) += snic/ obj-$(CONFIG_SCSI_SNIC) += snic/
obj-$(CONFIG_SCSI_BNX2X_FCOE) += libfc/ fcoe/ bnx2fc/ obj-$(CONFIG_SCSI_BNX2X_FCOE) += libfc/ fcoe/ bnx2fc/
obj-$(CONFIG_QEDF) += qedf/
obj-$(CONFIG_ISCSI_TCP) += libiscsi.o libiscsi_tcp.o iscsi_tcp.o obj-$(CONFIG_ISCSI_TCP) += libiscsi.o libiscsi_tcp.o iscsi_tcp.o
obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o
obj-$(CONFIG_ISCSI_BOOT_SYSFS) += iscsi_boot_sysfs.o obj-$(CONFIG_ISCSI_BOOT_SYSFS) += iscsi_boot_sysfs.o
......
config QEDF
tristate "QLogic QEDF 25/40/100Gb FCoE Initiator Driver Support"
depends on PCI && SCSI
depends on QED
depends on LIBFC
depends on LIBFCOE
select QED_LL2
select QED_FCOE
---help---
This driver supports FCoE offload for the QLogic FastLinQ
41000 Series Converged Network Adapters.
obj-$(CONFIG_QEDF) := qedf.o
qedf-y = qedf_dbg.o qedf_main.o qedf_io.o qedf_fip.o \
qedf_attr.o qedf_els.o
qedf-$(CONFIG_DEBUG_FS) += qedf_debugfs.o
This diff is collapsed.
/*
* QLogic FCoE Offload Driver
* Copyright (c) 2016 Cavium Inc.
*
* This software is available under the terms of the GNU General Public License
* (GPL) Version 2, available from the file COPYING in the main directory of
* this source tree.
*/
#include "qedf.h"
static ssize_t
qedf_fcoe_mac_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fc_lport *lport = shost_priv(class_to_shost(dev));
u32 port_id;
u8 lport_src_id[3];
u8 fcoe_mac[6];
port_id = fc_host_port_id(lport->host);
lport_src_id[2] = (port_id & 0x000000FF);
lport_src_id[1] = (port_id & 0x0000FF00) >> 8;
lport_src_id[0] = (port_id & 0x00FF0000) >> 16;
fc_fcoe_set_mac(fcoe_mac, lport_src_id);
return scnprintf(buf, PAGE_SIZE, "%pM\n", fcoe_mac);
}
static DEVICE_ATTR(fcoe_mac, S_IRUGO, qedf_fcoe_mac_show, NULL);
struct device_attribute *qedf_host_attrs[] = {
&dev_attr_fcoe_mac,
NULL,
};
extern const struct qed_fcoe_ops *qed_ops;
inline bool qedf_is_vport(struct qedf_ctx *qedf)
{
return (!(qedf->lport->vport == NULL));
}
/* Get base qedf for physical port from vport */
static struct qedf_ctx *qedf_get_base_qedf(struct qedf_ctx *qedf)
{
struct fc_lport *lport;
struct fc_lport *base_lport;
if (!(qedf_is_vport(qedf)))
return NULL;
lport = qedf->lport;
base_lport = shost_priv(vport_to_shost(lport->vport));
return (struct qedf_ctx *)(lport_priv(base_lport));
}
void qedf_capture_grc_dump(struct qedf_ctx *qedf)
{
struct qedf_ctx *base_qedf;
/* Make sure we use the base qedf to take the GRC dump */
if (qedf_is_vport(qedf))
base_qedf = qedf_get_base_qedf(qedf);
else
base_qedf = qedf;
if (test_bit(QEDF_GRCDUMP_CAPTURE, &base_qedf->flags)) {
QEDF_INFO(&(base_qedf->dbg_ctx), QEDF_LOG_INFO,
"GRC Dump already captured.\n");
return;
}
qedf_get_grc_dump(base_qedf->cdev, qed_ops->common,
&base_qedf->grcdump, &base_qedf->grcdump_size);
QEDF_ERR(&(base_qedf->dbg_ctx), "GRC Dump captured.\n");
set_bit(QEDF_GRCDUMP_CAPTURE, &base_qedf->flags);
qedf_uevent_emit(base_qedf->lport->host, QEDF_UEVENT_CODE_GRCDUMP,
NULL);
}
static ssize_t
qedf_sysfs_read_grcdump(struct file *filep, struct kobject *kobj,
struct bin_attribute *ba, char *buf, loff_t off,
size_t count)
{
ssize_t ret = 0;
struct fc_lport *lport = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
struct qedf_ctx *qedf = lport_priv(lport);
if (test_bit(QEDF_GRCDUMP_CAPTURE, &qedf->flags)) {
ret = memory_read_from_buffer(buf, count, &off,
qedf->grcdump, qedf->grcdump_size);
} else {
QEDF_ERR(&(qedf->dbg_ctx), "GRC Dump not captured!\n");
}
return ret;
}
static ssize_t
qedf_sysfs_write_grcdump(struct file *filep, struct kobject *kobj,
struct bin_attribute *ba, char *buf, loff_t off,
size_t count)
{
struct fc_lport *lport = NULL;
struct qedf_ctx *qedf = NULL;
long reading;
int ret = 0;
char msg[40];
if (off != 0)
return ret;
lport = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
qedf = lport_priv(lport);
buf[1] = 0;
ret = kstrtol(buf, 10, &reading);
if (ret) {
QEDF_ERR(&(qedf->dbg_ctx), "Invalid input, err(%d)\n", ret);
return ret;
}
memset(msg, 0, sizeof(msg));
switch (reading) {
case 0:
memset(qedf->grcdump, 0, qedf->grcdump_size);
clear_bit(QEDF_GRCDUMP_CAPTURE, &qedf->flags);
break;
case 1:
qedf_capture_grc_dump(qedf);
break;
}
return count;
}
static struct bin_attribute sysfs_grcdump_attr = {
.attr = {
.name = "grcdump",
.mode = S_IRUSR | S_IWUSR,
},
.size = 0,
.read = qedf_sysfs_read_grcdump,
.write = qedf_sysfs_write_grcdump,
};
static struct sysfs_bin_attrs bin_file_entries[] = {
{"grcdump", &sysfs_grcdump_attr},
{NULL},
};
void qedf_create_sysfs_ctx_attr(struct qedf_ctx *qedf)
{
qedf_create_sysfs_attr(qedf->lport->host, bin_file_entries);
}
void qedf_remove_sysfs_ctx_attr(struct qedf_ctx *qedf)
{
qedf_remove_sysfs_attr(qedf->lport->host, bin_file_entries);
}
/*
* QLogic FCoE Offload Driver
* Copyright (c) 2016 Cavium Inc.
*
* This software is available under the terms of the GNU General Public License
* (GPL) Version 2, available from the file COPYING in the main directory of
* this source tree.
*/
#include "qedf_dbg.h"
#include <linux/vmalloc.h>
void
qedf_dbg_err(struct qedf_dbg_ctx *qedf, const char *func, u32 line,
const char *fmt, ...)
{
va_list va;
struct va_format vaf;
char nfunc[32];
memset(nfunc, 0, sizeof(nfunc));
memcpy(nfunc, func, sizeof(nfunc) - 1);
va_start(va, fmt);
vaf.fmt = fmt;
vaf.va = &va;
if (likely(qedf) && likely(qedf->pdev))
pr_err("[%s]:[%s:%d]:%d: %pV", dev_name(&(qedf->pdev->dev)),
nfunc, line, qedf->host_no, &vaf);
else
pr_err("[0000:00:00.0]:[%s:%d]: %pV", nfunc, line, &vaf);
va_end(va);
}
void
qedf_dbg_warn(struct qedf_dbg_ctx *qedf, const char *func, u32 line,
const char *fmt, ...)
{
va_list va;
struct va_format vaf;
char nfunc[32];
memset(nfunc, 0, sizeof(nfunc));
memcpy(nfunc, func, sizeof(nfunc) - 1);
va_start(va, fmt);
vaf.fmt = fmt;
vaf.va = &va;
if (!(qedf_debug & QEDF_LOG_WARN))
goto ret;
if (likely(qedf) && likely(qedf->pdev))
pr_warn("[%s]:[%s:%d]:%d: %pV", dev_name(&(qedf->pdev->dev)),
nfunc, line, qedf->host_no, &vaf);
else
pr_warn("[0000:00:00.0]:[%s:%d]: %pV", nfunc, line, &vaf);
ret:
va_end(va);
}
void
qedf_dbg_notice(struct qedf_dbg_ctx *qedf, const char *func, u32 line,
const char *fmt, ...)
{
va_list va;
struct va_format vaf;
char nfunc[32];
memset(nfunc, 0, sizeof(nfunc));
memcpy(nfunc, func, sizeof(nfunc) - 1);
va_start(va, fmt);
vaf.fmt = fmt;
vaf.va = &va;
if (!(qedf_debug & QEDF_LOG_NOTICE))
goto ret;
if (likely(qedf) && likely(qedf->pdev))
pr_notice("[%s]:[%s:%d]:%d: %pV",
dev_name(&(qedf->pdev->dev)), nfunc, line,
qedf->host_no, &vaf);
else
pr_notice("[0000:00:00.0]:[%s:%d]: %pV", nfunc, line, &vaf);
ret:
va_end(va);
}
void
qedf_dbg_info(struct qedf_dbg_ctx *qedf, const char *func, u32 line,
u32 level, const char *fmt, ...)
{
va_list va;
struct va_format vaf;
char nfunc[32];
memset(nfunc, 0, sizeof(nfunc));
memcpy(nfunc, func, sizeof(nfunc) - 1);
va_start(va, fmt);
vaf.fmt = fmt;
vaf.va = &va;
if (!(qedf_debug & level))
goto ret;
if (likely(qedf) && likely(qedf->pdev))
pr_info("[%s]:[%s:%d]:%d: %pV", dev_name(&(qedf->pdev->dev)),
nfunc, line, qedf->host_no, &vaf);
else
pr_info("[0000:00:00.0]:[%s:%d]: %pV", nfunc, line, &vaf);
ret:
va_end(va);
}
int
qedf_alloc_grc_dump_buf(u8 **buf, uint32_t len)
{
*buf = vmalloc(len);
if (!(*buf))
return -ENOMEM;
memset(*buf, 0, len);
return 0;
}
void
qedf_free_grc_dump_buf(uint8_t **buf)
{
vfree(*buf);
*buf = NULL;
}
int
qedf_get_grc_dump(struct qed_dev *cdev, const struct qed_common_ops *common,
u8 **buf, uint32_t *grcsize)
{
if (!*buf)
return -EINVAL;
return common->dbg_grc(cdev, *buf, grcsize);
}
void
qedf_uevent_emit(struct Scsi_Host *shost, u32 code, char *msg)
{
char event_string[40];
char *envp[] = {event_string, NULL};
memset(event_string, 0, sizeof(event_string));
switch (code) {
case QEDF_UEVENT_CODE_GRCDUMP:
if (msg)
strncpy(event_string, msg, strlen(msg));
else
sprintf(event_string, "GRCDUMP=%u", shost->host_no);
break;
default:
/* do nothing */
break;
}
kobject_uevent_env(&shost->shost_gendev.kobj, KOBJ_CHANGE, envp);
}
int
qedf_create_sysfs_attr(struct Scsi_Host *shost, struct sysfs_bin_attrs *iter)
{
int ret = 0;
for (; iter->name; iter++) {
ret = sysfs_create_bin_file(&shost->shost_gendev.kobj,
iter->attr);
if (ret)
pr_err("Unable to create sysfs %s attr, err(%d).\n",
iter->name, ret);
}
return ret;
}
void
qedf_remove_sysfs_attr(struct Scsi_Host *shost, struct sysfs_bin_attrs *iter)
{
for (; iter->name; iter++)
sysfs_remove_bin_file(&shost->shost_gendev.kobj, iter->attr);
}
/*
* QLogic FCoE Offload Driver
* Copyright (c) 2016 Cavium Inc.
*
* This software is available under the terms of the GNU General Public License
* (GPL) Version 2, available from the file COPYING in the main directory of
* this source tree.
*/
#ifndef _QEDF_DBG_H_
#define _QEDF_DBG_H_
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/compiler.h>
#include <linux/string.h>
#include <linux/version.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <scsi/scsi_transport.h>
#include <linux/fs.h>
#include <linux/qed/common_hsi.h>
#include <linux/qed/qed_if.h>
extern uint qedf_debug;
/* Debug print level definitions */
#define QEDF_LOG_DEFAULT 0x1 /* Set default logging mask */
#define QEDF_LOG_INFO 0x2 /*
* Informational logs,
* MAC address, WWPN, WWNN
*/
#define QEDF_LOG_DISC 0x4 /* Init, discovery, rport */
#define QEDF_LOG_LL2 0x8 /* LL2, VLAN logs */
#define QEDF_LOG_CONN 0x10 /* Connection setup, cleanup */
#define QEDF_LOG_EVT 0x20 /* Events, link, mtu */
#define QEDF_LOG_TIMER 0x40 /* Timer events */
#define QEDF_LOG_MP_REQ 0x80 /* Middle Path (MP) logs */
#define QEDF_LOG_SCSI_TM 0x100 /* SCSI Aborts, Task Mgmt */
#define QEDF_LOG_UNSOL 0x200 /* unsolicited event logs */
#define QEDF_LOG_IO 0x400 /* scsi cmd, completion */
#define QEDF_LOG_MQ 0x800 /* Multi Queue logs */
#define QEDF_LOG_BSG 0x1000 /* BSG logs */
#define QEDF_LOG_DEBUGFS 0x2000 /* debugFS logs */
#define QEDF_LOG_LPORT 0x4000 /* lport logs */
#define QEDF_LOG_ELS 0x8000 /* ELS logs */
#define QEDF_LOG_NPIV 0x10000 /* NPIV logs */
#define QEDF_LOG_SESS 0x20000 /* Conection setup, cleanup */
#define QEDF_LOG_TID 0x80000 /*
* FW TID context acquire
* free
*/
#define QEDF_TRACK_TID 0x100000 /*
* Track TID state. To be
* enabled only at module load
* and not run-time.
*/
#define QEDF_TRACK_CMD_LIST 0x300000 /*
* Track active cmd list nodes,
* done with reference to TID,
* hence TRACK_TID also enabled.
*/
#define QEDF_LOG_NOTICE 0x40000000 /* Notice logs */
#define QEDF_LOG_WARN 0x80000000 /* Warning logs */
/* Debug context structure */
struct qedf_dbg_ctx {
unsigned int host_no;
struct pci_dev *pdev;
#ifdef CONFIG_DEBUG_FS
struct dentry *bdf_dentry;
#endif
};
#define QEDF_ERR(pdev, fmt, ...) \
qedf_dbg_err(pdev, __func__, __LINE__, fmt, ## __VA_ARGS__)
#define QEDF_WARN(pdev, fmt, ...) \
qedf_dbg_warn(pdev, __func__, __LINE__, fmt, ## __VA_ARGS__)
#define QEDF_NOTICE(pdev, fmt, ...) \
qedf_dbg_notice(pdev, __func__, __LINE__, fmt, ## __VA_ARGS__)
#define QEDF_INFO(pdev, level, fmt, ...) \
qedf_dbg_info(pdev, __func__, __LINE__, level, fmt, \
## __VA_ARGS__)
extern void qedf_dbg_err(struct qedf_dbg_ctx *qedf, const char *func, u32 line,
const char *fmt, ...);
extern void qedf_dbg_warn(struct qedf_dbg_ctx *qedf, const char *func, u32 line,
const char *, ...);
extern void qedf_dbg_notice(struct qedf_dbg_ctx *qedf, const char *func,
u32 line, const char *, ...);
extern void qedf_dbg_info(struct qedf_dbg_ctx *qedf, const char *func, u32 line,
u32 info, const char *fmt, ...);
/* GRC Dump related defines */
struct Scsi_Host;
#define QEDF_UEVENT_CODE_GRCDUMP 0
struct sysfs_bin_attrs {
char *name;
struct bin_attribute *attr;
};
extern int qedf_alloc_grc_dump_buf(uint8_t **buf, uint32_t len);
extern void qedf_free_grc_dump_buf(uint8_t **buf);
extern int qedf_get_grc_dump(struct qed_dev *cdev,
const struct qed_common_ops *common, uint8_t **buf,
uint32_t *grcsize);
extern void qedf_uevent_emit(struct Scsi_Host *shost, u32 code, char *msg);
extern int qedf_create_sysfs_attr(struct Scsi_Host *shost,
struct sysfs_bin_attrs *iter);
extern void qedf_remove_sysfs_attr(struct Scsi_Host *shost,
struct sysfs_bin_attrs *iter);
#ifdef CONFIG_DEBUG_FS
/* DebugFS related code */
struct qedf_list_of_funcs {
char *oper_str;
ssize_t (*oper_func)(struct qedf_dbg_ctx *qedf);
};
struct qedf_debugfs_ops {
char *name;
struct qedf_list_of_funcs *qedf_funcs;
};
#define qedf_dbg_fileops(drv, ops) \
{ \
.owner = THIS_MODULE, \
.open = simple_open, \
.read = drv##_dbg_##ops##_cmd_read, \
.write = drv##_dbg_##ops##_cmd_write \
}
/* Used for debugfs sequential files */
#define qedf_dbg_fileops_seq(drv, ops) \
{ \
.owner = THIS_MODULE, \
.open = drv##_dbg_##ops##_open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release, \
}
extern void qedf_dbg_host_init(struct qedf_dbg_ctx *qedf,
struct qedf_debugfs_ops *dops,
struct file_operations *fops);
extern void qedf_dbg_host_exit(struct qedf_dbg_ctx *qedf);
extern void qedf_dbg_init(char *drv_name);
extern void qedf_dbg_exit(void);
#endif /* CONFIG_DEBUG_FS */
#endif /* _QEDF_DBG_H_ */
This diff is collapsed.
This diff is collapsed.
/*
* QLogic FCoE Offload Driver
* Copyright (c) 2016 Cavium Inc.
*
* This software is available under the terms of the GNU General Public License
* (GPL) Version 2, available from the file COPYING in the main directory of
* this source tree.
*/
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include "qedf.h"
extern const struct qed_fcoe_ops *qed_ops;
/*
* FIP VLAN functions that will eventually move to libfcoe.
*/
void qedf_fcoe_send_vlan_req(struct qedf_ctx *qedf)
{
struct sk_buff *skb;
char *eth_fr;
int fr_len;
struct fip_vlan *vlan;
#define MY_FIP_ALL_FCF_MACS ((__u8[6]) { 1, 0x10, 0x18, 1, 0, 2 })
static u8 my_fcoe_all_fcfs[ETH_ALEN] = MY_FIP_ALL_FCF_MACS;
skb = dev_alloc_skb(sizeof(struct fip_vlan));
if (!skb)
return;
fr_len = sizeof(*vlan);
eth_fr = (char *)skb->data;
vlan = (struct fip_vlan *)eth_fr;
memset(vlan, 0, sizeof(*vlan));
ether_addr_copy(vlan->eth.h_source, qedf->mac);
ether_addr_copy(vlan->eth.h_dest, my_fcoe_all_fcfs);
vlan->eth.h_proto = htons(ETH_P_FIP);
vlan->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
vlan->fip.fip_op = htons(FIP_OP_VLAN);
vlan->fip.fip_subcode = FIP_SC_VL_REQ;
vlan->fip.fip_dl_len = htons(sizeof(vlan->desc) / FIP_BPW);
vlan->desc.mac.fd_desc.fip_dtype = FIP_DT_MAC;
vlan->desc.mac.fd_desc.fip_dlen = sizeof(vlan->desc.mac) / FIP_BPW;
ether_addr_copy(vlan->desc.mac.fd_mac, qedf->mac);
vlan->desc.wwnn.fd_desc.fip_dtype = FIP_DT_NAME;
vlan->desc.wwnn.fd_desc.fip_dlen = sizeof(vlan->desc.wwnn) / FIP_BPW;
put_unaligned_be64(qedf->lport->wwnn, &vlan->desc.wwnn.fd_wwn);
skb_put(skb, sizeof(*vlan));
skb->protocol = htons(ETH_P_FIP);
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Sending FIP VLAN "
"request.");
if (atomic_read(&qedf->link_state) != QEDF_LINK_UP) {
QEDF_WARN(&(qedf->dbg_ctx), "Cannot send vlan request "
"because link is not up.\n");
kfree_skb(skb);
return;
}
qed_ops->ll2->start_xmit(qedf->cdev, skb);
}
static void qedf_fcoe_process_vlan_resp(struct qedf_ctx *qedf,
struct sk_buff *skb)
{
struct fip_header *fiph;
struct fip_desc *desc;
u16 vid = 0;
ssize_t rlen;
size_t dlen;
fiph = (struct fip_header *)(((void *)skb->data) + 2 * ETH_ALEN + 2);
rlen = ntohs(fiph->fip_dl_len) * 4;
desc = (struct fip_desc *)(fiph + 1);
while (rlen > 0) {
dlen = desc->fip_dlen * FIP_BPW;
switch (desc->fip_dtype) {
case FIP_DT_VLAN:
vid = ntohs(((struct fip_vlan_desc *)desc)->fd_vlan);
break;
}
desc = (struct fip_desc *)((char *)desc + dlen);
rlen -= dlen;
}
QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "VLAN response, "
"vid=0x%x.\n", vid);
if (vid > 0 && qedf->vlan_id != vid) {
qedf_set_vlan_id(qedf, vid);
/* Inform waiter that it's ok to call fcoe_ctlr_link up() */
complete(&qedf->fipvlan_compl);
}
}
void qedf_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
struct qedf_ctx *qedf = container_of(fip, struct qedf_ctx, ctlr);
struct ethhdr *eth_hdr;
struct vlan_ethhdr *vlan_hdr;
struct fip_header *fiph;
u16 op, vlan_tci = 0;
u8 sub;
if (!test_bit(QEDF_LL2_STARTED, &qedf->flags)) {
QEDF_WARN(&(qedf->dbg_ctx), "LL2 not started\n");
kfree_skb(skb);
return;
}
fiph = (struct fip_header *) ((void *)skb->data + 2 * ETH_ALEN + 2);
eth_hdr = (struct ethhdr *)skb_mac_header(skb);
op = ntohs(fiph->fip_op);
sub = fiph->fip_subcode;
if (!qedf->vlan_hw_insert) {
vlan_hdr = (struct vlan_ethhdr *)skb_push(skb, sizeof(*vlan_hdr)
- sizeof(*eth_hdr));
memcpy(vlan_hdr, eth_hdr, 2 * ETH_ALEN);
vlan_hdr->h_vlan_proto = htons(ETH_P_8021Q);
vlan_hdr->h_vlan_encapsulated_proto = eth_hdr->h_proto;
vlan_hdr->h_vlan_TCI = vlan_tci = htons(qedf->vlan_id);
}
/* Update eth_hdr since we added a VLAN tag */
eth_hdr = (struct ethhdr *)skb_mac_header(skb);
QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "FIP frame send: "
"dest=%pM op=%x sub=%x vlan=%04x.", eth_hdr->h_dest, op, sub,
ntohs(vlan_tci));
if (qedf_dump_frames)
print_hex_dump(KERN_WARNING, "fip ", DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb->len, false);
qed_ops->ll2->start_xmit(qedf->cdev, skb);
}
/* Process incoming FIP frames. */
void qedf_fip_recv(struct qedf_ctx *qedf, struct sk_buff *skb)
{
struct ethhdr *eth_hdr;
struct fip_header *fiph;
struct fip_desc *desc;
struct fip_mac_desc *mp;
struct fip_wwn_desc *wp;
struct fip_vn_desc *vp;
size_t rlen, dlen;
uint32_t cvl_port_id;
__u8 cvl_mac[ETH_ALEN];
u16 op;
u8 sub;
eth_hdr = (struct ethhdr *)skb_mac_header(skb);
fiph = (struct fip_header *) ((void *)skb->data + 2 * ETH_ALEN + 2);
op = ntohs(fiph->fip_op);
sub = fiph->fip_subcode;
QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "FIP frame received: "
"skb=%p fiph=%p source=%pM op=%x sub=%x", skb, fiph,
eth_hdr->h_source, op, sub);
if (qedf_dump_frames)
print_hex_dump(KERN_WARNING, "fip ", DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb->len, false);
/* Handle FIP VLAN resp in the driver */
if (op == FIP_OP_VLAN && sub == FIP_SC_VL_NOTE) {
qedf_fcoe_process_vlan_resp(qedf, skb);
qedf->vlan_hw_insert = 0;
kfree_skb(skb);
} else if (op == FIP_OP_CTRL && sub == FIP_SC_CLR_VLINK) {
QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Clear virtual "
"link received.\n");
/* Check that an FCF has been selected by fcoe */
if (qedf->ctlr.sel_fcf == NULL) {
QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
"Dropping CVL since FCF has not been selected "
"yet.");
return;
}
cvl_port_id = 0;
memset(cvl_mac, 0, ETH_ALEN);
/*
* We need to loop through the CVL descriptors to determine
* if we want to reset the fcoe link
*/
rlen = ntohs(fiph->fip_dl_len) * FIP_BPW;
desc = (struct fip_desc *)(fiph + 1);
while (rlen >= sizeof(*desc)) {
dlen = desc->fip_dlen * FIP_BPW;
switch (desc->fip_dtype) {
case FIP_DT_MAC:
mp = (struct fip_mac_desc *)desc;
QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2,
"fd_mac=%pM.\n", __func__, mp->fd_mac);
ether_addr_copy(cvl_mac, mp->fd_mac);
break;
case FIP_DT_NAME:
wp = (struct fip_wwn_desc *)desc;
QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2,
"fc_wwpn=%016llx.\n",
get_unaligned_be64(&wp->fd_wwn));
break;
case FIP_DT_VN_ID:
vp = (struct fip_vn_desc *)desc;
QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2,
"fd_fc_id=%x.\n", ntoh24(vp->fd_fc_id));
cvl_port_id = ntoh24(vp->fd_fc_id);
break;
default:
/* Ignore anything else */
break;
}
desc = (struct fip_desc *)((char *)desc + dlen);
rlen -= dlen;
}
QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2,
"cvl_port_id=%06x cvl_mac=%pM.\n", cvl_port_id,
cvl_mac);
if (cvl_port_id == qedf->lport->port_id &&
ether_addr_equal(cvl_mac,
qedf->ctlr.sel_fcf->fcf_mac)) {
fcoe_ctlr_link_down(&qedf->ctlr);
qedf_wait_for_upload(qedf);
fcoe_ctlr_link_up(&qedf->ctlr);
}
kfree_skb(skb);
} else {
/* Everything else is handled by libfcoe */
__skb_pull(skb, ETH_HLEN);
fcoe_ctlr_recv(&qedf->ctlr, skb);
}
}
void qedf_update_src_mac(struct fc_lport *lport, u8 *addr)
{
struct qedf_ctx *qedf = lport_priv(lport);
QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
"Setting data_src_addr=%pM.\n", addr);
ether_addr_copy(qedf->data_src_addr, addr);
}
u8 *qedf_get_src_mac(struct fc_lport *lport)
{
u8 mac[ETH_ALEN];
u8 port_id[3];
struct qedf_ctx *qedf = lport_priv(lport);
/* We need to use the lport port_id to create the data_src_addr */
if (is_zero_ether_addr(qedf->data_src_addr)) {
hton24(port_id, lport->port_id);
fc_fcoe_set_mac(mac, port_id);
qedf->ctlr.update_mac(lport, mac);
}
return qedf->data_src_addr;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* QLogic FCoE Offload Driver
* Copyright (c) 2016 Cavium Inc.
*
* This software is available under the terms of the GNU General Public License
* (GPL) Version 2, available from the file COPYING in the main directory of
* this source tree.
*/
#define QEDF_VERSION "8.10.7.0"
#define QEDF_DRIVER_MAJOR_VER 8
#define QEDF_DRIVER_MINOR_VER 10
#define QEDF_DRIVER_REV_VER 7
#define QEDF_DRIVER_ENG_VER 0
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment