Don't use netdev for FCoE if pause is not correctly set, even for VLANs.

This modifies the previous tests to require that the underlying net-dev
supports the ethtool get_pauseparam operation.

NICs that are 10/100/1000 must have pause autonegotiation turned on.
All NICs must have RX and TX pause on.  If not, they are treated as if
there is no link.

Signed-off-by: Joe Eykholt <[EMAIL PROTECTED]>

---
 drivers/scsi/ofc/fcoe/fcoe_def.h |    1 
 drivers/scsi/ofc/fcoe/fcoe_if.c  |   90 ++++++++++++++++++++++----------------
 drivers/scsi/ofc/fcoe/fcoeinit.c |   66 ++++++++--------------------
 3 files changed, 71 insertions(+), 86 deletions(-)

diff --git a/drivers/scsi/ofc/fcoe/fcoe_def.h b/drivers/scsi/ofc/fcoe/fcoe_def.h
index 0c71dc7..6550c73 100644
--- a/drivers/scsi/ofc/fcoe/fcoe_def.h
+++ b/drivers/scsi/ofc/fcoe/fcoe_def.h
@@ -109,6 +109,7 @@ int         fcoe_create_interface(struct fcoe_info *, void 
*);
 int            fcoe_xmit(struct fcdev *, struct fc_frame *);
 int            fcoe_rcv(struct sk_buff *, struct net_device *,
                         struct packet_type *, struct net_device *);
+int            fcoe_link_ok(struct fcdev *);
 struct fc_frame *fcoe_frame_alloc(size_t);
 void fcoe_put_dev(struct fcdev *dev);
 struct fcoe_softc *fcoe_find_fcdev(char *);
diff --git a/drivers/scsi/ofc/fcoe/fcoe_if.c b/drivers/scsi/ofc/fcoe/fcoe_if.c
index d87ede1..fd3c7a7 100644
--- a/drivers/scsi/ofc/fcoe/fcoe_if.c
+++ b/drivers/scsi/ofc/fcoe/fcoe_if.c
@@ -160,6 +160,48 @@ int fcoe_destroy_interface(struct fcdev *fd)
 }
 
 /*
+ * Return non-zero if link is OK for use by FCoE.
+ * Any permanently-disqualifying conditions have been previously checked.
+ * This checks pause settings, which can change with link.
+ * This also updates the speed setting, which may change with link for 
100/1000.
+ */
+int fcoe_link_ok(struct fcdev *fdev)
+{
+       struct fcoe_softc *fc = (struct fcoe_softc *)fdev->drv_priv;
+       struct net_device *dev = fc->real_dev;
+       struct ethtool_pauseparam pause = { ETHTOOL_GPAUSEPARAM };
+       struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+       int rc = 0;
+
+       if ((dev->flags & IFF_UP) && netif_carrier_ok(dev)) {
+               dev->ethtool_ops->get_pauseparam(dev, &pause);
+               if (dev->ethtool_ops->get_settings) {
+                       dev->ethtool_ops->get_settings(dev, &ecmd);
+                       fdev->fd_speed_support &=
+                           ~(OFC_SPEED_1GBIT | OFC_SPEED_10GBIT);
+                       if (ecmd.supported & (SUPPORTED_1000baseT_Half |
+                                             SUPPORTED_1000baseT_Full))
+                               fdev->fd_speed_support |= OFC_SPEED_1GBIT;
+                       if (ecmd.supported & SUPPORTED_10000baseT_Full)
+                               fdev->fd_speed_support |= OFC_SPEED_10GBIT;
+                       if (ecmd.speed == SPEED_1000)
+                               fdev->fd_speed = OFC_SPEED_1GBIT;
+                       if (ecmd.speed == SPEED_10000)
+                               fdev->fd_speed = OFC_SPEED_10GBIT;
+
+                       /*
+                        * for 10 G (and faster), ignore autoneg requirement.
+                        */
+                       if (ecmd.speed >= SPEED_10000)
+                               pause.autoneg = 1;
+               }
+               if (pause.autoneg && pause.tx_pause && pause.rx_pause)
+                       rc = 1;
+       }
+       return rc;
+}
+
+/*
  * This function creates the fcoe interface
  * create struct fcdev which is a shared structure between opefc
  * and transport level protocol.
@@ -171,8 +213,6 @@ int fcoe_create_interface(struct fcoe_info *fci, void *ptr)
        struct fcoe_cfg *cfg = ptr;
        struct fcoe_softc *fc;
        struct fcoe_dev_stats *p;
-       struct ethtool_pauseparam pauseparam = { ETHTOOL_GPAUSEPARAM };
-       struct ethtool_cmd ecmd = { ETHTOOL_GSET };
        int rc = 0;
        int i;
 #ifdef HAVE_SET_RX_MODE
@@ -207,12 +247,6 @@ int fcoe_create_interface(struct fcoe_info *fci, void *ptr)
                goto out;
        }
 
-       fdev->fd_link_status = TRANS_LINK_DOWN;
-       if ((fc->real_dev->flags & IFF_UP) != 0 &&
-           netif_carrier_ok(fc->real_dev)) {
-               fdev->fd_link_status = TRANS_LINK_UP;
-       }
-
        /*
         * Do not support for bonding device
         */
