summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorsephe <sephe@FreeBSD.org>2016-06-21 07:05:49 +0000
committersephe <sephe@FreeBSD.org>2016-06-21 07:05:49 +0000
commitbc9e0dd07a76c4d7a1c6fcf21824ca2cecff2c6d (patch)
treeb130cc0a7e459334d166043869c71302e888a9c4 /sys/dev
parentb8d88a19ec41ade378eb1628c2cca68be449fc36 (diff)
downloadFreeBSD-src-bc9e0dd07a76c4d7a1c6fcf21824ca2cecff2c6d.zip
FreeBSD-src-bc9e0dd07a76c4d7a1c6fcf21824ca2cecff2c6d.tar.gz
MFC 299401,299888
299401 hyperv/hn: Extract RSS hash value and type. MFC after: 1 week Sponsored by: Microsoft OSTC Differential Revision: https://reviews.freebsd.org/D6287 299888 hyperv/hn: Combine per-packet-information parsing. MFC after: 1 week Sponsored by: Microsoft OSTC
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/hyperv/netvsc/hv_net_vsc.h1
-rw-r--r--sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c70
-rw-r--r--sys/dev/hyperv/netvsc/hv_rndis.h35
-rw-r--r--sys/dev/hyperv/netvsc/hv_rndis_filter.c114
4 files changed, 193 insertions, 27 deletions
diff --git a/sys/dev/hyperv/netvsc/hv_net_vsc.h b/sys/dev/hyperv/netvsc/hv_net_vsc.h
index db696e6..7c43f64 100644
--- a/sys/dev/hyperv/netvsc/hv_net_vsc.h
+++ b/sys/dev/hyperv/netvsc/hv_net_vsc.h
@@ -1164,6 +1164,7 @@ struct hn_rx_ring {
u_long hn_lro_tried;
u_long hn_small_pkts;
u_long hn_pkts;
+ u_long hn_rss_pkts;
/* Rarely used stuffs */
struct sysctl_oid *hn_rx_sysctl_tree;
diff --git a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
index 1d1fd0d..f670c12 100644
--- a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
+++ b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
@@ -142,7 +142,7 @@ __FBSDID("$FreeBSD$");
#define HN_RNDIS_MSG_LEN \
(sizeof(rndis_msg) + \
- RNDIS_HASH_PPI_SIZE + \
+ RNDIS_HASHVAL_PPI_SIZE + \
RNDIS_VLAN_PPI_SIZE + \
RNDIS_TSO_PPI_SIZE + \
RNDIS_CSUM_PPI_SIZE)
@@ -883,7 +883,7 @@ hn_encap(struct hn_tx_ring *txr, struct hn_txdesc *txd, struct mbuf **m_head0)
rndis_msg *rndis_mesg;
rndis_packet *rndis_pkt;
rndis_per_packet_info *rppi;
- struct ndis_hash_info *hash_info;
+ struct rndis_hash_value *hash_value;
uint32_t rndis_msg_size;
packet = &txd->netvsc_pkt;
@@ -909,16 +909,16 @@ hn_encap(struct hn_tx_ring *txr, struct hn_txdesc *txd, struct mbuf **m_head0)
rndis_msg_size = RNDIS_MESSAGE_SIZE(rndis_packet);
/*
- * Set the hash info for this packet, so that the host could
+ * Set the hash value for this packet, so that the host could
* dispatch the TX done event for this packet back to this TX
* ring's channel.
*/
- rndis_msg_size += RNDIS_HASH_PPI_SIZE;
- rppi = hv_set_rppi_data(rndis_mesg, RNDIS_HASH_PPI_SIZE,
+ rndis_msg_size += RNDIS_HASHVAL_PPI_SIZE;
+ rppi = hv_set_rppi_data(rndis_mesg, RNDIS_HASHVAL_PPI_SIZE,
nbl_hash_value);
- hash_info = (struct ndis_hash_info *)((uint8_t *)rppi +
+ hash_value = (struct rndis_hash_value *)((uint8_t *)rppi +
rppi->per_packet_info_offset);
- hash_info->hash = txr->hn_tx_idx;
+ hash_value->hash_value = txr->hn_tx_idx;
if (m_head->m_flags & M_VLANTAG) {
ndis_8021q_info *rppi_vlan_info;
@@ -1308,7 +1308,9 @@ hv_m_append(struct mbuf *m0, int len, c_caddr_t cp)
*/
int
netvsc_recv(struct hv_vmbus_channel *chan, netvsc_packet *packet,
- rndis_tcp_ip_csum_info *csum_info)
+ const rndis_tcp_ip_csum_info *csum_info,
+ const struct rndis_hash_info *hash_info,
+ const struct rndis_hash_value *hash_value)
{
struct hn_rx_ring *rxr = chan->hv_chan_rxr;
struct ifnet *ifp = rxr->hn_ifp;
@@ -1417,7 +1419,6 @@ netvsc_recv(struct hv_vmbus_channel *chan, netvsc_packet *packet,
CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
m_new->m_pkthdr.csum_data = 0xffff;
}
- /* Rely on SW csum verification though... */
do_lro = 1;
} else if (pr == IPPROTO_UDP) {
if (do_csum &&
@@ -1444,8 +1445,50 @@ skip:
m_new->m_flags |= M_VLANTAG;
}
- m_new->m_pkthdr.flowid = rxr->hn_rx_idx;
- M_HASHTYPE_SET(m_new, M_HASHTYPE_OPAQUE);
+ if (hash_info != NULL && hash_value != NULL) {
+ int hash_type = M_HASHTYPE_OPAQUE;
+
+ rxr->hn_rss_pkts++;
+ m_new->m_pkthdr.flowid = hash_value->hash_value;
+ if ((hash_info->hash_info & NDIS_HASH_FUNCTION_MASK) ==
+ NDIS_HASH_FUNCTION_TOEPLITZ) {
+ uint32_t type =
+ (hash_info->hash_info & NDIS_HASH_TYPE_MASK);
+
+ switch (type) {
+ case NDIS_HASH_IPV4:
+ hash_type = M_HASHTYPE_RSS_IPV4;
+ break;
+
+ case NDIS_HASH_TCP_IPV4:
+ hash_type = M_HASHTYPE_RSS_TCP_IPV4;
+ break;
+
+ case NDIS_HASH_IPV6:
+ hash_type = M_HASHTYPE_RSS_IPV6;
+ break;
+
+ case NDIS_HASH_IPV6_EX:
+ hash_type = M_HASHTYPE_RSS_IPV6_EX;
+ break;
+
+ case NDIS_HASH_TCP_IPV6:
+ hash_type = M_HASHTYPE_RSS_TCP_IPV6;
+ break;
+
+ case NDIS_HASH_TCP_IPV6_EX:
+ hash_type = M_HASHTYPE_RSS_TCP_IPV6_EX;
+ break;
+ }
+ }
+ M_HASHTYPE_SET(m_new, hash_type);
+ } else {
+ if (hash_value != NULL)
+ m_new->m_pkthdr.flowid = hash_value->hash_value;
+ else
+ m_new->m_pkthdr.flowid = rxr->hn_rx_idx;
+ M_HASHTYPE_SET(m_new, M_HASHTYPE_OPAQUE);
+ }
/*
* Note: Moved RX completion back to hv_nv_on_receive() so all
@@ -2265,6 +2308,11 @@ hn_create_rx_data(struct hn_softc *sc, int ring_cnt)
SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
OID_AUTO, "packets", CTLFLAG_RW,
&rxr->hn_pkts, "# of packets received");
+ SYSCTL_ADD_ULONG(ctx,
+ SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
+ OID_AUTO, "rss_pkts", CTLFLAG_RW,
+ &rxr->hn_rss_pkts,
+ "# of packets w/ RSS info received");
}
}
}
diff --git a/sys/dev/hyperv/netvsc/hv_rndis.h b/sys/dev/hyperv/netvsc/hv_rndis.h
index 8ece1d0..b27579d 100644
--- a/sys/dev/hyperv/netvsc/hv_rndis.h
+++ b/sys/dev/hyperv/netvsc/hv_rndis.h
@@ -617,6 +617,7 @@ typedef enum ndis_per_pkt_infotype_ {
} ndis_per_pkt_infotype;
#define nbl_hash_value pkt_cancel_id
+#define nbl_hash_info original_netbuf_list
typedef struct ndis_8021q_info_ {
union {
@@ -630,10 +631,6 @@ typedef struct ndis_8021q_info_ {
} u1;
} ndis_8021q_info;
-struct ndis_hash_info {
- uint32_t hash;
-} __packed;
-
struct rndis_object_header {
uint8_t type;
uint8_t revision;
@@ -694,6 +691,28 @@ typedef struct rndis_tcp_ip_csum_info_ {
};
} rndis_tcp_ip_csum_info;
+struct rndis_hash_value {
+ uint32_t hash_value;
+} __packed;
+
+struct rndis_hash_info {
+ uint32_t hash_info;
+} __packed;
+
+#define NDIS_HASH_FUNCTION_MASK 0x000000FF /* see hash function */
+#define NDIS_HASH_TYPE_MASK 0x00FFFF00 /* see hash type */
+
+/* hash function */
+#define NDIS_HASH_FUNCTION_TOEPLITZ 0x00000001
+
+/* hash type */
+#define NDIS_HASH_IPV4 0x00000100
+#define NDIS_HASH_TCP_IPV4 0x00000200
+#define NDIS_HASH_IPV6 0x00000400
+#define NDIS_HASH_IPV6_EX 0x00000800
+#define NDIS_HASH_TCP_IPV6 0x00001000
+#define NDIS_HASH_TCP_IPV6_EX 0x00002000
+
typedef struct rndis_tcp_tso_info_ {
union {
struct {
@@ -727,8 +746,8 @@ typedef struct rndis_tcp_tso_info_ {
};
} rndis_tcp_tso_info;
-#define RNDIS_HASH_PPI_SIZE (sizeof(rndis_per_packet_info) + \
- sizeof(struct ndis_hash_info))
+#define RNDIS_HASHVAL_PPI_SIZE (sizeof(rndis_per_packet_info) + \
+ sizeof(struct rndis_hash_value))
#define RNDIS_VLAN_PPI_SIZE (sizeof(rndis_per_packet_info) + \
sizeof(ndis_8021q_info))
@@ -1066,7 +1085,9 @@ typedef struct rndismp_rx_bufs_info_ {
struct hv_vmbus_channel;
int netvsc_recv(struct hv_vmbus_channel *chan,
- netvsc_packet *packet, rndis_tcp_ip_csum_info *csum_info);
+ netvsc_packet *packet, const rndis_tcp_ip_csum_info *csum_info,
+ const struct rndis_hash_info *hash_info,
+ const struct rndis_hash_value *hash_value);
void netvsc_channel_rollup(struct hv_vmbus_channel *chan);
void* hv_set_rppi_data(rndis_msg *rndis_mesg,
diff --git a/sys/dev/hyperv/netvsc/hv_rndis_filter.c b/sys/dev/hyperv/netvsc/hv_rndis_filter.c
index b320591..8e95510 100644
--- a/sys/dev/hyperv/netvsc/hv_rndis_filter.c
+++ b/sys/dev/hyperv/netvsc/hv_rndis_filter.c
@@ -50,6 +50,22 @@ __FBSDID("$FreeBSD$");
#include "hv_rndis.h"
#include "hv_rndis_filter.h"
+struct hv_rf_recvinfo {
+ const ndis_8021q_info *vlan_info;
+ const rndis_tcp_ip_csum_info *csum_info;
+ const struct rndis_hash_info *hash_info;
+ const struct rndis_hash_value *hash_value;
+};
+
+#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)
/*
* Forward declarations
@@ -434,6 +450,84 @@ hv_rf_receive_indicate_status(rndis_device *device, rndis_msg *response)
}
}
+static int
+hv_rf_find_recvinfo(const rndis_packet *rpkt, struct hv_rf_recvinfo *info)
+{
+ const rndis_per_packet_info *ppi;
+ uint32_t mask, len;
+
+ info->vlan_info = NULL;
+ info->csum_info = NULL;
+ info->hash_info = NULL;
+ info->hash_value = NULL;
+
+ if (rpkt->per_pkt_info_offset == 0)
+ return 0;
+
+ ppi = (const rndis_per_packet_info *)
+ ((const uint8_t *)rpkt + rpkt->per_pkt_info_offset);
+ len = rpkt->per_pkt_info_length;
+ mask = 0;
+
+ while (len != 0) {
+ const void *ppi_dptr;
+ uint32_t ppi_dlen;
+
+ if (__predict_false(ppi->size < ppi->per_packet_info_offset))
+ return EINVAL;
+ ppi_dlen = ppi->size - ppi->per_packet_info_offset;
+ ppi_dptr = (const uint8_t *)ppi + ppi->per_packet_info_offset;
+
+ switch (ppi->type) {
+ case ieee_8021q_info:
+ if (__predict_false(ppi_dlen < sizeof(ndis_8021q_info)))
+ return EINVAL;
+ info->vlan_info = ppi_dptr;
+ mask |= HV_RF_RECVINFO_VLAN;
+ break;
+
+ case tcpip_chksum_info:
+ if (__predict_false(ppi_dlen <
+ sizeof(rndis_tcp_ip_csum_info)))
+ return EINVAL;
+ info->csum_info = ppi_dptr;
+ mask |= HV_RF_RECVINFO_CSUM;
+ break;
+
+ case nbl_hash_value:
+ if (__predict_false(ppi_dlen <
+ sizeof(struct rndis_hash_value)))
+ return EINVAL;
+ info->hash_value = ppi_dptr;
+ mask |= HV_RF_RECVINFO_HASHVAL;
+ break;
+
+ case nbl_hash_info:
+ if (__predict_false(ppi_dlen <
+ sizeof(struct rndis_hash_info)))
+ return EINVAL;
+ info->hash_info = ppi_dptr;
+ mask |= HV_RF_RECVINFO_HASHINF;
+ break;
+
+ default:
+ goto skip;
+ }
+
+ if (mask == HV_RF_RECVINFO_ALL) {
+ /* All found; done */
+ break;
+ }
+skip:
+ if (__predict_false(len < ppi->size))
+ return EINVAL;
+ len -= ppi->size;
+ ppi = (const rndis_per_packet_info *)
+ ((const uint8_t *)ppi + ppi->size);
+ }
+ return 0;
+}
+
/*
* RNDIS filter receive data
*/
@@ -442,10 +536,9 @@ hv_rf_receive_data(rndis_device *device, rndis_msg *message,
struct hv_vmbus_channel *chan, netvsc_packet *pkt)
{
rndis_packet *rndis_pkt;
- ndis_8021q_info *rppi_vlan_info;
uint32_t data_offset;
- rndis_tcp_ip_csum_info *csum_info = NULL;
device_t dev = device->net_dev->dev->device;
+ struct hv_rf_recvinfo info;
rndis_pkt = &message->msg.packet;
@@ -469,15 +562,18 @@ hv_rf_receive_data(rndis_device *device, rndis_msg *message,
pkt->tot_data_buf_len = rndis_pkt->data_length;
pkt->data = (void *)((unsigned long)pkt->data + data_offset);
- rppi_vlan_info = hv_get_ppi_data(rndis_pkt, ieee_8021q_info);
- if (rppi_vlan_info) {
- pkt->vlan_tci = rppi_vlan_info->u1.s1.vlan_id;
- } else {
- pkt->vlan_tci = 0;
+ if (hv_rf_find_recvinfo(rndis_pkt, &info)) {
+ pkt->status = nvsp_status_failure;
+ device_printf(dev, "recvinfo parsing failed\n");
+ return;
}
- csum_info = hv_get_ppi_data(rndis_pkt, tcpip_chksum_info);
- netvsc_recv(chan, pkt, csum_info);
+ if (info.vlan_info != NULL)
+ pkt->vlan_tci = info.vlan_info->u1.s1.vlan_id;
+ else
+ pkt->vlan_tci = 0;
+
+ netvsc_recv(chan, pkt, info.csum_info, info.hash_info, info.hash_value);
}
/*
OpenPOWER on IntegriCloud