读书人

iomemory地址被强占(一)

发布时间: 2013-03-06 16:20:31 作者: rapoo

iomemory地址被抢占(一)

最近做的一个项目,系统启动时发现有warning,并打印出了一堆dump stack。
看了下代码,发现是在esai的probe函数中调用request_mem_region时失败代码走到了错误处理的部分。
错误处理中disable了一个clock,warning的内容是说该clock没enable就disable了。

从代码看,存在两个问题:
1、request_mem_region为什么会失败。
2、为什么在disable clock之前没有enable。

先看看request_mem_region为什么会失败。
经大牛指点,request_mem_region失败,应该是因为申请的一块内存,部分/全部已被占用,所以导致申请失败。
为了看看申请的内存是否与其他io申请的内存释放有冲突,将/proc/iomem文件cat出来,发现esai申请的内存与ssi-0申请的内存重叠。
对照cpu的reference manual,发现esai很守规矩,使用的是memory map中给自己指定的memory范围;而ssi-0没使用memory map中指定的内存范围,抢占了esai的内存范围。
找到esai base address和ssi-0(对应的是硬件ssi-1) base address定义,发现定义没问题,与memory map中一致。
在esai和ssi的probe函数中加log,发现首先是ssi-1申请了正确的内存,ssi-2也申请了正确的内存,接下来ssi-0申请的内存范围是esai的内存范围。
因为ssi在esai前面进行的probe,导致esai申请内存时,内存已经被ssi-0申请了去,所以失败。
可疑点是,ssi的顺序为什么是1、2、0,而不是0、1、2。
看了board文件中添加resource的地方,发现是先添加了ssi 1、2、3,然后添加了esai。
跟了下添加ssi resource的代码,发现知道的序号其实用作了数组下标,数组下标都是从0开始的,这儿为什么是1、2、3?
是不是添加ssi 1\2\3的时候,1、2没问题,由于ssi只有0\1\2,添加3的时候,抢占了esai的位置。
将board中的ssi 1\2\3改为0\1\2,果然问题解决。

若只是止步于此,少了些乐趣。
不仅要知其然,也要知其所以然。

先看看request_mem_region的实现。

#define request_mem_region(start, n, name) __request_region(&iomem_resource, (start), (n), (name), 0)


看看iomem_resource的定义:

struct resource iomem_resource = {.name= "PCI mem",.start= 0,.end= -1,.flags= IORESOURCE_MEM,};


resource type有以下几种类型:

#define IORESOURCE_IO0x00000100#define IORESOURCE_MEM0x00000200#define IORESOURCE_IRQ0x00000400#define IORESOURCE_DMA0x00000800#define IORESOURCE_BUS0x00001000


看看__request_region的实现:

