aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSepherosa Ziehau <sephe@FreeBSD.org>2016-10-24 05:10:35 +0000
committerSepherosa Ziehau <sephe@FreeBSD.org>2016-10-24 05:10:35 +0000
commit121e98e697eadf913c6df600960a3308ebb36193 (patch)
tree9c8646d9eaa930f6e6ff2fcf648df26a815029e6
parent665bc5ff49c8842c4b1a9915ad1fab9b78a63c55 (diff)
downloadsrc-121e98e697eadf913c6df600960a3308ebb36193.tar.gz
src-121e98e697eadf913c6df600960a3308ebb36193.zip
hyperv/hn: Fix RX filter settings.
MFC after: 1 week Sponsored by: Microsoft Differential Revision: https://reviews.freebsd.org/D8313
Notes
Notes: svn path=/head/; revision=307843
-rw-r--r--sys/dev/hyperv/netvsc/hv_net_vsc.h1
-rw-r--r--sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c114
-rw-r--r--sys/net/rndis.h10
3 files changed, 94 insertions, 31 deletions
diff --git a/sys/dev/hyperv/netvsc/hv_net_vsc.h b/sys/dev/hyperv/netvsc/hv_net_vsc.h
index 2e3e2aacc1e3..014f823d8846 100644
--- a/sys/dev/hyperv/netvsc/hv_net_vsc.h
+++ b/sys/dev/hyperv/netvsc/hv_net_vsc.h
@@ -232,6 +232,7 @@ struct hn_softc {
struct sysctl_oid *hn_rx_sysctl_tree;
struct vmbus_xact_ctx *hn_xact;
uint32_t hn_nvs_ver;
+ uint32_t hn_rx_filter;
struct taskqueue *hn_mgmt_taskq;
struct taskqueue *hn_mgmt_taskq0;
diff --git a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
index c4315166bdaa..5bd46e8e8273 100644
--- a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
+++ b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
@@ -326,6 +326,7 @@ static int hn_tx_conf_int_sysctl(SYSCTL_HANDLER_ARGS);
static int hn_ndis_version_sysctl(SYSCTL_HANDLER_ARGS);
static int hn_caps_sysctl(SYSCTL_HANDLER_ARGS);
static int hn_hwassist_sysctl(SYSCTL_HANDLER_ARGS);
+static int hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS);
static int hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS);
static int hn_rss_ind_sysctl(SYSCTL_HANDLER_ARGS);
static int hn_check_iplen(const struct mbuf *, int);
@@ -367,6 +368,7 @@ static int netvsc_detach(device_t dev);
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_nvs_handle_notify(struct hn_softc *sc,
const struct vmbus_chanpkt_hdr *pkt);
@@ -455,6 +457,43 @@ hn_sendpkt_rndis_chim(struct hn_tx_ring *txr, struct hn_txdesc *txd)
}
static int
+hn_set_rxfilter(struct hn_softc *sc)
+{
+ struct ifnet *ifp = sc->hn_ifp;
+ uint32_t filter;
+ int error = 0;
+
+ HN_LOCK_ASSERT(sc);
+
+ if (ifp->if_flags & IFF_PROMISC) {
+ filter = NDIS_PACKET_TYPE_PROMISCUOUS;
+ } else {
+ filter = NDIS_PACKET_TYPE_DIRECTED;
+ if (ifp->if_flags & IFF_BROADCAST)
+ filter |= NDIS_PACKET_TYPE_BROADCAST;
+#ifdef notyet
+ /*
+ * See the comment in SIOCADDMULTI/SIOCDELMULTI.
+ */
+ /* TODO: support multicast list */
+ if ((ifp->if_flags & IFF_ALLMULTI) ||
+ !TAILQ_EMPTY(&ifp->if_multiaddrs))
+ filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
+#else
+ /* Always enable ALLMULTI */
+ filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
+#endif
+ }
+
+ if (sc->hn_rx_filter != filter) {
+ error = hn_rndis_set_rxfilter(sc, filter);
+ if (!error)
+ sc->hn_rx_filter = filter;
+ }
+ return (error);
+}
+
+static int
hn_get_txswq_depth(const struct hn_tx_ring *txr)
{
@@ -728,6 +767,9 @@ netvsc_attach(device_t dev)
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "hwassist",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
hn_hwassist_sysctl, "A", "hwassist");
+ SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxfilter",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
+ hn_rxfilter_sysctl, "A", "rxfilter");
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_key",
CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
hn_rss_key_sysctl, "IU", "RSS key");
@@ -1840,31 +1882,13 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
}
if (ifp->if_flags & IFF_UP) {
- /*
- * If only the state of the PROMISC flag changed,
- * then just use the 'set promisc mode' command
- * instead of reinitializing the entire NIC. Doing
- * a full re-init means reloading the firmware and
- * waiting for it to start up, which may take a
- * second or two.
- */
-#ifdef notyet
- /* Fixme: Promiscuous mode? */
- if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
- ifp->if_flags & IFF_PROMISC &&
- !(sc->hn_if_flags & IFF_PROMISC)) {
- /* do something here for Hyper-V */
- } else if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
- !(ifp->if_flags & IFF_PROMISC) &&
- sc->hn_if_flags & IFF_PROMISC) {
- /* do something here for Hyper-V */
- } else
-#endif
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ hn_set_rxfilter(sc);
+ else
hn_init_locked(sc);
} else {
- if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
hn_stop(sc);
- }
}
sc->hn_if_flags = ifp->if_flags;
@@ -1922,12 +1946,27 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
case SIOCADDMULTI:
case SIOCDELMULTI:
- /* Always all-multi */
+#ifdef notyet
/*
- * TODO:
- * Enable/disable all-multi according to the emptiness of
- * the mcast address list.
+ * XXX
+ * Multicast uses mutex, while RNDIS RX filter setting
+ * sleeps. We workaround this by always enabling
+ * ALLMULTI. ALLMULTI would actually always be on, even
+ * if we supported the SIOCADDMULTI/SIOCDELMULTI, since
+ * we don't support multicast address list configuration
+ * for this driver.
*/
+ HN_LOCK(sc);
+
+ if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
+ HN_UNLOCK(sc);
+ break;
+ }
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ hn_set_rxfilter(sc);
+
+ HN_UNLOCK(sc);
+#endif
break;
case SIOCSIFMEDIA:
@@ -2035,8 +2074,8 @@ hn_init_locked(struct hn_softc *sc)
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
return;
- /* TODO: add hn_rx_filter */
- hn_rndis_set_rxfilter(sc, NDIS_PACKET_TYPE_PROMISCUOUS);
+ /* Configure RX filter */
+ hn_set_rxfilter(sc);
/* Clear OACTIVE bit. */
atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
@@ -2363,6 +2402,21 @@ hn_hwassist_sysctl(SYSCTL_HANDLER_ARGS)
}
static int
+hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct hn_softc *sc = arg1;
+ char filter_str[128];
+ uint32_t filter;
+
+ HN_LOCK(sc);
+ filter = sc->hn_rx_filter;
+ HN_UNLOCK(sc);
+ snprintf(filter_str, sizeof(filter_str), "%b", filter,
+ NDIS_PACKET_TYPES);
+ return sysctl_handle_string(oidp, filter_str, sizeof(filter_str), req);
+}
+
+static int
hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS)
{
struct hn_softc *sc = arg1;
@@ -3783,6 +3837,7 @@ hn_suspend_data(struct hn_softc *sc)
* Disable RX by clearing RX filter.
*/
hn_rndis_set_rxfilter(sc, 0);
+ sc->hn_rx_filter = 0;
/*
* Give RNDIS enough time to flush all pending data packets.
@@ -3870,9 +3925,8 @@ hn_resume_data(struct hn_softc *sc)
/*
* Re-enable RX.
- * TODO: add hn_rx_filter.
*/
- hn_rndis_set_rxfilter(sc, NDIS_PACKET_TYPE_PROMISCUOUS);
+ hn_set_rxfilter(sc);
/*
* Make sure to clear suspend status on "all" TX rings,
diff --git a/sys/net/rndis.h b/sys/net/rndis.h
index 95edb1c5d7e4..12385e35ca24 100644
--- a/sys/net/rndis.h
+++ b/sys/net/rndis.h
@@ -351,7 +351,7 @@ struct rndis_keepalive_comp {
uint32_t rm_status;
};
-/* packet filter bits used by OID_GEN_CURRENT_PACKET_FILTER */
+/* Packet filter bits used by OID_GEN_CURRENT_PACKET_FILTER */
#define NDIS_PACKET_TYPE_DIRECTED 0x00000001
#define NDIS_PACKET_TYPE_MULTICAST 0x00000002
#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004
@@ -365,6 +365,14 @@ struct rndis_keepalive_comp {
#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00004000
#define NDIS_PACKET_TYPE_MAC_FRAME 0x00008000
+/*
+ * Packet filter description for use with printf(9) %b identifier.
+ */
+#define NDIS_PACKET_TYPES \
+ "\20\1DIRECT\2MULTICAST\3ALLMULTI\4BROADCAST" \
+ "\5SRCROUTE\6PROMISC\7SMT\10ALLLOCAL" \
+ "\11GROUP\12ALLFUNC\13FUNC\14MACFRAME"
+
/* RNDIS offsets */
#define RNDIS_HEADER_OFFSET ((uint32_t)sizeof(struct rndis_msghdr))
#define RNDIS_DATA_OFFSET \