> -----Original Message-----
> From: Jeff Daly <je...@silicom-usa.com>
> Sent: Monday, February 28, 2022 10:30 AM
> To: dev@dpdk.org
> Cc: sta...@dpdk.org; Stephen Douthit <steph...@silicom-usa.com>; Haiyue
> Wang <haiyue.w...@intel.com>
> Subject: [PATCH v4 3/3] net/ixgbe: Fix SFP detection and linking on hotplug
> 
> Caution: This is an external email. Please take care when clicking links or
> opening attachments.
> 
> 
> Currently the ixgbe driver does not ID any SFP except for the first one 
> plugged in.
> This can lead to no-link, or incorrect speed conditions.
> 
> For example:
> 
> * If link is initially established with a 1G SFP, and later a 1G/10G 
> multispeed part
> is later installed, then the MAC link setup functions are never called to 
> change
> from 1000BASE-X to 10GBASE-R mode, and the link stays running at the slower
> rate.
> 
> * If link is initially established with a 1G SFP, and later a 10G only module 
> is later
> installed, no link is established, since we are still trasnsmitting in 
> 1000BASE-X
> mode to a 10GBASE-R only partner.
> 
> Refactor the SFP ID/setup, and link setup code, to more closely match the flow
> of the mainline kernel driver which does not have these issues.  In that 
> driver a
> service task runs periodically to handle these operations based on bit flags 
> that
> have been set (usually via interrupt or userspace request), and then get 
> cleared
> once the requested subtask has been completed.
> 
> Fixes: af75078fece ("first public release")
> Cc: sta...@dpdk.org
> 
> Signed-off-by: Stephen Douthit <steph...@silicom-usa.com>
> Signed-off-by: Jeff Daly <je...@silicom-usa.com>
> ---
>  drivers/net/ixgbe/ixgbe_ethdev.c | 499 +++++++++++++++++++++++--------
> drivers/net/ixgbe/ixgbe_ethdev.h |  14 +-
>  2 files changed, 392 insertions(+), 121 deletions(-)
> 
> diff --git a/drivers/net/ixgbe/ixgbe_ethdev.c 
> b/drivers/net/ixgbe/ixgbe_ethdev.c
> index e8f07cb405..b70985bb1d 100644
> --- a/drivers/net/ixgbe/ixgbe_ethdev.c
> +++ b/drivers/net/ixgbe/ixgbe_ethdev.c
> @@ -229,9 +229,6 @@ static int ixgbe_dev_interrupt_get_status(struct
> rte_eth_dev *dev);  static int ixgbe_dev_interrupt_action(struct rte_eth_dev
> *dev);  static void ixgbe_dev_interrupt_handler(void *param);  static void
> ixgbe_dev_interrupt_delayed_handler(void *param); -static void
> *ixgbe_dev_setup_link_thread_handler(void *param); -static int
> ixgbe_dev_wait_setup_link_complete(struct rte_eth_dev *dev,
> -                                             uint32_t timeout_ms);
> 
>  static int ixgbe_add_rar(struct rte_eth_dev *dev,
>                         struct rte_ether_addr *mac_addr, @@ -766,6 +763,33 @@ 
> static
> const struct rte_ixgbe_xstats_name_off rte_ixgbevf_stats_strings[] = {  
> #define
> IXGBEVF_NB_XSTATS (sizeof(rte_ixgbevf_stats_strings) / \
>                 sizeof(rte_ixgbevf_stats_strings[0]))
> 
> +/**
> + * This function is the same as ixgbe_need_crosstalk_fix() in
> +base/ixgbe_common.c
> + *
> + * ixgbe_need_crosstalk_fix - Determine if we need to do cross talk fix
> + * @hw: pointer to hardware structure
> + *
> + * Contains the logic to identify if we need to verify link for the
> + * crosstalk fix
> + **/
> +static bool ixgbe_need_crosstalk_fix(struct ixgbe_hw *hw) {
> +       /* Does FW say we need the fix */
> +       if (!hw->need_crosstalk_fix)
> +               return false;
> +
> +       /* Only consider SFP+ PHYs i.e. media type fiber */
> +       switch (ixgbe_get_media_type(hw)) {
> +       case ixgbe_media_type_fiber:
> +       case ixgbe_media_type_fiber_qsfp:
> +               break;
> +       default:
> +               return false;
> +       }
> +
> +       return true;
> +}
> +
>  /*
>   * This function is the same as ixgbe_is_sfp() in base/ixgbe.h.
>   */
> @@ -1032,6 +1056,306 @@ ixgbe_swfw_lock_reset(struct ixgbe_hw *hw)
>         ixgbe_release_swfw_semaphore(hw, mask);  }
> 
> +/**
> + * ixgbe_check_sfp_cage - Find present status of SFP module
> + * @hw: pointer to hardware structure
> + *
> + * Find if a SFP module is present and if this device supports SFPs
> +**/ enum ixgbe_sfp_cage_status ixgbe_check_sfp_cage(struct ixgbe_hw
> +*hw) {
> +       enum ixgbe_sfp_cage_status sfp_cage_status;
> +
> +       /* If we're not a fiber/fiber_qsfp, no cage to check */
> +       switch (hw->mac.ops.get_media_type(hw)) {
> +       case ixgbe_media_type_fiber:
> +       case ixgbe_media_type_fiber_qsfp:
> +               break;
> +       default:
> +               return IXGBE_SFP_CAGE_NOCAGE;
> +       }
> +
> +       switch (hw->mac.type) {
> +       case ixgbe_mac_82599EB:
> +               sfp_cage_status = !!(IXGBE_READ_REG(hw, IXGBE_ESDP) &
> +                            IXGBE_ESDP_SDP2);
> +               break;
> +       case ixgbe_mac_X550EM_x:
> +       case ixgbe_mac_X550EM_a:
> +               /* SDP0 is the active low signal PRSNT#, so invert this */
> +               sfp_cage_status = !(IXGBE_READ_REG(hw, IXGBE_ESDP) &
> +                                 IXGBE_ESDP_SDP0);
> +               break;
> +       default:
> +               /* Don't know how to check this device type yet */
> +               sfp_cage_status = IXGBE_SFP_CAGE_UNKNOWN;
> +               DEBUGOUT("IXGBE_SFP_CAGE_UNKNOWN, unknown mac type
> %d\n",
> +                        hw->mac.type);
> +               break;
> +       }
> +
> +       DEBUGOUT("sfp status %d for mac type %d\n", sfp_cage_status, hw-
> >mac.type);
> +       return sfp_cage_status;
> +}
> +
> +static s32
> +ixgbe_sfp_id_and_setup(struct rte_eth_dev *dev) {
> +       struct ixgbe_hw *hw =
> +               IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> +       enum ixgbe_sfp_cage_status sfp_cage_status;
> +       s32 err;
> +
> +       /* Can't ID or setup SFP if it's not plugged in */
> +       sfp_cage_status = ixgbe_check_sfp_cage(hw);
> +       if (sfp_cage_status == IXGBE_SFP_CAGE_EMPTY ||
> +           sfp_cage_status == IXGBE_SFP_CAGE_NOCAGE)
> +               return IXGBE_ERR_SFP_NOT_PRESENT;
> +
> +       /* Something's in the cage, ID it */
> +       hw->phy.ops.identify_sfp(hw);
> +
> +       /* Unknown module type, give up */
> +       if (hw->phy.sfp_type == ixgbe_sfp_type_unknown) {
> +               PMD_DRV_LOG(ERR, "unknown SFP type, giving up");
> +               return IXGBE_ERR_SFP_NOT_SUPPORTED;
> +       }
> +
> +       /* This should be a redundant check, since we looked at the
> +        * PRSNT# signal from the cage above, but just in case this is
> +        * an SFP that's slow to respond to I2C pokes correctly, try it
> +        * again later
> +        */
> +       if (hw->phy.sfp_type == ixgbe_sfp_type_not_present) {
> +               PMD_DRV_LOG(ERR, "IDed SFP as absent but cage PRSNT# 
> active!?");
> +               return IXGBE_ERR_SFP_NOT_PRESENT;
> +       }
> +
> +       /* SFP is present and identified, try to set it up */
> +       err = hw->mac.ops.setup_sfp(hw);
> +       if (err)
> +               PMD_DRV_LOG(ERR, "setup_sfp() failed %d", err);
> +
> +       return err;
> +}
> +
> +static void
> +ixgbe_sfp_service(struct rte_eth_dev *dev) {
> +       struct ixgbe_hw *hw =
> +               IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> +       struct ixgbe_interrupt *intr =
> +               IXGBE_DEV_PRIVATE_TO_INTR(dev->data->dev_private);
> +       enum ixgbe_sfp_cage_status sfp_cage_status;
> +       s32 err;
> +       u8 sff_id;
> +       bool have_int = false;
> +
> +       /* If there's no module cage, then there's nothing to service */
> +       sfp_cage_status = ixgbe_check_sfp_cage(hw);
> +       if (sfp_cage_status == IXGBE_SFP_CAGE_NOCAGE) {
> +               PMD_DRV_LOG(DEBUG, "No SFP to service\n");
> +               return;
> +       }
> +
> +       /* TODO - Even for platforms where ixgbe_check_sfp_cage() gives a 
> clear
> +        * status result, if there's no interrupts, or no interrupt for the 
> SFP
> +        * cage present pin, even if other interrupts exist, then we still 
> need
> +        * to poll here to set the flag.
> +        */
> +#ifndef RTE_EXEC_ENV_FREEBSD
> +       struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
> +       struct rte_intr_handle *intr_handle = pci_dev->intr_handle;
> +       if (rte_intr_allow_others(intr_handle)) {
> +               /* check if lsc interrupt is enabled */
> +               if (dev->data->dev_conf.intr_conf.lsc)
> +                       have_int = true;
> +       }
> +#endif /* #ifdef RTE_EXEC_ENV_FREEBSD */
> +
> +       if (!have_int && sfp_cage_status == IXGBE_SFP_CAGE_EMPTY) {
> +               intr->flags |= IXGBE_FLAG_NEED_SFP_SETUP;
> +               PMD_DRV_LOG(DEBUG, "No SFP, no LSC, set NEED_SFP_SETUP\n");
> +       }
> +
> +       /* For platforms that don't have a way to read the PRESENT# signal 
> from
> +        * the SFP cage, fallback to doing an I2C read and seeing if it's 
> ACKed
> +        * to determine if a module is present
> +        */
> +       if (sfp_cage_status == IXGBE_SFP_CAGE_UNKNOWN) {
> +               PMD_DRV_LOG(DEBUG,
> +                           "SFP present unknown (int? %d), try I2C read\n",
> +                           have_int);
> +
> +               /* Rather than calling identify_sfp, which will read a lot of 
> I2C
> +                * registers (and in a slow processor intensive fashion due to
> +                * bit-banging, just read the SFF ID register, which is at a
> +                * common address across SFP/SFP+/QSFP modules and see if
> +                * there's a NACK.  This works since we only expect a NACK if 
> no
> +                * module is present
> +                */
> +               err = ixgbe_read_i2c_eeprom(hw, IXGBE_SFF_IDENTIFIER, 
> &sff_id);
> +               if (err != IXGBE_SUCCESS) {
> +                       PMD_DRV_LOG(DEBUG, "Received I2C NAK from SFP, set
> NEED_SFP_SETUP flag\n");
> +                       intr->flags |= IXGBE_FLAG_NEED_SFP_SETUP;
> +                       sfp_cage_status = IXGBE_SFP_CAGE_EMPTY;
> +               } else {
> +                       PMD_DRV_LOG(DEBUG, "SFP ID read ACKed");
> +                       sfp_cage_status = IXGBE_SFP_CAGE_FULL;
> +               }
> +       }
> +
> +       if (sfp_cage_status == IXGBE_SFP_CAGE_EMPTY) {
> +               PMD_DRV_LOG(DEBUG, "SFP absent, cage_status %d\n",
> sfp_cage_status);
> +               return;
> +       }
> +
> +       /* No setup requested?  Nothing to do */
> +       if (!(intr->flags & IXGBE_FLAG_NEED_SFP_SETUP))
> +               return;
> +
> +       err = ixgbe_sfp_id_and_setup(dev);
> +       if (err) {
> +               PMD_DRV_LOG(DEBUG, "failed to ID & setup SFP %d", err);
> +               return;
> +       }
> +
> +       /* Setup is done, clear the flag, but make sure link config runs for 
> new SFP
> */
> +       intr->flags &= ~IXGBE_FLAG_NEED_SFP_SETUP;
> +       intr->flags |= IXGBE_FLAG_NEED_LINK_CONFIG;
> +
> +       /*
> +        * Since this is a new SFP, clear the old advertised speed mask so we 
> don't
> +        * end up using an old slower rate
> +        */
> +       hw->phy.autoneg_advertised = 0;
> +}
> +
> +static void
> +ixgbe_link_service(struct rte_eth_dev *dev) {
> +       struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data-
> >dev_private);
> +       struct ixgbe_interrupt *intr =
> +               IXGBE_DEV_PRIVATE_TO_INTR(dev->data->dev_private);
> +       bool link_up, autoneg = false, have_int = false;
> +       u32 speed;
> +       s32 err;
> +
> +       /* Test if we have a LSC interrupt for this platform, if not we need 
> to
> +        * manually check the link register since IXGBE_FLAG_NEED_LINK_CONFIG
> +        * will never be set in the interrupt handler
> +        */
> +#ifndef RTE_EXEC_ENV_FREEBSD
> +       struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
> +       struct rte_intr_handle *intr_handle = pci_dev->intr_handle;
> +       if (rte_intr_allow_others(intr_handle)) {
> +               /* check if lsc interrupt is enabled */
> +               if (dev->data->dev_conf.intr_conf.lsc)
> +                       have_int = true;
> +       }
> +#endif /* #ifdef RTE_EXEC_ENV_FREEBSD */
> +
> +       /* Skip if we still need to setup an SFP, or if no link config 
> requested
> +        */
> +       if ((intr->flags & IXGBE_FLAG_NEED_SFP_SETUP) ||
> +           (!(intr->flags & IXGBE_FLAG_NEED_LINK_CONFIG) && have_int))
> +               return;
> +
> +       if (!have_int && !(intr->flags & IXGBE_FLAG_NEED_LINK_CONFIG)) {
> +               err = ixgbe_check_link(hw, &speed, &link_up, 0);
> +               if (!err && !link_up) {
> +                       intr->flags |= IXGBE_FLAG_NEED_LINK_CONFIG;
> +                       PMD_DRV_LOG(DEBUG, "Link down, no LSC, set
> NEED_LINK_CONFIG\n");
> +               } else {
> +                       return;
> +               }
> +       }
> +
> +       speed = hw->phy.autoneg_advertised;
> +       if (!speed)
> +               ixgbe_get_link_capabilities(hw, &speed, &autoneg);
> +
> +       err = ixgbe_setup_link(hw, speed, true);
> +       if (err) {
> +               PMD_DRV_LOG(ERR, "ixgbe_setup_link failed %d", err);
> +               return;
> +       }
> +
> +       intr->flags &= ~IXGBE_FLAG_NEED_LINK_CONFIG; }
> +
> +static void
> +ixgbe_link_update_service(struct rte_eth_dev *dev) {
> +       /* Update internal link status, waiting for link */
> +       if (!ixgbe_dev_link_update(dev, 0)) {
> +               ixgbe_dev_link_status_print(dev);
> +               rte_eth_dev_callback_process(dev, RTE_ETH_EVENT_INTR_LSC,
> NULL);
> +       }
> +}
> +
> +/*
> + * Service task thread to handle periodic tasks  */ static void *
> +ixgbe_dev_service_thread_handler(void *param) {
> +       struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
> +       struct ixgbe_hw *hw =
> +               IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> +       uint64_t start, ticks, service_ms;
> +       uint32_t speed;
> +       s32 err;
> +       bool link_up;
> +
> +       while (1) {
> +               ixgbe_sfp_service(dev);
> +               ixgbe_link_service(dev);
> +               ixgbe_link_update_service(dev);
> +
> +               /* Run the service thread handler more frequently when link is
> +                * down to reduce link up latency (every 200ms vs 1s)
> +                *
> +                * Use a number of smaller sleeps to decrease exit latency 
> when
> +                * ixgbe_dev_stop() wants this thread to join
> +                */
> +               err = ixgbe_check_link(hw, &speed, &link_up, 0);
> +               if (err == IXGBE_SUCCESS && link_up)
> +                       service_ms = 2000;
> +               else
> +                       service_ms = 100;
> +
> +               /* Call msec_delay in a loop with several smaller sleeps to
> +                * provide periodic thread cancellation points
> +                */
> +               start = rte_get_timer_cycles();
> +               ticks = (uint64_t)service_ms * rte_get_timer_hz() / 1E3;
> +               while ((rte_get_timer_cycles() - start) < ticks)
> +                       msec_delay(100);
> +       }
> +
> +       /* Never return */
> +       return NULL;
> +}
> +
> +static s32
> +eth_ixgbe_check_mac_link_generic(struct ixgbe_hw *hw, ixgbe_link_speed
> *speed,
> +                                bool *link_up, bool
> +link_up_wait_to_complete) {
> +       if (ixgbe_need_crosstalk_fix(hw)) {
> +               enum ixgbe_sfp_cage_status sfp_cage_status;
> +
> +               sfp_cage_status = ixgbe_check_sfp_cage(hw);
> +               if (sfp_cage_status != IXGBE_SFP_CAGE_FULL) {
> +                       *link_up = false;
> +                       *speed = IXGBE_LINK_SPEED_UNKNOWN;
> +                       return IXGBE_SUCCESS;
> +               }
> +       }
> +
> +       return ixgbe_check_mac_link_generic(hw, speed, link_up,
> +link_up_wait_to_complete); }
> +
>  /*
>   * This function is based on code in ixgbe_attach() in base/ixgbe.c.
>   * It returns 0 on success.
> @@ -1054,6 +1378,7 @@ eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev, void
> *init_params __rte_unused)
>                 IXGBE_DEV_PRIVATE_TO_FILTER_INFO(eth_dev->data->dev_private);
>         struct ixgbe_bw_conf *bw_conf =
>                 IXGBE_DEV_PRIVATE_TO_BW_CONF(eth_dev->data->dev_private);
> +       struct ixgbe_mac_info *mac = &hw->mac;
>         uint32_t ctrl_ext;
>         uint16_t csum;
>         int diag, i, ret;
> @@ -1124,6 +1449,17 @@ eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev,
> void *init_params __rte_unused)
>                 return -EIO;
>         }
> 
> +       /* override mac_link_check to check for sfp cage full/empty */
> +       switch (hw->mac.type) {
> +       case ixgbe_mac_X550EM_x:
> +       case ixgbe_mac_X550EM_a:
> +       case ixgbe_mac_82599EB:
> +               mac->ops.check_link = eth_ixgbe_check_mac_link_generic;
> +               break;
> +       default:
> +               break;
> +       }
> +
>         /* pick up the PCI bus settings for reporting later */
>         ixgbe_get_bus_info(hw);
> 
> @@ -2558,8 +2894,11 @@ ixgbe_flow_ctrl_enable(struct rte_eth_dev *dev,
> struct ixgbe_hw *hw)  static int  ixgbe_dev_start(struct rte_eth_dev *dev)  {
> +       struct ixgbe_adapter *ad = dev->data->dev_private;
>         struct ixgbe_hw *hw =
>                 IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> +       struct ixgbe_interrupt *intr =
> +               IXGBE_DEV_PRIVATE_TO_INTR(dev->data->dev_private);
>         struct ixgbe_vf_info *vfinfo =
>                 *IXGBE_DEV_PRIVATE_TO_P_VFDATA(dev->data->dev_private);
>         struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev); @@ -2580,9
> +2919,6 @@ ixgbe_dev_start(struct rte_eth_dev *dev)
> 
>         PMD_INIT_FUNC_TRACE();
> 
> -       /* Stop the link setup handler before resetting the HW. */
> -       ixgbe_dev_wait_setup_link_complete(dev, 0);
> -
>         /* disable uio/vfio intr/eventfd mapping */
>         rte_intr_disable(intr_handle);
> 
> @@ -2815,6 +3151,20 @@ ixgbe_dev_start(struct rte_eth_dev *dev)
>         ixgbe_l2_tunnel_conf(dev);
>         ixgbe_filter_restore(dev);
> 
> +       /* Spawn service thread */
> +       if (ixgbe_is_sfp(hw)) {
> +               intr->flags |= IXGBE_FLAG_NEED_SFP_SETUP;
> +               err = rte_ctrl_thread_create(&ad->service_thread_tid,
> +                                            "ixgbe-service-thread",
> +                                            NULL,
> +                                            ixgbe_dev_service_thread_handler,
> +                                            dev);
> +               if (err) {
> +                       PMD_DRV_LOG(ERR, "service_thread err");
> +                       goto error;
> +               }
> +       }
> +
>         if (tm_conf->root && !tm_conf->committed)
>                 PMD_DRV_LOG(WARNING,
>                             "please call hierarchy_commit() "
> @@ -2860,13 +3210,21 @@ ixgbe_dev_stop(struct rte_eth_dev *dev)
>         int vf;
>         struct ixgbe_tm_conf *tm_conf =
>                 IXGBE_DEV_PRIVATE_TO_TM_CONF(dev->data->dev_private);
> +       void *res;
> +       s32 err;
> 
>         if (hw->adapter_stopped)
>                 return 0;
> 
>         PMD_INIT_FUNC_TRACE();
> 
> -       ixgbe_dev_wait_setup_link_complete(dev, 0);
> +       /* Cancel the service thread, and wait for it to join */
> +       err = pthread_cancel(adapter->service_thread_tid);
> +       if (err)
> +               PMD_DRV_LOG(ERR, "failed to cancel service thread %d", err);
> +       err = pthread_join(adapter->service_thread_tid, &res);
> +       if (err)
> +               PMD_DRV_LOG(ERR, "failed to join service thread %d",
> + err);
> 
>         /* disable interrupts */
>         ixgbe_disable_intr(hw);
> @@ -2945,7 +3303,6 @@ ixgbe_dev_set_link_up(struct rte_eth_dev *dev)
>         } else {
>                 /* Turn on the laser */
>                 ixgbe_enable_tx_laser(hw);
> -               ixgbe_dev_link_update(dev, 0);
>         }
> 
>         return 0;
> @@ -2976,7 +3333,6 @@ ixgbe_dev_set_link_down(struct rte_eth_dev *dev)
>         } else {
>                 /* Turn off the laser */
>                 ixgbe_disable_tx_laser(hw);
> -               ixgbe_dev_link_update(dev, 0);
>         }
> 
>         return 0;
> @@ -4128,57 +4484,6 @@ ixgbevf_check_link(struct ixgbe_hw *hw,
> ixgbe_link_speed *speed,
>         return ret_val;
>  }
> 
> -/*
> - * If @timeout_ms was 0, it means that it will not return until link 
> complete.
> - * It returns 1 on complete, return 0 on timeout.
> - */
> -static int
> -ixgbe_dev_wait_setup_link_complete(struct rte_eth_dev *dev, uint32_t
> timeout_ms) -{
> -#define WARNING_TIMEOUT    9000 /* 9s  in total */
> -       struct ixgbe_adapter *ad = dev->data->dev_private;
> -       uint32_t timeout = timeout_ms ? timeout_ms : WARNING_TIMEOUT;
> -
> -       while (rte_atomic32_read(&ad->link_thread_running)) {
> -               msec_delay(1);
> -               timeout--;
> -
> -               if (timeout_ms) {
> -                       if (!timeout)
> -                               return 0;
> -               } else if (!timeout) {
> -                       /* It will not return until link complete */
> -                       timeout = WARNING_TIMEOUT;
> -                       PMD_DRV_LOG(ERR, "IXGBE link thread not complete too 
> long
> time!");
> -               }
> -       }
> -
> -       return 1;
> -}
> -
> -static void *
> -ixgbe_dev_setup_link_thread_handler(void *param) -{
> -       struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
> -       struct ixgbe_adapter *ad = dev->data->dev_private;
> -       struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data-
> >dev_private);
> -       struct ixgbe_interrupt *intr =
> -               IXGBE_DEV_PRIVATE_TO_INTR(dev->data->dev_private);
> -       u32 speed;
> -       bool autoneg = false;
> -
> -       pthread_detach(pthread_self());
> -       speed = hw->phy.autoneg_advertised;
> -       if (!speed)
> -               ixgbe_get_link_capabilities(hw, &speed, &autoneg);
> -
> -       ixgbe_setup_link(hw, speed, true);
> -
> -       intr->flags &= ~IXGBE_FLAG_NEED_LINK_CONFIG;
> -       rte_atomic32_clear(&ad->link_thread_running);
> -       return NULL;
> -}
> -
>  /*
>   * In freebsd environment, nic_uio drivers do not support interrupts,
>   * rte_intr_callback_register() will fail to register interrupts.
> @@ -4218,11 +4523,8 @@ ixgbe_dev_link_update_share(struct rte_eth_dev
> *dev,
>                             int wait_to_complete, int vf)  {
>         struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data-
> >dev_private);
> -       struct ixgbe_adapter *ad = dev->data->dev_private;
>         struct rte_eth_link link;
>         ixgbe_link_speed link_speed = IXGBE_LINK_SPEED_UNKNOWN;
> -       struct ixgbe_interrupt *intr =
> -               IXGBE_DEV_PRIVATE_TO_INTR(dev->data->dev_private);
>         bool link_up;
>         int diag;
>         int wait = 1;
> @@ -4237,9 +4539,6 @@ ixgbe_dev_link_update_share(struct rte_eth_dev
> *dev,
> 
>         hw->mac.get_link_status = true;
> 
> -       if (intr->flags & IXGBE_FLAG_NEED_LINK_CONFIG)
> -               return rte_eth_linkstatus_set(dev, &link);
> -
>         /* check if it needs to wait to complete, if lsc interrupt is enabled 
> */
>         if (wait_to_complete == 0 || dev->data->dev_conf.intr_conf.lsc != 0)
>                 wait = 0;
> @@ -4254,7 +4553,7 @@ ixgbe_dev_link_update_share(struct rte_eth_dev
> *dev,
>         else
>                 diag = ixgbe_check_link(hw, &link_speed, &link_up, wait);
> 
> -       if (diag != 0) {
> +       if (diag != 0 || !link_up) {
>                 link.link_speed = RTE_ETH_SPEED_NUM_100M;
>                 link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
>                 return rte_eth_linkstatus_set(dev, &link); @@ -4267,32 
> +4566,6 @@
> ixgbe_dev_link_update_share(struct rte_eth_dev *dev,
>                         link_up = 0;
>         }
> 
> -       if (link_up == 0) {
> -               if (ixgbe_get_media_type(hw) == ixgbe_media_type_fiber) {
> -                       ixgbe_dev_wait_setup_link_complete(dev, 0);
> -                       if 
> (rte_atomic32_test_and_set(&ad->link_thread_running)) {
> -                               /* To avoid race condition between threads, 
> set
> -                                * the IXGBE_FLAG_NEED_LINK_CONFIG flag only
> -                                * when there is no link thread running.
> -                                */
> -                               intr->flags |= IXGBE_FLAG_NEED_LINK_CONFIG;
> -                               if 
> (rte_ctrl_thread_create(&ad->link_thread_tid,
> -                                       "ixgbe-link-handler",
> -                                       NULL,
> -                                       ixgbe_dev_setup_link_thread_handler,
> -                                       dev) < 0) {
> -                                       PMD_DRV_LOG(ERR,
> -                                               "Create link thread failed!");
> -                                       
> rte_atomic32_clear(&ad->link_thread_running);
> -                               }
> -                       } else {
> -                               PMD_DRV_LOG(ERR,
> -                                       "Other link thread is running now!");
> -                       }
> -               }
> -               return rte_eth_linkstatus_set(dev, &link);
> -       }
> -
>         link.link_status = RTE_ETH_LINK_UP;
>         link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
> 
> @@ -4498,8 +4771,6 @@ ixgbe_dev_interrupt_get_status(struct rte_eth_dev
> *dev)
>         eicr = IXGBE_READ_REG(hw, IXGBE_EICR);
>         PMD_DRV_LOG(DEBUG, "eicr %x", eicr);
> 
> -       intr->flags = 0;
> -
>         /* set flag for async link update */
>         if (eicr & IXGBE_EICR_LSC)
>                 intr->flags |= IXGBE_FLAG_NEED_LINK_UPDATE; @@ -4515,6
> +4786,14 @@ ixgbe_dev_interrupt_get_status(struct rte_eth_dev *dev)
>             (eicr & IXGBE_EICR_GPI_SDP0_X550EM_x))
>                 intr->flags |= IXGBE_FLAG_PHY_INTERRUPT;
> 
> +       /* Check for loss of SFP */
> +       /* TODO - For platforms that don't have this flag, do we need to set
> +        *  NEED_SFP_SETUP on LSC if we're a SFP platform?
> +        */
> +       if (hw->mac.type ==  ixgbe_mac_X550EM_a &&
> +           (eicr & IXGBE_EICR_GPI_SDP0_X550EM_a))
> +               intr->flags |= IXGBE_FLAG_NEED_SFP_SETUP;
> +
>         return 0;
>  }
> 
> @@ -4566,11 +4845,13 @@ ixgbe_dev_link_status_print(struct rte_eth_dev
> *dev)  static int  ixgbe_dev_interrupt_action(struct rte_eth_dev *dev)  {
> +       struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
> +       struct rte_intr_handle *intr_handle = pci_dev->intr_handle;
>         struct ixgbe_interrupt *intr =
>                 IXGBE_DEV_PRIVATE_TO_INTR(dev->data->dev_private);
> -       int64_t timeout;
>         struct ixgbe_hw *hw =
>                 IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> +       int64_t timeout;
> 
>         PMD_DRV_LOG(DEBUG, "intr action type %d", intr->flags);
> 
> @@ -4605,16 +4886,14 @@ ixgbe_dev_interrupt_action(struct rte_eth_dev
> *dev)
>                 if (rte_eal_alarm_set(timeout * 1000,
>                                       ixgbe_dev_interrupt_delayed_handler, 
> (void *)dev) < 0)
>                         PMD_DRV_LOG(ERR, "Error setting alarm");
> -               else {
> -                       /* remember original mask */
> -                       intr->mask_original = intr->mask;
> +               else
>                         /* only disable lsc interrupt */
>                         intr->mask &= ~IXGBE_EIMS_LSC;
> -               }
>         }
> 
>         PMD_DRV_LOG(DEBUG, "enable intr immediately");
>         ixgbe_enable_intr(dev);
> +       rte_intr_ack(intr_handle);
> 
>         return 0;
>  }
> @@ -4637,8 +4916,6 @@ static void
>  ixgbe_dev_interrupt_delayed_handler(void *param)  {
>         struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
> -       struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
> -       struct rte_intr_handle *intr_handle = pci_dev->intr_handle;
>         struct ixgbe_interrupt *intr =
>                 IXGBE_DEV_PRIVATE_TO_INTR(dev->data->dev_private);
>         struct ixgbe_hw *hw =
> @@ -4668,13 +4945,10 @@ ixgbe_dev_interrupt_delayed_handler(void
> *param)
>                 intr->flags &= ~IXGBE_FLAG_MACSEC;
>         }
> 
> -       /* restore original mask */
> -       intr->mask = intr->mask_original;
> -       intr->mask_original = 0;
> +       if (dev->data->dev_conf.intr_conf.lsc != 0)
> +               intr->mask |= IXGBE_EICR_LSC;
> 
> -       PMD_DRV_LOG(DEBUG, "enable intr in delayed handler S[%08x]", eicr);
>         ixgbe_enable_intr(dev);
> -       rte_intr_ack(intr_handle);
>  }
> 
>  /**
> @@ -5316,9 +5590,6 @@ ixgbevf_dev_start(struct rte_eth_dev *dev)
> 
>         PMD_INIT_FUNC_TRACE();
> 
> -       /* Stop the link setup handler before resetting the HW. */
> -       ixgbe_dev_wait_setup_link_complete(dev, 0);
> -
>         err = hw->mac.ops.reset_hw(hw);
> 
>         /**
> @@ -5398,12 +5669,6 @@ ixgbevf_dev_start(struct rte_eth_dev *dev)
>         /* Re-enable interrupt for VF */
>         ixgbevf_intr_enable(dev);
> 
> -       /*
> -        * Update link status right before return, because it may
> -        * start link configuration process in a separate thread.
> -        */
> -       ixgbevf_dev_link_update(dev, 0);
> -
>         hw->adapter_stopped = false;
> 
>         return 0;
> @@ -5422,8 +5687,6 @@ ixgbevf_dev_stop(struct rte_eth_dev *dev)
> 
>         PMD_INIT_FUNC_TRACE();
> 
> -       ixgbe_dev_wait_setup_link_complete(dev, 0);
> -
>         ixgbevf_intr_disable(dev);
> 
>         dev->data->dev_started = 0;
> diff --git a/drivers/net/ixgbe/ixgbe_ethdev.h
> b/drivers/net/ixgbe/ixgbe_ethdev.h
> index 69e0e82a5b..d243e417e9 100644
> --- a/drivers/net/ixgbe/ixgbe_ethdev.h
> +++ b/drivers/net/ixgbe/ixgbe_ethdev.h
> @@ -29,6 +29,7 @@
>  #define IXGBE_FLAG_PHY_INTERRUPT    (uint32_t)(1 << 2)
>  #define IXGBE_FLAG_MACSEC           (uint32_t)(1 << 3)
>  #define IXGBE_FLAG_NEED_LINK_CONFIG (uint32_t)(1 << 4)
> +#define IXGBE_FLAG_NEED_SFP_SETUP   ((uint32_t)(1 << 5))
> 
>  /*
>   * Defines that were not part of ixgbe_type.h as they are not used by the @@ 
> -
> 223,8 +224,6 @@ struct ixgbe_rte_flow_rss_conf {  struct ixgbe_interrupt {
>         uint32_t flags;
>         uint32_t mask;
> -       /*to save original mask during delayed handler */
> -       uint32_t mask_original;
>  };
> 
>  struct ixgbe_stat_mapping_registers {
> @@ -507,7 +506,7 @@ struct ixgbe_adapter {
>         uint8_t pflink_fullchk;
>         uint8_t mac_ctrl_frame_fwd;
>         rte_atomic32_t link_thread_running;
> -       pthread_t link_thread_tid;
> +       pthread_t service_thread_tid;
>  };
> 
>  struct ixgbe_vf_representor {
> @@ -670,6 +669,15 @@ int ixgbe_syn_filter_set(struct rte_eth_dev *dev,
>                         struct rte_eth_syn_filter *filter,
>                         bool add);
> 
> +enum ixgbe_sfp_cage_status {
> +       IXGBE_SFP_CAGE_EMPTY = 0,
> +       IXGBE_SFP_CAGE_FULL,
> +       IXGBE_SFP_CAGE_UNKNOWN = -1,
> +       IXGBE_SFP_CAGE_NOCAGE = -2,
> +};
> +enum ixgbe_sfp_cage_status ixgbe_check_sfp_cage(struct ixgbe_hw *hw);
> +
> +
>  /**
>   * l2 tunnel configuration.
>   */
> --
> 2.25.1

Hm, testing on a different platform there appears to be an issue when plugging 
in a module with a different speed from a prior module does not correctly 
update the link speed.  Currently investigating, could be a platform-specific 
issue.

Reply via email to