aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi/fcoe/fcoe.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/fcoe/fcoe.c')
-rw-r--r--drivers/scsi/fcoe/fcoe.c1720
1 files changed, 973 insertions, 747 deletions
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index d23a538a9df..00ee0ed642a 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -18,7 +18,6 @@
*/
#include <linux/module.h>
-#include <linux/version.h>
#include <linux/spinlock.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -31,6 +30,9 @@
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/ctype.h>
+#include <linux/workqueue.h>
+#include <net/dcbnl.h>
+#include <net/dcbevent.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsicam.h>
#include <scsi/scsi_transport.h>
@@ -39,6 +41,7 @@
#include <scsi/fc/fc_encaps.h>
#include <scsi/fc/fc_fip.h>
+#include <scsi/fc/fc_fcoe.h>
#include <scsi/libfc.h>
#include <scsi/fc_frame.h>
@@ -51,20 +54,26 @@ MODULE_DESCRIPTION("FCoE");
MODULE_LICENSE("GPL v2");
/* Performance tuning parameters for fcoe */
-static unsigned int fcoe_ddp_min;
+static unsigned int fcoe_ddp_min = 4096;
module_param_named(ddp_min, fcoe_ddp_min, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ddp_min, "Minimum I/O size in bytes for " \
"Direct Data Placement (DDP).");
-DEFINE_MUTEX(fcoe_config_mutex);
+unsigned int fcoe_debug_logging;
+module_param_named(debug_logging, fcoe_debug_logging, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
+
+static DEFINE_MUTEX(fcoe_config_mutex);
+
+static struct workqueue_struct *fcoe_wq;
/* fcoe_percpu_clean completion. Waiter protected by fcoe_create_mutex */
static DECLARE_COMPLETION(fcoe_flush_completion);
/* fcoe host list */
/* must only by accessed under the RTNL mutex */
-LIST_HEAD(fcoe_hostlist);
-DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
+static LIST_HEAD(fcoe_hostlist);
+static DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
/* Function Prototypes */
static int fcoe_reset(struct Scsi_Host *);
@@ -72,15 +81,13 @@ static int fcoe_xmit(struct fc_lport *, struct fc_frame *);
static int fcoe_rcv(struct sk_buff *, struct net_device *,
struct packet_type *, struct net_device *);
static int fcoe_percpu_receive_thread(void *);
-static void fcoe_clean_pending_queue(struct fc_lport *);
static void fcoe_percpu_clean(struct fc_lport *);
-static int fcoe_link_speed_update(struct fc_lport *);
static int fcoe_link_ok(struct fc_lport *);
static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *);
static int fcoe_hostlist_add(const struct fc_lport *);
+static void fcoe_hostlist_del(const struct fc_lport *);
-static void fcoe_check_wait_queue(struct fc_lport *, struct sk_buff *);
static int fcoe_device_notification(struct notifier_block *, ulong, void *);
static void fcoe_dev_setup(void);
static void fcoe_dev_cleanup(void);
@@ -98,13 +105,22 @@ static void fcoe_destroy_work(struct work_struct *);
static int fcoe_ddp_setup(struct fc_lport *, u16, struct scatterlist *,
unsigned int);
static int fcoe_ddp_done(struct fc_lport *, u16);
-
+static int fcoe_ddp_target(struct fc_lport *, u16, struct scatterlist *,
+ unsigned int);
static int fcoe_cpu_callback(struct notifier_block *, unsigned long, void *);
+static int fcoe_dcb_app_notification(struct notifier_block *notifier,
+ ulong event, void *ptr);
+
+static bool fcoe_match(struct net_device *netdev);
+static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode);
+static int fcoe_destroy(struct net_device *netdev);
+static int fcoe_enable(struct net_device *netdev);
+static int fcoe_disable(struct net_device *netdev);
+
+/* fcoe_syfs control interface handlers */
+static int fcoe_ctlr_alloc(struct net_device *netdev);
+static int fcoe_ctlr_enabled(struct fcoe_ctlr_device *cdev);
-static int fcoe_create(const char *, struct kernel_param *);
-static int fcoe_destroy(const char *, struct kernel_param *);
-static int fcoe_enable(const char *, struct kernel_param *);
-static int fcoe_disable(const char *, struct kernel_param *);
static struct fc_seq *fcoe_elsct_send(struct fc_lport *,
u32 did, struct fc_frame *,
@@ -115,26 +131,6 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *,
void *, u32 timeout);
static void fcoe_recv_frame(struct sk_buff *skb);
-static void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *);
-
-module_param_call(create, fcoe_create, NULL, (void *)FIP_MODE_FABRIC, S_IWUSR);
-__MODULE_PARM_TYPE(create, "string");
-MODULE_PARM_DESC(create, " Creates fcoe instance on a ethernet interface");
-module_param_call(create_vn2vn, fcoe_create, NULL,
- (void *)FIP_MODE_VN2VN, S_IWUSR);
-__MODULE_PARM_TYPE(create_vn2vn, "string");
-MODULE_PARM_DESC(create_vn2vn, " Creates a VN_node to VN_node FCoE instance "
- "on an Ethernet interface");
-module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(destroy, "string");
-MODULE_PARM_DESC(destroy, " Destroys fcoe instance on a ethernet interface");
-module_param_call(enable, fcoe_enable, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(enable, "string");
-MODULE_PARM_DESC(enable, " Enables fcoe on a ethernet interface.");
-module_param_call(disable, fcoe_disable, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(disable, "string");
-MODULE_PARM_DESC(disable, " Disables fcoe on a ethernet interface.");
-
/* notification function for packets from net device */
static struct notifier_block fcoe_notifier = {
.notifier_call = fcoe_device_notification,
@@ -145,31 +141,60 @@ static struct notifier_block fcoe_cpu_notifier = {
.notifier_call = fcoe_cpu_callback,
};
-static struct scsi_transport_template *fcoe_transport_template;
-static struct scsi_transport_template *fcoe_vport_transport_template;
+/* notification function for DCB events */
+static struct notifier_block dcb_notifier = {
+ .notifier_call = fcoe_dcb_app_notification,
+};
+
+static struct scsi_transport_template *fcoe_nport_scsi_transport;
+static struct scsi_transport_template *fcoe_vport_scsi_transport;
static int fcoe_vport_destroy(struct fc_vport *);
static int fcoe_vport_create(struct fc_vport *, bool disabled);
static int fcoe_vport_disable(struct fc_vport *, bool disable);
static void fcoe_set_vport_symbolic_name(struct fc_vport *);
static void fcoe_set_port_id(struct fc_lport *, u32, struct fc_frame *);
+static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *);
+
+static struct fcoe_sysfs_function_template fcoe_sysfs_templ = {
+ .set_fcoe_ctlr_mode = fcoe_ctlr_set_fip_mode,
+ .set_fcoe_ctlr_enabled = fcoe_ctlr_enabled,
+ .get_fcoe_ctlr_link_fail = fcoe_ctlr_get_lesb,
+ .get_fcoe_ctlr_vlink_fail = fcoe_ctlr_get_lesb,
+ .get_fcoe_ctlr_miss_fka = fcoe_ctlr_get_lesb,
+ .get_fcoe_ctlr_symb_err = fcoe_ctlr_get_lesb,
+ .get_fcoe_ctlr_err_block = fcoe_ctlr_get_lesb,
+ .get_fcoe_ctlr_fcs_error = fcoe_ctlr_get_lesb,
+
+ .get_fcoe_fcf_selected = fcoe_fcf_get_selected,
+ .get_fcoe_fcf_vlan_id = fcoe_fcf_get_vlan_id,
+};
static struct libfc_function_template fcoe_libfc_fcn_templ = {
.frame_send = fcoe_xmit,
.ddp_setup = fcoe_ddp_setup,
.ddp_done = fcoe_ddp_done,
+ .ddp_target = fcoe_ddp_target,
.elsct_send = fcoe_elsct_send,
.get_lesb = fcoe_get_lesb,
.lport_set_port_id = fcoe_set_port_id,
};
-struct fc_function_template fcoe_transport_function = {
+static struct fc_function_template fcoe_nport_fc_functions = {
.show_host_node_name = 1,
.show_host_port_name = 1,
.show_host_supported_classes = 1,
.show_host_supported_fc4s = 1,
.show_host_active_fc4s = 1,
.show_host_maxframe_size = 1,
+ .show_host_serial_number = 1,
+ .show_host_manufacturer = 1,
+ .show_host_model = 1,
+ .show_host_model_description = 1,
+ .show_host_hardware_version = 1,
+ .show_host_driver_version = 1,
+ .show_host_firmware_version = 1,
+ .show_host_optionrom_version = 1,
.show_host_port_id = 1,
.show_host_supported_speeds = 1,
@@ -203,13 +228,21 @@ struct fc_function_template fcoe_transport_function = {
.bsg_request = fc_lport_bsg_request,
};
-struct fc_function_template fcoe_vport_transport_function = {
+static struct fc_function_template fcoe_vport_fc_functions = {
.show_host_node_name = 1,
.show_host_port_name = 1,
.show_host_supported_classes = 1,
.show_host_supported_fc4s = 1,
.show_host_active_fc4s = 1,
.show_host_maxframe_size = 1,
+ .show_host_serial_number = 1,
+ .show_host_manufacturer = 1,
+ .show_host_model = 1,
+ .show_host_model_description = 1,
+ .show_host_hardware_version = 1,
+ .show_host_driver_version = 1,
+ .show_host_firmware_version = 1,
+ .show_host_optionrom_version = 1,
.show_host_port_id = 1,
.show_host_supported_speeds = 1,
@@ -268,7 +301,7 @@ static struct scsi_host_template fcoe_shost_template = {
static int fcoe_interface_setup(struct fcoe_interface *fcoe,
struct net_device *netdev)
{
- struct fcoe_ctlr *fip = &fcoe->ctlr;
+ struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);
struct netdev_hw_addr *ha;
struct net_device *real_dev;
u8 flogi_maddr[ETH_ALEN];
@@ -285,9 +318,7 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
}
/* Do not support for bonding device */
- if ((netdev->priv_flags & IFF_MASTER_ALB) ||
- (netdev->priv_flags & IFF_SLAVE_INACTIVE) ||
- (netdev->priv_flags & IFF_MASTER_8023AD)) {
+ if (netdev->priv_flags & IFF_BONDING && netdev->flags & IFF_MASTER) {
FCOE_NETDEV_DBG(netdev, "Bonded interfaces not supported\n");
return -EOPNOTSUPP;
}
@@ -296,6 +327,7 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
* use the first one for SPMA */
real_dev = (netdev->priv_flags & IFF_802_1Q_VLAN) ?
vlan_dev_real_dev(netdev) : netdev;
+ fcoe->realdev = real_dev;
rcu_read_lock();
for_each_dev_addr(real_dev, ha) {
if ((ha->type == NETDEV_HW_ADDR_T_SAN) &&
@@ -353,47 +385,69 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
enum fip_state fip_mode)
{
+ struct fcoe_ctlr_device *ctlr_dev;
+ struct fcoe_ctlr *ctlr;
struct fcoe_interface *fcoe;
+ int size;
int err;
- fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL);
- if (!fcoe) {
- FCOE_NETDEV_DBG(netdev, "Could not allocate fcoe structure\n");
- return NULL;
+ if (!try_module_get(THIS_MODULE)) {
+ FCOE_NETDEV_DBG(netdev,
+ "Could not get a reference to the module\n");
+ fcoe = ERR_PTR(-EBUSY);
+ goto out;
+ }
+
+ size = sizeof(struct fcoe_ctlr) + sizeof(struct fcoe_interface);
+ ctlr_dev = fcoe_ctlr_device_add(&netdev->dev, &fcoe_sysfs_templ,
+ size);
+ if (!ctlr_dev) {
+ FCOE_DBG("Failed to add fcoe_ctlr_device\n");
+ fcoe = ERR_PTR(-ENOMEM);
+ goto out_putmod;
}
+ ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+ ctlr->cdev = ctlr_dev;
+ fcoe = fcoe_ctlr_priv(ctlr);
+
dev_hold(netdev);
- kref_init(&fcoe->kref);
/*
* Initialize FIP.
*/
- fcoe_ctlr_init(&fcoe->ctlr, fip_mode);
- fcoe->ctlr.send = fcoe_fip_send;
- fcoe->ctlr.update_mac = fcoe_update_src_mac;
- fcoe->ctlr.get_src_addr = fcoe_get_src_mac;
+ fcoe_ctlr_init(ctlr, fip_mode);
+ ctlr->send = fcoe_fip_send;
+ ctlr->update_mac = fcoe_update_src_mac;
+ ctlr->get_src_addr = fcoe_get_src_mac;
err = fcoe_interface_setup(fcoe, netdev);
if (err) {
- fcoe_ctlr_destroy(&fcoe->ctlr);
- kfree(fcoe);
+ fcoe_ctlr_destroy(ctlr);
+ fcoe_ctlr_device_delete(ctlr_dev);
dev_put(netdev);
- return NULL;
+ fcoe = ERR_PTR(err);
+ goto out_putmod;
}
+ goto out;
+
+out_putmod:
+ module_put(THIS_MODULE);
+out:
return fcoe;
}
/**
- * fcoe_interface_cleanup() - Clean up a FCoE interface
+ * fcoe_interface_remove() - remove FCoE interface from netdev
* @fcoe: The FCoE interface to be cleaned up
*
* Caller must be holding the RTNL mutex
*/
-void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
+static void fcoe_interface_remove(struct fcoe_interface *fcoe)
{
struct net_device *netdev = fcoe->netdev;
- struct fcoe_ctlr *fip = &fcoe->ctlr;
+ struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);
u8 flogi_maddr[ETH_ALEN];
const struct net_device_ops *ops;
@@ -425,41 +479,30 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
FCOE_NETDEV_DBG(netdev, "Failed to disable FCoE"
" specific feature for LLD.\n");
}
+ fcoe->removed = 1;
}
+
/**
- * fcoe_interface_release() - fcoe_port kref release function
- * @kref: Embedded reference count in an fcoe_interface struct
+ * fcoe_interface_cleanup() - Clean up a FCoE interface
+ * @fcoe: The FCoE interface to be cleaned up
*/
-static void fcoe_interface_release(struct kref *kref)
+static void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
{
- struct fcoe_interface *fcoe;
- struct net_device *netdev;
+ struct net_device *netdev = fcoe->netdev;
+ struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);
+
+ rtnl_lock();
+ if (!fcoe->removed)
+ fcoe_interface_remove(fcoe);
+ rtnl_unlock();
- fcoe = container_of(kref, struct fcoe_interface, kref);
- netdev = fcoe->netdev;
+ /* Release the self-reference taken during fcoe_interface_create() */
/* tear-down the FCoE controller */
- fcoe_ctlr_destroy(&fcoe->ctlr);
- kfree(fcoe);
+ fcoe_ctlr_destroy(fip);
+ scsi_host_put(fip->lp->host);
dev_put(netdev);
-}
-
-/**
- * fcoe_interface_get() - Get a reference to a FCoE interface
- * @fcoe: The FCoE interface to be held
- */
-static inline void fcoe_interface_get(struct fcoe_interface *fcoe)
-{
- kref_get(&fcoe->kref);
-}
-
-/**
- * fcoe_interface_put() - Put a reference to a FCoE interface
- * @fcoe: The FCoE interface to be released
- */
-static inline void fcoe_interface_put(struct fcoe_interface *fcoe)
-{
- kref_put(&fcoe->kref, fcoe_interface_release);
+ module_put(THIS_MODULE);
}
/**
@@ -477,13 +520,28 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev,
struct net_device *orig_dev)
{
struct fcoe_interface *fcoe;
+ struct fcoe_ctlr *ctlr;
fcoe = container_of(ptype, struct fcoe_interface, fip_packet_type);
- fcoe_ctlr_recv(&fcoe->ctlr, skb);
+ ctlr = fcoe_to_ctlr(fcoe);
+ fcoe_ctlr_recv(ctlr, skb);
return 0;
}
/**
+ * fcoe_port_send() - Send an Ethernet-encapsulated FIP/FCoE frame
+ * @port: The FCoE port
+ * @skb: The FIP/FCoE packet to be sent
+ */
+static void fcoe_port_send(struct fcoe_port *port, struct sk_buff *skb)
+{
+ if (port->fcoe_pending_queue.qlen)
+ fcoe_check_wait_queue(port->lport, skb);
+ else if (fcoe_start_io(skb))
+ fcoe_check_wait_queue(port->lport, skb);
+}
+
+/**
* fcoe_fip_send() - Send an Ethernet-encapsulated FIP frame
* @fip: The FCoE controller
* @skb: The FIP packet to be sent
@@ -491,7 +549,7 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev,
static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
skb->dev = fcoe_from_ctlr(fip)->netdev;
- dev_queue_xmit(skb);
+ fcoe_port_send(lport_priv(fip->lp), skb);
}
/**
@@ -505,15 +563,13 @@ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
static void fcoe_update_src_mac(struct fc_lport *lport, u8 *addr)
{
struct fcoe_port *port = lport_priv(lport);
- struct fcoe_interface *fcoe = port->fcoe;
+ struct fcoe_interface *fcoe = port->priv;
- rtnl_lock();
if (!is_zero_ether_addr(port->data_src_addr))
dev_uc_del(fcoe->netdev, port->data_src_addr);
if (!is_zero_ether_addr(addr))
dev_uc_add(fcoe->netdev, addr);
memcpy(port->data_src_addr, addr, ETH_ALEN);
- rtnl_unlock();
}
/**
@@ -561,34 +617,6 @@ static int fcoe_lport_config(struct fc_lport *lport)
}
/**
- * fcoe_queue_timer() - The fcoe queue timer
- * @lport: The local port
- *
- * Calls fcoe_check_wait_queue on timeout
- */
-static void fcoe_queue_timer(ulong lport)
-{
- fcoe_check_wait_queue((struct fc_lport *)lport, NULL);
-}
-
-/**
- * fcoe_get_wwn() - Get the world wide name from LLD if it supports it
- * @netdev: the associated net device
- * @wwn: the output WWN
- * @type: the type of WWN (WWPN or WWNN)
- *
- * Returns: 0 for success
- */
-static int fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type)
-{
- const struct net_device_ops *ops = netdev->netdev_ops;
-
- if (ops->ndo_fcoe_get_wwn)
- return ops->ndo_fcoe_get_wwn(netdev, wwn, type);
- return -EINVAL;
-}
-
-/**
* fcoe_netdev_features_change - Updates the lport's offload flags based
* on the LLD netdev's FCoE feature flags
*/
@@ -646,11 +674,13 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
u32 mfs;
u64 wwnn, wwpn;
struct fcoe_interface *fcoe;
+ struct fcoe_ctlr *ctlr;
struct fcoe_port *port;
/* Setup lport private data to point to fcoe softc */
port = lport_priv(lport);
- fcoe = port->fcoe;
+ fcoe = port->priv;
+ ctlr = fcoe_to_ctlr(fcoe);
/*
* Determine max frame size based on underlying device and optional
@@ -677,10 +707,10 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
if (!lport->vport) {
if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN))
- wwnn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 1, 0);
+ wwnn = fcoe_wwn_from_mac(ctlr->ctl_src_addr, 1, 0);
fc_set_wwnn(lport, wwnn);
if (fcoe_get_wwn(netdev, &wwpn, NETDEV_FCOE_WWPN))
- wwpn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr,
+ wwpn = fcoe_wwn_from_mac(ctlr->ctl_src_addr,
2, 0);
fc_set_wwpn(lport, wwpn);
}
@@ -708,9 +738,9 @@ static int fcoe_shost_config(struct fc_lport *lport, struct device *dev)
lport->host->max_cmd_len = FCOE_MAX_CMD_LEN;
if (lport->vport)
- lport->host->transportt = fcoe_vport_transport_template;
+ lport->host->transportt = fcoe_vport_scsi_transport;
else
- lport->host->transportt = fcoe_transport_template;
+ lport->host->transportt = fcoe_nport_scsi_transport;
/* add the new host to the SCSI-ml */
rc = scsi_add_host(lport->host, dev);
@@ -730,6 +760,89 @@ static int fcoe_shost_config(struct fc_lport *lport, struct device *dev)
return 0;
}
+
+/**
+ * fcoe_fdmi_info() - Get FDMI related info from net devive for SW FCoE
+ * @lport: The local port that is associated with the net device
+ * @netdev: The associated net device
+ *
+ * Must be called after fcoe_shost_config() as it will use local port mutex
+ *
+ */
+static void fcoe_fdmi_info(struct fc_lport *lport, struct net_device *netdev)
+{
+ struct fcoe_interface *fcoe;
+ struct fcoe_port *port;
+ struct net_device *realdev;
+ int rc;
+
+ port = lport_priv(lport);
+ fcoe = port->priv;
+ realdev = fcoe->realdev;
+
+ if (!realdev)
+ return;
+
+ /* No FDMI state m/c for NPIV ports */
+ if (lport->vport)
+ return;
+
+ if (realdev->netdev_ops->ndo_fcoe_get_hbainfo) {
+ struct netdev_fcoe_hbainfo *fdmi;
+ fdmi = kzalloc(sizeof(*fdmi), GFP_KERNEL);
+ if (!fdmi)
+ return;
+
+ rc = realdev->netdev_ops->ndo_fcoe_get_hbainfo(realdev,
+ fdmi);
+ if (rc) {
+ printk(KERN_INFO "fcoe: Failed to retrieve FDMI "
+ "information from netdev.\n");
+ return;
+ }
+
+ snprintf(fc_host_serial_number(lport->host),
+ FC_SERIAL_NUMBER_SIZE,
+ "%s",
+ fdmi->serial_number);
+ snprintf(fc_host_manufacturer(lport->host),
+ FC_SERIAL_NUMBER_SIZE,
+ "%s",
+ fdmi->manufacturer);
+ snprintf(fc_host_model(lport->host),
+ FC_SYMBOLIC_NAME_SIZE,
+ "%s",
+ fdmi->model);
+ snprintf(fc_host_model_description(lport->host),
+ FC_SYMBOLIC_NAME_SIZE,
+ "%s",
+ fdmi->model_description);
+ snprintf(fc_host_hardware_version(lport->host),
+ FC_VERSION_STRING_SIZE,
+ "%s",
+ fdmi->hardware_version);
+ snprintf(fc_host_driver_version(lport->host),
+ FC_VERSION_STRING_SIZE,
+ "%s",
+ fdmi->driver_version);
+ snprintf(fc_host_optionrom_version(lport->host),
+ FC_VERSION_STRING_SIZE,
+ "%s",
+ fdmi->optionrom_version);
+ snprintf(fc_host_firmware_version(lport->host),
+ FC_VERSION_STRING_SIZE,
+ "%s",
+ fdmi->firmware_version);
+
+ /* Enable FDMI lport states */
+ lport->fdmi_enabled = 1;
+ kfree(fdmi);
+ } else {
+ lport->fdmi_enabled = 0;
+ printk(KERN_INFO "fcoe: No FDMI support.\n");
+ }
+}
+
/**
* fcoe_oem_match() - The match routine for the offloaded exchange manager
* @fp: The I/O frame
@@ -743,12 +856,28 @@ static int fcoe_shost_config(struct fc_lport *lport, struct device *dev)
* The offload EM that this routine is associated with will handle any
* packets that are for SCSI read requests.
*
+ * This has been enhanced to work when FCoE stack is operating in target
+ * mode.
+ *
* Returns: True for read types I/O, otherwise returns false.
*/
-bool fcoe_oem_match(struct fc_frame *fp)
+static bool fcoe_oem_match(struct fc_frame *fp)
{
- return fc_fcp_is_read(fr_fsp(fp)) &&
- (fr_fsp(fp)->data_len > fcoe_ddp_min);
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ struct fcp_cmnd *fcp;
+
+ if (fc_fcp_is_read(fr_fsp(fp)) &&
+ (fr_fsp(fp)->data_len > fcoe_ddp_min))
+ return true;
+ else if ((fr_fsp(fp) == NULL) &&
+ (fh->fh_r_ctl == FC_RCTL_DD_UNSOL_CMD) &&
+ (ntohs(fh->fh_rx_id) == FC_XID_UNKNOWN)) {
+ fcp = fc_frame_payload_get(fp, sizeof(*fcp));
+ if ((fcp->fc_flags & FCP_CFL_WRDATA) &&
+ (ntohl(fcp->fc_dl) > fcoe_ddp_min))
+ return true;
+ }
+ return false;
}
/**
@@ -760,7 +889,7 @@ bool fcoe_oem_match(struct fc_frame *fp)
static inline int fcoe_em_config(struct fc_lport *lport)
{
struct fcoe_port *port = lport_priv(lport);
- struct fcoe_interface *fcoe = port->fcoe;
+ struct fcoe_interface *fcoe = port->priv;
struct fcoe_interface *oldfcoe = NULL;
struct net_device *old_real_dev, *cur_real_dev;
u16 min_xid = FCOE_MIN_XID;
@@ -835,16 +964,11 @@ skip_oem:
* fcoe_if_destroy() - Tear down a SW FCoE instance
* @lport: The local port to be destroyed
*
- * Locking: must be called with the RTNL mutex held and RTNL mutex
- * needed to be dropped by this function since not dropping RTNL
- * would cause circular locking warning on synchronous fip worker
- * cancelling thru fcoe_interface_put invoked by this function.
- *
*/
static void fcoe_if_destroy(struct fc_lport *lport)
{
struct fcoe_port *port = lport_priv(lport);
- struct fcoe_interface *fcoe = port->fcoe;
+ struct fcoe_interface *fcoe = port->priv;
struct net_device *netdev = fcoe->netdev;
FCOE_NETDEV_DBG(netdev, "Destroying interface\n");
@@ -854,7 +978,6 @@ static void fcoe_if_destroy(struct fc_lport *lport)
/* Cleanup the fc_lport */
fc_lport_destroy(lport);
- fc_fcp_destroy(lport);
/* Stop the transmit retry timer */
del_timer_sync(&port->timer);
@@ -862,13 +985,15 @@ static void fcoe_if_destroy(struct fc_lport *lport)
/* Free existing transmit skbs */
fcoe_clean_pending_queue(lport);
+ rtnl_lock();
if (!is_zero_ether_addr(port->data_src_addr))
dev_uc_del(netdev, port->data_src_addr);
+ if (lport->vport)
+ synchronize_net();
+ else
+ fcoe_interface_remove(fcoe);
rtnl_unlock();
- /* receives may not be stopped until after this */
- fcoe_interface_put(fcoe);
-
/* Free queued packets for the per-CPU receive threads */
fcoe_percpu_clean(lport);
@@ -876,15 +1001,21 @@ static void fcoe_if_destroy(struct fc_lport *lport)
fc_remove_host(lport->host);
scsi_remove_host(lport->host);
+ /* Destroy lport scsi_priv */
+ fc_fcp_destroy(lport);
+
/* There are no more rports or I/O, free the EM */
fc_exch_mgr_free(lport);
/* Free memory used by statistical counters */
fc_lport_free_stats(lport);
- /* Release the Scsi_Host */
- scsi_host_put(lport->host);
- module_put(THIS_MODULE);
+ /*
+ * Release the Scsi_Host for vport but hold on to
+ * master lport until it fcoe interface fully cleaned-up.
+ */
+ if (lport->vport)
+ scsi_host_put(lport->host);
}
/**
@@ -910,6 +1041,28 @@ static int fcoe_ddp_setup(struct fc_lport *lport, u16 xid,
}
/**
+ * fcoe_ddp_target() - Call a LLD's ddp_target through the net device
+ * @lport: The local port to setup DDP for
+ * @xid: The exchange ID for this DDP transfer
+ * @sgl: The scatterlist describing this transfer
+ * @sgc: The number of sg items
+ *
+ * Returns: 0 if the DDP context was not configured
+ */
+static int fcoe_ddp_target(struct fc_lport *lport, u16 xid,
+ struct scatterlist *sgl, unsigned int sgc)
+{
+ struct net_device *netdev = fcoe_netdev(lport);
+
+ if (netdev->netdev_ops->ndo_fcoe_ddp_target)
+ return netdev->netdev_ops->ndo_fcoe_ddp_target(netdev, xid,
+ sgl, sgc);
+
+ return 0;
+}
+
+
+/**
* fcoe_ddp_done() - Call a LLD's ddp_done through the net device
* @lport: The local port to complete DDP on
* @xid: The exchange ID for this DDP transfer
@@ -938,9 +1091,11 @@ static int fcoe_ddp_done(struct fc_lport *lport, u16 xid)
static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
struct device *parent, int npiv)
{
+ struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
struct net_device *netdev = fcoe->netdev;
- struct fc_lport *lport = NULL;
+ struct fc_lport *lport, *n_port;
struct fcoe_port *port;
+ struct Scsi_Host *shost;
int rc;
/*
* parent is only a vport if npiv is 1,
@@ -950,13 +1105,11 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
FCOE_NETDEV_DBG(netdev, "Create Interface\n");
- if (!npiv) {
- lport = libfc_host_alloc(&fcoe_shost_template,
- sizeof(struct fcoe_port));
- } else {
- lport = libfc_vport_create(vport,
- sizeof(struct fcoe_port));
- }
+ if (!npiv)
+ lport = libfc_host_alloc(&fcoe_shost_template, sizeof(*port));
+ else
+ lport = libfc_vport_create(vport, sizeof(*port));
+
if (!lport) {
FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n");
rc = -ENOMEM;
@@ -964,9 +1117,18 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
}
port = lport_priv(lport);
port->lport = lport;
- port->fcoe = fcoe;
+ port->priv = fcoe;
+ port->get_netdev = fcoe_netdev;
+ port->max_queue_depth = FCOE_MAX_QUEUE_DEPTH;
+ port->min_queue_depth = FCOE_MIN_QUEUE_DEPTH;
INIT_WORK(&port->destroy_work, fcoe_destroy_work);
+ /*
+ * Need to add the lport to the hostlist
+ * so we catch NETDEV_CHANGE events.
+ */
+ fcoe_hostlist_add(lport);
+
/* configure a fc_lport including the exchange manager */
rc = fcoe_lport_config(lport);
if (rc) {
@@ -1000,39 +1162,45 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
}
/* Initialize the library */
- rc = fcoe_libfc_config(lport, &fcoe->ctlr, &fcoe_libfc_fcn_templ, 1);
+ rc = fcoe_libfc_config(lport, ctlr, &fcoe_libfc_fcn_templ, 1);
if (rc) {
FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the "
"interface\n");
goto out_lp_destroy;
}
- if (!npiv) {
- /*
- * fcoe_em_alloc() and fcoe_hostlist_add() both
- * need to be atomic with respect to other changes to the
- * hostlist since fcoe_em_alloc() looks for an existing EM
- * instance on host list updated by fcoe_hostlist_add().
- *
- * This is currently handled through the fcoe_config_mutex
- * begin held.
- */
+ /* Initialized FDMI information */
+ fcoe_fdmi_info(lport, netdev);
+ /*
+ * fcoe_em_alloc() and fcoe_hostlist_add() both
+ * need to be atomic with respect to other changes to the
+ * hostlist since fcoe_em_alloc() looks for an existing EM
+ * instance on host list updated by fcoe_hostlist_add().
+ *
+ * This is currently handled through the fcoe_config_mutex
+ * begin held.
+ */
+ if (!npiv)
/* lport exch manager allocation */
rc = fcoe_em_config(lport);
- if (rc) {
- FCOE_NETDEV_DBG(netdev, "Could not configure the EM "
- "for the interface\n");
- goto out_lp_destroy;
- }
+ else {
+ shost = vport_to_shost(vport);
+ n_port = shost_priv(shost);
+ rc = fc_exch_mgr_list_clone(n_port, lport);
+ }
+
+ if (rc) {
+ FCOE_NETDEV_DBG(netdev, "Could not configure the EM\n");
+ goto out_lp_destroy;
}
- fcoe_interface_get(fcoe);
return lport;
out_lp_destroy:
fc_exch_mgr_free(lport);
out_host_put:
+ fcoe_hostlist_del(lport);
scsi_host_put(lport->host);
out:
return ERR_PTR(rc);
@@ -1048,11 +1216,12 @@ out:
static int __init fcoe_if_init(void)
{
/* attach to scsi transport */
- fcoe_transport_template = fc_attach_transport(&fcoe_transport_function);
- fcoe_vport_transport_template =
- fc_attach_transport(&fcoe_vport_transport_function);
+ fcoe_nport_scsi_transport =
+ fc_attach_transport(&fcoe_nport_fc_functions);
+ fcoe_vport_scsi_transport =
+ fc_attach_transport(&fcoe_vport_fc_functions);
- if (!fcoe_transport_template) {
+ if (!fcoe_nport_scsi_transport) {
printk(KERN_ERR "fcoe: Failed to attach to the FC transport\n");
return -ENODEV;
}
@@ -1067,12 +1236,12 @@ static int __init fcoe_if_init(void)
*
* Returns: 0 on success
*/
-int __exit fcoe_if_exit(void)
+static int __exit fcoe_if_exit(void)
{
- fc_release_transport(fcoe_transport_template);
- fc_release_transport(fcoe_vport_transport_template);
- fcoe_transport_template = NULL;
- fcoe_vport_transport_template = NULL;
+ fc_release_transport(fcoe_nport_scsi_transport);
+ fc_release_transport(fcoe_vport_scsi_transport);
+ fcoe_nport_scsi_transport = NULL;
+ fcoe_vport_scsi_transport = NULL;
return 0;
}
@@ -1087,8 +1256,9 @@ static void fcoe_percpu_thread_create(unsigned int cpu)
p = &per_cpu(fcoe_percpu, cpu);
- thread = kthread_create(fcoe_percpu_receive_thread,
- (void *)p, "fcoethread/%d", cpu);
+ thread = kthread_create_on_node(fcoe_percpu_receive_thread,
+ (void *)p, cpu_to_node(cpu),
+ "fcoethread/%d", cpu);
if (likely(!IS_ERR(thread))) {
kthread_bind(thread, cpu);
@@ -1224,6 +1394,26 @@ static int fcoe_cpu_callback(struct notifier_block *nfb,
}
/**
+ * fcoe_select_cpu() - Selects CPU to handle post-processing of incoming
+ * command.
+ *
+ * This routine selects next CPU based on cpumask to distribute
+ * incoming requests in round robin.
+ *
+ * Returns: int CPU number
+ */
+static inline unsigned int fcoe_select_cpu(void)
+{
+ static unsigned int selected_cpu;
+
+ selected_cpu = cpumask_next(selected_cpu, cpu_online_mask);
+ if (selected_cpu >= nr_cpu_ids)
+ selected_cpu = cpumask_first(cpu_online_mask);
+
+ return selected_cpu;
+}
+
+/**
* fcoe_rcv() - Receive packets from a net device
* @skb: The received packet
* @netdev: The net device that the packet was received on
@@ -1235,11 +1425,12 @@ static int fcoe_cpu_callback(struct notifier_block *nfb,
*
* Returns: 0 for success
*/
-int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
+static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
struct packet_type *ptype, struct net_device *olddev)
{
struct fc_lport *lport;
struct fcoe_rcv_info *fr;
+ struct fcoe_ctlr *ctlr;
struct fcoe_interface *fcoe;
struct fc_frame_header *fh;
struct fcoe_percpu_s *fps;
@@ -1247,24 +1438,31 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
unsigned int cpu;
fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type);
- lport = fcoe->ctlr.lp;
+ ctlr = fcoe_to_ctlr(fcoe);
+ lport = ctlr->lp;
if (unlikely(!lport)) {
- FCOE_NETDEV_DBG(netdev, "Cannot find hba structure");
+ FCOE_NETDEV_DBG(netdev, "Cannot find hba structure\n");
goto err2;
}
if (!lport->link_up)
goto err2;
- FCOE_NETDEV_DBG(netdev, "skb_info: len:%d data_len:%d head:%p "
- "data:%p tail:%p end:%p sum:%d dev:%s",
+ FCOE_NETDEV_DBG(netdev,
+ "skb_info: len:%d data_len:%d head:%p data:%p tail:%p end:%p sum:%d dev:%s\n",
skb->len, skb->data_len, skb->head, skb->data,
skb_tail_pointer(skb), skb_end_pointer(skb),
skb->csum, skb->dev ? skb->dev->name : "<NULL>");
+
+ skb = skb_share_check(skb, GFP_ATOMIC);
+
+ if (skb == NULL)
+ return NET_RX_DROP;
+
eh = eth_hdr(skb);
- if (is_fip_mode(&fcoe->ctlr) &&
- compare_ether_addr(eh->h_source, fcoe->ctlr.dest_addr)) {
+ if (is_fip_mode(ctlr) &&
+ !ether_addr_equal(eh->h_source, ctlr->dest_addr)) {
FCOE_NETDEV_DBG(netdev, "wrong source mac address:%pM\n",
eh->h_source);
goto err;
@@ -1289,21 +1487,28 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
fr = fcoe_dev_from_skb(skb);
fr->fr_dev = lport;
- fr->ptype = ptype;
/*
* In case the incoming frame's exchange is originated from
* the initiator, then received frame's exchange id is ANDed
* with fc_cpu_mask bits to get the same cpu on which exchange
- * was originated, otherwise just use the current cpu.
+ * was originated, otherwise select cpu using rx exchange id
+ * or fcoe_select_cpu().
*/
if (ntoh24(fh->fh_f_ctl) & FC_FC_EX_CTX)
cpu = ntohs(fh->fh_ox_id) & fc_cpu_mask;
- else
- cpu = smp_processor_id();
+ else {
+ if (ntohs(fh->fh_rx_id) == FC_XID_UNKNOWN)
+ cpu = fcoe_select_cpu();
+ else
+ cpu = ntohs(fh->fh_rx_id) & fc_cpu_mask;
+ }
+
+ if (cpu >= nr_cpu_ids)
+ goto err;
fps = &per_cpu(fcoe_percpu, cpu);
- spin_lock_bh(&fps->fcoe_rx_list.lock);
+ spin_lock(&fps->fcoe_rx_list.lock);
if (unlikely(!fps->thread)) {
/*
* The targeted CPU is not ready, let's target
@@ -1314,12 +1519,12 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
"ready for incoming skb- using first online "
"CPU.\n");
- spin_unlock_bh(&fps->fcoe_rx_list.lock);
+ spin_unlock(&fps->fcoe_rx_list.lock);
cpu = cpumask_first(cpu_online_mask);
fps = &per_cpu(fcoe_percpu, cpu);
- spin_lock_bh(&fps->fcoe_rx_list.lock);
+ spin_lock(&fps->fcoe_rx_list.lock);
if (!fps->thread) {
- spin_unlock_bh(&fps->fcoe_rx_list.lock);
+ spin_unlock(&fps->fcoe_rx_list.lock);
goto err;
}
}
@@ -1330,137 +1535,44 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
* so we're free to queue skbs into it's queue.
*/
- /* If this is a SCSI-FCP frame, and this is already executing on the
- * correct CPU, and the queue for this CPU is empty, then go ahead
- * and process the frame directly in the softirq context.
- * This lets us process completions without context switching from the
- * NET_RX softirq, to our receive processing thread, and then back to
- * BLOCK softirq context.
+ /*
+ * Note: We used to have a set of conditions under which we would
+ * call fcoe_recv_frame directly, rather than queuing to the rx list
+ * as it could save a few cycles, but doing so is prohibited, as
+ * fcoe_recv_frame has several paths that may sleep, which is forbidden
+ * in softirq context.
*/
- if (fh->fh_type == FC_TYPE_FCP &&
- cpu == smp_processor_id() &&
- skb_queue_empty(&fps->fcoe_rx_list)) {
- spin_unlock_bh(&fps->fcoe_rx_list.lock);
- fcoe_recv_frame(skb);
- } else {
- __skb_queue_tail(&fps->fcoe_rx_list, skb);
- if (fps->fcoe_rx_list.qlen == 1)
- wake_up_process(fps->thread);
- spin_unlock_bh(&fps->fcoe_rx_list.lock);
- }
+ __skb_queue_tail(&fps->fcoe_rx_list, skb);
+ if (fps->thread->state == TASK_INTERRUPTIBLE)
+ wake_up_process(fps->thread);
+ spin_unlock(&fps->fcoe_rx_list.lock);
- return 0;
+ return NET_RX_SUCCESS;
err:
- per_cpu_ptr(lport->dev_stats, get_cpu())->ErrorFrames++;
+ per_cpu_ptr(lport->stats, get_cpu())->ErrorFrames++;
put_cpu();
err2:
kfree_skb(skb);
- return -1;
-}
-
-/**
- * fcoe_start_io() - Start FCoE I/O
- * @skb: The packet to be transmitted
- *
- * This routine is called from the net device to start transmitting
- * FCoE packets.
- *
- * Returns: 0 for success
- */
-static inline int fcoe_start_io(struct sk_buff *skb)
-{
- struct sk_buff *nskb;
- int rc;
-
- nskb = skb_clone(skb, GFP_ATOMIC);
- rc = dev_queue_xmit(nskb);
- if (rc != 0)
- return rc;
- kfree_skb(skb);
- return 0;
+ return NET_RX_DROP;
}
/**
- * fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC
+ * fcoe_alloc_paged_crc_eof() - Allocate a page to be used for the trailer CRC
* @skb: The packet to be transmitted
* @tlen: The total length of the trailer
*
- * This routine allocates a page for frame trailers. The page is re-used if
- * there is enough room left on it for the current trailer. If there isn't
- * enough buffer left a new page is allocated for the trailer. Reference to
- * the page from this function as well as the skbs using the page fragments
- * ensure that the page is freed at the appropriate time.
- *
* Returns: 0 for success
*/
-static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
+static int fcoe_alloc_paged_crc_eof(struct sk_buff *skb, int tlen)
{
struct fcoe_percpu_s *fps;
- struct page *page;
+ int rc;
fps = &get_cpu_var(fcoe_percpu);
- page = fps->crc_eof_page;
- if (!page) {
- page = alloc_page(GFP_ATOMIC);
- if (!page) {
- put_cpu_var(fcoe_percpu);
- return -ENOMEM;
- }
- fps->crc_eof_page = page;
- fps->crc_eof_offset = 0;
- }
-
- get_page(page);
- skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page,
- fps->crc_eof_offset, tlen);
- skb->len += tlen;
- skb->data_len += tlen;
- skb->truesize += tlen;
- fps->crc_eof_offset += sizeof(struct fcoe_crc_eof);
-
- if (fps->crc_eof_offset >= PAGE_SIZE) {
- fps->crc_eof_page = NULL;
- fps->crc_eof_offset = 0;
- put_page(page);
- }
+ rc = fcoe_get_paged_crc_eof(skb, tlen, fps);
put_cpu_var(fcoe_percpu);
- return 0;
-}
-/**
- * fcoe_fc_crc() - Calculates the CRC for a given frame
- * @fp: The frame to be checksumed
- *
- * This uses crc32() routine to calculate the CRC for a frame
- *
- * Return: The 32 bit CRC value
- */
-u32 fcoe_fc_crc(struct fc_frame *fp)
-{
- struct sk_buff *skb = fp_skb(fp);
- struct skb_frag_struct *frag;
- unsigned char *data;
- unsigned long off, len, clen;
- u32 crc;
- unsigned i;
-
- crc = crc32(~0, skb->data, skb_headlen(skb));
-
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- frag = &skb_shinfo(skb)->frags[i];
- off = frag->page_offset;
- len = frag->size;
- while (len > 0) {
- clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK));
- data = kmap_atomic(frag->page + (off >> PAGE_SHIFT),
- KM_SKB_DATA_SOFTIRQ);
- crc = crc32(crc, data + (off & ~PAGE_MASK), clen);
- kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ);
- off += clen;
- len -= clen;
- }
- }
- return crc;
+ return rc;
}
/**
@@ -1470,20 +1582,21 @@ u32 fcoe_fc_crc(struct fc_frame *fp)
*
* Return: 0 for success
*/
-int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
+static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
{
int wlen;
u32 crc;
struct ethhdr *eh;
struct fcoe_crc_eof *cp;
struct sk_buff *skb;
- struct fcoe_dev_stats *stats;
+ struct fc_stats *stats;
struct fc_frame_header *fh;
unsigned int hlen; /* header length implies the version */
unsigned int tlen; /* trailer length */
unsigned int elen; /* eth header, may include vlan */
struct fcoe_port *port = lport_priv(lport);
- struct fcoe_interface *fcoe = port->fcoe;
+ struct fcoe_interface *fcoe = port->priv;
+ struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
u8 sof, eof;
struct fcoe_hdr *hp;
@@ -1499,7 +1612,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
}
if (unlikely(fh->fh_type == FC_TYPE_ELS) &&
- fcoe_ctlr_els_send(&fcoe->ctlr, lport, skb))
+ fcoe_ctlr_els_send(ctlr, lport, skb))
return 0;
sof = fr_sof(fp);
@@ -1512,7 +1625,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
/* crc offload */
if (likely(lport->crc_offload)) {
- skb->ip_summed = CHECKSUM_PARTIAL;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->csum_start = skb_headroom(skb);
skb->csum_offset = skb->len;
crc = 0;
@@ -1524,12 +1637,12 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
/* copy port crc and eof to the skb buff */
if (skb_is_nonlinear(skb)) {
skb_frag_t *frag;
- if (fcoe_get_paged_crc_eof(skb, tlen)) {
+ if (fcoe_alloc_paged_crc_eof(skb, tlen)) {
kfree_skb(skb);
return -ENOMEM;
}
frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1];
- cp = kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ)
+ cp = kmap_atomic(skb_frag_page(frag))
+ frag->page_offset;
} else {
cp = (struct fcoe_crc_eof *)skb_put(skb, tlen);
@@ -1540,7 +1653,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
cp->fcoe_crc32 = cpu_to_le32(~crc);
if (skb_is_nonlinear(skb)) {
- kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ);
+ kunmap_atomic(cp);
cp = NULL;
}
@@ -1550,17 +1663,28 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
skb_reset_network_header(skb);
skb->mac_len = elen;
skb->protocol = htons(ETH_P_FCOE);
- skb->dev = fcoe->netdev;
+ skb->priority = fcoe->priority;
+
+ if (fcoe->netdev->priv_flags & IFF_802_1Q_VLAN &&
+ fcoe->realdev->features & NETIF_F_HW_VLAN_CTAG_TX) {
+ /* must set skb->dev before calling vlan_put_tag */
+ skb->dev = fcoe->realdev;
+ skb = __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+ vlan_dev_vlan_id(fcoe->netdev));
+ if (!skb)
+ return -ENOMEM;
+ } else
+ skb->dev = fcoe->netdev;
/* fill up mac and fcoe headers */
eh = eth_hdr(skb);
eh->h_proto = htons(ETH_P_FCOE);
- memcpy(eh->h_dest, fcoe->ctlr.dest_addr, ETH_ALEN);
- if (fcoe->ctlr.map_dest)
+ memcpy(eh->h_dest, ctlr->dest_addr, ETH_ALEN);
+ if (ctlr->map_dest)
memcpy(eh->h_dest + 3, fh->fh_d_id, 3);
- if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN))
- memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN);
+ if (unlikely(ctlr->flogi_oxid != FC_XID_UNKNOWN))
+ memcpy(eh->h_source, ctlr->ctl_src_addr, ETH_ALEN);
else
memcpy(eh->h_source, port->data_src_addr, ETH_ALEN);
@@ -1579,18 +1703,14 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
skb_shinfo(skb)->gso_size = 0;
}
/* update tx stats: regardless if LLD fails */
- stats = per_cpu_ptr(lport->dev_stats, get_cpu());
+ stats = per_cpu_ptr(lport->stats, get_cpu());
stats->TxFrames++;
stats->TxWords += wlen;
put_cpu();
/* send down to lld */
fr_dev(fp) = lport;
- if (port->fcoe_pending_queue.qlen)
- fcoe_check_wait_queue(lport, skb);
- else if (fcoe_start_io(skb))
- fcoe_check_wait_queue(lport, skb);
-
+ fcoe_port_send(port, skb);
return 0;
}
@@ -1604,6 +1724,59 @@ static void fcoe_percpu_flush_done(struct sk_buff *skb)
}
/**
+ * fcoe_filter_frames() - filter out bad fcoe frames, i.e. bad CRC
+ * @lport: The local port the frame was received on
+ * @fp: The received frame
+ *
+ * Return: 0 on passing filtering checks
+ */
+static inline int fcoe_filter_frames(struct fc_lport *lport,
+ struct fc_frame *fp)
+{
+ struct fcoe_ctlr *ctlr;
+ struct fcoe_interface *fcoe;
+ struct fc_frame_header *fh;
+ struct sk_buff *skb = (struct sk_buff *)fp;
+ struct fc_stats *stats;
+
+ /*
+ * We only check CRC if no offload is available and if it is
+ * it's solicited data, in which case, the FCP layer would
+ * check it during the copy.
+ */
+ if (lport->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY)
+ fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+ else
+ fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
+
+ fh = (struct fc_frame_header *) skb_transport_header(skb);
+ fh = fc_frame_header_get(fp);
+ if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && fh->fh_type == FC_TYPE_FCP)
+ return 0;
+
+ fcoe = ((struct fcoe_port *)lport_priv(lport))->priv;
+ ctlr = fcoe_to_ctlr(fcoe);
+ if (is_fip_mode(ctlr) && fc_frame_payload_op(fp) == ELS_LOGO &&
+ ntoh24(fh->fh_s_id) == FC_FID_FLOGI) {
+ FCOE_DBG("fcoe: dropping FCoE lport LOGO in fip mode\n");
+ return -EINVAL;
+ }
+
+ if (!(fr_flags(fp) & FCPHF_CRC_UNCHECKED) ||
+ le32_to_cpu(fr_crc(fp)) == ~crc32(~0, skb->data, skb->len)) {
+ fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+ return 0;
+ }
+
+ stats = per_cpu_ptr(lport->stats, get_cpu());
+ stats->InvalidCRCCount++;
+ if (stats->InvalidCRCCount < 5)
+ printk(KERN_WARNING "fcoe: dropping frame with CRC error\n");
+ put_cpu();
+ return -EINVAL;
+}
+
+/**
* fcoe_recv_frame() - process a single received frame
* @skb: frame to process
*/
@@ -1612,8 +1785,7 @@ static void fcoe_recv_frame(struct sk_buff *skb)
u32 fr_len;
struct fc_lport *lport;
struct fcoe_rcv_info *fr;
- struct fcoe_dev_stats *stats;
- struct fc_frame_header *fh;
+ struct fc_stats *stats;
struct fcoe_crc_eof crc_eof;
struct fc_frame *fp;
struct fcoe_port *port;
@@ -1623,30 +1795,28 @@ static void fcoe_recv_frame(struct sk_buff *skb)
lport = fr->fr_dev;
if (unlikely(!lport)) {
if (skb->destructor != fcoe_percpu_flush_done)
- FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb");
+ FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb\n");
kfree_skb(skb);
return;
}
- FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d "
- "head:%p data:%p tail:%p end:%p sum:%d dev:%s",
+ FCOE_NETDEV_DBG(skb->dev,
+ "skb_info: len:%d data_len:%d head:%p data:%p tail:%p end:%p sum:%d dev:%s\n",
skb->len, skb->data_len,
skb->head, skb->data, skb_tail_pointer(skb),
skb_end_pointer(skb), skb->csum,
skb->dev ? skb->dev->name : "<NULL>");
port = lport_priv(lport);
- if (skb_is_nonlinear(skb))
- skb_linearize(skb); /* not ideal */
+ skb_linearize(skb); /* check for skb_is_nonlinear is within skb_linearize */
/*
* Frame length checks and setting up the header pointers
* was done in fcoe_rcv already.
*/
hp = (struct fcoe_hdr *) skb_network_header(skb);
- fh = (struct fc_frame_header *) skb_transport_header(skb);
- stats = per_cpu_ptr(lport->dev_stats, get_cpu());
+ stats = per_cpu_ptr(lport->stats, get_cpu());
if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
if (stats->ErrorFrames < 5)
printk(KERN_WARNING "fcoe: FCoE version "
@@ -1677,35 +1847,11 @@ static void fcoe_recv_frame(struct sk_buff *skb)
if (pskb_trim(skb, fr_len))
goto drop;
- /*
- * We only check CRC if no offload is available and if it is
- * it's solicited data, in which case, the FCP layer would
- * check it during the copy.
- */
- if (lport->crc_offload &&
- skb->ip_summed == CHECKSUM_UNNECESSARY)
- fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
- else
- fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
-
- fh = fc_frame_header_get(fp);
- if ((fh->fh_r_ctl != FC_RCTL_DD_SOL_DATA ||
- fh->fh_type != FC_TYPE_FCP) &&
- (fr_flags(fp) & FCPHF_CRC_UNCHECKED)) {
- if (le32_to_cpu(fr_crc(fp)) !=
- ~crc32(~0, skb->data, fr_len)) {
- if (stats->InvalidCRCCount < 5)
- printk(KERN_WARNING "fcoe: dropping "
- "frame with CRC error\n");
- stats->InvalidCRCCount++;
- goto drop;
- }
- fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+ if (!fcoe_filter_frames(lport, fp)) {
+ put_cpu();
+ fc_exch_recv(lport, fp);
+ return;
}
- put_cpu();
- fc_exch_recv(lport, fp);
- return;
-
drop:
stats->ErrorFrames++;
put_cpu();
@@ -1718,87 +1864,37 @@ drop:
*
* Return: 0 for success
*/
-int fcoe_percpu_receive_thread(void *arg)
+static int fcoe_percpu_receive_thread(void *arg)
{
struct fcoe_percpu_s *p = arg;
struct sk_buff *skb;
+ struct sk_buff_head tmp;
+
+ skb_queue_head_init(&tmp);
- set_user_nice(current, -20);
+ set_user_nice(current, MIN_NICE);
+retry:
while (!kthread_should_stop()) {
spin_lock_bh(&p->fcoe_rx_list.lock);
- while ((skb = __skb_dequeue(&p->fcoe_rx_list)) == NULL) {
+ skb_queue_splice_init(&p->fcoe_rx_list, &tmp);
+
+ if (!skb_queue_len(&tmp)) {
set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_bh(&p->fcoe_rx_list.lock);
schedule();
set_current_state(TASK_RUNNING);
- if (kthread_should_stop())
- return 0;
- spin_lock_bh(&p->fcoe_rx_list.lock);
+ goto retry;
}
- spin_unlock_bh(&p->fcoe_rx_list.lock);
- fcoe_recv_frame(skb);
- }
- return 0;
-}
-
-/**
- * fcoe_check_wait_queue() - Attempt to clear the transmit backlog
- * @lport: The local port whose backlog is to be cleared
- *
- * This empties the wait_queue, dequeues the head of the wait_queue queue
- * and calls fcoe_start_io() for each packet. If all skb have been
- * transmitted it returns the qlen. If an error occurs it restores
- * wait_queue (to try again later) and returns -1.
- *
- * The wait_queue is used when the skb transmit fails. The failed skb
- * will go in the wait_queue which will be emptied by the timer function or
- * by the next skb transmit.
- */
-static void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb)
-{
- struct fcoe_port *port = lport_priv(lport);
- int rc;
-
- spin_lock_bh(&port->fcoe_pending_queue.lock);
-
- if (skb)
- __skb_queue_tail(&port->fcoe_pending_queue, skb);
- if (port->fcoe_pending_queue_active)
- goto out;
- port->fcoe_pending_queue_active = 1;
-
- while (port->fcoe_pending_queue.qlen) {
- /* keep qlen > 0 until fcoe_start_io succeeds */
- port->fcoe_pending_queue.qlen++;
- skb = __skb_dequeue(&port->fcoe_pending_queue);
+ spin_unlock_bh(&p->fcoe_rx_list.lock);
- spin_unlock_bh(&port->fcoe_pending_queue.lock);
- rc = fcoe_start_io(skb);
- spin_lock_bh(&port->fcoe_pending_queue.lock);
+ while ((skb = __skb_dequeue(&tmp)) != NULL)
+ fcoe_recv_frame(skb);
- if (rc) {
- __skb_queue_head(&port->fcoe_pending_queue, skb);
- /* undo temporary increment above */
- port->fcoe_pending_queue.qlen--;
- break;
- }
- /* undo temporary increment above */
- port->fcoe_pending_queue.qlen--;
}
-
- if (port->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
- lport->qfull = 0;
- if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer))
- mod_timer(&port->timer, jiffies + 2);
- port->fcoe_pending_queue_active = 0;
-out:
- if (port->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
- lport->qfull = 1;
- spin_unlock_bh(&port->fcoe_pending_queue.lock);
- return;
+ return 0;
}
/**
@@ -1806,6 +1902,7 @@ out:
*/
static void fcoe_dev_setup(void)
{
+ register_dcbevent_notifier(&dcb_notifier);
register_netdevice_notifier(&fcoe_notifier);
}
@@ -1814,9 +1911,69 @@ static void fcoe_dev_setup(void)
*/
static void fcoe_dev_cleanup(void)
{
+ unregister_dcbevent_notifier(&dcb_notifier);
unregister_netdevice_notifier(&fcoe_notifier);
}
+static struct fcoe_interface *
+fcoe_hostlist_lookup_realdev_port(struct net_device *netdev)
+{
+ struct fcoe_interface *fcoe;
+ struct net_device *real_dev;
+
+ list_for_each_entry(fcoe, &fcoe_hostlist, list) {
+ if (fcoe->netdev->priv_flags & IFF_802_1Q_VLAN)
+ real_dev = vlan_dev_real_dev(fcoe->netdev);
+ else
+ real_dev = fcoe->netdev;
+
+ if (netdev == real_dev)
+ return fcoe;
+ }
+ return NULL;
+}
+
+static int fcoe_dcb_app_notification(struct notifier_block *notifier,
+ ulong event, void *ptr)
+{
+ struct dcb_app_type *entry = ptr;
+ struct fcoe_ctlr *ctlr;
+ struct fcoe_interface *fcoe;
+ struct net_device *netdev;
+ int prio;
+
+ if (entry->app.selector != DCB_APP_IDTYPE_ETHTYPE)
+ return NOTIFY_OK;
+
+ netdev = dev_get_by_index(&init_net, entry->ifindex);
+ if (!netdev)
+ return NOTIFY_OK;
+
+ fcoe = fcoe_hostlist_lookup_realdev_port(netdev);
+ dev_put(netdev);
+ if (!fcoe)
+ return NOTIFY_OK;
+
+ ctlr = fcoe_to_ctlr(fcoe);
+
+ if (entry->dcbx & DCB_CAP_DCBX_VER_CEE)
+ prio = ffs(entry->app.priority) - 1;
+ else
+ prio = entry->app.priority;
+
+ if (prio < 0)
+ return NOTIFY_OK;
+
+ if (entry->app.protocol == ETH_P_FIP ||
+ entry->app.protocol == ETH_P_FCOE)
+ ctlr->priority = prio;
+
+ if (entry->app.protocol == ETH_P_FCOE)
+ fcoe->priority = prio;
+
+ return NOTIFY_OK;
+}
+
/**
* fcoe_device_notification() - Handler for net device events
* @notifier: The context of the notification
@@ -1830,18 +1987,21 @@ static void fcoe_dev_cleanup(void)
static int fcoe_device_notification(struct notifier_block *notifier,
ulong event, void *ptr)
{
+ struct fcoe_ctlr_device *cdev;
struct fc_lport *lport = NULL;
- struct net_device *netdev = ptr;
+ struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
+ struct fcoe_ctlr *ctlr;
struct fcoe_interface *fcoe;
struct fcoe_port *port;
- struct fcoe_dev_stats *stats;
+ struct fc_stats *stats;
u32 link_possible = 1;
u32 mfs;
int rc = NOTIFY_OK;
list_for_each_entry(fcoe, &fcoe_hostlist, list) {
if (fcoe->netdev == netdev) {
- lport = fcoe->ctlr.lp;
+ ctlr = fcoe_to_ctlr(fcoe);
+ lport = ctlr->lp;
break;
}
}
@@ -1870,9 +2030,8 @@ static int fcoe_device_notification(struct notifier_block *notifier,
break;
case NETDEV_UNREGISTER:
list_del(&fcoe->list);
- port = lport_priv(fcoe->ctlr.lp);
- fcoe_interface_cleanup(fcoe);
- schedule_work(&port->destroy_work);
+ port = lport_priv(ctlr->lp);
+ queue_work(fcoe_wq, &port->destroy_work);
goto out;
break;
case NETDEV_FEAT_CHANGE:
@@ -1885,202 +2044,158 @@ static int fcoe_device_notification(struct notifier_block *notifier,
fcoe_link_speed_update(lport);
- if (link_possible && !fcoe_link_ok(lport))
- fcoe_ctlr_link_up(&fcoe->ctlr);
- else if (fcoe_ctlr_link_down(&fcoe->ctlr)) {
- stats = per_cpu_ptr(lport->dev_stats, get_cpu());
- stats->LinkFailureCount++;
- put_cpu();
- fcoe_clean_pending_queue(lport);
+ cdev = fcoe_ctlr_to_ctlr_dev(ctlr);
+
+ if (link_possible && !fcoe_link_ok(lport)) {
+ switch (cdev->enabled) {
+ case FCOE_CTLR_DISABLED:
+ pr_info("Link up while interface is disabled.\n");
+ break;
+ case FCOE_CTLR_ENABLED:
+ case FCOE_CTLR_UNUSED:
+ fcoe_ctlr_link_up(ctlr);
+ };
+ } else if (fcoe_ctlr_link_down(ctlr)) {
+ switch (cdev->enabled) {
+ case FCOE_CTLR_DISABLED:
+ pr_info("Link down while interface is disabled.\n");
+ break;
+ case FCOE_CTLR_ENABLED:
+ case FCOE_CTLR_UNUSED:
+ stats = per_cpu_ptr(lport->stats, get_cpu());
+ stats->LinkFailureCount++;
+ put_cpu();
+ fcoe_clean_pending_queue(lport);
+ };
}
out:
return rc;
}
/**
- * fcoe_if_to_netdev() - Parse a name buffer to get a net device
- * @buffer: The name of the net device
- *
- * Returns: NULL or a ptr to net_device
- */
-static struct net_device *fcoe_if_to_netdev(const char *buffer)
-{
- char *cp;
- char ifname[IFNAMSIZ + 2];
-
- if (buffer) {
- strlcpy(ifname, buffer, IFNAMSIZ);
- cp = ifname + strlen(ifname);
- while (--cp >= ifname && *cp == '\n')
- *cp = '\0';
- return dev_get_by_name(&init_net, ifname);
- }
- return NULL;
-}
-
-/**
* fcoe_disable() - Disables a FCoE interface
- * @buffer: The name of the Ethernet interface to be disabled
- * @kp: The associated kernel parameter
+ * @netdev : The net_device object the Ethernet interface to create on
*
- * Called from sysfs.
+ * Called from fcoe transport.
*
* Returns: 0 for success
+ *
+ * Deprecated: use fcoe_ctlr_enabled()
*/
-static int fcoe_disable(const char *buffer, struct kernel_param *kp)
+static int fcoe_disable(struct net_device *netdev)
{
+ struct fcoe_ctlr *ctlr;
struct fcoe_interface *fcoe;
- struct net_device *netdev;
int rc = 0;
mutex_lock(&fcoe_config_mutex);
-#ifdef CONFIG_FCOE_MODULE
- /*
- * Make sure the module has been initialized, and is not about to be
- * removed. Module paramter sysfs files are writable before the
- * module_init function is called and after module_exit.
- */
- if (THIS_MODULE->state != MODULE_STATE_LIVE) {
- rc = -ENODEV;
- goto out_nodev;
- }
-#endif
-
- netdev = fcoe_if_to_netdev(buffer);
- if (!netdev) {
- rc = -ENODEV;
- goto out_nodev;
- }
-
- if (!rtnl_trylock()) {
- dev_put(netdev);
- mutex_unlock(&fcoe_config_mutex);
- return restart_syscall();
- }
+ rtnl_lock();
fcoe = fcoe_hostlist_lookup_port(netdev);
rtnl_unlock();
if (fcoe) {
- fcoe_ctlr_link_down(&fcoe->ctlr);
- fcoe_clean_pending_queue(fcoe->ctlr.lp);
+ ctlr = fcoe_to_ctlr(fcoe);
+ fcoe_ctlr_link_down(ctlr);
+ fcoe_clean_pending_queue(ctlr->lp);
} else
rc = -ENODEV;
- dev_put(netdev);
-out_nodev:
mutex_unlock(&fcoe_config_mutex);
return rc;
}
/**
* fcoe_enable() - Enables a FCoE interface
- * @buffer: The name of the Ethernet interface to be enabled
- * @kp: The associated kernel parameter
+ * @netdev : The net_device object the Ethernet interface to create on
*
- * Called from sysfs.
+ * Called from fcoe transport.
*
* Returns: 0 for success
*/
-static int fcoe_enable(const char *buffer, struct kernel_param *kp)
+static int fcoe_enable(struct net_device *netdev)
{
+ struct fcoe_ctlr *ctlr;
struct fcoe_interface *fcoe;
- struct net_device *netdev;
int rc = 0;
mutex_lock(&fcoe_config_mutex);
-#ifdef CONFIG_FCOE_MODULE
- /*
- * Make sure the module has been initialized, and is not about to be
- * removed. Module paramter sysfs files are writable before the
- * module_init function is called and after module_exit.
- */
- if (THIS_MODULE->state != MODULE_STATE_LIVE) {
- rc = -ENODEV;
- goto out_nodev;
- }
-#endif
+ rtnl_lock();
+ fcoe = fcoe_hostlist_lookup_port(netdev);
+ rtnl_unlock();
- netdev = fcoe_if_to_netdev(buffer);
- if (!netdev) {
+ if (!fcoe) {
rc = -ENODEV;
- goto out_nodev;
- }
-
- if (!rtnl_trylock()) {
- dev_put(netdev);
- mutex_unlock(&fcoe_config_mutex);
- return restart_syscall();
+ goto out;
}
- fcoe = fcoe_hostlist_lookup_port(netdev);
- rtnl_unlock();
+ ctlr = fcoe_to_ctlr(fcoe);
- if (!fcoe)
- rc = -ENODEV;
- else if (!fcoe_link_ok(fcoe->ctlr.lp))
- fcoe_ctlr_link_up(&fcoe->ctlr);
+ if (!fcoe_link_ok(ctlr->lp))
+ fcoe_ctlr_link_up(ctlr);
- dev_put(netdev);
-out_nodev:
+out:
mutex_unlock(&fcoe_config_mutex);
return rc;
}
/**
+ * fcoe_ctlr_enabled() - Enable or disable an FCoE Controller
+ * @cdev: The FCoE Controller that is being enabled or disabled
+ *
+ * fcoe_sysfs will ensure that the state of 'enabled' has
+ * changed, so no checking is necessary here. This routine simply
+ * calls fcoe_enable or fcoe_disable, both of which are deprecated.
+ * When those routines are removed the functionality can be merged
+ * here.
+ */
+static int fcoe_ctlr_enabled(struct fcoe_ctlr_device *cdev)
+{
+ struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(cdev);
+ struct fc_lport *lport = ctlr->lp;
+ struct net_device *netdev = fcoe_netdev(lport);
+
+ switch (cdev->enabled) {
+ case FCOE_CTLR_ENABLED:
+ return fcoe_enable(netdev);
+ case FCOE_CTLR_DISABLED:
+ return fcoe_disable(netdev);
+ case FCOE_CTLR_UNUSED:
+ default:
+ return -ENOTSUPP;
+ };
+}
+
+/**
* fcoe_destroy() - Destroy a FCoE interface
- * @buffer: The name of the Ethernet interface to be destroyed
- * @kp: The associated kernel parameter
+ * @netdev : The net_device object the Ethernet interface to create on
*
- * Called from sysfs.
+ * Called from fcoe transport
*
* Returns: 0 for success
*/
-static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
+static int fcoe_destroy(struct net_device *netdev)
{
+ struct fcoe_ctlr *ctlr;
struct fcoe_interface *fcoe;
- struct net_device *netdev;
+ struct fc_lport *lport;
+ struct fcoe_port *port;
int rc = 0;
mutex_lock(&fcoe_config_mutex);
-#ifdef CONFIG_FCOE_MODULE
- /*
- * Make sure the module has been initialized, and is not about to be
- * removed. Module paramter sysfs files are writable before the
- * module_init function is called and after module_exit.
- */
- if (THIS_MODULE->state != MODULE_STATE_LIVE) {
- rc = -ENODEV;
- goto out_nodev;
- }
-#endif
-
- netdev = fcoe_if_to_netdev(buffer);
- if (!netdev) {
- rc = -ENODEV;
- goto out_nodev;
- }
-
- if (!rtnl_trylock()) {
- dev_put(netdev);
- mutex_unlock(&fcoe_config_mutex);
- return restart_syscall();
- }
-
+ rtnl_lock();
fcoe = fcoe_hostlist_lookup_port(netdev);
if (!fcoe) {
- rtnl_unlock();
rc = -ENODEV;
- goto out_putdev;
+ goto out_nodev;
}
- fcoe_interface_cleanup(fcoe);
+ ctlr = fcoe_to_ctlr(fcoe);
+ lport = ctlr->lp;
+ port = lport_priv(lport);
list_del(&fcoe->list);
- /* RTNL mutex is dropped by fcoe_if_destroy */
- fcoe_if_destroy(fcoe->ctlr.lp);
-
-out_putdev:
- dev_put(netdev);
+ queue_work(fcoe_wq, &port->destroy_work);
out_nodev:
+ rtnl_unlock();
mutex_unlock(&fcoe_config_mutex);
return rc;
}
@@ -2091,148 +2206,230 @@ out_nodev:
*/
static void fcoe_destroy_work(struct work_struct *work)
{
+ struct fcoe_ctlr_device *cdev;
+ struct fcoe_ctlr *ctlr;
struct fcoe_port *port;
+ struct fcoe_interface *fcoe;
+ struct Scsi_Host *shost;
+ struct fc_host_attrs *fc_host;
+ unsigned long flags;
+ struct fc_vport *vport;
+ struct fc_vport *next_vport;
port = container_of(work, struct fcoe_port, destroy_work);
+ shost = port->lport->host;
+ fc_host = shost_to_fc_host(shost);
+
+ /* Loop through all the vports and mark them for deletion */
+ spin_lock_irqsave(shost->host_lock, flags);
+ list_for_each_entry_safe(vport, next_vport, &fc_host->vports, peers) {
+ if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING)) {
+ continue;
+ } else {
+ vport->flags |= FC_VPORT_DELETING;
+ queue_work(fc_host_work_q(shost),
+ &vport->vport_delete_work);
+ }
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ flush_workqueue(fc_host_work_q(shost));
+
mutex_lock(&fcoe_config_mutex);
- rtnl_lock();
- /* RTNL mutex is dropped by fcoe_if_destroy */
+
+ fcoe = port->priv;
+ ctlr = fcoe_to_ctlr(fcoe);
+ cdev = fcoe_ctlr_to_ctlr_dev(ctlr);
+
fcoe_if_destroy(port->lport);
+ fcoe_interface_cleanup(fcoe);
+
mutex_unlock(&fcoe_config_mutex);
+
+ fcoe_ctlr_device_delete(cdev);
}
/**
- * fcoe_create() - Create a fcoe interface
- * @buffer: The name of the Ethernet interface to create on
- * @kp: The associated kernel param
+ * fcoe_match() - Check if the FCoE is supported on the given netdevice
+ * @netdev : The net_device object the Ethernet interface to create on
*
- * Called from sysfs.
+ * Called from fcoe transport.
*
- * Returns: 0 for success
+ * Returns: always returns true as this is the default FCoE transport,
+ * i.e., support all netdevs.
*/
-static int fcoe_create(const char *buffer, struct kernel_param *kp)
+static bool fcoe_match(struct net_device *netdev)
{
- enum fip_state fip_mode = (enum fip_state)(long)kp->arg;
- int rc;
- struct fcoe_interface *fcoe;
- struct fc_lport *lport;
- struct net_device *netdev;
+ return true;
+}
- mutex_lock(&fcoe_config_mutex);
+/**
+ * fcoe_dcb_create() - Initialize DCB attributes and hooks
+ * @netdev: The net_device object of the L2 link that should be queried
+ * @port: The fcoe_port to bind FCoE APP priority with
+ * @
+ */
+static void fcoe_dcb_create(struct fcoe_interface *fcoe)
+{
+#ifdef CONFIG_DCB
+ int dcbx;
+ u8 fup, up;
+ struct net_device *netdev = fcoe->realdev;
+ struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
+ struct dcb_app app = {
+ .priority = 0,
+ .protocol = ETH_P_FCOE
+ };
- if (!rtnl_trylock()) {
- mutex_unlock(&fcoe_config_mutex);
- return restart_syscall();
- }
+ /* setup DCB priority attributes. */
+ if (netdev && netdev->dcbnl_ops && netdev->dcbnl_ops->getdcbx) {
+ dcbx = netdev->dcbnl_ops->getdcbx(netdev);
-#ifdef CONFIG_FCOE_MODULE
- /*
- * Make sure the module has been initialized, and is not about to be
- * removed. Module paramter sysfs files are writable before the
- * module_init function is called and after module_exit.
- */
- if (THIS_MODULE->state != MODULE_STATE_LIVE) {
- rc = -ENODEV;
- goto out_nomod;
+ if (dcbx & DCB_CAP_DCBX_VER_IEEE) {
+ app.selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE;
+ up = dcb_ieee_getapp_mask(netdev, &app);
+ app.protocol = ETH_P_FIP;
+ fup = dcb_ieee_getapp_mask(netdev, &app);
+ } else {
+ app.selector = DCB_APP_IDTYPE_ETHTYPE;
+ up = dcb_getapp(netdev, &app);
+ app.protocol = ETH_P_FIP;
+ fup = dcb_getapp(netdev, &app);
+ }
+
+ fcoe->priority = ffs(up) ? ffs(up) - 1 : 0;
+ ctlr->priority = ffs(fup) ? ffs(fup) - 1 : fcoe->priority;
}
#endif
+}
- if (!try_module_get(THIS_MODULE)) {
- rc = -EINVAL;
- goto out_nomod;
- }
+enum fcoe_create_link_state {
+ FCOE_CREATE_LINK_DOWN,
+ FCOE_CREATE_LINK_UP,
+};
- netdev = fcoe_if_to_netdev(buffer);
- if (!netdev) {
- rc = -ENODEV;
- goto out_nodev;
- }
+/**
+ * _fcoe_create() - (internal) Create a fcoe interface
+ * @netdev : The net_device object the Ethernet interface to create on
+ * @fip_mode: The FIP mode for this creation
+ * @link_state: The ctlr link state on creation
+ *
+ * Called from either the libfcoe 'create' module parameter
+ * via fcoe_create or from fcoe_syfs's ctlr_create file.
+ *
+ * libfcoe's 'create' module parameter is deprecated so some
+ * consolidation of code can be done when that interface is
+ * removed.
+ */
+static int _fcoe_create(struct net_device *netdev, enum fip_state fip_mode,
+ enum fcoe_create_link_state link_state)
+{
+ int rc = 0;
+ struct fcoe_ctlr_device *ctlr_dev;
+ struct fcoe_ctlr *ctlr;
+ struct fcoe_interface *fcoe;
+ struct fc_lport *lport;
+
+ mutex_lock(&fcoe_config_mutex);
+ rtnl_lock();
/* look for existing lport */
if (fcoe_hostlist_lookup(netdev)) {
rc = -EEXIST;
- goto out_putdev;
+ goto out_nodev;
}
fcoe = fcoe_interface_create(netdev, fip_mode);
- if (!fcoe) {
- rc = -ENOMEM;
- goto out_putdev;
+ if (IS_ERR(fcoe)) {
+ rc = PTR_ERR(fcoe);
+ goto out_nodev;
}
- lport = fcoe_if_create(fcoe, &netdev->dev, 0);
+ ctlr = fcoe_to_ctlr(fcoe);
+ ctlr_dev = fcoe_ctlr_to_ctlr_dev(ctlr);
+ lport = fcoe_if_create(fcoe, &ctlr_dev->dev, 0);
if (IS_ERR(lport)) {
printk(KERN_ERR "fcoe: Failed to create interface (%s)\n",
netdev->name);
rc = -EIO;
+ rtnl_unlock();
fcoe_interface_cleanup(fcoe);
- goto out_free;
+ mutex_unlock(&fcoe_config_mutex);
+ fcoe_ctlr_device_delete(ctlr_dev);
+ goto out;
}
/* Make this the "master" N_Port */
- fcoe->ctlr.lp = lport;
+ ctlr->lp = lport;
- /* add to lports list */
- fcoe_hostlist_add(lport);
+ /* setup DCB priority attributes. */
+ fcoe_dcb_create(fcoe);
/* start FIP Discovery and FLOGI */
lport->boot_time = jiffies;
fc_fabric_login(lport);
- if (!fcoe_link_ok(lport))
- fcoe_ctlr_link_up(&fcoe->ctlr);
/*
- * Release from init in fcoe_interface_create(), on success lport
- * should be holding a reference taken in fcoe_if_create().
+ * If the fcoe_ctlr_device is to be set to DISABLED
+ * it must be done after the lport is added to the
+ * hostlist, but before the rtnl_lock is released.
+ * This is because the rtnl_lock protects the
+ * hostlist that fcoe_device_notification uses. If
+ * the FCoE Controller is intended to be created
+ * DISABLED then 'enabled' needs to be considered
+ * handling link events. 'enabled' must be set
+ * before the lport can be found in the hostlist
+ * when a link up event is received.
*/
- fcoe_interface_put(fcoe);
- dev_put(netdev);
- rtnl_unlock();
- mutex_unlock(&fcoe_config_mutex);
+ if (link_state == FCOE_CREATE_LINK_UP)
+ ctlr_dev->enabled = FCOE_CTLR_ENABLED;
+ else
+ ctlr_dev->enabled = FCOE_CTLR_DISABLED;
+
+ if (link_state == FCOE_CREATE_LINK_UP &&
+ !fcoe_link_ok(lport)) {
+ rtnl_unlock();
+ fcoe_ctlr_link_up(ctlr);
+ mutex_unlock(&fcoe_config_mutex);
+ return rc;
+ }
- return 0;
-out_free:
- fcoe_interface_put(fcoe);
-out_putdev:
- dev_put(netdev);
out_nodev:
- module_put(THIS_MODULE);
-out_nomod:
rtnl_unlock();
mutex_unlock(&fcoe_config_mutex);
+out:
return rc;
}
/**
- * fcoe_link_speed_update() - Update the supported and actual link speeds
- * @lport: The local port to update speeds for
+ * fcoe_create() - Create a fcoe interface
+ * @netdev : The net_device object the Ethernet interface to create on
+ * @fip_mode: The FIP mode for this creation
+ *
+ * Called from fcoe transport
*
- * Returns: 0 if the ethtool query was successful
- * -1 if the ethtool query failed
+ * Returns: 0 for success
*/
-int fcoe_link_speed_update(struct fc_lport *lport)
+static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
{
- struct fcoe_port *port = lport_priv(lport);
- struct net_device *netdev = port->fcoe->netdev;
- struct ethtool_cmd ecmd = { ETHTOOL_GSET };
-
- if (!dev_ethtool_get_settings(netdev, &ecmd)) {
- lport->link_supported_speeds &=
- ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
- if (ecmd.supported & (SUPPORTED_1000baseT_Half |
- SUPPORTED_1000baseT_Full))
- lport->link_supported_speeds |= FC_PORTSPEED_1GBIT;
- if (ecmd.supported & SUPPORTED_10000baseT_Full)
- lport->link_supported_speeds |=
- FC_PORTSPEED_10GBIT;
- if (ecmd.speed == SPEED_1000)
- lport->link_speed = FC_PORTSPEED_1GBIT;
- if (ecmd.speed == SPEED_10000)
- lport->link_speed = FC_PORTSPEED_10GBIT;
+ return _fcoe_create(netdev, fip_mode, FCOE_CREATE_LINK_UP);
+}
- return 0;
- }
- return -1;
+/**
+ * fcoe_ctlr_alloc() - Allocate a fcoe interface from fcoe_sysfs
+ * @netdev: The net_device to be used by the allocated FCoE Controller
+ *
+ * This routine is called from fcoe_sysfs. It will start the fcoe_ctlr
+ * in a link_down state. The allows the user an opportunity to configure
+ * the FCoE Controller from sysfs before enabling the FCoE Controller.
+ *
+ * Creating in with this routine starts the FCoE Controller in Fabric
+ * mode. The user can change to VN2VN or another mode before enabling.
+ */
+static int fcoe_ctlr_alloc(struct net_device *netdev)
+{
+ return _fcoe_create(netdev, FIP_MODE_FABRIC,
+ FCOE_CREATE_LINK_DOWN);
}
/**
@@ -2242,10 +2439,9 @@ int fcoe_link_speed_update(struct fc_lport *lport)
* Returns: 0 if link is UP and OK, -1 if not
*
*/
-int fcoe_link_ok(struct fc_lport *lport)
+static int fcoe_link_ok(struct fc_lport *lport)
{
- struct fcoe_port *port = lport_priv(lport);
- struct net_device *netdev = port->fcoe->netdev;
+ struct net_device *netdev = fcoe_netdev(lport);
if (netif_oper_up(netdev))
return 0;
@@ -2263,42 +2459,25 @@ int fcoe_link_ok(struct fc_lport *lport)
* there no packets that will be handled by the lport, but also that any
* threads already handling packet have returned.
*/
-void fcoe_percpu_clean(struct fc_lport *lport)
+static void fcoe_percpu_clean(struct fc_lport *lport)
{
struct fcoe_percpu_s *pp;
- struct fcoe_rcv_info *fr;
- struct sk_buff_head *list;
- struct sk_buff *skb, *next;
- struct sk_buff *head;
+ struct sk_buff *skb;
unsigned int cpu;
for_each_possible_cpu(cpu) {
pp = &per_cpu(fcoe_percpu, cpu);
- spin_lock_bh(&pp->fcoe_rx_list.lock);
- list = &pp->fcoe_rx_list;
- head = list->next;
- for (skb = head; skb != (struct sk_buff *)list;
- skb = next) {
- next = skb->next;
- fr = fcoe_dev_from_skb(skb);
- if (fr->fr_dev == lport) {
- __skb_unlink(skb, list);
- kfree_skb(skb);
- }
- }
- if (!pp->thread || !cpu_online(cpu)) {
- spin_unlock_bh(&pp->fcoe_rx_list.lock);
+ if (!pp->thread || !cpu_online(cpu))
continue;
- }
skb = dev_alloc_skb(0);
- if (!skb) {
- spin_unlock_bh(&pp->fcoe_rx_list.lock);
+ if (!skb)
continue;
- }
+
skb->destructor = fcoe_percpu_flush_done;
+ spin_lock_bh(&pp->fcoe_rx_list.lock);
__skb_queue_tail(&pp->fcoe_rx_list, skb);
if (pp->fcoe_rx_list.qlen == 1)
wake_up_process(pp->thread);
@@ -2309,33 +2488,25 @@ void fcoe_percpu_clean(struct fc_lport *lport)
}
/**
- * fcoe_clean_pending_queue() - Dequeue a skb and free it
- * @lport: The local port to dequeue a skb on
- */
-void fcoe_clean_pending_queue(struct fc_lport *lport)
-{
- struct fcoe_port *port = lport_priv(lport);
- struct sk_buff *skb;
-
- spin_lock_bh(&port->fcoe_pending_queue.lock);
- while ((skb = __skb_dequeue(&port->fcoe_pending_queue)) != NULL) {
- spin_unlock_bh(&port->fcoe_pending_queue.lock);
- kfree_skb(skb);
- spin_lock_bh(&port->fcoe_pending_queue.lock);
- }
- spin_unlock_bh(&port->fcoe_pending_queue.lock);
-}
-
-/**
* fcoe_reset() - Reset a local port
* @shost: The SCSI host associated with the local port to be reset
*
* Returns: Always 0 (return value required by FC transport template)
*/
-int fcoe_reset(struct Scsi_Host *shost)
+static int fcoe_reset(struct Scsi_Host *shost)
{
struct fc_lport *lport = shost_priv(shost);
- fc_lport_reset(lport);
+ struct fcoe_port *port = lport_priv(lport);
+ struct fcoe_interface *fcoe = port->priv;
+ struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
+ struct fcoe_ctlr_device *cdev = fcoe_ctlr_to_ctlr_dev(ctlr);
+
+ fcoe_ctlr_link_down(ctlr);
+ fcoe_clean_pending_queue(ctlr->lp);
+
+ if (cdev->enabled != FCOE_CTLR_DISABLED &&
+ !fcoe_link_ok(ctlr->lp))
+ fcoe_ctlr_link_up(ctlr);
return 0;
}
@@ -2370,10 +2541,12 @@ fcoe_hostlist_lookup_port(const struct net_device *netdev)
*/
static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
{
+ struct fcoe_ctlr *ctlr;
struct fcoe_interface *fcoe;
fcoe = fcoe_hostlist_lookup_port(netdev);
- return (fcoe) ? fcoe->ctlr.lp : NULL;
+ ctlr = fcoe_to_ctlr(fcoe);
+ return (fcoe) ? ctlr->lp : NULL;
}
/**
@@ -2393,13 +2566,44 @@ static int fcoe_hostlist_add(const struct fc_lport *lport)
fcoe = fcoe_hostlist_lookup_port(fcoe_netdev(lport));
if (!fcoe) {
port = lport_priv(lport);
- fcoe = port->fcoe;
+ fcoe = port->priv;
list_add_tail(&fcoe->list, &fcoe_hostlist);
}
return 0;
}
/**
+ * fcoe_hostlist_del() - Remove the FCoE interface identified by a local
+ * port to the hostlist
+ * @lport: The local port that identifies the FCoE interface to be added
+ *
+ * Locking: must be called with the RTNL mutex held
+ *
+ */
+static void fcoe_hostlist_del(const struct fc_lport *lport)
+{
+ struct fcoe_interface *fcoe;
+ struct fcoe_port *port;
+
+ port = lport_priv(lport);
+ fcoe = port->priv;
+ list_del(&fcoe->list);
+ return;
+}
+
+static struct fcoe_transport fcoe_sw_transport = {
+ .name = {FCOE_TRANSPORT_DEFAULT},
+ .attached = false,
+ .list = LIST_HEAD_INIT(fcoe_sw_transport.list),
+ .match = fcoe_match,
+ .alloc = fcoe_ctlr_alloc,
+ .create = fcoe_create,
+ .destroy = fcoe_destroy,
+ .enable = fcoe_enable,
+ .disable = fcoe_disable,
+};
+
+/**
* fcoe_init() - Initialize fcoe.ko
*
* Returns: 0 on success, or a negative value on failure
@@ -2410,6 +2614,18 @@ static int __init fcoe_init(void)
unsigned int cpu;
int rc = 0;
+ fcoe_wq = alloc_workqueue("fcoe", 0, 0);
+ if (!fcoe_wq)
+ return -ENOMEM;
+
+ /* register as a fcoe transport */
+ rc = fcoe_transport_attach(&fcoe_sw_transport);
+ if (rc) {
+ printk(KERN_ERR "failed to register an fcoe transport, check "
+ "if libfcoe is loaded\n");
+ return rc;
+ }
+
mutex_lock(&fcoe_config_mutex);
for_each_possible_cpu(cpu) {
@@ -2417,14 +2633,18 @@ static int __init fcoe_init(void)
skb_queue_head_init(&p->fcoe_rx_list);
}
+ cpu_notifier_register_begin();
+
for_each_online_cpu(cpu)
fcoe_percpu_thread_create(cpu);
/* Initialize per CPU interrupt thread */
- rc = register_hotcpu_notifier(&fcoe_cpu_notifier);
+ rc = __register_hotcpu_notifier(&fcoe_cpu_notifier);
if (rc)
goto out_free;
+ cpu_notifier_register_done();
+
/* Setup link change notification */
fcoe_dev_setup();
@@ -2439,7 +2659,11 @@ out_free:
for_each_online_cpu(cpu) {
fcoe_percpu_thread_destroy(cpu);
}
+
+ cpu_notifier_register_done();
+
mutex_unlock(&fcoe_config_mutex);
+ destroy_workqueue(fcoe_wq);
return rc;
}
module_init(fcoe_init);
@@ -2452,6 +2676,7 @@ module_init(fcoe_init);
static void __exit fcoe_exit(void)
{
struct fcoe_interface *fcoe, *tmp;
+ struct fcoe_ctlr *ctlr;
struct fcoe_port *port;
unsigned int cpu;
@@ -2462,30 +2687,39 @@ static void __exit fcoe_exit(void)
/* releases the associated fcoe hosts */
rtnl_lock();
list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list) {
- list_del(&fcoe->list);
- port = lport_priv(fcoe->ctlr.lp);
- fcoe_interface_cleanup(fcoe);
- schedule_work(&port->destroy_work);
+ ctlr = fcoe_to_ctlr(fcoe);
+ port = lport_priv(ctlr->lp);
+ fcoe_hostlist_del(port->lport);
+ queue_work(fcoe_wq, &port->destroy_work);
}
rtnl_unlock();
- unregister_hotcpu_notifier(&fcoe_cpu_notifier);
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu)
fcoe_percpu_thread_destroy(cpu);
+ __unregister_hotcpu_notifier(&fcoe_cpu_notifier);
+
+ cpu_notifier_register_done();
+
mutex_unlock(&fcoe_config_mutex);
- /* flush any asyncronous interface destroys,
- * this should happen after the netdev notifier is unregistered */
- flush_scheduled_work();
- /* That will flush out all the N_Ports on the hostlist, but now we
- * may have NPIV VN_Ports scheduled for destruction */
- flush_scheduled_work();
+ /*
+ * destroy_work's may be chained but destroy_workqueue()
+ * can take care of them. Just kill the fcoe_wq.
+ */
+ destroy_workqueue(fcoe_wq);
- /* detach from scsi transport
- * must happen after all destroys are done, therefor after the flush */
+ /*
+ * Detaching from the scsi transport must happen after all
+ * destroys are done on the fcoe_wq. destroy_workqueue will
+ * enusre the fcoe_wq is flushed.
+ */
fcoe_if_exit();
+
+ /* detach from fcoe transport */
+ fcoe_transport_detach(&fcoe_sw_transport);
}
module_exit(fcoe_exit);
@@ -2509,14 +2743,11 @@ static void fcoe_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
goto done;
mac = fr_cb(fp)->granted_mac;
- if (is_zero_ether_addr(mac)) {
- /* pre-FIP */
- if (fcoe_ctlr_recv_flogi(fip, lport, fp)) {
- fc_frame_free(fp);
- return;
- }
- }
- fcoe_update_src_mac(lport, mac);
+ /* pre-FIP */
+ if (is_zero_ether_addr(mac))
+ fcoe_ctlr_recv_flogi(fip, lport, fp);
+ if (!is_zero_ether_addr(mac))
+ fcoe_update_src_mac(lport, mac);
done:
fc_lport_flogi_resp(seq, fp, lport);
}
@@ -2557,8 +2788,8 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did,
void *arg, u32 timeout)
{
struct fcoe_port *port = lport_priv(lport);
- struct fcoe_interface *fcoe = port->fcoe;
- struct fcoe_ctlr *fip = &fcoe->ctlr;
+ struct fcoe_interface *fcoe = port->priv;
+ struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);
struct fc_frame_header *fh = fc_frame_header_get(fp);
switch (op) {
@@ -2590,12 +2821,25 @@ static int fcoe_vport_create(struct fc_vport *vport, bool disabled)
struct Scsi_Host *shost = vport_to_shost(vport);
struct fc_lport *n_port = shost_priv(shost);
struct fcoe_port *port = lport_priv(n_port);
- struct fcoe_interface *fcoe = port->fcoe;
+ struct fcoe_interface *fcoe = port->priv;
struct net_device *netdev = fcoe->netdev;
struct fc_lport *vn_port;
+ int rc;
+ char buf[32];
+
+ rc = fcoe_validate_vport_create(vport);
+ if (rc) {
+ fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf));
+ printk(KERN_ERR "fcoe: Failed to create vport, "
+ "WWPN (0x%s) already exists\n",
+ buf);
+ return rc;
+ }
mutex_lock(&fcoe_config_mutex);
+ rtnl_lock();
vn_port = fcoe_if_create(fcoe, &vport->dev, 1);
+ rtnl_unlock();
mutex_unlock(&fcoe_config_mutex);
if (IS_ERR(vn_port)) {
@@ -2625,12 +2869,15 @@ static int fcoe_vport_destroy(struct fc_vport *vport)
struct Scsi_Host *shost = vport_to_shost(vport);
struct fc_lport *n_port = shost_priv(shost);
struct fc_lport *vn_port = vport->dd_data;
- struct fcoe_port *port = lport_priv(vn_port);
mutex_lock(&n_port->lp_mutex);
list_del(&vn_port->list);
mutex_unlock(&n_port->lp_mutex);
- schedule_work(&port->destroy_work);
+
+ mutex_lock(&fcoe_config_mutex);
+ fcoe_if_destroy(vn_port);
+ mutex_unlock(&fcoe_config_mutex);
+
return 0;
}
@@ -2686,36 +2933,14 @@ static void fcoe_set_vport_symbolic_name(struct fc_vport *vport)
NULL, NULL, 3 * lport->r_a_tov);
}
-/**
- * fcoe_get_lesb() - Fill the FCoE Link Error Status Block
- * @lport: the local port
- * @fc_lesb: the link error status block
- */
-static void fcoe_get_lesb(struct fc_lport *lport,
- struct fc_els_lesb *fc_lesb)
+static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *fcf_dev)
{
- unsigned int cpu;
- u32 lfc, vlfc, mdac;
- struct fcoe_dev_stats *devst;
- struct fcoe_fc_els_lesb *lesb;
- struct rtnl_link_stats64 temp;
- struct net_device *netdev = fcoe_netdev(lport);
+ struct fcoe_ctlr_device *ctlr_dev =
+ fcoe_fcf_dev_to_ctlr_dev(fcf_dev);
+ struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+ struct fcoe_interface *fcoe = fcoe_ctlr_priv(ctlr);
- lfc = 0;
- vlfc = 0;
- mdac = 0;
- lesb = (struct fcoe_fc_els_lesb *)fc_lesb;
- memset(lesb, 0, sizeof(*lesb));
- for_each_possible_cpu(cpu) {
- devst = per_cpu_ptr(lport->dev_stats, cpu);
- lfc += devst->LinkFailureCount;
- vlfc += devst->VLinkFailureCount;
- mdac += devst->MissDiscAdvCount;
- }
- lesb->lesb_link_fail = htonl(lfc);
- lesb->lesb_vlink_fail = htonl(vlfc);
- lesb->lesb_miss_fka = htonl(mdac);
- lesb->lesb_fcs_error = htonl(dev_get_stats(netdev, &temp)->rx_crc_errors);
+ fcf_dev->vlan_id = vlan_dev_vlan_id(fcoe->netdev);
}
/**
@@ -2734,8 +2959,9 @@ static void fcoe_set_port_id(struct fc_lport *lport,
u32 port_id, struct fc_frame *fp)
{
struct fcoe_port *port = lport_priv(lport);
- struct fcoe_interface *fcoe = port->fcoe;
+ struct fcoe_interface *fcoe = port->priv;
+ struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
if (fp && fc_frame_payload_op(fp) == ELS_FLOGI)
- fcoe_ctlr_recv_flogi(&fcoe->ctlr, lport, fp);
+ fcoe_ctlr_recv_flogi(ctlr, lport, fp);
}