@@ -224,38 +258,16 @@ int fcoe_create_interface(struct fcoe_info *fci, void 
*ptr)
        }
 
        /*
-        * if it is not a vlan driver then do more check */
-       if (!(fc->real_dev->priv_flags & IFF_802_1Q_VLAN)) {
-
-               if (!fc->real_dev->ethtool_ops) {
-                       rc = -EOPNOTSUPP;
-                       goto out;
-               }
-               if (!fc->real_dev->ethtool_ops->get_pauseparam) {
-                       rc = -EOPNOTSUPP;
-                       goto out;
-               }
-               fc->real_dev->ethtool_ops->get_pauseparam(fc->real_dev,
-                                                         &pauseparam);
-               if (!pauseparam.rx_pause || !pauseparam.tx_pause) {
-                       rc = -EOPNOTSUPP;
-                       goto out;
-               }
-               if (fc->real_dev->ethtool_ops->get_settings) {
-                       fc->real_dev->ethtool_ops->get_settings(fc->real_dev,
-                                                               &ecmd);
-                       if (ecmd.
-                           supported & (SUPPORTED_1000baseT_Half |
-                                        SUPPORTED_1000baseT_Full))
-                               fdev->fd_speed_support |= OFC_SPEED_1GBIT;
-                       if (ecmd.supported & SUPPORTED_10000baseT_Full)
-                               fdev->fd_speed_support |= OFC_SPEED_10GBIT;
-                       if (ecmd.speed == SPEED_1000)
-                               fdev->fd_speed = OFC_SPEED_1GBIT;
-                       if (ecmd.speed == SPEED_10000)
-                               fdev->fd_speed = OFC_SPEED_10GBIT;
-               }
+        * Require support for get_pauseparam ethtool op.
+        */
+       if (!fc->real_dev->ethtool_ops ||
+           !fc->real_dev->ethtool_ops->get_pauseparam) {
+               rc = -EOPNOTSUPP;
+               goto out;
        }
+       fdev->fd_link_status = TRANS_LINK_DOWN;
+       if (fcoe_link_ok(fdev))
+               fdev->fd_link_status = TRANS_LINK_UP;
 
        if (fc->real_dev->features & NETIF_F_SG)
                fdev->capabilities = TRANS_C_SG;
diff --git a/drivers/scsi/ofc/fcoe/fcoeinit.c b/drivers/scsi/ofc/fcoe/fcoeinit.c
index 182a9b1..3b80946 100644
--- a/drivers/scsi/ofc/fcoe/fcoeinit.c
+++ b/drivers/scsi/ofc/fcoe/fcoeinit.c
@@ -170,7 +170,6 @@ static void fcoe_dev_cleanup(void)
  * This function is called by the ethernet driver
  * this is called in case of link change event
  */
-
 static int fcoe_device_notification(struct notifier_block *notifier,
                                    ulong event, void *ptr)
 {
@@ -179,7 +178,7 @@ static int fcoe_device_notification(struct notifier_block 
*notifier,
        struct fcdev *fc_dev = NULL;
        struct fcoe_dev_stats *stats;
        struct fcoe_info *fci = &fcoei;
-       struct ethtool_pauseparam pauseparam = { ETHTOOL_GPAUSEPARAM };
+       int new_status;
        int rc;
 
        read_lock(&fci->fcoe_hostlist_lock);
@@ -190,67 +189,40 @@ static int fcoe_device_notification(struct notifier_block 
*notifier,
                }
        }
        read_unlock(&fci->fcoe_hostlist_lock);
-
        if (fc_dev == NULL) {
                rc = NOTIFY_DONE;
                goto out;
        }
+       new_status = fc_dev->fd_link_status;
        switch (event) {
        case NETDEV_DOWN:
-               fc_dev->fd_link_status = TRANS_LINK_DOWN;
-               /* notify upper layer about link down */
-               openfc_linkdown(fc_dev);
-               stats = fc_dev->dev_stats[smp_processor_id()];
-               stats->LinkFailureCount++;
-               fcoe_clean_pending_queue(fc_dev);
-               rc = NOTIFY_OK;
+       case NETDEV_GOING_DOWN:
+               new_status = TRANS_LINK_DOWN;
                break;
        case NETDEV_UP:
-               fc_dev->fd_link_status = TRANS_LINK_UP;
-               /* notify upper layer about link up */
-               if (netif_carrier_ok(real_dev)) {
-                       if ((real_dev->priv_flags & IFF_802_1Q_VLAN) == 0) {
-                               real_dev->ethtool_ops->get_pauseparam(fc->
-                                                                     real_dev,
-                                                                     
&pauseparam);
-                               if (!pauseparam.rx_pause
-                                   || !pauseparam.tx_pause) {
-                                       rc = NOTIFY_OK;
-                                       goto out;
-                               }
-                       }
-                       openfc_linkup(fc_dev);
-               }
-               rc = NOTIFY_OK;
-               break;
        case NETDEV_CHANGE:
-               if (netif_carrier_ok(real_dev)) {
-                       if ((real_dev->priv_flags & IFF_802_1Q_VLAN) == 0) {
-                               real_dev->ethtool_ops->get_pauseparam(fc->
-                                                                     real_dev,
-                                                                     
&pauseparam);
-                               if (!pauseparam.rx_pause
-                                   || !pauseparam.tx_pause) {
-                                       rc = NOTIFY_OK;
-                                       goto out;
-                               }
-                       }
-                       if (fc->real_dev->flags & IFF_UP)
-                               openfc_linkup(fc_dev);
-               } else {
-                       openfc_linkdown(fc_dev);
-                       fcoe_clean_pending_queue(fc_dev);
-               }
-               rc = NOTIFY_OK;
+               new_status = TRANS_LINK_DOWN;
+               if (fcoe_link_ok(fc_dev))
+                       new_status = TRANS_LINK_UP;
                break;
        case NETDEV_REGISTER:
-       case NETDEV_GOING_DOWN:
                rc = NOTIFY_OK;
                break;
        default:
-               SA_LOG("unknow event %d call", event);
+               SA_LOG("unknown event %d call", event);
                rc = NOTIFY_OK;
        }
+       if (fc_dev->fd_link_status != new_status) {
+               fc_dev->fd_link_status = new_status;
+               if (new_status == TRANS_LINK_UP) {
+                       openfc_linkup(fc_dev);
+               } else {
+                       stats = fc_dev->dev_stats[smp_processor_id()];
+                       stats->LinkFailureCount++;
+                       openfc_linkdown(fc_dev);
+                       fcoe_clean_pending_queue(fc_dev);
+               }
+       }
 out:
        return rc;
 }


-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to