Sungju's Slow Life

Personal journal


What happens if you try two commands ‘ethtool -p ‘ and ‘ethtool ‘ in parallel.

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

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: