Signed-off-by: Simon Horman --- Fri, 13 Feb 2009 13:50:13 +1100 * Account for vslot supplied for hotplug insert Fri, 13 Feb 2009 15:22:10 +1100 * Rebased from "Restore xenfb.h and atkbd_ translation tables for * xenfbfront" to "fix memory/fd leak in pt_msix_init()" Index: ioemu-remote/hw/pass-through.c =================================================================== --- ioemu-remote.orig/hw/pass-through.c 2009-02-17 17:55:37.000000000 +0900 +++ ioemu-remote/hw/pass-through.c 2009-02-17 17:56:46.000000000 +0900 @@ -34,6 +34,7 @@ struct php_dev { uint8_t r_bus; uint8_t r_dev; uint8_t r_func; + uint8_t v_devfn; char *opt; }; static struct dpci_infos { @@ -726,37 +727,65 @@ static const struct pt_reg_grp_info_tbl }, }; +static int cmp_php_dev(const struct php_dev *a, const struct php_dev *b) +{ + if (a->r_bus != b->r_bus) + { + return a->r_bus - b->r_bus; + } + if (a->r_dev != b->r_dev) + { + return a->r_dev - b->r_dev; + } + if (a->r_func != b->r_func) + { + return a->r_func - b->r_func; + } + + return 0; +} + +static void cpy_php_dev(struct php_dev *dst, const struct php_dev *src) +{ + memcpy(dst, src, sizeof(*dst)); +} + static int token_value(char *token) { return strtol(token, NULL, 16); } -static int next_bdf(char **str, int *seg, int *bus, int *dev, int *func, char **opt) +static struct php_dev next_bdf(char **str) { char *token; const char *delim = ":.-"; + struct php_dev bdf; + + memset(&bdf, 0, sizeof(bdf)); if ( !(*str) || ( !strchr(*str, ':') && !strchr(*str, '.')) ) - return 0; + return bdf; token = strsep(str, delim); - *seg = token_value(token); + /* segment */ token = strsep(str, delim); - *bus = token_value(token); + bdf.r_bus = token_value(token); token = strsep(str, delim); - *dev = token_value(token); + bdf.r_dev = token_value(token); token = strsep(str, delim); - *opt = strchr(token, ','); - if (*opt) - *(*opt)++ = '\0'; + bdf.opt = strchr(token, ','); + if (bdf.opt) + *(bdf.opt)++ = '\0'; - *func = token_value(token); + bdf.r_func = token_value(token); - return 1; + bdf.valid = 1; + + return bdf; } static int get_next_keyval(char **option, char **key, char **val) @@ -802,31 +831,31 @@ static void msi_set_enable(struct pt_dev * 0: no free hotplug slots, but normal slot should okay * >0: the new hotplug devfn */ -static int insert_to_php_devfn(int bus, int dev, int func, int devfn, - char *opt) +static int insert_to_php_devfn(const struct php_dev *bdf) { int php_slot, php_func, php_devfn, php_devfn_match; /* preferred virt pci slot */ - if ( devfn >= PHP_DEVFN_START && devfn < PHP_DEVFN_END ) + if ( bdf->v_devfn >= PHP_DEVFN_START && bdf->v_devfn < PHP_DEVFN_END ) { - php_devfn = PCI_TO_PHP_DEVFN(devfn); - if ( !dpci_infos.php_devs[php_devfn].valid ) + php_devfn = PCI_TO_PHP_DEVFN(bdf->v_devfn); + if ( !dpci_infos.php_devs[php_devfn].valid || + !cmp_php_dev(&dpci_infos.php_devs[php_devfn], bdf) ) goto found; } - if ( devfn != 0 ) + if ( bdf->v_devfn != 0 ) return -1; /* Co-locate functions for the same device in the same slot */ for ( php_slot = 0; php_slot < PHP_SLOT_LEN; php_slot++ ) { - php_devfn = PCI_DEVFN(php_slot, func); + php_devfn = PCI_DEVFN(php_slot, bdf->r_func); for ( php_func = 0; php_func < 8; php_func++ ) { php_devfn_match = PCI_DEVFN(php_slot, php_func); if ( dpci_infos.php_devs[php_devfn_match].valid && - dpci_infos.php_devs[php_devfn_match].r_bus == bus && - dpci_infos.php_devs[php_devfn_match].r_dev == dev && + dpci_infos.php_devs[php_devfn_match].r_bus == bdf->r_bus && + dpci_infos.php_devs[php_devfn_match].r_dev == bdf->r_dev && !dpci_infos.php_devs[php_devfn].valid ) goto found; } @@ -844,12 +873,9 @@ static int insert_to_php_devfn(int bus, return 0; found: - dpci_infos.php_devs[php_devfn].valid = 1; - dpci_infos.php_devs[php_devfn].r_bus = bus; - dpci_infos.php_devs[php_devfn].r_dev = dev; - dpci_infos.php_devs[php_devfn].r_func = func; - dpci_infos.php_devs[php_devfn].opt = opt; - return PHP_TO_PCI_DEVFN(php_devfn); + cpy_php_dev(&dpci_infos.php_devs[php_devfn], bdf); + dpci_infos.php_devs[php_devfn].v_devfn = PHP_TO_PCI_DEVFN(php_devfn); + return dpci_infos.php_devs[php_devfn].v_devfn; } /* Insert a new pass-through device into function 0 of a specific pci slot. @@ -857,7 +883,8 @@ found: */ int insert_bdf_to_php_devfn(char *bdf_slt) { - int seg, bus, dev, func, slot; + int slot; + struct php_dev bdf; char *bdf_str, *slt_str, *opt; const char *delim="@"; @@ -865,12 +892,15 @@ int insert_bdf_to_php_devfn(char *bdf_sl slt_str = bdf_slt; slot = token_value(slt_str); - if ( !next_bdf(&bdf_str, &seg, &bus, &dev, &func, &opt)) + bdf = next_bdf(&bdf_str); + if (!bdf.valid) { return -1; } - return insert_to_php_devfn(bus, dev, func, PCI_DEVFN(slot, 0), opt); + bdf.v_devfn = PCI_DEVFN(slot, 0); + + return insert_to_php_devfn(&bdf); } @@ -896,22 +926,22 @@ int test_php_devfn(int devfn) /* find the pci slot for pass-through dev with specified BDF */ int bdf_to_php_devfn(char *bdf_str) { - int seg, bus, dev, func, i; - char *opt; + int i; + struct php_dev bdf; - if ( !next_bdf(&bdf_str, &seg, &bus, &dev, &func, &opt)) + bdf = next_bdf(&bdf_str); + if (!bdf.valid) { return -1; } - PT_LOG("%s: bdf: %04x:%02x.%x\n", __func__, bus, dev, func); + PT_LOG("%s: bdf: %04x:%02x.%x\n", __func__, bdf.r_bus, bdf.r_dev, + bdf.r_func); /* locate the virtual pci slot for this VTd device */ for ( i = 0; i < PHP_DEVFN_LEN; i++ ) { if ( dpci_infos.php_devs[i].valid && - dpci_infos.php_devs[i].r_bus == bus && - dpci_infos.php_devs[i].r_dev == dev && - dpci_infos.php_devs[i].r_func == func ) + cmp_php_dev(&dpci_infos.php_devs[i], &bdf) ) { return PHP_TO_PCI_DEVFN(i); } @@ -3093,9 +3123,8 @@ static int pt_msixctrl_reg_write(struct } static struct pt_dev * register_real_device(PCIBus *e_bus, - const char *e_dev_name, int e_devfn, uint8_t r_bus, uint8_t r_dev, - uint8_t r_func, uint32_t machine_irq, struct pci_access *pci_access, - char *opt) + const char *e_dev_name, struct php_dev *bdf, + uint32_t machine_irq, struct pci_access *pci_access) { int rc = -1, i; struct pt_dev *assigned_device = NULL; @@ -3107,14 +3136,14 @@ static struct pt_dev * register_real_dev int msi_translate; PT_LOG("Assigning real physical device %02x:%02x.%x ...\n", - r_bus, r_dev, r_func); + bdf->r_bus, bdf->r_dev, bdf->r_func); /* Find real device structure */ for (pci_dev = pci_access->devices; pci_dev != NULL; pci_dev = pci_dev->next) { - if ((r_bus == pci_dev->bus) && (r_dev == pci_dev->dev) - && (r_func == pci_dev->func)) + if ((bdf->r_bus == pci_dev->bus) && (bdf->r_dev == pci_dev->dev) + && (bdf->r_func == pci_dev->func)) break; } if ( pci_dev == NULL ) @@ -3125,19 +3154,16 @@ static struct pt_dev * register_real_dev pci_fill_info(pci_dev, PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES); pt_libpci_fixup(pci_dev); - if ( e_devfn == PCI_DEVFN_AUTO ) { - /*indicate a static assignment(not hotplug), so find a free PCI hot plug slot */ - free_devfn = insert_to_php_devfn(r_bus, r_dev, r_func, 0, NULL); - if ( free_devfn > 0 ) - e_devfn = free_devfn; - else - PT_LOG("Error: no free virtual PCI hot plug slot, thus no live migration.\n"); - } + free_devfn = insert_to_php_devfn(bdf); + if ( free_devfn <= 0 ) + PT_LOG("Error: no free virtual PCI hot plug slot, " + "thus no live migration.\n"); msi_translate = direct_pci_msitranslate; - while (opt) { - if (get_next_keyval(&opt, &key, &val)) { - PT_LOG("Error: unrecognized PCI assignment option \"%s\"\n", opt); + while (bdf->opt) { + if (get_next_keyval(&bdf->opt, &key, &val)) { + PT_LOG("Error: unrecognized PCI assignment option \"%s\"\n", + bdf->opt); break; } @@ -3164,7 +3190,7 @@ static struct pt_dev * register_real_dev /* Register device */ assigned_device = (struct pt_dev *) pci_register_device(e_bus, e_dev_name, - sizeof(struct pt_dev), e_devfn, + sizeof(struct pt_dev), free_devfn, pt_pci_read_config, pt_pci_write_config); if ( assigned_device == NULL ) { @@ -3181,9 +3207,9 @@ static struct pt_dev * register_real_dev /* Assign device */ machine_bdf.reg = 0; - machine_bdf.bus = r_bus; - machine_bdf.dev = r_dev; - machine_bdf.func = r_func; + machine_bdf.bus = bdf->r_bus; + machine_bdf.dev = bdf->r_dev; + machine_bdf.func = bdf->r_func; rc = xc_assign_device(xc_handle, domid, machine_bdf.value); if ( rc < 0 ) PT_LOG("Error: xc_assign_device error %d\n", rc); @@ -3273,7 +3299,7 @@ static struct pt_dev * register_real_dev out: PT_LOG("Real physical device %02x:%02x.%x registered successfuly!\n" - "IRQ type = %s\n", r_bus, r_dev, r_func, + "IRQ type = %s\n", bdf->r_bus, bdf->r_dev, bdf->r_func, assigned_device->msi_trans_en? "MSI-INTx":"INTx"); return assigned_device; @@ -3353,18 +3379,11 @@ static int unregister_real_device(int ph int power_on_php_devfn(int php_devfn) { struct php_dev *php_dev = &dpci_infos.php_devs[php_devfn]; - int pci_devfn = PHP_TO_PCI_DEVFN(php_devfn); struct pt_dev *pt_dev; - pt_dev = - register_real_device(dpci_infos.e_bus, - "DIRECT PCI", - pci_devfn, - php_dev->r_bus, - php_dev->r_dev, - php_dev->r_func, - PT_MACHINE_IRQ_AUTO, - dpci_infos.pci_access, - php_dev->opt); + + pt_dev = register_real_device(dpci_infos.e_bus, "DIRECT PCI", + php_dev, PT_MACHINE_IRQ_AUTO, + dpci_infos.pci_access); php_dev->opt = NULL; @@ -3381,14 +3400,14 @@ int power_off_php_devfn(int php_devfn) int pt_init(PCIBus *e_bus, const char *direct_pci) { - int seg, b, d, f, status = -1; + int status = -1; struct pt_dev *pt_dev; struct pci_access *pci_access; char *vslots; char slot_str[8]; char *direct_pci_head = NULL; char *direct_pci_p = NULL; - char *opt; + struct php_dev bdf; /* Initialize libpci */ pci_access = pci_alloc(); @@ -3417,14 +3436,20 @@ int pt_init(PCIBus *e_bus, const char *d vslots = qemu_mallocz ( strlen(direct_pci) / 3 ); /* Assign given devices to guest */ - while ( next_bdf(&direct_pci_p, &seg, &b, &d, &f, &opt) ) + while (1) { + bdf = next_bdf(&direct_pci_p); + if (!bdf.valid) + { + break; + } /* Register real device with the emulated bus */ - pt_dev = register_real_device(e_bus, "DIRECT PCI", PCI_DEVFN_AUTO, - b, d, f, PT_MACHINE_IRQ_AUTO, pci_access, opt); + pt_dev = register_real_device(e_bus, "DIRECT PCI", + &bdf, PT_MACHINE_IRQ_AUTO, pci_access); if ( pt_dev == NULL ) { - PT_LOG("Error: Registration failed (%02x:%02x.%x)\n", b, d, f); + PT_LOG("Error: Registration failed (%02x:%02x.%x)\n", + bdf.r_bus, bdf.r_dev, bdf.r_func); goto err; } -- -- Simon Horman VA Linux Systems Japan K.K., Sydney, Australia Satellite Office H: www.vergenet.net/~horms/ W: www.valinux.co.jp/en