- /* generate a link event based on the state of the underlying interface */
- bzero(&ifmr, sizeof(ifmr));
- snprintf(ifmr.ifm_name, sizeof(ifmr.ifm_name),
- "%s%d", p->if_name, p->if_unit);
- if ((*p->if_ioctl)(p, SIOCGIFMEDIA, (caddr_t)&ifmr) == 0
- && ifmr.ifm_count > 0 && ifmr.ifm_status & IFM_AVALID) {
- u_long event;
-
- event = (ifmr.ifm_status & IFM_ACTIVE)
- ? KEV_DL_LINK_ON : KEV_DL_LINK_OFF;
- interface_link_event(ifp, event);
+ /* generate a link event based on the state of the underlying interface */
+ bzero(&ifmr, sizeof(ifmr));
+ snprintf(ifmr.ifm_name, sizeof(ifmr.ifm_name),
+ "%s%d", ifnet_name(p), ifnet_unit(p));
+ if (ifnet_ioctl(p, 0, SIOCGIFMEDIA, &ifmr) == 0
+ && ifmr.ifm_count > 0 && ifmr.ifm_status & IFM_AVALID) {
+ u_int32_t event;
+
+ event = (ifmr.ifm_status & IFM_ACTIVE)
+ ? KEV_DL_LINK_ON : KEV_DL_LINK_OFF;
+ interface_link_event(ifp, event);
+ }
+ return;
+}
+
+static int
+vlan_unconfig(ifvlan_ref ifv, int need_to_wait)
+{
+ struct ifnet * ifp = ifv->ifv_ifp;
+ int last_vlan = FALSE;
+ int need_ifv_release = 0;
+ int need_vlp_release = 0;
+ struct ifnet * p;
+ vlan_parent_ref vlp;
+
+ vlan_assert_lock_held();
+ vlp = ifv->ifv_vlp;
+ if (vlp == NULL) {
+ return (0);
+ }
+ if (need_to_wait) {
+ need_vlp_release++;
+ vlan_parent_retain(vlp);
+ vlan_parent_wait(vlp, "vlan_unconfig");
+
+ /* check again because another thread could be in vlan_unconfig */
+ if (ifv != ifnet_get_ifvlan(ifp)) {
+ goto signal_done;
+ }
+ if (ifv->ifv_vlp != vlp) {
+ /* vlan parent changed */
+ goto signal_done;
+ }
+ }
+
+ /* ifv has a reference on vlp, need to remove it */
+ need_vlp_release++;
+ p = vlp->vlp_ifp;
+
+ /* remember whether we're the last VLAN on the parent */
+ if (LIST_NEXT(LIST_FIRST(&vlp->vlp_vlan_list), ifv_vlan_list) == NULL) {
+ if (g_vlan->verbose) {
+ printf("vlan_unconfig: last vlan on %s%d\n",
+ ifnet_name(p), ifnet_unit(p));
+ }
+ last_vlan = TRUE;
+ }
+
+ /* back-out any effect our mtu might have had on the parent */
+ (void)ifvlan_new_mtu(ifv, ETHERMTU - ifv->ifv_mtufudge);
+
+ vlan_unlock();
+
+ /* un-join multicast on parent interface */
+ (void)multicast_list_remove(&ifv->ifv_multicast);
+
+ /* Clear our MAC address. */
+ ifnet_set_lladdr_and_type(ifp, NULL, 0, IFT_L2VLAN);
+
+ /* detach VLAN "protocol" */
+ if (last_vlan) {
+ (void)vlan_detach_protocol(p);
+ }
+
+ vlan_lock();
+
+ /* return to the state we were in before SIFVLAN */
+ ifnet_set_mtu(ifp, 0);
+ ifnet_set_flags(ifp, 0,
+ IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_RUNNING);
+ ifnet_set_offload(ifp, 0);
+ ifv->ifv_mtufudge = 0;
+
+ /* Disconnect from parent. */
+ vlan_parent_remove_vlan(vlp, ifv);
+ ifv->ifv_flags = 0;
+
+ /* vlan_parent has reference to ifv, remove it */
+ need_ifv_release++;
+
+ /* from this point on, no more referencing ifv */
+ if (last_vlan && !vlan_parent_flags_detaching(vlp)) {
+ /* the vlan parent has no more VLAN's */
+ ifnet_set_eflags(p, 0, IFEF_VLAN);
+ LIST_REMOVE(vlp, vlp_parent_list);
+
+ /* one for being in the list */
+ need_vlp_release++;
+
+ /* release outside of the lock below */
+ need_vlp_release++;
+ }
+
+ signal_done:
+ if (need_to_wait) {
+ vlan_parent_signal(vlp, "vlan_unconfig");