From 7ca56ab509779dc4fe4fbe9f00f4c025d597396d Mon Sep 17 00:00:00 2001 From: Jason Andryuk Date: Fri, 6 Oct 2017 13:19:25 -0400 Subject: [PATCH 1/2] Use 'force-emulate' to attach USB devices to a stubdom We allow overloading the pv-addons-version to 'force-emulate' to attach the USB device to the stubdom. The stubdom can then use the emulated USB to passthrough the device. This is a step toward allowing USB passthrough without pv addons. Signed-off-by: Jason Andryuk --- src/project.h | 1 + src/vm.c | 31 ++++++++++++++++++++++++++++++- src/xenstore.c | 17 +++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/project.h b/src/project.h index d2d32a6..6866035 100644 --- a/src/project.h +++ b/src/project.h @@ -124,6 +124,7 @@ typedef struct { struct list_head list; /**< Linux-kernel-style list item */ int domid; /**< VM domid */ char *uuid; /**< VM UUID */ + bool emulate; /**< Use stubdom for emulated passthrough */ } vm_t; /** diff --git a/src/vm.c b/src/vm.c index 4687a70..e3d006d 100644 --- a/src/vm.c +++ b/src/vm.c @@ -110,6 +110,10 @@ vm_add(const int domid, const char *uuid) struct list_head *pos; vm_t *vm; char *new_uuid; + char path[64]; + char *pvaddons_version = NULL; + gboolean pvaddons = true; + gboolean force_emulate = false; /* The UUID may have "_"s instead of "-"s, like in the xenmgr dbus reply. Fix this while duplicating the UUID. */ @@ -134,10 +138,35 @@ vm_add(const int domid, const char *uuid) } } - xd_log(LOG_DEBUG, "Adding vm, domid=%d, uuid=%s", domid, new_uuid); + snprintf(path, sizeof(path), "/vm/%s", new_uuid); + for (char *p = path; *p; p++) { + if (*p == '-') { + *p = '_'; + } + } + + if (!property_get_com_citrix_xenclient_xenmgr_vm_pv_addons_(g_xcbus, XENMGR, path, &pvaddons)) { + xd_log(LOG_ERR, "Error retrieving pvaddon status: %s %d", new_uuid, domid); + } + + if (!property_get_com_citrix_xenclient_xenmgr_vm_pv_addons_version_(g_xcbus, XENMGR, path, &pvaddons_version)) { + xd_log(LOG_ERR, "Error retrieving pvaddon version: %s %d", new_uuid, domid); + pvaddons_version = NULL; + } + + if (pvaddons_version && strcmp(pvaddons_version, "force-emulate") == 0) { + xd_log(LOG_INFO, "Forcing emulation: %s %d", new_uuid, domid); + force_emulate = true; + } + + free(pvaddons_version); + + xd_log(LOG_DEBUG, "Adding vm, domid=%d, uuid=%s pvaddons=%d force_emulate=%d", + domid, new_uuid, pvaddons, force_emulate); vm = malloc(sizeof(vm_t)); vm->domid = domid; vm->uuid = new_uuid; + vm->emulate = force_emulate || !pvaddons; list_add(&vm->list, &vms.list); return vm; diff --git a/src/xenstore.c b/src/xenstore.c index 65c365a..943e81a 100644 --- a/src/xenstore.c +++ b/src/xenstore.c @@ -151,7 +151,24 @@ xenstore_dom_read(unsigned int domid, const char *format, ...) int xenstore_get_dominfo(int domid, dominfo_t *di) { + char *stubdomid; + int stubid = 0; + vm_t *vm; + di->di_domid = domid; + + vm = vm_lookup(domid); + if (vm && vm->emulate) { + stubdomid = xenstore_dom_read(domid, "image/device-model-domid"); + if (stubdomid) { + stubid = strtol(stubdomid, NULL, 0); + free(stubdomid); + } + if (stubid > 0 && stubid < DOMID_FIRST_RESERVED) { + di->di_domid = stubid; + } + } + di->di_dompath = xs_get_domain_path(xs_handle, di->di_domid); if (!di->di_dompath) { xd_log(LOG_ERR, "Could not get domain %d path from xenstore", domid); From fa520801608d569a9c5f236ef6f8a2578fe630e0 Mon Sep 17 00:00:00 2001 From: Jason Andryuk Date: Fri, 18 Jan 2019 14:10:57 -0500 Subject: [PATCH 2/2] Add 'running' policy command A 'running' rule will check for matching devices and VMs, but only apply the rule to the first running VM found in policy search order. It will skip the assignment for shutdown VMs. *This is a change from the previous first match only configuration.* Prior to this new command, boot-time plugging devices required an 'always' or 'default' rule with a vm uuid. However, that meant that only the first entry in usb-rules would work for hotplug a device after boot - a second VM could not be matched. A generic 'allow' rule for a device would hotplug to any VM, but it would not attach on boot. The new rule will allow handling rules for the same vid:pid, but with differing uuids. The first match rule meant rule search would stop on whichever VM came first. Here we want to skip rules for non-running VMs so the Nth VM can get the device assignment if it is running and the others are not. Signed-off-by: Jason Andryuk --- src/policy.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++--- src/policy.h | 1 + 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/policy.c b/src/policy.c index 7c27b5a..eb48a8e 100644 --- a/src/policy.c +++ b/src/policy.c @@ -139,6 +139,8 @@ dump_rules(void) printf("default\n"); else if (rule->cmd == ALLOW) printf("allow\n"); + else if (rule->cmd == RUNNING) + printf("running\n"); else if (rule->cmd == DENY) printf("deny\n"); printf(" pos %d\n", rule->pos); @@ -255,8 +257,26 @@ vm_matches_rule(rule_t *rule, vm_t *vm) return true; } +static bool +rule_vm_running(rule_t *rule) +{ + vm_t *vm; + + /* If no VM UUID, then we never match */ + if (rule->vm_uuid == NULL) + return false; + + /* Lookup the VM */ + vm = vm_lookup_by_uuid(rule->vm_uuid); + if (vm == NULL) + return false; + + /* Is the vm running */ + return vm->domid > 0; +} + static rule_t* -rule_lookup(device_t *device, enum command cmd) +_rule_lookup(device_t *device, enum command cmd, bool check_vm_running) { struct list_head *pos; rule_t *rule; @@ -265,13 +285,32 @@ rule_lookup(device_t *device, enum command cmd) rule = list_entry(pos, rule_t, list); if (rule->cmd == cmd && device_matches_rule(rule, device)) { - return rule; + if (check_vm_running) { + if (rule_vm_running(rule)) { + return rule; + } + /* continue */ + } else { + return rule; + } } } return NULL; } +static rule_t * +rule_lookup(device_t *device, enum command cmd) +{ + return _rule_lookup(device, cmd, false); +} + +static rule_t * +rule_lookup_running(device_t *device, enum command cmd) +{ + return _rule_lookup(device, cmd, true); +} + static rule_t* sticky_lookup(device_t *device) { @@ -284,6 +323,12 @@ default_lookup(device_t *device) return rule_lookup(device, DEFAULT); } +static rule_t* +running_lookup(device_t *device) +{ + return rule_lookup_running(device, RUNNING); +} + void policy_add_rule(rule_t *new_rule) { @@ -550,6 +595,8 @@ policy_auto_assign_new_device(device_t *device) rule = sticky_lookup(device); if (rule == NULL) rule = default_lookup(device); + if (rule == NULL) + rule = running_lookup(device); if (rule != NULL) { vm = vm_lookup_by_uuid(rule->vm_uuid); } else { @@ -603,7 +650,7 @@ policy_auto_assign_devices_to_new_vm(vm_t *vm) * assign all devices that match the rule to the VM */ list_for_each_safe(pos, tmp, &rules.list) { rule = list_entry(pos, rule_t, list); - if ((rule->cmd == ALWAYS || rule->cmd == DEFAULT) && + if ((rule->cmd == ALWAYS || rule->cmd == DEFAULT || rule->cmd == RUNNING) && rule->vm_uuid != NULL && /* NULL vm_uuid means dom0, means no assignment */ !strcmp(rule->vm_uuid, vm->uuid)) { list_for_each(device_pos, &devices.list) { @@ -671,6 +718,7 @@ policy_parse_command_string(const char* cmd) if (strcasecmp(cmd, "always") == 0) return ALWAYS; if (strcasecmp(cmd, "default") == 0) return DEFAULT; if (strcasecmp(cmd, "deny") == 0) return DENY; + if (strcasecmp(cmd, "running") == 0) return RUNNING; return UNKNOWN; } @@ -690,6 +738,9 @@ policy_parse_command_enum(enum command cmd) case DEFAULT: strcpy(command, "default"); break; + case RUNNING: + strcpy(command, "running"); + break; default: strcpy(command, "deny"); break; diff --git a/src/policy.h b/src/policy.h index 64a4a62..c1ca35c 100644 --- a/src/policy.h +++ b/src/policy.h @@ -37,6 +37,7 @@ enum command { ALWAYS, /**< Always plug device to VM. implies ALLOW */ DEFAULT, /**< Plug device to VM by default, implies ALLOW */ ALLOW, /**< Allow device to be plugged to VM */ + RUNNING, /**< Plug device into running VM */ DENY, /**< Deny device to be plugged to VM */ UNKNOWN /**< Unknown command, usually due to input parsing */ };