IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> linux PCI/PCIe驱动之assign_requested_resources_sorted的理解 -> 正文阅读

[系统运维]linux PCI/PCIe驱动之assign_requested_resources_sorted的理解

1 assign_requested_resources_sorted

该函数是把要申请的资源, 按照对齐要求排序,最后从region中分配资源,配置到对应的PCI/PCIe设备的BAR配置空间中。
assign_requested_resources_sorted函数会调用pci_assign_resource函数,在pci_assign_resource函数中有两个需要关注的重要的函数,_pci_assign_resource和pci_update_resource,其调用过程,和对这两个函数的简单解释如下所示:

1.1 从初始化到资源配置的调用过程

|- pci_bus_size_bridges(bus);
|- pci_bus_assign_resources(bus);
  |- __pci_bus_assign_resources
    |- pbus_assign_resources_sorted
	  /* pci_dev->resource[]里记录有想申请的资源的大小, 
	  * 把这些资源按对齐的要求排序
	  * 比如资源A要求1K地址对齐,资源B要求32地址对齐
	  * 那么资源A排在资源B前面, 优先分配资源A
	  */
	  list_for_each_entry(dev, &bus->devices, bus_list)
	  	__dev_sort_resources(dev, &head);
	  // 分配资源
	  |- __assign_resources_sorted
          |- assign_requested_resources_sorted(head, &local_fail_head);
              |- pci_assign_resource
                  |- ret = _pci_assign_resource(dev, resno, size, align);
                  	  // 分配地址空间
                      |- __pci_assign_resource
                          |- pci_bus_alloc_resource
                              |- pci_bus_alloc_from_region
                                  /* Ok, try it out.. */
                                  |- ret = allocate_resource(r, res, size, ...);
                                      |- err = find_resource(root, new, size,...);
                                          |- __find_resource
                                              
                                              // 从资源链表中分配地址空间
                                              // 设置pci_dev->resource[]
                                              new->start = alloc.start;
                                              new->end = alloc.end;
                  // 把对应的PCI地址写入BAR
                  |- pci_update_resource(dev, resno);
                      |- pci_std_update_resource
                          /* 把CPU地址转换为PCI地址: PCI地址 = CPU地址 - offset 
                           * 写入BAR
                           */
                          pcibios_resource_to_bus(dev->bus, &region, res);
                          new = region.start;
                          reg = PCI_BASE_ADDRESS_0 + 4 * resno;
                          pci_write_config_dword(dev, reg, new);

1.2 _pci_assign_resource

在_pci_assign_resource函数中,最终要的函数是__find_resource,该函数在资源树中找到具有给定范围和对齐约束的资源。

1.2.1 调用过程

assign_requested_resources_sorted(head, &local_fail_head);
    pci_assign_resource
        ret = _pci_assign_resource(dev, resno, size, align);
        	// 分配地址空间
            __pci_assign_resource
                pci_bus_alloc_resource
                    pci_bus_alloc_from_region
                        /* Ok, try it out.. */
                        ret = allocate_resource(r, res, size, ...);
                            err = find_resource(root, new, size,...);
                                __find_resource
                                    // 从资源链表中分配地址空间
                                    // 设置pci_dev->resource[]
                                    new->start = alloc.start;
                                    new->end = alloc.end;

1.2.2 __find_resource函数实现

/*
 * Find empty slot in the resource tree with the given range and
 * alignment constraints
 */
static int __find_resource(struct resource *root, struct resource *old,
			 struct resource *new,
			 resource_size_t  size,
			 struct resource_constraint *constraint)
{
	struct resource *this = root->child;
	struct resource tmp = *new, avail, alloc;

	tmp.start = root->start;
	/*
	 * Skip past an allocated resource that starts at 0, since the assignment
	 * of this->start - 1 to tmp->end below would cause an underflow.
	 */
	if (this && this->start == root->start) {
		tmp.start = (this == old) ? old->start : this->end + 1;
		this = this->sibling;
	}
	for(;;) {
		if (this)
			tmp.end = (this == old) ?  this->end : this->start - 1;
		else
			tmp.end = root->end;

		if (tmp.end < tmp.start)
			goto next;

		resource_clip(&tmp, constraint->min, constraint->max);
		arch_remove_reservations(&tmp);

		/* Check for overflow after ALIGN() */
		avail.start = ALIGN(tmp.start, constraint->align);
		avail.end = tmp.end;
		avail.flags = new->flags & ~IORESOURCE_UNSET;
		if (avail.start >= tmp.start) {
			alloc.flags = avail.flags;
			alloc.start = constraint->alignf(constraint->alignf_data, &avail,
					size, constraint->align);
			alloc.end = alloc.start + size - 1;
			if (alloc.start <= alloc.end &&
			    resource_contains(&avail, &alloc)) {
				new->start = alloc.start;
				new->end = alloc.end;
				return 0;
			}
		}

next:		if (!this || this->end == root->end)
			break;

		if (this != old)
			tmp.start = this->end + 1;
		this = this->sibling;
	}
	return -EBUSY;
}

