hyperv/hn: Reorganize RX path; mainly pull non-control code path up
MFC after: 1 week Sponsored by: Microsoft Differential Revision: https://reviews.freebsd.org/D8356
This commit is contained in:
parent
96a98cbf14
commit
91938ebba8
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=308014
@ -166,6 +166,16 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#define HN_EARLY_TXEOF_THRESH 8
|
||||
|
||||
#define HN_RXINFO_VLAN 0x0001
|
||||
#define HN_RXINFO_CSUM 0x0002
|
||||
#define HN_RXINFO_HASHINF 0x0004
|
||||
#define HN_RXINFO_HASHVAL 0x0008
|
||||
#define HN_RXINFO_ALL \
|
||||
(HN_RXINFO_VLAN | \
|
||||
HN_RXINFO_CSUM | \
|
||||
HN_RXINFO_HASHINF | \
|
||||
HN_RXINFO_HASHVAL)
|
||||
|
||||
struct hn_txdesc {
|
||||
#ifndef HN_USE_TXDESC_BUFRING
|
||||
SLIST_ENTRY(hn_txdesc) link;
|
||||
@ -188,6 +198,17 @@ struct hn_txdesc {
|
||||
#define HN_TXD_FLAG_ONLIST 0x1
|
||||
#define HN_TXD_FLAG_DMAMAP 0x2
|
||||
|
||||
#define HN_NDIS_VLAN_INFO_INVALID 0xffffffff
|
||||
#define HN_NDIS_RXCSUM_INFO_INVALID 0
|
||||
#define HN_NDIS_HASH_INFO_INVALID 0
|
||||
|
||||
struct hn_rxinfo {
|
||||
uint32_t vlan_info;
|
||||
uint32_t csum_info;
|
||||
uint32_t hash_info;
|
||||
uint32_t hash_value;
|
||||
};
|
||||
|
||||
#define HN_LRO_LENLIM_MULTIRX_DEF (12 * ETHERMTU)
|
||||
#define HN_LRO_LENLIM_DEF (25 * ETHERMTU)
|
||||
/* YYY 2*MTU is a bit rough, but should be good enough. */
|
||||
@ -377,12 +398,18 @@ static void hn_link_status(struct hn_softc *);
|
||||
static int hn_sendpkt_rndis_sglist(struct hn_tx_ring *, struct hn_txdesc *);
|
||||
static int hn_sendpkt_rndis_chim(struct hn_tx_ring *, struct hn_txdesc *);
|
||||
static int hn_set_rxfilter(struct hn_softc *);
|
||||
static void hn_link_status_update(struct hn_softc *);
|
||||
static void hn_network_change(struct hn_softc *);
|
||||
|
||||
static int hn_rndis_rxinfo(const void *, int, struct hn_rxinfo *);
|
||||
static void hn_rndis_rx_data(struct hn_rx_ring *, const void *, int);
|
||||
static void hn_rndis_rx_status(struct hn_softc *, const void *, int);
|
||||
|
||||
static void hn_nvs_handle_notify(struct hn_softc *sc,
|
||||
const struct vmbus_chanpkt_hdr *pkt);
|
||||
static void hn_nvs_handle_comp(struct hn_softc *sc, struct vmbus_channel *chan,
|
||||
const struct vmbus_chanpkt_hdr *pkt);
|
||||
static void hn_nvs_handle_rxbuf(struct hn_softc *sc, struct hn_rx_ring *rxr,
|
||||
static void hn_nvs_handle_rxbuf(struct hn_rx_ring *rxr,
|
||||
struct vmbus_channel *chan,
|
||||
const struct vmbus_chanpkt_hdr *pkthdr);
|
||||
static void hn_nvs_ack_rxbuf(struct vmbus_channel *chan, uint64_t tid);
|
||||
@ -1006,7 +1033,7 @@ hn_netchg_status_taskfunc(void *xsc, int pending __unused)
|
||||
hn_link_status(sc);
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
hn_link_status_update(struct hn_softc *sc)
|
||||
{
|
||||
|
||||
@ -1014,7 +1041,7 @@ hn_link_status_update(struct hn_softc *sc)
|
||||
taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_link_task);
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
hn_network_change(struct hn_softc *sc)
|
||||
{
|
||||
|
||||
@ -1615,15 +1642,9 @@ hn_lro_rx(struct lro_ctrl *lc, struct mbuf *m)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Called when we receive a data packet from the "wire" on the
|
||||
* specified device
|
||||
*
|
||||
* Note: This is no longer used as a callback
|
||||
*/
|
||||
int
|
||||
static int
|
||||
hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen,
|
||||
const struct hn_recvinfo *info)
|
||||
const struct hn_rxinfo *info)
|
||||
{
|
||||
struct ifnet *ifp = rxr->hn_ifp;
|
||||
struct mbuf *m_new;
|
||||
@ -4025,6 +4046,325 @@ hn_resume(struct hn_softc *sc)
|
||||
hn_resume_mgmt(sc);
|
||||
}
|
||||
|
||||
static void
|
||||
hn_rndis_rx_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");
|
||||
return;
|
||||
}
|
||||
msg = data;
|
||||
|
||||
switch (msg->rm_status) {
|
||||
case RNDIS_STATUS_MEDIA_CONNECT:
|
||||
case RNDIS_STATUS_MEDIA_DISCONNECT:
|
||||
hn_link_status_update(sc);
|
||||
break;
|
||||
|
||||
case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG:
|
||||
/* Not really useful; ignore. */
|
||||
break;
|
||||
|
||||
case RNDIS_STATUS_NETWORK_CHANGE:
|
||||
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:
|
||||
if_printf(sc->hn_ifp, "unknown RNDIS status 0x%08x\n",
|
||||
msg->rm_status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_rxinfo *info)
|
||||
{
|
||||
const struct rndis_pktinfo *pi = info_data;
|
||||
uint32_t mask = 0;
|
||||
|
||||
while (info_dlen != 0) {
|
||||
const void *data;
|
||||
uint32_t dlen;
|
||||
|
||||
if (__predict_false(info_dlen < sizeof(*pi)))
|
||||
return (EINVAL);
|
||||
if (__predict_false(info_dlen < pi->rm_size))
|
||||
return (EINVAL);
|
||||
info_dlen -= pi->rm_size;
|
||||
|
||||
if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK))
|
||||
return (EINVAL);
|
||||
if (__predict_false(pi->rm_size < pi->rm_pktinfooffset))
|
||||
return (EINVAL);
|
||||
dlen = pi->rm_size - pi->rm_pktinfooffset;
|
||||
data = pi->rm_data;
|
||||
|
||||
switch (pi->rm_type) {
|
||||
case NDIS_PKTINFO_TYPE_VLAN:
|
||||
if (__predict_false(dlen < NDIS_VLAN_INFO_SIZE))
|
||||
return (EINVAL);
|
||||
info->vlan_info = *((const uint32_t *)data);
|
||||
mask |= HN_RXINFO_VLAN;
|
||||
break;
|
||||
|
||||
case NDIS_PKTINFO_TYPE_CSUM:
|
||||
if (__predict_false(dlen < NDIS_RXCSUM_INFO_SIZE))
|
||||
return (EINVAL);
|
||||
info->csum_info = *((const uint32_t *)data);
|
||||
mask |= HN_RXINFO_CSUM;
|
||||
break;
|
||||
|
||||
case HN_NDIS_PKTINFO_TYPE_HASHVAL:
|
||||
if (__predict_false(dlen < HN_NDIS_HASH_VALUE_SIZE))
|
||||
return (EINVAL);
|
||||
info->hash_value = *((const uint32_t *)data);
|
||||
mask |= HN_RXINFO_HASHVAL;
|
||||
break;
|
||||
|
||||
case HN_NDIS_PKTINFO_TYPE_HASHINF:
|
||||
if (__predict_false(dlen < HN_NDIS_HASH_INFO_SIZE))
|
||||
return (EINVAL);
|
||||
info->hash_info = *((const uint32_t *)data);
|
||||
mask |= HN_RXINFO_HASHINF;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (mask == HN_RXINFO_ALL) {
|
||||
/* All found; done */
|
||||
break;
|
||||
}
|
||||
next:
|
||||
pi = (const struct rndis_pktinfo *)
|
||||
((const uint8_t *)pi + pi->rm_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Final fixup.
|
||||
* - If there is no hash value, invalidate the hash info.
|
||||
*/
|
||||
if ((mask & HN_RXINFO_HASHVAL) == 0)
|
||||
info->hash_info = HN_NDIS_HASH_INFO_INVALID;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static __inline bool
|
||||
hn_rndis_check_overlap(int off, int len, int check_off, int check_len)
|
||||
{
|
||||
|
||||
if (off < check_off) {
|
||||
if (__predict_true(off + len <= check_off))
|
||||
return (false);
|
||||
} else if (off > check_off) {
|
||||
if (__predict_true(check_off + check_len <= off))
|
||||
return (false);
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
static void
|
||||
hn_rndis_rx_data(struct hn_rx_ring *rxr, const void *data, int dlen)
|
||||
{
|
||||
const struct rndis_packet_msg *pkt;
|
||||
struct hn_rxinfo info;
|
||||
int data_off, pktinfo_off, data_len, pktinfo_len;
|
||||
|
||||
/*
|
||||
* Check length.
|
||||
*/
|
||||
if (__predict_false(dlen < sizeof(*pkt))) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n");
|
||||
return;
|
||||
}
|
||||
pkt = data;
|
||||
|
||||
if (__predict_false(dlen < pkt->rm_len)) {
|
||||
if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, "
|
||||
"dlen %d, msglen %u\n", dlen, pkt->rm_len);
|
||||
return;
|
||||
}
|
||||
if (__predict_false(pkt->rm_len <
|
||||
pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, "
|
||||
"msglen %u, data %u, oob %u, pktinfo %u\n",
|
||||
pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen,
|
||||
pkt->rm_pktinfolen);
|
||||
return;
|
||||
}
|
||||
if (__predict_false(pkt->rm_datalen == 0)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check offests.
|
||||
*/
|
||||
#define IS_OFFSET_INVALID(ofs) \
|
||||
((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN || \
|
||||
((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK))
|
||||
|
||||
/* XXX Hyper-V does not meet data offset alignment requirement */
|
||||
if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"data offset %u\n", pkt->rm_dataoffset);
|
||||
return;
|
||||
}
|
||||
if (__predict_false(pkt->rm_oobdataoffset > 0 &&
|
||||
IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"oob offset %u\n", pkt->rm_oobdataoffset);
|
||||
return;
|
||||
}
|
||||
if (__predict_true(pkt->rm_pktinfooffset > 0) &&
|
||||
__predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"pktinfo offset %u\n", pkt->rm_pktinfooffset);
|
||||
return;
|
||||
}
|
||||
|
||||
#undef IS_OFFSET_INVALID
|
||||
|
||||
data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset);
|
||||
data_len = pkt->rm_datalen;
|
||||
pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset);
|
||||
pktinfo_len = pkt->rm_pktinfolen;
|
||||
|
||||
/*
|
||||
* Check OOB coverage.
|
||||
*/
|
||||
if (__predict_false(pkt->rm_oobdatalen != 0)) {
|
||||
int oob_off, oob_len;
|
||||
|
||||
if_printf(rxr->hn_ifp, "got oobdata\n");
|
||||
oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset);
|
||||
oob_len = pkt->rm_oobdatalen;
|
||||
|
||||
if (__predict_false(oob_off + oob_len > pkt->rm_len)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"oob overflow, msglen %u, oob abs %d len %d\n",
|
||||
pkt->rm_len, oob_off, oob_len);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check against data.
|
||||
*/
|
||||
if (hn_rndis_check_overlap(oob_off, oob_len,
|
||||
data_off, data_len)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"oob overlaps data, oob abs %d len %d, "
|
||||
"data abs %d len %d\n",
|
||||
oob_off, oob_len, data_off, data_len);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check against pktinfo.
|
||||
*/
|
||||
if (pktinfo_len != 0 &&
|
||||
hn_rndis_check_overlap(oob_off, oob_len,
|
||||
pktinfo_off, pktinfo_len)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"oob overlaps pktinfo, oob abs %d len %d, "
|
||||
"pktinfo abs %d len %d\n",
|
||||
oob_off, oob_len, pktinfo_off, pktinfo_len);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check per-packet-info coverage and find useful per-packet-info.
|
||||
*/
|
||||
info.vlan_info = HN_NDIS_VLAN_INFO_INVALID;
|
||||
info.csum_info = HN_NDIS_RXCSUM_INFO_INVALID;
|
||||
info.hash_info = HN_NDIS_HASH_INFO_INVALID;
|
||||
if (__predict_true(pktinfo_len != 0)) {
|
||||
bool overlap;
|
||||
int error;
|
||||
|
||||
if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"pktinfo overflow, msglen %u, "
|
||||
"pktinfo abs %d len %d\n",
|
||||
pkt->rm_len, pktinfo_off, pktinfo_len);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check packet info coverage.
|
||||
*/
|
||||
overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len,
|
||||
data_off, data_len);
|
||||
if (__predict_false(overlap)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"pktinfo overlap data, pktinfo abs %d len %d, "
|
||||
"data abs %d len %d\n",
|
||||
pktinfo_off, pktinfo_len, data_off, data_len);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find useful per-packet-info.
|
||||
*/
|
||||
error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off,
|
||||
pktinfo_len, &info);
|
||||
if (__predict_false(error)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg "
|
||||
"pktinfo\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (__predict_false(data_off + data_len > pkt->rm_len)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"data overflow, msglen %u, data abs %d len %d\n",
|
||||
pkt->rm_len, data_off, data_len);
|
||||
return;
|
||||
}
|
||||
hn_rxpkt(rxr, ((const uint8_t *)pkt) + data_off, data_len, &info);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
hn_rndis_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen)
|
||||
{
|
||||
const struct rndis_msghdr *hdr;
|
||||
|
||||
if (__predict_false(dlen < sizeof(*hdr))) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS msg\n");
|
||||
return;
|
||||
}
|
||||
hdr = data;
|
||||
|
||||
if (__predict_true(hdr->rm_type == REMOTE_NDIS_PACKET_MSG)) {
|
||||
/* Hot data path. */
|
||||
hn_rndis_rx_data(rxr, data, dlen);
|
||||
/* Done! */
|
||||
return;
|
||||
}
|
||||
|
||||
if (hdr->rm_type == REMOTE_NDIS_INDICATE_STATUS_MSG)
|
||||
hn_rndis_rx_status(rxr->hn_ifp->if_softc, data, dlen);
|
||||
else
|
||||
hn_rndis_rx_ctrl(rxr->hn_ifp->if_softc, data, dlen);
|
||||
}
|
||||
|
||||
static void
|
||||
hn_nvs_handle_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt)
|
||||
{
|
||||
@ -4060,8 +4400,8 @@ hn_nvs_handle_comp(struct hn_softc *sc, struct vmbus_channel *chan,
|
||||
}
|
||||
|
||||
static void
|
||||
hn_nvs_handle_rxbuf(struct hn_softc *sc, struct hn_rx_ring *rxr,
|
||||
struct vmbus_channel *chan, const struct vmbus_chanpkt_hdr *pkthdr)
|
||||
hn_nvs_handle_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan,
|
||||
const struct vmbus_chanpkt_hdr *pkthdr)
|
||||
{
|
||||
const struct vmbus_chanpkt_rxbuf *pkt;
|
||||
const struct hn_nvs_hdr *nvs_hdr;
|
||||
@ -4111,9 +4451,9 @@ hn_nvs_handle_rxbuf(struct hn_softc *sc, struct hn_rx_ring *rxr,
|
||||
"ofs %d, len %d\n", i, ofs, len);
|
||||
continue;
|
||||
}
|
||||
hv_rf_on_receive(sc, rxr, rxr->hn_rxbuf + ofs, len);
|
||||
hn_rndis_rxpkt(rxr, rxr->hn_rxbuf + ofs, len);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Moved completion call back here so that all received
|
||||
* messages (not just data messages) will trigger a response
|
||||
@ -4177,7 +4517,7 @@ hn_chan_callback(struct vmbus_channel *chan, void *xrxr)
|
||||
hn_nvs_handle_comp(sc, chan, pkt);
|
||||
break;
|
||||
case VMBUS_CHANPKT_TYPE_RXBUF:
|
||||
hn_nvs_handle_rxbuf(sc, rxr, chan, pkt);
|
||||
hn_nvs_handle_rxbuf(rxr, chan, pkt);
|
||||
break;
|
||||
case VMBUS_CHANPKT_TYPE_INBAND:
|
||||
hn_nvs_handle_notify(sc, pkt);
|
||||
|
@ -59,16 +59,6 @@ __FBSDID("$FreeBSD$");
|
||||
#include <dev/hyperv/netvsc/hn_nvs.h>
|
||||
#include <dev/hyperv/netvsc/hv_rndis_filter.h>
|
||||
|
||||
#define HV_RF_RECVINFO_VLAN 0x1
|
||||
#define HV_RF_RECVINFO_CSUM 0x2
|
||||
#define HV_RF_RECVINFO_HASHINF 0x4
|
||||
#define HV_RF_RECVINFO_HASHVAL 0x8
|
||||
#define HV_RF_RECVINFO_ALL \
|
||||
(HV_RF_RECVINFO_VLAN | \
|
||||
HV_RF_RECVINFO_CSUM | \
|
||||
HV_RF_RECVINFO_HASHINF | \
|
||||
HV_RF_RECVINFO_HASHVAL)
|
||||
|
||||
#define HN_RNDIS_RID_COMPAT_MASK 0xffff
|
||||
#define HN_RNDIS_RID_COMPAT_MAX HN_RNDIS_RID_COMPAT_MASK
|
||||
|
||||
@ -89,11 +79,6 @@ __FBSDID("$FreeBSD$");
|
||||
/*
|
||||
* Forward declarations
|
||||
*/
|
||||
static void hv_rf_receive_indicate_status(struct hn_softc *sc,
|
||||
const void *data, int dlen);
|
||||
static void hv_rf_receive_data(struct hn_rx_ring *rxr,
|
||||
const void *data, int dlen);
|
||||
|
||||
static int hn_rndis_query(struct hn_softc *sc, uint32_t oid,
|
||||
const void *idata, size_t idlen, void *odata, size_t *odlen0);
|
||||
static int hn_rndis_query2(struct hn_softc *sc, uint32_t oid,
|
||||
@ -155,335 +140,22 @@ hn_rndis_pktinfo_append(struct rndis_packet_msg *pkt, size_t pktsize,
|
||||
return (pi->rm_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* RNDIS filter receive indicate status
|
||||
*/
|
||||
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");
|
||||
return;
|
||||
}
|
||||
msg = data;
|
||||
|
||||
switch (msg->rm_status) {
|
||||
case RNDIS_STATUS_MEDIA_CONNECT:
|
||||
case RNDIS_STATUS_MEDIA_DISCONNECT:
|
||||
hn_link_status_update(sc);
|
||||
break;
|
||||
|
||||
case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG:
|
||||
/* Not really useful; ignore. */
|
||||
break;
|
||||
|
||||
case RNDIS_STATUS_NETWORK_CHANGE:
|
||||
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:
|
||||
/* TODO: */
|
||||
if_printf(sc->hn_ifp, "unknown RNDIS status 0x%08x\n",
|
||||
msg->rm_status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_recvinfo *info)
|
||||
{
|
||||
const struct rndis_pktinfo *pi = info_data;
|
||||
uint32_t mask = 0;
|
||||
|
||||
while (info_dlen != 0) {
|
||||
const void *data;
|
||||
uint32_t dlen;
|
||||
|
||||
if (__predict_false(info_dlen < sizeof(*pi)))
|
||||
return (EINVAL);
|
||||
if (__predict_false(info_dlen < pi->rm_size))
|
||||
return (EINVAL);
|
||||
info_dlen -= pi->rm_size;
|
||||
|
||||
if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK))
|
||||
return (EINVAL);
|
||||
if (__predict_false(pi->rm_size < pi->rm_pktinfooffset))
|
||||
return (EINVAL);
|
||||
dlen = pi->rm_size - pi->rm_pktinfooffset;
|
||||
data = pi->rm_data;
|
||||
|
||||
switch (pi->rm_type) {
|
||||
case NDIS_PKTINFO_TYPE_VLAN:
|
||||
if (__predict_false(dlen < NDIS_VLAN_INFO_SIZE))
|
||||
return (EINVAL);
|
||||
info->vlan_info = *((const uint32_t *)data);
|
||||
mask |= HV_RF_RECVINFO_VLAN;
|
||||
break;
|
||||
|
||||
case NDIS_PKTINFO_TYPE_CSUM:
|
||||
if (__predict_false(dlen < NDIS_RXCSUM_INFO_SIZE))
|
||||
return (EINVAL);
|
||||
info->csum_info = *((const uint32_t *)data);
|
||||
mask |= HV_RF_RECVINFO_CSUM;
|
||||
break;
|
||||
|
||||
case HN_NDIS_PKTINFO_TYPE_HASHVAL:
|
||||
if (__predict_false(dlen < HN_NDIS_HASH_VALUE_SIZE))
|
||||
return (EINVAL);
|
||||
info->hash_value = *((const uint32_t *)data);
|
||||
mask |= HV_RF_RECVINFO_HASHVAL;
|
||||
break;
|
||||
|
||||
case HN_NDIS_PKTINFO_TYPE_HASHINF:
|
||||
if (__predict_false(dlen < HN_NDIS_HASH_INFO_SIZE))
|
||||
return (EINVAL);
|
||||
info->hash_info = *((const uint32_t *)data);
|
||||
mask |= HV_RF_RECVINFO_HASHINF;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (mask == HV_RF_RECVINFO_ALL) {
|
||||
/* All found; done */
|
||||
break;
|
||||
}
|
||||
next:
|
||||
pi = (const struct rndis_pktinfo *)
|
||||
((const uint8_t *)pi + pi->rm_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Final fixup.
|
||||
* - If there is no hash value, invalidate the hash info.
|
||||
*/
|
||||
if ((mask & HV_RF_RECVINFO_HASHVAL) == 0)
|
||||
info->hash_info = HN_NDIS_HASH_INFO_INVALID;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static __inline bool
|
||||
hn_rndis_check_overlap(int off, int len, int check_off, int check_len)
|
||||
{
|
||||
|
||||
if (off < check_off) {
|
||||
if (__predict_true(off + len <= check_off))
|
||||
return (false);
|
||||
} else if (off > check_off) {
|
||||
if (__predict_true(check_off + check_len <= off))
|
||||
return (false);
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
/*
|
||||
* RNDIS filter receive data
|
||||
*/
|
||||
static void
|
||||
hv_rf_receive_data(struct hn_rx_ring *rxr, const void *data, int dlen)
|
||||
{
|
||||
const struct rndis_packet_msg *pkt;
|
||||
struct hn_recvinfo info;
|
||||
int data_off, pktinfo_off, data_len, pktinfo_len;
|
||||
|
||||
/*
|
||||
* Check length.
|
||||
*/
|
||||
if (__predict_false(dlen < sizeof(*pkt))) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n");
|
||||
return;
|
||||
}
|
||||
pkt = data;
|
||||
|
||||
if (__predict_false(dlen < pkt->rm_len)) {
|
||||
if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, "
|
||||
"dlen %d, msglen %u\n", dlen, pkt->rm_len);
|
||||
return;
|
||||
}
|
||||
if (__predict_false(pkt->rm_len <
|
||||
pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, "
|
||||
"msglen %u, data %u, oob %u, pktinfo %u\n",
|
||||
pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen,
|
||||
pkt->rm_pktinfolen);
|
||||
return;
|
||||
}
|
||||
if (__predict_false(pkt->rm_datalen == 0)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check offests.
|
||||
*/
|
||||
#define IS_OFFSET_INVALID(ofs) \
|
||||
((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN || \
|
||||
((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK))
|
||||
|
||||
/* XXX Hyper-V does not meet data offset alignment requirement */
|
||||
if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"data offset %u\n", pkt->rm_dataoffset);
|
||||
return;
|
||||
}
|
||||
if (__predict_false(pkt->rm_oobdataoffset > 0 &&
|
||||
IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"oob offset %u\n", pkt->rm_oobdataoffset);
|
||||
return;
|
||||
}
|
||||
if (__predict_true(pkt->rm_pktinfooffset > 0) &&
|
||||
__predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"pktinfo offset %u\n", pkt->rm_pktinfooffset);
|
||||
return;
|
||||
}
|
||||
|
||||
#undef IS_OFFSET_INVALID
|
||||
|
||||
data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset);
|
||||
data_len = pkt->rm_datalen;
|
||||
pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset);
|
||||
pktinfo_len = pkt->rm_pktinfolen;
|
||||
|
||||
/*
|
||||
* Check OOB coverage.
|
||||
*/
|
||||
if (__predict_false(pkt->rm_oobdatalen != 0)) {
|
||||
int oob_off, oob_len;
|
||||
|
||||
if_printf(rxr->hn_ifp, "got oobdata\n");
|
||||
oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset);
|
||||
oob_len = pkt->rm_oobdatalen;
|
||||
|
||||
if (__predict_false(oob_off + oob_len > pkt->rm_len)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"oob overflow, msglen %u, oob abs %d len %d\n",
|
||||
pkt->rm_len, oob_off, oob_len);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check against data.
|
||||
*/
|
||||
if (hn_rndis_check_overlap(oob_off, oob_len,
|
||||
data_off, data_len)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"oob overlaps data, oob abs %d len %d, "
|
||||
"data abs %d len %d\n",
|
||||
oob_off, oob_len, data_off, data_len);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check against pktinfo.
|
||||
*/
|
||||
if (pktinfo_len != 0 &&
|
||||
hn_rndis_check_overlap(oob_off, oob_len,
|
||||
pktinfo_off, pktinfo_len)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"oob overlaps pktinfo, oob abs %d len %d, "
|
||||
"pktinfo abs %d len %d\n",
|
||||
oob_off, oob_len, pktinfo_off, pktinfo_len);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check per-packet-info coverage and find useful per-packet-info.
|
||||
*/
|
||||
info.vlan_info = HN_NDIS_VLAN_INFO_INVALID;
|
||||
info.csum_info = HN_NDIS_RXCSUM_INFO_INVALID;
|
||||
info.hash_info = HN_NDIS_HASH_INFO_INVALID;
|
||||
if (__predict_true(pktinfo_len != 0)) {
|
||||
bool overlap;
|
||||
int error;
|
||||
|
||||
if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"pktinfo overflow, msglen %u, "
|
||||
"pktinfo abs %d len %d\n",
|
||||
pkt->rm_len, pktinfo_off, pktinfo_len);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check packet info coverage.
|
||||
*/
|
||||
overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len,
|
||||
data_off, data_len);
|
||||
if (__predict_false(overlap)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"pktinfo overlap data, pktinfo abs %d len %d, "
|
||||
"data abs %d len %d\n",
|
||||
pktinfo_off, pktinfo_len, data_off, data_len);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find useful per-packet-info.
|
||||
*/
|
||||
error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off,
|
||||
pktinfo_len, &info);
|
||||
if (__predict_false(error)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg "
|
||||
"pktinfo\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (__predict_false(data_off + data_len > pkt->rm_len)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
|
||||
"data overflow, msglen %u, data abs %d len %d\n",
|
||||
pkt->rm_len, data_off, data_len);
|
||||
return;
|
||||
}
|
||||
hn_rxpkt(rxr, ((const uint8_t *)pkt) + data_off, data_len, &info);
|
||||
}
|
||||
|
||||
/*
|
||||
* RNDIS filter on receive
|
||||
*/
|
||||
void
|
||||
hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr,
|
||||
const void *data, int dlen)
|
||||
hn_rndis_rx_ctrl(struct hn_softc *sc, const void *data, int dlen)
|
||||
{
|
||||
const struct rndis_comp_hdr *comp;
|
||||
const struct rndis_msghdr *hdr;
|
||||
|
||||
if (__predict_false(dlen < sizeof(*hdr))) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS msg\n");
|
||||
return;
|
||||
}
|
||||
KASSERT(dlen >= sizeof(*hdr), ("invalid RNDIS msg\n"));
|
||||
hdr = data;
|
||||
|
||||
switch (hdr->rm_type) {
|
||||
case REMOTE_NDIS_PACKET_MSG:
|
||||
hv_rf_receive_data(rxr, data, dlen);
|
||||
break;
|
||||
|
||||
case REMOTE_NDIS_INITIALIZE_CMPLT:
|
||||
case REMOTE_NDIS_QUERY_CMPLT:
|
||||
case REMOTE_NDIS_SET_CMPLT:
|
||||
case REMOTE_NDIS_KEEPALIVE_CMPLT: /* unused */
|
||||
if (dlen < sizeof(*comp)) {
|
||||
if_printf(rxr->hn_ifp, "invalid RNDIS cmplt\n");
|
||||
if_printf(sc->hn_ifp, "invalid RNDIS cmplt\n");
|
||||
return;
|
||||
}
|
||||
comp = data;
|
||||
@ -493,10 +165,6 @@ hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr,
|
||||
vmbus_xact_ctx_wakeup(sc->hn_xact, comp, dlen);
|
||||
break;
|
||||
|
||||
case REMOTE_NDIS_INDICATE_STATUS_MSG:
|
||||
hv_rf_receive_indicate_status(sc, data, dlen);
|
||||
break;
|
||||
|
||||
case REMOTE_NDIS_RESET_CMPLT:
|
||||
/*
|
||||
* Reset completed, no rid.
|
||||
@ -505,11 +173,11 @@ hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr,
|
||||
* RESET is not issued by hn(4), so this message should
|
||||
* _not_ be observed.
|
||||
*/
|
||||
if_printf(rxr->hn_ifp, "RESET cmplt received\n");
|
||||
if_printf(sc->hn_ifp, "RESET cmplt received\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
if_printf(rxr->hn_ifp, "unknown RNDIS msg 0x%x\n",
|
||||
if_printf(sc->hn_ifp, "unknown RNDIS msg 0x%x\n",
|
||||
hdr->rm_type);
|
||||
break;
|
||||
}
|
||||
|
@ -31,16 +31,9 @@
|
||||
#ifndef __HV_RNDIS_FILTER_H__
|
||||
#define __HV_RNDIS_FILTER_H__
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <dev/hyperv/netvsc/if_hnvar.h>
|
||||
struct hn_softc;
|
||||
|
||||
/*
|
||||
* Externs
|
||||
*/
|
||||
struct hn_rx_ring;
|
||||
|
||||
void hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr,
|
||||
const void *data, int dlen);
|
||||
void hn_rndis_rx_ctrl(struct hn_softc *sc, const void *data,
|
||||
int dlen);
|
||||
|
||||
#endif /* __HV_RNDIS_FILTER_H__ */
|
||||
|
@ -51,17 +51,6 @@
|
||||
|
||||
#define HN_GPACNT_MAX 32
|
||||
|
||||
#define HN_NDIS_VLAN_INFO_INVALID 0xffffffff
|
||||
#define HN_NDIS_RXCSUM_INFO_INVALID 0
|
||||
#define HN_NDIS_HASH_INFO_INVALID 0
|
||||
|
||||
struct hn_recvinfo {
|
||||
uint32_t vlan_info;
|
||||
uint32_t csum_info;
|
||||
uint32_t hash_info;
|
||||
uint32_t hash_value;
|
||||
};
|
||||
|
||||
struct hn_txdesc;
|
||||
#ifndef HN_USE_TXDESC_BUFRING
|
||||
SLIST_HEAD(hn_txdesc_list, hn_txdesc);
|
||||
@ -256,9 +245,4 @@ int hn_rndis_get_linkstatus(struct hn_softc *sc,
|
||||
/* filter: NDIS_PACKET_TYPE_. */
|
||||
int hn_rndis_set_rxfilter(struct hn_softc *sc, uint32_t filter);
|
||||
|
||||
int hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen,
|
||||
const struct hn_recvinfo *info);
|
||||
void hn_link_status_update(struct hn_softc *sc);
|
||||
void hn_network_change(struct hn_softc *sc);
|
||||
|
||||
#endif /* !_IF_HNVAR_H_ */
|
||||
|
Loading…
Reference in New Issue
Block a user