RED硬件卸载支持

在RED参数处理的最后,调用red_offload设置RED卸载功能。

static int red_change(struct Qdisc *sch, struct nlattr *opt,
              struct netlink_ext_ack *extack)
{
    struct Qdisc *old_child = NULL, *child = NULL;
    struct red_sched_data *q = qdisc_priv(sch);
    struct nlattr *tb[TCA_RED_MAX + 1];
    struct tc_red_qopt *ctl;

    err = nla_parse_nested(tb, TCA_RED_MAX, opt, red_policy, NULL);
    if (err < 0)
        return err;

    max_P = tb[TCA_RED_MAX_P] ? nla_get_u32(tb[TCA_RED_MAX_P]) : 0;

    ctl = nla_data(tb[TCA_RED_PARMS]);
    ...
    red_offload(sch, true);

    if (old_child)
        qdisc_put(old_child);
    return 0;

首先来看一下辅助函数tc_can_offload,其用于判断网络设备是否具有流量控制(TC)的硬件处理功能。

static inline bool tc_can_offload(const struct net_device *dev)
{
    return dev->features & NETIF_F_HW_TC;
} 

在red_offload函数中,进行相应的检查,确定RED对应的网络设备是否在硬件上支持TC功能,并且,驱动程序是否实现了接口函数ndo_setup_tc,如果以上不成立,返回错误码EOPNOTSUPP。注意这里传递给驱动层的参数中包含的handle和parent,用于匹配配置的RED队列。

static int red_offload(struct Qdisc *sch, bool enable)
{
    struct red_sched_data *q = qdisc_priv(sch);
    struct net_device *dev = qdisc_dev(sch);
    struct tc_red_qopt_offload opt = {
        .handle = sch->handle,
        .parent = sch->parent,
    };

    if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
        return -EOPNOTSUPP;

对于offload的使能情况,这里设置了两类信息:一是RED的基本参数,包括qth_min和qth_max(这里需要的是原值,需要右移Wlog位恢复成原值),max_P等;另外就是统计信息结构,这里将此结构的地址进行下发,由下层直接修改。

最后,由网络设备的驱动接口函数ndo_setup_tc进行处理。

    if (enable) {
        opt.command = TC_RED_REPLACE;
        opt.set.min = q->parms.qth_min >> q->parms.Wlog;
        opt.set.max = q->parms.qth_max >> q->parms.Wlog;
        opt.set.probability = q->parms.max_P;
        opt.set.limit = q->limit;
        opt.set.is_ecn = red_use_ecn(q);
        opt.set.is_harddrop = red_use_harddrop(q);
        opt.set.qstats = &sch->qstats;
    } else {
        opt.command = TC_RED_DESTROY;
    }

    return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_RED, &opt);

以下,在删除RED队列时,调用以上的red_offload函数,这是enable为false,仅将opt.command设置为TC_RED_DESTROY,由驱动函数ndo_setup_tc执行删除操作。

static void red_destroy(struct Qdisc *sch)
{
    struct red_sched_data *q = qdisc_priv(sch);

    del_timer_sync(&q->adapt_timer);
    red_offload(sch, false);
    qdisc_put(q->qdisc);
}

网卡驱动TC_OFFLOAD

目前,支持TC卸载功能的网络硬件并不多,内核中仅有Netronome和Mellanox的网卡和驱动支持。以下看一下mellanox驱动相关部分。在文件:drivers/net/ethernet/mellanox/mlxsw/spectrum.c中,函数mlxsw_sp_port_create声明了对TC卸载的硬件支持,设置了NETIF_F_HW_TC标志位。

static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
                bool split, u8 module, u8 width, u8 lane)
{
    struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
    struct mlxsw_sp_port *mlxsw_sp_port;
    struct net_device *dev;

    err = mlxsw_core_port_init(mlxsw_sp->core, local_port);
    if (err) {
        dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to init core port\n", local_port);
        return err;
    }

    dev = alloc_etherdev(sizeof(struct mlxsw_sp_port));
    ...
    dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_LLTX | NETIF_F_SG |
             NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_TC;
    dev->hw_features |= NETIF_F_HW_TC;

在网络设备操作集结构变量mlxsw_sp_port_netdev_ops中,初始化了函数指针ndo_setup_tc,即mlxsw_sp_setup_tc,处理TC卸载功能。

static const struct net_device_ops mlxsw_sp_port_netdev_ops = {
    .ndo_open       = mlxsw_sp_port_open,
    .ndo_stop       = mlxsw_sp_port_stop,
    .ndo_start_xmit     = mlxsw_sp_port_xmit,
    .ndo_setup_tc           = mlxsw_sp_setup_tc,

如下mlxsw_sp_setup_tc函数,仅看一下TC_SETUP_QDISC_RED对应的mlxsw_sp_setup_tc_red函数。

static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data)
{
    struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);

    switch (type) {
    case TC_SETUP_BLOCK:
        return mlxsw_sp_setup_tc_block(mlxsw_sp_port, type_data);
    case TC_SETUP_QDISC_RED:
        return mlxsw_sp_setup_tc_red(mlxsw_sp_port, type_data);
    case TC_SETUP_QDISC_PRIO:
        return mlxsw_sp_setup_tc_prio(mlxsw_sp_port, type_data);
    default:
        return -EOPNOTSUPP;

对于命令字为TC_RED_REPLACE的情况,由函数mlxsw_sp_qdisc_replace设置RED参数。

int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, struct tc_red_qopt_offload *p)
{    
    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;

    mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false);
    if (!mlxsw_sp_qdisc)
        return -EOPNOTSUPP;

    if (p->command == TC_RED_REPLACE)
        return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 
                          mlxsw_sp_qdisc, &mlxsw_sp_qdisc_ops_red, &p->set);

对于其它命令字,首先确认此handle表示的qdisc是否存在,之后执行请求的操作,如删除qdisc、获取统计信息和状态,即命令字:TC_RED_DESTROY,TC_RED_XSTATS和TC_RED_STATS。

    if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, MLXSW_SP_QDISC_RED))
        return -EOPNOTSUPP;

    switch (p->command) {
    case TC_RED_DESTROY:
        return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
    case TC_RED_XSTATS:
        return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc, p->xstats);
    case TC_RED_STATS:
        return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, &p->stats);
    default:
        return -EOPNOTSUPP;
    }

如下,Mellanox的网卡驱动注册了qdisc操作函数集结构变量mlxsw_sp_qdisc_ops_red,来处理RED的相关操作,设置相关寄存器。

static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = {
    .type = MLXSW_SP_QDISC_RED,
    .check_params = mlxsw_sp_qdisc_red_check_params,
    .replace = mlxsw_sp_qdisc_red_replace,
    .unoffload = mlxsw_sp_qdisc_red_unoffload,
    .destroy = mlxsw_sp_qdisc_red_destroy,
    .get_stats = mlxsw_sp_qdisc_get_red_stats,
    .get_xstats = mlxsw_sp_qdisc_get_red_xstats,
    .clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats,
};

内核版本 5.0

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页