From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1760320AbcLAPfu (ORCPT ); Thu, 1 Dec 2016 10:35:50 -0500 Received: from p3plsmtps2ded03.prod.phx3.secureserver.net ([208.109.80.60]:38336 "EHLO p3plsmtps2ded03.prod.phx3.secureserver.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1758658AbcLAPeD (ORCPT ); Thu, 1 Dec 2016 10:34:03 -0500 x-originating-ip: 72.167.245.219 From: kys@exchange.microsoft.com To: gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, devel@linuxdriverproject.org, olaf@aepfle.de, apw@canonical.com, vkuznets@redhat.com, jasowang@redhat.com, leann.ogasawara@canonical.com Cc: Stephen Hemminger , "K. Y. Srinivasan" Subject: [PATCH 13/15] vmbus: add support for dynamic device id's Date: Thu, 1 Dec 2016 09:28:50 -0800 Message-Id: <1480613332-7788-13-git-send-email-kys@exchange.microsoft.com> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1480613332-7788-1-git-send-email-kys@exchange.microsoft.com> References: <1480613287-7748-1-git-send-email-kys@exchange.microsoft.com> <1480613332-7788-1-git-send-email-kys@exchange.microsoft.com> Reply-To: kys@microsoft.com X-CMAE-Envelope: MS4wfM9xhNmSGO8n4Xy2mN1ehI3gkb+CxtN5nPQTs4R6TazFAdtQ0ZY0GYNgyhiDoQq9c0GlUhcaTrrJxxl55G6Hsd9sZ+Zs7CaF+DmpMcM7oOmjWl8eTioN I5J4nE6XyHYkuhUwMJ5LKIdYbFzP2DlCmA9zfd9vdsIYwpQAENQTrjlysYuhZ04NUTkcetmoftNCnV04URYfZby4GeAiAhoekPEyek7PaIAdPba2rFxc3XGt jZfmakmSuyZrbPGr7D5dZjTRbrTJhjgiZQdp1OZR61gjyXBZRWnx9q0MPUqyOnidk5gqXvK82VsYlNV79/cfYB/0u35LnM4ThSWjD464vR07xSCi3Wb86fma KnJpQswwsuqeo9D8JVYJVayhsNkn8LP9ARtRCHpPAr/8a/zUWKh9Oilgw/+t0YQoaugKenfi0Wl4V/+U9zFAvdrtv4lVVeyos2OdKVp5AuYTnaLkcOnm4p/b vWqqx0hEwjO4MRupXM/WM1KFE5w1Q9OZbQOnzQ== Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Stephen Hemminger This patch adds sysfs interface to dynamically bind new UUID values to existing VMBus device. This is useful for generic UIO driver to act similar to uio_pci_generic. Signed-off-by: Stephen Hemminger Signed-off-by: K. Y. Srinivasan --- drivers/hv/vmbus_drv.c | 174 +++++++++++++++++++++++++++++++++++++++++++++-- include/linux/hyperv.h | 6 ++ 2 files changed, 172 insertions(+), 8 deletions(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 1e2a413..fbf1422 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -45,6 +45,11 @@ #include #include "hyperv_vmbus.h" +struct vmbus_dynid { + struct list_head node; + struct hv_vmbus_device_id id; +}; + static struct acpi_device *hv_acpi_dev; static struct completion probe_event; @@ -501,7 +506,7 @@ static ssize_t device_show(struct device *dev, static DEVICE_ATTR_RO(device); /* Set up per device attributes in /sys/bus/vmbus/devices/ */ -static struct attribute *vmbus_attrs[] = { +static struct attribute *vmbus_dev_attrs[] = { &dev_attr_id.attr, &dev_attr_state.attr, &dev_attr_monitor_id.attr, @@ -529,7 +534,7 @@ static ssize_t device_show(struct device *dev, &dev_attr_device.attr, NULL, }; -ATTRIBUTE_GROUPS(vmbus); +ATTRIBUTE_GROUPS(vmbus_dev); /* * vmbus_uevent - add uevent for our device @@ -566,10 +571,29 @@ static inline bool is_null_guid(const uuid_le *guid) * Return a matching hv_vmbus_device_id pointer. * If there is no match, return NULL. */ -static const struct hv_vmbus_device_id *hv_vmbus_get_id( - const struct hv_vmbus_device_id *id, +static const struct hv_vmbus_device_id *hv_vmbus_get_id(struct hv_driver *drv, const uuid_le *guid) { + const struct hv_vmbus_device_id *id = NULL; + struct vmbus_dynid *dynid; + + /* Look at the dynamic ids first, before the static ones */ + spin_lock(&drv->dynids.lock); + list_for_each_entry(dynid, &drv->dynids.list, node) { + if (!uuid_le_cmp(dynid->id.guid, *guid)) { + id = &dynid->id; + break; + } + } + spin_unlock(&drv->dynids.lock); + + if (id) + return id; + + id = drv->id_table; + if (id == NULL) + return NULL; /* empty device table */ + for (; !is_null_guid(&id->guid); id++) if (!uuid_le_cmp(id->guid, *guid)) return id; @@ -577,6 +601,134 @@ static inline bool is_null_guid(const uuid_le *guid) return NULL; } +/* vmbus_add_dynid - add a new device ID to this driver and re-probe devices */ +static int vmbus_add_dynid(struct hv_driver *drv, uuid_le *guid) +{ + struct vmbus_dynid *dynid; + + dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); + if (!dynid) + return -ENOMEM; + + dynid->id.guid = *guid; + + spin_lock(&drv->dynids.lock); + list_add_tail(&dynid->node, &drv->dynids.list); + spin_unlock(&drv->dynids.lock); + + return driver_attach(&drv->driver); +} + +static void vmbus_free_dynids(struct hv_driver *drv) +{ + struct vmbus_dynid *dynid, *n; + + spin_lock(&drv->dynids.lock); + list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { + list_del(&dynid->node); + kfree(dynid); + } + spin_unlock(&drv->dynids.lock); +} + +/* Parse string of form: 1b4e28ba-2fa1-11d2-883f-b9a761bde3f */ +static int get_uuid_le(const char *str, uuid_le *uu) +{ + unsigned int b[16]; + int i; + + if (strlen(str) < 37) + return -1; + + for (i = 0; i < 36; i++) { + switch (i) { + case 8: case 13: case 18: case 23: + if (str[i] != '-') + return -1; + break; + default: + if (!isxdigit(str[i])) + return -1; + } + } + + /* unparse little endian output byte order */ + if (sscanf(str, + "%2x%2x%2x%2x-%2x%2x-%2x%2x-%2x%2x-%2x%2x%2x%2x%2x%2x", + &b[3], &b[2], &b[1], &b[0], + &b[5], &b[4], &b[7], &b[6], &b[8], &b[9], + &b[10], &b[11], &b[12], &b[13], &b[14], &b[15]) != 16) + return -1; + + for (i = 0; i < 16; i++) + uu->b[i] = b[i]; + return 0; +} + +/* + * store_new_id - sysfs frontend to vmbus_add_dynid() + * + * Allow GUIDs to be added to an existing driver via sysfs. + */ +static ssize_t new_id_store(struct device_driver *driver, const char *buf, + size_t count) +{ + struct hv_driver *drv = drv_to_hv_drv(driver); + uuid_le guid = NULL_UUID_LE; + ssize_t retval; + + if (get_uuid_le(buf, &guid) != 0) + return -EINVAL; + + if (hv_vmbus_get_id(drv, &guid)) + return -EEXIST; + + retval = vmbus_add_dynid(drv, &guid); + if (retval) + return retval; + return count; +} +static DRIVER_ATTR_WO(new_id); + +/* + * store_remove_id - remove a PCI device ID from this driver + * + * Removes a dynamic pci device ID to this driver. + */ +static ssize_t remove_id_store(struct device_driver *driver, const char *buf, + size_t count) +{ + struct hv_driver *drv = drv_to_hv_drv(driver); + struct vmbus_dynid *dynid, *n; + uuid_le guid = NULL_UUID_LE; + size_t retval = -ENODEV; + + if (get_uuid_le(buf, &guid)) + return -EINVAL; + + spin_lock(&drv->dynids.lock); + list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { + struct hv_vmbus_device_id *id = &dynid->id; + + if (!uuid_le_cmp(id->guid, guid)) { + list_del(&dynid->node); + kfree(dynid); + retval = count; + break; + } + } + spin_unlock(&drv->dynids.lock); + + return retval; +} +static DRIVER_ATTR_WO(remove_id); + +static struct attribute *vmbus_drv_attrs[] = { + &driver_attr_new_id.attr, + &driver_attr_remove_id.attr, + NULL, +}; +ATTRIBUTE_GROUPS(vmbus_drv); /* @@ -591,7 +743,7 @@ static int vmbus_match(struct device *device, struct device_driver *driver) if (is_hvsock_channel(hv_dev->channel)) return drv->hvsock; - if (hv_vmbus_get_id(drv->id_table, &hv_dev->dev_type)) + if (hv_vmbus_get_id(drv, &hv_dev->dev_type)) return 1; return 0; @@ -608,7 +760,7 @@ static int vmbus_probe(struct device *child_device) struct hv_device *dev = device_to_hv_device(child_device); const struct hv_vmbus_device_id *dev_id; - dev_id = hv_vmbus_get_id(drv->id_table, &dev->dev_type); + dev_id = hv_vmbus_get_id(drv, &dev->dev_type); if (drv->probe) { ret = drv->probe(dev, dev_id); if (ret != 0) @@ -685,7 +837,8 @@ static void vmbus_device_release(struct device *device) .remove = vmbus_remove, .probe = vmbus_probe, .uevent = vmbus_uevent, - .dev_groups = vmbus_groups, + .dev_groups = vmbus_dev_groups, + .drv_groups = vmbus_drv_groups, }; struct onmessage_work_context { @@ -914,6 +1067,9 @@ int __vmbus_driver_register(struct hv_driver *hv_driver, struct module *owner, c hv_driver->driver.mod_name = mod_name; hv_driver->driver.bus = &hv_bus; + spin_lock_init(&hv_driver->dynids.lock); + INIT_LIST_HEAD(&hv_driver->dynids.list); + ret = driver_register(&hv_driver->driver); return ret; @@ -932,8 +1088,10 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver) { pr_info("unregistering driver %s\n", hv_driver->name); - if (!vmbus_exists()) + if (!vmbus_exists()) { driver_unregister(&hv_driver->driver); + vmbus_free_dynids(hv_driver); + } } EXPORT_SYMBOL_GPL(vmbus_driver_unregister); diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 5051eca..f4c0788 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1122,6 +1122,12 @@ struct hv_driver { struct device_driver driver; + /* dynamic device GUID's */ + struct { + spinlock_t lock; + struct list_head list; + } dynids; + int (*probe)(struct hv_device *, const struct hv_vmbus_device_id *); int (*remove)(struct hv_device *); void (*shutdown)(struct hv_device *); -- 1.7.4.1