设为首页 收藏本站
查看: 1183|回复: 0

[经验分享] 虚拟化原理之xen-io虚拟化

[复制链接]

尚未签到

发表于 2015-10-12 07:36:15 | 显示全部楼层 |阅读模式
第4章 io设备虚拟化
  Io设备的虚拟化,不可避免要涉及到设备的概念和隐藏在设备背后的总线。理解了这两个概念,就比较容易理解io设备的虚拟化。
4.1   设备,总线和驱动
  设备是计算机系统中一个重要概念。通常的显卡网卡声卡等设备,都是先插入计算机系统的PCI总线插槽(早期还有ISAMCA总线等。现在PC领域基本PCI总线统一了),然后安装驱动,之后应用程序可以通过文件系统打开和读写设备文件。这个过程可以从三个层面理解:设备本身的特性,总线和操作系统对设备的处理,驱动层次。
  以PCI设备为例,一个PCI设备,本身就包含一个配置表。配置表包含设备制造商填充的厂商信息,设备属性等等的通用配置信息。此外,设备厂商还应该提供设备的控制寄存器信息,通过这些控制寄存器,系统可以设置设备的状态,控制设备的运行,或者从设备获得状态信息。另外设备还可能配备了内存(也有的设备可能没有),系统可以读写设备的内存。用图3-1来解释设备的基本信息:
  
  图3-1  设备配置表的信息
  
  如上图,设备本身有一些配置信息。配置信息里面的设备内存基址,指示了设备内存的地址和长度,而设备寄存器基址,则指向了设备的寄存器地址和长度。该设备有两个寄存器,一个输入寄存器,一个输出寄存器。当输入寄存器写入数值后,可以从输出寄存器读到另一个数值。
  设备寄存器基址,这个概念有点难理解。实际上,可以看做是一个地址,对这个地址写指令,就可以控制设备。所以,设备寄存器其实就是设备的控制接口。这个接口必须要映射到计算机系统的io空间,这样内核就可以访问设备了。
  
4.1.1 io端口和io内存
  不同的处理器对io访问有不同的处理方式。对X86系统来说,专门提供了特别的指令来访问设备寄存器。所有这些设备寄存器占据了655368位的空间。这个空间称为计算机的IO端口空间。
  对上文的例子设备来说,需要把设备的寄存器基址纳入到系统的IO端口空间里面,然后驱动就可以通过系统提供的特别指令来访问设备的寄存器。假设设备厂商提供的寄存器基址是0x1c00,长度是8个字节。那么有两种情况,一种是这个0x1c00地址和别的设备没有冲突,可以直接使用,那么操作系统内核就记录设备的寄存器基址为0x1c00,驱动通过X86系统提供的io指令访问0x1c00 io地址,或者叫0x1c00 io端口,就可以设置设备输入寄存器的内容。访问地址0x1c04,就可以读到设备输出寄存器的内容。
  另外一种情况是其它设备也使用了0x1c00这个io地址。那么操作系统内核就需要寻找一个合适的寄存器基址,然后更新设备的寄存器基址,并记录到内核的设备信息里面。驱动使用x86io指令,访问这个更新的地址,就可以设置设备输入寄存器的内容了。
  通过设备的io端口访问设备寄存器来控制设备,这就是设备驱动的功能。设备厂商会提供设备寄存器的详细内容,这也是驱动开发者所必须关注的。而发现设备,扫描设备信息,为设备提供合适的io地址空间,这是内核的总线部分要处理的事情。后文将继续分析。
  设备的内存处理过程差不多一样。内核同样要读取设备内存基址,然后找到合适的内存空间,把设备的内存映射到内存空间。然后驱动就可以标准的内存接口访问设备的内存了。
