免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1760 | 回复: 3
打印 上一主题 下一主题

[进程管理] 安卓内核代码在调用SPI发送信息时,出现原子错误,求大神指点! [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2015-06-05 16:47 |只看该作者 |倒序浏览
最近在调试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/0x5
[ 3777.010000] [<c0058084>] (__schedule_bug+0x44/0x5 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/0x8
[ 3777.085000] [<c027a034>] (s3c64xx_spi_prepare_transfer+0x44/0x8 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/0x9
[ 3777.115000] [<c004b710>] (kthread+0x8c/0x9 from [<c000f598>] (kernel_thread_exit+0x0/0x

该错误是概率性的,并不是每次都出现,我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;
}

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

论坛徽章:
0
2 [报告]
发表于 2015-06-05 16:51 |只看该作者
部分内核源码:
static void *pl330_request_channel(const struct pl330_info *pi)
{
struct pl330_thread *thrd = NULL;
struct pl330_dmac *pl330;
unsigned long flags;
int chans, i;

if (!pi || !pi->pl330_data)
return NULL;

pl330 = pi->pl330_data;

if (pl330->state == DYING)
return NULL;

chans = pi->pcfg.num_chan;

spin_lock_irqsave(&pl330->lock, flags);

for (i = 0; i < chans; i++) {
thrd = &pl330->channels[i];
if ((thrd->free) && (!_manager_ns(thrd) ||
_chan_ns(pi, i))) {
thrd->ev = _alloc_event(thrd);
if (thrd->ev >= 0) {
thrd->free = false;
thrd->lstenq = 1;
thrd->req[0].r = NULL;
mark_free(thrd, 0);
thrd->req[1].r = NULL;
mark_free(thrd, 1);
break;
}
}
thrd = NULL;
}

spin_unlock_irqrestore(&pl330->lock, flags);
pm_runtime_get_sync(pl330->pinfo->dev);

return thrd;
}

static void pl330_release_channel(void *ch_id)
{
struct pl330_thread *thrd = ch_id;
struct pl330_dmac *pl330;
unsigned long flags;

if (!thrd || thrd->free)
return;

_stop(thrd);

_callback(thrd->req[1 - thrd->lstenq].r, PL330_ERR_ABORT);
_callback(thrd->req[thrd->lstenq].r, PL330_ERR_ABORT);

pl330 = thrd->dmac;

spin_lock_irqsave(&pl330->lock, flags);
_free_event(thrd, thrd->ev);
thrd->free = true;
spin_unlock_irqrestore(&pl330->lock, flags);
pm_runtime_put(pl330->pinfo->dev);
}

static int __devinit
pl330_probe(struct amba_device *adev, const struct amba_id *id)
{
struct dma_pl330_platdata *pdat;
struct dma_pl330_dmac *pdmac;
struct dma_pl330_chan *pch;
struct pl330_info *pi;
struct dma_device *pd;
struct resource *res;
int i, ret, irq;
int num_chan;

pdat = adev->dev.platform_data;

/* Allocate a new DMAC and its Channels */
pdmac = kzalloc(sizeof(*pdmac), GFP_KERNEL);
if (!pdmac) {
dev_err(&adev->dev, "unable to allocate mem\n");
return -ENOMEM;
}

pi = &pdmac->pif;
pi->dev = &adev->dev;
pi->pl330_data = NULL;
pi->mcbufsz = pdat ? pdat->mcbuf_sz : 0;

res = &adev->res;
request_mem_region(res->start, resource_size(res), "dma-pl330");

pi->base = ioremap(res->start, resource_size(res));
if (!pi->base) {
ret = -ENXIO;
goto probe_err1;
}

pdmac->clk = clk_get(&adev->dev, "dma");
if (IS_ERR(pdmac->clk)) {
dev_err(&adev->dev, "Cannot get operation clock.\n");
ret = -EINVAL;
goto probe_err2;
}

amba_set_drvdata(adev, pdmac);

#ifndef CONFIG_PM_RUNTIME
/* enable dma clk */
clk_enable(pdmac->clk);
#endif

irq = adev->irq[0];
ret = request_irq(irq, pl330_irq_handler, 0,
dev_name(&adev->dev), pi);
if (ret)
goto probe_err3;

ret = pl330_add(pi);
if (ret)
goto probe_err4;

INIT_LIST_HEAD(&pdmac->desc_pool);
spin_lock_init(&pdmac->pool_lock);

/* Create a descriptor pool of default size */
if (!add_desc(pdmac, GFP_KERNEL, NR_DEFAULT_DESC))
dev_warn(&adev->dev, "unable to allocate desc\n");

pd = &pdmac->ddma;
INIT_LIST_HEAD(&pd->channels);

/* Initialize channel parameters */
if (pdat)
num_chan = max_t(int, pdat->nr_valid_peri, pi->pcfg.num_chan);
else
num_chan = max_t(int, pi->pcfg.num_peri, pi->pcfg.num_chan);

pdmac->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL);

for (i = 0; i < num_chan; i++) {
pch = &pdmac->peripherals[i];
if (!adev->dev.of_node)
pch->chan.private = pdat ? &pdat->peri_id[i] : NULL;
else
pch->chan.private = adev->dev.of_node;

INIT_LIST_HEAD(&pch->work_list);
spin_lock_init(&pch->lock);
pch->pl330_chid = NULL;
pch->chan.device = pd;
pch->dmac = pdmac;

/* Add the channel to the DMAC list */
list_add_tail(&pch->chan.device_node, &pd->channels);
}

pd->dev = &adev->dev;
if (pdat) {
pd->cap_mask = pdat->cap_mask;
} else {
dma_cap_set(DMA_MEMCPY, pd->cap_mask);
if (pi->pcfg.num_peri) {
dma_cap_set(DMA_SLAVE, pd->cap_mask);
dma_cap_set(DMA_CYCLIC, pd->cap_mask);
}
}

pd->device_alloc_chan_resources = pl330_alloc_chan_resources;
pd->device_free_chan_resources = pl330_free_chan_resources;
pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy;
pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic;
pd->device_tx_status = pl330_tx_status;
pd->device_prep_slave_sg = pl330_prep_slave_sg;
pd->device_control = pl330_control;
pd->device_issue_pending = pl330_issue_pending;

ret = dma_async_device_register(pd);
if (ret) {
dev_err(&adev->dev, "unable to register DMAC\n");
goto probe_err5;
}

dev_info(&adev->dev,
"Loaded driver for PL330 DMAC-%d\n", adev->periphid);
dev_info(&adev->dev,
"\tDBUFF-%ux%ubytes Num_Chans-%u Num_Peri-%u Num_Events-%u\n",
pi->pcfg.data_buf_dep,
pi->pcfg.data_bus_width / 8, pi->pcfg.num_chan,
pi->pcfg.num_peri, pi->pcfg.num_events);

pm_runtime_put(&adev->dev);
return 0;

probe_err5:
pl330_del(pi);
probe_err4:
free_irq(irq, pi);
probe_err3:
#ifndef CONFIG_PM_RUNTIME
clk_disable(pdmac->clk);
#endif
clk_put(pdmac->clk);
probe_err2:
iounmap(pi->base);
probe_err1:
release_mem_region(res->start, resource_size(res));
kfree(pdmac);

return ret;
}

这些都是没有屏蔽的。

论坛徽章:
0
3 [报告]
发表于 2015-06-05 16:52 |只看该作者
顶一下,顶一下!!!

论坛徽章:
0
4 [报告]
发表于 2015-06-05 16:53 |只看该作者

顶一下,顶一下!!!
顶一下,顶一下!!!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP