
On Thu, Jan 21, 2016 at 02:54:10PM -0500, Laine Stump wrote: [...]
src/libvirt_private.syms | 2 + src/qemu/qemu_process.c | 10 +- src/util/virnetdevmacvlan.c | 438 +++++++++++++++++++++++++++++++++++++------- src/util/virnetdevmacvlan.h | 5 +- 4 files changed, 390 insertions(+), 65 deletions(-)
[...]
diff --git a/src/util/virnetdevmacvlan.c b/src/util/virnetdevmacvlan.c index 496416e..20a821a 100644 --- a/src/util/virnetdevmacvlan.c +++ b/src/util/virnetdevmacvlan.c
[...]
+ * virNetDevMacVLanReserveID: + * + * @id: id 0 - MACVLAN_MAX_ID+1 to reserve (or -1 for "first free") + * @flags: set VIR_NETDEV_MACVLAN_CREATE_WITH_TAP for macvtapN else macvlanN + * @quietfail: don't log an error if this name is already in-use + * + * Reserve the indicated ID in the appropriate bitmap, or find the + * first free ID if @id is -1. + * + * returns id or -1 to indicate failure + */ +static int +virNetDevMacVLanReserveID(int id, unsigned int flags, bool quietfail) +{ + virBitmapPtr bitmap; + + if (virNetDevMacVLanInitialize() < 0) + return -1; + + bitmap = (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? + macvtapIDs : macvlanIDs; + + if (id > MACVLAN_MAX_ID) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("can't use name %s%d - out of range 0-%d"), + (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? + MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX, + id, MACVLAN_MAX_ID); + return -1; + } + + if (id < 0 && + (id = virBitmapNextClearBit(bitmap, 0)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("no unused %s names available"), + (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? + MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX); + return -1; + } + + if (virBitmapIsBitSet(bitmap, id)) { + if (quietfail) { + VIR_INFO("couldn't reserve name %s%d - already in use", + (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? + MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX, id); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("couldn't reserve name %s%d - already in use"), + (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? + MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX, id); + } + return -1; + } + + if (virBitmapSetBit(bitmap, id) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("couldn't mark %s%d as used"), + (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? + MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX, id); + return -1; + } + VIR_INFO("reserving device %s%d", + (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? + MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX, id); + return id; +} + + +/** + * virNetDevMacVLanReserveNextFreeID: + * + * @id: id to start scanning at - return first free ID *after* this + * id (use -1 to start looking at the beginning) + * @flags: set VIR_NETDEV_MACVLAN_CREATE_WITH_TAP for macvtapN else macvlanN + * + * Reserve the indicated ID in the appropriate bitmap, or find the + * first free ID if @id is -1. + *
This comment isn't true. The function takes the id, pass it to the virBitmanNextClearBit and get a next free ID even if original id >= 0. It always tries to find next free id starting from the position specified by id. The second issue I have with those function is that they are almost identical and I think they can be easily merged together extending the virNetDevMacVLanReserveID() like this: static int virNetDevMacVLanReserverID(int id, unsigned int flags, bool quietfail, bool nextFree) { [...] if ((id < 0 || nextFree ) && (id = virBitmapNextClearBit(bitmap, 0)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("no unused %s names available"), (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX); return -1; } [...] }
+ * returns id or -1 to indicate failure + */ +static int +virNetDevMacVLanReserveNextFreeID(int id, unsigned int flags) +{ + virBitmapPtr bitmap; + + if (virNetDevMacVLanInitialize() < 0) + return -1; + + bitmap = (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? + macvtapIDs : macvlanIDs; + + if (id > MACVLAN_MAX_ID) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("can't use name %s%d - out of range 0-%d"), + (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? + MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX, + id, MACVLAN_MAX_ID); + return -1; + } + + if ((id = virBitmapNextClearBit(bitmap, id)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("no unused %s names available"), + (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? + MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX); + return -1; + } + + if (virBitmapIsBitSet(bitmap, id)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("couldn't reserve name %s%d - already in use"), + (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? + MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX, id); + return -1; + } + + if (virBitmapSetBit(bitmap, id) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("couldn't mark %s%d as used"), + (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? + MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX, id); + return -1; + } + VIR_INFO("reserving device %s%d", + (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? + MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX, id); + return id; +} +
[...]
@@ -724,14 +1002,20 @@ virNetDevMacVLanVPortProfileRegisterCallback(const char *ifname, * virNetDevMacVLanCreateWithVPortProfile: * Create an instance of a macvtap device and open its tap character * device. - * @tgifname: Interface name that the macvtap is supposed to have. May - * be NULL if this function is supposed to choose a name + + * @ifnameRequested: Interface name that the caller wants the macvtap + * device to have, or NULL to pick the first available name + * appropriate for the type (macvlan%d or macvtap%d). If the + * suggested name fits one of those patterns, but is already in + * use, we will fallback to finding the first available. If the + * suggested name *doesn't* fit a pattern and the name is in use, + * we will fail. * @macaddress: The MAC address for the macvtap device * @linkdev: The interface name of the NIC to connect to the external bridge - * @mode: int describing the mode for 'bridge', 'vepa', 'private' or 'passthru'. + * @mode: macvtap mode (VIR_NETDEV_MACVLAN_MODE_(BRIDGE|VEPA|PRIVATE|PASSTHRU) * @vmuuid: The UUID of the VM the macvtap belongs to * @virtPortProfile: pointer to object holding the virtual port profile data - * @res_ifname: Pointer to a string pointer where the actual name of the + * @ifnameResult: Pointer to a string pointer where the actual name of the * interface will be stored into if everything succeeded. It is up * to the caller to free the string. * @tapfd: array of file descriptor return value for the new tap device @@ -744,39 +1028,36 @@ virNetDevMacVLanVPortProfileRegisterCallback(const char *ifname, * * Return 0 on success, -1 on error. */ -int virNetDevMacVLanCreateWithVPortProfile(const char *tgifname, - const virMacAddr *macaddress, - const char *linkdev, - virNetDevMacVLanMode mode, - const unsigned char *vmuuid, - virNetDevVPortProfilePtr virtPortProfile, - char **res_ifname, - virNetDevVPortProfileOp vmOp, - char *stateDir, - int *tapfd, - size_t tapfdSize, - unsigned int flags) +int +virNetDevMacVLanCreateWithVPortProfile(const char *ifnameRequested, + const virMacAddr *macaddress, + const char *linkdev, + virNetDevMacVLanMode mode, + const unsigned char *vmuuid, + virNetDevVPortProfilePtr virtPortProfile, + char **ifnameResult, + virNetDevVPortProfileOp vmOp, + char *stateDir, + int *tapfd, + size_t tapfdSize, + unsigned int flags) { const char *type = (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? "macvtap" : "macvlan"; - const char *prefix = (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? - MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX; const char *pattern = (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? MACVTAP_NAME_PATTERN : MACVLAN_NAME_PATTERN; - int c, rc; + int rc, reservedID = -1; char ifname[IFNAMSIZ]; int retries, do_retry = 0; uint32_t macvtapMode; - const char *cr_ifname = NULL; + const char *ifnameCreated = NULL; int ret; int vf = -1; bool vnet_hdr = flags & VIR_NETDEV_MACVLAN_VNET_HDR;
macvtapMode = modeMap[mode];
- *res_ifname = NULL; - - VIR_DEBUG("%s: VM OPERATION: %s", __FUNCTION__, virNetDevVPortProfileOpTypeToString(vmOp)); + *ifnameResult = NULL;
/** Note: When using PASSTHROUGH mode with MACVTAP devices the link * device's MAC address must be set to the VMs MAC address. In @@ -803,52 +1084,81 @@ int virNetDevMacVLanCreateWithVPortProfile(const char *tgifname, } }
- if (tgifname) { - if ((ret = virNetDevExists(tgifname)) < 0) - return -1; + if (ifnameRequested) { + bool isAutoName + = (STRPREFIX(ifnameRequested, MACVTAP_NAME_PREFIX) || + STRPREFIX(ifnameRequested, MACVLAN_NAME_PREFIX)); + + VIR_INFO("Requested macvtap device name: %s", ifnameRequested); + virMutexLock(&virNetDevMacVLanCreateMutex);
+ if ((ret = virNetDevExists(ifnameRequested)) < 0) { + virMutexUnlock(&virNetDevMacVLanCreateMutex); + return -1; + } if (ret) { - if (STRPREFIX(tgifname, prefix)) + if (isAutoName) goto create_name; virReportSystemError(EEXIST, - _("Unable to create macvlan device %s"), tgifname); + _("Unable to create macvlan device %s"), ifnameRequested); + virMutexUnlock(&virNetDevMacVLanCreateMutex);
Maybe use %s instead of macvlan as you've done in the new functions.
return -1; } - cr_ifname = tgifname; - rc = virNetDevMacVLanCreate(tgifname, type, macaddress, linkdev, - macvtapMode, &do_retry); - if (rc < 0) + if (isAutoName && + (reservedID = virNetDevMacVLanReserveName(ifnameRequested, true)) < 0) { + reservedID = -1; + goto create_name; + } + + if (virNetDevMacVLanCreate(ifnameRequested, type, macaddress, + linkdev, macvtapMode, &do_retry) < 0) { + if (isAutoName) { + virNetDevMacVLanReleaseName(ifnameRequested); + reservedID = -1; + goto create_name; + } + virMutexUnlock(&virNetDevMacVLanCreateMutex); return -1; - } else { + } + /* virNetDevMacVLanCreate() was successful - use this name */ + ifnameCreated = ifnameRequested; create_name: - retries = 5; + virMutexUnlock(&virNetDevMacVLanCreateMutex); + } + + retries = 8192; + while (!ifnameCreated && retries) { virMutexLock(&virNetDevMacVLanCreateMutex); - for (c = 0; c < 8192; c++) { - snprintf(ifname, sizeof(ifname), pattern, c); - if ((ret = virNetDevExists(ifname)) < 0) { - virMutexUnlock(&virNetDevMacVLanCreateMutex); + if ((reservedID = virNetDevMacVLanReserveNextFreeID(reservedID, flags)) < 0) { + virMutexUnlock(&virNetDevMacVLanCreateMutex); + virReportSystemError(EEXIST, "%s", + _("All macvlan device names are already being used")); + return -1;
Isn't this error message redundant? There will already be an error reported by virNetDevMacVLanReserveNextFreeID().