Print this page
MFV: illumos-gate@54b146cf23443d91aef04e2d2a59b7434add3030
7096 vioif should not log to the console on boot, or ever
Reviewed by: Alexander Pyhalov <apyhalov@gmail.com>
Reviewed by: Andy Stormont <astormont@racktopsystems.com>
Reviewed by: Igor Kozhukhov <igor@dilos.org>
Reviewed by: Toomas Soome <tsoome@me.com>
Approved by: Dan McDonald <danmcd@joyent.com>
Author: Joshua M. Clulow <jmc@joyent.com>
OS-76 vioif kernel heap corruption, NULL pointer dereference and mtu problem
port of illumos-3644
    3644 Add virtio-net support into the Illumos
    Reviewed by: Alexey Zaytsev <alexey.zaytsev@gmail.com>
    Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
    Reviewed by: David Hoppner <0xffea@gmail.com>

*** 8,19 **** * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* ! * Copyright 2013 Nexenta Inc. All rights reserved. * Copyright (c) 2014, 2016 by Delphix. All rights reserved. */ /* Based on the NetBSD virtio driver by Minoura Makoto. */ /* * Copyright (c) 2010 Minoura Makoto. --- 8,20 ---- * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* ! * Copyright 2013 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2014, 2016 by Delphix. All rights reserved. + * Copyright 2015 Joyent, Inc. */ /* Based on the NetBSD virtio driver by Minoura Makoto. */ /* * Copyright (c) 2010 Minoura Makoto.
*** 282,291 **** --- 283,299 ---- /* Feature bits. */ unsigned int sc_rx_csum:1; unsigned int sc_tx_csum:1; unsigned int sc_tx_tso4:1; + /* + * For debugging, it is useful to know whether the MAC address we + * are using came from the host (via VIRTIO_NET_CONFIG_MAC) or + * was otherwise generated or set from within the guest. + */ + unsigned int sc_mac_from_host:1; + int sc_mtu; uint8_t sc_mac[ETHERADDRL]; /* * For rx buffers, we keep a pointer array, because the buffers * can be loaned upstream, and we have to repopulate the array with
*** 309,319 **** ulong_t sc_rxloan; /* Copying small packets turns out to be faster then mapping them. */ unsigned long sc_rxcopy_thresh; unsigned long sc_txcopy_thresh; ! /* Some statistic coming here */ uint64_t sc_ipackets; uint64_t sc_opackets; uint64_t sc_rbytes; uint64_t sc_obytes; uint64_t sc_brdcstxmt; --- 317,330 ---- ulong_t sc_rxloan; /* Copying small packets turns out to be faster then mapping them. */ unsigned long sc_rxcopy_thresh; unsigned long sc_txcopy_thresh; ! ! /* ! * Statistics visible through mac: ! */ uint64_t sc_ipackets; uint64_t sc_opackets; uint64_t sc_rbytes; uint64_t sc_obytes; uint64_t sc_brdcstxmt;
*** 322,331 **** --- 333,354 ---- uint64_t sc_multircv; uint64_t sc_norecvbuf; uint64_t sc_notxbuf; uint64_t sc_ierrors; uint64_t sc_oerrors; + + /* + * Internal debugging statistics: + */ + uint64_t sc_rxfail_dma_handle; + uint64_t sc_rxfail_dma_buffer; + uint64_t sc_rxfail_dma_bind; + uint64_t sc_rxfail_chain_undersize; + uint64_t sc_rxfail_no_descriptors; + uint64_t sc_txfail_dma_handle; + uint64_t sc_txfail_dma_bind; + uint64_t sc_txfail_indirect_limit; }; #define ETHER_HEADER_LEN sizeof (struct ether_header) /* MTU + the ethernet header. */
*** 471,501 **** struct vioif_rx_buf *buf = buffer; size_t len; if (ddi_dma_alloc_handle(sc->sc_dev, &vioif_mapped_buf_dma_attr, DDI_DMA_SLEEP, NULL, &buf->rb_mapping.vbm_dmah)) { ! dev_err(sc->sc_dev, CE_WARN, ! "Can't allocate dma handle for rx buffer"); goto exit_handle; } if (ddi_dma_mem_alloc(buf->rb_mapping.vbm_dmah, VIOIF_RX_SIZE + sizeof (struct virtio_net_hdr), &vioif_bufattr, DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &buf->rb_mapping.vbm_buf, &len, &buf->rb_mapping.vbm_acch)) { ! dev_err(sc->sc_dev, CE_WARN, ! "Can't allocate rx buffer"); goto exit_alloc; } ASSERT(len >= VIOIF_RX_SIZE); if (ddi_dma_addr_bind_handle(buf->rb_mapping.vbm_dmah, NULL, buf->rb_mapping.vbm_buf, len, DDI_DMA_READ | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &buf->rb_mapping.vbm_dmac, &buf->rb_mapping.vbm_ncookies)) { ! dev_err(sc->sc_dev, CE_WARN, "Can't bind tx buffer"); ! goto exit_bind; } ASSERT(buf->rb_mapping.vbm_ncookies <= VIOIF_INDIRECT_MAX); --- 494,521 ---- struct vioif_rx_buf *buf = buffer; size_t len; if (ddi_dma_alloc_handle(sc->sc_dev, &vioif_mapped_buf_dma_attr, DDI_DMA_SLEEP, NULL, &buf->rb_mapping.vbm_dmah)) { ! sc->sc_rxfail_dma_handle++; goto exit_handle; } if (ddi_dma_mem_alloc(buf->rb_mapping.vbm_dmah, VIOIF_RX_SIZE + sizeof (struct virtio_net_hdr), &vioif_bufattr, DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &buf->rb_mapping.vbm_buf, &len, &buf->rb_mapping.vbm_acch)) { ! sc->sc_rxfail_dma_buffer++; goto exit_alloc; } ASSERT(len >= VIOIF_RX_SIZE); if (ddi_dma_addr_bind_handle(buf->rb_mapping.vbm_dmah, NULL, buf->rb_mapping.vbm_buf, len, DDI_DMA_READ | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &buf->rb_mapping.vbm_dmac, &buf->rb_mapping.vbm_ncookies)) { ! sc->sc_rxfail_dma_bind++; goto exit_bind; } ASSERT(buf->rb_mapping.vbm_ncookies <= VIOIF_INDIRECT_MAX);
*** 715,734 **** struct vq_entry *ve; while ((ve = vq_alloc_entry(sc->sc_rx_vq)) != NULL) { struct vioif_rx_buf *buf = sc->sc_rxbufs[ve->qe_index]; ! if (!buf) { /* First run, allocate the buffer. */ buf = kmem_cache_alloc(sc->sc_rxbuf_cache, kmflag); sc->sc_rxbufs[ve->qe_index] = buf; } /* Still nothing? Bye. */ ! if (!buf) { ! dev_err(sc->sc_dev, CE_WARN, ! "Can't allocate rx buffer"); sc->sc_norecvbuf++; vq_free_entry(sc->sc_rx_vq, ve); break; } --- 735,752 ---- struct vq_entry *ve; while ((ve = vq_alloc_entry(sc->sc_rx_vq)) != NULL) { struct vioif_rx_buf *buf = sc->sc_rxbufs[ve->qe_index]; ! if (buf == NULL) { /* First run, allocate the buffer. */ buf = kmem_cache_alloc(sc->sc_rxbuf_cache, kmflag); sc->sc_rxbufs[ve->qe_index] = buf; } /* Still nothing? Bye. */ ! if (buf == NULL) { sc->sc_norecvbuf++; vq_free_entry(sc->sc_rx_vq, ve); break; }
*** 798,809 **** buf = sc->sc_rxbufs[ve->qe_index]; ASSERT(buf); if (len < sizeof (struct virtio_net_hdr)) { ! dev_err(sc->sc_dev, CE_WARN, "RX: Cnain too small: %u", ! len - (uint32_t)sizeof (struct virtio_net_hdr)); sc->sc_ierrors++; virtio_free_chain(ve); continue; } --- 816,826 ---- buf = sc->sc_rxbufs[ve->qe_index]; ASSERT(buf); if (len < sizeof (struct virtio_net_hdr)) { ! sc->sc_rxfail_chain_undersize++; sc->sc_ierrors++; virtio_free_chain(ve); continue; }
*** 813,823 **** * cookie and reuse the buffers. For bigger ones, we loan * the buffers upstream. */ if (len < sc->sc_rxcopy_thresh) { mp = allocb(len, 0); ! if (!mp) { sc->sc_norecvbuf++; sc->sc_ierrors++; virtio_free_chain(ve); break; --- 830,840 ---- * cookie and reuse the buffers. For bigger ones, we loan * the buffers upstream. */ if (len < sc->sc_rxcopy_thresh) { mp = allocb(len, 0); ! if (mp == NULL) { sc->sc_norecvbuf++; sc->sc_ierrors++; virtio_free_chain(ve); break;
*** 830,840 **** } else { mp = desballoc((unsigned char *) buf->rb_mapping.vbm_buf + sizeof (struct virtio_net_hdr) + VIOIF_IP_ALIGN, len, 0, &buf->rb_frtn); ! if (!mp) { sc->sc_norecvbuf++; sc->sc_ierrors++; virtio_free_chain(ve); break; --- 847,857 ---- } else { mp = desballoc((unsigned char *) buf->rb_mapping.vbm_buf + sizeof (struct virtio_net_hdr) + VIOIF_IP_ALIGN, len, 0, &buf->rb_frtn); ! if (mp == NULL) { sc->sc_norecvbuf++; sc->sc_ierrors++; virtio_free_chain(ve); break;
*** 896,915 **** buf = &sc->sc_txbufs[ve->qe_index]; mp = buf->tb_mp; buf->tb_mp = NULL; ! if (mp) { for (int i = 0; i < buf->tb_external_num; i++) (void) ddi_dma_unbind_handle( buf->tb_external_mapping[i].vbm_dmah); } virtio_free_chain(ve); /* External mapping used, mp was not freed in vioif_send() */ ! if (mp) freemsg(mp); num_reclaimed++; } if (sc->sc_tx_stopped && num_reclaimed > 0) { --- 913,932 ---- buf = &sc->sc_txbufs[ve->qe_index]; mp = buf->tb_mp; buf->tb_mp = NULL; ! if (mp != NULL) { for (int i = 0; i < buf->tb_external_num; i++) (void) ddi_dma_unbind_handle( buf->tb_external_mapping[i].vbm_dmah); } virtio_free_chain(ve); /* External mapping used, mp was not freed in vioif_send() */ ! if (mp != NULL) freemsg(mp); num_reclaimed++; } if (sc->sc_tx_stopped && num_reclaimed > 0) {
*** 949,960 **** if (!buf->tb_external_mapping[i].vbm_dmah) { ret = ddi_dma_alloc_handle(sc->sc_dev, &vioif_mapped_buf_dma_attr, DDI_DMA_SLEEP, NULL, &buf->tb_external_mapping[i].vbm_dmah); if (ret != DDI_SUCCESS) { ! dev_err(sc->sc_dev, CE_WARN, ! "Can't allocate dma handle for external tx buffer"); } } return (ret); } --- 966,976 ---- if (!buf->tb_external_mapping[i].vbm_dmah) { ret = ddi_dma_alloc_handle(sc->sc_dev, &vioif_mapped_buf_dma_attr, DDI_DMA_SLEEP, NULL, &buf->tb_external_mapping[i].vbm_dmah); if (ret != DDI_SUCCESS) { ! sc->sc_txfail_dma_handle++; } } return (ret); }
*** 1004,1024 **** (caddr_t)nmp->b_rptr, len, DDI_DMA_WRITE | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &dmac, &ncookies); if (ret != DDI_SUCCESS) { sc->sc_oerrors++; - dev_err(sc->sc_dev, CE_NOTE, - "TX: Failed to bind external handle"); goto exit_bind; } /* Check if we still fit into the indirect table. */ if (virtio_ve_indirect_available(ve) < ncookies) { ! dev_err(sc->sc_dev, CE_NOTE, ! "TX: Indirect descriptor table limit reached." ! " It took %d fragments.", i); sc->sc_notxbuf++; sc->sc_oerrors++; ret = DDI_FAILURE; goto exit_limit; --- 1020,1037 ---- (caddr_t)nmp->b_rptr, len, DDI_DMA_WRITE | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &dmac, &ncookies); if (ret != DDI_SUCCESS) { + sc->sc_txfail_dma_bind++; sc->sc_oerrors++; goto exit_bind; } /* Check if we still fit into the indirect table. */ if (virtio_ve_indirect_available(ve) < ncookies) { ! sc->sc_txfail_indirect_limit++; sc->sc_notxbuf++; sc->sc_oerrors++; ret = DDI_FAILURE; goto exit_limit;
*** 1073,1083 **** lso_required = (lso_flags & HW_LSO); } ve = vq_alloc_entry(sc->sc_tx_vq); ! if (!ve) { sc->sc_notxbuf++; /* Out of free descriptors - try later. */ return (B_FALSE); } buf = &sc->sc_txbufs[ve->qe_index]; --- 1086,1096 ---- lso_required = (lso_flags & HW_LSO); } ve = vq_alloc_entry(sc->sc_tx_vq); ! if (ve == NULL) { sc->sc_notxbuf++; /* Out of free descriptors - try later. */ return (B_FALSE); } buf = &sc->sc_txbufs[ve->qe_index];
*** 1191,1202 **** { struct vioif_softc *sc = arg; struct vq_entry *ve; uint32_t len; ! mac_link_update(sc->sc_mac_handle, ! vioif_link_state(sc)); virtio_start_vq_intr(sc->sc_rx_vq); /* * Don't start interrupts on sc_tx_vq. We use VIRTIO_F_NOTIFY_ON_EMPTY, --- 1204,1214 ---- { struct vioif_softc *sc = arg; struct vq_entry *ve; uint32_t len; ! mac_link_update(sc->sc_mac_handle, vioif_link_state(sc)); virtio_start_vq_intr(sc->sc_rx_vq); /* * Don't start interrupts on sc_tx_vq. We use VIRTIO_F_NOTIFY_ON_EMPTY,
*** 1408,1421 **** break; case MAC_PROP_PRIVATE: bzero(valstr, sizeof (valstr)); if (strcmp(pr_name, vioif_txcopy_thresh) == 0) { - value = sc->sc_txcopy_thresh; ! } else if (strcmp(pr_name, ! vioif_rxcopy_thresh) == 0) { value = sc->sc_rxcopy_thresh; } else { return; } (void) snprintf(valstr, sizeof (valstr), "%d", value); --- 1420,1431 ---- break; case MAC_PROP_PRIVATE: bzero(valstr, sizeof (valstr)); if (strcmp(pr_name, vioif_txcopy_thresh) == 0) { value = sc->sc_txcopy_thresh; ! } else if (strcmp(pr_name, vioif_rxcopy_thresh) == 0) { value = sc->sc_rxcopy_thresh; } else { return; } (void) snprintf(valstr, sizeof (valstr), "%d", value);
*** 1487,1497 **** bufp += snprintf(bufp, bufend - bufp, prefix); /* LINTED E_PTRDIFF_OVERFLOW */ bufp += virtio_show_features(features, bufp, bufend - bufp); *bufp = '\0'; - /* Using '!' to only CE_NOTE this to the system log. */ dev_err(sc->sc_dev, CE_NOTE, "!%s Vioif (%b)", buf, features, VIRTIO_NET_FEATURE_BITS); } --- 1497,1506 ----
*** 1516,1527 **** vioif_show_features(sc, "Host features: ", host_features); vioif_show_features(sc, "Negotiated features: ", sc->sc_virtio.sc_features); if (!(sc->sc_virtio.sc_features & VIRTIO_F_RING_INDIRECT_DESC)) { ! dev_err(sc->sc_dev, CE_NOTE, ! "Host does not support RING_INDIRECT_DESC, bye."); return (DDI_FAILURE); } return (DDI_SUCCESS); } --- 1525,1536 ---- vioif_show_features(sc, "Host features: ", host_features); vioif_show_features(sc, "Negotiated features: ", sc->sc_virtio.sc_features); if (!(sc->sc_virtio.sc_features & VIRTIO_F_RING_INDIRECT_DESC)) { ! dev_err(sc->sc_dev, CE_WARN, ! "Host does not support RING_INDIRECT_DESC. Cannot attach."); return (DDI_FAILURE); } return (DDI_SUCCESS); }
*** 1539,1548 **** --- 1548,1558 ---- for (i = 0; i < ETHERADDRL; i++) { virtio_write_device_config_1(&sc->sc_virtio, VIRTIO_NET_CONFIG_MAC + i, sc->sc_mac[i]); } + sc->sc_mac_from_host = 0; } /* Get the mac address out of the hardware, or make up one. */ static void vioif_get_mac(struct vioif_softc *sc)
*** 1552,1563 **** for (i = 0; i < ETHERADDRL; i++) { sc->sc_mac[i] = virtio_read_device_config_1( &sc->sc_virtio, VIRTIO_NET_CONFIG_MAC + i); } ! dev_err(sc->sc_dev, CE_NOTE, "Got MAC address from host: %s", ! ether_sprintf((struct ether_addr *)sc->sc_mac)); } else { /* Get a few random bytes */ (void) random_get_pseudo_bytes(sc->sc_mac, ETHERADDRL); /* Make sure it's a unicast MAC */ sc->sc_mac[0] &= ~1; --- 1562,1572 ---- for (i = 0; i < ETHERADDRL; i++) { sc->sc_mac[i] = virtio_read_device_config_1( &sc->sc_virtio, VIRTIO_NET_CONFIG_MAC + i); } ! sc->sc_mac_from_host = 1; } else { /* Get a few random bytes */ (void) random_get_pseudo_bytes(sc->sc_mac, ETHERADDRL); /* Make sure it's a unicast MAC */ sc->sc_mac[0] &= ~1;
*** 1565,1575 **** sc->sc_mac[1] |= 2; vioif_set_mac(sc); dev_err(sc->sc_dev, CE_NOTE, ! "Generated a random MAC address: %s", ether_sprintf((struct ether_addr *)sc->sc_mac)); } } /* --- 1574,1584 ---- sc->sc_mac[1] |= 2; vioif_set_mac(sc); dev_err(sc->sc_dev, CE_NOTE, ! "!Generated a random MAC address: %s", ether_sprintf((struct ether_addr *)sc->sc_mac)); } } /*
*** 1638,1648 **** sc->sc_rx_csum = 1; if (!vioif_has_feature(sc, VIRTIO_NET_F_GUEST_CSUM)) { sc->sc_rx_csum = 0; } ! cmn_err(CE_NOTE, "Csum enabled."); if (vioif_has_feature(sc, VIRTIO_NET_F_HOST_TSO4)) { sc->sc_tx_tso4 = 1; /* --- 1647,1657 ---- sc->sc_rx_csum = 1; if (!vioif_has_feature(sc, VIRTIO_NET_F_GUEST_CSUM)) { sc->sc_rx_csum = 0; } ! dev_err(sc->sc_dev, CE_NOTE, "!Csum enabled."); if (vioif_has_feature(sc, VIRTIO_NET_F_HOST_TSO4)) { sc->sc_tx_tso4 = 1; /*
*** 1652,1666 **** * the device to support it in order to do * LSO. */ if (!vioif_has_feature(sc, VIRTIO_NET_F_HOST_ECN)) { dev_err(sc->sc_dev, CE_NOTE, ! "TSO4 supported, but not ECN. " "Not using LSO."); sc->sc_tx_tso4 = 0; } else { ! cmn_err(CE_NOTE, "LSO enabled"); } } } } --- 1661,1675 ---- * the device to support it in order to do * LSO. */ if (!vioif_has_feature(sc, VIRTIO_NET_F_HOST_ECN)) { dev_err(sc->sc_dev, CE_NOTE, ! "!TSO4 supported, but not ECN. " "Not using LSO."); sc->sc_tx_tso4 = 0; } else { ! dev_err(sc->sc_dev, CE_NOTE, "!LSO enabled"); } } } }
*** 1780,1790 **** sc->sc_txcopy_thresh = 300; sc->sc_mtu = ETHERMTU; vioif_check_features(sc); ! if (vioif_alloc_mems(sc)) goto exit_alloc_mems; if ((macp = mac_alloc(MAC_VERSION)) == NULL) { dev_err(devinfo, CE_WARN, "Failed to allocate a mac_register"); goto exit_macalloc; --- 1789,1799 ---- sc->sc_txcopy_thresh = 300; sc->sc_mtu = ETHERMTU; vioif_check_features(sc); ! if (vioif_alloc_mems(sc) != 0) goto exit_alloc_mems; if ((macp = mac_alloc(MAC_VERSION)) == NULL) { dev_err(devinfo, CE_WARN, "Failed to allocate a mac_register"); goto exit_macalloc;
*** 1868,1878 **** default: return (DDI_FAILURE); } ! if (sc->sc_rxloan) { dev_err(devinfo, CE_WARN, "!Some rx buffers are still upstream," " not detaching."); return (DDI_FAILURE); } --- 1877,1887 ---- default: return (DDI_FAILURE); } ! if (sc->sc_rxloan > 0) { dev_err(devinfo, CE_WARN, "!Some rx buffers are still upstream," " not detaching."); return (DDI_FAILURE); }