1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-15 10:17:20 +00:00

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
This commit is contained in:
Sepherosa Ziehau 2016-10-21 08:02:05 +00:00
parent 31f05efd89
commit 970ead008d
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=307712
6 changed files with 94 additions and 13 deletions

View File

@ -207,7 +207,6 @@ struct hn_softc {
struct ifnet *hn_ifp; struct ifnet *hn_ifp;
struct ifmedia hn_media; struct ifmedia hn_media;
device_t hn_dev; device_t hn_dev;
int hn_carrier;
int hn_if_flags; int hn_if_flags;
struct sx hn_lock; struct sx hn_lock;
struct vmbus_channel *hn_prichan; struct vmbus_channel *hn_prichan;
@ -236,6 +235,9 @@ struct hn_softc {
struct taskqueue *hn_mgmt_taskq; struct taskqueue *hn_mgmt_taskq;
struct taskqueue *hn_mgmt_taskq0; struct taskqueue *hn_mgmt_taskq0;
struct task hn_link_task; 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_caps; /* HN_CAP_ */
uint32_t hn_flags; /* HN_FLAG_ */ uint32_t hn_flags; /* HN_FLAG_ */
@ -271,6 +273,9 @@ struct hn_softc {
#define HN_CAP_TSO6 0x0100 #define HN_CAP_TSO6 0x0100
#define HN_CAP_HASHVAL 0x0200 #define HN_CAP_HASHVAL 0x0200
#define HN_LINK_FLAG_LINKUP 0x0001
#define HN_LINK_FLAG_NETCHG 0x0002
/* /*
* Externs * Externs
*/ */

View File

@ -335,6 +335,8 @@ 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_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 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);
@ -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_resume(struct hn_softc *, int);
static void hn_tx_ring_qflush(struct hn_tx_ring *); static void hn_tx_ring_qflush(struct hn_tx_ring *);
static int netvsc_detach(device_t dev); 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, static void hn_nvs_handle_notify(struct hn_softc *sc,
const struct vmbus_chanpkt_hdr *pkt); 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_status = IFM_AVALID;
ifmr->ifm_active = IFM_ETHER; ifmr->ifm_active = IFM_ETHER;
if (!sc->hn_carrier) { if ((sc->hn_link_flags & HN_LINK_FLAG_LINKUP) == 0) {
ifmr->ifm_active |= IFM_NONE; ifmr->ifm_active |= IFM_NONE;
return; return;
} }
@ -563,6 +566,9 @@ netvsc_attach(device_t dev)
taskqueue_start_threads(&sc->hn_mgmt_taskq0, 1, PI_NET, "%s mgmt", taskqueue_start_threads(&sc->hn_mgmt_taskq0, 1, PI_NET, "%s mgmt",
device_get_nameunit(dev)); device_get_nameunit(dev));
TASK_INIT(&sc->hn_link_task, 0, hn_link_taskfunc, sc); 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 * Allocate ifnet and setup its name earlier, so that if_printf
@ -808,10 +814,8 @@ netvsc_shutdown(device_t dev)
} }
static void 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; uint32_t link_status;
int error; int error;
@ -822,11 +826,51 @@ hn_link_taskfunc(void *xsc, int pending __unused)
} }
if (link_status == NDIS_MEDIA_STATE_CONNECTED) if (link_status == NDIS_MEDIA_STATE_CONNECTED)
sc->hn_carrier = 1; sc->hn_link_flags |= HN_LINK_FLAG_LINKUP;
else else
sc->hn_carrier = 0; sc->hn_link_flags &= ~HN_LINK_FLAG_LINKUP;
if_link_state_change(ifp, if_link_state_change(sc->hn_ifp,
sc->hn_carrier ? LINK_STATE_UP : LINK_STATE_DOWN); (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 void
@ -837,6 +881,14 @@ hn_link_status_update(struct hn_softc *sc)
taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_link_task); 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 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)
@ -3719,6 +3771,8 @@ hn_suspend_mgmt(struct hn_softc *sc)
/* /*
* Make sure that all pending management tasks are completed. * 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); 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; sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0;
hn_link_status_update(sc); hn_network_change(sc);
} }
static void static void

View File

@ -158,6 +158,7 @@ static void
hv_rf_receive_indicate_status(struct hn_softc *sc, const void *data, int dlen) hv_rf_receive_indicate_status(struct hn_softc *sc, const void *data, int dlen)
{ {
const struct rndis_status_msg *msg; const struct rndis_status_msg *msg;
int ofs;
if (dlen < sizeof(*msg)) { if (dlen < sizeof(*msg)) {
if_printf(sc->hn_ifp, "invalid RNDIS status\n"); 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; break;
case RNDIS_STATUS_NETWORK_CHANGE: case RNDIS_STATUS_NETWORK_CHANGE:
/* TODO */ ofs = RNDIS_STBUFOFFSET_ABS(msg->rm_stbufoffset);
if_printf(sc->hn_ifp, "network changed\n"); 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; break;
default: default:

View File

@ -139,6 +139,7 @@ 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); 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; extern struct hn_send_ctx hn_send_ctx_none;

View File

@ -32,6 +32,10 @@
#define NDIS_MEDIA_STATE_CONNECTED 0 #define NDIS_MEDIA_STATE_CONNECTED 0
#define NDIS_MEDIA_STATE_DISCONNECTED 1 #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_NOCHG 0
#define NDIS_OFFLOAD_SET_ON 1 #define NDIS_OFFLOAD_SET_ON 1
#define NDIS_OFFLOAD_SET_OFF 2 #define NDIS_OFFLOAD_SET_OFF 2

View File

@ -320,6 +320,10 @@ struct rndis_status_msg {
/* rndis_diag_info */ /* 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 * Immediately after rndis_status_msg.rm_stbufoffset, if a control
* message is malformatted, or a packet message contains inappropriate * message is malformatted, or a packet message contains inappropriate