ioctl 详解,以ethtool为例

相关的ethtool命令:

ethtool -s|--change DEVNAME     Change generic options
                [ speed %d ]
                [ duplex half|full ]
                [ port tp|aui|bnc|mii|fibre ]
                [ mdix auto|on|off ]
                [ autoneg on|off ]
                [ advertise %x ]
                [ phyad %d ]
                [ xcvr internal|external ]
                [ wol p|u|m|b|a|g|s|d... ]
                [ sopass %x:%x:%x:%x:%x:%x ]
                [ msglvl %d | msglvl type on|off ... ]

 

该命令可以设置网卡的速率双工等等参数,我实际用到的命令是 ethtool -s ens1f1 speed 1000,即将ens1f1网口的速率设置为 GE,下面我们来看看配置是怎么下到网卡上的:

  • ethtool侧: do_sset --> ecmd.cmd = ETHTOOL_SSET;  send_ioctl --> ioctl(ctx->fd, SIOCETHTOOL, &ctx->ifr);标红的是命令字,内核是根据命令字做分发处理的。

  • linux侧:

第一步:dev_ioctl根据   SIOCETHTOOL 命令字调用dev_ethtool

	case SIOCETHTOOL:
		dev_load(net, ifr.ifr_name);
		rtnl_lock();
		ret = dev_ethtool(net, &ifr);
		rtnl_unlock();
		if (!ret) {
			if (colon)
				*colon = ':';
			if (copy_to_user(arg, &ifr,
					 sizeof(struct ifreq)))
				ret = -EFAULT;
		}
		return ret;

第二步:dev_ethtool 根据ETHTOOL_SSET 调用ethtool_set_settings

	case ETHTOOL_SSET:
		rc = ethtool_set_settings(dev, useraddr);
		break;

第三步:ethtool_set_settings

static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
{
	struct ethtool_cmd cmd;

	ASSERT_RTNL();

	if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
		return -EFAULT;

	/* first, try new %ethtool_link_ksettings API. */
	if (dev->ethtool_ops->set_link_ksettings) {
		struct ethtool_link_ksettings link_ksettings;

		if (!convert_legacy_settings_to_link_ksettings(&link_ksettings,
							       &cmd))
			return -EINVAL;

		link_ksettings.base.cmd = ETHTOOL_SLINKSETTINGS;
		link_ksettings.base.link_mode_masks_nwords
			= __ETHTOOL_LINK_MODE_MASK_NU32;
		return dev->ethtool_ops->set_link_ksettings(dev,
							    &link_ksettings);
	}

	/* legacy %ethtool_cmd API */

	/* TODO: return -EOPNOTSUPP when ethtool_ops::get_settings
	 * disappears internally
	 */

	if (!dev->ethtool_ops->set_settings)
		return -EOPNOTSUPP;

	return dev->ethtool_ops->set_settings(dev, &cmd);
}

最后调用到 dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings);  这个是网卡初始化时挂的ops指针

#ifdef ETHTOOL_GLINKSETTINGS
	.get_link_ksettings = i40e_get_link_ksettings,
	.set_link_ksettings = i40e_set_link_ksettings,
#endif /* ETHTOOL_GLINKSETTINGS */

我用的是X710网卡,下面我们看看i40e的源码是如何处理的:

static int i40e_set_link_ksettings(struct net_device *netdev,
				   const struct ethtool_link_ksettings *ks)
{
	struct i40e_netdev_priv *np = netdev_priv(netdev);
	struct i40e_aq_get_phy_abilities_resp abilities;
	struct ethtool_link_ksettings safe_ks;
	struct ethtool_link_ksettings copy_ks;
	struct i40e_aq_set_phy_config config;
	struct i40e_pf *pf = np->vsi->back;
	struct i40e_vsi *vsi = np->vsi;
	struct i40e_hw *hw = &pf->hw;
	bool autoneg_changed = false;
	i40e_status status = 0;
	int timeout = 50;
	int err = 0;
	u8 autoneg;

	/* Changing port settings is not supported if this isn't the
	 * port's controlling PF
	 */
	if (hw->partition_id != 1) {
		i40e_partition_setting_complaint(pf);
		return -EOPNOTSUPP;
	}
	if (vsi != pf->vsi[pf->lan_vsi])
		return -EOPNOTSUPP;
	if (hw->phy.media_type != I40E_MEDIA_TYPE_BASET &&
	    hw->phy.media_type != I40E_MEDIA_TYPE_FIBER &&
	    hw->phy.media_type != I40E_MEDIA_TYPE_BACKPLANE &&
	    hw->phy.media_type != I40E_MEDIA_TYPE_DA &&
	    hw->phy.link_info.link_info & I40E_AQ_LINK_UP)
		return -EOPNOTSUPP;
	if (hw->device_id == I40E_DEV_ID_KX_B ||
	    hw->device_id == I40E_DEV_ID_KX_C ||
	    hw->device_id == I40E_DEV_ID_20G_KR2 ||
	    hw->device_id == I40E_DEV_ID_20G_KR2_A ||
	    hw->device_id == I40E_DEV_ID_25G_B ||
	    hw->device_id == I40E_DEV_ID_KX_X722) {
		netdev_info(netdev, "Changing settings is not supported on backplane.\n");
		return -EOPNOTSUPP;
	}

	/* copy the ksettings to copy_ks to avoid modifying the origin */
	memcpy(&copy_ks, ks, sizeof(struct ethtool_link_ksettings));

	/* save autoneg out of ksettings */
	autoneg = copy_ks.base.autoneg;

	/* get our own copy of the bits to check against */
	memset(&safe_ks, 0, sizeof(struct ethtool_link_ksettings));
	safe_ks.base.cmd = copy_ks.base.cmd;
	safe_ks.base.link_mode_masks_nwords =
		copy_ks.base.link_mode_masks_nwords;
	i40e_get_link_ksettings(netdev, &safe_ks);

	/* Get link modes supported by hardware and check against modes
	 * requested by user.  Return an error if unsupported mode was set.
	 */
	if (!bitmap_subset(copy_ks.link_modes.advertising,
			   safe_ks.link_modes.supported,
			   __ETHTOOL_LINK_MODE_MASK_NBITS))
		return -EINVAL;

	/* set autoneg back to what it currently is */
	copy_ks.base.autoneg = safe_ks.base.autoneg;

	/* If copy_ks.base and safe_ks.base are not the same now, then they are
	 * trying to set something that we do not support.
	 */
	if (memcmp(&copy_ks.base, &safe_ks.base,
		   sizeof(struct ethtool_link_settings)))
		return -EOPNOTSUPP;

	while (test_and_set_bit(__I40E_CONFIG_BUSY, pf->state)) {
		timeout--;
		if (!timeout)
			return -EBUSY;
		usleep_range(1000, 2000);
	}

	/* Get the current phy config */
	status = i40e_aq_get_phy_capabilities(hw, false, false, &abilities,
					      NULL);
	if (status) {
		err = -EAGAIN;
		goto done;
	}

	/* Copy abilities to config in case autoneg is not
	 * set below
	 */
	memset(&config, 0, sizeof(struct i40e_aq_set_phy_config));
	config.abilities = abilities.abilities;

	/* Check autoneg */
	if (autoneg == AUTONEG_ENABLE) {
		/* If autoneg was not already enabled */
		if (!(hw->phy.link_info.an_info & I40E_AQ_AN_COMPLETED)) {
			/* If autoneg is not supported, return error */
			if (!ethtool_link_ksettings_test_link_mode(
				      &safe_ks, supported, Autoneg)) {
				netdev_info(netdev, "Autoneg not supported on this phy\n");
				err = -EINVAL;
				goto done;
			}
			/* Autoneg is allowed to change */
			config.abilities = abilities.abilities |
					   I40E_AQ_PHY_ENABLE_AN;
			autoneg_changed = true;
		}
	} else {
		/* If autoneg is currently enabled */
		if (hw->phy.link_info.an_info & I40E_AQ_AN_COMPLETED) {
			/* If autoneg is supported 10GBASE_T is the only PHY
			 * that can disable it, so otherwise return error
			 */
			if (ethtool_link_ksettings_test_link_mode(
				     &safe_ks, supported, Autoneg) &&
			    hw->phy.link_info.phy_type !=
			    I40E_PHY_TYPE_10GBASE_T) {
				netdev_info(netdev, "Autoneg cannot be disabled on this phy\n");
				err = -EINVAL;
				goto done;
			}
			/* Autoneg is allowed to change */
			config.abilities = abilities.abilities &
					   ~I40E_AQ_PHY_ENABLE_AN;
			autoneg_changed = true;
		}
	}

	if (ethtool_link_ksettings_test_link_mode(ks, advertising,
						  100baseT_Full))
		config.link_speed |= I40E_LINK_SPEED_100MB;
	if (ethtool_link_ksettings_test_link_mode(ks, advertising,
						  1000baseT_Full) ||
#ifdef HAVE_ETHTOOL_NEW_10G_BITS
	    ethtool_link_ksettings_test_link_mode(ks, advertising,
						  1000baseX_Full) ||
#endif
	    ethtool_link_ksettings_test_link_mode(ks, advertising,
						  1000baseKX_Full))
		config.link_speed |= I40E_LINK_SPEED_1GB;
	if (ethtool_link_ksettings_test_link_mode(ks, advertising,
						  10000baseT_Full) ||
	    ethtool_link_ksettings_test_link_mode(ks, advertising,
						  10000baseKX4_Full) ||
	    ethtool_link_ksettings_test_link_mode(ks, advertising,
						  10000baseKR_Full) ||
#ifdef HAVE_ETHTOOL_NEW_10G_BITS
	    ethtool_link_ksettings_test_link_mode(ks, advertising,
						  10000baseCR_Full) ||
	    ethtool_link_ksettings_test_link_mode(ks, advertising,
						  10000baseSR_Full) ||
	    ethtool_link_ksettings_test_link_mode(ks, advertising,
						  10000baseLR_Full))
#else
	    0)
