Index: sys/dev/hyperv/netvsc/if_hn.c =================================================================== --- sys/dev/hyperv/netvsc/if_hn.c +++ sys/dev/hyperv/netvsc/if_hn.c @@ -69,6 +69,8 @@ #include #include #include +#include +#include #include #include #include @@ -119,6 +121,8 @@ #define HN_RING_CNT_DEF_MAX 8 +#define HN_VFMAP_SIZE_DEF 8 + /* YYY should get it from the underlying channel */ #define HN_TX_DESC_CNT 512 @@ -255,6 +259,11 @@ static void hn_ifmedia_sts(struct ifnet *, struct ifmediareq *); +static void hn_ifnet_event(void *, struct ifnet *, int); +static void hn_ifaddr_event(void *, struct ifnet *); +static void hn_ifnet_attevent(void *, struct ifnet *); +static void hn_ifnet_detevent(void *, struct ifnet *); + static int hn_rndis_rxinfo(const void *, int, struct hn_rxinfo *); static void hn_rndis_rx_data(struct hn_rx_ring *, @@ -303,6 +312,9 @@ static int hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS); static int hn_polling_sysctl(SYSCTL_HANDLER_ARGS); static int hn_vf_sysctl(SYSCTL_HANDLER_ARGS); +static int hn_rxvf_sysctl(SYSCTL_HANDLER_ARGS); +static int hn_vflist_sysctl(SYSCTL_HANDLER_ARGS); +static int hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS); static void hn_stop(struct hn_softc *, bool); static void hn_init_locked(struct hn_softc *); @@ -502,9 +514,21 @@ SYSCTL_INT(_hw_hn, OID_AUTO, tx_agg_pkts, CTLFLAG_RDTUN, &hn_tx_agg_pkts, 0, "Packet transmission aggregation packet limit"); +/* VF list */ +SYSCTL_PROC(_hw_hn, OID_AUTO, vflist, CTLFLAG_RD | CTLTYPE_STRING, + 0, 0, hn_vflist_sysctl, "A", "VF list"); + +/* VF mapping */ +SYSCTL_PROC(_hw_hn, OID_AUTO, vfmap, CTLFLAG_RD | CTLTYPE_STRING, + 0, 0, hn_vfmap_sysctl, "A", "VF mapping"); + static u_int hn_cpu_index; /* next CPU for channel */ static struct taskqueue **hn_tx_taskque;/* shared TX taskqueues */ +static struct rmlock hn_vfmap_lock; +static int hn_vfmap_size; +static struct ifnet **hn_vfmap; + #ifndef RSS static const uint8_t hn_rss_key_default[NDIS_HASH_KEYSIZE_TOEPLITZ] = { @@ -971,7 +995,7 @@ { struct hn_update_vf *uv = arg; - uv->rxr->hn_vf = uv->vf; + uv->rxr->hn_rxvf_ifp = uv->vf; } static void @@ -994,37 +1018,50 @@ uv.vf = vf; vmbus_chan_run_task(rxr->hn_chan, &task); } else { - rxr->hn_vf = vf; + rxr->hn_rxvf_ifp = vf; } } } -static void -hn_set_vf(struct hn_softc *sc, struct ifnet *ifp, bool vf) +static __inline bool +hn_ismyvf(const struct hn_softc *sc, const struct ifnet *ifp) { - struct ifnet *hn_ifp; - - HN_LOCK(sc); - - if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED)) - goto out; + const struct ifnet *hn_ifp; hn_ifp = sc->hn_ifp; if (ifp == hn_ifp) - goto out; + return (false); if (ifp->if_alloctype != IFT_ETHER) - goto out; + return (false); /* Ignore lagg/vlan interfaces */ if (strcmp(ifp->if_dname, "lagg") == 0 || strcmp(ifp->if_dname, "vlan") == 0) - goto out; + return (false); if (bcmp(IF_LLADDR(ifp), IF_LLADDR(hn_ifp), ETHER_ADDR_LEN) != 0) + return (false); + + return (true); +} + +static void +hn_set_vf(struct hn_softc *sc, struct ifnet *ifp, bool vf) +{ + struct ifnet *hn_ifp; + + HN_LOCK(sc); + + if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED)) + goto out; + + if (!hn_ismyvf(sc, ifp)) goto out; + hn_ifp = sc->hn_ifp; + /* Now we're sure 'ifp' is a real VF device. */ if (vf) { if (sc->hn_flags & HN_FLAG_VF) @@ -1037,7 +1074,7 @@ goto out; sc->hn_flags &= ~HN_FLAG_VF; - if (sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) + if (hn_ifp->if_drv_flags & IFF_DRV_RUNNING) hn_rxfilter_config(sc); else hn_set_rxfilter(sc, NDIS_PACKET_TYPE_NONE); @@ -1052,7 +1089,7 @@ hn_suspend_mgmt(sc); sc->hn_link_flags &= ~(HN_LINK_FLAG_LINKUP | HN_LINK_FLAG_NETCHG); - if_link_state_change(sc->hn_ifp, LINK_STATE_DOWN); + if_link_state_change(hn_ifp, LINK_STATE_DOWN); } else { hn_resume_mgmt(sc); } @@ -1082,6 +1119,85 @@ hn_set_vf(arg, ifp, ifp->if_flags & IFF_UP); } +static void +hn_ifnet_attevent(void *xsc, struct ifnet *ifp) +{ + struct hn_softc *sc = xsc; + + HN_LOCK(sc); + + if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED)) + goto done; + + if (!hn_ismyvf(sc, ifp)) + goto done; + + if (sc->hn_vf_ifp != NULL) { + if_printf(sc->hn_ifp, "%s was attached as VF\n", + sc->hn_vf_ifp->if_xname); + goto done; + } + + rm_wlock(&hn_vfmap_lock); + + if (ifp->if_index >= hn_vfmap_size) { + struct ifnet **newmap; + int newsize; + + newsize = ifp->if_index + HN_VFMAP_SIZE_DEF; + newmap = malloc(sizeof(struct ifnet *) * newsize, M_DEVBUF, + M_WAITOK | M_ZERO); + + memcpy(newmap, hn_vfmap, + sizeof(struct ifnet *) * hn_vfmap_size); + free(hn_vfmap, M_DEVBUF); + hn_vfmap = newmap; + hn_vfmap_size = newsize; + } + KASSERT(hn_vfmap[ifp->if_index] == NULL, + ("%s: ifindex %d was mapped to %s", + ifp->if_xname, ifp->if_index, hn_vfmap[ifp->if_index]->if_xname)); + hn_vfmap[ifp->if_index] = sc->hn_ifp; + + rm_wunlock(&hn_vfmap_lock); + + sc->hn_vf_ifp = ifp; +done: + HN_UNLOCK(sc); +} + +static void +hn_ifnet_detevent(void *xsc, struct ifnet *ifp) +{ + struct hn_softc *sc = xsc; + + HN_LOCK(sc); + + if (sc->hn_vf_ifp == NULL) + goto done; + + if (!hn_ismyvf(sc, ifp)) + goto done; + + sc->hn_vf_ifp = NULL; + + rm_wlock(&hn_vfmap_lock); + + KASSERT(ifp->if_index < hn_vfmap_size, + ("ifindex %d, vfmapsize %d", ifp->if_index, hn_vfmap_size)); + if (hn_vfmap[ifp->if_index] != NULL) { + KASSERT(hn_vfmap[ifp->if_index] == sc->hn_ifp, + ("%s: ifindex %d was mapped to %s", + ifp->if_xname, ifp->if_index, + hn_vfmap[ifp->if_index]->if_xname)); + hn_vfmap[ifp->if_index] = NULL; + } + + rm_wunlock(&hn_vfmap_lock); +done: + HN_UNLOCK(sc); +} + /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */ static const struct hyperv_guid g_net_vsc_device_type = { .hv_guid = {0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, @@ -1322,6 +1438,9 @@ SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, hn_vf_sysctl, "A", "Virtual Function's name"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxvf", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, + hn_rxvf_sysctl, "A", "activated Virtual Function's name"); /* * Setup the ifmedia, which has been initialized earlier. @@ -1412,10 +1531,14 @@ sc->hn_ifnet_evthand = EVENTHANDLER_REGISTER(ifnet_event, hn_ifnet_event, sc, EVENTHANDLER_PRI_ANY); - sc->hn_ifaddr_evthand = EVENTHANDLER_REGISTER(ifaddr_event, hn_ifaddr_event, sc, EVENTHANDLER_PRI_ANY); + sc->hn_ifnet_atthand = EVENTHANDLER_REGISTER(ether_ifattach_event, + hn_ifnet_attevent, sc, EVENTHANDLER_PRI_ANY); + sc->hn_ifnet_dethand = EVENTHANDLER_REGISTER(ifnet_departure_event, + hn_ifnet_detevent, sc, EVENTHANDLER_PRI_ANY); + return (0); failed: if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) @@ -1428,12 +1551,25 @@ hn_detach(device_t dev) { struct hn_softc *sc = device_get_softc(dev); - struct ifnet *ifp = sc->hn_ifp; + struct ifnet *ifp = sc->hn_ifp, *hn_ifp; if (sc->hn_ifaddr_evthand != NULL) EVENTHANDLER_DEREGISTER(ifaddr_event, sc->hn_ifaddr_evthand); if (sc->hn_ifnet_evthand != NULL) EVENTHANDLER_DEREGISTER(ifnet_event, sc->hn_ifnet_evthand); + if (sc->hn_ifnet_atthand != NULL) { + EVENTHANDLER_DEREGISTER(ifnet_arrival_event, + sc->hn_ifnet_atthand); + } + if (sc->hn_ifnet_dethand != NULL) { + EVENTHANDLER_DEREGISTER(ifnet_departure_event, + sc->hn_ifnet_dethand); + } + + hn_ifp = sc->hn_vf_ifp; + __compiler_membar(); + if (hn_ifp != NULL) + hn_ifnet_detevent(sc, hn_ifp); if (sc->hn_xact != NULL && vmbus_chan_is_revoked(sc->hn_prichan)) { /* @@ -2326,7 +2462,7 @@ int hash_type; /* If the VF is active, inject the packet through the VF */ - ifp = rxr->hn_vf ? rxr->hn_vf : rxr->hn_ifp; + ifp = rxr->hn_rxvf_ifp ? rxr->hn_rxvf_ifp : rxr->hn_ifp; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { /* @@ -3301,12 +3437,28 @@ hn_vf_sysctl(SYSCTL_HANDLER_ARGS) { struct hn_softc *sc = arg1; - char vf_name[128]; + char vf_name[IFNAMSIZ + 1]; + struct ifnet *vf; + + HN_LOCK(sc); + vf_name[0] = '\0'; + vf = sc->hn_vf_ifp; + if (vf != NULL) + snprintf(vf_name, sizeof(vf_name), "%s", if_name(vf)); + HN_UNLOCK(sc); + return sysctl_handle_string(oidp, vf_name, sizeof(vf_name), req); +} + +static int +hn_rxvf_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct hn_softc *sc = arg1; + char vf_name[IFNAMSIZ + 1]; struct ifnet *vf; HN_LOCK(sc); vf_name[0] = '\0'; - vf = sc->hn_rx_ring[0].hn_vf; + vf = sc->hn_rx_ring[0].hn_rxvf_ifp; if (vf != NULL) snprintf(vf_name, sizeof(vf_name), "%s", if_name(vf)); HN_UNLOCK(sc); @@ -3314,6 +3466,94 @@ } static int +hn_vflist_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct rm_priotracker pt; + struct sbuf *sb; + int error, i; + bool first; + + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) + return (error); + + sb = sbuf_new_for_sysctl(NULL, NULL, 128, req); + if (sb == NULL) + return (ENOMEM); + + rm_rlock(&hn_vfmap_lock, &pt); + + first = true; + for (i = 0; i < hn_vfmap_size; ++i) { + struct ifnet *ifp; + + if (hn_vfmap[i] == NULL) + continue; + + ifp = ifnet_byindex(i); + if (ifp != NULL) { + if (first) + sbuf_printf(sb, "%s", ifp->if_xname); + else + sbuf_printf(sb, " %s", ifp->if_xname); + first = false; + } + } + + rm_runlock(&hn_vfmap_lock, &pt); + + error = sbuf_finish(sb); + sbuf_delete(sb); + return (error); +} + +static int +hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct rm_priotracker pt; + struct sbuf *sb; + int error, i; + bool first; + + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) + return (error); + + sb = sbuf_new_for_sysctl(NULL, NULL, 128, req); + if (sb == NULL) + return (ENOMEM); + + rm_rlock(&hn_vfmap_lock, &pt); + + first = true; + for (i = 0; i < hn_vfmap_size; ++i) { + struct ifnet *ifp, *hn_ifp; + + hn_ifp = hn_vfmap[i]; + if (hn_ifp == NULL) + continue; + + ifp = ifnet_byindex(i); + if (ifp != NULL) { + if (first) { + sbuf_printf(sb, "%s:%s", ifp->if_xname, + hn_ifp->if_xname); + } else { + sbuf_printf(sb, " %s:%s", ifp->if_xname, + hn_ifp->if_xname); + } + first = false; + } + } + + rm_runlock(&hn_vfmap_lock, &pt); + + error = sbuf_finish(sb); + sbuf_delete(sb); + return (error); +} + +static int hn_check_iplen(const struct mbuf *m, int hoff) { const struct ip *ip; @@ -5829,11 +6069,19 @@ } static void -hn_tx_taskq_create(void *arg __unused) +hn_sysinit(void *arg __unused) { int i; /* + * Initialize VF map. + */ + rm_init_flags(&hn_vfmap_lock, "hn_vfmap", RM_SLEEPABLE); + hn_vfmap_size = HN_VFMAP_SIZE_DEF; + hn_vfmap = malloc(sizeof(struct ifnet *) * hn_vfmap_size, M_DEVBUF, + M_WAITOK | M_ZERO); + + /* * Fix the # of TX taskqueues. */ if (hn_tx_taskq_cnt <= 0) @@ -5869,11 +6117,10 @@ "hn tx%d", i); } } -SYSINIT(hn_txtq_create, SI_SUB_DRIVERS, SI_ORDER_SECOND, - hn_tx_taskq_create, NULL); +SYSINIT(hn_sysinit, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_sysinit, NULL); static void -hn_tx_taskq_destroy(void *arg __unused) +hn_sysuninit(void *arg __unused) { if (hn_tx_taskque != NULL) { @@ -5883,6 +6130,9 @@ taskqueue_free(hn_tx_taskque[i]); free(hn_tx_taskque, M_DEVBUF); } + + if (hn_vfmap != NULL) + free(hn_vfmap, M_DEVBUF); + rm_destroy(&hn_vfmap_lock); } -SYSUNINIT(hn_txtq_destroy, SI_SUB_DRIVERS, SI_ORDER_SECOND, - hn_tx_taskq_destroy, NULL); +SYSUNINIT(hn_sysuninit, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_sysuninit, NULL); Index: sys/dev/hyperv/netvsc/if_hnvar.h =================================================================== --- sys/dev/hyperv/netvsc/if_hnvar.h +++ sys/dev/hyperv/netvsc/if_hnvar.h @@ -59,7 +59,7 @@ struct hn_rx_ring { struct ifnet *hn_ifp; - struct ifnet *hn_vf; /* SR-IOV VF */ + struct ifnet *hn_rxvf_ifp; /* SR-IOV VF for RX */ struct hn_tx_ring *hn_txr; void *hn_pktbuf; int hn_pktbuf_len; @@ -174,6 +174,7 @@ */ struct hn_softc { struct ifnet *hn_ifp; + struct ifnet *hn_vf_ifp; /* SR-IOV VF */ struct ifmedia hn_media; device_t hn_dev; int hn_if_flags; @@ -238,6 +239,8 @@ eventhandler_tag hn_ifaddr_evthand; eventhandler_tag hn_ifnet_evthand; + eventhandler_tag hn_ifnet_atthand; + eventhandler_tag hn_ifnet_dethand; }; #define HN_FLAG_RXBUF_CONNECTED 0x0001