Net DIM - 通用网络动态中断调节

作者:

Tal Gilboa <talgi@mellanox.com>

假设

本文档假设读者具备网络驱动程序和一般中断调节的基本知识。

简介

动态中断调节 (DIM)(在网络中)指的是更改通道的中断调节配置,以优化数据包处理。该机制包含一个算法,该算法决定是否以及如何更改通道的调节参数,通常是通过对从系统采样的运行时数据执行分析。Net DIM 就是这样一种机制。在算法的每次迭代中,它都会分析给定的数据样本,将其与之前的样本进行比较,如果需要,它可以决定更改一些中断调节配置字段。数据样本由数据带宽、数据包数量和事件数量组成。还会测量样本之间的时间。Net DIM 比较当前和之前的数据,并返回调整后的中断调节配置对象。在某些情况下,算法可能会决定不进行任何更改。配置字段是事件之间允许的最小持续时间(微秒)和每个事件所需的最大数据包数。Net DIM 算法重视提高带宽而不是降低中断率。

Net DIM 算法

Net DIM 算法的每次迭代都遵循以下步骤

  1. 计算新的数据样本。

  2. 将其与之前的样本进行比较。

  3. 做出决策 - 建议中断调节配置字段。

  4. 应用计划的工作函数,该函数应用建议的配置。

前两个步骤很简单,新数据和先前的数据都由注册到 Net DIM 的驱动程序提供。先前的数据是上一次迭代提供的新数据。比较步骤检查新旧数据之间的差异,并决定最后一步的结果。如果带宽增加,则该步骤的结果为“更好”,如果带宽减少,则该步骤的结果为“更差”。如果带宽没有变化,则以类似的方式比较数据包速率 - 增加 == “更好”,减少 == “更差”。如果数据包速率也没有变化,则比较中断率。这里,算法会尝试优化以降低中断率,因此中断率的增加被认为是“更差”,而减少被认为是“更好”。步骤 #2 有一个避免错误结果的优化:仅当样本之间的差异大于某个百分比时,才认为该差异有效。此外,由于 Net DIM 本身不测量任何内容,因此它假设驱动程序提供的数据有效。

步骤 #3 根据步骤 #2 的结果和算法的内部状态决定建议的配置。这些状态反映了算法的“方向”:它是向左(减少调节)、向右(增加调节)还是保持不动。另一个优化是,如果多次决定保持不动,则算法迭代之间的时间间隔会增加,以减少计算开销。此外,在“停靠”在最左或最右的决策之一后,算法可能会决定通过向另一个方向迈出一步来验证此决策。这样做是为了避免陷入“深度睡眠”场景。一旦做出决定,就会从预定义的配置文件中选择中断调节配置。

最后一步是通知已注册的驱动程序应应用建议的配置。这是通过计划一个工作函数来完成的,该函数由 Net DIM API 定义并由已注册的驱动程序提供。

如您所见,Net DIM 本身不与系统主动交互。如果向其提供错误的数据,它将难以做出正确的决定,并且如果工作函数不应用建议的配置,它将毫无用处。但是,这确实允许已注册的驱动程序有一定的操作空间,因为它可能会提供部分数据或在某些情况下忽略算法的建议。

将网络设备注册到 DIM

Net DIM API 公开了主函数 net_dim()。此函数是 Net DIM 算法的入口点,每次驱动程序想要检查是否应更改中断调节参数时都必须调用该函数。驱动程序应提供两个数据结构:struct dimstruct dim_samplestruct dim 描述了特定对象(RX 队列、TX 队列、其他队列等)的 DIM 状态。这包括当前选择的配置文件、先前的数据样本、驱动程序提供的回调函数等。struct dim_sample 描述了一个数据样本,该样本将与存储在 struct dim 中的数据样本进行比较,以决定算法的下一步。该样本应包括驱动程序测量的字节数、数据包数和中断数。

为了从网络驱动程序中使用 Net DIM,驱动程序需要调用主 net_dim() 函数。建议的方法是在每次中断时调用 net_dim()。由于 Net DIM 具有内置的调节功能,并且在某些情况下可能会决定跳过迭代,因此无需调节 net_dim() 调用。如上所述,驱动程序需要向 net_dim() 函数调用提供 struct dim 类型的对象。建议每个使用 Net DIM 的实体将 struct dim 作为其数据结构的一部分,并将其用作主 Net DIM API 对象。struct dim_sample 应保存最新的字节数、数据包数和中断计数。无需执行任何计算,只需包含原始数据即可。

net_dim() 调用本身不返回任何内容。相反,Net DIM 依赖于驱动程序提供回调函数,当算法决定更改中断调节参数时,将调用该函数。此回调将在单独的线程中计划和运行,以免给数据流增加开销。完成工作后,需要将 Net DIM 算法设置为正确的状态,以便进入下一次迭代。

