Open vSwitchのソースコードを読む(6) ovs-vswitchd の bridge
2013-06-17 by Daisuke Kotani前回はmain loopの流れを一通り眺めましたが、長い関数(bridge, ofproto, ofproto-dpif, netdev)は後回しにしていました。今回はbridge_run関数、bridge_run_fast関数、bridge_wait関数を少し詳しく見ていきます。
bridgeとは
Open vSwitch内では、いくつもの仮想スイッチのようなもの(それぞれ別々のポートが割り当てられていて、別々のOpenFlow Controllerとのコネクションを持つことができます)が作られています。それぞれの仮想スイッチをbridgeと呼びます。
OpenFlow Controller OpenFlow Controller
| |
OVS------------|----------------------------|------------+
| +----------------------+ +----------------------+ |
| | Virtual Switch | | Virtual Switch | |
| | (bridge 1) | | (bridge 2) | |
| +----------------------+ +----------------------+ |
+------|-----|----|----|---------|----|-----|------|----+
Port Port Port Port Port Port Port Port
bridge_run
bridge_run関数はvswitchd/bridge.cにあります。ちょっと長いです。
void
bridge_run(void)
{
static struct ovsrec_open_vswitch null_cfg;
const struct ovsrec_open_vswitch *cfg;
struct ovsdb_idl_txn *reconf_txn = NULL;
struct sset types;
const char *type;
bool vlan_splinters_changed;
struct bridge *br;
ovsrec_open_vswitch_init(&null_cfg);
/* (Re)configure if necessary. */
if (!reconfiguring) {
ovsdb_idl_run(idl);
if (ovsdb_idl_is_lock_contended(idl)) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
struct bridge *br, *next_br;
VLOG_ERR_RL(&rl, "another ovs-vswitchd process is running, "
"disabling this process until it goes away");
HMAP_FOR_EACH_SAFE (br, next_br, node, &all_bridges) {
bridge_destroy(br);
}
/* Since we will not be running system_stats_run() in this process
* with the current situation of multiple ovs-vswitchd daemons,
* disable system stats collection. */
system_stats_enable(false);
return;
} else if (!ovsdb_idl_has_lock(idl)) {
return;
}
}
ここまでは、他のovs-vswitchdが動いていないかチェックしているところ。
cfg = ovsrec_open_vswitch_first(idl);
/* Initialize the ofproto library. This only needs to run once, but
* it must be done after the configuration is set. If the
* initialization has already occurred, bridge_init_ofproto()
* returns immediately. */
bridge_init_ofproto(cfg);
ofproto libraryの初期化をしています。bridge_init_ofprotoは前々回読みました。
/* 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);
datapath typeを抽出し、それぞれのtypeを引数にしてofproto_type_runを呼び出しています。ofproto_type_runはofproto/ofproto-dpif.cに書かれています。
int
ofproto_type_run(const char *datapath_type)
{
const struct ofproto_class *class;
int error;
datapath_type = ofproto_normalize_type(datapath_type);
class = ofproto_class_find__(datapath_type);
error = class->type_run ? class->type_run(datapath_type) : 0;
if (error && error != EAGAIN) {
VLOG_ERR_RL(&rl, "%s: type_run failed (%s)",
datapath_type, strerror(error));
}
return error;
}
引数で指定されたtypeのtype_runの関数を実行しているだけのようです。これより深いところはofprotoやofproto-dpifのところで読みます。 main loopに戻ります。
/* Let each bridge do the work that it needs to do. */
HMAP_FOR_EACH (br, node, &all_bridges) {
ofproto_run(br->ofproto);
}
ofproto_run関数を実行しているようです。この関数の中はofprotoのところで読みます。
/* Re-configure SSL. We do this on every trip through the main loop,
* instead of just when the database changes, because the contents of the
* key and certificate files can change without the database changing.
*
* We do this before bridge_reconfigure() because that function might
* initiate SSL connections and thus requires SSL to be configured. */
if (cfg && cfg->ssl) {
const struct ovsrec_ssl *ssl = cfg->ssl;
stream_ssl_set_key_and_cert(ssl->private_key, ssl->certificate);
stream_ssl_set_ca_cert_file(ssl->ca_cert, ssl->bootstrap_ca_cert);
}
SSL関連の処理ですが、今回は無視します。
if (!reconfiguring) {
/* If VLAN splinters are in use, then we need to reconfigure if VLAN
* usage has changed. */
vlan_splinters_changed = false;
if (vlan_splinters_enabled_anywhere) {
HMAP_FOR_EACH (br, node, &all_bridges) {
if (ofproto_has_vlan_usage_changed(br->ofproto)) {
vlan_splinters_changed = true;
break;
}
}
}
if (ovsdb_idl_get_seqno(idl) != idl_seqno || vlan_splinters_changed) {
idl_seqno = ovsdb_idl_get_seqno(idl);
if (cfg) {
reconf_txn = ovsdb_idl_txn_create(idl);
bridge_reconfigure(cfg);
} else {
/* We still need to reconfigure to avoid dangling pointers to
* now-destroyed ovsrec structures inside bridge data. */
bridge_reconfigure(&null_cfg);
}
}
}
reconfiguringがfalseのときに、VLAN splinterの再設定をします。
VLAN splinterはVLANをサポートしていない古いLinuxのために用意されているもので、仮想的にtag VLANを実現しています(manの記述。manを読む限りでは、tagを使いたいポートに対して、そのポートに出すVLAN一つ一つに対応する仮想インターフェイスを作っているようです。VLANの設定が変更になったらその仮想ポートを変更しないといけないので、このような処理が入っているのではないかと思います。それに従ってbridgeの設定も変更しなければならない、と。
bridge_reconfigure関数はvswitchd/bridge.cにあります。
static void
bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
{
unsigned long int *splinter_vlans;
struct bridge *br;
COVERAGE_INC(bridge_reconfigure);
ovs_assert(!reconfiguring);
reconfiguring = true;
/* Destroy "struct bridge"s, "struct port"s, and "struct iface"s according
* to 'ovs_cfg' while update the "if_cfg_queue", 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);
splinter_vlans = collect_splinter_vlans(ovs_cfg);
HMAP_FOR_EACH (br, node, &all_bridges) {
bridge_add_del_ports(br, splinter_vlans);
}
free(splinter_vlans);
add_del_bridges関数は、bridgeのデータを新しいものに更新する関数、collect_splinter_vlansはVLAN splinter機能で使われているVLANを抽出する関数、bridge_add_del_portsはそれぞれのbridgeに所属するポートのデータを更新する関数。
/* Delete datapaths that are no longer configured, and create ones which
* don't exist but should. */
bridge_update_ofprotos();
/* Make sure each "struct iface" has a correct ofp_port in its ofproto. */
HMAP_FOR_EACH (br, node, &all_bridges) {
bridge_refresh_ofp_port(br);
}
/* Clear database records for "if_cfg"s which haven't been instantiated. */
HMAP_FOR_EACH (br, node, &all_bridges) {
struct if_cfg *if_cfg;
HMAP_FOR_EACH (if_cfg, hmap_node, &br->if_cfg_todo) {
iface_clear_db_record(if_cfg->cfg);
}
}
reconfigure_system_stats(ovs_cfg);
}
ここら辺はbridge関連のデータを再設定しているところでしょうか。コメントを読むとそういう気がします。
if (reconfiguring) {
if (!reconf_txn) {
reconf_txn = ovsdb_idl_txn_create(idl);
}
if (bridge_reconfigure_continue(cfg ? cfg : &null_cfg)) {
reconfiguring = false;
if (cfg) {
ovsrec_open_vswitch_set_cur_cfg(cfg, cfg->next_cfg);
}
/* If we are completing our initial configuration for this run
* of ovs-vswitchd, then keep the transaction around to monitor
* it for completion. */
if (!initial_config_done) {
initial_config_done = true;
daemonize_txn = reconf_txn;
reconf_txn = NULL;
}
}
}
if (reconf_txn) {
ovsdb_idl_txn_commit(reconf_txn);
ovsdb_idl_txn_destroy(reconf_txn);
reconf_txn = NULL;
}
if (daemonize_txn) {
enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(daemonize_txn);
if (status != TXN_INCOMPLETE) {
ovsdb_idl_txn_destroy(daemonize_txn);
daemonize_txn = NULL;
/* ovs-vswitchd has completed initialization, so allow the
* process that forked us to exit successfully. */
daemonize_complete();
VLOG_INFO_ONCE("%s (Open vSwitch) %s", program_name, VERSION);
}
}
ここまではOpen vSwitchのデータベースを更新している部分(たぶん)。 あまり興味がないので適当です(^^;
/* Refresh interface and mirror stats if necessary. */
if (time_msec() >= iface_stats_timer) {
if (cfg) {
struct ovsdb_idl_txn *txn;
txn = ovsdb_idl_txn_create(idl);
HMAP_FOR_EACH (br, node, &all_bridges) {
struct port *port;
struct mirror *m;
HMAP_FOR_EACH (port, hmap_node, &br->ports) {
struct iface *iface;
LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
iface_refresh_stats(iface);
iface_refresh_status(iface);
}
}
HMAP_FOR_EACH (m, hmap_node, &br->mirrors) {
mirror_refresh_stats(m);
}
}
refresh_controller_status();
ovsdb_idl_txn_commit(txn);
ovsdb_idl_txn_destroy(txn); /* XXX */
}
iface_stats_timer = time_msec() + IFACE_STATS_INTERVAL;
}
これは interface status を更新している部分。iface_refresh_stats, iface_refresh_status, mirror_refresh_stats が値を更新している関数のようです。どれもvswitchd/bridge.cの中にあります。
run_system_stats();
instant_stats_run();
}
run_system_stats はシステムの状態を収集してデータベースをアップデートする関数です。内部ではsystem_stats_run を経由してLoad Averageやメモリ使用量などを収集しています。
instant_stats_run は、ソースコードのコメントによると、「状態が変更されたらすぐにデータベースに反映しなければならないものを更新するもの」だそうです。実際に instant_stats_run 関数の中を読んでみると、STP, LACP, インターフェイスのup/downなどが更新されています。vswitchd/bridge.c にあります。
bridge_run_fast
bridge_run_fast関数はvswitchd/bridge.cにあります。
/* Performs periodic activity required by bridges that needs to be done with
* the least possible latency.
*
* It makes sense to call this function a couple of times per poll loop, to
* provide a significant performance boost on some benchmarks with ofprotos
* that use the ofproto-dpif implementation. */
void
bridge_run_fast(void)
{
struct sset types;
const char *type;
struct bridge *br;
sset_init(&types);
ofproto_enumerate_types(&types);
SSET_FOR_EACH (type, &types) {
ofproto_type_run_fast(type);
}
sset_destroy(&types);
HMAP_FOR_EACH (br, node, &all_bridges) {
ofproto_run_fast(br->ofproto);
}
}
ofproto_enumerate_typesで何かの種類を列挙して、それぞれに対してofproto_type_run_fast関数を呼び出し、最後にそれぞれのbridgeのofprotoに対してofproto_run_fastを実行しているようです。次回以降、それぞれの関数をもう少し詳しく見てみます。
bridge_wait
bridge_wait 関数は vswitchd/bridge.c にあります。
void
bridge_wait(void)
{
struct sset types;
const char *type;
ovsdb_idl_wait(idl);
if (daemonize_txn) {
ovsdb_idl_txn_wait(daemonize_txn);
}
if (reconfiguring) {
poll_immediate_wake();
}
sset_init(&types);
ofproto_enumerate_types(&types);
SSET_FOR_EACH (type, &types) {
ofproto_type_wait(type);
}
sset_destroy(&types);
if (!hmap_is_empty(&all_bridges)) {
struct bridge *br;
HMAP_FOR_EACH (br, node, &all_bridges) {
ofproto_wait(br->ofproto);
}
poll_timer_wait_until(iface_stats_timer);
}
system_stats_wait();
instant_stats_wait();
}
何か処理することがあればpollのtimeoutを0にして次のループで処理されるように、そうでなければ待ち、という処理をしているようです。ofproto_type_waitやofproto_waitはofprotoの時に読みます。
- Open vSwitchのソースコードを読む(11) flow table のエントリ
- Open vSwitchのソースコードを読む(10) flow table の検索
- Open vSwitchのソースコードを読む(9) dpif
- Open vSwitchのソースコードを読む(8) OpenFlow channel の管理
- Open vSwitchのソースコードを読む(7) ovs-vswitchd の ofproto
Tweet