1.3 pci_update_resource

pci_update_resource函数最重要的功能是把前面在_pci_assign_resource函数处理流程中最终通过调用pci_std_update_resource函数把给设备分配的资源配置到对应的设备BAR空间中。

1.3.1 调用过程

assign_requested_resources_sorted(head, &local_fail_head);
    pci_assign_resource
        // 把对应的PCI地址写入BAR
        pci_update_resource(dev, resno);
            pci_std_update_resource
                /* 把CPU地址转换为PCI地址: PCI地址 = CPU地址 - offset 
                 * 写入BAR
                 */
                pcibios_resource_to_bus(dev->bus, &region, res);
                new = region.start;
                reg = PCI_BASE_ADDRESS_0 + 4 * resno;
                pci_write_config_dword(dev, reg, new);

1.3.2 pci_std_update_resource函数实现

在pci_std_update_resource函数中,需要重点关注两部分

  • pcibios_resource_to_bus该函数是在region资源中查找和分配的资源匹配类型并且包含设备需要的资源。
  • 将前面分配的资源的起始地址写入设备对应的BAR寄存器空间中
    • reg = PCI_BASE_ADDRESS_0 + 4 * resno;
    • pci_write_config_dword(dev, reg, new);
static void pci_std_update_resource(struct pci_dev *dev, int resno)
{
	struct pci_bus_region region;
	bool disable;
	u16 cmd;
	u32 new, check, mask;
	int reg;
	struct resource *res = dev->resource + resno;

	/* Per SR-IOV spec 3.4.1.11, VF BARs are RO zero */
	if (dev->is_virtfn)
		return;

	/*
	 * Ignore resources for unimplemented BARs and unused resource slots
	 * for 64 bit BARs.
	 */
	if (!res->flags)
		return;

	if (res->flags & IORESOURCE_UNSET)
		return;

	/*
	 * Ignore non-moveable resources.  This might be legacy resources for
	 * which no functional BAR register exists or another important
	 * system resource we shouldn't move around.
	 */
	if (res->flags & IORESOURCE_PCI_FIXED)
		return;

	pcibios_resource_to_bus(dev->bus, &region, res);
	new = region.start;

	if (res->flags & IORESOURCE_IO) {
		mask = (u32)PCI_BASE_ADDRESS_IO_MASK;
		new |= res->flags & ~PCI_BASE_ADDRESS_IO_MASK;
	} else if (resno == PCI_ROM_RESOURCE) {
		mask = PCI_ROM_ADDRESS_MASK;
	} else {
		mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
		new |= res->flags & ~PCI_BASE_ADDRESS_MEM_MASK;
	}

	if (resno < PCI_ROM_RESOURCE) {
		reg = PCI_BASE_ADDRESS_0 + 4 * resno;
	} else if (resno == PCI_ROM_RESOURCE) {

		/*
		 * Apparently some Matrox devices have ROM BARs that read
		 * as zero when disabled, so don't update ROM BARs unless
		 * they're enabled.  See https://lkml.org/lkml/2005/8/30/138.
		 */
		if (!(res->flags & IORESOURCE_ROM_ENABLE))
			return;

		reg = dev->rom_base_reg;
		new |= PCI_ROM_ADDRESS_ENABLE;
	} else
		return;

	/*
	 * We can't update a 64-bit BAR atomically, so when possible,
	 * disable decoding so that a half-updated BAR won't conflict
	 * with another device.
	 */
	disable = (res->flags & IORESOURCE_MEM_64) && !dev->mmio_always_on;
	if (disable) {
		pci_read_config_word(dev, PCI_COMMAND, &cmd);
		pci_write_config_word(dev, PCI_COMMAND,
				      cmd & ~PCI_COMMAND_MEMORY);
	}

	pci_write_config_dword(dev, reg, new);
	pci_read_config_dword(dev, reg, &check);

	if ((new ^ check) & mask) {
		pci_err(dev, "BAR %d: error updating (%#08x != %#08x)\n",
			resno, new, check);
	}

	if (res->flags & IORESOURCE_MEM_64) {
		new = region.start >> 16 >> 16;
		pci_write_config_dword(dev, reg + 4, new);
		pci_read_config_dword(dev, reg + 4, &check);
		if (check != new) {
			pci_err(dev, "BAR %d: error updating (high %#08x != %#08x)\n",
				resno, new, check);
		}
	}

	if (disable)
		pci_write_config_word(dev, PCI_COMMAND, cmd);
}
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-05-19 12:06:11  更:2022-05-19 12:07:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 15:22:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码