#endif /* HAVE_ETHTOOL_NEW_10G_BITS */
		config.link_speed |= I40E_LINK_SPEED_10GB;
#ifdef HAVE_ETHTOOL_NEW_2500MB_BITS
	if (ethtool_link_ksettings_test_link_mode(ks, advertising,
						  2500baseT_Full))
		config.link_speed |= I40E_LINK_SPEED_2_5GB;
#endif /* HAVE_ETHTOOL_NEW_2500MB_BITS */
#ifdef HAVE_ETHTOOL_5G_BITS
	if (ethtool_link_ksettings_test_link_mode(ks, advertising,
						  5000baseT_Full))
		config.link_speed |= I40E_LINK_SPEED_5GB;
#endif /* HAVE_ETHTOOL_5G_BITS */
	if (ethtool_link_ksettings_test_link_mode(ks, advertising,
						  20000baseKR2_Full))
		config.link_speed |= I40E_LINK_SPEED_20GB;
#ifdef HAVE_ETHTOOL_25G_BITS
	if (ethtool_link_ksettings_test_link_mode(ks, advertising,
						  25000baseCR_Full) ||
	    ethtool_link_ksettings_test_link_mode(ks, advertising,
						  25000baseKR_Full) ||
	    ethtool_link_ksettings_test_link_mode(ks, advertising,
						  25000baseSR_Full))
		config.link_speed |= I40E_LINK_SPEED_25GB;
#endif /* HAVE_ETHTOOL_25G_BITS */
	if (ethtool_link_ksettings_test_link_mode(ks, advertising,
						  40000baseKR4_Full) ||
	    ethtool_link_ksettings_test_link_mode(ks, advertising,
						  40000baseCR4_Full) ||
	    ethtool_link_ksettings_test_link_mode(ks, advertising,
						  40000baseSR4_Full) ||
	    ethtool_link_ksettings_test_link_mode(ks, advertising,
						  40000baseLR4_Full))
		config.link_speed |= I40E_LINK_SPEED_40GB;

	/* If speed didn't get set, set it to what it currently is.
	 * This is needed because if advertise is 0 (as it is when autoneg
	 * is disabled) then speed won't get set.
	 */
	if (!config.link_speed)
		config.link_speed = abilities.link_speed;
	if (autoneg_changed || (abilities.link_speed != config.link_speed)) {
		/* copy over the rest of the abilities */
		config.phy_type = abilities.phy_type;
		config.phy_type_ext = abilities.phy_type_ext;
		config.eee_capability = abilities.eee_capability;
		config.eeer = abilities.eeer_val;
		config.low_power_ctrl = abilities.d3_lpan;
		config.fec_config = abilities.fec_cfg_curr_mod_ext_info &
				    I40E_AQ_PHY_FEC_CONFIG_MASK;

		/* save the requested speeds */
		hw->phy.link_info.requested_speeds = config.link_speed;
		/* set link and auto negotiation so changes take effect */
		config.abilities |= I40E_AQ_PHY_ENABLE_ATOMIC_LINK;
		/* If link is up put link down */
		if (hw->phy.link_info.link_info & I40E_AQ_LINK_UP) {
			/* Tell the OS link is going down, the link will go
			 * back up when fw says it is ready asynchronously
			 */
			i40e_print_link_message(vsi, false);
			netif_carrier_off(netdev);
			netif_tx_stop_all_queues(netdev);
		}

		/* make the aq call */
		status = i40e_aq_set_phy_config(hw, &config, NULL);
		if (status) {
			netdev_info(netdev,
				    "Set phy config failed, err %s aq_err %s\n",
				    i40e_stat_str(hw, status),
				    i40e_aq_str(hw, hw->aq.asq_last_status));
			err = -EAGAIN;
			goto done;
		}

		status = i40e_update_link_info(hw);
		if (status)
			netdev_dbg(netdev,
				   "Updating link info failed with err %s aq_err %s\n",
				   i40e_stat_str(hw, status),
				   i40e_aq_str(hw, hw->aq.asq_last_status));

	} else {
		netdev_info(netdev, "Nothing changed, exiting without setting anything.\n");
	}

done:
	clear_bit(__I40E_CONFIG_BUSY, pf->state);

	return err;
}

 

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值