カーネルモジュールことはじめ #7

今日は ip_vs_test_schedule() 関数に手をいれます。(コードはこちら
この関数は、IPVSが新しい接続を受け入れる時に呼び出されます。そして接続すべきリアルサーバを指す ip_vs_dest 構造体のポインタを返します。リアルサーバのリストは、引数で渡される ip_vs_service 構造体の destinations に格納されています。このコードでは、リストの先頭から以下の条件を満たすサーバを検索します。

  • weightが0じゃないサーバ
  • 接続数がしきい値(u_threshold)を越えていないサーバ

動作確認

# ipvsadm -A -t 10.211.55.3:21 -s test
# ipvsadm -a -t 10.211.55.3:21 -r 10.211.55.5:21 -x 4 -y 1
# ipvsadm -a -t 10.211.55.3:21 -r 10.211.55.6:21 -x 4 -y 1
# ipvsadm -a -t 10.211.55.3:21 -r 10.211.55.7:21 -x 4 -y 1
# ipvsadm -L -n --thresholds
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port            Uthreshold Lthreshold ActiveConn InActConn
  -> RemoteAddress:Port
TCP  10.211.55.3:21 test
  -> 10.211.55.253:21             4          1          0          0
  -> 10.211.55.252:21             4          1          0          0
  -> 10.211.55.251:21             4          1          0          0
  -> 10.211.55.250:21             4          1          0          0

接続確認

client:$ telnet 10.211.55.3 21
Connected to 10.211.55.3.
Escape character is '^]'.
220 ProFTPD 1.3.0 Server (Debian) [10.211.55.253]

これを5個動かすとIPVSの状態は以下のようになります。

# ipvsadm -L -n --thresholds
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port            Uthreshold Lthreshold ActiveConn InActConn
  -> RemoteAddress:Port
TCP  10.211.55.3:21 test
  -> 10.211.55.253:21             4          1          5          0
  -> 10.211.55.252:21             4          1          0          0
  -> 10.211.55.251:21             4          1          0          0
  -> 10.211.55.250:21             4          1          0          0

リストの先頭のサーバに5セッション張られました。
さらにもうひとつ接続すると、、

# ipvsadm -L -n --thresholds
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port            Uthreshold Lthreshold ActiveConn InActConn
  -> RemoteAddress:Port
TCP  10.211.55.3:21 test
  -> 10.211.55.253:21             4          1          5          0
  -> 10.211.55.252:21             4          1          1          0
  -> 10.211.55.251:21             4          1          0          0
  -> 10.211.55.250:21             4          1          0          0

今度は2番目のサーバに繋ぎにいきました。
では、一番最初に起動した telnet をいったん止めて再接続するとどうなるでしょう。

# ipvsadm -L -n --thresholds
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port            Uthreshold Lthreshold ActiveConn InActConn
  -> RemoteAddress:Port
TCP  10.211.55.3:21 test
  -> 10.211.55.253:21             4          1          4          1
  -> 10.211.55.252:21             4          1          2          0
  -> 10.211.55.251:21             4          1          0          0
  -> 10.211.55.250:21             4          1          0          0

接続が切れても 10.211.55.253 は OverLoadのままなので 10.211.55.252 に繋がります。
どうやら想定通りの動きをしてくれているようです。


ソースコード(ip_vs_test.c)

#include <linux/module.h>
#include <linux/kernel.h>
#include <net/ip_vs.h>

static int ip_vs_test_init_svc(struct ip_vs_service *svc)
{
  IP_VS_DBG(7, "ip_vs_test: init_svc\n");
  return 0;
}

static int ip_vs_test_done_svc(struct ip_vs_service *svc)
{
  IP_VS_DBG(7, "ip_vs_test: done_svc\n");
  return 0;
}

static int ip_vs_test_update_svc(struct ip_vs_service *svc)
{
  IP_VS_DBG(7, "ip_vs_test: update_svc\n");
  return 0;
}

static struct ip_vs_dest *
ip_vs_test_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
{
  struct ip_vs_dest *dest;

  list_for_each_entry(dest, &svc->destinations, n_list){
    if(atomic_read(&dest->weight)){
      if(dest->flags & IP_VS_DEST_F_OVERLOAD){
        /* OverLoad */
      }else{
        return(dest);
      }
    }
  }

  return(NULL) ;
}

static struct ip_vs_scheduler ip_vs_test_scheduler = {
  .name           = "test",
  .refcnt         = ATOMIC_INIT(0),
  .module         = THIS_MODULE,
  .init_service   = ip_vs_test_init_svc,
  .done_service   = ip_vs_test_done_svc,
  .update_service = ip_vs_test_update_svc,
  .schedule       = ip_vs_test_schedule,
};

static int __init ip_vs_test_init(void)
{
  INIT_LIST_HEAD(&ip_vs_test_scheduler.n_list);
  return register_ip_vs_scheduler(&ip_vs_test_scheduler) ;
}

static void __exit ip_vs_test_cleanup(void)
{
  unregister_ip_vs_scheduler(&ip_vs_test_scheduler);
}

module_init(ip_vs_test_init);
module_exit(ip_vs_test_cleanup);
MODULE_LICENSE("GPL");