On 01/23/2012 09:08 AM, Paolo Bonzini wrote:
<devices>
<interface type='hostdev'>
<source>
<address type='pci' bus='0x06' slot='0x02'
function='0x0'/>
</source>
<mac address='00:16:3e:5d:c7:9e'/>
<address type='pci' .../>
</interface>
</devices>
This is the model that I'm now following.
Looking further into it, I've found that there are lots of places in the
libvirt code that scan through all the <hostdev> entries, and call
functions that expect a virDomainHostdevDef as an argument. Of course
all those same places will need to be visited with devices that are
assigned via <interface> (virDomainNetDef) as well (and this will also
apparently be needed for <controller> devices in the near future).
What I'm thinking of doing now, is changing virDomainHostdevDef in the
following way:
typedef virDomainDeviceSourceInfo *virDomainDeviceSourceInfoPtr;
struct _virDomainDeviceSourceInfo {
int mode; /* enum virDomainHostdevMode */
bool managed;
union {
virDomainDeviceSubsysAddress subsys; /* USB or PCI */
struct {
int dummy;
} caps;
};
virDomainHostdevOrigStates origstates;
};
typedef struct _virDomainHostdevDef virDomainHostdevDef;
typedef virDomainHostdevDef *virDomainHostdevDefPtr;
struct _virDomainHostdevDef {
virDomainDeviceDefPtr parent; /* specific device containing
this def */
virDomainDeviceSourceInfo source; /* host address info */
virDomainDeviceInfoPtr info; /* guest address info */
};
(note that "info" is now a separate object, rather than simply being
contained in the HostdevDef!)
This new HostdevDef can then be included directly in higher level device
types, e.g:
struct _virDomainNetDef {
enum virDomainNetType type;
unsigned char mac[VIR_MAC_BUFLEN];
...
union {
...
struct {
char *linkdev;
int mode; /* enum virMacvtapMode from util/macvtap.h */
virNetDevVPortProfilePtr virtPortProfile;
} direct;
** struct {
** virDomainHostdevDef def;
} hostdev;
} data;
struct {
bool sndbuf_specified;
unsigned long sndbuf;
} tune;
...
char *ifname;
virDomainDeviceInfo info;
...
};
for <interface type='hostdev'>, the hostdev would be populated like this:
(interface->data.hostdev.def.source will already be filled in from
Parse)
interface->data.hostdev.def.info = &interface->info;
interface->data.hostdev.def.parent.type = VIR_DOMAIN_DEVICE_NET;
interface->data.hostdev.def.parent.data.net = interface;
At this point, &interface->data.hostdev.def can be sent as a parameter
to any function that's expecting a virDomainHostdevDef. Beyond that, I'm
thinking it can *even be added to the hostdevs list in the DomainDef*.
This would work in the following way:
0) If a parent device (in our example a virDomainNetDef) is
type='hostdev', in addition to be included in its normal higher level
device list (e.g. domain->nets), parent->data.hostdev will be filled in
as indicated above, and &parent->data.hostdev will be added to the
domain's hostdevs list.
1) When a function is scanning through all the hostdevs to do device
management, it will act on this higher level device just as any generic
device, except that there may be callouts to setup functions based on
the value of hostdevs[n]->parent.type (e.g. to setup a MAC address or
virtual port profile).
2) When an entry from the hostdevs list is being freed, any hostdev that
has a non-NULL parent will simply be removed from the list (and a
callout made to the equivalent function to remove the hostdev's parent
from its own list).
3) When an entry from the higher level device list is being freed, it
will also be removed from the hostdev list.
4) when one of these "intelligent hostdevs" is attached/detached,
depending on hostdev->parent.type, it may callout to a device-specific
function,
By doing things this way, we assure that these new higher level devices
will always be included in any scans of hostdevs, while avoiding the
necessity to add a new loop to every one of the functions that scans
them each time we add support for PCI passthrough of another
higher-level device type.
Does this sound reasonable? (I'm making a proof-of-concept now, but
figured I'd solicit input in the meantime).
-------------------
The next problem: We will need to be able to configure everything that's
in a <hostdev> from within an <interface>, but there are a few things we
haven't discussed yet:
1) "type='pci'" vs. "type='usb'"
<hostdev> has one of these directly as an attribute of the toplevel
element, so it isn't given in the source <address> element. In the case
of <interface>, type is already used for something else in the toplevel
element, but it can instead be given as part of the <address>. So which
do you think is better:
<interface type='hostdev'>
<source>
<address type='pci' domain='0' ... />
</source>
...
or:
<interface type='pci'>
<source>
<address domain='0' .... />
?? In either case, "type='pci'" could be replaced with
"type='usb'".
Note that if we use the first option, it will be possible to do
something like:
<interface type='hostdev'>
<source dev='eth22'/>
and have libvirt determine at attachtime whether eth22 is a usb or pci
device (I'm sure 99 44/100% of all uses of this will be with pci
devices, but still...).
2) "managed='yes'"
This obviously needs to go *somewhere*. Does this look okay?
<interface type='hostdev' managed='yes'>
...
3) "mode='subsystem'"
Since the other mode "capability" has never been implemented, and
apparently won't be, I don't see any need to give this a place in the
<interface> XML. For now it will always be subsystem, and if we ever
need to add a mode attribute, "subsystem" will just be the default.
So what I end up with is this:
<interface type='hostdev' managed='yes'>
<source dev='eth22'/>
...
and
<interface type='hostdev' managed='yes'>
<source>
<address type='pci' domain='0' .... />
</source>
...
Note that when "dev='eth22'" is given, an <address> element will
be
added at attach time (I haven't decided yet if it's best for this
element to persist (with appropriate checks to make sure it continues to
match the named network device), or should be erased and re-learned each
time.