一、bridge_run 交换机运行模块
书接上回 vswitchd 交换机功能实现(上) 我们继续探讨 vswitchd 交换机功能实现的关键内容。通过之前的介绍可以看到,交换机运行模块 bridge_run() 在守护进程的 while 主循环中运行,是负责维护交换机状态和转发功能的主要模块,这里对它调用的函数和方法展开讲解。(此处为了节约空间,对代码做了省略和细微调整)
(1)ofproto 初始化模块 bridge_init_ofproto()
首先 bridge_init_ofproto(cfg) 函数也定义在 ovs-main/vswitchd/bridge.c 文件中,并且其中的输入参数 cfg 是 bridge_run() 函数运行时获取的指向数据库表的指针:
cfg = ovsrec_open_vswitch_first(idl);
也就是说,它包含了 open_vswitch 行的所有数据,可以理解为从 ovsdb 数据库中获取的第一个 open_vswitch 行的数据结构。
static void bridge_init_ofproto(const struct ovsrec_open_vswitch *cfg) {
struct shash iface_hints;
static bool initialized = false;
int i;
if (initialized) {
return;
}
shash_init(&iface_hints);
if (cfg) {
for (i = 0; i < cfg->n_bridges; i++) {
const struct ovsrec_bridge *br_cfg = cfg->bridges[i];
int j;
for (j = 0; j < br_cfg->n_ports; j++) {
struct ovsrec_port *port_cfg = br_cfg->ports[j];
int k;
for (k = 0; k < port_cfg->n_interfaces; k++) {
struct ovsrec_interface *if_cfg = port_cfg->interfaces[k];
struct iface_hint *iface_hint;
iface_hint = xmalloc(sizeof *iface_hint);
iface_hint->br_name = br_cfg->name;
iface_hint->br_type = br_cfg->datapath_type;
iface_hint->ofp_port = iface_pick_ofport(if_cfg);
shash_add(&iface_hints, if_cfg->name, iface_hint);
}
}
}
}
ofproto_init(&iface_hints);
shash_destroy_free_data(&iface_hints);
initialized = true;
}
这段代码的主要工作是从 Open vSwitch 数据库中读取网桥、端口和接口的配置信息,构建相应信息的哈希表,并将其传递给 OpenFlow 相关的初始化函数,完成 OpenFlow 交换机(ofproto)的初始化工作。
这里对 ofproto_init() 函数展开,相应代码存储在 ovs-main/ofproto/ofproto.c 文件中:
/* Must be called to initialize the ofproto library.
* The caller may pass in 'iface_hints', which contains an shash of "iface_hint" elements indexed by the interface's name.
* The provider may use these hints to describe the startup configuration in order to reinitialize its state.
* The caller owns the provided data, so a provider will make copies of anything required.
* An ofproto provider will remove any existing state that is not described by the hint, and may choose to remove it all. */
void ofproto_init(const struct shash *iface_hints) {
struct shash_node *node;
size_t i;
ofproto_class_register(&ofproto_dpif_class);
/* Make a local copy, since we don't own 'iface_hints' elements. */
SHASH_FOR_EACH(node, iface_hints) {
const struct iface_hint *orig_hint = node->data;
struct iface_hint *new_hint = xmalloc(sizeof *new_hint);
const char *br_type = ofproto_normalize_type(orig_hint->br_type);
new_hint->br_name = xstrdup(orig_hint->br_name);
new_hint->br_type = xstrdup(br_type);
new_hint->ofp_port = orig_hint->ofp_port;
shash_add(&init_ofp_ports, node->name, new_hint);
}
for (i = 0; i < n_ofproto_classes; i++) {
ofproto_classes[i]->init(&init_ofp_ports);
}
ofproto_unixctl_init();
}
这个函数首先进行 ofproto class 类的注册,并创建存储相应信息的哈希表。接下来遍历所有的 ofproto 并调用 ofproto_classes[i]->init 完成 ofproto 的初始化。最后还初始化了 ofproto 的 unixctl 子系统。
(2)交换机运行模块 bridge_run__()
函数 bridge_run__() 主要负责运行 ofproto 子系统的各个部分,定义在 ovs-main/vswitchd/bridge.c 文件中:
static void bridge_run__(void) {
struct bridge *br;
struct sset types;
const char *type;
/* Let each datapath type do the work that it needs to do. */
sset_init(&types);
ofproto_enumerate_types(&types);
SSET_FOR_EACH (type, &types) {
ofproto_type_run(type);
}
sset_destroy(&types);
/* Let each bridge do the work that it needs to do. */
HMAP_FOR_EACH (br, node, &all_bridges) {
ofproto_run(br->ofproto);
}
}
bridge_run__() 函数首先遍历所有 ofproto 类型,调用 ofproto_enumerate_types() 函数,获取所有已注册的 ofproto 类型,并将它们添加到 types 集合中,然后函数遍历 types 集合中的每个 ofproto 类型,并调用 ofproto_type_run() 函数,让每个 ofproto 执行相应的操作。接下来遍历所有 bridge 实例,对于每个 bridge 实例调用 ofproto_run() 函数,让每个 bridge 实例对应的 ofproto 实例执行相应的操作。总的来说,这个函数主要负责协调 ofproto 子系统的整体运行,它首先让每个已注册的 ofproto 类型执行相应的操作,然后让每个 bridge 实例也执行相应的操作(这里是先操作类型,再操作实例,注意二者的区别和顺序),这确保了 ofproto 子系统中的所有组件都能正常运行和协作。
(3)数据库同步模块 bridge_reconfigure()
void bridge_run(void) {
......
if (ovsdb_idl_get_seqno(idl) != idl_seqno || if_notifier_changed(ifnotifier)) {
struct ovsdb_idl_txn *txn;
idl_seqno = ovsdb_idl_get_seqno(idl);
txn = ovsdb_idl_txn_create(idl);
bridge_reconfigure(cfg ? cfg : &null_cfg);
......
}
......
}
在 bridge 的运行过程即 bridge_run() 函数中,使用 bridge_reconfigure() 函数实现实际配置信息和数据库信息的同步也是非常重要的一个部分,bridge_reconfigure() 函数也定义在 ovs-main/vswitchd/bridge.c 文件中:
static void bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) {
......
/* Destroy "struct bridge"s, "struct port"s, and "struct iface"s according to 'ovs_cfg', with only very minimal configuration otherwise.
* This is mostly an update to bridge data structures.
* Nothing is pushed down to ofproto or lower layers. */
add_del_bridges(ovs_cfg);
HMAP_FOR_EACH (br, node, &all_bridges) {
bridge_collect_wanted_ports(br, &br->wanted_ports);
bridge_del_ports(br, &br->wanted_ports);
}
/* Start pushing configuration changes down to the ofproto layer:
* - Delete ofprotos that are no longer configured.
* - Delete ports that are no longer configured.
* - Reconfigure existing ports to their desired configurations, or delete them if not possible.
* We have to do all the deletions before we can do any additions, because the ports to be added might require resources that will be freed up by deletions (they might especially overlap in name). */
bridge_delete_ofprotos();
HMAP_FOR_EACH (br, node, &all_bridges) {
if (br->ofproto) {
bridge_delete_or_reconfigure_ports(br);
}
}
/* Finish pushing configuration changes to the ofproto layer:
* - Create ofprotos that are missing.
* - Add ports that are missing. */
HMAP_FOR_EACH_SAFE (br, node, &all_bridges) {
if (!br->ofproto) {
int error;
error = ofproto_create(br->name, br->type, &br->ofproto);
if (error) {
VLOG_ERR("failed to create bridge %s: %s", br->name,
ovs_strerror(error));
shash_destroy(&br->wanted_ports);
bridge_destroy(br, true);
} else {
/* Trigger storing datapath version. */
seq_change(connectivity_seq_get());
}
}
}
config_ofproto_types(&ovs_cfg->other_config);
HMAP_FOR_EACH (br, node, &all_bridges) {
bridge_add_ports(br, &br->wanted_ports);
shash_destroy(&br->wanted_ports);
}
reconfigure_system_stats(ovs_cfg);
datapath_reconfigure(ovs_cfg);
/* Complete the configuration. */
sflow_bridge_number = 0;
collect_in_band_managers(ovs_cfg, &managers, &n_managers);
HMAP_FOR_EACH (br, node, &all_bridges) {
......
bridge_configure_mirrors(br);
bridge_configure_forward_bpdu(br);
bridge_configure_mac_table(br);
bridge_configure_mcast_snooping(br);
bridge_configure_remotes(br, managers, n_managers);
bridge_configure_netflow(br);
bridge_configure_sflow(br, &sflow_bridge_number);
bridge_configure_ipfix(br);
bridge_configure_spanning_tree(br);
bridge_configure_tables(br);
bridge_configure_dp_desc(br);
bridge_configure_serial_desc(br);
bridge_configure_aa(br);
}
......
bridge_run__();
}
这里首先调用 add_del_bridges() 函数,根据数据库中的记录添加或删除 bridge,并遍历每个 bridge 对 port 进行添加或删除(从数据库同步 bridge 相关信息)。然后调用 bridge_delete_ofprotos() 和 ofproto_create() 函数,根据数据库中的记录添加或删除 ofproto,并遍历每个 ofproto 对 port 进行添加或删除(从数据库同步 ofproto 相关信息)。接下来对 bridge 进行各种配置,在所有配置运行完毕后,再次调用 bridge_run__() 函数,运行相应的 ofproto 子系统(确保配置生效)。
结语:
由于本人水平有限,以上内容如有不足之处欢迎大家指正(评论区/私信均可)。
参考资料:
Open vSwitch 源码阅读笔记(7)vswitchd 交换机功能实现(上)-优快云博客