hyperv/hn: Rework link status support.
This is the preamble for network device SR-IOV and NDIS_STATUS_NETWORK_CHANGE handling. MFC after: 1 week Sponsored by: Microsoft Differential Revision: https://reviews.freebsd.org/D8209
This commit is contained in:
parent
cca23e7c3c
commit
4afe249835
@ -233,6 +233,10 @@ struct hn_softc {
|
|||||||
struct vmbus_xact_ctx *hn_xact;
|
struct vmbus_xact_ctx *hn_xact;
|
||||||
uint32_t hn_nvs_ver;
|
uint32_t hn_nvs_ver;
|
||||||
|
|
||||||
|
struct taskqueue *hn_mgmt_taskq;
|
||||||
|
struct taskqueue *hn_mgmt_taskq0;
|
||||||
|
struct task hn_link_task;
|
||||||
|
|
||||||
uint32_t hn_caps; /* HN_CAP_ */
|
uint32_t hn_caps; /* HN_CAP_ */
|
||||||
uint32_t hn_flags; /* HN_FLAG_ */
|
uint32_t hn_flags; /* HN_FLAG_ */
|
||||||
void *hn_rxbuf;
|
void *hn_rxbuf;
|
||||||
@ -271,7 +275,6 @@ struct hn_softc {
|
|||||||
*/
|
*/
|
||||||
struct hn_send_ctx;
|
struct hn_send_ctx;
|
||||||
|
|
||||||
void netvsc_linkstatus_callback(struct hn_softc *sc, uint32_t status);
|
|
||||||
int hv_nv_on_send(struct vmbus_channel *chan, uint32_t rndis_mtype,
|
int hv_nv_on_send(struct vmbus_channel *chan, uint32_t rndis_mtype,
|
||||||
struct hn_send_ctx *sndc, struct vmbus_gpa *gpa, int gpa_cnt);
|
struct hn_send_ctx *sndc, struct vmbus_gpa *gpa, int gpa_cnt);
|
||||||
|
|
||||||
|
@ -334,6 +334,8 @@ 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_start_taskfunc(void *, int);
|
static void hn_start_taskfunc(void *, int);
|
||||||
static void hn_start_txeof_taskfunc(void *, int);
|
static void hn_start_txeof_taskfunc(void *, int);
|
||||||
|
static void hn_link_taskfunc(void *, int);
|
||||||
|
static void hn_suspend_mgmt_taskfunc(void *, int);
|
||||||
static int hn_encap(struct hn_tx_ring *, struct hn_txdesc *, struct mbuf **);
|
static int hn_encap(struct hn_tx_ring *, struct hn_txdesc *, struct mbuf **);
|
||||||
static int hn_create_rx_data(struct hn_softc *sc, int);
|
static int hn_create_rx_data(struct hn_softc *sc, int);
|
||||||
static void hn_destroy_rx_data(struct hn_softc *sc);
|
static void hn_destroy_rx_data(struct hn_softc *sc);
|
||||||
@ -349,6 +351,8 @@ static int hn_synth_attach(struct hn_softc *, int);
|
|||||||
static void hn_synth_detach(struct hn_softc *);
|
static void hn_synth_detach(struct hn_softc *);
|
||||||
static bool hn_tx_ring_pending(struct hn_tx_ring *);
|
static bool hn_tx_ring_pending(struct hn_tx_ring *);
|
||||||
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_mgmt(struct hn_softc *);
|
||||||
static void hn_resume(struct hn_softc *);
|
static void hn_resume(struct hn_softc *);
|
||||||
static void hn_rx_drain(struct vmbus_channel *);
|
static void hn_rx_drain(struct vmbus_channel *);
|
||||||
static void hn_tx_resume(struct hn_softc *, int);
|
static void hn_tx_resume(struct hn_softc *, int);
|
||||||
@ -518,7 +522,6 @@ netvsc_attach(device_t dev)
|
|||||||
struct sysctl_oid_list *child;
|
struct sysctl_oid_list *child;
|
||||||
struct sysctl_ctx_list *ctx;
|
struct sysctl_ctx_list *ctx;
|
||||||
uint8_t eaddr[ETHER_ADDR_LEN];
|
uint8_t eaddr[ETHER_ADDR_LEN];
|
||||||
uint32_t link_status;
|
|
||||||
struct ifnet *ifp = NULL;
|
struct ifnet *ifp = NULL;
|
||||||
int error, ring_cnt, tx_ring_cnt;
|
int error, ring_cnt, tx_ring_cnt;
|
||||||
|
|
||||||
@ -550,6 +553,15 @@ netvsc_attach(device_t dev)
|
|||||||
sc->hn_tx_taskq = hn_tx_taskq;
|
sc->hn_tx_taskq = hn_tx_taskq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup taskqueue for mangement tasks, e.g. link status.
|
||||||
|
*/
|
||||||
|
sc->hn_mgmt_taskq0 = taskqueue_create("hn_mgmt", M_WAITOK,
|
||||||
|
taskqueue_thread_enqueue, &sc->hn_mgmt_taskq0);
|
||||||
|
taskqueue_start_threads(&sc->hn_mgmt_taskq0, 1, PI_NET, "%s mgmt",
|
||||||
|
device_get_nameunit(dev));
|
||||||
|
TASK_INIT(&sc->hn_link_task, 0, hn_link_taskfunc, sc);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate ifnet and setup its name earlier, so that if_printf
|
* Allocate ifnet and setup its name earlier, so that if_printf
|
||||||
* can be used by functions, which will be called after
|
* can be used by functions, which will be called after
|
||||||
@ -621,12 +633,6 @@ netvsc_attach(device_t dev)
|
|||||||
if (error)
|
if (error)
|
||||||
goto failed;
|
goto failed;
|
||||||
|
|
||||||
error = hn_rndis_get_linkstatus(sc, &link_status);
|
|
||||||
if (error)
|
|
||||||
goto failed;
|
|
||||||
if (link_status == NDIS_MEDIA_STATE_CONNECTED)
|
|
||||||
sc->hn_carrier = 1;
|
|
||||||
|
|
||||||
error = hn_rndis_get_eaddr(sc, eaddr);
|
error = hn_rndis_get_eaddr(sc, eaddr);
|
||||||
if (error)
|
if (error)
|
||||||
goto failed;
|
goto failed;
|
||||||
@ -736,6 +742,12 @@ netvsc_attach(device_t dev)
|
|||||||
/* Inform the upper layer about the long frame support. */
|
/* Inform the upper layer about the long frame support. */
|
||||||
ifp->if_hdrlen = sizeof(struct ether_vlan_header);
|
ifp->if_hdrlen = sizeof(struct ether_vlan_header);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Kick off link status check.
|
||||||
|
*/
|
||||||
|
sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0;
|
||||||
|
hn_link_status_update(sc);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
failed:
|
failed:
|
||||||
if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED)
|
if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED)
|
||||||
@ -755,6 +767,12 @@ netvsc_detach(device_t dev)
|
|||||||
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:
|
||||||
|
* hn_stop() only suspends data, so managment
|
||||||
|
* stuffs have to be suspended manually here.
|
||||||
|
*/
|
||||||
|
hn_suspend_mgmt(sc);
|
||||||
hn_synth_detach(sc);
|
hn_synth_detach(sc);
|
||||||
}
|
}
|
||||||
HN_UNLOCK(sc);
|
HN_UNLOCK(sc);
|
||||||
@ -767,6 +785,7 @@ netvsc_detach(device_t dev)
|
|||||||
|
|
||||||
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);
|
||||||
|
|
||||||
if (sc->hn_xact != NULL)
|
if (sc->hn_xact != NULL)
|
||||||
vmbus_xact_ctx_destroy(sc->hn_xact);
|
vmbus_xact_ctx_destroy(sc->hn_xact);
|
||||||
@ -786,6 +805,36 @@ netvsc_shutdown(device_t dev)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hn_link_taskfunc(void *xsc, int pending __unused)
|
||||||
|
{
|
||||||
|
struct hn_softc *sc = xsc;
|
||||||
|
struct ifnet *ifp = sc->hn_ifp;
|
||||||
|
uint32_t link_status;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = hn_rndis_get_linkstatus(sc, &link_status);
|
||||||
|
if (error) {
|
||||||
|
/* XXX what to do? */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (link_status == NDIS_MEDIA_STATE_CONNECTED)
|
||||||
|
sc->hn_carrier = 1;
|
||||||
|
else
|
||||||
|
sc->hn_carrier = 0;
|
||||||
|
if_link_state_change(ifp,
|
||||||
|
sc->hn_carrier ? LINK_STATE_UP : LINK_STATE_DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hn_link_status_update(struct hn_softc *sc)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (sc->hn_mgmt_taskq != NULL)
|
||||||
|
taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_link_task);
|
||||||
|
}
|
||||||
|
|
||||||
static __inline int
|
static __inline int
|
||||||
hn_txdesc_dmamap_load(struct hn_tx_ring *txr, struct hn_txdesc *txd,
|
hn_txdesc_dmamap_load(struct hn_tx_ring *txr, struct hn_txdesc *txd,
|
||||||
struct mbuf **m_head, bus_dma_segment_t *segs, int *nsegs)
|
struct mbuf **m_head, bus_dma_segment_t *segs, int *nsegs)
|
||||||
@ -1310,19 +1359,6 @@ hn_start_locked(struct hn_tx_ring *txr, int len)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Link up/down notification
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
netvsc_linkstatus_callback(struct hn_softc *sc, uint32_t status)
|
|
||||||
{
|
|
||||||
if (status == 1) {
|
|
||||||
sc->hn_carrier = 1;
|
|
||||||
} else {
|
|
||||||
sc->hn_carrier = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Append the specified data to the indicated mbuf chain,
|
* Append the specified data to the indicated mbuf chain,
|
||||||
* Extend the mbuf chain if the new data does not fit in
|
* Extend the mbuf chain if the new data does not fit in
|
||||||
@ -1801,9 +1837,9 @@ hn_stop(struct hn_softc *sc)
|
|||||||
KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
|
KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
|
||||||
("synthetic parts were not attached"));
|
("synthetic parts were not attached"));
|
||||||
|
|
||||||
/* Clear RUNNING bit _before_ hn_suspend() */
|
/* Clear RUNNING bit _before_ hn_suspend_data() */
|
||||||
atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_RUNNING);
|
atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_RUNNING);
|
||||||
hn_suspend(sc);
|
hn_suspend_data(sc);
|
||||||
|
|
||||||
/* Clear OACTIVE bit. */
|
/* Clear OACTIVE bit. */
|
||||||
atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
|
atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
|
||||||
@ -1896,9 +1932,6 @@ hn_init_locked(struct hn_softc *sc)
|
|||||||
|
|
||||||
/* Everything is ready; unleash! */
|
/* Everything is ready; unleash! */
|
||||||
atomic_set_int(&ifp->if_drv_flags, IFF_DRV_RUNNING);
|
atomic_set_int(&ifp->if_drv_flags, IFF_DRV_RUNNING);
|
||||||
|
|
||||||
/* TODO: check RNDIS link status. */
|
|
||||||
if_link_state_change(ifp, LINK_STATE_UP);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -3600,7 +3633,7 @@ hn_rx_drain(struct vmbus_channel *chan)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
hn_suspend(struct hn_softc *sc)
|
hn_suspend_data(struct hn_softc *sc)
|
||||||
{
|
{
|
||||||
struct vmbus_channel **subch = NULL;
|
struct vmbus_channel **subch = NULL;
|
||||||
int i, nsubch;
|
int i, nsubch;
|
||||||
@ -3653,6 +3686,41 @@ hn_suspend(struct hn_softc *sc)
|
|||||||
vmbus_subchan_rel(subch, nsubch);
|
vmbus_subchan_rel(subch, nsubch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hn_suspend_mgmt_taskfunc(void *xsc, int pending __unused)
|
||||||
|
{
|
||||||
|
|
||||||
|
((struct hn_softc *)xsc)->hn_mgmt_taskq = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hn_suspend_mgmt(struct hn_softc *sc)
|
||||||
|
{
|
||||||
|
struct task task;
|
||||||
|
|
||||||
|
HN_LOCK_ASSERT(sc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure that hn_mgmt_taskq0 can nolonger be accessed
|
||||||
|
* through hn_mgmt_taskq.
|
||||||
|
*/
|
||||||
|
TASK_INIT(&task, 0, hn_suspend_mgmt_taskfunc, sc);
|
||||||
|
vmbus_chan_run_task(sc->hn_prichan, &task);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure that all pending management tasks are completed.
|
||||||
|
*/
|
||||||
|
taskqueue_drain_all(sc->hn_mgmt_taskq0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hn_suspend(struct hn_softc *sc)
|
||||||
|
{
|
||||||
|
|
||||||
|
hn_suspend_data(sc);
|
||||||
|
hn_suspend_mgmt(sc);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
hn_tx_resume(struct hn_softc *sc, int tx_ring_cnt)
|
hn_tx_resume(struct hn_softc *sc, int tx_ring_cnt)
|
||||||
{
|
{
|
||||||
@ -3685,7 +3753,8 @@ hn_resume(struct hn_softc *sc)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure to clear suspend status on "all" TX rings,
|
* Make sure to clear suspend status on "all" TX rings,
|
||||||
* since hn_tx_ring_inuse can be changed after hn_suspend().
|
* since hn_tx_ring_inuse can be changed after
|
||||||
|
* hn_suspend_data().
|
||||||
*/
|
*/
|
||||||
hn_tx_resume(sc, sc->hn_tx_ring_cnt);
|
hn_tx_resume(sc, sc->hn_tx_ring_cnt);
|
||||||
|
|
||||||
@ -3710,6 +3779,12 @@ hn_resume(struct hn_softc *sc)
|
|||||||
*/
|
*/
|
||||||
taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
|
taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Kick off link status check.
|
||||||
|
*/
|
||||||
|
sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0;
|
||||||
|
hn_link_status_update(sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -167,11 +167,8 @@ hv_rf_receive_indicate_status(struct hn_softc *sc, const void *data, int dlen)
|
|||||||
|
|
||||||
switch (msg->rm_status) {
|
switch (msg->rm_status) {
|
||||||
case RNDIS_STATUS_MEDIA_CONNECT:
|
case RNDIS_STATUS_MEDIA_CONNECT:
|
||||||
netvsc_linkstatus_callback(sc, 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RNDIS_STATUS_MEDIA_DISCONNECT:
|
case RNDIS_STATUS_MEDIA_DISCONNECT:
|
||||||
netvsc_linkstatus_callback(sc, 0);
|
hn_link_status_update(sc);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG:
|
case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG:
|
||||||
|
@ -138,6 +138,7 @@ void hn_nvs_sent_xact(struct hn_send_ctx *sndc, struct hn_softc *sc,
|
|||||||
int hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen,
|
int hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen,
|
||||||
const struct hn_recvinfo *info);
|
const struct hn_recvinfo *info);
|
||||||
void hn_chan_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr);
|
void hn_chan_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr);
|
||||||
|
void hn_link_status_update(struct hn_softc *sc);
|
||||||
|
|
||||||
extern struct hn_send_ctx hn_send_ctx_none;
|
extern struct hn_send_ctx hn_send_ctx_none;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user