aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/hv/vmbus_drv.c174
-rw-r--r--include/linux/hyperv.h6
2 files changed, 172 insertions, 8 deletions
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index 0276d2ef06ee..230c62e7f567 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -45,6 +45,11 @@
45#include <linux/random.h> 45#include <linux/random.h>
46#include "hyperv_vmbus.h" 46#include "hyperv_vmbus.h"
47 47
48struct vmbus_dynid {
49 struct list_head node;
50 struct hv_vmbus_device_id id;
51};
52
48static struct acpi_device *hv_acpi_dev; 53static struct acpi_device *hv_acpi_dev;
49 54
50static struct completion probe_event; 55static struct completion probe_event;
@@ -500,7 +505,7 @@ static ssize_t device_show(struct device *dev,
500static DEVICE_ATTR_RO(device); 505static DEVICE_ATTR_RO(device);
501 506
502/* Set up per device attributes in /sys/bus/vmbus/devices/<bus device> */ 507/* Set up per device attributes in /sys/bus/vmbus/devices/<bus device> */
503static struct attribute *vmbus_attrs[] = { 508static struct attribute *vmbus_dev_attrs[] = {
504 &dev_attr_id.attr, 509 &dev_attr_id.attr,
505 &dev_attr_state.attr, 510 &dev_attr_state.attr,
506 &dev_attr_monitor_id.attr, 511 &dev_attr_monitor_id.attr,
@@ -528,7 +533,7 @@ static struct attribute *vmbus_attrs[] = {
528 &dev_attr_device.attr, 533 &dev_attr_device.attr,
529 NULL, 534 NULL,
530}; 535};
531ATTRIBUTE_GROUPS(vmbus); 536ATTRIBUTE_GROUPS(vmbus_dev);
532 537
533/* 538/*
534 * vmbus_uevent - add uevent for our device 539 * vmbus_uevent - add uevent for our device
@@ -565,10 +570,29 @@ static inline bool is_null_guid(const uuid_le *guid)
565 * Return a matching hv_vmbus_device_id pointer. 570 * Return a matching hv_vmbus_device_id pointer.
566 * If there is no match, return NULL. 571 * If there is no match, return NULL.
567 */ 572 */
568static const struct hv_vmbus_device_id *hv_vmbus_get_id( 573static const struct hv_vmbus_device_id *hv_vmbus_get_id(struct hv_driver *drv,
569 const struct hv_vmbus_device_id *id,
570 const uuid_le *guid) 574 const uuid_le *guid)
571{ 575{
576 const struct hv_vmbus_device_id *id = NULL;
577 struct vmbus_dynid *dynid;
578
579 /* Look at the dynamic ids first, before the static ones */
580 spin_lock(&drv->dynids.lock);
581 list_for_each_entry(dynid, &drv->dynids.list, node) {
582 if (!uuid_le_cmp(dynid->id.guid, *guid)) {
583 id = &dynid->id;
584 break;
585 }
586 }
587 spin_unlock(&drv->dynids.lock);
588
589 if (id)
590 return id;
591
592 id = drv->id_table;
593 if (id == NULL)
594 return NULL; /* empty device table */
595
572 for (; !is_null_guid(&id->guid); id++) 596 for (; !is_null_guid(&id->guid); id++)
573 if (!uuid_le_cmp(id->guid, *guid)) 597 if (!uuid_le_cmp(id->guid, *guid))
574 return id; 598 return id;
@@ -576,6 +600,134 @@ static const struct hv_vmbus_device_id *hv_vmbus_get_id(
576 return NULL; 600 return NULL;
577} 601}
578 602
603/* vmbus_add_dynid - add a new device ID to this driver and re-probe devices */
604static int vmbus_add_dynid(struct hv_driver *drv, uuid_le *guid)
605{
606 struct vmbus_dynid *dynid;
607
608 dynid = kzalloc(sizeof(*dynid), GFP_KERNEL);
609 if (!dynid)
610 return -ENOMEM;
611
612 dynid->id.guid = *guid;
613
614 spin_lock(&drv->dynids.lock);
615 list_add_tail(&dynid->node, &drv->dynids.list);
616 spin_unlock(&drv->dynids.lock);
617
618 return driver_attach(&drv->driver);
619}
620
621static void vmbus_free_dynids(struct hv_driver *drv)
622{
623 struct vmbus_dynid *dynid, *n;
624
625 spin_lock(&drv->dynids.lock);
626 list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {
627 list_del(&dynid->node);
628 kfree(dynid);
629 }
630 spin_unlock(&drv->dynids.lock);
631}
632
633/* Parse string of form: 1b4e28ba-2fa1-11d2-883f-b9a761bde3f */
634static int get_uuid_le(const char *str, uuid_le *uu)
635{
636 unsigned int b[16];
637 int i;
638
639 if (strlen(str) < 37)
640 return -1;
641
642 for (i = 0; i < 36; i++) {
643 switch (i) {
644 case 8: case 13: case 18: case 23:
645 if (str[i] != '-')
646 return -1;
647 break;
648 default:
649 if (!isxdigit(str[i]))
650 return -1;
651 }
652 }
653
654 /* unparse little endian output byte order */
655 if (sscanf(str,
656 "%2x%2x%2x%2x-%2x%2x-%2x%2x-%2x%2x-%2x%2x%2x%2x%2x%2x",
657 &b[3], &b[2], &b[1], &b[0],
658 &b[5], &b[4], &b[7], &b[6], &b[8], &b[9],
659 &b[10], &b[11], &b[12], &b[13], &b[14], &b[15]) != 16)
660 return -1;
661
662 for (i = 0; i < 16; i++)
663 uu->b[i] = b[i];
664 return 0;
665}
666
667/*
668 * store_new_id - sysfs frontend to vmbus_add_dynid()
669 *
670 * Allow GUIDs to be added to an existing driver via sysfs.
671 */
672static ssize_t new_id_store(struct device_driver *driver, const char *buf,
673 size_t count)
674{
675 struct hv_driver *drv = drv_to_hv_drv(driver);
676 uuid_le guid = NULL_UUID_LE;
677 ssize_t retval;
678
679 if (get_uuid_le(buf, &guid) != 0)
680 return -EINVAL;
681
682 if (hv_vmbus_get_id(drv, &guid))
683 return -EEXIST;
684
685 retval = vmbus_add_dynid(drv, &guid);
686 if (retval)
687 return retval;
688 return count;
689}
690static DRIVER_ATTR_WO(new_id);
691
692/*
693 * store_remove_id - remove a PCI device ID from this driver
694 *
695 * Removes a dynamic pci device ID to this driver.
696 */
697static ssize_t remove_id_store(struct device_driver *driver, const char *buf,
698 size_t count)
699{
700 struct hv_driver *drv = drv_to_hv_drv(driver);
701 struct vmbus_dynid *dynid, *n;
702 uuid_le guid = NULL_UUID_LE;
703 size_t retval = -ENODEV;
704
705 if (get_uuid_le(buf, &guid))
706 return -EINVAL;
707
708 spin_lock(&drv->dynids.lock);
709 list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {
710 struct hv_vmbus_device_id *id = &dynid->id;
711
712 if (!uuid_le_cmp(id->guid, guid)) {
713 list_del(&dynid->node);
714 kfree(dynid);
715 retval = count;
716 break;
717 }
718 }
719 spin_unlock(&drv->dynids.lock);
720
721 return retval;
722}
723static DRIVER_ATTR_WO(remove_id);
724
725static struct attribute *vmbus_drv_attrs[] = {
726 &driver_attr_new_id.attr,
727 &driver_attr_remove_id.attr,
728 NULL,
729};
730ATTRIBUTE_GROUPS(vmbus_drv);
579 731
580 732
581/* 733/*
@@ -590,7 +742,7 @@ static int vmbus_match(struct device *device, struct device_driver *driver)
590 if (is_hvsock_channel(hv_dev->channel)) 742 if (is_hvsock_channel(hv_dev->channel))
591 return drv->hvsock; 743 return drv->hvsock;
592 744
593 if (hv_vmbus_get_id(drv->id_table, &hv_dev->dev_type)) 745 if (hv_vmbus_get_id(drv, &hv_dev->dev_type))
594 return 1; 746 return 1;
595 747
596 return 0; 748 return 0;
@@ -607,7 +759,7 @@ static int vmbus_probe(struct device *child_device)
607 struct hv_device *dev = device_to_hv_device(child_device); 759 struct hv_device *dev = device_to_hv_device(child_device);
608 const struct hv_vmbus_device_id *dev_id; 760 const struct hv_vmbus_device_id *dev_id;
609 761
610 dev_id = hv_vmbus_get_id(drv->id_table, &dev->dev_type); 762 dev_id = hv_vmbus_get_id(drv, &dev->dev_type);
611 if (drv->probe) { 763 if (drv->probe) {
612 ret = drv->probe(dev, dev_id); 764 ret = drv->probe(dev, dev_id);
613 if (ret != 0) 765 if (ret != 0)
@@ -684,7 +836,8 @@ static struct bus_type hv_bus = {
684 .remove = vmbus_remove, 836 .remove = vmbus_remove,
685 .probe = vmbus_probe, 837 .probe = vmbus_probe,
686 .uevent = vmbus_uevent, 838 .uevent = vmbus_uevent,
687 .dev_groups = vmbus_groups, 839 .dev_groups = vmbus_dev_groups,
840 .drv_groups = vmbus_drv_groups,
688}; 841};
689 842
690struct onmessage_work_context { 843struct onmessage_work_context {
@@ -905,6 +1058,9 @@ int __vmbus_driver_register(struct hv_driver *hv_driver, struct module *owner, c
905 hv_driver->driver.mod_name = mod_name; 1058 hv_driver->driver.mod_name = mod_name;
906 hv_driver->driver.bus = &hv_bus; 1059 hv_driver->driver.bus = &hv_bus;
907 1060
1061 spin_lock_init(&hv_driver->dynids.lock);
1062 INIT_LIST_HEAD(&hv_driver->dynids.list);
1063
908 ret = driver_register(&hv_driver->driver); 1064 ret = driver_register(&hv_driver->driver);
909 1065
910 return ret; 1066 return ret;
@@ -923,8 +1079,10 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver)
923{ 1079{
924 pr_info("unregistering driver %s\n", hv_driver->name); 1080 pr_info("unregistering driver %s\n", hv_driver->name);
925 1081
926 if (!vmbus_exists()) 1082 if (!vmbus_exists()) {
927 driver_unregister(&hv_driver->driver); 1083 driver_unregister(&hv_driver->driver);
1084 vmbus_free_dynids(hv_driver);
1085 }
928} 1086}
929EXPORT_SYMBOL_GPL(vmbus_driver_unregister); 1087EXPORT_SYMBOL_GPL(vmbus_driver_unregister);
930 1088
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index 35053f99522b..42fe43fb0c80 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -1119,6 +1119,12 @@ struct hv_driver {
1119 1119
1120 struct device_driver driver; 1120 struct device_driver driver;
1121 1121
1122 /* dynamic device GUID's */
1123 struct {
1124 spinlock_t lock;
1125 struct list_head list;
1126 } dynids;
1127
1122 int (*probe)(struct hv_device *, const struct hv_vmbus_device_id *); 1128 int (*probe)(struct hv_device *, const struct hv_vmbus_device_id *);
1123 int (*remove)(struct hv_device *); 1129 int (*remove)(struct hv_device *);
1124 void (*shutdown)(struct hv_device *); 1130 void (*shutdown)(struct hv_device *);