/** * __request_region - create a new busy resource region * @parent: parent resource descriptor * @start: resource start address * @n: resource region size * @name: reserving caller's ID string * @flags: IO resource flags */struct resource * __request_region(struct resource *parent,   resource_size_t start, resource_size_t n,   const char *name, int flags){// 定义并初始化一个wait queue/*#define DECLARE_WAITQUEUE(name, tsk)\wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)*//*#define __WAITQUEUE_INITIALIZER(name, tsk) {\.private= tsk,\.func= default_wake_function,\.task_list= { NULL, NULL } }*/DECLARE_WAITQUEUE(wait, current);/*/* * Resources are tree-like, allowing * nesting etc.. */struct resource {resource_size_t start;resource_size_t end;const char *name;unsigned long flags;struct resource *parent, *sibling, *child;};*//*/** * kzalloc - allocate memory. The memory is set to zero. * @size: how many bytes of memory are required. * @flags: the type of memory to allocate (see kmalloc). */static inline void *kzalloc(size_t size, gfp_t flags){return kmalloc(size, flags | __GFP_ZERO);}*/struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);if (!res)return NULL;res->name = name;res->start = start;res->end = start + n - 1;res->flags = IORESOURCE_BUSY;res->flags |= flags;write_lock(&resource_lock);for (;;) {struct resource *conflict;// 检查申请的内存与其他内存是否冲突/×/* Return the conflict entry if you can't request it */static struct resource * __request_resource(struct resource *root, struct resource *new){resource_size_t start = new->start;resource_size_t end = new->end;struct resource *tmp, **p;// 头比尾大,自相矛盾if (end < start)return root;// 头还在根的头前面,越界了if (start < root->start)return root;// 尾在根的尾后面,也越界了if (end > root->end)return root;// 从下面的循环看,root的所有child应该是从低地址到高地址依次排开,并通过他们的sibling进行关联。// 刚开始,p指向root的最大孩子,也就是地址最低的,并判断其与new是否冲突。// 若不冲突,通过最大孩子的sibling成员,找到它的下一个兄弟,并判断其与new是否冲突。// 这样依次判断下去,直到找到了一个空child,或者找到的找到的child的start还在new的end之后,也即完全在new之后。p = &root->child;for (;;) {tmp = *p;if (!tmp || tmp->start > end) {new->sibling = tmp;*p = new;new->parent = root;return NULL;}p = &tmp->sibling;if (tmp->end < start)continue;return tmp;}}×/conflict = __request_resource(parent, res);if (!conflict)break;// 若有冲突,但冲突的不是根,并且conflict的标志并没有说是busy,也就是说并没有被占用,则说明new落在了原来根的一个child的范围内。// 也就是说conflict其实是new的根,那就将conflict赋值为new的根,再次进行冲突检测。if (conflict != parent) {parent = conflict;if (!(conflict->flags & IORESOURCE_BUSY))continue;}// 如果有冲突,并且conflict和new都是可软件多路复用的,则说明暂时是被别人占用了,// 要做到就是把锁释放了,并等待内存被释放if (conflict->flags & flags & IORESOURCE_MUXED) {/*/* * This is compatibility stuff for IO resources. * * Note how this, unlike the above, knows about * the IO flag meanings (busy etc). * * request_region creates a new busy region. * * check_region returns non-zero if the area is already busy. * * release_region releases a matching busy region. */static DECLARE_WAIT_QUEUE_HEAD(muxed_resource_wait);void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait){unsigned long flags;wait->flags &= ~WQ_FLAG_EXCLUSIVE;spin_lock_irqsave(&q->lock, flags);__add_wait_queue(q, wait);spin_unlock_irqrestore(&q->lock, flags);}static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new){list_add(&new->task_list, &head->task_list);}*/add_wait_queue(&muxed_resource_wait, &wait);// 释放锁write_unlock(&resource_lock);// TASK_INTERRUPTIBLE是可以被信号和wake_up()唤醒的,当信号到来时,进程会被设置为可运行。// TASK_UNINTERRUPTIBLE只能被wake_up()唤醒。set_current_state(TASK_UNINTERRUPTIBLE);/*asmlinkage void __sched schedule(void){struct task_struct *tsk = current;sched_submit_work(tsk);__schedule();}static inline void sched_submit_work(struct task_struct *tsk){if (!tsk->state)return;/* * If we are going to sleep and we have plugged IO queued, * make sure to submit it to avoid deadlocks. */if (blk_needs_flush_plug(tsk))blk_schedule_flush_plug(tsk);}*/// 调度函数,不能简单的一笔带过schedule();remove_wait_queue(&muxed_resource_wait, &wait);write_lock(&resource_lock);continue;}/* Uhhuh, that didn't work out.. */kfree(res);res = NULL;break;}write_unlock(&resource_lock);return res;}


上面有将wait添加到queue,在__release_region中调用了wake_up用来唤醒等待的进程。

/** * __release_region - release a previously reserved resource region * @parent: parent resource descriptor * @start: resource start address * @n: resource region size * * The described resource region must match a currently busy region. */void __release_region(struct resource *parent, resource_size_t start,resource_size_t n){struct resource **p;resource_size_t end;p = &parent->child;end = start + n - 1;write_lock(&resource_lock);for (;;) {struct resource *res = *p;if (!res)break;if (res->start <= start && res->end >= end) {if (!(res->flags & IORESOURCE_BUSY)) {p = &res->child;continue;}if (res->start != start || res->end != end)break;*p = res->sibling;write_unlock(&resource_lock);if (res->flags & IORESOURCE_MUXED)wake_up(&muxed_resource_wait);kfree(res);return;}p = &res->sibling;}write_unlock(&resource_lock);printk(KERN_WARNING "Trying to free nonexistent resource ""<%016llx-%016llx>\n", (unsigned long long)start,(unsigned long long)end);}


读书人网 >移动开发

热点推荐