svn commit: r249325 - user/bryanv/vtnetmq/sys/dev/virtio/network

Bryan Venteicher bryanv at FreeBSD.org
Wed Apr 10 06:21:40 UTC 2013


Author: bryanv
Date: Wed Apr 10 06:21:39 2013
New Revision: 249325
URL: http://svnweb.freebsd.org/changeset/base/249325

Log:
  Commit development snapshot of the multiqueue driver
  
  This commit includes various (mostly minor) changes:
    - Support for instance specific and global tunables
    - Fix a swap argument bug that broke TSO
    - Initial support for the VIRTIO_NET_F_CTRL_MAC_ADDR feature
    - Shuffle some structure around to be more cache line friendly

Modified:
  user/bryanv/vtnetmq/sys/dev/virtio/network/if_vtnet.c
  user/bryanv/vtnetmq/sys/dev/virtio/network/if_vtnetvar.h
  user/bryanv/vtnetmq/sys/dev/virtio/network/virtio_net.h

Modified: user/bryanv/vtnetmq/sys/dev/virtio/network/if_vtnet.c
==============================================================================
--- user/bryanv/vtnetmq/sys/dev/virtio/network/if_vtnet.c	Wed Apr 10 05:59:07 2013	(r249324)
+++ user/bryanv/vtnetmq/sys/dev/virtio/network/if_vtnet.c	Wed Apr 10 06:21:39 2013	(r249325)
@@ -175,6 +175,7 @@ static void	vtnet_init(void *);
 static void	vtnet_free_ctrl_vq(struct vtnet_softc *);
 static void	vtnet_exec_ctrl_cmd(struct vtnet_softc *, void *,
 		    struct sglist *, int, int);
+static int	vtnet_ctrl_mac_cmd(struct vtnet_softc *, uint8_t *);
 static int	vtnet_ctrl_mq_cmd(struct vtnet_softc *, uint16_t);
 static int	vtnet_ctrl_rx_cmd(struct vtnet_softc *, int, int);
 static int	vtnet_set_promisc(struct vtnet_softc *, int);