4.1.2 总线
  设备的配置信息提供了设备寄存器基址和设备内存基址。因此首先要读到这两个寄存器的内容,也就是设备寄存器基址和长度以及设备内存基址和长度,然后操作系统才能安排合适的io端口和io内存。
  但是如何去读设备的配置信息?X86系统使用的PCI总线对这个问题的解决方法是:保留了8个字节的io端口地址,就是0xCF8~0xCFF。要访问设备的某个配置信息,先往0xCF8地址写入目标地址信息,然后通过0xCFC地址读数据,就可以获得这个配置信息。这里的写和读,都使用的是x86所特有的io指令。
  写入0xCF8的目标地址信息,是一种包括了总线号,设备号,功能号和配置寄存器地址的综合信息。
  1) 总线对设备的扫描和管理
  计算机系统的设备是如何被发现的?对PCI总线上的设备来说,这是总线扫描所得到的结果(非PCI总线各有各自的扫描方式)。每个PCI设备有一个总线号,一个设备号,一个功能号标识。Pci规范允许一个系统最多拥有256条总线,每条总线最多可以带32个设备,每个设备可以是最多8个功能的多功能板。Pci扫描就是对单条总线的地址范围进行扫描。根据前面关于pci配置信息的知识,如果某个地址存在设备,那么在0xCF8写入目标地址信息,就可以从0xCFC读到设备的信息。包括设备的io端口和io内存,设备的中断号和DMA信息。对每个读到的pci设备,都要为它创建设备对象。
  
  2) 总线对驱动和设备的管理
  当设备插入计算机系统时,可以找到自己的驱动开始工作。而升级了设备驱动,也能找到适合的设备,自动产生设备。这些功能其实就是pci总线结构完成的。通过代码分析来了解一下:
  
  代码清单3-1  pci_register_driver
  int __pci_register_driver(struct pci_driver *drv, struct module *owner)
  {
  int error;
        /*设置驱动的总线类型和参数*/
  /* initialize common driver fields */
  drv->driver.name = drv->name;
  drv->driver.bus = &pci_bus_type;
  drv->driver.owner = owner;
  drv->driver.kobj.ktype = &pci_driver_kobj_type;
  spin_lock_init(&drv->dynids.lock);
  INIT_LIST_HEAD(&drv->dynids.list);
  /* register with core */
  error = driver_register(&drv->driver);
  if (!error)
  error = pci_create_newid_file(drv);
  return error;
  } <object data=&quot;data:application/x-silverlight-2,&quot; type=&quot;application/x-silverlight-2&quot;
  注册一个pci驱动,其实就是调用了__pci_register_driver函数。首先设置了驱动的总线类型为pci,然后调用driver_register登记。
  
  代码清单3-2  driver_register
  int driver_register(struct device_driver * drv)
  {
  klist_init(&drv->klist_devices, klist_devices_get, klist_devices_put);
  init_completion(&drv->unloaded);
  return bus_add_driver(drv);
  }<object data=&quot;data:application/x-silverlight-2,&quot; type=&quot;application/x-silverlight-2&quot;
  实际是调用了bus_add_driver。bus_add_driver中真正起作用的是driver_attach这个函数。
  
  代码清单3-3  设
  void driver_attach(struct device_driver * drv)
  {
  bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
  } <object data=&quot;data:application/x-silverlight-2,&quot; type=&quot;application/x-silverlight-2&quot;
  这里就清楚了,driver_attach实际上把总线上面的所有设备都遍历了一遍,通过__driver_attach函数判断驱动和设备是否匹配。而__driver_attach实际是调用了driver_probe_device来检查设备和驱动的匹配关系。
  
  代码清单3-4  设
  int driver_probe_device(struct device_driver * drv, struct device * dev)
  {
  int ret = 0;
         /*总线定义了match函数,通过match函数判断是否匹配*/
  if (drv->bus->match && !drv->bus->match(dev, drv))
  goto Done;
  pr_debug(&quot;%s: Matched Device %s with Driver %s\n&quot;,
   drv->bus->name, dev->bus_id, drv->name);
  dev->driver = drv;
         /*匹配,调用probe函数*/
  if (dev->bus->probe) {
  ret = dev->bus->probe(dev);
  if (ret) {
  dev->driver = NULL;
  goto ProbeFailed;
  }
  } else if (drv->probe) {
  ret = drv->probe(dev);
  if (ret) {
  dev->driver = NULL;
  goto ProbeFailed;
  }
  }
  device_bind_driver(dev);
  ret = 1;
  pr_debug(&quot;%s: Bound Device %s to Driver %s\n&quot;,
   drv->bus->name, dev->bus_id, drv->name);
  goto Done;
   Done:
  return ret;
  } <object data=&quot;data:application/x-silverlight-2,&quot; type=&quot;application/x-silverlight-2&quot;
  判断驱动和设备是否匹配,首先是通过注册的match函数判断。通常情况,匹配是通过驱动里面包含的id表和扫描设备发现的id比较,如果相同,则说明驱动和设备是适合的。当匹配通过后,调用驱动里面的probe函数。这个函数往往用来继续对设备做进一步的检测工作。在后面块设备的驱动可以看到具体的用法。
