3.3 管理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的。
  
    3.3.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结构。
  
    ②然后,相应的根据参数?**跏蓟峙涞膔esource结构。注意!flags成员被初始化为IORESOURCE_BUSY。
  
    ③接下来,用一个for循环开始进行资源分配,循环体的步骤如下:
  
    l 首先,调用__request_resource()函数进行资源分配。假如返回NULL,说明分配成功,因此就执行break语句推出for循环,返回所分配的resource结构的指针,函数成功地结束。
  
    l 假如__request_resource()函数分配不成功,则进一步判断所返回的冲突资源节点是否就是父资源节点parent。假如不是,则将分配行为下降一个层次,即试图在当前冲突的资源节点中进行分配(只有在冲突的资源节点没有配置IORESOURCE_BUSY的情况下才能够),于是让 parent指针等于conflict,并在conflict->flags&IORESOURCE_BUSY为0的情况下执行 continue语句继续for循环。
  
    l 否则假如相冲突的资源节点就是父节点parent,或相冲突资源节点配置了IORESOURCE_BUSY标志位,则宣告分配失败。于是调用kfree ()函数释放所分配的resource结构,并将res指针置为NULL,最后用break语句推出for循环。
  
    ④最后,返回所分配的resource结构的指针。
  
    3.3.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 <lx-lx>
  , 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循环。假如属于,则执行下列步骤:
  
    l 先看看当前资源节点是否配置了IORESOURCE_BUSY标志位。假如没有配置该标志位,则说明该资源节点下面可能还会有子节点,因此将扫描过程下降一个层次,于是修改p指针,使他指向res->child,然后执行continue语句继续for循环。
  
    l 假如配置了IORESOURCE_BUSY标志位。则一定要确保当前资源节点就是所指定的I/O区域,然后将当前资源节点从其父资源的child链表中去除。这能够通过让前一个兄弟资源节点的sibling指针指向当前资源节点的下一个兄弟资源节点来实现(即让*p=res->sibling),最后调用kfree()函数释放当前资源节点的resource结构。然后函数就能够成功返回了。

文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!