{"id":819309,"url":"http://patchwork.ozlabs.org/api/patches/819309/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-pci/patch/1506544822-2632-2-git-send-email-jonathan.derrick@intel.com/","project":{"id":28,"url":"http://patchwork.ozlabs.org/api/projects/28/?format=json","name":"Linux PCI development","link_name":"linux-pci","list_id":"linux-pci.vger.kernel.org","list_email":"linux-pci@vger.kernel.org","web_url":null,"scm_url":null,"webscm_url":null,"list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<1506544822-2632-2-git-send-email-jonathan.derrick@intel.com>","list_archive_url":null,"date":"2017-09-27T20:40:20","name":"[RFC,1/3] PCI: pci-driver: Introduce pci device delete list","commit_ref":null,"pull_url":null,"state":"not-applicable","archived":false,"hash":"dcfa1e54f7509964b965dac31613d5016ff04bf3","submitter":{"id":68102,"url":"http://patchwork.ozlabs.org/api/people/68102/?format=json","name":"Jon Derrick","email":"jonathan.derrick@intel.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/linux-pci/patch/1506544822-2632-2-git-send-email-jonathan.derrick@intel.com/mbox/","series":[{"id":5445,"url":"http://patchwork.ozlabs.org/api/series/5445/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-pci/list/?series=5445","date":"2017-09-27T20:40:19","name":"Introduce PCI device blacklisting","version":1,"mbox":"http://patchwork.ozlabs.org/series/5445/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/819309/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/819309/checks/","tags":{},"related":[],"headers":{"Return-Path":"<linux-pci-owner@vger.kernel.org>","X-Original-To":"incoming@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming@bilbo.ozlabs.org","Authentication-Results":"ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=linux-pci-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)","Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3y2VD00zjzz9t66\n\tfor <incoming@patchwork.ozlabs.org>;\n\tThu, 28 Sep 2017 06:44:00 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1751890AbdI0Un7 (ORCPT <rfc822;incoming@patchwork.ozlabs.org>);\n\tWed, 27 Sep 2017 16:43:59 -0400","from mga09.intel.com ([134.134.136.24]:28966 \"EHLO mga09.intel.com\"\n\trhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP\n\tid S1751708AbdI0UnT (ORCPT <rfc822;linux-pci@vger.kernel.org>);\n\tWed, 27 Sep 2017 16:43:19 -0400","from fmsmga004.fm.intel.com ([10.253.24.48])\n\tby orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384;\n\t27 Sep 2017 13:43:18 -0700","from eremita.lm.intel.com (HELO centos7-vm.localdomain)\n\t([10.232.112.85])\n\tby fmsmga004.fm.intel.com with ESMTP; 27 Sep 2017 13:43:17 -0700"],"X-ExtLoop1":"1","X-IronPort-AV":"E=Sophos;i=\"5.42,446,1500966000\"; d=\"scan'208\";a=\"316977880\"","From":"Jon Derrick <jonathan.derrick@intel.com>","To":"Greg Kroah-Hartman <gregkh@linuxfoundation.org>,\n\tBjorn Helgaas <helgaas@kernel.org>","Cc":"<linux-pci@vger.kernel.org>, <linux-kernel@vger.kernel.org>,\n\tArjan van de Ven <arjan@linux.intel.com>,\n\tAlan Cox <alan@linux.intel.com>,\n\tDan J Williams <dan.j.williams@intel.com>,\n\tJon Derrick <jonathan.derrick@intel.com>","Subject":"[RFC 1/3] PCI: pci-driver: Introduce pci device delete list","Date":"Wed, 27 Sep 2017 16:40:20 -0400","Message-Id":"<1506544822-2632-2-git-send-email-jonathan.derrick@intel.com>","X-Mailer":"git-send-email 1.8.3.1","In-Reply-To":"<1506544822-2632-1-git-send-email-jonathan.derrick@intel.com>","References":"<1506544822-2632-1-git-send-email-jonathan.derrick@intel.com>","Sender":"linux-pci-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<linux-pci.vger.kernel.org>","X-Mailing-List":"linux-pci@vger.kernel.org"},"content":"This patch introduces a new kernel command line parameter to mask pci\ndevice ids from pci driver id tables. This prevents masked devices from\nautomatically binding to both built-in and module drivers.\n\nDevices can be later attached through the driver's sysfs new_id\ninteface.\n\nThe use cases for this are primarily for debugging, eg, being able to\nprevent attachment before probes are set up. It can also be used to mask\noff faulty built-in hardware or faulty simulated hardware.\n\nAnother use case is to prevent attachment of devices which will be\npassed to VMs, shortcutting the detachment effort.\n\nThe format is similar to the sysfs new_id format. Device ids are\nspecified with:\n\nVVVV:DDDD[:SVVV:SDDD][:CCCC][:MMMM]\n\nWhere:\nVVVV = Vendor ID\nDDDD = Device ID\nSVVV = Subvendor ID\nSDDD = Subdevice ID\nCCCC = Class\nMMMM = Class Mask\n\nIDs can be chained with commas.\n\nExamples:\n\t<driver>.delete_id=1234:5678\n\t<driver>.delete_id=ffffffff:ffffffff\n\t<driver>.delete_id=ffffffff:ffffffff:ffffffff:ffffffff:010802\n\t<driver>.delete_id=1234:5678,abcd:ef01,2345:ffffffff\n\nSigned-off-by: Jon Derrick <jonathan.derrick@intel.com>\n---\n drivers/pci/pci-driver.c | 253 ++++++++++++++++++++++++++++++++++++++++++++++-\n include/linux/pci.h      |   1 +\n 2 files changed, 252 insertions(+), 2 deletions(-)","diff":"diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c\nindex 11bd267..7acdf13 100644\n--- a/drivers/pci/pci-driver.c\n+++ b/drivers/pci/pci-driver.c\n@@ -20,6 +20,7 @@\n #include <linux/pm_runtime.h>\n #include <linux/suspend.h>\n #include <linux/kexec.h>\n+#include <asm/setup.h>\n #include \"pci.h\"\n \n struct pci_dynid {\n@@ -27,6 +28,250 @@ struct pci_dynid {\n \tstruct pci_device_id id;\n };\n \n+static struct pci_device_id *pci_match_deleted_ids(struct pci_driver *drv,\n+\t\t\t\t\t\t   struct pci_dev *dev,\n+\t\t\t\t\t\t   bool inverse)\n+{\n+\tstruct pci_dynid *deleteid;\n+\tstruct pci_device_id *found_id = NULL;\n+\n+\tspin_lock(&drv->deleteids.lock);\n+\tlist_for_each_entry(deleteid, &drv->deleteids.list, node) {\n+\t\tif (!inverse && pci_match_one_device(&deleteid->id, dev)) {\n+\t\t\tfound_id = &deleteid->id;\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (inverse && !pci_match_one_device(&deleteid->id, dev)) {\n+\t\t\tfound_id = &deleteid->id;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tspin_unlock(&drv->deleteids.lock);\n+\n+\treturn found_id;\n+}\n+\n+/**\n+ * pci_match_non_deleted_ids - match dev against not-deleted ids\n+ * @ids: array of PCI device id structures to search in\n+ * @dev: the PCI device structure to match against.\n+ *\n+ * Finds devices in driver id table not matching the delete list and tries to\n+ * match them individually to dev. Returns the matching pci_dev_id structure,\n+ * %NULL if there is no match, of -errno if there was a failure to allocate\n+ * memory for the temporary pci_dev\n+ */\n+static struct pci_device_id *pci_match_non_deleted_ids(struct pci_driver *drv,\n+\t\t\t\t\t\t       struct pci_dev *dev)\n+{\n+\tconst struct pci_device_id *ids = drv->id_table;\n+\tstruct pci_device_id *match = NULL;\n+\tstruct pci_dev *tmpdev;\n+\n+\ttmpdev = kzalloc(sizeof(*tmpdev), GFP_KERNEL);\n+\tif (!tmpdev)\n+\t\treturn ERR_PTR(-ENOMEM);\n+\n+\twhile (ids->vendor || ids->subvendor || ids->class_mask) {\n+\t\tstruct pci_device_id *found_id = NULL;\n+\n+\t\ttmpdev->vendor = ids->vendor;\n+\t\ttmpdev->device = ids->device,\n+\t\ttmpdev->subsystem_vendor = ids->subvendor,\n+\t\ttmpdev->subsystem_device = ids->subdevice,\n+\t\ttmpdev->class = ids->class;\n+\n+\t\tfound_id = pci_match_deleted_ids(drv, tmpdev, true);\n+\t\tif (found_id) {\n+\t\t\tconst struct pci_device_id id[2] = { *found_id, {0,} };\n+\t\t\tif (pci_match_id(id, dev)) {\n+\t\t\t\tmatch = found_id;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tids++;\n+\t}\n+\n+\tkfree(tmpdev);\n+\n+\treturn match;\n+}\n+\n+/**\n+ * pci_add_delete_id - add a new PCI device ID to a built-in driver's blacklist\n+ * @drv: target pci driver\n+ * @vendor: PCI vendor ID\n+ * @device: PCI device ID\n+ * @subvendor: PCI subvendor ID\n+ * @subdevice: PCI subdevice ID\n+ * @class: PCI class\n+ * @class_mask: PCI class mask\n+ *\n+ * Adds a new dynamic pci device ID to this driver's delete list, preventing\n+ * the device from attaching to built-in drivers\n+ *\n+ * CONTEXT:\n+ * Does GFP_KERNEL allocation.\n+ *\n+ * RETURNS:\n+ * 0 on success, -errno on failure.\n+ */\n+static int pci_add_delete_id(struct pci_driver *drv,\n+\t\t  unsigned int vendor, unsigned int device,\n+\t\t  unsigned int subvendor, unsigned int subdevice,\n+\t\t  unsigned int class, unsigned int class_mask)\n+{\n+\tstruct pci_dynid *deleteid;\n+\n+\tdeleteid = kzalloc(sizeof(*deleteid), GFP_KERNEL);\n+\tif (!deleteid)\n+\t\treturn -ENOMEM;\n+\n+\tdeleteid->id.vendor = vendor;\n+\tdeleteid->id.device = device;\n+\tdeleteid->id.subvendor = subvendor;\n+\tdeleteid->id.subdevice = subdevice;\n+\tdeleteid->id.class = class;\n+\tdeleteid->id.class_mask = class_mask;\n+\n+\tspin_lock(&drv->deleteids.lock);\n+\tlist_add_tail(&deleteid->node, &drv->deleteids.list);\n+\tspin_unlock(&drv->deleteids.lock);\n+\n+\tpr_debug(\"%s: added %04x:%04x:[%04x:%04x]:[%08x:%08x] to delete list\\n\",\n+\t\tdrv->driver.name, vendor, device, subvendor, subdevice,\n+\t\tclass, class_mask);\n+\treturn 0;\n+}\n+\n+static void pci_free_delete_ids(struct pci_driver *drv)\n+{\n+\tstruct pci_dynid *deleteid, *n;\n+\n+\tspin_lock(&drv->deleteids.lock);\n+\tlist_for_each_entry_safe(deleteid, n, &drv->deleteids.list, node) {\n+\t\tlist_del(&deleteid->node);\n+\t\tkfree(deleteid);\n+\t}\n+\tspin_unlock(&drv->deleteids.lock);\n+}\n+\n+/**\n+ * pci_parse_and_add_delete_id - parses kernel cmdline for delete ids\n+ * @drv: the driver structure to register\n+ * @ids: string of comma-delimited ids\n+ *\n+ * Parses a comma-delimited list of pci ids and adds them to the driver's\n+ * delete blacklist\n+ *\n+ * Available formats: VVVV:DDDD[:SVVV:SDDD][:CCCC][:MMMM]\n+ * Use full 32-bit format for PCI_ANY_ID (FFFFFFFF)\n+ */\n+static void pci_parse_and_add_delete_id(struct pci_driver *drv, char *ids)\n+{\n+\tint vendor, device, subvendor, subdevice, class, class_mask;\n+\tint count, fields;\n+\n+\twhile (*ids) {\n+\t\tcount = 0;\n+\t\tfields = sscanf(ids, \"%x:%x:%x:%x:%x:%x%n\",\n+\t\t\t\t&vendor, &device, &subvendor, &subdevice,\n+\t\t\t\t&class, &class_mask, &count);\n+\n+\t\tswitch (fields) {\n+\t\tcase 6:\n+\t\t\tbreak;\n+\t\tcase 5:\n+\t\t\tsscanf(ids, \"%x:%x:%x:%x:%x%n\", &vendor, &device,\n+\t\t\t       &subvendor, &subdevice, &class, &count);\n+\t\t\tclass_mask = 0;\n+\t\t\tbreak;\n+\t\tcase 4:\n+\t\t\tsscanf(ids, \"%x:%x:%x:%x%n\", &vendor, &device,\n+\t\t\t       &subvendor, &subdevice, &count);\n+\t\t\tclass_mask = 0;\n+\t\t\tclass = PCI_ANY_ID;\n+\t\t\tbreak;\n+\t\tcase 2:\n+\t\t\tsscanf(ids, \"%x:%x%n\", &vendor, &device, &count);\n+\t\t\tclass_mask = 0;\n+\t\t\tclass = PCI_ANY_ID;\n+\t\t\tsubvendor = subdevice = PCI_ANY_ID;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tpr_err(\"%s: Can't parse delete_id parameter: %s\\n\",\n+\t\t\t\tdrv->driver.name, ids);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tif (pci_add_delete_id(drv, vendor, device,\n+\t\t\tsubvendor, subdevice, class, class_mask))\n+\t\t\tbreak;\n+\n+\t\tids += count;\n+\t\tif (*ids != ',') {\n+\t\t\t/* End of param or invalid format */\n+\t\t\tbreak;\n+\t\t}\n+\t\tids++;\n+\t}\n+}\n+\n+static int pci_boot_param_cb(char *param, char *val,\n+\t\t\t     const char *modname, void *arg)\n+{\n+\tstruct pci_driver *drv = arg;\n+\tsize_t len;\n+\tchar *p;\n+\n+\t/* Missing arg on right side of <param>= */\n+\tif (!val)\n+\t\treturn 0;\n+\n+\tp = strchr(param, '.');\n+\tif (!p)\n+\t\treturn 0;\n+\n+\tif (strcmp(p + 1, \"delete_id\"))\n+\t\treturn 0;\n+\n+\tlen = p - param;\n+\tif (len != strlen(drv->driver.name) ||\n+\t   (strncmp(param, drv->driver.name, len)))\n+\t\treturn 0;\n+\n+\tpci_parse_and_add_delete_id(drv, val);\n+\n+\treturn 0;\n+}\n+\n+static void __pci_init_delete_ids(struct pci_driver *drv)\n+{\n+\tstatic char cmdline[COMMAND_LINE_SIZE];\n+\tchar doing[strlen(\"pci-driver() params\") + strlen(drv->driver.name) + 1];\n+\n+\tstrcpy(cmdline, saved_command_line);\n+\tsnprintf(doing, sizeof(doing), \"pci-driver(%s) params\",\n+\t\t drv->driver.name);\n+\tparse_args(doing, cmdline, NULL,\n+\t\t   0, 0, 0, drv, &pci_boot_param_cb);\n+}\n+\n+static void pci_init_delete_ids(struct pci_driver *drv)\n+{\n+\tspin_lock_init(&drv->deleteids.lock);\n+\tINIT_LIST_HEAD(&drv->deleteids.list);\n+\n+\t/*\n+\t * Most configurations won't need to blacklist any device ids.\n+\t * Shortcut those cases before calling into parse_args.\n+\t */\n+\tif (!strstr(saved_command_line, \"delete_id\"))\n+\t\treturn;\n+\n+\t__pci_init_delete_ids(drv);\n+}\n+\n /**\n  * pci_add_dynid - add a new PCI device ID to this driver and re-probe devices\n  * @drv: target pci driver\n@@ -124,7 +369,7 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf,\n \t\tpdev->subsystem_device = subdevice;\n \t\tpdev->class = class;\n \n-\t\tif (pci_match_id(pdrv->id_table, pdev))\n+\t\tif (pci_match_non_deleted_ids(pdrv, pdev))\n \t\t\tretval = -EEXIST;\n \n \t\tkfree(pdev);\n@@ -269,7 +514,8 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv,\n \t}\n \tspin_unlock(&drv->dynids.lock);\n \n-\tif (!found_id)\n+\t/* Check the device blacklist before matching the driver id table */\n+\tif (!found_id && !pci_match_deleted_ids(drv, dev, false))\n \t\tfound_id = pci_match_id(drv->id_table, dev);\n \n \t/* driver_override will always match, send a dummy id */\n@@ -1310,6 +1556,8 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,\n \tspin_lock_init(&drv->dynids.lock);\n \tINIT_LIST_HEAD(&drv->dynids.list);\n \n+\tpci_init_delete_ids(drv);\n+\n \t/* register with core */\n \treturn driver_register(&drv->driver);\n }\n@@ -1328,6 +1576,7 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,\n void pci_unregister_driver(struct pci_driver *drv)\n {\n \tdriver_unregister(&drv->driver);\n+\tpci_free_delete_ids(drv);\n \tpci_free_dynids(drv);\n }\n EXPORT_SYMBOL(pci_unregister_driver);\ndiff --git a/include/linux/pci.h b/include/linux/pci.h\nindex f68c58a..6dc190a 100644\n--- a/include/linux/pci.h\n+++ b/include/linux/pci.h\n@@ -757,6 +757,7 @@ struct pci_driver {\n \tconst struct attribute_group **groups;\n \tstruct device_driver\tdriver;\n \tstruct pci_dynids dynids;\n+\tstruct pci_dynids deleteids;\n };\n \n #define\tto_pci_driver(drv) container_of(drv, struct pci_driver, driver)\n","prefixes":["RFC","1/3"]}