If you start ‘ethtool -p ‘ and also start ‘ethtool ‘ after that, you may see the delays in ‘ethtool ‘ command.
It is because any ethtool commands start by taking ‘rtnl_lock’ and ‘ethtool -p’ is keep running for LED on/off.
In the below, bnx2x’s identity function just turns on or off the led. get_settings() function is even simpler and print stastics.
static int bnx2x_set_phys_id(struct net_device *dev, enum ethtool_phys_id_state state) { struct bnx2x *bp = netdev_priv(dev); if (!bnx2x_is_nvm_accessible(bp)) { DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "cannot access eeprom when the interface is down\n"); return -EAGAIN; } switch (state) { case ETHTOOL_ID_ACTIVE: return 1; /* cycle on/off once per second */ case ETHTOOL_ID_ON: bnx2x_acquire_phy_lock(bp); bnx2x_set_led(&bp->link_params, &bp->link_vars, LED_MODE_ON, SPEED_1000); bnx2x_release_phy_lock(bp); break; case ETHTOOL_ID_OFF: bnx2x_acquire_phy_lock(bp); bnx2x_set_led(&bp->link_params, &bp->link_vars, LED_MODE_FRONT_PANEL_OFF, 0); bnx2x_release_phy_lock(bp); break; case ETHTOOL_ID_INACTIVE: bnx2x_acquire_phy_lock(bp); bnx2x_set_led(&bp->link_params, &bp->link_vars, LED_MODE_OPER, bp->link_vars.line_speed); bnx2x_release_phy_lock(bp); } return 0; } static int bnx2x_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct bnx2x *bp = netdev_priv(dev); int cfg_idx = bnx2x_get_link_cfg_idx(bp); u32 media_type; /* Dual Media boards present all available port types */ cmd->supported = bp->port.supported[cfg_idx] | (bp->port.supported[cfg_idx ^ 1] & (SUPPORTED_TP | SUPPORTED_FIBRE)); cmd->advertising = bp->port.advertising[cfg_idx]; media_type = bp->link_params.phy[bnx2x_get_cur_phy_idx(bp)].media_type; if (media_type == ETH_PHY_SFP_1G_FIBER) { cmd->supported &= ~(SUPPORTED_10000baseT_Full); cmd->advertising &= ~(ADVERTISED_10000baseT_Full); } if ((bp->state == BNX2X_STATE_OPEN) && bp->link_vars.link_up && !(bp->flags & MF_FUNC_DIS)) { cmd->duplex = bp->link_vars.duplex; if (IS_MF(bp) && !BP_NOMCP(bp)) ethtool_cmd_speed_set(cmd, bnx2x_get_mf_speed(bp)); else ethtool_cmd_speed_set(cmd, bp->link_vars.line_speed); } else { cmd->duplex = DUPLEX_UNKNOWN; ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN); } cmd->port = bnx2x_get_port_type(bp); cmd->phy_address = bp->mdio.prtad; cmd->transceiver = XCVR_INTERNAL; if (bp->link_params.req_line_speed[cfg_idx] == SPEED_AUTO_NEG) cmd->autoneg = AUTONEG_ENABLE; else cmd->autoneg = AUTONEG_DISABLE; /* Publish LP advertised speeds and FC */ if (bp->link_vars.link_status & LINK_STATUS_AUTO_NEGOTIATE_COMPLETE) { u32 status = bp->link_vars.link_status; cmd->lp_advertising |= ADVERTISED_Autoneg; if (status & LINK_STATUS_LINK_PARTNER_SYMMETRIC_PAUSE) cmd->lp_advertising |= ADVERTISED_Pause; if (status & LINK_STATUS_LINK_PARTNER_ASYMMETRIC_PAUSE) cmd->lp_advertising |= ADVERTISED_Asym_Pause; if (status & LINK_STATUS_LINK_PARTNER_10THD_CAPABLE) cmd->lp_advertising |= ADVERTISED_10baseT_Half; if (status & LINK_STATUS_LINK_PARTNER_10TFD_CAPABLE) cmd->lp_advertising |= ADVERTISED_10baseT_Full; if (status & LINK_STATUS_LINK_PARTNER_100TXHD_CAPABLE) cmd->lp_advertising |= ADVERTISED_100baseT_Half; if (status & LINK_STATUS_LINK_PARTNER_100TXFD_CAPABLE) cmd->lp_advertising |= ADVERTISED_100baseT_Full; if (status & LINK_STATUS_LINK_PARTNER_1000THD_CAPABLE) cmd->lp_advertising |= ADVERTISED_1000baseT_Half; if (status & LINK_STATUS_LINK_PARTNER_1000TFD_CAPABLE) { if (media_type == ETH_PHY_KR) { cmd->lp_advertising |= ADVERTISED_1000baseKX_Full; } else { cmd->lp_advertising |= ADVERTISED_1000baseT_Full; } } if (status & LINK_STATUS_LINK_PARTNER_2500XFD_CAPABLE) cmd->lp_advertising |= ADVERTISED_2500baseX_Full; if (status & LINK_STATUS_LINK_PARTNER_10GXFD_CAPABLE) { if (media_type == ETH_PHY_KR) { cmd->lp_advertising |= ADVERTISED_10000baseKR_Full; } else { cmd->lp_advertising |= ADVERTISED_10000baseT_Full; } } if (status & LINK_STATUS_LINK_PARTNER_20GXFD_CAPABLE) cmd->lp_advertising |= ADVERTISED_20000baseKR2_Full; } cmd->maxtxpkt = 0; cmd->maxrxpkt = 0; DP(BNX2X_MSG_ETHTOOL, "ethtool_cmd: cmd %d\n" " supported 0x%x advertising 0x%x speed %u\n" " duplex %d port %d phy_address %d transceiver %d\n" " autoneg %d maxtxpkt %d maxrxpkt %d\n", cmd->cmd, cmd->supported, cmd->advertising, ethtool_cmd_speed(cmd), cmd->duplex, cmd->port, cmd->phy_address, cmd->transceiver, cmd->autoneg, cmd->maxtxpkt, cmd->maxrxpkt); return 0; }
Usually ethtool_phys_id() tries 2 steps – turn on and turn off – before return.
static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) { struct ethtool_value id; static bool busy; int rc; if (!GET_ETHTOOL_OP_EXT(dev, set_phys_id) && !dev->ethtool_ops->phys_id) return -EOPNOTSUPP; if (busy) return -EBUSY; if (copy_from_user(&id, useraddr, sizeof(id))) return -EFAULT; if (!GET_ETHTOOL_OP_EXT(dev, set_phys_id)) /* Do it the old way */ return dev->ethtool_ops->phys_id(dev, id.data); rc = GET_ETHTOOL_OP_EXT(dev, set_phys_id)(dev, ETHTOOL_ID_ACTIVE); if (rc ethtool_ops->get_settings) return -EOPNOTSUPP; err = dev->ethtool_ops->get_settings(dev, &cmd); if (err < 0) return err; if (copy_to_user(useraddr, &cmd, sizeof(cmd))) return -EFAULT; return 0; } int dev_ethtool(struct net *net, struct ifreq *ifr) { ... case ETHTOOL_GSET: rc = ethtool_get_settings(dev, useraddr); break; ... }
This is called from dev_ioctl() which is starting by holding ‘rtnl_lock()’.
int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) { ... case SIOCETHTOOL: dev_load(net, ifr.ifr_name); rtnl_lock(); ret = dev_ethtool(net, &ifr); rtnl_unlock(); ... }
Leave a Reply