debugfs


debugfsはprocfsのように、カーネル内部情報を参照するもので、本質機能の目的はprocfsと同じです。ただ実装の仕方がprocfsのような実装上の制約がなく、通常のファイルイメージでファイルを作成するように、ファイルを作成することができます。そしてファイル単位に、ファイルオペレーションコールバック関数を設定可能となります。

カーネルは/procのようにマウントまで行っていません。マウントはユーザランドの操作となり、従ってシステム依存になります。tools/perf/util/debugfs.cにデフォルトで
static const char *debugfs_known_mountpoints[] = {
       "/sys/kernel/debug/",
       "/debug/",
       0,
};
とされていて、通常は/sys/kernel/debugにマウントされています。
サンプルです。
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <asm/uaccess.h>

MODULE_LICENSE("GPL");

static struct dentry *hogehoge_root;
static  char    hoge_buff[128];

static ssize_t
debugfs_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
{
       return simple_read_from_buffer(buf, len, ppos, hoge_buff, strlen(hoge_buff));
}

static ssize_t
debugfs_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{
       return simple_write_to_buffer(hoge_buff, sizeof(hoge_buff), ppos, buf, len);
}

static struct file_operations hoge_fops = {
       .owner = THIS_MODULE,
       .read = debugfs_read,
       .write = debugfs_write,
};

static int debugfs_init_module(void)
{
       hogehoge_root = debugfs_create_dir("a-hogehoge", NULL);
       if (!hogehoge_root)
               return -ENOMEM;

       if (!debugfs_create_file("test", 0444,
                      hogehoge_root, NULL, &hoge_fops))
               return -ENOMEM;
       return 0;
}

static void debugfs_exit_module(void)
{
       debugfs_remove_recursive(hogehoge_root);
}

module_init(debugfs_init_module);
module_exit(debugfs_exit_module);
実効結果
[root@localhost lkm]# insmod debugfs.ko
[root@localhost lkm]# ls /sys/kernel/debug/
a-hogehoge  boot_params    extfrag  kprobes  sched_features  usb           wakeup_sources
acpi        cxgb4          gpio     mce      suspend_stats   virtio-ports  x86
bdi         dynamic_debug  hid      regmap   tracing         vmmemctl
[root@localhost lkm]# ls /sys/kernel/debug/a-hogehoge/
test
[root@localhost lkm]# echo "abcde" > /sys/kernel/debug/a-hogehoge/test
[root@localhost lkm]# cat /sys/kernel/debug/a-hogehoge/test
abcde
debugfs_create_dir()は、debugfsにディレクトリを作成します。parentは作成する親ディレクトリです。NULLの場合、debugfsのroot(/sys/kernel/debug)に作成されます。
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
{
       return debugfs_create_file(name, 
                                  S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
                                  parent, NULL, NULL);
}
debugfs_create_file()は、ディレクトリ/ファイル/リンク作成のすべてにおいて、modeを該当値に設定することでコールされます。simple_pin_fs()はdebugfsのmnt構造体の参照カウンターをインクリメントし、debugfs_create_by_name()で対応するinodeおよびdentryを取得します。
struct dentry *debugfs_create_file(const char *name, umode_t mode,
                                  struct dentry *parent, void *data,
                                  const struct file_operations *fops)
{
       struct dentry *dentry = NULL;
       int error;

       pr_debug("debugfs: creating file '%s'\n",name);

       error = simple_pin_fs(&debug_fs_type, &debugfs_mount,
                             &debugfs_mount_count);
       if (error)
               goto exit;

       error = debugfs_create_by_name(name, mode, parent, &dentry,
                                      data, fops);
       if (error) {
               dentry = NULL;
               simple_release_fs(&debugfs_mount, &debugfs_mount_count);
               goto exit;
       }
exit:
       return dentry;
}
debugfs_create_by_name()でmodeに対応するdentryを取得します。
static int debugfs_create_by_name(const char *name, umode_t mode,
                                 struct dentry *parent,
                                 struct dentry **dentry,
                                 void *data,
                                 const struct file_operations *fops)
{
       int error = 0;

       if (!parent)
               parent = debugfs_mount->mnt_root;

       *dentry = NULL;
       mutex_lock(&parent->d_inode->i_mutex);
       *dentry = lookup_one_len(name, parent, strlen(name));
       if (!IS_ERR(*dentry)) {
               switch (mode & S_IFMT) {
               case S_IFDIR:
                       error = debugfs_mkdir(parent->d_inode, *dentry, mode,
                                             data, fops);
                       break;
               case S_IFLNK:
                       error = debugfs_link(parent->d_inode, *dentry, mode,
                                            data, fops);
                       break;
               default:
                       error = debugfs_create(parent->d_inode, *dentry, mode,
                                              data, fops);
                       break;
               }
               dput(*dentry);
       } else
               error = PTR_ERR(*dentry);
       mutex_unlock(&parent->d_inode->i_mutex);

       return error;
}
debugfs_get_inode()は、debugfs_mkdir()/debugfs_link()/debugfs_create()でinodeを取得する時にコールされます。この時ファイルの場合、inode->i_fop = fops ? fops : &debugfs_file_operationsとしています。debugfs_file_operationsは、何もしない関数です。
static struct inode *debugfs_get_inode(struct super_block *sb, umode_t mode, dev_t dev,
                                      void *data, const struct file_operations *fops)
{
       struct inode *inode = new_inode(sb);

       if (inode) {
               inode->i_ino = get_next_ino();
               inode->i_mode = mode;
               inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
               switch (mode & S_IFMT) {
               default:
                       init_special_inode(inode, mode, dev);
                       break;
               case S_IFREG:
                       inode->i_fop = fops ? fops : &debugfs_file_operations;
                       inode->i_private = data;
                       break;
               case S_IFLNK:
                       inode->i_op = &debugfs_link_operations;
                       inode->i_fop = fops;
                       inode->i_private = data;
                       break;
               case S_IFDIR:
                       inode->i_op = &simple_dir_inode_operations;
                       inode->i_fop = fops ? fops : &simple_dir_operations;
                       inode->i_private = data;

                       /* directory inodes start off with i_nlink == 2
                        * (for "." entry) */
                       inc_nlink(inode);
                       break;
               }
       }
       return inode; 
}
ファイル毎のファイルオペレーションを定義していない場合、以下のコールバックが設定されます。
const struct file_operations debugfs_file_operations = {
       .read =         default_read_file,
       .write =        default_write_file,
       .open =         default_open,
       .llseek =       noop_llseek,
};