示例

以下代码演示如何将驱动程序注册到 Net DIM。实际用法并不完整,但它应该使用法的大纲清晰明了。

#include <linux/dim.h>

/* Callback for net DIM to schedule on a decision to change moderation */
void my_driver_do_dim_work(struct work_struct *work)
{
      /* Get struct dim from struct work_struct */
      struct dim *dim = container_of(work, struct dim,
                                     work);
      /* Do interrupt moderation related stuff */
      ...

      /* Signal net DIM work is done and it should move to next iteration */
      dim->state = DIM_START_MEASURE;
}

/* My driver's interrupt handler */
int my_driver_handle_interrupt(struct my_driver_entity *my_entity, ...)
{
      ...
      /* A struct to hold current measured data */
      struct dim_sample dim_sample;
      ...
      /* Initiate data sample struct with current data */
      dim_update_sample(my_entity->events,
                        my_entity->packets,
                        my_entity->bytes,
                        &dim_sample);
      /* Call net DIM */
      net_dim(&my_entity->dim, &dim_sample);
      ...
}

/* My entity's initialization function (my_entity was already allocated) */
int my_driver_init_my_entity(struct my_driver_entity *my_entity, ...)
{
      ...
      /* Initiate struct work_struct with my driver's callback function */
      INIT_WORK(&my_entity->dim.work, my_driver_do_dim_work);
      ...
}

调整 DIM

Net DIM 服务于一系列网络设备,并提供出色的加速优势。然而,已经观察到 DIM 的某些预设配置可能无法与网络设备的不同规格无缝对齐,并且这种差异已被确定为导致启用 DIM 的网络设备的次优性能结果的因素,这与配置文件中的不匹配有关。

为了解决这个问题,Net DIM 引入了针对每个设备的控制,以修改和访问设备的 rx-profiletx-profile 参数:假设目标网络设备名为 ethx,并且 ethx 仅声明支持 RX 配置文件设置,并支持修改 usec 字段和 pkts 字段(请参阅数据结构:struct dim_cq_moder)。

您可以使用 ethtool 修改当前的 RX DIM 配置文件,其中所有值为 64。

$ ethtool -C ethx rx-profile 1,1,n_2,2,n_3,n,n_n,4,n_n,n,n

n 表示不修改此字段,_ 分隔配置文件数组的结构元素。

使用以下命令查询当前配置文件:

$ ethtool -c ethx
...
rx-profile:
{.usec =   1, .pkts =   1, .comps = n/a,},
{.usec =   2, .pkts =   2, .comps = n/a,},
{.usec =   3, .pkts =  64, .comps = n/a,},
{.usec =  64, .pkts =   4, .comps = n/a,},
{.usec =  64, .pkts =  64, .comps = n/a,}
tx-profile:   n/a

如果网络设备不支持 DIM 配置文件的特定字段,则将显示相应的 n/a。 如果正在修改 n/a 字段,则会报告错误消息。

动态中断调节 (DIM) 库 API

struct dim_cq_moder

CQ 调节值的结构。 用于 DIM 和其使用者之间的通信。

定义:

struct dim_cq_moder {
    u16 usec;
    u16 pkts;
    u16 comps;
    u8 cq_period_mode;
    struct rcu_head rcu;
};

成员

usec

CQ 定时器建议(由 DIM 提供)

pkts

CQ 数据包计数器建议(由 DIM 提供)

comps

完成计数器

cq_period_mode

CQ 周期计数模式(来自 CQE/EQE)

rcu

用于异步 kfree_rcu

struct dim_irq_moder

用于 irq 调节信息的结构。 用于收集与 irq 调节相关的信息。

定义:

struct dim_irq_moder {
    u8 profile_flags;
    u8 coal_flags;
    u8 dim_rx_mode;
    u8 dim_tx_mode;
    struct dim_cq_moder __rcu *rx_profile;
    struct dim_cq_moder __rcu *tx_profile;
    void (*rx_dim_work)(struct work_struct *work);
    void (*tx_dim_work)(struct work_struct *work);
};

成员

profile_flags

DIM_PROFILE_*

coal_flags

用于 Rx 和 Tx 的 DIM_COALESCE_*

dim_rx_mode

Rx DIM 周期计数模式:CQE 或 EQE

dim_tx_mode

Tx DIM 周期计数模式:CQE 或 EQE

rx_profile

Rx 的 DIM 配置文件列表

tx_profile

Tx 的 DIM 配置文件列表

rx_dim_work

net_dim() 调度的 Rx DIM 工作程序

tx_dim_work

net_dim() 调度的 Tx DIM 工作程序

struct dim_sample

用于 DIM 采样数据的结构。 用于 DIM 和其使用者之间的通信。

定义:

