最近在阅读vfs的代码时,发现inode的rw_semaphore,上写锁的时候,有的是执行:
static inline void inode_lock(struct inode *inode)
{
down_write(&inode->i_rwsem);
}
有的是执行:
static inline void inode_lock_nested(struct inode *inode, unsigned subclass)
{
down_write_nested(&inode->i_rwsem, subclass);
}
阅读代码发现,只有在开启lockdep的时候,down_write()/down_write_nested()才有区别。
https://docs.kernel.org/locking/lockdep-design.html
原来lockdep是内核用来调试lock的一个工具,它不影响原来的功能。在发生死锁的时候,能打印足够的信息用来定位问题。
lockdep有一个lock class的功能。不同的lock可以属于同一个class。
写一个代码:
static struct rw_semaphore rwsem_1;
static struct rw_semaphore rwsem_2;
static struct lock_class_key key;
static int __init test_init(void)
{
init_rwsem(&rwsem_1);
lockdep_set_class(&rwsem_1, &key);
init_rwsem(&rwsem_2);
lockdep_set_class(&rwsem_2, &key);
#if 1
down_write(&rwsem_1);
down_write(&rwsem_2);
up_write(&rwsem_2);
up_write(&rwsem_1);
#else
down_write_nested(&rwsem_1, 0);
down_write_nested(&rwsem_2, 1);
up_write(&rwsem_2);
up_write(&rwsem_1);
#endif
return 0;
}运行后报告警,但是实际功能是正常的。
# insmod /mnt/host0/app/test_ko/test.ko
[ 15.927235] test: loading out-of-tree module taints kernel.
[ 15.935408]
[ 15.935669] ============================================
[ 15.936092] WARNING: possible recursive locking detected
[ 15.936801] 6.12.37+ #13 Tainted: G O
[ 15.937146] --------------------------------------------
[ 15.937591] insmod/113 is trying to acquire lock:
[ 15.939295] ffff80007922c638 (&key){+.+.}-{4:4}, at: test_init+0xa8/0x1000 [test]
[ 15.941229]
[ 15.941229] but task is already holding lock:
[ 15.941620] ffff80007922c580 (&key){+.+.}-{4:4}, at: test_init+0xa0/0x1000 [test]
[ 15.942854]
[ 15.942854] other info that might help us debug this:
[ 15.943396] Possible unsafe locking scenario:
[ 15.943396]
[ 15.943788] CPU0
[ 15.943922] ----
[ 15.944103] lock(&key);
[ 15.944394] lock(&key);
[ 15.944692]
[ 15.944692] *** DEADLOCK ***
[ 15.944692]
[ 15.944986] May be due to missing lock nesting notationrw semaphore是不允许嵌套锁的(recursive)。比如 down_write(&lock); down_write(&lock); 这里会死锁。
但是上面两个是不同的锁。由于设置了同一个class。所以lockdep也告警了。尽管实际上没有死锁。
这种场景下,就需要使用_nested()接口,带不同的subclass。使用#else的代码,lockdep就没有告警了。
# insmod /mnt/host0/app/test_ko/test.ko [ 21.126974] test: loading out-of-tree module taints kernel.