Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/hyperv/netvsc/if_hn.c
Show First 20 Lines • Show All 297 Lines • ▼ Show 20 Lines | static int hn_synth_alloc_subchans(struct hn_softc *, | ||||
int *); | int *); | ||||
static void hn_suspend(struct hn_softc *); | static void hn_suspend(struct hn_softc *); | ||||
static void hn_suspend_data(struct hn_softc *); | static void hn_suspend_data(struct hn_softc *); | ||||
static void hn_suspend_mgmt(struct hn_softc *); | static void hn_suspend_mgmt(struct hn_softc *); | ||||
static void hn_resume(struct hn_softc *); | static void hn_resume(struct hn_softc *); | ||||
static void hn_resume_data(struct hn_softc *); | static void hn_resume_data(struct hn_softc *); | ||||
static void hn_resume_mgmt(struct hn_softc *); | static void hn_resume_mgmt(struct hn_softc *); | ||||
static void hn_suspend_mgmt_taskfunc(void *, int); | static void hn_suspend_mgmt_taskfunc(void *, int); | ||||
static void hn_chan_drain(struct vmbus_channel *); | static void hn_chan_drain(struct hn_softc *, | ||||
struct vmbus_channel *); | |||||
static void hn_update_link_status(struct hn_softc *); | static void hn_update_link_status(struct hn_softc *); | ||||
static void hn_change_network(struct hn_softc *); | static void hn_change_network(struct hn_softc *); | ||||
static void hn_link_taskfunc(void *, int); | static void hn_link_taskfunc(void *, int); | ||||
static void hn_netchg_init_taskfunc(void *, int); | static void hn_netchg_init_taskfunc(void *, int); | ||||
static void hn_netchg_status_taskfunc(void *, int); | static void hn_netchg_status_taskfunc(void *, int); | ||||
static void hn_link_status(struct hn_softc *); | static void hn_link_status(struct hn_softc *); | ||||
static int hn_create_rx_data(struct hn_softc *, int); | static int hn_create_rx_data(struct hn_softc *, int); | ||||
static void hn_destroy_rx_data(struct hn_softc *); | static void hn_destroy_rx_data(struct hn_softc *); | ||||
static int hn_check_iplen(const struct mbuf *, int); | static int hn_check_iplen(const struct mbuf *, int); | ||||
static int hn_set_rxfilter(struct hn_softc *); | static int hn_set_rxfilter(struct hn_softc *); | ||||
static int hn_rss_reconfig(struct hn_softc *); | static int hn_rss_reconfig(struct hn_softc *); | ||||
static void hn_rss_ind_fixup(struct hn_softc *, int); | static void hn_rss_ind_fixup(struct hn_softc *, int); | ||||
static int hn_rxpkt(struct hn_rx_ring *, const void *, | static int hn_rxpkt(struct hn_rx_ring *, const void *, | ||||
int, const struct hn_rxinfo *); | int, const struct hn_rxinfo *); | ||||
static int hn_tx_ring_create(struct hn_softc *, int); | static int hn_tx_ring_create(struct hn_softc *, int); | ||||
static void hn_tx_ring_destroy(struct hn_tx_ring *); | static void hn_tx_ring_destroy(struct hn_tx_ring *); | ||||
static int hn_create_tx_data(struct hn_softc *, int); | static int hn_create_tx_data(struct hn_softc *, int); | ||||
static void hn_fixup_tx_data(struct hn_softc *); | static void hn_fixup_tx_data(struct hn_softc *); | ||||
static void hn_destroy_tx_data(struct hn_softc *); | static void hn_destroy_tx_data(struct hn_softc *); | ||||
static void hn_txdesc_dmamap_destroy(struct hn_txdesc *); | static void hn_txdesc_dmamap_destroy(struct hn_txdesc *); | ||||
static void hn_txdesc_gc(struct hn_tx_ring *, | |||||
struct hn_txdesc *); | |||||
static int hn_encap(struct ifnet *, struct hn_tx_ring *, | static int hn_encap(struct ifnet *, struct hn_tx_ring *, | ||||
struct hn_txdesc *, struct mbuf **); | struct hn_txdesc *, struct mbuf **); | ||||
static int hn_txpkt(struct ifnet *, struct hn_tx_ring *, | static int hn_txpkt(struct ifnet *, struct hn_tx_ring *, | ||||
struct hn_txdesc *); | struct hn_txdesc *); | ||||
static void hn_set_chim_size(struct hn_softc *, int); | static void hn_set_chim_size(struct hn_softc *, int); | ||||
static void hn_set_tso_maxsize(struct hn_softc *, int, int); | static void hn_set_tso_maxsize(struct hn_softc *, int, int); | ||||
static bool hn_tx_ring_pending(struct hn_tx_ring *); | static bool hn_tx_ring_pending(struct hn_tx_ring *); | ||||
static void hn_tx_ring_qflush(struct hn_tx_ring *); | static void hn_tx_ring_qflush(struct hn_tx_ring *); | ||||
▲ Show 20 Lines • Show All 651 Lines • ▼ Show 20 Lines | #endif | ||||
if (error) | if (error) | ||||
goto failed; | goto failed; | ||||
/* | /* | ||||
* Create transaction context for NVS and RNDIS transactions. | * Create transaction context for NVS and RNDIS transactions. | ||||
*/ | */ | ||||
sc->hn_xact = vmbus_xact_ctx_create(bus_get_dma_tag(dev), | sc->hn_xact = vmbus_xact_ctx_create(bus_get_dma_tag(dev), | ||||
HN_XACT_REQ_SIZE, HN_XACT_RESP_SIZE, 0); | HN_XACT_REQ_SIZE, HN_XACT_RESP_SIZE, 0); | ||||
if (sc->hn_xact == NULL) | if (sc->hn_xact == NULL) { | ||||
error = ENXIO; | |||||
goto failed; | goto failed; | ||||
} | |||||
/* | /* | ||||
* Install orphan handler for the revocation of this device's | |||||
* primary channel. | |||||
* | |||||
* NOTE: | |||||
* The processing order is critical here: | |||||
* Install the orphan handler, _before_ testing whether this | |||||
* device's primary channel has been revoked or not. | |||||
*/ | |||||
vmbus_chan_set_orphan(sc->hn_prichan, sc->hn_xact); | |||||
if (vmbus_chan_is_revoked(sc->hn_prichan)) { | |||||
error = ENXIO; | |||||
goto failed; | |||||
} | |||||
/* | |||||
* Attach the synthetic parts, i.e. NVS and RNDIS. | * Attach the synthetic parts, i.e. NVS and RNDIS. | ||||
*/ | */ | ||||
error = hn_synth_attach(sc, ETHERMTU); | error = hn_synth_attach(sc, ETHERMTU); | ||||
if (error) | if (error) | ||||
goto failed; | goto failed; | ||||
error = hn_rndis_get_eaddr(sc, eaddr); | error = hn_rndis_get_eaddr(sc, eaddr); | ||||
if (error) | if (error) | ||||
▲ Show 20 Lines • Show All 156 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static int | static int | ||||
hn_detach(device_t dev) | hn_detach(device_t dev) | ||||
{ | { | ||||
struct hn_softc *sc = device_get_softc(dev); | struct hn_softc *sc = device_get_softc(dev); | ||||
struct ifnet *ifp = sc->hn_ifp; | struct ifnet *ifp = sc->hn_ifp; | ||||
if (sc->hn_xact != NULL && vmbus_chan_is_revoked(sc->hn_prichan)) { | |||||
/* | |||||
* In case that the vmbus missed the orphan handler | |||||
* installation. | |||||
*/ | |||||
vmbus_xact_ctx_orphan(sc->hn_xact); | |||||
} | |||||
if (device_is_attached(dev)) { | if (device_is_attached(dev)) { | ||||
HN_LOCK(sc); | HN_LOCK(sc); | ||||
if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) { | if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) { | ||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) | if (ifp->if_drv_flags & IFF_DRV_RUNNING) | ||||
hn_stop(sc); | hn_stop(sc); | ||||
/* | /* | ||||
* NOTE: | * NOTE: | ||||
* hn_stop() only suspends data, so managment | * hn_stop() only suspends data, so managment | ||||
Show All 9 Lines | hn_detach(device_t dev) | ||||
ifmedia_removeall(&sc->hn_media); | ifmedia_removeall(&sc->hn_media); | ||||
hn_destroy_rx_data(sc); | hn_destroy_rx_data(sc); | ||||
hn_destroy_tx_data(sc); | hn_destroy_tx_data(sc); | ||||
if (sc->hn_tx_taskq != hn_tx_taskq) | if (sc->hn_tx_taskq != hn_tx_taskq) | ||||
taskqueue_free(sc->hn_tx_taskq); | taskqueue_free(sc->hn_tx_taskq); | ||||
taskqueue_free(sc->hn_mgmt_taskq0); | taskqueue_free(sc->hn_mgmt_taskq0); | ||||
if (sc->hn_xact != NULL) | if (sc->hn_xact != NULL) { | ||||
/* | |||||
* Uninstall the orphan handler _before_ the xact is | |||||
* destructed. | |||||
*/ | |||||
vmbus_chan_unset_orphan(sc->hn_prichan); | |||||
vmbus_xact_ctx_destroy(sc->hn_xact); | vmbus_xact_ctx_destroy(sc->hn_xact); | ||||
} | |||||
if_free(ifp); | if_free(ifp); | ||||
HN_LOCK_DESTROY(sc); | HN_LOCK_DESTROY(sc); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 222 Lines • ▼ Show 20 Lines | #endif | ||||
return txd; | return txd; | ||||
} | } | ||||
static __inline void | static __inline void | ||||
hn_txdesc_hold(struct hn_txdesc *txd) | hn_txdesc_hold(struct hn_txdesc *txd) | ||||
{ | { | ||||
/* 0->1 transition will never work */ | /* 0->1 transition will never work */ | ||||
KASSERT(txd->refs > 0, ("invalid refs %d", txd->refs)); | KASSERT(txd->refs > 0, ("invalid txd refs %d", txd->refs)); | ||||
atomic_add_int(&txd->refs, 1); | atomic_add_int(&txd->refs, 1); | ||||
} | } | ||||
static __inline void | static __inline void | ||||
hn_txdesc_agg(struct hn_txdesc *agg_txd, struct hn_txdesc *txd) | hn_txdesc_agg(struct hn_txdesc *agg_txd, struct hn_txdesc *txd) | ||||
{ | { | ||||
KASSERT((agg_txd->flags & HN_TXD_FLAG_ONAGG) == 0, | KASSERT((agg_txd->flags & HN_TXD_FLAG_ONAGG) == 0, | ||||
▲ Show 20 Lines • Show All 1,997 Lines • ▼ Show 20 Lines | hn_txdesc_dmamap_destroy(struct hn_txdesc *txd) | ||||
bus_dmamap_unload(txr->hn_tx_rndis_dtag, txd->rndis_pkt_dmap); | bus_dmamap_unload(txr->hn_tx_rndis_dtag, txd->rndis_pkt_dmap); | ||||
bus_dmamem_free(txr->hn_tx_rndis_dtag, txd->rndis_pkt, | bus_dmamem_free(txr->hn_tx_rndis_dtag, txd->rndis_pkt, | ||||
txd->rndis_pkt_dmap); | txd->rndis_pkt_dmap); | ||||
bus_dmamap_destroy(txr->hn_tx_data_dtag, txd->data_dmap); | bus_dmamap_destroy(txr->hn_tx_data_dtag, txd->data_dmap); | ||||
} | } | ||||
static void | static void | ||||
hn_txdesc_gc(struct hn_tx_ring *txr, struct hn_txdesc *txd) | |||||
{ | |||||
KASSERT(txd->refs == 0 || txd->refs == 1, | |||||
("invalid txd refs %d", txd->refs)); | |||||
/* Aggregated txds will be freed by their aggregating txd. */ | |||||
if (txd->refs > 0 && (txd->flags & HN_TXD_FLAG_ONAGG) == 0) { | |||||
int freed; | |||||
freed = hn_txdesc_put(txr, txd); | |||||
KASSERT(freed, ("can't free txdesc")); | |||||
} | |||||
} | |||||
static void | |||||
hn_tx_ring_destroy(struct hn_tx_ring *txr) | hn_tx_ring_destroy(struct hn_tx_ring *txr) | ||||
{ | { | ||||
struct hn_txdesc *txd; | int i; | ||||
if (txr->hn_txdesc == NULL) | if (txr->hn_txdesc == NULL) | ||||
return; | return; | ||||
#ifndef HN_USE_TXDESC_BUFRING | /* | ||||
while ((txd = SLIST_FIRST(&txr->hn_txlist)) != NULL) { | * NOTE: | ||||
SLIST_REMOVE_HEAD(&txr->hn_txlist, link); | * Because the freeing of aggregated txds will be deferred | ||||
hn_txdesc_dmamap_destroy(txd); | * to the aggregating txd, two passes are used here: | ||||
} | * - The first pass GCes any pending txds. This GC is necessary, | ||||
#else | * since if the channels are revoked, hypervisor will not | ||||
mtx_lock(&txr->hn_tx_lock); | * deliver send-done for all pending txds. | ||||
while ((txd = buf_ring_dequeue_sc(txr->hn_txdesc_br)) != NULL) | * - The second pass frees the busdma stuffs, i.e. after all txds | ||||
hn_txdesc_dmamap_destroy(txd); | * were freed. | ||||
mtx_unlock(&txr->hn_tx_lock); | */ | ||||
#endif | for (i = 0; i < txr->hn_txdesc_cnt; ++i) | ||||
hn_txdesc_gc(txr, &txr->hn_txdesc[i]); | |||||
for (i = 0; i < txr->hn_txdesc_cnt; ++i) | |||||
hn_txdesc_dmamap_destroy(&txr->hn_txdesc[i]); | |||||
if (txr->hn_tx_data_dtag != NULL) | if (txr->hn_tx_data_dtag != NULL) | ||||
bus_dma_tag_destroy(txr->hn_tx_data_dtag); | bus_dma_tag_destroy(txr->hn_tx_data_dtag); | ||||
if (txr->hn_tx_rndis_dtag != NULL) | if (txr->hn_tx_rndis_dtag != NULL) | ||||
bus_dma_tag_destroy(txr->hn_tx_rndis_dtag); | bus_dma_tag_destroy(txr->hn_tx_rndis_dtag); | ||||
#ifdef HN_USE_TXDESC_BUFRING | #ifdef HN_USE_TXDESC_BUFRING | ||||
buf_ring_free(txr->hn_txdesc_br, M_DEVBUF); | buf_ring_free(txr->hn_txdesc_br, M_DEVBUF); | ||||
▲ Show 20 Lines • Show All 1,016 Lines • ▼ Show 20 Lines | hn_set_ring_inuse(struct hn_softc *sc, int ring_cnt) | ||||
if (bootverbose) { | if (bootverbose) { | ||||
if_printf(sc->hn_ifp, "%d TX ring, %d RX ring\n", | if_printf(sc->hn_ifp, "%d TX ring, %d RX ring\n", | ||||
sc->hn_tx_ring_inuse, sc->hn_rx_ring_inuse); | sc->hn_tx_ring_inuse, sc->hn_rx_ring_inuse); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
hn_chan_drain(struct vmbus_channel *chan) | hn_chan_drain(struct hn_softc *sc, struct vmbus_channel *chan) | ||||
{ | { | ||||
while (!vmbus_chan_rx_empty(chan) || !vmbus_chan_tx_empty(chan)) | /* | ||||
* NOTE: | |||||
* The TX bufring will not be drained by the hypervisor, | |||||
* if the primary channel is revoked. | |||||
*/ | |||||
while (!vmbus_chan_rx_empty(chan) || | |||||
(!vmbus_chan_is_revoked(sc->hn_prichan) && | |||||
!vmbus_chan_tx_empty(chan))) | |||||
pause("waitch", 1); | pause("waitch", 1); | ||||
vmbus_chan_intr_drain(chan); | vmbus_chan_intr_drain(chan); | ||||
} | } | ||||
static void | static void | ||||
hn_suspend_data(struct hn_softc *sc) | hn_suspend_data(struct hn_softc *sc) | ||||
{ | { | ||||
struct vmbus_channel **subch = NULL; | struct vmbus_channel **subch = NULL; | ||||
struct hn_tx_ring *txr; | |||||
int i, nsubch; | int i, nsubch; | ||||
HN_LOCK_ASSERT(sc); | HN_LOCK_ASSERT(sc); | ||||
/* | /* | ||||
* Suspend TX. | * Suspend TX. | ||||
*/ | */ | ||||
for (i = 0; i < sc->hn_tx_ring_inuse; ++i) { | for (i = 0; i < sc->hn_tx_ring_inuse; ++i) { | ||||
struct hn_tx_ring *txr = &sc->hn_tx_ring[i]; | txr = &sc->hn_tx_ring[i]; | ||||
mtx_lock(&txr->hn_tx_lock); | mtx_lock(&txr->hn_tx_lock); | ||||
txr->hn_suspended = 1; | txr->hn_suspended = 1; | ||||
mtx_unlock(&txr->hn_tx_lock); | mtx_unlock(&txr->hn_tx_lock); | ||||
/* No one is able send more packets now. */ | /* No one is able send more packets now. */ | ||||
/* Wait for all pending sends to finish. */ | /* | ||||
while (hn_tx_ring_pending(txr)) | * Wait for all pending sends to finish. | ||||
* | |||||
* NOTE: | |||||
* We will _not_ receive all pending send-done, if the | |||||
* primary channel is revoked. | |||||
*/ | |||||
while (hn_tx_ring_pending(txr) && | |||||
!vmbus_chan_is_revoked(sc->hn_prichan)) | |||||
pause("hnwtx", 1 /* 1 tick */); | pause("hnwtx", 1 /* 1 tick */); | ||||
taskqueue_drain(txr->hn_tx_taskq, &txr->hn_tx_task); | |||||
taskqueue_drain(txr->hn_tx_taskq, &txr->hn_txeof_task); | |||||
} | } | ||||
/* | /* | ||||
* Disable RX by clearing RX filter. | * Disable RX by clearing RX filter. | ||||
*/ | */ | ||||
sc->hn_rx_filter = NDIS_PACKET_TYPE_NONE; | sc->hn_rx_filter = NDIS_PACKET_TYPE_NONE; | ||||
hn_rndis_set_rxfilter(sc, sc->hn_rx_filter); | hn_rndis_set_rxfilter(sc, sc->hn_rx_filter); | ||||
/* | /* | ||||
* Give RNDIS enough time to flush all pending data packets. | * Give RNDIS enough time to flush all pending data packets. | ||||
*/ | */ | ||||
pause("waitrx", (200 * hz) / 1000); | pause("waitrx", (200 * hz) / 1000); | ||||
/* | /* | ||||
* Drain RX/TX bufrings and interrupts. | * Drain RX/TX bufrings and interrupts. | ||||
*/ | */ | ||||
nsubch = sc->hn_rx_ring_inuse - 1; | nsubch = sc->hn_rx_ring_inuse - 1; | ||||
if (nsubch > 0) | if (nsubch > 0) | ||||
subch = vmbus_subchan_get(sc->hn_prichan, nsubch); | subch = vmbus_subchan_get(sc->hn_prichan, nsubch); | ||||
if (subch != NULL) { | if (subch != NULL) { | ||||
for (i = 0; i < nsubch; ++i) | for (i = 0; i < nsubch; ++i) | ||||
hn_chan_drain(subch[i]); | hn_chan_drain(sc, subch[i]); | ||||
} | } | ||||
hn_chan_drain(sc->hn_prichan); | hn_chan_drain(sc, sc->hn_prichan); | ||||
if (subch != NULL) | if (subch != NULL) | ||||
vmbus_subchan_rel(subch, nsubch); | vmbus_subchan_rel(subch, nsubch); | ||||
/* | |||||
* Drain any pending TX tasks. | |||||
* | |||||
* NOTE: | |||||
* The above hn_chan_drain() can dispatch TX tasks, so the TX | |||||
* tasks will have to be drained _after_ the above hn_chan_drain() | |||||
* calls. | |||||
*/ | |||||
for (i = 0; i < sc->hn_tx_ring_inuse; ++i) { | |||||
txr = &sc->hn_tx_ring[i]; | |||||
taskqueue_drain(txr->hn_tx_taskq, &txr->hn_tx_task); | |||||
taskqueue_drain(txr->hn_tx_taskq, &txr->hn_txeof_task); | |||||
} | |||||
} | } | ||||
static void | static void | ||||
hn_suspend_mgmt_taskfunc(void *xsc, int pending __unused) | hn_suspend_mgmt_taskfunc(void *xsc, int pending __unused) | ||||
{ | { | ||||
((struct hn_softc *)xsc)->hn_mgmt_taskq = NULL; | ((struct hn_softc *)xsc)->hn_mgmt_taskq = NULL; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 671 Lines • Show Last 20 Lines |