struct dim_sample {
    ktime_t time;
    u32 pkt_ctr;
    u32 byte_ctr;
    u16 event_ctr;
    u32 comp_ctr;
};

成员

time

采样时间戳

pkt_ctr

数据包数量

byte_ctr

字节数

event_ctr

事件数量

comp_ctr

当前完成计数器

struct dim_stats

用于 DIM 统计信息的结构。 用于保存当前测量的速率。

定义:

struct dim_stats {
    int ppms;
    int bpms;
    int epms;
    int cpms;
    int cpe_ratio;
};

成员

ppms

每毫秒数据包数

bpms

每毫秒字节数

epms

每毫秒事件数

cpms

每毫秒完成数

cpe_ratio

完成数与事件数的比率

struct dim

用于动态中断调节 (DIM) 的主结构。 用于保存有关特定 DIM 实例的所有信息。

定义:

struct dim {
    u8 state;
    struct dim_stats prev_stats;
    struct dim_sample start_sample;
    struct dim_sample measuring_sample;
    struct work_struct work;
    void *priv;
    u8 profile_ix;
    u8 mode;
    u8 tune_state;
    u8 steps_right;
    u8 steps_left;
    u8 tired;
};

成员

state

算法状态(见下文)

prev_stats

上次迭代的测量速率(用于比较)

start_sample

当前迭代开始时的采样数据

measuring_sample

用于更新当前事件的 dim_sample

work

在需要执行操作时执行的工作

priv

指向 dim 的结构的指针

profile_ix

当前调节配置文件

mode

CQ 周期计数模式

tune_state

算法调整状态(见下文)

steps_right

向更高调节采取的步数

steps_left

向更低调节采取的步数

tired

停车深度计数器

enum dim_cq_period_mode

CQ 周期计数的模式

常量

DIM_CQ_PERIOD_MODE_START_FROM_EQE

从 EQE 开始计数

DIM_CQ_PERIOD_MODE_START_FROM_CQE

从 CQE 开始计数(意味着定时器重置)

DIM_CQ_PERIOD_NUM_MODES

模式数量

enum dim_state

DIM 算法状态

常量

DIM_START_MEASURE

这是第一次迭代(也适用于应用新配置文件之后)

DIM_MEASURE_IN_PROGRESS

算法已在进行中 - 检查是否需要执行操作

DIM_APPLY_NEW_PROFILE

DIM 使用者当前正在应用配置文件 - 无需测量

描述

这些将确定算法是否处于可以开始迭代的有效状态。

enum dim_tune_state

DIM 算法调整状态

常量

DIM_PARKING_ON_TOP

算法找到局部最高点 - 在有显着差异时退出

DIM_PARKING_TIRED

算法找到一个深层最高点 - 如果 tired > 0,则不退出

DIM_GOING_RIGHT

算法当前正在尝试更高的调节级别

DIM_GOING_LEFT

算法当前正在尝试更低的调节级别

描述

这些将确定算法应执行的操作。

enum dim_stats_state

DIM 算法统计状态

常量

DIM_STATS_WORSE

当前迭代显示的性能比以前差

DIM_STATS_SAME

当前迭代显示的性能与之前相同

DIM_STATS_BETTER

当前迭代显示的性能比以前好

描述

这些将确定当前迭代的结果。

enum dim_step_result

DIM 算法步进结果

常量

DIM_STEPPED

执行了常规步骤

DIM_TOO_TIRED

多次执行了相同类型的步骤 - 应转到疲劳停车状态

DIM_ON_EDGE

已步进到最左/最右的配置文件

描述

这些描述了步骤的结果。

int net_dim_init_irq_moder(struct net_device *dev, u8 profile_flags, u8 coal_flags, u8 rx_mode, u8 tx_mode, void (*rx_dim_work)(struct work_struct *work), void (*tx_dim_work)(struct work_struct *work))

收集信息以初始化 irq 调节

参数

struct net_device *dev

目标网络设备

u8 profile_flags

Rx 或 Tx 配置文件修改功能

u8 coal_flags

irq 调节参数标志

u8 rx_mode

Rx 的 CQ 周期模式

u8 tx_mode

Tx 的 CQ 周期模式

void (*rx_dim_work)(struct work_struct *work)

DIM 决策后调用的 Rx 工作程序

void (*tx_dim_work)(struct work_struct *work)

DIM 决策后调用的 Tx 工作程序

返回

成功返回 0 或负错误代码。

void net_dim_free_irq_moder(struct net_device *dev)

释放 irq 调节的字段

参数

struct net_device *dev

目标网络设备

void net_dim_setting(struct net_device *dev, struct dim *dim, bool is_tx)

初始化 DIM 的 cq 模式并调度工作线程

参数

struct net_device *dev

目标网络设备

struct dim *dim

DIM 上下文