@@ -214,6 +215,8 @@ static void	vtnet_disable_rx_interrupts(
 static void	vtnet_disable_tx_interrupts(struct vtnet_softc *);
 static void	vtnet_disable_interrupts(struct vtnet_softc *);
 
+static int	vtnet_tunable_int(struct vtnet_softc *, const char *, int);
+
 /* Tunables. */
 static int vtnet_csum_disable = 0;
 TUNABLE_INT("hw.vtnet.csum_disable", &vtnet_csum_disable);
@@ -222,10 +225,10 @@ TUNABLE_INT("hw.vtnet.tso_disable", &vtn
 static int vtnet_lro_disable = 0;
 TUNABLE_INT("hw.vtnet.lro_disable", &vtnet_lro_disable);
 static int vtnet_mq_disable = 0;
-TUNABLE_INT("hw.vtnet.mq_dislabe", &vtnet_mq_disable);
-static int vtnet_mq_max_queues = 0;
-TUNABLE_INT("hw.vtnet.mq_max_queues", &vtnet_mq_max_queues);
-static int vtnet_rx_process_limit = 256;
+TUNABLE_INT("hw.vtnet.mq_disable", &vtnet_mq_disable);
+static int vtnet_mq_max_pairs = 0;
+TUNABLE_INT("hw.vtnet.mq_max_pairs", &vtnet_mq_max_pairs);
+static int vtnet_rx_process_limit = 512;
 TUNABLE_INT("hw.vtnet.rx_process_limit", &vtnet_rx_process_limit);
 
 /*
@@ -519,13 +522,15 @@ vtnet_negotiate_features(struct vtnet_so
 	 * TSO and LRO are only available when their corresponding checksum
 	 * offload feature is also negotiated.
 	 */
-	if (vtnet_csum_disable)
+	if (vtnet_tunable_int(sc, "csum_disable", vtnet_csum_disable)) {
 		mask |= VIRTIO_NET_F_CSUM | VIRTIO_NET_F_GUEST_CSUM;
-	if (vtnet_csum_disable || vtnet_tso_disable)
+		mask |= VTNET_TSO_FEATURES | VTNET_LRO_FEATURES;
+	}
+	if (vtnet_tunable_int(sc, "tso_disable", vtnet_tso_disable))
 		mask |= VTNET_TSO_FEATURES;
-	if (vtnet_csum_disable || vtnet_lro_disable)
+	if (vtnet_tunable_int(sc, "lro_disable", vtnet_lro_disable))
 		mask |= VTNET_LRO_FEATURES;
-	if (vtnet_mq_disable)
+	if (vtnet_tunable_int(sc, "mq_disable", vtnet_mq_disable))
 		mask |= VIRTIO_NET_F_MQ;
 
 	features = VTNET_FEATURES & ~mask;
@@ -559,12 +564,17 @@ static void
 vtnet_setup_features(struct vtnet_softc *sc)
 {
 	device_t dev;
-	int max_pairs;
+	int max_pairs, max;
 
 	dev = sc->vtnet_dev;
 
 	vtnet_negotiate_features(sc);
 
+	if (virtio_with_feature(dev, VIRTIO_NET_F_MAC)) {
+		/* This feature should always be negotiated. */
+		sc->vtnet_flags |= VTNET_FLAG_MAC;
+	}
+
 	if (virtio_with_feature(dev, VIRTIO_NET_F_MRG_RXBUF)) {
 		sc->vtnet_flags |= VTNET_FLAG_MRG_RXBUFS;
 		sc->vtnet_hdr_size = sizeof(struct virtio_net_hdr_mrg_rxbuf);
@@ -578,6 +588,8 @@ vtnet_setup_features(struct vtnet_softc 
 			sc->vtnet_flags |= VTNET_FLAG_CTRL_RX;
 		if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_VLAN))
 			sc->vtnet_flags |= VTNET_FLAG_VLAN_FILTER;
+		if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_MAC_ADDR))
+			sc->vtnet_flags |= VTNET_FLAG_CTRL_MAC;
 	}
 
 	if (virtio_with_feature(dev, VIRTIO_NET_F_MQ) &&
@@ -592,16 +604,17 @@ vtnet_setup_features(struct vtnet_softc 
 
 	if (max_pairs > 1) {
 		/*
-		 * Limit the maximum number of queue pairs to the number
-		 * of CPUs or the configured maximum. The actual number
-		 * of queues that get used may be less.
+		 * Limit the maximum number of queue pairs to the number of
+		 * CPUs or the configured maximum. The actual number of
+		 * queues that get used may be less.
 		 */
+		max = vtnet_tunable_int(sc, "mq_max_pairs", vtnet_mq_max_pairs);
+		if (max > 0 && max_pairs > max)
+			max_pairs = max;
 		if (max_pairs > mp_ncpus)
 			max_pairs = mp_ncpus;
 		if (max_pairs > VTNET_MAX_QUEUE_PAIRS)
 			max_pairs = VTNET_MAX_QUEUE_PAIRS;
-		if (vtnet_mq_max_queues != 0)
-			max_pairs = vtnet_mq_max_queues;
 		if (max_pairs > 1)
 			sc->vtnet_flags |= VTNET_FLAG_MULTIQ;
 	}
@@ -622,7 +635,6 @@ vtnet_init_rxq(struct vtnet_softc *sc, i
 
 	rxq->vtnrx_sc = sc;
 	rxq->vtnrx_id = id;
-	rxq->vtnrx_process_limit = vtnet_rx_process_limit;
 
 	TASK_INIT(&rxq->vtnrx_intrtask, 0, vtnet_rxq_tq_intr, rxq);
 	rxq->vtnrx_tq = taskqueue_create(rxq->vtnrx_name, M_NOWAIT,
@@ -708,10 +720,12 @@ vtnet_destroy_txq(struct vtnet_txq *txq)
 	txq->vtntx_sc = NULL;
 	txq->vtntx_id = -1;
 
+#ifndef VTNET_LEGACY_TX
 	if (txq->vtntx_br != NULL) {
 		buf_ring_free(txq->vtntx_br, M_DEVBUF);
 		txq->vtntx_br = NULL;
 	}
+#endif
 
 	if (mtx_initialized(&txq->vtntx_mtx) != 0)
 		mtx_destroy(&txq->vtntx_mtx);
@@ -1035,35 +1049,15 @@ vtnet_ioctl(struct ifnet *ifp, u_long cm
 		VTNET_CORE_LOCK(sc);
 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
 
-		if (mask & IFCAP_TXCSUM) {
+		if (mask & IFCAP_TXCSUM)
 			ifp->if_capenable ^= IFCAP_TXCSUM;
-			if (ifp->if_capenable & IFCAP_TXCSUM)
-				ifp->if_hwassist |= VTNET_CSUM_OFFLOAD;
-			else
-				ifp->if_hwassist &= ~VTNET_CSUM_OFFLOAD;
-		}
-
-		if (mask & IFCAP_TXCSUM_IPV6) {
+		if (mask & IFCAP_TXCSUM_IPV6)
 			ifp->if_capenable ^= IFCAP_TXCSUM_IPV6;
-			if (ifp->if_capenable & IFCAP_TXCSUM_IPV6)
-				ifp->if_hwassist |= VTNET_CSUM_OFFLOAD_IPV6;
-			else
-				ifp->if_hwassist &= ~VTNET_CSUM_OFFLOAD_IPV6;
-		}
-
 		if (mask & IFCAP_TSO) {
 			if (mask & IFCAP_TSO4)
 				ifp->if_capenable ^= IFCAP_TSO4;
 			if (mask & IFCAP_TSO6)
 				ifp->if_capenable ^= IFCAP_TSO6;
-			/*
-			 * Set if either is enabled. The CSUM_TSO_IPV6 flag is
-			 * currently commented out.
-			 */
-			if (ifp->if_capenable & IFCAP_TSO)
-				ifp->if_hwassist |= CSUM_TSO;
-			else
-				ifp->if_hwassist &= ~CSUM_TSO;
 		}
 
 		if (mask & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6 | IFCAP_LRO |
@@ -1423,10 +1417,10 @@ vtnet_rxq_csum_by_offset(struct vtnet_rx
 	}
 
 	/*
-	 * Use the offset to determine the appropriate CSUM_* flags. This
-	 * is a bit dirty, but we can get by with it since the checksum
-	 * offsets happen to be different. We assume the host host does
-	 * not do IPv4 header checksum offloading.
+	 * Use the offset to determine the appropriate CSUM_* flags. This is
+	 * a bit dirty, but we can get by with it since the checksum offsets
+	 * happen to be different. We assume the host host does not do IPv4
+	 * header checksum offloading.
 	 */
 	switch (hdr->csum_offset) {
 	case offsetof(struct udphdr, uh_sum):
@@ -1648,7 +1642,7 @@ vtnet_rxq_input(struct vtnet_rxq *rxq, s
 	m->m_flags |= M_FLOWID;
 
 	/*
-	 * BVM: FreeBSD does not have the UNNECESSARY and PARTIAL checksum
+	 * BMV: FreeBSD does not have the UNNECESSARY and PARTIAL checksum
 	 * distinction that Linux does. Need to reevaluate if performing
 	 * offloading for the NEEDS_CSUM case is really appropriate.
 	 */
@@ -1686,7 +1680,7 @@ vtnet_rxq_eof(struct vtnet_rxq *rxq)
 
 	hdr = &lhdr;
 	deq = 0;
-	count = rxq->vtnrx_process_limit;
+	count = sc->vtnet_rx_process_limit;
 
 	VTNET_RXQ_LOCK_ASSERT(rxq);
 
@@ -1962,7 +1956,7 @@ vtnet_txq_offload(struct vtnet_txq *txq,
 	sc = txq->vtntx_sc;
 	flags = m->m_pkthdr.csum_flags;
 
-	error = vtnet_txq_offload_ctx(txq, m, &etype, &csum_start, &proto);
+	error = vtnet_txq_offload_ctx(txq, m, &etype, &proto, &csum_start);
 	if (error)
 		goto drop;
 
@@ -2695,7 +2689,7 @@ vtnet_virtio_reinit(struct vtnet_softc *
 	if (ifp->if_capabilities & _RXCSUM_IPV46) {
 		/*
 		 * We require both IPv4 and IPv6 offloading to be enabled
-		 * inorder to negotiated it: VirtIO does not distinguish
+		 * in order to negotiated it: VirtIO does not distinguish
 		 * between the two.
 		 *
 		 * BMV: What about when INET and/or INET6 is not defined?
@@ -2736,7 +2730,6 @@ vtnet_init_rx_filters(struct vtnet_softc
 		vtnet_rx_filter_mac(sc);
 	}
 
-	/* Restore filtered VLANs. */
 	if (ifp->if_capenable & IFCAP_VLAN_HWFILTER)
 		vtnet_rx_filter_vlan(sc);
 }
@@ -2746,11 +2739,21 @@ vtnet_init_rx_queues(struct vtnet_softc 
 {
 	device_t dev;
 	struct vtnet_rxq *rxq;
-	int i, clsize, error;
+	int i, limit, clsize, error;
 
 	dev = sc->vtnet_dev;
 
 	/*
+	 * Assume the same limit is appropriate for all the Rx queues.
+	 * We may later want to scale this by each virtqueue's size.
+	 */
+	limit = vtnet_tunable_int(sc, "rx_process_limit",
+	    vtnet_rx_process_limit);
+	if (limit < 0)
+		limit = INT_MAX;
+	sc->vtnet_rx_process_limit = limit;
+
+	/*
 	 * Use the new cluster size if one has been set (via a MTU
 	 * change). Otherwise, use the standard 2K clusters.
 	 *
@@ -2974,6 +2977,32 @@ vtnet_exec_ctrl_cmd(struct vtnet_softc *
 }
 
 static int
+vtnet_ctrl_mac_cmd(struct vtnet_softc *sc, uint8_t *hwaddr)
+{
+	struct virtio_net_ctrl_hdr hdr;
+	struct sglist_seg segs[3];
+	struct sglist sg;
+	uint8_t ack;
+	int error;
+
+	hdr.class = VIRTIO_NET_CTRL_MAC;
+	hdr.cmd = VIRTIO_NET_CTRL_MAC_ADDR_SET;
+	ack = VIRTIO_NET_ERR;
+
+	sglist_init(&sg, 3, segs);
+	error = 0;
+	error |= sglist_append(&sg, &hdr, sizeof(struct virtio_net_ctrl_hdr));
+	error |= sglist_append(&sg, hwaddr, ETHER_ADDR_LEN);
+	error |= sglist_append(&sg, &ack, sizeof(uint8_t));
+	KASSERT(error == 0 && sg.sg_nseg == 3,
+	    ("%s: error %d adding set MAC msg to sglist", __func__, error));
+
+	vtnet_exec_ctrl_cmd(sc, &ack, &sg, sg.sg_nseg - 1, 1);
+
+	return (ack == VIRTIO_NET_OK ? 0 : EIO);
+}
+
+static int
 vtnet_ctrl_mq_cmd(struct vtnet_softc *sc, uint16_t npairs)
 {
 	struct sglist_seg segs[3];
@@ -2987,9 +3016,6 @@ vtnet_ctrl_mq_cmd(struct vtnet_softc *sc
 	} s;
 	int error;
 
-	if ((sc->vtnet_flags & VTNET_FLAG_CTRL_VQ) == 0)
-		return (ENOTSUP);
-
 	s.hdr.class = VIRTIO_NET_CTRL_MQ;
 	s.hdr.cmd = VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET;
 	s.mq.virtqueue_pairs = npairs;
@@ -3204,12 +3230,10 @@ vtnet_rx_filter_mac(struct vtnet_softc *
 		if_printf(ifp, "error setting host MAC filter table\n");
 
 out:
-	if (promisc)
-		if (vtnet_set_promisc(sc, 1) != 0)
-			if_printf(ifp, "cannot enable promiscuous mode\n");
-	if (allmulti)
-		if (vtnet_set_allmulti(sc, 1) != 0)
-			if_printf(ifp, "cannot enable all-multicast mode\n");
+	if (promisc && vtnet_set_promisc(sc, 1) != 0)
+		if_printf(ifp, "cannot enable promiscuous mode\n");
+	if (allmulti && vtnet_set_allmulti(sc, 1) != 0)
+		if_printf(ifp, "cannot enable all-multicast mode\n");
 }
 
 static int
@@ -3286,11 +3310,6 @@ vtnet_update_vlan_filter(struct vtnet_so
 
 	VTNET_CORE_LOCK(sc);
 
-	/*
-	 * Update the in-memory table. We must keep the table current with
-	 * configured VLANs even if HW filtering is disabled, since it could
-	 * be enabled later.
-	 */
 	if (add)
 		sc->vtnet_vlan_filter[idx] |= (1 << bit);
 	else
@@ -3407,8 +3426,14 @@ vtnet_set_hwaddr(struct vtnet_softc *sc)
 
 	dev = sc->vtnet_dev;
 
-	virtio_write_device_config(dev, offsetof(struct virtio_net_config, mac),
-	    sc->vtnet_hwaddr, ETHER_ADDR_LEN);
+	if (sc->vtnet_flags & VTNET_FLAG_CTRL_MAC) {
+		if (vtnet_ctrl_mac_cmd(sc, sc->vtnet_hwaddr) != 0)
+			device_printf(dev, "unable to set MAC address\n");
+	} else if (sc->vtnet_flags & VTNET_FLAG_MAC) {
+		virtio_write_device_config(dev,
+		    offsetof(struct virtio_net_config, mac),
+		    sc->vtnet_hwaddr, ETHER_ADDR_LEN);
+	}
 }
 
 static void
@@ -3418,7 +3443,7 @@ vtnet_get_hwaddr(struct vtnet_softc *sc)
 
 	dev = sc->vtnet_dev;
 
-	if (virtio_with_feature(dev, VIRTIO_NET_F_MAC) == 0) {
+	if ((sc->vtnet_flags & VTNET_FLAG_MAC) == 0) {
 		/*
 		 * Generate a random locally administered unicast address.
 		 *
@@ -3707,3 +3732,15 @@ vtnet_disable_interrupts(struct vtnet_so
 	vtnet_disable_rx_interrupts(sc);
 	vtnet_disable_tx_interrupts(sc);
 }
+
+static int
+vtnet_tunable_int(struct vtnet_softc *sc, const char *knob, int def)
+{
+	char path[64];
+
+	snprintf(path, sizeof(path),
+	    "hw.vtnet.%d.%s", device_get_unit(sc->vtnet_dev), knob);
+	TUNABLE_INT_FETCH(path, &def);
+
+	return (def);
+}

Modified: user/bryanv/vtnetmq/sys/dev/virtio/network/if_vtnetvar.h
==============================================================================
--- user/bryanv/vtnetmq/sys/dev/virtio/network/if_vtnetvar.h	Wed Apr 10 05:59:07 2013	(r249324)
+++ user/bryanv/vtnetmq/sys/dev/virtio/network/if_vtnetvar.h	Wed Apr 10 06:21:39 2013	(r249325)
@@ -100,7 +100,9 @@ struct vtnet_txq {
 	struct mtx		 vtntx_mtx;
 	struct vtnet_softc	*vtntx_sc;
 	struct virtqueue	*vtntx_vq;
+#ifndef VTNET_LEGACY_TX
 	struct buf_ring		*vtntx_br;
+#endif
 	int			 vtntx_id;
 	int			 vtntx_watchdog;
 	struct vtnet_txq_stats	 vtntx_stats;
@@ -123,43 +125,45 @@ struct vtnet_txq {
 struct vtnet_softc {
 	device_t		 vtnet_dev;
 	struct ifnet		*vtnet_ifp;
-	struct mtx		 vtnet_mtx;
+	struct vtnet_rxq	*vtnet_rxqs;
+	struct vtnet_txq	*vtnet_txqs;
 
 	uint32_t		 vtnet_flags;
 #define VTNET_FLAG_SUSPENDED	 0x0001
-#define VTNET_FLAG_CTRL_VQ	 0x0002
-#define VTNET_FLAG_CTRL_RX	 0x0004
-#define VTNET_FLAG_VLAN_FILTER	 0x0008
-#define VTNET_FLAG_TSO_ECN	 0x0010
-#define VTNET_FLAG_MRG_RXBUFS	 0x0020
-#define VTNET_FLAG_LRO_NOMRG	 0x0040
-#define VTNET_FLAG_MULTIQ	 0x0080
-
-	struct vtnet_rxq	*vtnet_rxqs;
-	struct vtnet_txq	*vtnet_txqs;
-	struct virtqueue	*vtnet_ctrl_vq;
+#define VTNET_FLAG_MAC		 0x0002
+#define VTNET_FLAG_CTRL_VQ	 0x0004
+#define VTNET_FLAG_CTRL_RX	 0x0008
+#define VTNET_FLAG_CTRL_MAC	 0x0010
+#define VTNET_FLAG_VLAN_FILTER	 0x0020
+#define VTNET_FLAG_TSO_ECN	 0x0040
+#define VTNET_FLAG_MRG_RXBUFS	 0x0080
+#define VTNET_FLAG_LRO_NOMRG	 0x0100
+#define VTNET_FLAG_MULTIQ	 0x0200
 
-	int			 vtnet_hdr_size;
 	int			 vtnet_link_active;
+	int			 vtnet_hdr_size;
+	int			 vtnet_rx_process_limit;
 	int			 vtnet_rx_nmbufs;
 	int			 vtnet_rx_clsize;
 	int			 vtnet_rx_new_clsize;
 	int			 vtnet_if_flags;
 	int			 vtnet_act_vq_pairs;
 	int			 vtnet_max_vq_pairs;
-	uint64_t		 vtnet_features;
-
-	struct vtnet_statistics	 vtnet_stats;
-	struct callout		 vtnet_tick_ch;
 
+	struct virtqueue	*vtnet_ctrl_vq;
 	struct vtnet_mac_filter	*vtnet_mac_filter;
 	uint32_t		*vtnet_vlan_filter;
+
+	uint64_t		 vtnet_features;
+	struct vtnet_statistics	 vtnet_stats;
+	struct callout		 vtnet_tick_ch;
 	struct ifmedia		 vtnet_media;
 	eventhandler_tag	 vtnet_vlan_attach;
 	eventhandler_tag	 vtnet_vlan_detach;
 
-	char			 vtnet_hwaddr[ETHER_ADDR_LEN];
+	struct mtx		 vtnet_mtx;
 	char			 vtnet_mtx_name[16];
+	char			 vtnet_hwaddr[ETHER_ADDR_LEN];
 };
 
 /*
@@ -181,8 +185,8 @@ struct vtnet_softc {
 #define VTNET_MEDIATYPE		 (IFM_ETHER | IFM_10G_T | IFM_FDX)
 
 /*
- * Number of words to allocate for the VLAN shadow table. Each possible
- * VLAN gets one bit.
+ * Number of words to allocate for the VLAN shadow table. There is one
+ * bit for each VLAN.
  */
 #define VTNET_VLAN_FILTER_NWORDS	(4096 / 32)
 
@@ -263,7 +267,6 @@ CTASSERT(sizeof(struct vtnet_mac_filter)
      VIRTIO_NET_F_GUEST_TSO6		| \
      VIRTIO_NET_F_GUEST_ECN		| \
      VIRTIO_NET_F_MRG_RXBUF		| \
-     VIRTIO_NET_F_MQ			| \
      VIRTIO_RING_F_INDIRECT_DESC)
 
 /*

Modified: user/bryanv/vtnetmq/sys/dev/virtio/network/virtio_net.h
==============================================================================
--- user/bryanv/vtnetmq/sys/dev/virtio/network/virtio_net.h	Wed Apr 10 05:59:07 2013	(r249324)
+++ user/bryanv/vtnetmq/sys/dev/virtio/network/virtio_net.h	Wed Apr 10 06:21:39 2013	(r249325)
@@ -51,7 +51,8 @@
 #define VIRTIO_NET_F_CTRL_VLAN	0x80000 /* Control channel VLAN filtering */
 #define VIRTIO_NET_F_CTRL_RX_EXTRA 0x100000 /* Extra RX mode control support */
 #define VIRTIO_NET_F_GUEST_ANNOUNCE 0x200000 /* Announce device on network */
-#define VIRTIO_NET_F_MQ		0x400000
+#define VIRTIO_NET_F_MQ		0x400000 /* Device supports RFS */
+#define VIRTIO_NET_F_CTRL_MAC_ADDR 0x800000 /* Set MAC address */
 
 #define VIRTIO_NET_S_LINK_UP	1	/* Link is up */
 
@@ -142,6 +143,10 @@ typedef uint8_t virtio_net_ctrl_ack;
  * first sg list contains unicast addresses, the second is for multicast.
  * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature
  * is available.
+ *
+ * The ADDR_SET command requests one out scatterlist, it contains a
+ * 6 bytes MAC address. This functionality is present if the
+ * VIRTIO_NET_F_CTRL_MAC_ADDR feature is available.
  */
 struct virtio_net_ctrl_mac {
 	uint32_t	entries;
@@ -150,6 +155,7 @@ struct virtio_net_ctrl_mac {
 
 #define VIRTIO_NET_CTRL_MAC	1
 #define VIRTIO_NET_CTRL_MAC_TABLE_SET	0
+#define VIRTIO_NET_CTRL_MAC_ADDR_SET	1
 
 /*
  * Control VLAN filtering


More information about the svn-src-user mailing list