4.1.3 设备中断
  Cpu虚拟化一节已经讨论了设备中断的处理。
  
4.2 虚拟化的设备驱动和总线
  半虚拟化结构的xen提出了虚拟设备的架构。Xen的虚拟设备架构采用前后端分离的设备驱动结构。虚拟设备驱动包含两个部分:domU中的前段设备驱动(fronted)和dom0中的后端设备驱动。后端设备驱动可以访问真实的硬件设备。
  前端设备驱动从Guest OS接收io请求,然后将io请求转发到后端,而后端接收到前端转发的设备请求后,检查请求是否合法,然后通过本地的设备驱动访问真实的硬件设备。Io完成后,后端设备驱动通知前端设备驱动已经准备就绪,然后前端驱动向Guest OS报告io操作完成。
  在Xen的半虚拟化架构中,同样需要一种机制来发现设备,连接设备和驱动,自动匹配设备和驱动。而且和linux不同的是,因为前端设备和后端设备是联动的关系,当某一方设备变动的时候,还必须通知另一方设备的变动情况。为了完成这个工作,xen提供了一条虚拟总线xenbus来管理所有的虚拟设备和驱动。Xen系统的所有虚拟设备都要注册到xenbus。Pci总线也是作为一个设备注册到xenbus ,通过注册的pci总线,执行扫描动作可以产生所有的pci设备。而所有的虚拟驱动也都要注册到xenbus,从而可以自动完成虚拟设备和驱动的匹配。
