Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

vmbus: add driver_override support

Add support for overriding the default driver for a VMBus device
in the same way that it can be done for PCI devices. This patch
adds the /sys/bus/vmbus/devices/.../driver_override file
and the logic for matching.

This is used by driverctl tool to do driver override.
https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgitlab.com%2Fdriverctl%2Fdriverctl&data=02%7C01%7Ckys%40microsoft.com%7C42e803feb2c544ef6ea908d5fd538878%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636693457619960040&sdata=kEyYHRIjNZCk%2B37moCSqbrZL426YccNQrsWpENcrZdw%3D&reserved=0

Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Stephen Hemminger and committed by
Greg Kroah-Hartman
d765edbb 83b15fed

+118 -19
+21
Documentation/ABI/testing/sysfs-bus-vmbus
··· 1 + What: /sys/bus/vmbus/devices/.../driver_override 2 + Date: August 2019 3 + Contact: Stephen Hemminger <sthemmin@microsoft.com> 4 + Description: 5 + This file allows the driver for a device to be specified which 6 + will override standard static and dynamic ID matching. When 7 + specified, only a driver with a name matching the value written 8 + to driver_override will have an opportunity to bind to the 9 + device. The override is specified by writing a string to the 10 + driver_override file (echo uio_hv_generic > driver_override) and 11 + may be cleared with an empty string (echo > driver_override). 12 + This returns the device to standard matching rules binding. 13 + Writing to driver_override does not automatically unbind the 14 + device from its current driver or make any attempt to 15 + automatically load the specified driver. If no driver with a 16 + matching name is currently loaded in the kernel, the device 17 + will not bind to any driver. This also allows devices to 18 + opt-out of driver binding using a driver_override name such as 19 + "none". Only a single driver may be specified in the override, 20 + there is no support for parsing delimiters. 21 +
+96 -19
drivers/hv/vmbus_drv.c
··· 498 498 } 499 499 static DEVICE_ATTR_RO(device); 500 500 501 + static ssize_t driver_override_store(struct device *dev, 502 + struct device_attribute *attr, 503 + const char *buf, size_t count) 504 + { 505 + struct hv_device *hv_dev = device_to_hv_device(dev); 506 + char *driver_override, *old, *cp; 507 + 508 + /* We need to keep extra room for a newline */ 509 + if (count >= (PAGE_SIZE - 1)) 510 + return -EINVAL; 511 + 512 + driver_override = kstrndup(buf, count, GFP_KERNEL); 513 + if (!driver_override) 514 + return -ENOMEM; 515 + 516 + cp = strchr(driver_override, '\n'); 517 + if (cp) 518 + *cp = '\0'; 519 + 520 + device_lock(dev); 521 + old = hv_dev->driver_override; 522 + if (strlen(driver_override)) { 523 + hv_dev->driver_override = driver_override; 524 + } else { 525 + kfree(driver_override); 526 + hv_dev->driver_override = NULL; 527 + } 528 + device_unlock(dev); 529 + 530 + kfree(old); 531 + 532 + return count; 533 + } 534 + 535 + static ssize_t driver_override_show(struct device *dev, 536 + struct device_attribute *attr, char *buf) 537 + { 538 + struct hv_device *hv_dev = device_to_hv_device(dev); 539 + ssize_t len; 540 + 541 + device_lock(dev); 542 + len = snprintf(buf, PAGE_SIZE, "%s\n", hv_dev->driver_override); 543 + device_unlock(dev); 544 + 545 + return len; 546 + } 547 + static DEVICE_ATTR_RW(driver_override); 548 + 501 549 /* Set up per device attributes in /sys/bus/vmbus/devices/<bus device> */ 502 550 static struct attribute *vmbus_dev_attrs[] = { 503 551 &dev_attr_id.attr, ··· 576 528 &dev_attr_channel_vp_mapping.attr, 577 529 &dev_attr_vendor.attr, 578 530 &dev_attr_device.attr, 531 + &dev_attr_driver_override.attr, 579 532 NULL, 580 533 }; 581 534 ATTRIBUTE_GROUPS(vmbus_dev); ··· 612 563 return true; 613 564 } 614 565 615 - /* 616 - * Return a matching hv_vmbus_device_id pointer. 617 - * If there is no match, return NULL. 618 - */ 619 - static const struct hv_vmbus_device_id *hv_vmbus_get_id(struct hv_driver *drv, 620 - const uuid_le *guid) 566 + static const struct hv_vmbus_device_id * 567 + hv_vmbus_dev_match(const struct hv_vmbus_device_id *id, const uuid_le *guid) 568 + 569 + { 570 + if (id == NULL) 571 + return NULL; /* empty device table */ 572 + 573 + for (; !is_null_guid(&id->guid); id++) 574 + if (!uuid_le_cmp(id->guid, *guid)) 575 + return id; 576 + 577 + return NULL; 578 + } 579 + 580 + static const struct hv_vmbus_device_id * 581 + hv_vmbus_dynid_match(struct hv_driver *drv, const uuid_le *guid) 621 582 { 622 583 const struct hv_vmbus_device_id *id = NULL; 623 584 struct vmbus_dynid *dynid; 624 585 625 - /* Look at the dynamic ids first, before the static ones */ 626 586 spin_lock(&drv->dynids.lock); 627 587 list_for_each_entry(dynid, &drv->dynids.list, node) { 628 588 if (!uuid_le_cmp(dynid->id.guid, *guid)) { ··· 641 583 } 642 584 spin_unlock(&drv->dynids.lock); 643 585 644 - if (id) 645 - return id; 586 + return id; 587 + } 646 588 647 - id = drv->id_table; 648 - if (id == NULL) 649 - return NULL; /* empty device table */ 589 + static const struct hv_vmbus_device_id vmbus_device_null = { 590 + .guid = NULL_UUID_LE, 591 + }; 650 592 651 - for (; !is_null_guid(&id->guid); id++) 652 - if (!uuid_le_cmp(id->guid, *guid)) 653 - return id; 593 + /* 594 + * Return a matching hv_vmbus_device_id pointer. 595 + * If there is no match, return NULL. 596 + */ 597 + static const struct hv_vmbus_device_id *hv_vmbus_get_id(struct hv_driver *drv, 598 + struct hv_device *dev) 599 + { 600 + const uuid_le *guid = &dev->dev_type; 601 + const struct hv_vmbus_device_id *id; 654 602 655 - return NULL; 603 + /* When driver_override is set, only bind to the matching driver */ 604 + if (dev->driver_override && strcmp(dev->driver_override, drv->name)) 605 + return NULL; 606 + 607 + /* Look at the dynamic ids first, before the static ones */ 608 + id = hv_vmbus_dynid_match(drv, guid); 609 + if (!id) 610 + id = hv_vmbus_dev_match(drv->id_table, guid); 611 + 612 + /* driver_override will always match, send a dummy id */ 613 + if (!id && dev->driver_override) 614 + id = &vmbus_device_null; 615 + 616 + return id; 656 617 } 657 618 658 619 /* vmbus_add_dynid - add a new device ID to this driver and re-probe devices */ ··· 720 643 if (retval) 721 644 return retval; 722 645 723 - if (hv_vmbus_get_id(drv, &guid)) 646 + if (hv_vmbus_dynid_match(drv, &guid)) 724 647 return -EEXIST; 725 648 726 649 retval = vmbus_add_dynid(drv, &guid); ··· 785 708 if (is_hvsock_channel(hv_dev->channel)) 786 709 return drv->hvsock; 787 710 788 - if (hv_vmbus_get_id(drv, &hv_dev->dev_type)) 711 + if (hv_vmbus_get_id(drv, hv_dev)) 789 712 return 1; 790 713 791 714 return 0; ··· 802 725 struct hv_device *dev = device_to_hv_device(child_device); 803 726 const struct hv_vmbus_device_id *dev_id; 804 727 805 - dev_id = hv_vmbus_get_id(drv, &dev->dev_type); 728 + dev_id = hv_vmbus_get_id(drv, dev); 806 729 if (drv->probe) { 807 730 ret = drv->probe(dev, dev_id); 808 731 if (ret != 0)
+1
include/linux/hyperv.h
··· 1125 1125 u16 device_id; 1126 1126 1127 1127 struct device device; 1128 + char *driver_override; /* Driver name to force a match */ 1128 1129 1129 1130 struct vmbus_channel *channel; 1130 1131 struct kset *channels_kset;