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;
相关推荐
切糕师学AI1 小时前
家庭网络“双网关”现象解析与通用桥接配置指南
网络·网关
帅气的钟先生2 小时前
OpenClaw + QQBot 实战:从 0 到 1 搭建你的消息自动化助手
运维·人工智能·自动化
techdashen2 小时前
把 Matrix 聊天服务器搬到 Serverless 上,还顺便免费升级了量子加密
运维·服务器·serverless
一叶龙洲2 小时前
Ubuntu24.04向日葵远程控制
linux·运维·ubuntu
计算机安禾2 小时前
【计算机网络】第2篇:端到端通信的形式化刻画——时延、带宽、丢包与吞吐量的数学模型
网络·计算机网络
似水এ᭄往昔2 小时前
【Linux】--文件系统之软硬链接
linux·运维·服务器
叶 落2 小时前
Ubuntu 通过 Docker 安装 Mysql8
linux·ubuntu·docker
灰子学技术2 小时前
Envoy TCP 层面的 Metric 指标分析
开发语言·网络·网络协议·tcp/ip·php
网络工程小王2 小时前
【LangChain Prompt 完整指南】提示词篇
运维·人工智能·学习