static ssize_t default_read_file(struct file *file, char __user *buf,
                                size_t count, loff_t *ppos)
{
       return 0;
}

static ssize_t default_write_file(struct file *file, const char __user *buf,
                                  size_t count, loff_t *ppos)
{
       return count;
}

static int default_open(struct inode *inode, struct file *file)
{
       if (inode->i_private)
               file->private_data = inode->i_private;

       return 0;
}

補足

kprobeでのdebugfsの利用例です。kprobeのkprobeサンプルをinsmodした時の内容です。
[root@localhost kprobes]# pwd
/sys/kernel/debug/kprobes
[root@localhost kprobes]# ls
enabled  list
[root@localhost kprobes]# cat list
c0436270  k  do_fork+0x0

実装(kprobes)

debugfs_kprobe_init()は、モジュール初期化時にコールされ(late_initcallマクロで宣言)、最初にdebugfs_create_dir()で、/sys/kernel/debug下にkprobesが作成され、debugfs_create_fill()で、その下にlistとenableのファイルが作成されます。そのファイルの参照は、debugfs_kprobes_operations/fops_kpオペレーションに記述する事で、kprobeでの内部情報をユーザプロセスとやり取りする事が可能となります。
static const struct file_operations debugfs_kprobes_operations = {
       .open           = kprobes_open,
       .read           = seq_read,
       .llseek         = seq_lseek,
       .release        = seq_release,
};

static int __kprobes debugfs_kprobe_init(void)
{
       struct dentry *dir, *file;
       unsigned int value = 1;

       dir = debugfs_create_dir("kprobes", NULL);
       if (!dir)
               return -ENOMEM;

       file = debugfs_create_file("list", 0444, dir, NULL,
                               &debugfs_kprobes_operations);
       if (!file) {
               debugfs_remove(dir);
               return -ENOMEM;
       }

       file = debugfs_create_file("enabled", 0600, dir,
                                       &value, &fops_kp);
       if (!file) {
               debugfs_remove(dir);
               return -ENOMEM;
       }

       return 0;
}

late_initcall(debugfs_kprobe_init);
ちなみに、listの内容は以下の様に、kproobeのアドレス、kprobeタイプ(r:kretprobe j:kjprobe k:kprobe)、kprobeのシンボル+オフセット 設定モジュール)となっています。
[root@localhost kprobes]# cat list
c0436270  k  do_fork+0x0

static void __kprobes report_probe(struct seq_file *pi, struct kprobe *p,
               const char *sym, int offset, char *modname, struct kprobe *pp)
{
       char *kprobe_type;

       if (p->pre_handler == pre_handler_kretprobe)
               kprobe_type = "r";
       else if (p->pre_handler == setjmp_pre_handler)
               kprobe_type = "j";
       else
               kprobe_type = "k";

       if (sym)
               seq_printf(pi, "%p  %s  %s+0x%x  %s ",
                       p->addr, kprobe_type, sym, offset,
                       (modname ? modname : " "));
       else
               seq_printf(pi, "%p  %s  %p ",
                       p->addr, kprobe_type, p->addr);

       if (!pp)
               pp = p;
       seq_printf(pi, "%s%s%s\n",
               (kprobe_gone(p) ? "[GONE]" : ""),
               ((kprobe_disabled(p) && !kprobe_gone(p)) ?  "[DISABLED]" : ""),
               (kprobe_optimized(pp) ? "[OPTIMIZED]" : ""));
}
module_init/late_initcallマクロは、関数を.initcallセクションに配置するものですが、module_initマクロは6、late_initcallマクロは7と言う具合に、そのセクション内で順位を有しています。従って、late_initcallマクロで宣言された関数は、module_initマクロで宣言された関数の後にコールされる事になります。
#define module_init(x)  __initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn)             __define_initcall("6",fn,6)

#define late_initcall(fn)               __define_initcall("7",fn,7)
#define __define_initcall(level,fn,id) \
       static initcall_t __initcall_##fn##id __used \
       __attribute__((__section__(".initcall" level ".init"))) = fn


最終更新 2014/01/12 18:03:33 - north
(2012/11/14 02:12:49 作成)


検索

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