bool is_tx

true 表示发送方向,false 表示接收方向

void net_dim_work_cancel(struct dim *dim)

同步取消 dim 的工作线程

参数

struct dim *dim

DIM 上下文

struct dim_cq_moder net_dim_get_rx_irq_moder(struct net_device *dev, struct dim *dim)

根据 profile_ix 获取 DIM rx 结果

参数

struct net_device *dev

目标网络设备

struct dim *dim

DIM 上下文

返回

DIM 中断调节

struct dim_cq_moder net_dim_get_tx_irq_moder(struct net_device *dev, struct dim *dim)

根据 profile_ix 获取 DIM tx 结果

参数

struct net_device *dev

目标网络设备

struct dim *dim

DIM 上下文

返回

DIM 中断调节

void net_dim_set_rx_mode(struct net_device *dev, u8 rx_mode)

设置 DIM rx cq 模式

参数

struct net_device *dev

目标网络设备

u8 rx_mode

目标 rx cq 模式

void net_dim_set_tx_mode(struct net_device *dev, u8 tx_mode)

设置 DIM tx cq 模式

参数

struct net_device *dev

目标网络设备

u8 tx_mode

目标 tx cq 模式

bool dim_on_top(struct dim *dim)

检查当前状态是否适合停止(顶部位置)

参数

struct dim *dim

DIM 上下文

描述

检查当前 profile 是否适合停靠。这将减少 DIM 检查频率,因为我们假设除非流量模式发生变化,否则我们可能不应该更改 profile。

void dim_turn(struct dim *dim)

改变 profile 的方向

参数

struct dim *dim

DIM 上下文

描述

如果之前向右移动,则向左移动,反之亦然。如果当前处于停靠状态,则不执行任何操作。

void dim_park_on_top(struct dim *dim)

在顶部位置进入停靠状态

参数

struct dim *dim

DIM 上下文

描述

进入停靠状态。清除所有移动历史记录。

void dim_park_tired(struct dim *dim)

进入疲劳的停靠状态

参数

struct dim *dim

DIM 上下文

描述

进入停靠状态。清除所有移动历史记录并降低 DIM 检查频率。

bool dim_calc_stats(const struct dim_sample *start, const struct dim_sample *end, struct dim_stats *curr_stats)

计算两个采样之间的差异

参数

const struct dim_sample *start

起始采样

const struct dim_sample *end

结束采样

struct dim_stats *curr_stats

采样之间的差值

描述

计算两个采样之间的差值(以数据速率表示)。考虑计数器回绕。返回的布尔值表示 curr_stats 是否可靠。

void dim_update_sample(u16 event_ctr, u64 packets, u64 bytes, struct dim_sample *s)

使用给定的值设置采样的字段

参数

u16 event_ctr

要设置的事件数

u64 packets

要设置的数据包数

u64 bytes

要设置的字节数

struct dim_sample *s

DIM 采样

void dim_update_sample_with_comps(u16 event_ctr, u64 packets, u64 bytes, u64 comps, struct dim_sample *s)

使用给定的值(包括完成参数)设置采样的字段

参数

u16 event_ctr

要设置的事件数

u64 packets

要设置的数据包数

u64 bytes

要设置的字节数

u64 comps

要设置的完成数

struct dim_sample *s

DIM 采样

struct dim_cq_moder net_dim_get_rx_moderation(u8 cq_period_mode, int ix)

为给定的 RX profile 提供 CQ 调节对象

参数

u8 cq_period_mode

CQ 周期模式

int ix

Profile 索引

struct dim_cq_moder net_dim_get_def_rx_moderation(u8 cq_period_mode)

提供默认的 RX 调节

参数

u8 cq_period_mode

CQ 周期模式

struct dim_cq_moder net_dim_get_tx_moderation(u8 cq_period_mode, int ix)

为给定的 TX profile 提供 CQ 调节对象

参数

u8 cq_period_mode

CQ 周期模式

int ix

Profile 索引

struct dim_cq_moder net_dim_get_def_tx_moderation(u8 cq_period_mode)

提供默认的 TX 调节

参数

u8 cq_period_mode

CQ 周期模式

void net_dim(struct dim *dim, const struct dim_sample *end_sample)

DIM 算法的主要入口点

参数

struct dim *dim

DIM 实例信息

const struct dim_sample *end_sample

当前数据测量

描述

由消费者调用。 这是算法的主要逻辑,在此处理数据以决定下一个所需的操作。

void rdma_dim(struct dim *dim, u64 completions)

运行自适应调节。

参数

struct dim *dim

调节结构体。

u64 completions

本轮收集到的完成数。

描述

每次调用 rdma_dim 都会获取最近收集的完成量,并将它们计为一个新事件。 一旦收集到足够的事件,该算法就会决定一个新的调节级别。