4.2.1 Pci前端注册和扫描
  
  代码清单3-5  pcifront_init
  static int __init pcifront_init(void)
  {
  if (!is_running_on_xen())
  return -ENODEV;
  return xenbus_register_frontend(&xenbus_pcifront_driver);
  }<object data=&quot;data:application/x-silverlight-2,&quot;
  Pcifront作为一个前端驱动注册到xenbus。注册驱动后,会自动调用驱动的probe函数,完成初始化后,把设备状态改为初始完成状态。通过xenbuspci后端检测到前端变化,将信息同步后,将状态改为connected。前端此时要调用pcifront_try_connect完成扫描。
  代码清单3-6  设
  static int pcifront_try_connect(struct pcifront_device *pdev)
  {
  int err = -EFAULT;
  int i, num_roots, len;
  char str[64];
  unsigned int domain, bus;
  spin_lock(&pdev->dev_lock);
  /* Only connect once */
  if (xenbus_read_driver_state(pdev->xdev->nodename) !=
      XenbusStateInitialised)
  goto out;
         /*检查状态,保证只连接一次*/
  err = pcifront_connect(pdev);
  if (err) {
  xenbus_dev_fatal(pdev->xdev, err,
   &quot;Error connecting PCI Frontend&quot;);
  goto out;
  }
        
  err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend,
     &quot;root_num&quot;, &quot;%d&quot;, &num_roots);
  if (err == -ENOENT) {
  xenbus_dev_error(pdev->xdev, err,
   &quot;No PCI Roots found, trying 0000:00&quot;);
  err = pcifront_scan_root(pdev, 0, 0);
  num_roots = 0;
  } else if (err != 1) {
  if (err == 0)
  err = -EINVAL;
  xenbus_dev_fatal(pdev->xdev, err,
   &quot;Error reading number of PCI roots&quot;);
  goto out;
  }
  for (i = 0; i < num_roots; i&#43;&#43;) {
  len = snprintf(str, sizeof(str), &quot;root-%d&quot;, i);
  if (unlikely(len >= (sizeof(str) - 1))) {
  err = -ENOMEM;
  goto out;
  }
                   /*获得pci的域号和总线号*/
  err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str,
     &quot;%x:%x&quot;, &domain, &bus);
  if (err != 2) {
  if (err >= 0)
  err = -EINVAL;
  xenbus_dev_fatal(pdev->xdev, err,
   &quot;Error reading PCI root %d&quot;, i);
  goto out;
  }
                   /*扫描pci总线*/
  err = pcifront_scan_root(pdev, domain, bus);
  if (err) {
  xenbus_dev_fatal(pdev->xdev, err,
   &quot;Error scanning PCI root %04x:%02x&quot;,
   domain, bus);
  goto out;
  }
  }
  err = xenbus_switch_state(pdev->xdev, XenbusStateConnected);
  if (err)
  goto out;
        out:
  spin_unlock(&pdev->dev_lock);
  return err;
  }<object data=&quot;data:application/x-silverlight-2,&quot; type=&quot;application/x-silverlight-2&quot;
  获得后端设置的pci域号和pci总线号后,调用pcifront_scan_root来扫描设备。
  <object data=&quot;data:application/x-silverlight-2,&quot;
  代码清单3-7  pcifront_scan_root
  int pcifront_scan_root(struct pcifront_device *pdev,
         unsigned int domain, unsigned int bus)
  {
  struct pci_bus *b;
  struct pcifront_sd *sd = NULL;
  struct pci_bus_entry *bus_entry = NULL;
  int err = 0;
  bus_entry = kmalloc(sizeof(*bus_entry), GFP_KERNEL);
  sd = kmalloc(sizeof(*sd), GFP_KERNEL);
  if (!bus_entry || !sd) {
  err = -ENOMEM;
  goto err_out;
  }
  pcifront_init_sd(sd, domain, pdev);
  b = pci_scan_bus_parented(&pdev->xdev->dev, bus,
    &pcifront_bus_ops, sd);
  if (!b) {
  dev_err(&pdev->xdev->dev,
  &quot;Error creating PCI Frontend Bus!\n&quot;);
  err = -ENOMEM;
  goto err_out;
  }
  bus_entry->bus = b;
  list_add(&bus_entry->list, &pdev->root_buses);
  /* Claim resources before going &quot;live&quot; with our devices */
  pci_walk_bus(b, pcifront_claim_resource, pdev);
  pci_bus_add_devices(b);
  return 0;
        err_out:
  kfree(bus_entry);
  kfree(sd);
  return err;
  }<object data=&quot;data:application/x-silverlight-2,&quot; type=&quot;application/x-silverlight-2&quot;
  pci_scan_bus_parented就是对pci总线的扫描,扫描之后,产生的pci设备就连接到总线的链表头,从而通过总线可以遍历pci设备。而pci_walk_bus要把设备的资源(io端口,io内存)全都注册到系统内。
  
4.3 虚拟化的块设备
  硬盘等块设备采用了前后端的设备驱动架构。对原生的驱动比较,采用半虚拟化之后的驱动分成了两部分。在domU里面运行的是前端驱动,而dom0里面运行后端驱动。
  后端驱动和前端一一对应。当前端驱动状态改变的时候,就会触发事件,通知后端驱动。当后端驱动完成事务处理后,通过改变状态,就可以触发事件,通知前端驱动。所以前端驱动和后端驱动是互相呼应,共同完成io请求。
