pci/guestdev: enhance guestdev to accept +iomul. enhance guestdev to accept +iomul and use it. Signed-off-by: Isaku Yamahata diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -577,11 +577,15 @@ running once the system is up. guestdev= [PCI,ACPI] Format: {|}][,{|}[,...]] - Format of device path: [:]-.[-.[,...]] - Format of sbdf: [:]:. + Format of device path: [:]-.[-.[,...]][+iomul] + Format of sbdf: [:]:.[+iomul] Specifies PCI device for guest domain. If PCI-PCI bridge is specified, all PCI devices behind PCI-PCI bridge are reserved. + +iomul means that this PCI function will share + IO ports with other +iomul functions under same + switch. NOTE: if +iomul is specfied, all the functions + of the device will share IO ports. guestiomuldev= [PCI] Format: [sbd][,][,...] diff --git a/drivers/pci/guestdev.c b/drivers/pci/guestdev.c --- a/drivers/pci/guestdev.c +++ b/drivers/pci/guestdev.c @@ -1,5 +1,7 @@ /* * Copyright (c) 2008, 2009 NEC Corporation. + * Copyright (c) 2009 Isaku Yamahata + * VA Linux Systems Japan K.K. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -38,8 +40,11 @@ #define GUESTDEV_FLAG_DEVICEPATH 0x1 #define GUESTDEV_FLAG_SBDF 0x2 +#define GUESTDEV_OPT_IOMUL 0x1 + struct guestdev { int flags; + int options; struct list_head root_list; union { struct devicepath { @@ -276,6 +281,7 @@ struct guestdev __init *pci_copy_guestde memset(gdev, 0, sizeof(*gdev)); INIT_LIST_HEAD(&gdev->root_list); gdev->flags = gdev_src->flags; + gdev->options = gdev_src->options; strcpy(gdev->u.devicepath.hid, gdev_src->u.devicepath.hid); strcpy(gdev->u.devicepath.uid, gdev_src->u.devicepath.uid); gdev->u.devicepath.seg = gdev_src->u.devicepath.seg; @@ -309,7 +315,7 @@ allocate_err_end: } /* Make guestdev from path strings */ -static int __init pci_make_devicepath_guestdev(char *path_str) +static int __init pci_make_devicepath_guestdev(char *path_str, int options) { char hid[HID_LEN + 1], uid[UID_LEN + 1]; char *sp, *ep; @@ -337,6 +343,7 @@ static int __init pci_make_devicepath_gu memset(gdev_org, 0, sizeof(*gdev_org)); INIT_LIST_HEAD(&gdev_org->root_list); gdev_org->flags = GUESTDEV_FLAG_DEVICEPATH; + gdev_org->options = options; strcpy(gdev_org->u.devicepath.hid, hid); strcpy(gdev_org->u.devicepath.uid, uid); gdev_org->u.devicepath.seg = INVALID_SEG; @@ -436,7 +443,7 @@ end: return ret_val; } -static int __init pci_make_sbdf_guestdev(char* str) +static int __init pci_make_sbdf_guestdev(char* str, int options) { struct guestdev *gdev; int seg, bus, dev, func; @@ -453,6 +460,7 @@ static int __init pci_make_sbdf_guestdev } INIT_LIST_HEAD(&gdev->root_list); gdev->flags = GUESTDEV_FLAG_SBDF; + gdev->options = options; gdev->u.sbdf.seg = seg; gdev->u.sbdf.bus = bus; gdev->u.sbdf.dev = dev; @@ -461,11 +469,31 @@ static int __init pci_make_sbdf_guestdev return 0; } +static int __init pci_parse_options(const char *str) +{ + int options = 0; + char *ep; + + while (str) { + str++; + ep = strchr(str, '+'); + if (ep) + ep = '\0'; /* Chop */ + + if (!strcmp(str, "iomul")) + options |= GUESTDEV_OPT_IOMUL; + + str = ep; + } + return options; +} + /* Parse guestdev parameter */ static int __init pci_parse_guestdev(void) { int len; - char *sp, *ep; + char *sp, *ep, *op; + int options; struct list_head *head; struct guestdev *gdev; char path_str[GUESTDEV_STR_MAX]; @@ -482,16 +510,26 @@ static int __init pci_parse_guestdev(voi /* Chop */ if (ep) *ep = '\0'; - ret_val = pci_make_sbdf_guestdev(sp); + options = 0; + op = strchr(sp, '+'); + if (op && (!ep || op < ep)) { + options = pci_parse_options(op); + *op = '\0'; /* Chop */ + } + ret_val = pci_make_sbdf_guestdev(sp, options); if (ret_val == -EINVAL) { if (pci_check_extended_guestdev_format(sp)) { - ret_val = pci_make_devicepath_guestdev(sp); + ret_val = pci_make_devicepath_guestdev( + sp, options); if (ret_val && ret_val != -EINVAL) break; } } else if (ret_val) break; - sp = ep + 1; + + if (ep) + ep++; + sp = ep; } while (ep); list_for_each(head, &guestdev_list) { @@ -532,8 +570,21 @@ static void pci_free_sbdf(struct pcidev_ } /* Does PCI device belong to sub tree specified by guestdev with device path? */ +typedef int (*pci_node_match_t)(const struct devicepath_node *gdev_node, + const struct pcidev_sbdf_node *sbdf_node, + int options); + +static int pci_node_match(const struct devicepath_node *gdev_node, + const struct pcidev_sbdf_node *sbdf_node, + int options_unused) +{ + return (gdev_node->dev == sbdf_node->dev && + gdev_node->func == sbdf_node->func); +} + static int pci_is_in_devicepath_sub_tree(struct guestdev *gdev, - struct pcidev_sbdf *sbdf) + struct pcidev_sbdf *sbdf, + pci_node_match_t match) { int seg, bbn; struct devicepath_node *gdev_node; @@ -566,8 +617,7 @@ static int pci_is_in_devicepath_sub_tree while (gdev_node) { if (!sbdf_node) return FALSE; - if (gdev_node->dev != sbdf_node->dev || - gdev_node->func != sbdf_node->func) + if (!match(gdev_node, sbdf_node, gdev->options)) return FALSE; gdev_node = gdev_node->child; sbdf_node = sbdf_node->child; @@ -616,16 +666,29 @@ err_end: } /* Does PCI device belong to sub tree specified by guestdev with sbdf? */ -static int pci_is_in_sbdf_sub_tree(struct guestdev *gdev, struct pci_dev *dev) +typedef int (*pci_sbdf_match_t)(const struct guestdev *gdev, + const struct pci_dev *dev); + +static int pci_sbdf_match(const struct guestdev *gdev, + const struct pci_dev *dev) { int seg, bus; + + if (sscanf(dev->dev.bus_id, "%04x:%02x", &seg, &bus) != 2) + return FALSE; + + return gdev->u.sbdf.seg == seg && + gdev->u.sbdf.bus == bus && + gdev->u.sbdf.dev == PCI_SLOT(dev->devfn) && + gdev->u.sbdf.func == PCI_FUNC(dev->devfn); +} + +static int pci_is_in_sbdf_sub_tree(struct guestdev *gdev, struct pci_dev *dev, + pci_sbdf_match_t match) +{ BUG_ON(!(gdev->flags & GUESTDEV_FLAG_SBDF)); for (;;) { - if (sscanf(dev->dev.bus_id, "%04x:%02x", &seg, &bus) != 2) - continue; - if (gdev->u.sbdf.seg == seg && gdev->u.sbdf.bus == bus && - gdev->u.sbdf.dev == PCI_SLOT(dev->devfn) && - gdev->u.sbdf.func == PCI_FUNC(dev->devfn)) + if (match(gdev, dev)) return TRUE; if (!dev->bus || !dev->bus->self) break; @@ -635,7 +698,8 @@ static int pci_is_in_sbdf_sub_tree(struc } /* Does PCI device belong to sub tree specified by guestdev parameter? */ -int pci_is_guestdev(struct pci_dev *dev) +static int __pci_is_guestdev(struct pci_dev *dev, pci_node_match_t node_match, + pci_sbdf_match_t sbdf_match) { struct guestdev *gdev; struct pcidev_sbdf pcidev_sbdf, *sbdf = NULL; @@ -655,13 +719,14 @@ int pci_is_guestdev(struct pci_dev *dev) if (!pci_get_sbdf_from_pcidev(dev, sbdf)) goto out; } - if (pci_is_in_devicepath_sub_tree(gdev, sbdf)) { + if (pci_is_in_devicepath_sub_tree(gdev, sbdf, + node_match)) { result = TRUE; goto out; } break; case GUESTDEV_FLAG_SBDF: - if (pci_is_in_sbdf_sub_tree(gdev, dev)) { + if (pci_is_in_sbdf_sub_tree(gdev, dev, sbdf_match)) { result = TRUE; goto out; } @@ -675,6 +740,11 @@ out: pci_free_sbdf(sbdf); return result; } + +int pci_is_guestdev(struct pci_dev *dev) +{ + return __pci_is_guestdev(dev, pci_node_match, pci_sbdf_match); +} EXPORT_SYMBOL(pci_is_guestdev); static int __init pci_set_reassign_resources(char *str) @@ -694,6 +764,43 @@ int pci_is_reassigndev(struct pci_dev *d } EXPORT_SYMBOL(pci_is_reassigndev); +#ifdef CONFIG_PCI_IOMULTI +static int pci_iomul_node_match(const struct devicepath_node *gdev_node, + const struct pcidev_sbdf_node *sbdf_node, + int options) +{ + return (options & GUESTDEV_OPT_IOMUL) && + ((gdev_node->child != NULL && + sbdf_node->child != NULL && + gdev_node->dev == sbdf_node->dev && + gdev_node->func == sbdf_node->func) || + (gdev_node->child == NULL && + sbdf_node->child == NULL && + gdev_node->dev == sbdf_node->dev)); +} + +static int pci_iomul_sbdf_match(const struct guestdev *gdev, + const struct pci_dev *dev) +{ + int seg, bus; + + if (sscanf(dev->dev.bus_id, "%04x:%02x", &seg, &bus) != 2) + return FALSE; + + return (gdev->options & GUESTDEV_OPT_IOMUL) && + gdev->u.sbdf.seg == seg && + gdev->u.sbdf.bus == bus && + gdev->u.sbdf.dev == PCI_SLOT(dev->devfn); +} + +int pci_is_iomuldev(struct pci_dev *dev) +{ + return __pci_is_guestdev(dev, + pci_iomul_node_match, pci_iomul_sbdf_match); +} +EXPORT_SYMBOL_GPL(pci_is_iomuldev); +#endif /* CONFIG_PCI_IOMULTI */ + /* Check whether the devicepath exists under the pci root bus */ static int __init pci_check_devicepath_exists( struct guestdev *gdev, struct pci_bus *bus) diff --git a/drivers/pci/iomulti.c b/drivers/pci/iomulti.c --- a/drivers/pci/iomulti.c +++ b/drivers/pci/iomulti.c @@ -27,6 +27,7 @@ #include #include +#include "pci.h" #include "iomulti.h" #define PCI_NUM_BARS 6 @@ -423,7 +424,8 @@ static int pci_is_iomul_dev_param(struct break; } - return 0; + /* check guestcev=+iomul option */ + return pci_is_iomuldev(pdev); } /* diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -105,9 +105,13 @@ pci_match_one_device(const struct pci_de extern void pci_disable_bridge_window(struct pci_dev *dev); #endif #ifdef CONFIG_PCI_GUESTDEV +extern int pci_is_guestdev(struct pci_dev *dev); extern int pci_is_reassigndev(struct pci_dev *dev); +extern int pci_is_iomuldev(struct pci_dev *dev); #else -#define pci_is_reassigndev(dev) 0 +#define pci_is_guestdev(dev) 0 +#define pci_is_reassigndev(dev) 0 +#define pci_is_iomuldev(dev) 0 #endif #ifdef CONFIG_PCI_GUESTDEV