一段关于死锁的讨论

20 篇文章 0 订阅
转自:http://topic.csdn.net/u/20080222/23/70e12272-2f9c-4004-81a5-85bdac086729.html
/*******************************************************************************/
spin lock 可以用在 中断处理程序用,
其他的 如信号量等是不行的.
可能引起睡眠的函数都不能用在中断里.
/*******************************************************************************/
谢谢,第一次碰到,我有个i2c的设备驱动,在中断里进行读写,就碰到以下情况,不知道是不是使用mutex_lock引起的.
出错的地方在i2c转串口(SC16IS740)的驱动程序里,该模块申请了一个IRQ中断,
中断处理函数是serial_sc16is7_interrupt(),在这个函数里
要调用i2c-core模块里的i2c_smbus_read_byte_data()函数读I2C总线,
但这时系统就panic掉了。

内核版本是2.6.21,下面是出错信息。
---------------------------------------------------------
<3>BUG: scheduling while atomic: swapper/0x00010000/0
<3>bad: scheduling from the idle thread!
<1>Unable to handle kernel NULL pointer dereference at virtual address 00000000
<1>pgd = c0004000
<1>[00000000] *pgd=00000000

Internal error: Oops: 17 [#1]
Modules linked in: nmeapipe gtspmdev

CPU: 0
PC is at dequeue_task+0xc/0x84
LR is at deactivate_task+0x34/0x40
pc : [<bf06633c>] lr : [<bf0666c0>] Not tainted
sp : c0009cec ip : c0009d00 fp : c0009cfc
r10: 00000005 r9 : b9a0f85d r8 : c00129b8
r7 : 00000002 r6 : c00129b8 r5 : c0008000 r4 : c00129b8
r3 : 00000080 r2 : 00000080 r1 : 00000000 r0 : c00129b8
Flags: nZCv IRQs off FIQs on Mode SVC_32 Segment kernel
Control: 317F
Table: 103C8000 DAC: 00000017
Process swapper (pid: 0, stack limit = 0xc0008250)
Stack: (0xc0009cec to 0xc000a000)
9fe0: 9fe0: c002f364 c002f364 c00137a4 c00137a4 00000000 00000000 c0009ff8 c0009ff8 00040070 00040070 bf0408ac bf0408ac 00000000 00000000 00000000 00000000

Backtrace:
[<bf066330>] (dequeue_task+0x0/0x84) from [<bf0666c0>] (deactivate_task+0x34/0x40)
r4 = C00129B8

[<bf06668c>] (deactivate_task+0x0/0x40) from [<bf1f2410>] (schedule+0x1b0/0x744)
r4 = 00000005

[<bf1f2260>] (schedule+0x0/0x744) from [<bf1f375c>] (__mutex_lock_slowpath+0x5c/0x88)
[<bf1f3700>] (__mutex_lock_slowpath+0x0/0x88) from [<bf1f36d4>] (mutex_lock+0x20/0x28)
r7 = C0074054 r6 = C0009DB8 r5 = 00000002 r4 = C0074034
[<bf1f36b4>] (mutex_lock+0x0/0x28) from [<bf165098>](i2c_transfer+0x38/0x64)
[<bf165060>] (i2c_transfer+0x0/0x64) from [<bf165674>] (i2c_smbus_xfer+0x350/0x464)
r7 = 00000002 r6 = 00000000 r5 = C0009DB8 r4 = 00000000
[<bf165324>] (i2c_smbus_xfer+0x0/0x464) from [<bf165a1c>] (i2c_smbus_read_byte_data+0x44/0x58)
[<bf1659d8>] (i2c_smbus_read_byte_data+0x0/0x58) from [<bf167fb0>] (Sc16is7xx_Read_Register+0x38/0x44)
[<bf167f78>] (Sc16is7xx_Read_Register+0x0/0x44) (from [<bf150480>] (serial_in+0x20/0x28)
r4 = 00000000

[<bf150460>] (serial_in+0x0/0x28) from [<bf1506a4>] (serial_sc16is7_interrupt+0x34/0xcc)
[<bf150670>] (serial_sc16is7_interrupt+0x0/0xcc) from [<bf08ae30>](handle_IRQ_event+0x44/0x80)
[<bf08adec>](handle_IRQ_event+0x0/0x80) from [<bf08bffc>] (handle_simple_irq+0x80/0xb4)
r7 = C001D96C r6 = 000000AE r5 = 000000AE r4 = C000EB80
[<bf08bf7c>] (handle_simple_irq+0x0/0xb4) from [<bf064a70>] (gpio_irq_handler+0x1ac/0x234)
r5 = 00000001 r4 = C000EB80

[<bf0648c4>] (gpio_irq_handler+0x0/0x234) from [<bf05813c>] (asm_do_IRQ+0x44/0x5c)
[<bf0580f8>] (asm_do_IRQ+0x0/0x5c) from [<bf057424>](__irq_svc+0x24/0x80)
r7 = C0048E04 r6 = FFFFFFFF r5 = FEFECB00 r4 = FFFFFFFF
[<bf058340>] (cpu_idle+0x0/0x7c) from [<bf056c24>] (rest_init+0x24/0x2c)
r6 = C000AF28 r5 = C002EEA8 r4 = C0037C00
[<bf056c00>] (rest_init+0x0/0x2c) from [<bf040b18>] (start_kernel+0x27c/0x2e4)
[<bf04089c>] (start_kernel+0x0/0x2e4) from [<00040070>] (0x40070)
r6 = C00137A4 r5 = C002F364 r4 = 0000317D

Code:c0030148 e1a0c00d e92dd810 e24cb004 (e5913000)

<0>Kernel panic - not syncing: Aiee, killing interrupt handler!
---------------------------------------------------------  
/*******************************************************************************/
/*kernel/drivers/i2c/i2c-core.c*/
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
int ret;

if (adap->algo->master_xfer) {
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
"len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif

mutex_lock_nested(&adap->bus_lock, adap->level);
ret = adap->algo->master_xfer(adap,msgs,num);
mutex_unlock(&adap->bus_lock);

return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -ENOSYS;
}
}

---------------
这里的mutex_lock_nested 与mutex_lock有什么区别啊,跟中断有没有关系呢?
/*******************************************************************************/
从 Backtrace 来看,应该是 i2c_transfer 中调用 mutex_lock 导致 schedule 调用
而产生 进程调度,导致死机. 而在中断上下文,这种情况是绝对不允许发生的.换句话说是不允许
睡眠的,不允许进程调度. 

你可以 把 mutex_lock 注释掉再试试 .
另外你的内核是什么版本?
/*******************************************************************************/
刚才看了下 mutex_lock 的具体实现, 

如果内核 打开了 CONFIG_PREEMPT_VOLUNTARY 选项,
mutex_lock 函数 会调用 schedule 而产生进程调度.

建议你把 mutex_lock 换成 spinlock_t  
或者干脆不加锁.
/*******************************************************************************/
谢谢 拉拉是头猪 的热心解答,^_^
我用的内核版本是2.6.21.
google时有篇文章不错就顺便转贴过来共享以备查询,也不知道作者是谁。
网址:http://www.yuanma.org/data/2006/0825/article_1409.htm
Linux Device Drivers学习笔记-Chapter.5
--------------------------------
本章主要涉及并发管理,我觉得这部分的理解需要建立在大量实践的基础之上,光是像本科操作系统课上那样讲道理是很难体会的。我记录了一些关键的函数和数据结构。

  1.Race Conditions(RC)主要是因为对共享数据的并发访问没有作出合适的访问策略造成的。例如两个进程同时访问了一个共享数据。
所以制定好的访问策略,才能在Concurrency的时候避免RC。
所以作者建议尽量减少对资源的并发访问,但是又说明这几乎是不太可能的。所以唯有开发者加入并发控制的代码到module中去。目前主要采用的思想就是lock or mutual exclusion(锁和互斥)。

  2.一般当某段代码需要进行并发访问管理的时候,就称之为critical section(CS)。一般为了对CS进行访问控制,采用lock的方法。当某个进程运行到某个CS的时候,它会去尝试获得lock,如果获取失败,就会进入block状态,从而sleep。根据不同的进程调度策略,该进程可能会等待若干时间后重新尝试获取lock。同时如果该进程获取了lock之后,进入CS,在CS执行的过程中也可能因为等待IO等进入sleep。

  3.我个人认为semaphore也是lock思想的一种实现方式,P/V原语实际上可以代表进程对锁的获取和释放过程。当某个CS允许n个进程同时访问的时候,我们可以初始化semaphore=n,如果对某个CS的访问必须是互斥的(也就是同一时间只能有一个进程访问之),那么semaphore就要初始化为1.这时候的semaphore也称之为mutex。Linux kernel中几乎所有的semaphore都是mutex。

  4.在linux中如果要使用semaphore,需要:
  #include <asm/semaphore.h>
  可见semaphore的实现是和平台相关的。具体的数据结构是:
  struct semaphore {
  atomic_t count;
  int sleepers;
  wait_queue_head_t wait;
  };
  如果想要建立一个普通的semaphore:
  void sema_init(struct semaphore *sem, int val);

  如果想要建立一个mutex,可以使用下面的两个宏:
  DECLARE_MUTEX(name);
  DECLARE_MUTEX_LOCKED(name);
  二者的不同在于后者初始化之后mutex为0,任何进程想要进入CS,必须先解锁。

  如果想要在运行期间创建mutex,可以使用:
  void init_MUTEX(struct semaphore *sem);
  void init_MUTEX_LOCKED(struct semaphore *sem);

  针对P/V原语,P在linux 中称之为down,也就是减少1,V称之为up,也就是增加1,down有三种版本:
  void down(struct semaphore *sem);
  int down_interruptible(struct semaphore *sem);
  int down_trylock(struct semaphore *sem);
  其中第一个版本会尝试使用P原语,如果semaphore已经为0,该函数就会一直等待。第二个版本也是如此,但是可以被中断(interruptible)。第三个函数如果使用P原语失败,则立即返回。所以第二个和第三个函数返回后需要检验它们的返回值来确定是否真的获得了semaphore。

  up函数为:
  void up(struct semaphore *sem);

  5.读写信号量
  上面介绍的普通semaphore操作,无论是什么操作,都是一样的处理方法。但是针对写少读多的情况,我们可以让读操作在CS中并发,这样不会引起RC。这就需要一类新的semaphore,linux kernel也为我们提供了(书中提到这类semaphore现在已经很少使用了)
  #include <linux/rwsem.h>
  数据结构为
  struct rw_semaphore;

  这个数据结构必须在运行期间被初始化:
  void init_rwsem(struct rw_semaphore *sem);

  如果只是进行读操作,down/up函数有
  void down_read(struct rw_semaphore *sem)
  int down_read_trylock(struct rw_semaphore *sem)
  void up_read(struct rw_semaphore *sem)
  第二个函数如果请求读失败则立即返回,这里需要注意的是如果请求成功则返回非0,失败则0.这个返回值和linux中大多数函数成功返回0的习惯恰好相反。

  对于写操作,down/up函数有:
  void down_write(struct rw_semaphore *sem)
  int down_write_trylock(struct rw_semaphore *sem)
  void up_write(struct rw_semaphore *sem)
  void downgrade_write(struct rw_semaphore *sem)
  其中前三个函数和read版的行为类似,最后一个函数用于短期的写操作使用。

  这种semaphore的形式可以让read的数量无限制,write进入CS需要互斥,同时保证write进入CS的时候没有read在CS中(否则读出的数据是dirty)。这样可能会导致reader starvation。所以这种方法适用于那些写操作不多而且持续时间不长的场所。

  6.Completions
  有一种情况就是在当前进程(A)启动一个处于进程之外的任务(B),可能在kernel space,可能在user space,然后等待B完成。在这种情况下,我们可以使用信号量来协调二者的先后顺序。用语句表示就是
  struct semaphore sem;
  init_MUTEX_LOCKED(&sem);
  start_external_task(&sem);
  down(&sem);
  首先A创建一个mutex类型的semaphore,并立即将自己锁死在上面(参考前面的init_MUTEX_LOCKED宏定义,初始化sem为0),然后等待B完成后对sem进行up操作,此后A才能进行down操作,从而进入CS。

  从上面的语句中可以看出,A必须等到B完成之后才能继续工作,这会极大的影响性能,所以semaphore是不适合用于这里的。为了不在这种情况下继续使用semaphore,从kernel 2.4.17开始加入了completion接口。

  #include <linux/completion.h>

  可以声明如下:
  DECLARE_COMPLETION(my_completion);
  或者动态声明如下:
  struct completion my_completion;
  init_completion(&my_completion);

  如果A等待某个进程完成只需要执行如下函数
  void wait_for_completion(struct completion *c);
  需要注意的是,这个函数是不可中断的,所以如果没有进程completion,则A进程则会一直等待下去。

  另一方面,B调用下列函数来通知completion:
  void complete(struct completion *c);
  void complete_all(struct completion *c);
  第一个函数只能通知一个等待的进程,第二个函数可以通知全部进程。

  源代码目录下的misc-modules/complete.c给出了使用completion的例子,是一个使用completion完成读写的字符设备。每个读操作都在等待一个写操作通知completion,此后至于谁读则不能控制。

  7.Spinlocks
  spinlocks是linux kernel提供的另一种互斥方法。spinlocks用于不能sleep的代码中,例如中断处理。较之semaphore可以提供更好的性能,当然也有更多的约束。

  一个spinlock只有locked和unlocked两种状态,通常它是一个整型变量的一个bit,测试的时候如果是unlocked则设置为locked,然后进程会进入CS,否则就会执行一个循环,并不断查询bit,直到它可用为止,这个循环就是spin。

  当然这个测试和设置locked的操作是atomic的,这样才能保证只有一个线程获得锁。另外如果spinlock运行在一个非抢占式的单cpu的系统中,那么一旦进入spin则会一直循环下去,因为不可能有别的线程获得cpu并释放lock。所以在这种情况下spinlocks会优化成为什么都不作。在SMP下则不会有这个问题,所以从这里也能看出来,spinlocks是平台相关的。

  8.Spinlock的基本函数
  #include <linux/spinlock.h>

  编译期间的初始化:
  spinlokc_t my_lock = SPIN_LOCK_UNLOCKED

  运行期间初始化:
  void spin_lock_init(spinlock_t *lock);

  申请加锁的时候:
  void spin_lock(spinlock_t *lock);
  注意这个函数没有返回值,所以我们猜也可以猜到,它和前面介绍的那些可以interruptible的不同,一旦申请,就会一直spin直到得到锁为止。

  解锁的时候:
  void spin_unlock(spinlock_t *lock);
  
  9.Spinlock和原子上下文
设想当一个进程获得spinlock之后进入了CS,随后调用了一个可能sleep的函数(例如cpoy_from_user,因为需要的page可能不在内存中,需要从硬盘copy,这回引起copy_from_user的sleep),这时就会引起别的正在spin的进程,在未来一段时间无法获得lock。

  所以使用spinlock的时间要尽量短,而且在持有锁的时候,执行的代码要是原子的,也就是不能sleep的。

  另一种情况就是在单cpu的机器上,一个进程获得了某个设备上的锁,这时出现中断,中断处理会首先要求获得该设备上的锁,就会不断的spin,占用cpu。而那个持有锁的进程确无法再获得cpu而释放锁,从而变成了deadlock。所以在单cpu上使用spinlock的时候要关中断。

  10.Spinlock的其余函数
  void spin_lock_irqsave(spinlock_t *lock, unsigned long flag);
  void spin_lock_irq(spinlock_t *lock);
  void spin_lock_bh(spinlock_t *lock);
  第一个函数在获取spinlock之前关中断,并将其状态放入flag中。第二个函数需要你负责在释放 spinlock后来开中断。第三个函数在获取spinlock之前关掉软件中断。

  对应的释放函数
  void spin_unlock_irqstore(spinlock_t *lock, unsigned long flag);
  void spin_unlock_irq(spinlock_t *lock);
  void spin_unlock_bh(spinlock_t *lock);

  另外还有非阻塞的获取函数
  int spin_trylock(spinlock_t *lock);
  int spin_trylock_bh(spinlock_t *lock);

  11.读/写 Spinlocks
  spinlocks还有一组用于读写操作,允许多个读者同时进入CS,而写者进入CS必须是互斥的。
  #include <linux/spinlock.h>
  数据结构为rwlock_t

  编译期间初始化为:
  rwlock_t my_rwlock = RW_LOCK_UNLOCKED

  运行期间初始化为:
  rwlock_t my_rwlock;
  rwlock_init(&my_rwlock);
  它们也有和上面类似的操作函数,可以参考书P120-121.

  12.原子变量
  当CS中只含有一个简单的变量的时候,可以考虑使用原子变量。具体的可以参考书P124-126.
/*******************************************************************************/
>>>>在将mutex lock去掉或者 改为spin lock这两种情况下,问题并没有改善,
仍然会出现kernel panic的现象,只是Backtrace略有不同而已。下面是这两种
情况下的oops,是一样的。

<6>serial_sc16is7_interrupt
<3>BUG: scheduling while atomic: swapper/0x00010000/0
<3>BUG: scheduling while atomic: swapper/0x00010000/0
<3>bad: scheduling from the idle thread!
<3>BUG: scheduling while atomic: swapper/0x00010000/0
<3>bad: scheduling from the idle thread!
<1>Unable to handle kernel NULL pointer dereference at virtual address 00000000
<1>pgd = c0004000
<1>[00000000] *pgd=00000000
Internal error: Oops: d5 [#1]
Modules linked in:
CPU: 0
PC is at dequeue_task+0xc/0x84
LR is at deactivate_task+0x34/0x40
pc : [<bf06633c>] lr : [<bf0666c0>] Not tainted
sp : c0009c6c ip : c0009c80 fp : c0009c7c
r10: 00000003 r9 : 5c3f39a9 r8 : c00129b8
r7 : c0009ccc r6 : 00000062 r5 : c0008000 r4 : c00129b8
r3 : ffffff80 r2 : 00000080 r1 : 00000000 r0 : c00129b8
Flags: nZCv IRQs off FIQs on Mode SVC_32 Segment kernel
Control: 317F
Table: 10004000 DAC: 00000017
Process swapper (pid: 0, stack limit = 0xc0008250)
Stack: (0xc0009c6c to 0xc000a000)
9c60: c00129b8 c0009c90 c0009c80 bf0666c0 bf066340
9c80: 00000003 c0009cc8 c0009c94 bf1f23d0 bf06669c c00129f4 c0012ac4 008b80c0
9ca0: ffff90b3 c0008000 00000062 c0009ccc c001e6b0 00000000 00000002 c0009d04
9cc0: c0009ccc bf1f3378 bf1f2230 c0035ba8 c0035ba8 ffff90b3 bf074590 c00129b8
9ce0: c0035608 c0009d18 c0008000 c0075014 00000062 c0075000 c0009d4c c0009d08
9d00: bf1f2b20 bf1f32fc 00000000 c00129b8 bf06721c 00000000 00000000 00000001
9d20: c00129b8 bf06721c c0075018 c0075018 c0075000 c0009db4 c0075014 00000000
9d40: c0009d90 c0009d50 bf166230 bf1f2a80 00000000 00000000 c0075074 c0009db4
9d60: 000663c4 00000003 00000000 c0009db4 00000000 00000002 c0075034 00000000
9d80: c0009e4c c0009da0 c0009d94 bf165060 bf1660b4 c0009e3c c0009da4 bf16561c
9da0: bf165048 00000001 60000093 c0009e98 00000001 0000004d 00000001 c0009df0
9dc0: 0001004d c0000001 c0009dcc bf0672dc bf067244 00000000 80000093 c0009dfc
9de0: c0009dec bf059658 bf0672c0 c0030ee8 c0009e10 c0009e00 bf1f1f8c bf059610
9e00: 00000023 ffffd627 00000000 c001dabc c0030ee8 00000010 c00455e0 c0045678
9e20: 00000000 00000000 000000ae c0045ab8 c0009e7c c0009e40 bf1659c4 bf1652dc
9e40: 00000010 00000002 c0009e4c bf08c128 bf0612c8 c000c900 c00c7780 c00455e0
9e60: c0045678 00000000 00000000 000000ae c0009e90 c0009e80 bf167f58 bf165990
9e80: c00c7780 c0009ea0 c0009e94 bf150480 bf167f30 c0009ed0 c0009ea4 bf1506ac
9ea0: bf150470 00000000 c00c7780 00000000 00000000 000000ae 0000000e 00000001
9ec0: c000c380 c0009ef0 c0009ed4 bf08ae30 bf150680 c000eb80 000000ae 000000ae
9ee0: c001d96c c0009f08 c0009ef4 bf08bffc bf08adfc c000eb80 00000001 c0009f3c
9f00: c0009f0c bf064a70 bf08bf8c 00000000 fefce014 c000c380 0000000e c0041310
9f20: 00000000 00056428 54029252 000563c0 c0009f5c c0009f40 bf05813c bf0648d4
9f40: ffffffff fefecb00 ffffffff c00493c4 c0009fc0 c0009f60 bf057424 bf058108
9f60: 00000000 c0030148 a0000013 00000000 c0008000 bf061530 c0013ae8 c00493c4
9f80: 00056428 54029252 000563c0 c0009fc0 c0009fa8 c0009fa8 bf058398 bf05838c
9fa0: 60000013 ffffffff c0037c00 c002eea8 c000af28 c0009fd0 c0009fc4 bf056c24
9fc0: bf058350 c0009ff4 c0009fd4 bf040b18 bf056c10 bf040504 c000af28 0000317d
9fe0: c002f364 c00137a4 00000000 c0009ff8 00040070 bf0408ac 00000000 00000000
Backtrace:
[<bf066330>] (dequeue_task+0x0/0x84) from [<bf0666c0>] (deactivate_task+0x34/0x40)
 r4 = C00129B8
[<bf06668c>] (deactivate_task+0x0/0x40) from [<bf1f23d0>] (schedule+0x1b0/0x744) r4 = 00000003
[<bf1f2220>] (schedule+0x0/0x744) from [<bf1f3378>] (schedule_timeout+0x8c/0xbc)[<bf1f32ec>] (schedule_timeout+0x0/0xbc) from [<bf1f2b20>] (wait_for_completion_timeout+0xb0/0x148)
 r8 = C0075000 r7 = 00000062 r6 = C0075014 r5 = C0008000
 r4 = C0009D18
[<bf1f2a70>] (wait_for_completion_timeout+0x0/0x148) from [<bf166230>] (omap_i2c_xfer+0x18c/0x288)
 r7 = 00000000 r6 = C0075014 r5 = C0009DB4 r4 = C0075000
[<bf1660a4>] (omap_i2c_xfer+0x0/0x288) from [<bf165060>] (i2c_transfer+0x28/0x34)
[<bf165038>] (i2c_transfer+0x0/0x34) from [<bf16561c>] (i2c_smbus_xfer+0x350/0x464)
[<bf1652cc>] (i2c_smbus_xfer+0x0/0x464) from [<bf1659c4>] (i2c_smbus_read_byte_data+0x44/0x58)
[<bf165980>] (i2c_smbus_read_byte_data+0x0/0x58) from [<bf167f58>] (Sc16is7xx_Read_Register+0x38/0x44)
[<bf167f20>] (Sc16is7xx_Read_Register+0x0/0x44) from [<bf150480>] (serial_in+0x20/0x28)
 r4 = C00C7780
[<bf150460>] (serial_in+0x0/0x28) from [<bf1506ac>] (serial_sc16is7_interrupt+0x3c/0xe4)
[<bf150670>] (serial_sc16is7_interrupt+0x0/0xe4) from [<bf08ae30>] (handle_IRQ_event+0x44/0x80)
[<bf08adec>] (handle_IRQ_event+0x0/0x80) from [<bf08bffc>] (handle_simple_irq+0x80/0xb4)
 r7 = C001D96C r6 = 000000AE r5 = 000000AE r4 = C000EB80
[<bf08bf7c>] (handle_simple_irq+0x0/0xb4) from [<bf064a70>] (gpio_irq_handler+0x1ac/0x234)
 r5 = 00000001 r4 = C000EB80
[<bf0648c4>] (gpio_irq_handler+0x0/0x234) from [<bf05813c>] (asm_do_IRQ+0x44/0x5c)
[<bf0580f8>] (asm_do_IRQ+0x0/0x5c) from [<bf057424>] (__irq_svc+0x24/0x80)
 r7 = C00493C4 r6 = FFFFFFFF r5 = FEFECB00 r4 = FFFFFFFF
[<bf058340>] (cpu_idle+0x0/0x7c) from [<bf056c24>] (rest_init+0x24/0x2c)
 r6 = C000AF28 r5 = C002EEA8 r4 = C0037C00
[<bf056c00>] (rest_init+0x0/0x2c) from [<bf040b18>] (start_kernel+0x27c/0x2e4)
[<bf04089c>] (start_kernel+0x0/0x2e4) from [<00040070>] (0x40070)
 r6 = C00137A4 r5 = C002F364 r4 = 0000317D
Code: c0030148 e1a0c00d e92dd810 e24cb004 (e5913000)
<0>Kernel panic - not syncing: Aiee, killing interrupt handler!
---------------------------------------------------------
在上面的oops里看到了wait_for_completion_timeout,目的是i2c在执行读写操作时
要等待数据传送完毕或者超时,该函数在sched.c文件里。
不知道是不是在这里面崩掉的?
/*******************************************************************************/
该函数在sched.c文件里。
不知道是不是在这里面崩掉的?
------------------------------------
//sched.c
unsigned long fastcall __sched
wait_for_completion_timeout(struct completion *x, unsigned long timeout)
{
might_sleep();

spin_lock_irq(&x->wait.lock);
if (!x->done) {
DECLARE_WAITQUEUE(wait, current);

wait.flags |= WQ_FLAG_EXCLUSIVE;
__add_wait_queue_tail(&x->wait, &wait);
do {
__set_current_state(TASK_UNINTERRUPTIBLE);
spin_unlock_irq(&x->wait.lock);
timeout = schedule_timeout(timeout);
spin_lock_irq(&x->wait.lock);
if (!timeout) {
__remove_wait_queue(&x->wait, &wait);
goto out;
}
} while (!x->done);
__remove_wait_queue(&x->wait, &wait);
}
x->done--;
out:
spin_unlock_irq(&x->wait.lock);
return timeout;
}
------------------------------------
/*******************************************************************************/
和你的内核 编译选项有关系.

关掉 CONFIG_PREEMPT_VOLUNTARY 可以解决这个问题.
或者不加锁.
或者你去修改 i2c_transfer 里所调用的函数,
任何可能引起睡眠的函数都不能使用.

又或者使用下半部分的方法,

中断中不不要使用任何可能引起睡眠的函数.
/*******************************************************************************/
修改i2c_transfer或i2c总线驱动比较麻烦,
找到了另一种解决办法,使用workqueue来处理,
不再出现先前的oops了,
但是响应好像有点慢。

相关结构函数:struct work_struck,INIT_WORK(),schedule_work().
/*******************************************************************************/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YasinLeeX

再来一杯西湖龙井。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值