diff options
author | Sepherosa Ziehau <sephe@FreeBSD.org> | 2016-10-21 08:02:05 +0000 |
---|---|---|
committer | Sepherosa Ziehau <sephe@FreeBSD.org> | 2016-10-21 08:02:05 +0000 |
commit | 970ead008d48e1e27e6da6abb143e31bbabbf752 (patch) | |
tree | 47a6dd450ba0114c09afdb5bcb819de304c770bb /sys | |
parent | 31f05efd89ded4d2fe78d31a0b2be685ed1717f3 (diff) | |
download | src-970ead008d48e1e27e6da6abb143e31bbabbf752.tar.gz src-970ead008d48e1e27e6da6abb143e31bbabbf752.zip |
hyperv/hn: Add network change support.
Currently the network change is simulated by link status changes.
MFC after: 1 week
Sponsored by: Microsoft
Differential Revision: https://reviews.freebsd.org/D8295
Notes
Notes:
svn path=/head/; revision=307712
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/hyperv/netvsc/hv_net_vsc.h | 7 | ||||
-rw-r--r-- | sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c | 75 | ||||
-rw-r--r-- | sys/dev/hyperv/netvsc/hv_rndis_filter.c | 16 | ||||
-rw-r--r-- | sys/dev/hyperv/netvsc/if_hnvar.h | 1 | ||||
-rw-r--r-- | sys/dev/hyperv/netvsc/ndis.h | 4 | ||||
-rw-r--r-- | sys/net/rndis.h | 4 |
6 files changed, 94 insertions, 13 deletions
diff --git a/sys/dev/hyperv/netvsc/hv_net_vsc.h b/sys/dev/hyperv/netvsc/hv_net_vsc.h index a07fa7f1a944..06a6dfe8c98b 100644 --- a/sys/dev/hyperv/netvsc/hv_net_vsc.h +++ b/sys/dev/hyperv/netvsc/hv_net_vsc.h @@ -207,7 +207,6 @@ struct hn_softc { struct ifnet *hn_ifp; struct ifmedia hn_media; device_t hn_dev; - int hn_carrier; int hn_if_flags; struct sx hn_lock; struct vmbus_channel *hn_prichan; @@ -236,6 +235,9 @@ struct hn_softc { struct taskqueue *hn_mgmt_taskq; struct taskqueue *hn_mgmt_taskq0; struct task hn_link_task; + struct task hn_netchg_init; + struct timeout_task hn_netchg_status; + uint32_t hn_link_flags; /* HN_LINK_FLAG_ */ uint32_t hn_caps; /* HN_CAP_ */ uint32_t hn_flags; /* HN_FLAG_ */ @@ -271,6 +273,9 @@ struct hn_softc { #define HN_CAP_TSO6 0x0100 #define HN_CAP_HASHVAL 0x0200 +#define HN_LINK_FLAG_LINKUP 0x0001 +#define HN_LINK_FLAG_NETCHG 0x0002 + /* * Externs */ diff --git a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c index 46688b08d7e0..79b5ec6a814f 100644 --- a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c +++ b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c @@ -335,6 +335,8 @@ static void hn_destroy_tx_data(struct hn_softc *); static void hn_start_taskfunc(void *, int); static void hn_start_txeof_taskfunc(void *, int); static void hn_link_taskfunc(void *, int); +static void hn_netchg_init_taskfunc(void *, int); +static void hn_netchg_status_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_create_rx_data(struct hn_softc *sc, int); @@ -360,6 +362,7 @@ static void hn_rx_drain(struct vmbus_channel *); static void hn_tx_resume(struct hn_softc *, int); static void hn_tx_ring_qflush(struct hn_tx_ring *); static int netvsc_detach(device_t dev); +static void hn_link_status(struct hn_softc *); static void hn_nvs_handle_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt); @@ -482,7 +485,7 @@ hn_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) ifmr->ifm_status = IFM_AVALID; ifmr->ifm_active = IFM_ETHER; - if (!sc->hn_carrier) { + if ((sc->hn_link_flags & HN_LINK_FLAG_LINKUP) == 0) { ifmr->ifm_active |= IFM_NONE; return; } @@ -563,6 +566,9 @@ netvsc_attach(device_t dev) 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); + TASK_INIT(&sc->hn_netchg_init, 0, hn_netchg_init_taskfunc, sc); + TIMEOUT_TASK_INIT(sc->hn_mgmt_taskq0, &sc->hn_netchg_status, 0, + hn_netchg_status_taskfunc, sc); /* * Allocate ifnet and setup its name earlier, so that if_printf @@ -808,10 +814,8 @@ netvsc_shutdown(device_t dev) } static void -hn_link_taskfunc(void *xsc, int pending __unused) +hn_link_status(struct hn_softc *sc) { - struct hn_softc *sc = xsc; - struct ifnet *ifp = sc->hn_ifp; uint32_t link_status; int error; @@ -822,11 +826,51 @@ hn_link_taskfunc(void *xsc, int pending __unused) } if (link_status == NDIS_MEDIA_STATE_CONNECTED) - sc->hn_carrier = 1; + sc->hn_link_flags |= HN_LINK_FLAG_LINKUP; else - sc->hn_carrier = 0; - if_link_state_change(ifp, - sc->hn_carrier ? LINK_STATE_UP : LINK_STATE_DOWN); + sc->hn_link_flags &= ~HN_LINK_FLAG_LINKUP; + if_link_state_change(sc->hn_ifp, + (sc->hn_link_flags & HN_LINK_FLAG_LINKUP) ? + LINK_STATE_UP : LINK_STATE_DOWN); +} + +static void +hn_link_taskfunc(void *xsc, int pending __unused) +{ + struct hn_softc *sc = xsc; + + if (sc->hn_link_flags & HN_LINK_FLAG_NETCHG) + return; + hn_link_status(sc); +} + +static void +hn_netchg_init_taskfunc(void *xsc, int pending __unused) +{ + struct hn_softc *sc = xsc; + + /* Prevent any link status checks from running. */ + sc->hn_link_flags |= HN_LINK_FLAG_NETCHG; + + /* + * Fake up a [link down --> link up] state change; 5 seconds + * delay is used, which closely simulates miibus reaction + * upon link down event. + */ + sc->hn_link_flags &= ~HN_LINK_FLAG_LINKUP; + if_link_state_change(sc->hn_ifp, LINK_STATE_DOWN); + taskqueue_enqueue_timeout(sc->hn_mgmt_taskq0, + &sc->hn_netchg_status, 5 * hz); +} + +static void +hn_netchg_status_taskfunc(void *xsc, int pending __unused) +{ + struct hn_softc *sc = xsc; + + /* Re-allow link status checks. */ + sc->hn_link_flags &= ~HN_LINK_FLAG_NETCHG; + hn_link_status(sc); } void @@ -837,6 +881,14 @@ hn_link_status_update(struct hn_softc *sc) taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_link_task); } +void +hn_network_change(struct hn_softc *sc) +{ + + if (sc->hn_mgmt_taskq != NULL) + taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_netchg_init); +} + static __inline int hn_txdesc_dmamap_load(struct hn_tx_ring *txr, struct hn_txdesc *txd, struct mbuf **m_head, bus_dma_segment_t *segs, int *nsegs) @@ -3719,6 +3771,8 @@ hn_suspend_mgmt(struct hn_softc *sc) /* * Make sure that all pending management tasks are completed. */ + taskqueue_drain(sc->hn_mgmt_taskq0, &sc->hn_netchg_init); + taskqueue_drain_timeout(sc->hn_mgmt_taskq0, &sc->hn_netchg_status); taskqueue_drain_all(sc->hn_mgmt_taskq0); } @@ -3796,10 +3850,11 @@ hn_resume_mgmt(struct hn_softc *sc) { /* - * Kick off link status check. + * Kick off network change detection, which will + * do link status check too. */ sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0; - hn_link_status_update(sc); + hn_network_change(sc); } static void diff --git a/sys/dev/hyperv/netvsc/hv_rndis_filter.c b/sys/dev/hyperv/netvsc/hv_rndis_filter.c index dde3acb4886a..14446c6c5401 100644 --- a/sys/dev/hyperv/netvsc/hv_rndis_filter.c +++ b/sys/dev/hyperv/netvsc/hv_rndis_filter.c @@ -158,6 +158,7 @@ static void hv_rf_receive_indicate_status(struct hn_softc *sc, const void *data, int dlen) { const struct rndis_status_msg *msg; + int ofs; if (dlen < sizeof(*msg)) { if_printf(sc->hn_ifp, "invalid RNDIS status\n"); @@ -176,8 +177,19 @@ hv_rf_receive_indicate_status(struct hn_softc *sc, const void *data, int dlen) break; case RNDIS_STATUS_NETWORK_CHANGE: - /* TODO */ - if_printf(sc->hn_ifp, "network changed\n"); + ofs = RNDIS_STBUFOFFSET_ABS(msg->rm_stbufoffset); + if (dlen < ofs + msg->rm_stbuflen || + msg->rm_stbuflen < sizeof(uint32_t)) { + if_printf(sc->hn_ifp, "network changed\n"); + } else { + uint32_t change; + + memcpy(&change, ((const uint8_t *)msg) + ofs, + sizeof(change)); + if_printf(sc->hn_ifp, "network changed, change %u\n", + change); + } + hn_network_change(sc); break; default: diff --git a/sys/dev/hyperv/netvsc/if_hnvar.h b/sys/dev/hyperv/netvsc/if_hnvar.h index 517d2815908b..4afe1f14e124 100644 --- a/sys/dev/hyperv/netvsc/if_hnvar.h +++ b/sys/dev/hyperv/netvsc/if_hnvar.h @@ -139,6 +139,7 @@ int hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen, const struct hn_recvinfo *info); void hn_chan_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr); void hn_link_status_update(struct hn_softc *sc); +void hn_network_change(struct hn_softc *sc); extern struct hn_send_ctx hn_send_ctx_none; diff --git a/sys/dev/hyperv/netvsc/ndis.h b/sys/dev/hyperv/netvsc/ndis.h index fed262dddf67..5c741c6db330 100644 --- a/sys/dev/hyperv/netvsc/ndis.h +++ b/sys/dev/hyperv/netvsc/ndis.h @@ -32,6 +32,10 @@ #define NDIS_MEDIA_STATE_CONNECTED 0 #define NDIS_MEDIA_STATE_DISCONNECTED 1 +#define NDIS_NETCHANGE_TYPE_POSSIBLE 1 +#define NDIS_NETCHANGE_TYPE_DEFINITE 2 +#define NDIS_NETCHANGE_TYPE_FROMMEDIA 3 + #define NDIS_OFFLOAD_SET_NOCHG 0 #define NDIS_OFFLOAD_SET_ON 1 #define NDIS_OFFLOAD_SET_OFF 2 diff --git a/sys/net/rndis.h b/sys/net/rndis.h index 9da76bc08d24..95edb1c5d7e4 100644 --- a/sys/net/rndis.h +++ b/sys/net/rndis.h @@ -320,6 +320,10 @@ struct rndis_status_msg { /* rndis_diag_info */ }; +/* stbuf offset from the beginning of rndis_status_msg. */ +#define RNDIS_STBUFOFFSET_ABS(ofs) \ + ((ofs) + __offsetof(struct rndis_status_msg, rm_status)) + /* * Immediately after rndis_status_msg.rm_stbufoffset, if a control * message is malformatted, or a packet message contains inappropriate |