[libvirt] [RFC] Host CPU passthrough
by Jiri Denemark
Hi,
AFAIK this topic is not new but I think we still do not have a good solution
for it. Libvirt already supports specifying what CPU and its features a guest
should see but imagine one wants to run a guest on the best possible CPU. The
current way is to copy the <cpu> element from capabilities XML into domain
XML. This approach is fine since it provides reproducible environment and such
domain can even be migrated to a different host. But the CPU shown provided to
a guest is not the same as the real host CPU. It's based on a model which
doesn't reflect all aspects of real CPUs. Ideally, we would model everything
but that's quite complicated and may need updating anytime a new CPU is
introduced.
I think we should add a possibility to passthrough host CPU into a domain no
matter what the CPU looks like. Domains configured in such a way will not be
allowed to be migrated since we cannot guarantee the same CPU on destination
host but they would be able to see the real CPU. Another benefit is that a
single XML configuration would work on any CPU without modifications. Since
possible issues with such domains will be harder to debug we can mark them as
tainted just like we do if someone calls qemu monitor commands directly.
I'm not sure what XML representation makes the most sense so I'll mention
several options which I'm thinking about:
- "host" ("native", "passthrough", whatever) special model; same name space as
real CPU models, I don't like this too much:
<cpu match='exact'>
<model>host</model>
</cpu>
- "host" match policy; has nice first-look semantics that the guest CPU
matches host's but doesn't play very nice with the defined semantics of
"match" attribute:
<cpu match='host'/>
- new "mode" attribute; IMHO the best one, similar to what we have for SMBIOS:
<cpu mode='host'/>
vs.
<cpu mode='custom' match='...'>
<model>...</model.
...
</cpu>
What do you think?
Jirka
13 years, 10 months
[libvirt] [PATCH] snapshot: tweak misleading wording
by Eric Blake
Fixes confusion introduced in commit 98369d3.
* tools/virsh.c (cmdSnapshotParent): Operates on named snapshot,
not current.
---
virsh.pod got it right. I'm pushing this under the trivial rule.
tools/virsh.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c
index 51ba0a8..49917f3 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -12282,7 +12282,7 @@ cleanup:
* "snapshot-parent" command
*/
static const vshCmdInfo info_snapshot_parent[] = {
- {"help", N_("Get the name of the parent of the current snapshot")},
+ {"help", N_("Get the name of the parent of a snapshot")},
{"desc", N_("Extract the snapshot's parent, if any")},
{NULL, NULL}
};
--
1.7.4.4
13 years, 10 months
[libvirt] main: fix some compilation issues on non-linux platforms
by Stefan Berger
This patch fixes *some* compilation issues on non-Linux platforms (cygwin).
Signed-off-by: Stefan Berger <stefanb(a)linux.vnet.ibm.com>
diff --git a/src/util/interface.c b/src/util/interface.c
index aec12f5..5d473b7 100644
--- a/src/util/interface.c
+++ b/src/util/interface.c
@@ -1317,7 +1317,7 @@ ifaceGetPhysicalFunction(const char *ifname, char
**pfname)
}
#else
int
-ifaceIsVirtualFunction(const char *ifname)
+ifaceIsVirtualFunction(const char *ifname ATTRIBUTE_UNUSED)
{
ifaceError(VIR_ERR_INTERNAL_ERROR, "%s",
_("ifaceIsVirtualFunction is not supported on non-linux "
@@ -1326,8 +1326,9 @@ ifaceIsVirtualFunction(const char *ifname)
}
int
-ifaceGetVirtualFunctionIndex(const char *pfname, const char *vfname,
- int *vf_index)
+ifaceGetVirtualFunctionIndex(const char *pfname ATTRIBUTE_UNUSED,
+ const char *vfname ATTRIBUTE_UNUSED,
+ int *vf_index ATTRIBUTE_UNUSED)
{
ifaceError(VIR_ERR_INTERNAL_ERROR, "%s",
_("ifaceGetVirtualFunctionIndex is not supported on
non-linux "
@@ -1336,7 +1337,8 @@ ifaceGetVirtualFunctionIndex(const char *pfname,
const char *vfname,
}
int
-ifaceGetPhysicalFunction(const char *ifname, char **pfname)
+ifaceGetPhysicalFunction(const char *ifname ATTRIBUTE_UNUSED,
+ char **pfname ATTRIBUTE_UNUSED)
{
ifaceError(VIR_ERR_INTERNAL_ERROR, "%s",
_("ifaceGetPhysicalFunction is not supported on non-linux "
diff --git a/src/util/pci.c b/src/util/pci.c
index 7040e55..9873f33 100644
--- a/src/util/pci.c
+++ b/src/util/pci.c
@@ -1685,6 +1685,8 @@ int pciDeviceIsAssignable(pciDevice *dev,
return 1;
}
+#ifdef __linux__
+
/*
* returns 1 if equal and 0 if not
*/
@@ -1804,7 +1806,6 @@ out:
return ret;
}
-#ifdef __linux__
/*
* Returns Physical function given a virtual function
*/
@@ -2010,8 +2011,8 @@ out:
}
#else
int
-pciGetPhysicalFunction(const char *vf_sysfs_path,
- struct pci_config_address **physical_function)
+pciGetPhysicalFunction(const char *vf_sysfs_path ATTRIBUTE_UNUSED,
+ struct pci_config_address **physical_function
ATTRIBUTE_UNUSED)
{
pciReportError(VIR_ERR_INTERNAL_ERROR, _("pciGetPhysicalFunction
is not "
"supported on non-linux platforms"));
@@ -2019,9 +2020,9 @@ pciGetPhysicalFunction(const char *vf_sysfs_path,
}
int
-pciGetVirtualFunctions(const char *sysfs_path,
- struct pci_config_address ***virtual_functions,
- unsigned int *num_virtual_functions)
+pciGetVirtualFunctions(const char *sysfs_path ATTRIBUTE_UNUSED,
+ struct pci_config_address ***virtual_functions
ATTRIBUTE_UNUSED,
+ unsigned int *num_virtual_functions ATTRIBUTE_UNUSED)
{
pciReportError(VIR_ERR_INTERNAL_ERROR, _("pciGetVirtualFunctions
is not "
"supported on non-linux platforms"));
@@ -2029,7 +2030,7 @@ pciGetVirtualFunctions(const char *sysfs_path,
}
int
-pciDeviceIsVirtualFunction(const char *vf_sysfs_device_link)
+pciDeviceIsVirtualFunction(const char *vf_sysfs_device_link
ATTRIBUTE_UNUSED)
{
pciReportError(VIR_ERR_INTERNAL_ERROR,
_("pciDeviceIsVirtualFunction is "
"not supported on non-linux platforms"));
@@ -2037,9 +2038,9 @@ pciDeviceIsVirtualFunction(const char
*vf_sysfs_device_link)
}
int
-pciGetVirtualFunctionIndex(const char *pf_sysfs_device_link,
- const char *vf_sysfs_device_link,
- int *vf_index)
+pciGetVirtualFunctionIndex(const char *pf_sysfs_device_link
ATTRIBUTE_UNUSED,
+ const char *vf_sysfs_device_link
ATTRIBUTE_UNUSED,
+ int *vf_index ATTRIBUTE_UNUSED)
{
pciReportError(VIR_ERR_INTERNAL_ERROR,
_("pciGetVirtualFunctionIndex is "
"not supported on non-linux platforms"));
@@ -2048,7 +2049,8 @@ pciGetVirtualFunctionIndex(const char
*pf_sysfs_device_link,
}
int
-pciDeviceNetName(char *device_link_sysfs_path, char **netname)
+pciDeviceNetName(char *device_link_sysfs_path ATTRIBUTE_UNUSED,
+ char **netname ATTRIBUTE_UNUSED)
{
pciReportError(VIR_ERR_INTERNAL_ERROR, _("pciDeviceNetName is not "
"supported on non-linux platforms"));
diff --git a/src/util/virfile.c b/src/util/virfile.c
index 0edf058..1158998 100644
--- a/src/util/virfile.c
+++ b/src/util/virfile.c
@@ -44,7 +44,7 @@
int virFileClose(int *fdptr, bool preserve_errno)
{
- int saved_errno;
+ int saved_errno = 0;
int rc = 0;
if (*fdptr >= 0) {
@@ -62,7 +62,7 @@ int virFileClose(int *fdptr, bool preserve_errno)
int virFileFclose(FILE **file, bool preserve_errno)
{
- int saved_errno;
+ int saved_errno = 0;
int rc = 0;
if (*file) {
diff --git a/src/util/virpidfile.c b/src/util/virpidfile.c
index 7dd3c51..1927051 100644
--- a/src/util/virpidfile.c
+++ b/src/util/virpidfile.c
@@ -213,6 +213,8 @@ int virPidFileReadPathIfAlive(const char *path,
#ifdef __linux__
if (virFileLinkPointsTo(procpath, binpath) == 0)
*pid = -1;
+#else
+ (void)binpath;
#endif
VIR_FREE(procpath);
13 years, 10 months
[libvirt] Bug - libvirt persists on having dnsmasq
by Zdenek Styblik
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hi,
I've noticed libvirt is persisting to have dnsmasq present and use it.
Version of libvirt in question is 0.9.4.
There is no dnsmasq present on the Host during libvirt compilation, yet
it seems to me ./configure wrongfully finds one:
~~~ SNIP ~~~
configure:46087: checking for dnsmasq
configure:46119: result: dnsmasq
~~~ SNIP ~~~
shouldn't there be "result: no"? As far as I remember - no dnsmasq
present during compilation, no usage of it later. However I might have
missed something - change in behavior, requirements etc. This is
possible. Still, if required "component" is missing, I say compilation
resp. configure should fail.
And this of course breaks things later on:
~~~ SNIP ~~~
virsh # net-start virtnet01
error: Failed to start network virtnet01
error: Cannot find 'dnsmasq' in path: No such file or directory
~~~ SNIP ~~~
and there is no DHCP defined for 'virtnet01' thus there is no reason to
bring dnsmasq in. Neither can be new network created and started via
virt-manager.
Thanks,
Zdenek
- --
Zdenek Styblik
email: stybla(a)turnovfree.net
jabber: stybla(a)jabber.turnovfree.net
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iEYEARECAAYFAk5H2OwACgkQ8MreUbSH7ingtgCePYxU4zroZQo+3xrSxT7/hekN
Qc8An3U/nDY/C8Z0oLQ76Za1SrR++0ON
=TkKh
-----END PGP SIGNATURE-----
13 years, 10 months
[libvirt] [PATCH] Fix race condition in abort of stream
by Daniel P. Berrange
From: "Daniel P. Berrange" <berrange(a)redhat.com>
If a stream gets a server initiated abort, the client may still
send an abort request before it receives the server side abort.
This causes the server to send back another abort for the
stream. Since the protocol defines that abort is the last thing
to be sent, the client gets confused by this second abort from
the server. If the stream is already shutdown, just drop any
client requested abort, rather than sending back another message.
This fixes the regression from previous versions.
Tested as follows
In one virsh session
virsh # start foo
virsh # console foo
In other virsh session
virsh # destroy foo
The first virsh session should be able to continue issues
commands without error. Prior to this patch it saw
virsh # list
error: Failed to list active domains
error: An error occurred, but the cause is unknown
virsh # list
error: Failed to list active domains
error: no call waiting for reply with prog 536903814 vers 1 serial 9
* src/rpc/virnetserverprogram.c: Drop abort requests
for streams which no longer exist
---
src/rpc/virnetserverprogram.c | 25 ++++++++-----------------
1 files changed, 8 insertions(+), 17 deletions(-)
diff --git a/src/rpc/virnetserverprogram.c b/src/rpc/virnetserverprogram.c
index 63a6b6d..ca80ae0 100644
--- a/src/rpc/virnetserverprogram.c
+++ b/src/rpc/virnetserverprogram.c
@@ -257,23 +257,14 @@ int virNetServerProgramDispatch(virNetServerProgramPtr prog,
* stream packets after we closed down a stream. Just drop & ignore
* these.
*/
- if (msg->header.status == VIR_NET_CONTINUE) {
- VIR_INFO("Ignoring unexpected stream data serial=%d proc=%d status=%d",
- msg->header.serial, msg->header.proc, msg->header.status);
- /* Send a dummy reply to free up 'msg' & unblock client rx */
- memset(msg, 0, sizeof(*msg));
- msg->header.type = VIR_NET_REPLY;
- if (virNetServerClientSendMessage(client, msg) < 0) {
- ret = -1;
- goto cleanup;
- }
- } else {
- VIR_INFO("Unexpected stream control message serial=%d proc=%d status=%d",
- msg->header.serial, msg->header.proc, msg->header.status);
- virNetError(VIR_ERR_RPC,
- _("Unexpected stream control message serial=%d proc=%d status=%d"),
- msg->header.serial, msg->header.proc, msg->header.status);
- goto error;
+ VIR_INFO("Ignoring unexpected stream data serial=%d proc=%d status=%d",
+ msg->header.serial, msg->header.proc, msg->header.status);
+ /* Send a dummy reply to free up 'msg' & unblock client rx */
+ memset(msg, 0, sizeof(*msg));
+ msg->header.type = VIR_NET_REPLY;
+ if (virNetServerClientSendMessage(client, msg) < 0) {
+ ret = -1;
+ goto cleanup;
}
ret = 0;
break;
--
1.7.6
13 years, 10 months
[libvirt] [PATCH 0/4 v3] macvtap: Support for sending port profile message for a SRIOV VF to its PF
by Roopa Prabhu
This patch tries to fix getPhysFn in macvtap.c to get the physical
function(PF) of the direct attach interface, if the interface is a SR-IOV VF.
It moves some of the sriov pci device handling code from node_device_driver
to src/util/pci.[ch].
This patch series implements the following
01/4 - pci: Move some pci sriov helper code out of node device driver to util/pci
02/4 - pci: Add helper functions for sriov devices
03/4 - interface: Add functions to get sriov PF/VF relationship of a net interface
04/4 - macvtap: Fix getPhysfn to get the PF of a direct attach network interface
Changelog:
----------
v1: RFC patch introduced new functions to get PF/VF relationship of SRIOV
net devices by comparing PF/VF sysfs device links. Also mentioned the
existance of some related code in node_device_driver. The feedback was to
move and use the node_device_driver code
v2: Moves sriov get_physical_function and get_virtual_functions from
node_device_driver to src/util/pci*. And reworks the rest of the patches
around the new code.
v3: Incorporated Stefan Berger's comments. Fixed indentation errors shown by
'make syntax-check'. Used pciReportError in src/util/pci
Signed-off-by: Roopa Prabhu <roprabhu(a)cisco.com>
Signed-off-by: Christian Benvenuti <benve(a)cisco.com>
Signed-off-by: David Wang <dwang2(a)cisco.com>
13 years, 10 months
[libvirt] [PATCH v3] daemon: Add early libvirtd start verbose errors.
by Peter Krempa
Early errors during start of libvirtd didn't have
an error reporting mechanism and caused libvirtd
to exit silently (only the return value indicated
an error).
Libvirt logging is initialized very early using
enviroment variables and the internal error reporting
API is used to report early errors.
v2 changes:
- print errors unconditionaly before logging starts
- fix message to US spelling
v2.5 changes:
- initialize logging from enviroment
- log all early errors using VIR_ERROR
v3 changes:
- move virSetLogFromEnv() after virInitialize()
fixes: https://bugzilla.redhat.com/show_bug.cgi?id=728654
---
On 08/12/2011 12:09 PM, Daniel P. Berrange wrote:
> Ok, not quiet that early. virInitialize() must be the first libvirt
> call made since it initializes threading which is needed by logging.
Uh, sorry. I didn't check for that. Now it should be in the correct place.
>
> Daniel
Peter Krempa
daemon/libvirtd.c | 39 +++++++++++++++++++++++++++++----------
1 files changed, 29 insertions(+), 10 deletions(-)
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c
index 53f1002..c7382b0 100644
--- a/daemon/libvirtd.c
+++ b/daemon/libvirtd.c
@@ -1293,6 +1293,9 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE);
}
+ /* initialize early logging */
+ virLogSetFromEnv();
+
while (1) {
int optidx = 0;
int c;
@@ -1328,14 +1331,18 @@ int main(int argc, char **argv) {
case 'p':
VIR_FREE(pid_file);
- if (!(pid_file = strdup(optarg)))
+ if (!(pid_file = strdup(optarg))) {
+ VIR_ERROR(_("Can't allocate memory"));
exit(EXIT_FAILURE);
+ }
break;
case 'f':
VIR_FREE(remote_config_file);
- if (!(remote_config_file = strdup(optarg)))
+ if (!(remote_config_file = strdup(optarg))) {
+ VIR_ERROR(_("Can't allocate memory"));
exit(EXIT_FAILURE);
+ }
break;
case OPT_VERSION:
@@ -1347,27 +1354,33 @@ int main(int argc, char **argv) {
return 2;
default:
- fprintf (stderr, _("%s: internal error: unknown flag: %c\n"),
- argv[0], c);
+ VIR_ERROR(_("%s: internal error: unknown flag: %c"),
+ argv[0], c);
exit (EXIT_FAILURE);
}
}
- if (!(config = daemonConfigNew(privileged)))
+ if (!(config = daemonConfigNew(privileged))) {
+ VIR_ERROR(_("Can't create initial configuration"));
exit(EXIT_FAILURE);
+ }
/* No explicit config, so try and find a default one */
if (remote_config_file == NULL) {
implicit_conf = true;
if (daemonConfigFilePath(privileged,
- &remote_config_file) < 0)
+ &remote_config_file) < 0) {
+ VIR_ERROR(_("Can't determine config path"));
exit(EXIT_FAILURE);
+ }
}
/* Read the config file if it exists*/
if (remote_config_file &&
- daemonConfigLoad(config, remote_config_file, implicit_conf) < 0)
+ daemonConfigLoad(config, remote_config_file, implicit_conf) < 0) {
+ VIR_ERROR(_("Can't load config file '%s'"), remote_config_file);
exit(EXIT_FAILURE);
+ }
if (config->host_uuid &&
virSetHostUUIDStr(config->host_uuid) < 0) {
@@ -1375,19 +1388,25 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE);
}
- if (daemonSetupLogging(config, privileged, verbose, godaemon) < 0)
+ if (daemonSetupLogging(config, privileged, verbose, godaemon) < 0) {
+ VIR_ERROR(_("Can't initialize logging"));
exit(EXIT_FAILURE);
+ }
if (!pid_file && privileged &&
daemonPidFilePath(privileged,
- &pid_file) < 0)
+ &pid_file) < 0) {
+ VIR_ERROR(_("Can't determine pid file path."));
exit(EXIT_FAILURE);
+ }
if (daemonUnixSocketPaths(config,
privileged,
&sock_file,
- &sock_file_ro) < 0)
+ &sock_file_ro) < 0) {
+ VIR_ERROR(_("Can't determine socket paths"));
exit(EXIT_FAILURE);
+ }
if (godaemon) {
char ebuf[1024];
--
1.7.6
13 years, 10 months
[libvirt] Patch for verbose Disk transfer
by Tom Vijlbrief
A virsh command like:
migrate --live --copy-storage-all Guest qemu+ssh://user@host/system
--persistent --verbose
shows
Migration: [ 0 %]
during the storage copy and does not start counting
untill the ram transfer starts
Patch for including the storage copy:
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index 7bf733d..4ac8008 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -1183,6 +1183,9 @@ cleanup:
#define MIGRATION_TRANSFER_PREFIX "transferred ram: "
#define MIGRATION_REMAINING_PREFIX "remaining ram: "
#define MIGRATION_TOTAL_PREFIX "total ram: "
+#define MIGRATION_DISK_TRANSFER_PREFIX "transferred disk: "
+#define MIGRATION_DISK_REMAINING_PREFIX "remaining disk: "
+#define MIGRATION_DISK_TOTAL_PREFIX "total disk: "
int qemuMonitorTextGetMigrationStatus(qemuMonitorPtr mon,
int *status,
@@ -1246,6 +1249,7 @@ int qemuMonitorTextGetMigrationStatus(qemuMonitorPtr mon,
goto cleanup;
}
*remaining *= 1024;
+ tmp = end;
if (!(tmp = strstr(tmp, MIGRATION_TOTAL_PREFIX)))
goto done;
@@ -1257,7 +1261,53 @@ int qemuMonitorTextGetMigrationStatus(qemuMonitorPtr mon,
goto cleanup;
}
*total *= 1024;
+ tmp = end;
+
+ /*
+ * Check for Optional Disk Migration status
+ */
+
+ unsigned long long disk_transferred= 0;
+ unsigned long long disk_remaining= 0;
+ unsigned long long disk_total= 0;
+
+ if (!(tmp = strstr(tmp, MIGRATION_DISK_TRANSFER_PREFIX)))
+ goto done;
+ tmp += strlen(MIGRATION_DISK_TRANSFER_PREFIX);
+ if (virStrToLong_ull(tmp, &end, 10, &disk_transferred) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse disk migration data
transferred statistic %s"), tmp);
+ goto cleanup;
+ }
+ disk_transferred *= 1024;
+ (*transferred)+= disk_transferred;
+ tmp = end;
+
+ if (!(tmp = strstr(tmp, MIGRATION_DISK_REMAINING_PREFIX)))
+ goto done;
+ tmp += strlen(MIGRATION_DISK_REMAINING_PREFIX);
+
+ if (virStrToLong_ull(tmp, &end, 10, &disk_remaining) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse disk migration data
remaining statistic %s"), tmp);
+ goto cleanup;
+ }
+ disk_remaining *= 1024;
+ (*remaining)+= disk_remaining;
+ tmp = end;
+
+ if (!(tmp = strstr(tmp, MIGRATION_DISK_TOTAL_PREFIX)))
+ goto done;
+ tmp += strlen(MIGRATION_DISK_TOTAL_PREFIX);
+
+ if (virStrToLong_ull(tmp, &end, 10, &disk_total) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse disk migration data
total statistic %s"), tmp);
+ goto cleanup;
+ }
+ disk_total *= 1024;
+ (*total)+= disk_total;
}
}
13 years, 10 months
[libvirt] maint: add missing symbols
by Stefan Berger
Add missing symbols to libvirt_private.syms.
Signed-off-by: Stefan Berger <stefanb(a)linux.vnet.ibm.com>
---
src/libvirt_private.syms | 8 ++++++++
1 file changed, 8 insertions(+)
Index: libvirt-acl/src/libvirt_private.syms
===================================================================
--- libvirt-acl.orig/src/libvirt_private.syms
+++ libvirt-acl/src/libvirt_private.syms
@@ -539,8 +539,11 @@ ifaceGetIndex;
ifaceGetMacAddress;
ifaceGetIPAddress;
ifaceGetNthParent;
+ifaceGetPhysicalFunction;
+ifaceGetVirtualFunctionIndex;
ifaceGetVlanID;
ifaceIsUp;
+ifaceIsVirtualFunction;
ifaceLinkDel;
ifaceMacvtapLinkAdd;
ifaceMacvtapLinkDump;
@@ -841,6 +844,7 @@ pciDettachDevice;
pciDeviceFileIterate;
pciDeviceGetManaged;
pciDeviceIsAssignable;
+pciDeviceIsVirtualFunction;
pciDeviceListAdd;
pciDeviceListCount;
pciDeviceListDel;
@@ -848,10 +852,14 @@ pciDeviceListFree;
pciDeviceListGet;
pciDeviceListNew;
pciDeviceListSteal;
+pciDeviceNetName;
pciDeviceReAttachInit;
pciDeviceSetManaged;
pciFreeDevice;
pciGetDevice;
+pciGetPhysicalFunction;
+pciGetVirtualFunctionIndex;
+pciGetVirtualFunctions;
pciReAttachDevice;
pciResetDevice;
pciWaitForDeviceCleanup;
13 years, 10 months