|
@@ -31,6 +31,8 @@
|
|
|
#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>
|
|
@@ -101,6 +103,8 @@ 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);
|
|
@@ -129,6 +133,11 @@ static struct notifier_block fcoe_cpu_notifier = {
|
|
|
.notifier_call = fcoe_cpu_callback,
|
|
|
};
|
|
|
|
|
|
+/* 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;
|
|
|
|
|
@@ -1522,6 +1531,8 @@ 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->priority = port->priority;
|
|
|
+
|
|
|
if (fcoe->netdev->priv_flags & IFF_802_1Q_VLAN &&
|
|
|
fcoe->realdev->features & NETIF_F_HW_VLAN_TX) {
|
|
|
skb->vlan_tci = VLAN_TAG_PRESENT |
|
|
@@ -1747,6 +1758,7 @@ int fcoe_percpu_receive_thread(void *arg)
|
|
|
*/
|
|
|
static void fcoe_dev_setup(void)
|
|
|
{
|
|
|
+ register_dcbevent_notifier(&dcb_notifier);
|
|
|
register_netdevice_notifier(&fcoe_notifier);
|
|
|
}
|
|
|
|
|
@@ -1755,9 +1767,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_interface *fcoe;
|
|
|
+ struct net_device *netdev;
|
|
|
+ struct fcoe_port *port;
|
|
|
+ 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;
|
|
|
+
|
|
|
+ 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)
|
|
|
+ fcoe->ctlr.priority = prio;
|
|
|
+
|
|
|
+ if (entry->app.protocol == ETH_P_FCOE) {
|
|
|
+ port = lport_priv(fcoe->ctlr.lp);
|
|
|
+ port->priority = prio;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NOTIFY_OK;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* fcoe_device_notification() - Handler for net device events
|
|
|
* @notifier: The context of the notification
|
|
@@ -1965,6 +2037,46 @@ static bool fcoe_match(struct net_device *netdev)
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * 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_port *port = lport_priv(fcoe->ctlr.lp);
|
|
|
+ struct dcb_app app = {
|
|
|
+ .priority = 0,
|
|
|
+ .protocol = ETH_P_FCOE
|
|
|
+ };
|
|
|
+
|
|
|
+ /* setup DCB priority attributes. */
|
|
|
+ if (netdev && netdev->dcbnl_ops && netdev->dcbnl_ops->getdcbx) {
|
|
|
+ dcbx = netdev->dcbnl_ops->getdcbx(netdev);
|
|
|
+
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ port->priority = ffs(up) ? ffs(up) - 1 : 0;
|
|
|
+ fcoe->ctlr.priority = ffs(fup) ? ffs(fup) - 1 : port->priority;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* fcoe_create() - Create a fcoe interface
|
|
|
* @netdev : The net_device object the Ethernet interface to create on
|
|
@@ -2008,6 +2120,9 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
|
|
|
/* Make this the "master" N_Port */
|
|
|
fcoe->ctlr.lp = lport;
|
|
|
|
|
|
+ /* setup DCB priority attributes. */
|
|
|
+ fcoe_dcb_create(fcoe);
|
|
|
+
|
|
|
/* add to lports list */
|
|
|
fcoe_hostlist_add(lport);
|
|
|
|