inurl 湖北网站建设/惠州网络营销
最近做的一个项目,系统启动时发现有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_IO 0x00000100
#define IORESOURCE_MEM 0x00000200
#define IORESOURCE_IRQ 0x00000400
#define IORESOURCE_DMA 0x00000800
#define IORESOURCE_BUS 0x00001000
看看__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);
}