rmmodできないLKMをrmmodする


カーネルを調べるにあたって、LKMは欠かせません。しかしそれだけに、モジュール初期化の段階で強制終了してしまって、再コンパイルにあたりMakefileでモジュール名を変更しながら、再度insmodしたりしています。この際このあたりの仕組みの調査兼ねて、rmmodできなくなったモジュールをrmmodできるLKMを作成しました。

モジュールのロードは、 init_module()で行われます。ここからload_module()がコールされ、struct module *modnに、ロードするモジュールを取得/設定いたします。この時mod->state = MODULE_STATE_COMINGとしています。そこから復帰して、モジュール参照カンターを、インクリメント(正確には参照カウンターをインクリメント)、LKMソースのmodule_init()で定義する関数が起動されます。その結果が0以上(正の場合、KERN_WARNINGとしてスタックをダンプするだけです。)なら、mod->state = MODULE_STATE_LIVEとし、またモジュール参照カンターを、デクリメント(正確には非参照カウンターをインクリメント)いたします。

なお負の値を帰り値とした場合、モジュールはlkmとして登録されません。1発処理のlkmでは、返り値をマイナスで返したほうが、改めてrmmodしてinsmodする手間が省けます。訝しげなエラーがでますが、これはinsmodコマンドで出力しているだけで、lkmとしてシステム的な問題はありません。ただカーネルに登録されないことだけです。ここではreturn -1としています。

module_init()の処理で強制終了すると、参照状態のまま、mod->stateはMODULE_STATE_COMINGのままになってしまいます。

rmmodコマンドはシステムコールdelete_moduleが呼ばれます。解放チェックは、mod->state = MODULE_STATE_LIVE、module_xit()が定義されている。(されてない場合コンパイルオプションにより強制的に解放することができます。)、そして参照カンターが0(正確には参照カウンター - 参照終了したカウンター = 0)の時です。
SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
               unsigned int, flags)
{
   :
       mod = find_module(name);
   :
       if (mod->state != MODULE_STATE_LIVE) {
               DEBUGP("%s already dying\n", mod->name);
               ret = -EBUSY;
               goto out;
       }
   :
       if (mod->init && !mod->exit) {
               forced = try_force_unload(flags);
               if (!forced) {
                       /* This module can't be removed */
                       ret = -EBUSY;
                       goto out;
               }
       }
   :
       if (!forced && module_refcount(mod) != 0)
               wait_for_zero_refcount(mod);
   :
}

[root@localhost kitamura]# cat /proc/modules
decmod 648 0 - Live 0xd1986000
bug_mod1 1591 1 - Loading 0xd13d0000
bug_mod2 1579 0 [permanent] Live 0xd14c0000
 :
ipv6 229420 28 - Live 0xd11bc000
uinput 5228 0 - Live 0xd106d000
ppdev 6808 0 - Live 0xd1010000
 :
順に、名前/サイズ/参照数/モード/状態/アドレスとなります。rmmodするにはモジュールの参照数が0で、状態がLiveである必要があります。rmmodできないモジュールはどちらかが、そうなっていないからです。なお[permanent]はmodule_exit()を定義していないモジュールで、このモジュールは原則rmmodできません。

下記は上記設定をリセットするLKMです。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/init.h>

MODULE_AUTHOR("y.kitamura");
MODULE_DESCRIPTION("pre_rmmod");
MODULE_LICENSE("GPL");

static char* name = "";
module_param(name, charp, S_IRUGO);

static int __init pre_rmmod_init(void)
{
   truct module* mod;

   if (name == NULL) {
       printk("insmod pre_rmmod.ko name=module name\n");
       return -1;
   }
   mod = find_module(name);
   if (!mod) {
       printk("not found %s\n", name);
       return -1;
   }
   if (module_refcount(mod)) {
       module_put(mod);
   }
   mod->state = MODULE_STATE_LIVE;
   printk("%s:%d\n", mod->name,  module_refcount(mod));
   return -1;
}
module_init(pre_rmmod_init);

使用例

bug_mod1はrmmodできません。引数にbug_mod1でpre_rmmod.koをinsmodします。エラーが出ますが問題ありません。pre_rmmod.koはlkmとしてカーネルに登録されません。mmod bug_mod1.koでbug_mod1.koが削除されました。
[root@localhost kitamura]# rmmod bug_mod1.ko                        
ERROR:Module bug_mod1 is in use
[root@localhost kitamura]# insmod pre_rmmod.ko name=bug_mod1
insmod: error inserting 'pre_rmmod.ko': -1 Operation not permitted
[root@localhost kitamura]# rmmod bug_mod1.ko
[root@localhost kitamura]#

追記

fedora14では問題ありませんでしたが、fedora10ではfind_moduleがEXPORTされてないので、コンパイルできませんでした。

最終更新 2012/02/17 01:31:22 - north
(2012/02/02 01:52:26 作成)


検索

アクセス数
3689334
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。