UPF 定时器使用(以节点故障报告流程为例)

节点故障报告

UPF 与 SMF 建立好偶联之后,需要定时向 SMF 发送心跳保活消息 heartbeat request , 如果 SMF 连续无响应次数达到上限,则偶联断链, UPF 本地记录 “ SMF 节点不可达”告警。

该功能需要 UPF 通过定时器方式实现定时发送心跳保活消息,并且 UPF 需支持设置心跳保活消息重发上限次数,当达到上限次数 SMF 仍无响应, UPF 需要释放偶联、删除会话、登记 SMF 节点不可达日志。

代码设计

1. 实现原理

1.1 定时器原理

image-20210414174509439

interval 是时间轮, tickMs 是时间轮轮转一个槽位的时间, wheelSize 是时间轮的大小,表示由多少个槽位组成。当定时任务的时间超出一个时间轮,则升级到第二层时间轮中,第二层的 tickMs 等于上一层的 interval ,时间轮的大小 wheelSize 保持不变。如果第二层不够则再升级到第三层,以此类推。 vpp 中时间轮的数量最多支持3个,每个轮的环槽数量必须是2的幂。

1.2 UPF N4 接口定时器

[1] 定时器初始化:

初始化一个时间轮槽数为1024,时间轮数量是3,每个对象的句柄的占用比特数量是1的定时器:

# file: upf.c
upf_init
 |_ tw_timer_wheel_init_1t_3w_1024sl_ov

[2] 添加计时任务:

# file: upf_pfcp_server.c
upf_pfcp_session_start_stop_urr_time
 |_ tw_timer_start_1t_3w_1024sl_ov

[3] 删除计时任务:

# file: upf_pfcp_server.c
upf_pfcp_session_stop_urr_time
 |_ tw_timer_stop_1t_3w_1024sl_ov

[4] 周期性调用查看是否过期事件:

# file: upf_pfcp_server.c
pfcp_timer_process
 |_ tw_timer_expire_timers_vec_1t_3w_1024sl_ov

[5] 计时时间到,调用回调处理函数:

# file: upf_pfcp_server.c
pfcp_timer_process
 |_ for (int i = 0; i < vec_len (expired); i++)
    |_ pool_index = expired[i] & 0x0FFFFFFF;
    |_ timer_id = expired[i] >> 28;
    |_ sx = pool_elt_at_index (um->sessions, si);

2、代码设计

在定时器处理的函数中增加当心跳包重发次数达到上限时断开偶联和打印故障记录操作。

# file: upf_pfcp_server.c
VLIB_REGISTER_NODE (pfcp_timer_node, static) = {
    .function = pfcp_timer_process,
    .name = "pfcp-timer",
    .type = VLIB_NODE_TYPE_INPUT,
    .state = VLIB_NODE_STATE_INTERRUPT,
};

# 定时器
pfcp_timer_node
 |_ pfcp_timer_process
    |_ upf_get_per_pfcp
    |_ expired = tw_timer_expire_timers_vec_1t_3w_1024sl_ov
    |_ foreach ( expired )
       |_ timer_id = expired[i] >> 28;
       |_ switch (timer_id)
         |_ case PFCP_SERVER_HB_TIMER
         |  |_ upf_server_send_heartbeat (pool_index)
         |     |_ upf_pfcp_server_send_node_request
         |        |_ upf_pfcp_server_send_request
         |           |_ enqueue_request (msg, gtm->pfcp_retry_n1, gtm->pfcp_t1_timeout);
         |              |_ msg->n1 = n1; # 设置重复发送心跳包次数
         |_ case PFCP_SERVER_T1
       	      |_ request_t1_expired (pool_index); # 目标函数
       		     |_ if (msg->n1-- != 0) # 统计次数不为0
       			 |  |_ upf_pfcp_server_start_timer
       			 |  |_ upf_pfcp_send_data
       			 |_ else # 统计次数达到上限
       				|_ if (type == PFCP_HEARTBEAT_REQUEST && …… )
       				   |_ sx_release_association # 释放节点相关会话
       				   |_ upf_debug # 节点故障记录打印节点编号和 IP 地址

其中发送心跳包次数上限通过命令行设置:

upf pfcp_retry timeout 2 retry_n1 4

其中 timeout 参数是时间间隔, retry_n1 参数是上限次数减一。命令行实现代码:

# fille: upf.c
# 设置 retry 次数
upf_pfcp_retry_command_fn
 |_ unformat (line_input, "timeout %d", &um->pfcp_t1_timeout)
 |_ unformat (line_input, "retry_n1 %d", &um->pfcp_retry_n1) # 设置重发次数