Linux内核相关论坛问题回复(1)

http://bbs.csdn.net/topics/391048758?page=1#post-399361268
有人在论坛提了下面的问题,比较典型。

最近在调试SPI子系统时发现如下错误:

[ 3777.005000] BUG: scheduling while atomic: spi0/25/0x00000002
[ 3777.005000] Modules linked in: focal_fp_sensor(O) rtl8192cu(O) snd_soc_tiny4412_wm8960 snd_soc_wm8960 [last unloaded: focal_fp_sensor]
[ 3777.005000] [<c0015484>] (unwind_backtrace+0x0/0xf0) from [<c0058084>] (__schedule_bug+0x44/0x58)
[ 3777.010000] [<c0058084>] (__schedule_bug+0x44/0x58) from [<c058b180>] (__schedule+0x760/0x830)
[ 3777.020000] [<c058b180>] (__schedule+0x760/0x830) from [<c025343c>] (rpm_resume+0x12c/0x640)
[ 3777.025000] [<c025343c>] (rpm_resume+0x12c/0x640) from [<c0253bb8>] (__pm_runtime_resume+0x48/0x60)
[ 3777.035000] [<c0253bb8>] (__pm_runtime_resume+0x48/0x60) from [<c0219f18>] (pl330_alloc_chan_resources+0x1bc/0x1f0)
[ 3777.045000] [<c0219f18>] (pl330_alloc_chan_resources+0x1bc/0x1f0) from [<c021898c>] (dma_chan_get+0x5c/0xfc)
[ 3777.055000] [<c021898c>] (dma_chan_get+0x5c/0xfc) from [<c0219144>] (__dma_request_channel+0x110/0x1d4)
[ 3777.065000] [<c0219144>] (__dma_request_channel+0x110/0x1d4) from [<c0027324>] (samsung_dmadev_request+0x40/0x4c)
[ 3777.075000] [<c0027324>] (samsung_dmadev_request+0x40/0x4c) from [<c027a034>] (s3c64xx_spi_prepare_transfer+0x44/0x88)
[ 3777.085000] [<c027a034>] (s3c64xx_spi_prepare_transfer+0x44/0x88) from [<c0277d08>] (spi_pump_messages+0xe8/0x160)
[ 3777.095000] [<c0277d08>] (spi_pump_messages+0xe8/0x160) from [<c004b504>] (kthread_worker_fn+0x4c/0x164)
[ 3777.105000] [<c004b504>] (kthread_worker_fn+0x4c/0x164) from [<c004b710>] (kthread+0x8c/0x98)
[ 3777.115000] [<c004b710>] (kthread+0x8c/0x98) from [<c000f598>] (kernel_thread_exit+0x0/0x8)

该错误是概率性的,并不是每次都出现,我google和百度发现类似问题的原因是如下:
中断处理函数中调用了可以休眠的函数,如semaphore,mutex,sleep之类的可休眠的函数,
而linux内核要求在中断处理的时候,不允许系统调度,不允许抢占,要等到中断处理完成才能做其他事情。
因此,要充分考虑中断处理的时间,一定不能太久。

但是我这边并不是在中断系统中,发了几天时间也没有找出原因来,我追踪源码最后到了pl330_request_channel里面的pm_runtime_get_sync(pl330->pinfo->dev); 这个RPM函数因为调用__schedule 而发生原子错误,由于这个错误时概率性事件,小弟百思不得其解,求大神们指点一二。我的子系统驱动函数应该没有问题,内核版本为三星平台3.5,开发板是用的友善之臂的。

我通过修改内核源码是解决了这个问题,我的修改如下,我基本上是吧DMA的RPM唤醒机制屏蔽掉了。
1.将pl330_probe函数里的pm_runtime_put(&adev->dev); 注释掉;
2.将pl330_request_channel的pm_runtime_get_sync注释掉;
3.将pl330_release_channel的pm_runtime_put(pl330->pinfo->dev);注释掉;
这样修改之后不会出现上述问题,具体请看下面注释:

static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
{
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);

/* Acquire DMA channels */
while (!acquire_dma(sdd))  //原先这里面有调用pm_runtime_ 就把里面的注释了
msleep(10);

pm_runtime_get_sync(&sdd->pdev->dev);//但是为什么这里调用就不会出现原子错误呢???

return 0;
}

static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi)
{
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);

/* Free DMA channels */
sdd->ops->release(sdd->rx_dma.ch, &s3c64xx_spi_dma_client);//原先这里面也有调用pm_runtime_ 就把里面的注释了
sdd->ops->release(sdd->tx_dma.ch, &s3c64xx_spi_dma_client);//原先这里面也有调用pm_runtime_ 就把里面的注释了

pm_runtime_put(&sdd->pdev->dev);

return 0;
}

求大神指点!感激万分!!谢谢!!

以下是我的回复

网上说“中断处理函数中调用了可以休眠的函数,如semaphore,mutex,sleep之类的可休眠的函数”就会打印
“[ 3777.005000] BUG: scheduling while atomic: spi0/25/0x00000002”这种log。
这种说法有一定道理,但没有说到本质。
我想你也找过打印上面log的地方,就在__schedule_bug()函数里。
运行__schedule_bug()函数的条件是
static inline void schedule_debug(struct task_struct *prev)
if (unlikely(in_atomic_preempt_off() && !prev->exit_state))
__schedule_bug(prev);
}
所以打印上面Log的条件是在preempt_disable的进程里,运行schedule()函数。
也就是说某个进程已经不允许其他进程抢占,但在这个进程的某个函数里边却运行了schedule()试图放弃当前的进程执行权。
你可以看一下“[ 3777.005000] BUG: scheduling while atomic: spi0/25/0x00000002”里边0x00000002代表的意思,
其实就是preempt_count的值,这个值大于0表示当前进程不能被抢占。
那可以找一下在哪里会增加preempt_count的值。我没有你的代码,所以看不到详细的内容。
但我找了我本地的代码,可能是三星的spi驱动里边设置的spinlock函数。
你可以找一下spinlock的实现,里边都有增加preempt_count。
所以要避免出现上面的错误,必须要注意spinlock lock和spinlock unlock的地方。最好在schedule之前解spinlock锁,在schedule之后再打开。
以避免出现上面的错误

你可能感兴趣的:(Linux内核架构)