`
mmdev
  • 浏览: 13300071 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

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_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);
}


分享到:
评论

相关推荐

    Linux IO 之 IO与网络模型.pdf

    抢占是一种机制,它可以在时间片用完后调用 schedule 函数,以便让出 CPU 控制权。抢占可以提高系统的响应速度和性能。 七、Per-CPU 变量 Per-CPU 变量是一种机制,它可以解决 CPU 各自使用的 L2 cache 数据与内存...

    23届秋招嵌入式-ARM体系与架构

    在嵌入式系统中,DMA(Direct Memory Access)是一种无须 CPU 的参与,就可以让外设与系统内存之间进行双向数据传输的硬件机制,使用 DMA 可以使系统 CPU 从实际的 IO 数据传输过程中摆脱出来,从而大大提高系统的...

    XRM复习.pdf

    ·共享内存(Shared Memory):映射一段能被其他进程访问的内存,由一个进程创建,多个进程可访问。优点是无需复制,速度快,信息量大,但通信是通过将共享内存缓冲区直接附加到进程虚拟地址空间中实现,因此存在...

    stm32知识思维导图

    stm32知识思维导图 STM32是一种基于ARM Cortex-M内核的微控制器单元(MCU),它广泛应用于嵌入式系统、自动控制、机器人、智能家居等领域。...* DMA_M2M设置2个 memory 中的变量互相访问,一般是禁止的

    cxuan-os-修改第二版.pdf

    * 主存储器(Memory)的结构和工作原理 *辅助存储器(Disk)的结构和工作原理 三、进程管理 * 进程的定义和类型(进程、线程、协程) * 进程的生命周期(创建、执行、等待、结束) * 进程的状态转换(就绪、运行、...

    云计算性能测评指标及方法研究.pdf

    在动态场景下,用户业务量的增大导致性能指标劣化,如时延增加和IO速度变慢等,因此需要一个更加科学和合理的测评方法。 针对IaaS层的CPU性能测评,文档分析了目前主要通过在CPU资源充足的主机上创建虚拟机进行测试...

    SQLServer内存问题排查思路.docx

    sys.sysprocesses视图中,查找与IO相关的等待类型(ASYNC_IO_COMPLETION、PAGEIOLATCH_SH等)可识别数据库页的读写压力。waittype为0x00B9、0x007B、0x011A的进程等待事件揭示了Stolen Memory和Multi-page缓存的问题...

    嵌入式课程设计I/O接口

    嵌入式系统是一种专用计算机系统,它被设计用于执行特定任务,通常在限制的硬件和资源环境下。在嵌入式课程设计中,I/O接口是一个关键的组成部分,它连接着嵌入式系统的微处理器与其他外部设备,如传感器、显示器、...

    hadoop 2.9.0 mapred-default.xml 属性集

    Hadoop是一个开源的、能够存储和处理大规模数据集的软件框架。Hadoop 2.9.0版本中的mapred-default...由于有些内容在原文中可能被OCR扫描错误或者漏识别,需要结合实际的mapred-site.xml文件和Hadoop文档进行深入理解。

    H13-527-HCIP云计算试题.docx

    【H13-527-HCIP 云计算试题】涉及华为的 HCIP(Huawei Certified ICT Professional)认证,这是华为认证体系中的一个重要级别,专为具有云计算领域专业知识和技术技能的专业人士设计。试题涵盖FusionCompute,华为的...

    OSAssignment3

    "Craft.io调度"可能是指一种特定的调度策略或者是一个模拟或分析调度问题的工具,但具体的细节没有给出,我们只能根据一般调度理论进行讨论。 在操作系统中,主要有四种调度层次:高级调度(Job Scheduler)、中级...

Global site tag (gtag.js) - Google Analytics