diff --git a/sys/net/if.c b/sys/net/if.c --- a/sys/net/if.c +++ b/sys/net/if.c @@ -1289,6 +1289,11 @@ if (rc != 0) return (rc); + /* + * Release old unit number or accquire a new one. + */ + ifc_reassign_unit_vnet(ifp, new_vnet); + /* * Perform interface-specific reassignment tasks, if provided by * the driver. @@ -2762,12 +2767,21 @@ * ifunit() checks not being atomic with namespace * changes (renames, vmoves, if_attach, etc). */ + /* FIXME atomic bit flag */ + if (ifp->if_flags & IFF_RENAMING) + return (EBUSY); ifp->if_flags |= IFF_RENAMING; - + EVENTHANDLER_INVOKE(ifnet_departure_event, ifp); if_printf(ifp, "changing name to '%s'\n", new_name); + if ((error = ifc_reassign_unit(ifp, new_name)) != 0) { + EVENTHANDLER_INVOKE(ifnet_arrival_event, ifp); + + ifp->if_flags &= ~IFF_RENAMING; + return (error); + } IF_ADDR_WLOCK(ifp); strlcpy(old_name, ifp->if_xname, sizeof(old_name)); strlcpy(ifp->if_xname, new_name, sizeof(ifp->if_xname)); diff --git a/sys/net/if_clone.h b/sys/net/if_clone.h --- a/sys/net/if_clone.h +++ b/sys/net/if_clone.h @@ -119,6 +119,8 @@ int if_clone_destroy(const char *); int if_clone_list(struct if_clonereq *); void if_clone_restoregroup(struct ifnet *); +int ifc_reassign_unit(struct ifnet *, char *); +void ifc_reassign_unit_vnet(struct ifnet *, struct vnet *); /* The below interfaces are used only by epair(4). */ void if_clone_addif(struct if_clone *, struct ifnet *); diff --git a/sys/net/if_clone.c b/sys/net/if_clone.c --- a/sys/net/if_clone.c +++ b/sys/net/if_clone.c @@ -381,8 +381,11 @@ if (err != 0) ifc_link_ifp(ifc, ifp); - else if (ifc->ifc_flags & IFC_F_AUTOUNIT) - ifc_free_unit(ifc, unit); + else if (ifc->ifc_flags & IFC_F_AUTOUNIT) { + if (unit != IF_DUNIT_NONE) + free_unr(ifc->ifc_unrhdr, unit); + IF_CLONE_REMREF(ifc); + } CURVNET_RESTORE(); return (err); } @@ -517,7 +520,9 @@ static int ifc_simple_destroy_wrapper(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags) { - if (ifp->if_dunit < ifc->ifcs_minifs && (flags & IFC_F_FORCE) == 0) + if (ifp->if_dunit != IF_DUNIT_NONE && + ifp->if_dunit < ifc->ifcs_minifs && + (flags & IFC_F_FORCE) == 0) return (EINVAL); ifc->ifcs_destroy(ifp); @@ -781,6 +786,7 @@ ifc_free_unit(struct if_clone *ifc, int unit) { + KASSERT(unit != IF_DUNIT_NONE, ("Invalid unit")); free_unr(ifc->ifc_unrhdr, unit); IF_CLONE_REMREF(ifc); } @@ -872,3 +878,86 @@ { return (ifc->ifc_flags); } + +/* + * Reassign unit number from new name. + */ +int +ifc_reassign_unit(struct ifnet *ifp, char *new_name) +{ + struct if_clone *ifc, *nifc; + + KASSERT(new_name[0] != '\0', ("Invalid interface name")); + + if (ifp->if_vnet != ifp->if_home_vnet) + return (0); + /* XXX lock cloners ??? */ + ifc = ifc_find_cloner_in_vnet(ifp->if_dname, ifp->if_home_vnet); + /* Physical interfaces do not have cloner */ + if (ifc == NULL || (ifc->ifc_flags & IFC_F_AUTOUNIT) == 0) + return (0); + + CURVNET_SET_QUIET(ifp->if_home_vnet); + nifc = ifc_find_cloner_match(new_name); + CURVNET_RESTORE(); + if (nifc == ifc) { + int unit = -1; + ifc_name2unit(new_name, &unit); + if (unit >= 0) { + if (unit > ifc->ifc_maxunit) + return (ENOSPC); + if (alloc_unr_specific(ifc->ifc_unrhdr, unit) == -1) + return (EEXIST); + if (ifp->if_dunit != IF_DUNIT_NONE) + free_unr(ifc->ifc_unrhdr, ifp->if_dunit); + /* FIXME need lock ??? */ + ifp->if_dunit = unit; + return (0); + } + } + if (ifp->if_dunit != IF_DUNIT_NONE) { + free_unr(ifc->ifc_unrhdr, ifp->if_dunit); + /* FIXME need lock ??? */ + ifp->if_dunit = IF_DUNIT_NONE; + } + return (0); +} + +void +ifc_reassign_unit_vnet(struct ifnet *ifp, struct vnet *new_vnet) +{ + struct if_clone *ifc; + KASSERT(ifp->if_vnet != new_vnet, ("Require different vnet")); + + ifc = ifc_find_cloner_in_vnet(ifp->if_dname, ifp->if_home_vnet); + if (ifc == NULL || (ifc->ifc_flags & IFC_F_AUTOUNIT) == 0) + return; + + if (new_vnet != ifp->if_home_vnet) { + if (ifp->if_vnet == ifp->if_home_vnet && + ifp->if_dunit != IF_DUNIT_NONE) { + free_unr(ifc->ifc_unrhdr, ifp->if_dunit); + /* FIXME need lock ??? */ + ifp->if_dunit = IF_DUNIT_NONE; + } + } else { + KASSERT(ifp->if_dunit == IF_DUNIT_NONE, ("Unit number is not released")); + + struct if_clone *nifc; + CURVNET_SET_QUIET(ifp->if_home_vnet); + nifc = ifc_find_cloner_match(ifp->if_xname); + CURVNET_RESTORE(); + if (nifc != ifc) + return; + + int unit = -1; + ifc_name2unit(ifp->if_xname, &unit); + if (unit < 0 || unit > ifc->ifc_maxunit) + return; + if (alloc_unr_specific(ifc->ifc_unrhdr, unit) == -1) + return; + + /* FIXME need lock ??? */ + ifp->if_dunit = unit; + } +}