操作系统: 08 2010存档

对自己写的kernel module进行压力测试,连续跑了24小时,没啥问题,但是,继续跑到30几个小时的时候,内核panic了。

从panic的信息看,是在dcache_readdir的时候,就是从cache里读各inode的时候遇到麻烦了。

kernel本身是很坚固的,肯定是我的module某个地方把cache里的东西弄坏了,想想我确实有遍历目录的操作:

next = dentry->d_subdirs.next;
while (next != &dentry->d_subdirs)
......

再仔细看看内核里dcache_readdir的实现,喔,我少加了锁,应该是:

spin_lock(&dcache_lock);
遍历目录
spin_unlock(&dcache_lock);

之前的panic应该是我遍历目录的同时系统在调整dcache,结果竞争了。

内核模块里自己做了一个简化版的 find_get_page:

  struct page * my_get_page(struct address_space *mapping, unsigned long offset)
  {
      struct page *page;

      read_lock_irq(&mapping->tree_lock);
      page = radix_tree_lookup(&mapping->page_tree, offset);
      read_unlock_irq(&mapping->tree_lock);
      return page;
  }

代码是从2.6.18的内核里抄来的。

在2.6.9内核的xen虚拟机上测试,现象很诡异:服务器的ssh连接突然断开,只能重连,重连后看见模块被自动卸载掉了。仔细看代码,才发现2.6.9内核的struct address_space里的tree_lock是spinlock_t,而不是rwlock_t,结果我的代码就是对着一个spinlock_t调用read_lock_irq,锁操作不对,然后又发生了什么呢?

read_lock_irq 调用了 _raw_read_lock:

  static inline void _raw_read_lock(rwlock_t *rw)
  {
  #ifdef CONFIG_DEBUG_SPINLOCK
      BUG_ON(rw->magic != RWLOCK_MAGIC);
  #endif
      __build_read_lock(rw, "__read_lock_failed");
  }

喔,kernel会判断锁的magic number,不对的话会调用BUG_ON(),进而调用BUG(),所以机器打了个嗝。

如果没有这个magic number的检测,一旦死机,错误就更难找了,这个检测虽然很土,但很体贴,也很有效。