4.3.1 块设备的前端驱动
  前端驱动定义了一个xenbus的驱动类型。如清单所示:
  代码清单3-8  blkfront
  static struct xenbus_driver blkfront = {
  .name = &quot;vbd&quot;,
  .owner = THIS_MODULE,
  .ids = blkfront_ids,
  .probe = blkfront_probe,
  .remove = blkfront_remove,
  .resume = blkfront_resume,
  .otherend_changed = backend_changed,
  
  根据前面总线的分析,这个驱动要注册到xenbus总线。如果和xenbus扫描出来的设备能匹配,那么要调用驱动提供的probe函数,做进一步的初始化。
  
  代码清单3-9  虚拟驱动例子
  static int blkfront_probe(struct xenbus_device *dev,
    const struct xenbus_device_id *id)
  {
  int err, vdevice, i;
  struct blkfront_info *info;
  /* FIXME: Use dynamic device id if this is not set. */
  err = xenbus_scanf(XBT_NIL, dev->nodename,
     &quot;virtual-device&quot;, &quot;%i&quot;, &vdevice);
  if (err != 1) {
  xenbus_dev_fatal(dev, err, &quot;reading virtual-device&quot;);
  return err;
  }
  info = kzalloc(sizeof(*info), GFP_KERNEL);
  if (!info) {
  xenbus_dev_fatal(dev, -ENOMEM, &quot;allocating info structure&quot;);
  return -ENOMEM;
  }
  info->xbdev = dev;
  info->vdevice = vdevice;
  info->connected = BLKIF_STATE_DISCONNECTED;
  INIT_WORK(&info->work, blkif_restart_queue, (void *)info);
  for (i = 0; i < BLK_RING_SIZE; i&#43;&#43;)
  info->shadow.req.id = i&#43;1;
  info->shadow[BLK_RING_SIZE-1].req.id = 0x0fffffff;
  /* Front end dir is a number, which is used as the id. */
  info->handle = simple_strtoul(strrchr(dev->nodename,'/')&#43;1, NULL, 0);
  dev->dev.driver_data = info;
         /*连接后端*/
  err = talk_to_backend(dev, info);
  if (err) {
  kfree(info);
  dev->dev.driver_data = NULL;
  return err;
  }
  return 0;
  }<object data=&quot;data:application/x-silverlight-2,&quot; type=&quot;application/x-silverlight-2&quot;
  这个函数主要是申请一个blkfront_info类型的数据结构。这个数据结构是个容器,既包括块设备有关的请求队列和gendisk数据结构,也包括和虚拟化有关的ring结构,和grantbable结构。
  初始化之后,调用talk_to_backend来和后端设备连接。
  
  代码清单3-10  虚拟驱动例子
  static int talk_to_backend(struct xenbus_device *dev,
     struct blkfront_info *info)
  {
  const char *message = NULL;
  struct xenbus_transaction xbt;
  int err;
         /*ring是前端和后端通讯的一种方式*/
  /* Create shared ring, alloc event channel. */
  err = setup_blkring(dev, info);
  if (err)
  goto out;
  again:
         /*启动一个xenbus事务*/
  err = xenbus_transaction_start(&xbt);
  if (err) {
  xenbus_dev_fatal(dev, err, &quot;starting transaction&quot;);
  goto destroy_blkring;
  }
         /*输出信息,在后端可以看到输出的信息*/
  err = xenbus_printf(xbt, dev->nodename,
      &quot;ring-ref&quot;,&quot;%u&quot;, info->ring_ref);
  if (err) {
  message = &quot;writing ring-ref&quot;;
  goto abort_transaction;
  }
  err = xenbus_printf(xbt, dev->nodename, &quot;event-channel&quot;, &quot;%u&quot;,
      irq_to_evtchn_port(info->irq));
  if (err) {
  message = &quot;writing event-channel&quot;;
  goto abort_transaction;
  }
  err = xenbus_printf(xbt, dev->nodename, &quot;protocol&quot;, &quot;%s&quot;,
      XEN_IO_PROTO_ABI_NATIVE);
  if (err) {
  message = &quot;writing protocol&quot;;
  goto abort_transaction;
  }
  err = xenbus_transaction_end(xbt, 0);
  if (err) {
  if (err == -EAGAIN)
  goto again;
  xenbus_dev_fatal(dev, err, &quot;completing transaction&quot;);
  goto destroy_blkring;
  }
         /*改变前端状态为初始化完成*/
  xenbus_switch_state(dev, XenbusStateInitialised);
  return 0;
   abort_transaction:
  xenbus_transaction_end(xbt, 1);
  if (message)
  xenbus_dev_fatal(dev, err, &quot;%s&quot;, message);
   destroy_blkring:
  blkif_free(info, 0);
   out:
  return err;
  }<object data=&quot;data:application/x-silverlight-2,&quot; type=&quot;application/x-silverlight-2&quot;
  talk_to_backend主要是建立和后端的通讯机制,也就是xenbus_transaction。这种通讯机制是基于xen提供的机制xen store实现的。xenbus_transaction实现了一种类&#20284;文件系统的读写接口。建立通讯机制后,就可以调用xenbus_printf写入信息,而后端驱动就可以读取这些信息。
  然后前端驱动把状态改为“初始化完成”,等待后端驱动的回应。
  从虚拟化的逻辑来说,此时控制逻辑转入后端驱动。当后端驱动完成需要的处理,将状态改为“connected&quot;后,前端驱动将调用connect函数,完成虚拟化块设备的整个初始化工作。后端掌管的是物理设备,只有后端才能看到设备的真正信息,所以后端要完成设备的物理信息。比如块设备的扇区数目,扇区大小等等。
  
  代码清单3-11  connect
  static void connect(struct blkfront_info *info)
  {
  unsigned long long sectors;
  unsigned long sector_size;
  unsigned int binfo;
  int err;
         ............................................
         /**获得设备的物理信息,扇区,扇区大小等等/
  err = xenbus_gather(XBT_NIL, info->xbdev->otherend,
      &quot;sectors&quot;, &quot;%Lu&quot;, §ors,
      &quot;info&quot;, &quot;%u&quot;, &binfo,
      &quot;sector-size&quot;, &quot;%lu&quot;, §or_size,
      NULL);
  err = xenbus_gather(XBT_NIL, info->xbdev->otherend,
      &quot;feature-barrier&quot;, &quot;%lu&quot;, &info->feature_barrier,
      NULL);
  err = xlvbd_add(sectors, info->vdevice, binfo, sector_size, info);
         /*切换状态为connected*/
  (void)xenbus_switch_state(info->xbdev, XenbusStateConnected);
  /* Kick pending requests. */
  spin_lock_irq(&blkif_io_lock);
  info->connected = BLKIF_STATE_CONNECTED;
  kick_pending_request_queues(info);
  spin_unlock_irq(&blkif_io_lock);
        /*把创建的磁盘设备加入到系统*/
  add_disk(info->gd);
  }<object data=&quot;data:application/x-silverlight-2,&quot; type=&quot;application/x-silverlight-2&quot;
  Connect做的事情是根据后端写入的物理信息,来创建磁盘设备,设置设备的参数。最重要的是,要截获io请求,转入虚拟化的处理过程。
  Linux的文件读写,是以请求队列的形式发送给块设备。每个块设备都有自己的请求队列处理函数。如果改写这个函数,就可以截获linuxio请求。
  xlvbd_add要调用虚拟块设备的核心函数来完成块设备创建,初始化的工作。虚拟块设备的函数中,最重要的是xlvbd_alloc_gendisk
  
  代码清单3-12  虚拟驱动例子
  xlvbd_alloc_gendisk(int minor, blkif_sector_t capacity, int vdevice,
      u16 vdisk_info, u16 sector_size,
      struct blkfront_info *info)
  {
        ...............................
         /*分配一个gendisk数据结构*/
  gd = alloc_disk(nr_minors);
         ................................................
         /*设置gd的主设备号,私有数据和磁盘容量*/
  gd->major = mi->major;
  gd->first_minor = minor;
  gd->fops = &xlvbd_block_fops;
  gd->private_data = info;
  gd->driverfs_dev = &(info->xbdev->dev);
  set_capacity(gd, capacity);
         /*注册虚拟块设备的io队列处理函数*/
  if (xlvbd_init_blk_queue(gd, sector_size)) {
  del_gendisk(gd);
  goto out;
  }
  if (vdisk_info & VDISK_CDROM)
  gd->flags |= GENHD_FL_CD;
         return 0;
  }
  <object data=&quot;data:application/x-silverlight-2,&quot; type=&quot;application/x-silverlight-2&quot;
  这里首先是申请一个gendisk设备数据结构,然后设置设备参数。注意,这里的参数是后端驱动写入的。
  xlvbd_init_blk_queue的作用就是注册虚拟块设备的请求队列处理函数,这个函数是do_blkif_request。所有发送到块设备的io都首先经过这个函数。
  do_blkif_request这个函数要把所有接收的io请求都转发到后端驱动。这里就不分析了。
  
  
4.3.2 块设备后端驱动
  对后端驱动的分析,重点在后端对前端驱动的呼应和配合。首先分析后端驱动对前端状态改变的处理函数。
  
  代码清单3-13  虚拟驱动例子
  static void frontend_changed(struct xenbus_device *dev,
       enum xenbus_state frontend_state)
  {
  struct backend_info *be = dev->dev.driver_data;
  int err;
  DPRINTK(&quot;%s&quot;, xenbus_strstate(frontend_state));
  switch (frontend_state) {
  case XenbusStateInitialising:
  break;
  case XenbusStateInitialised:
  case XenbusStateConnected:
                   ..........................
  err = connect_ring(be);
  update_blkif_status(be->blkif);
  break;
  case XenbusStateClosing:
  blkif_disconnect(be->blkif);
  xenbus_switch_state(dev, XenbusStateClosing);
  break;
  }
  }<object data=&quot;data:application/x-silverlight-2,&quot; type=&quot;application/x-silverlight-2&quot;
  简化后端的状态处理函数,当前端状态变为“初始化完成”,后端要调用connect_ring来连接前端定义的ring,然后update_blkif_status来写入设备信息和更新状态。
  
  代码清单3-14  虚拟驱动例子
  static void update_blkif_status(blkif_t *blkif)
  {
  int err;
  char name[TASK_COMM_LEN];
         .........................................   
  /* Attempt to connect: exit if we fail to. */
  connect(blkif->be);
  err = blkback_name(blkif, name);
  blkif->xenblkd = kthread_run(blkif_schedule, blkif, name);
  }<object data=&quot;data:application/x-silverlight-2,&quot; type=&quot;application/x-silverlight-2&quot;
  实际调用的是connect来写入设备信息。
  
  代码清单3-15  connect
  static void connect(struct backend_info *be)
  {
  struct xenbus_transaction xbt;
  int err;
  struct xenbus_device *dev = be->dev;
         /*写入块设备扇区*/
  err = xenbus_printf(xbt, dev->nodename, &quot;sectors&quot;, &quot;%llu&quot;,
      vbd_size(&be->blkif->vbd));
  /* FIXME: use a typename instead */
  err = xenbus_printf(xbt, dev->nodename, &quot;info&quot;, &quot;%u&quot;,
      vbd_info(&be->blkif->vbd));
         /*写入扇区大小*/
  err = xenbus_printf(xbt, dev->nodename, &quot;sector-size&quot;, &quot;%lu&quot;,
      vbd_secsize(&be->blkif->vbd));
  
         /*设置状态为connected*/
  err = xenbus_switch_state(dev, XenbusStateConnected);
        
  return;
  }<object data=&quot;data:application/x-silverlight-2,&quot; type=&quot;application/x-silverlight-2&quot;
  后端要写入块设备的扇区数目和扇区大小,以及node的名字。在前面块设备前端驱动的分析中,要通过connect函数来读入扇区数目和扇区大小来完成块设备初始化。读入的信息就是在这是由后端驱动写入的。
  本节的分析只涉及块设备的创建和初始化。至于块设备对文件io的处理,以及前端和后端联动处理io的过程,读者可以自行分析一下。
         版权声明:本文为博主原创文章,未经博主允许不得转载。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-125582-1-1.html 上篇帖子: xen的事件通道 下篇帖子: Xen虚拟技术的发展现状与红帽集成情况
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表