2.客户机PCI设备进行枚举和资源分配 当Linux客户机系统启动时,对PCI设备进行枚举和资源分配(配置PCI的配置空间),通常由BIOS完成。不过对Linux系统提供方式,一种由BIOS实现,另一种自己实现枚举和资源分配功能。代码位于kernel:arch/x86/pci/init.c static __init int pci_arch_init(void) { #ifdef CONFIG_PCI_DIRECT int type = 0;
return 0; } pcibios_scan_root()---->pci_scan_bus_parented()---->pci_scan_child_bus()--->pci_scan_slot()--->pci_scan_single_device()----->pci_device_add() 将PCI总线上的设备添加到链表 void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) { /* * Add the device to our list of discovered devices * and the bus list for fixup functions, etc. */ down_write(&pci_bus_sem); list_add_tail(&dev->bus_list, &bus->devices); up_write(&pci_bus_sem); } 上述过程执行完成,在/sys/devices/pci0000:00目录下,创建virtio pci设备。并且在/sys/bus/pci/devices/目录下,创建相应对于pci设备的符号连接,同时在/sys/bus/pci/drivers/目录下,创建virtio-pci目录,目录下存在支持设备符号连接文件。
5,virtio总线子设备注册 上面步骤2,对PCI设备进行枚举和资源分配中介绍了,枚举的设备,已经关联到总线链表中。对函数调用pci_register_driver(&virtio_pci_driver)就是对链表的每一个pci设备进行探测,该驱动是否支持该设备,如果支持进,调用驱动probe函数,完成启用该pci设备,同时在virtio总线进行注册设备。 bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); if (drv->probe) { ret = drv->probe(dev); }
static int __devinit virtio_pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) { struct virtio_pci_device *vp_dev; int err; /* We only own devices >= 0x1000 and <= 0x103f: leave the rest. */ if (pci_dev->device < 0x1000 || pci_dev->device > 0x103f) return -ENODEV; /* allocate our structure and fill it out */ vp_dev = kzalloc(sizeof(struct virtio_pci_device), GFP_KERNEL); if (vp_dev == NULL) return -ENOMEM;
vp_dev->vdev.dev.parent = virtio_pci_root; vp_dev->vdev.dev.release = virtio_pci_release_dev; vp_dev->vdev.config = &virtio_pci_config_ops; vp_dev->pci_dev = pci_dev; INIT_LIST_HEAD(&vp_dev->virtqueues); spin_lock_init(&vp_dev->lock); /* Disable MSI/MSIX to bring device to a known good state. */ pci_msi_off(pci_dev);
/* enable the device */ err = pci_enable_device(pci_dev); if (err) goto out;
err = pci_request_regions(pci_dev, "virtio-pci"); if (err) goto out_enable_device;
/* we use the subsystem vendor/device id as the virtio vendor/device * id. this allows us to use the same PCI vendor/device id for all * virtio devices and to identify the particular virtio driver by * the subsytem ids */ vp_dev->vdev.id.vendor = pci_dev->subsystem_vendor; vp_dev->vdev.id.device = pci_dev->subsystem_device; /* finally register the virtio device */ err = register_virtio_device(&vp_dev->vdev); if (err) goto out_set_drvdata;
/* We have a driver! */ add_status(dev, VIRTIO_CONFIG_S_DRIVER);
/* Figure out what features the device supports. */ device_features = dev->config->get_features(dev); /* Features supported by both device and driver into dev->features. */ memset(dev->features, 0, sizeof(dev->features)); for (i = 0; i < drv->feature_table_size; i++) { unsigned int f = drv->feature_table[i]; BUG_ON(f >= 32); if (device_features & (1 << f)) set_bit(f, dev->features); }
/* Transport features always preserved to pass to finalize_features. */ for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++) if (device_features & (1 << i)) set_bit(i, dev->features);
return add_uevent_var(env, "MODALIAS=virtio:d%08Xv%08X", dev->id.device, dev->id.vendor); } 下面函数工作流程如下: 1.由设备对象往上查找,直到找到包含kset的kobject(总线包含着kset) 2.判断kobject对象是否提供filter,name,uevent函数,如果提供,调用它。 3.分配一个kobj_uevent_env,并开始填充env环境变量:ACTION,DEVPATH,SUBSYSTEM,SEQNUM,MODALIAS 4.通过netlink发送到用户空间 register_virtio_device()---->device_register()---->device_add()---->kobject_uevent()---->kobject_uevent_env() int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp_ext[]) { /* search the kset we belong to */ top_kobj = kobj; while (!top_kobj->kset && top_kobj->parent) top_kobj = top_kobj->parent; kset = top_kobj->kset; uevent_ops = kset->uevent_ops;
/* skip the event, if the filter returns zero. */ if (uevent_ops && uevent_ops->filter) if (!uevent_ops->filter(kset, kobj)) { pr_debug("kobject: ‘%s‘ (%p): %s: filter function " "caused the event to drop!\n", kobject_name(kobj), kobj, __func__); return 0; }
/* keys passed in from the caller */ if (envp_ext) { for (i = 0; envp_ext[i]; i++) { retval = add_uevent_var(env, "%s", envp_ext[i]); if (retval) goto exit; } } /* let the kset specific function add its stuff */ if (uevent_ops && uevent_ops->uevent) { retval = uevent_ops->uevent(kset, kobj, env); if (retval) { pr_debug("kobject: ‘%s‘ (%p): %s: uevent() returned " "%d\n", kobject_name(kobj), kobj, __func__, retval); goto exit; } }
/* * Mark "add" and "remove" events in the object to ensure proper * events to userspace during automatic cleanup. If the object did * send an "add" event, "remove" will automatically generated by * the core, if not already done by the caller. */ if (action == KOBJ_ADD) kobj->state_add_uevent_sent = 1; else if (action == KOBJ_REMOVE) kobj->state_remove_uevent_sent = 1; /* we will send an event, so request a new sequence number */ spin_lock(&sequence_lock); seq = ++uevent_seqnum; spin_unlock(&sequence_lock); retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq); if (retval) goto exit; /* send netlink message */ if (uevent_sock) { struct sk_buff *skb; size_t len;
/* allocate message with the maximum possible size */ len = strlen(action_string) + strlen(devpath) + 2; skb = alloc_skb(len + env->buflen, GFP_KERNEL); if (skb) { char *scratch;