科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道Linux内核对I/O端口的管理实现(下) (1)

Linux内核对I/O端口的管理实现(下) (1)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

在find_resource()函数的基础上,函数allocate_resource()实现:在一颗资源树中分配一条指定大小的、且包含在指定区域[min,max]中的、未使用资源区域。其源代码如下

作者:linuxaid.com 来源:linuxaid.com 2007年10月19日

关键字: 管理 I/O 内核 Linux

  • 评论
  • 分享微博
  • 分享邮件

5 分配接口allocate_resource()

在find_resource()函数的基础上,函数allocate_resource()实现:在一颗资源树中分配一条指定大小的、且包含在指定区域[min,max]中的、未使用资源区域。其源代码如下:

/*
 * Allocate empty slot in the resource tree given range and alignment.
 */
int allocate_resource(struct resource *root, struct resource *new,
		      unsigned long size,
		      unsigned long min, unsigned long max,
		      unsigned long align,
		      void (*alignf)(void *, struct resource *, unsigned long),
		      void *alignf_data)
{
    int err;

    write_lock(&resource_lock);
    err = find_resource(root, new, size, min, max, align, alignf, alignf_data);
    if (err >= 0 && __request_resource(root, new))
	err = -EBUSY;
    write_unlock(&resource_lock);
    return err;
}

6 获取资源的名称列表

函数get_resource_list()用于获取根节点root的子资源名字列表。该函数主要用来支持/proc/文件系统(比如实现proc/ioports文件和/proc/iomem文件)。其源代码如下:

int get_resource_list(struct resource *root, char *buf, int size)
{
	char *fmt;
	int retval;

	fmt = "        %08lx-%08lx : %s
";
	if (root->end < 0x10000)
		fmt = "        %04lx-%04lx : %s
";
	read_lock(&resource_lock);
	retval = do_resource_list(root->child, fmt, 8, buf, buf + size) - buf;
	read_unlock(&resource_lock);
	return retval;
}

可以看出,该函数主要通过调用内部静态函数do_resource_list()来实现其功能,其源代码如下:

/*
 * This generates reports for /proc/ioports and /proc/iomem
 */
static char * do_resource_list(struct resource *entry, const char *fmt, 
  int offset, char *buf, char *end)
{
	if (offset < 0)
		offset = 0;

	while (entry) {
		const char *name = entry->name;
		unsigned long from, to;

		if ((int) (end-buf) < 80)
			return buf;

		from = entry->start;
		to = entry->end;
		if (!name)
			name = "";

		buf += sprintf(buf, fmt + offset, from, to, name);
		if (entry->child)
		   buf = do_resource_list(entry->child, fmt, offset-2, buf, end);
		entry = entry->sibling;
	}

	return buf;
}

函数do_resource_list()主要通过一个while{}循环以及递归嵌套调用来实现,较为简单,这里就不在详细解释了。

管理I/O Region资源

Linux将基于I/O映射方式的I/O端口和基于内存映射方式的I/O端口资源统称为“I/O区域”(I/O Region)。I/O Region仍然是一种I/O资源,因此它仍然可以用resource结构类型来描述。下面我们就来看看Linux是如何管理I/O Region的。

1 I/O Region的分配

在函数__request_resource()的基础上,Linux实现了用于分配I/O区域的函数__request_region(),如下:

struct resource * __request_region(struct resource *parent, 
  unsigned long start, unsigned long n, const char *name)
{
	struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL);

	if (res) {
		memset(res, 0, sizeof(*res));
		res->name = name;
		res->start = start;
		res->end = start + n - 1;
		res->flags = IORESOURCE_BUSY;

		write_lock(&resource_lock);

		for (;;) {
			struct resource *conflict;

			conflict = __request_resource(parent, res);
			if (!conflict)
				break;
			if (conflict != parent) {
				parent = conflict;
				if (!(conflict->flags & IORESOURCE_BUSY))
					continue;
			}

			/* Uhhuh, that didn't work out.. */
			kfree(res);
			res = NULL;
			break;
		}
		write_unlock(&resource_lock);
	}
	return res;
}

NOTE:

①首先,调用kmalloc()函数在SLAB分配器缓存中分配一个resource结构。

②然后,相应的根据参数值初始化所分配的resource结构。注意!flags成员被初始化为IORESOURCE_BUSY。

③接下来,用一个for循环开始进行资源分配,循环体的步骤如下:

首先,调用__request_resource()函数进行资源分配。如果返回NULL,说明分配成功,因此就执行break语句推出for循环,返回所分配的resource结构的指针,函数成功地结束。

如果__request_resource()函数分配不成功,则进一步判断所返回的冲突资源节点是否就是父资源节点parent。如果不是,则将分配行为下降一个层次,即试图在当前冲突的资源节点中进行分配(只有在冲突的资源节点没有设置IORESOURCE_BUSY的情况下才可以),于是让parent指针等于conflict,并在conflict->flags&IORESOURCE_BUSY为0的情况下执行continue语句继续for循环。

否则如果相冲突的资源节点就是父节点parent,或者相冲突资源节点设置了IORESOURCE_BUSY标志位,则宣告分配失败。于是调用kfree()函数释放所分配的resource结构,并将res指针置为NULL,最后用break语句推出for循环。

④最后,返回所分配的resource结构的指针。

2 I/O Region的释放

函数__release_region()实现在一个父资源节点parent中释放给定范围的I/O Region。实际上该函数的实现思想与__release_resource()相类似。其源代码如下:

void __release_region(struct resource *parent, 
    unsigned long start, unsigned long n)
{
	struct resource **p;
	unsigned long end;

	p = &parent->child;
	end = start + n - 1;

	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;
			kfree(res);
			return;
		}
		p = &res->sibling;
	}
	printk("Trying to free nonexistent resource <%08lx-%08lx>
", start, end);
}

类似地,该函数也是通过一个for循环来遍历父资源parent的child链表。为此,它让指针res指向当前正被扫描的子资源节点,指针p指向前一个子资源节点的sibling成员变量,p的初始值为指向parent->child。For循环体的步骤如下:

①让res指针指向当前被扫描的子资源节点(res=*p)。

②如果res指针为NULL,说明已经扫描完整个child链表,所以退出for循环。

③如果res指针不为NULL,则继续看看所指定的I/O区域范围是否完全包含在当前资源节点中,也即看看[start,start+n-1]是否包含在res->[start,end]中。如果不属于,则让p指向当前资源节点的sibling成员,然后继续for循环。如果属于,则执行下列步骤:

先看看当前资源节点是否设置了IORESOURCE_BUSY标志位。如果没有设置该标志位,则说明该资源节点下面可能还会有子节点,因此将扫描过程下降一个层次,于是修改p指针,使它指向res->child,然后执行continue语句继续for循环。

如果设置了IORESOURCE_BUSY标志位。则一定要确保当前资源节点就是所指定的I/O区域,然后将当前资源节点从其父资源的child链表中去除。这可以通过让前一个兄弟资源节点的sibling指针指向当前资源节点的下一个兄弟资源节点来实现(即让*p=res->sibling),最后调用kfree()函数释放当前资源节点的resource结构。然后函数就可以成功返回了。

3 检查指定的I/O Region是否已被占用

函数__check_region()检查指定的I/O Region是否已被占用。其源代码如下:

int __check_region(struct resource *parent, 
unsigned long start, unsigned long n)
{
	struct resource * res;

	res = __request_region(parent, start, n, "check-region");
	if (!res)
		return -EBUSY;

	release_resource(res);
	kfree(res);
	return 0;
}

该函数的实现与__check_resource()的实现思想类似。首先,它通过调用__request_region()函数试图在父资源parent中分配指定的I/O Region。如果分配不成功,将返回NULL,因此此时函数返回错误值-EBUSY表示所指定的I/O Region已被占用。如果res指针不为空则说明所指定的I/O Region没有被占用。于是调用__release_resource()函数将刚刚分配的资源释放掉(实际上是将res结构从parent的child链表去除),然后调用kfree()函数释放res结构所占用的内存。最后,返回0值表示指定的I/O Region没有被占用。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章