Linux网络驱动之Fixed-Link(29)

接前一篇文章:Linux网络驱动之Fixed-Link(28)

三、深入了解

上一回继续解析platform_device_add函数,没有讲完,本回继续往下讲解。为了便于理解和回顾,再次贴出其源码,在drivers/base/platform.c中,如下:

cpp 复制代码
/**
 * platform_device_add - add a platform device to device hierarchy
 * @pdev: platform device we're adding
 *
 * This is part 2 of platform_device_register(), though may be called
 * separately _iff_ pdev was allocated by platform_device_alloc().
 */
int platform_device_add(struct platform_device *pdev)
{
	u32 i;
	int ret;
 
	if (!pdev)
		return -EINVAL;
 
	if (!pdev->dev.parent)
		pdev->dev.parent = &platform_bus;
 
	pdev->dev.bus = &platform_bus_type;
 
	switch (pdev->id) {
	default:
		dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
		break;
	case PLATFORM_DEVID_NONE:
		dev_set_name(&pdev->dev, "%s", pdev->name);
		break;
	case PLATFORM_DEVID_AUTO:
		/*
		 * Automatically allocated device ID. We mark it as such so
		 * that we remember it must be freed, and we append a suffix
		 * to avoid namespace collision with explicit IDs.
		 */
		ret = ida_alloc(&platform_devid_ida, GFP_KERNEL);
		if (ret < 0)
			goto err_out;
		pdev->id = ret;
		pdev->id_auto = true;
		dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
		break;
	}
 
	for (i = 0; i < pdev->num_resources; i++) {
		struct resource *p, *r = &pdev->resource[i];
 
		if (r->name == NULL)
			r->name = dev_name(&pdev->dev);
 
		p = r->parent;
		if (!p) {
			if (resource_type(r) == IORESOURCE_MEM)
				p = &iomem_resource;
			else if (resource_type(r) == IORESOURCE_IO)
				p = &ioport_resource;
		}
 
		if (p) {
			ret = insert_resource(p, r);
			if (ret) {
				dev_err(&pdev->dev, "failed to claim resource %d: %pR\n", i, r);
				goto failed;
			}
		}
	}
 
	pr_debug("Registering platform device '%s'. Parent at %s\n",
		 dev_name(&pdev->dev), dev_name(pdev->dev.parent));
 
	ret = device_add(&pdev->dev);
	if (ret == 0)
		return ret;
 
 failed:
	if (pdev->id_auto) {
		ida_free(&platform_devid_ida, pdev->id);
		pdev->id = PLATFORM_DEVID_AUTO;
	}
 
	while (i--) {
		struct resource *r = &pdev->resource[i];
		if (r->parent)
			release_resource(r);
	}
 
 err_out:
	return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);

上一回开始解析__insert_resource函数,它是由platform_device_add函数中的insert_resource函数引出的。该函数总体上分为3段,上一回解析了第1段代码,本回解析后两段代码。为了便于理解和回顾,再次贴出__insert_resource函数源码,在kernel/linux-5.10-origin/kernel/resource.c中,如下:

cpp 复制代码
/*
 * Insert a resource into the resource tree. If successful, return NULL,
 * otherwise return the conflicting resource (compare to __request_resource())
 */
static struct resource * __insert_resource(struct resource *parent, struct resource *new)
{
	struct resource *first, *next;
 
	for (;; parent = first) {
		first = __request_resource(parent, new);
		if (!first)
			return first;
 
		if (first == parent)
			return first;
		if (WARN_ON(first == new))	/* duplicated insertion */
			return first;
 
		if ((first->start > new->start) || (first->end < new->end))
			break;
		if ((first->start == new->start) && (first->end == new->end))
			break;
	}
 
	for (next = first; ; next = next->sibling) {
		/* Partial overlap? Bad, and unfixable */
		if (next->start < new->start || next->end > new->end)
			return next;
		if (!next->sibling)
			break;
		if (next->sibling->start > new->end)
			break;
	}
 
	new->parent = parent;
	new->sibling = next->sibling;
	new->child = first;
 
	next->sibling = NULL;
	for (next = first; next; next = next->sibling)
		next->parent = new;
 
	if (parent->child == first) {
		parent->child = new;
	} else {
		next = parent->child;
		while (next->sibling != first)
			next = next->sibling;
		next->sibling = new;
	}
	return NULL;
}

前文书已讲过,__insert_resource函数的功能和返回值为:将资源插入资源树。若成功,返回空指针(NULL);否则返回冲突资源。

__insert_resource函数代码整体上分为三段,一段一段来看。

1)先尝试简单插入

2)检查冲突是否可嵌套(new完全包含所有冲突子节点)

代码片段如下:

cpp 复制代码
    for (next = first; ; next = next->sibling) {
		/* Partial overlap? Bad, and unfixable */
		if (next->start < new->start || next->end > new->end)
			return next;
		if (!next->sibling)
			break;
		if (next->sibling->start > new->end)
			break;
	}

上一步中的first指向了资源冲突的节点,见上一回中的示意图:

那么这里就由此节点向后遍历各个兄弟节点,作以下进一步判断:

  • 如果某兄弟节点的start < 新资源节点的start,或者该兄弟节点的end大于新资源节点的end,也就是图中的前三种情况,那么就直接返回该兄弟节点;
  • 如果直到遍历结束都没有前三种情况,那么就是第4种情况,跳出循环;
  • 如果下一个兄弟节点的起始还小于新节点的结束,那么说明不止冲突了一个节点,起码有两个节点资源冲突了。那么也跳出循环,往下进行。

3)插入new节点,调整父子关系

代码片段如下:

cpp 复制代码
	new->parent = parent;
	new->sibling = next->sibling;
	new->child = first;
 
	next->sibling = NULL;
	for (next = first; next; next = next->sibling)
		next->parent = new;
 
	if (parent->child == first) {
		parent->child = new;
	} else {
		next = parent->child;
		while (next->sibling != first)
			next = next->sibling;
		next->sibling = new;
	}
	return NULL;
相关推荐
艾伦_耶格宇14 分钟前
【DOCKER容器实战】-2MySQL
运维·docker·容器
Brilliantwxx20 分钟前
42525
运维
小鱼仙官22 分钟前
Ubuntu C++ NTP校时程序
linux·c++·ubuntu
RisunJan23 分钟前
Linux命令-vgdisplay(显示卷组详细属性)
linux·运维·数据库
深念Y1 小时前
RIO-UL00(EMUI 4.1 / Android 6.0.1 / arm64)开机自启动 sshd
android·linux·华为·安卓·chroot·sshd·emui
Escalating_xu1 小时前
【Linux线程】线程控制全解析:终止、join/detach、cancel、线程栈与 NPTL(下篇)
java·linux·运维
老当益壮梁奶奶1 小时前
Linux软件编程学习笔记(二)Linux文件IO编程入门:从fopen到fgetc
linux·c语言·c++·笔记·学习
sunoo-2291 小时前
C 语言文件 IO 全攻略:从基础函数到实战踩坑(BMP 读取 + 词典查询)
linux·c语言·前端·笔记·vscode·学习
白猫不黑1 小时前
从运维到网络安全:转行优势、路径与薪资前景全解析
运维·网络·web安全·网络安全·信息安全·渗透测试
dog2501 小时前
多路径聚合效率的统计理论
网络·tcp/ip