1*8a324c92SDan McDonald /* 2*8a324c92SDan McDonald * This file and its contents are supplied under the terms of the 3*8a324c92SDan McDonald * Common Development and Distribution License ("CDDL"), version 1.0. 4*8a324c92SDan McDonald * You may only use this file in accordance with the terms of version 5*8a324c92SDan McDonald * 1.0 of the CDDL. 6*8a324c92SDan McDonald * 7*8a324c92SDan McDonald * A full copy of the text of the CDDL should have accompanied this 8*8a324c92SDan McDonald * source. A copy of the CDDL is also available via the Internet at 9*8a324c92SDan McDonald * http://www.illumos.org/license/CDDL. 10*8a324c92SDan McDonald */ 11*8a324c92SDan McDonald 12*8a324c92SDan McDonald /* 13*8a324c92SDan McDonald * Copyright 2013 Nexenta Inc. All rights reserved. 14*8a324c92SDan McDonald * Copyright (c) 2014, 2015 by Delphix. All rights reserved. 15*8a324c92SDan McDonald */ 16*8a324c92SDan McDonald 17*8a324c92SDan McDonald /* Based on the NetBSD virtio driver by Minoura Makoto. */ 18*8a324c92SDan McDonald /* 19*8a324c92SDan McDonald * Copyright (c) 2010 Minoura Makoto. 20*8a324c92SDan McDonald * All rights reserved. 21*8a324c92SDan McDonald * 22*8a324c92SDan McDonald * Redistribution and use in source and binary forms, with or without 23*8a324c92SDan McDonald * modification, are permitted provided that the following conditions 24*8a324c92SDan McDonald * are met: 25*8a324c92SDan McDonald * 1. Redistributions of source code must retain the above copyright 26*8a324c92SDan McDonald * notice, this list of conditions and the following disclaimer. 27*8a324c92SDan McDonald * 2. Redistributions in binary form must reproduce the above copyright 28*8a324c92SDan McDonald * notice, this list of conditions and the following disclaimer in the 29*8a324c92SDan McDonald * documentation and/or other materials provided with the distribution. 30*8a324c92SDan McDonald * 31*8a324c92SDan McDonald * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 32*8a324c92SDan McDonald * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 33*8a324c92SDan McDonald * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 34*8a324c92SDan McDonald * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 35*8a324c92SDan McDonald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 36*8a324c92SDan McDonald * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 37*8a324c92SDan McDonald * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 38*8a324c92SDan McDonald * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 39*8a324c92SDan McDonald * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 40*8a324c92SDan McDonald * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 41*8a324c92SDan McDonald */ 42*8a324c92SDan McDonald 43*8a324c92SDan McDonald #include <sys/types.h> 44*8a324c92SDan McDonald #include <sys/errno.h> 45*8a324c92SDan McDonald #include <sys/param.h> 46*8a324c92SDan McDonald #include <sys/stropts.h> 47*8a324c92SDan McDonald #include <sys/stream.h> 48*8a324c92SDan McDonald #include <sys/strsubr.h> 49*8a324c92SDan McDonald #include <sys/kmem.h> 50*8a324c92SDan McDonald #include <sys/conf.h> 51*8a324c92SDan McDonald #include <sys/devops.h> 52*8a324c92SDan McDonald #include <sys/ksynch.h> 53*8a324c92SDan McDonald #include <sys/stat.h> 54*8a324c92SDan McDonald #include <sys/modctl.h> 55*8a324c92SDan McDonald #include <sys/debug.h> 56*8a324c92SDan McDonald #include <sys/pci.h> 57*8a324c92SDan McDonald #include <sys/ethernet.h> 58*8a324c92SDan McDonald #include <sys/vlan.h> 59*8a324c92SDan McDonald 60*8a324c92SDan McDonald #include <sys/dlpi.h> 61*8a324c92SDan McDonald #include <sys/taskq.h> 62*8a324c92SDan McDonald #include <sys/cyclic.h> 63*8a324c92SDan McDonald 64*8a324c92SDan McDonald #include <sys/pattr.h> 65*8a324c92SDan McDonald #include <sys/strsun.h> 66*8a324c92SDan McDonald 67*8a324c92SDan McDonald #include <sys/random.h> 68*8a324c92SDan McDonald #include <sys/sysmacros.h> 69*8a324c92SDan McDonald #include <sys/stream.h> 70*8a324c92SDan McDonald 71*8a324c92SDan McDonald #include <sys/mac.h> 72*8a324c92SDan McDonald #include <sys/mac_provider.h> 73*8a324c92SDan McDonald #include <sys/mac_ether.h> 74*8a324c92SDan McDonald 75*8a324c92SDan McDonald #include "virtiovar.h" 76*8a324c92SDan McDonald #include "virtioreg.h" 77*8a324c92SDan McDonald 78*8a324c92SDan McDonald /* Configuration registers */ 79*8a324c92SDan McDonald #define VIRTIO_NET_CONFIG_MAC 0 /* 8bit x 6byte */ 80*8a324c92SDan McDonald #define VIRTIO_NET_CONFIG_STATUS 6 /* 16bit */ 81*8a324c92SDan McDonald 82*8a324c92SDan McDonald /* Feature bits */ 83*8a324c92SDan McDonald #define VIRTIO_NET_F_CSUM (1 << 0) /* Host handles pkts w/ partial csum */ 84*8a324c92SDan McDonald #define VIRTIO_NET_F_GUEST_CSUM (1 << 1) /* Guest handles pkts w/ part csum */ 85*8a324c92SDan McDonald #define VIRTIO_NET_F_MAC (1 << 5) /* Host has given MAC address. */ 86*8a324c92SDan McDonald #define VIRTIO_NET_F_GSO (1 << 6) /* Host handles pkts w/ any GSO type */ 87*8a324c92SDan McDonald #define VIRTIO_NET_F_GUEST_TSO4 (1 << 7) /* Guest can handle TSOv4 in. */ 88*8a324c92SDan McDonald #define VIRTIO_NET_F_GUEST_TSO6 (1 << 8) /* Guest can handle TSOv6 in. */ 89*8a324c92SDan McDonald #define VIRTIO_NET_F_GUEST_ECN (1 << 9) /* Guest can handle TSO[6] w/ ECN in */ 90*8a324c92SDan McDonald #define VIRTIO_NET_F_GUEST_UFO (1 << 10) /* Guest can handle UFO in. */ 91*8a324c92SDan McDonald #define VIRTIO_NET_F_HOST_TSO4 (1 << 11) /* Host can handle TSOv4 in. */ 92*8a324c92SDan McDonald #define VIRTIO_NET_F_HOST_TSO6 (1 << 12) /* Host can handle TSOv6 in. */ 93*8a324c92SDan McDonald #define VIRTIO_NET_F_HOST_ECN (1 << 13) /* Host can handle TSO[6] w/ ECN in */ 94*8a324c92SDan McDonald #define VIRTIO_NET_F_HOST_UFO (1 << 14) /* Host can handle UFO in. */ 95*8a324c92SDan McDonald #define VIRTIO_NET_F_MRG_RXBUF (1 << 15) /* Host can merge receive buffers. */ 96*8a324c92SDan McDonald #define VIRTIO_NET_F_STATUS (1 << 16) /* Config.status available */ 97*8a324c92SDan McDonald #define VIRTIO_NET_F_CTRL_VQ (1 << 17) /* Control channel available */ 98*8a324c92SDan McDonald #define VIRTIO_NET_F_CTRL_RX (1 << 18) /* Control channel RX mode support */ 99*8a324c92SDan McDonald #define VIRTIO_NET_F_CTRL_VLAN (1 << 19) /* Control channel VLAN filtering */ 100*8a324c92SDan McDonald #define VIRTIO_NET_F_CTRL_RX_EXTRA (1 << 20) /* Extra RX mode control support */ 101*8a324c92SDan McDonald 102*8a324c92SDan McDonald #define VIRTIO_NET_FEATURE_BITS \ 103*8a324c92SDan McDonald "\020" \ 104*8a324c92SDan McDonald "\1CSUM" \ 105*8a324c92SDan McDonald "\2GUEST_CSUM" \ 106*8a324c92SDan McDonald "\6MAC" \ 107*8a324c92SDan McDonald "\7GSO" \ 108*8a324c92SDan McDonald "\10GUEST_TSO4" \ 109*8a324c92SDan McDonald "\11GUEST_TSO6" \ 110*8a324c92SDan McDonald "\12GUEST_ECN" \ 111*8a324c92SDan McDonald "\13GUEST_UFO" \ 112*8a324c92SDan McDonald "\14HOST_TSO4" \ 113*8a324c92SDan McDonald "\15HOST_TSO6" \ 114*8a324c92SDan McDonald "\16HOST_ECN" \ 115*8a324c92SDan McDonald "\17HOST_UFO" \ 116*8a324c92SDan McDonald "\20MRG_RXBUF" \ 117*8a324c92SDan McDonald "\21STATUS" \ 118*8a324c92SDan McDonald "\22CTRL_VQ" \ 119*8a324c92SDan McDonald "\23CTRL_RX" \ 120*8a324c92SDan McDonald "\24CTRL_VLAN" \ 121*8a324c92SDan McDonald "\25CTRL_RX_EXTRA" 122*8a324c92SDan McDonald 123*8a324c92SDan McDonald /* Status */ 124*8a324c92SDan McDonald #define VIRTIO_NET_S_LINK_UP 1 125*8a324c92SDan McDonald 126*8a324c92SDan McDonald #pragma pack(1) 127*8a324c92SDan McDonald /* Packet header structure */ 128*8a324c92SDan McDonald struct virtio_net_hdr { 129*8a324c92SDan McDonald uint8_t flags; 130*8a324c92SDan McDonald uint8_t gso_type; 131*8a324c92SDan McDonald uint16_t hdr_len; 132*8a324c92SDan McDonald uint16_t gso_size; 133*8a324c92SDan McDonald uint16_t csum_start; 134*8a324c92SDan McDonald uint16_t csum_offset; 135*8a324c92SDan McDonald }; 136*8a324c92SDan McDonald #pragma pack() 137*8a324c92SDan McDonald 138*8a324c92SDan McDonald #define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 /* flags */ 139*8a324c92SDan McDonald #define VIRTIO_NET_HDR_GSO_NONE 0 /* gso_type */ 140*8a324c92SDan McDonald #define VIRTIO_NET_HDR_GSO_TCPV4 1 /* gso_type */ 141*8a324c92SDan McDonald #define VIRTIO_NET_HDR_GSO_UDP 3 /* gso_type */ 142*8a324c92SDan McDonald #define VIRTIO_NET_HDR_GSO_TCPV6 4 /* gso_type */ 143*8a324c92SDan McDonald #define VIRTIO_NET_HDR_GSO_ECN 0x80 /* gso_type, |'ed */ 144*8a324c92SDan McDonald 145*8a324c92SDan McDonald 146*8a324c92SDan McDonald /* Control virtqueue */ 147*8a324c92SDan McDonald #pragma pack(1) 148*8a324c92SDan McDonald struct virtio_net_ctrl_cmd { 149*8a324c92SDan McDonald uint8_t class; 150*8a324c92SDan McDonald uint8_t command; 151*8a324c92SDan McDonald }; 152*8a324c92SDan McDonald #pragma pack() 153*8a324c92SDan McDonald 154*8a324c92SDan McDonald #define VIRTIO_NET_CTRL_RX 0 155*8a324c92SDan McDonald #define VIRTIO_NET_CTRL_RX_PROMISC 0 156*8a324c92SDan McDonald #define VIRTIO_NET_CTRL_RX_ALLMULTI 1 157*8a324c92SDan McDonald 158*8a324c92SDan McDonald #define VIRTIO_NET_CTRL_MAC 1 159*8a324c92SDan McDonald #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 160*8a324c92SDan McDonald 161*8a324c92SDan McDonald #define VIRTIO_NET_CTRL_VLAN 2 162*8a324c92SDan McDonald #define VIRTIO_NET_CTRL_VLAN_ADD 0 163*8a324c92SDan McDonald #define VIRTIO_NET_CTRL_VLAN_DEL 1 164*8a324c92SDan McDonald 165*8a324c92SDan McDonald #pragma pack(1) 166*8a324c92SDan McDonald struct virtio_net_ctrl_status { 167*8a324c92SDan McDonald uint8_t ack; 168*8a324c92SDan McDonald }; 169*8a324c92SDan McDonald 170*8a324c92SDan McDonald struct virtio_net_ctrl_rx { 171*8a324c92SDan McDonald uint8_t onoff; 172*8a324c92SDan McDonald }; 173*8a324c92SDan McDonald 174*8a324c92SDan McDonald struct virtio_net_ctrl_mac_tbl { 175*8a324c92SDan McDonald uint32_t nentries; 176*8a324c92SDan McDonald uint8_t macs[][ETHERADDRL]; 177*8a324c92SDan McDonald }; 178*8a324c92SDan McDonald 179*8a324c92SDan McDonald struct virtio_net_ctrl_vlan { 180*8a324c92SDan McDonald uint16_t id; 181*8a324c92SDan McDonald }; 182*8a324c92SDan McDonald #pragma pack() 183*8a324c92SDan McDonald 184*8a324c92SDan McDonald static int vioif_quiesce(dev_info_t *); 185*8a324c92SDan McDonald static int vioif_attach(dev_info_t *, ddi_attach_cmd_t); 186*8a324c92SDan McDonald static int vioif_detach(dev_info_t *, ddi_detach_cmd_t); 187*8a324c92SDan McDonald 188*8a324c92SDan McDonald DDI_DEFINE_STREAM_OPS(vioif_ops, 189*8a324c92SDan McDonald nulldev, /* identify */ 190*8a324c92SDan McDonald nulldev, /* probe */ 191*8a324c92SDan McDonald vioif_attach, /* attach */ 192*8a324c92SDan McDonald vioif_detach, /* detach */ 193*8a324c92SDan McDonald nodev, /* reset */ 194*8a324c92SDan McDonald NULL, /* cb_ops */ 195*8a324c92SDan McDonald D_MP, /* bus_ops */ 196*8a324c92SDan McDonald NULL, /* power */ 197*8a324c92SDan McDonald vioif_quiesce /* quiesce */ 198*8a324c92SDan McDonald ); 199*8a324c92SDan McDonald 200*8a324c92SDan McDonald static char vioif_ident[] = "VirtIO ethernet driver"; 201*8a324c92SDan McDonald 202*8a324c92SDan McDonald /* Standard Module linkage initialization for a Streams driver */ 203*8a324c92SDan McDonald extern struct mod_ops mod_driverops; 204*8a324c92SDan McDonald 205*8a324c92SDan McDonald static struct modldrv modldrv = { 206*8a324c92SDan McDonald &mod_driverops, /* Type of module. This one is a driver */ 207*8a324c92SDan McDonald vioif_ident, /* short description */ 208*8a324c92SDan McDonald &vioif_ops /* driver specific ops */ 209*8a324c92SDan McDonald }; 210*8a324c92SDan McDonald 211*8a324c92SDan McDonald static struct modlinkage modlinkage = { 212*8a324c92SDan McDonald MODREV_1, 213*8a324c92SDan McDonald { 214*8a324c92SDan McDonald (void *)&modldrv, 215*8a324c92SDan McDonald NULL, 216*8a324c92SDan McDonald }, 217*8a324c92SDan McDonald }; 218*8a324c92SDan McDonald 219*8a324c92SDan McDonald ddi_device_acc_attr_t vioif_attr = { 220*8a324c92SDan McDonald DDI_DEVICE_ATTR_V0, 221*8a324c92SDan McDonald DDI_NEVERSWAP_ACC, /* virtio is always native byte order */ 222*8a324c92SDan McDonald DDI_STORECACHING_OK_ACC, 223*8a324c92SDan McDonald DDI_DEFAULT_ACC 224*8a324c92SDan McDonald }; 225*8a324c92SDan McDonald 226*8a324c92SDan McDonald /* 227*8a324c92SDan McDonald * A mapping represents a binding for a single buffer that is contiguous in the 228*8a324c92SDan McDonald * virtual address space. 229*8a324c92SDan McDonald */ 230*8a324c92SDan McDonald struct vioif_buf_mapping { 231*8a324c92SDan McDonald caddr_t vbm_buf; 232*8a324c92SDan McDonald ddi_dma_handle_t vbm_dmah; 233*8a324c92SDan McDonald ddi_acc_handle_t vbm_acch; 234*8a324c92SDan McDonald ddi_dma_cookie_t vbm_dmac; 235*8a324c92SDan McDonald unsigned int vbm_ncookies; 236*8a324c92SDan McDonald }; 237*8a324c92SDan McDonald 238*8a324c92SDan McDonald /* 239*8a324c92SDan McDonald * Rx buffers can be loaned upstream, so the code has 240*8a324c92SDan McDonald * to allocate them dynamically. 241*8a324c92SDan McDonald */ 242*8a324c92SDan McDonald struct vioif_rx_buf { 243*8a324c92SDan McDonald struct vioif_softc *rb_sc; 244*8a324c92SDan McDonald frtn_t rb_frtn; 245*8a324c92SDan McDonald 246*8a324c92SDan McDonald struct vioif_buf_mapping rb_mapping; 247*8a324c92SDan McDonald }; 248*8a324c92SDan McDonald 249*8a324c92SDan McDonald /* 250*8a324c92SDan McDonald * Tx buffers have two mapping types. One, "inline", is pre-allocated and is 251*8a324c92SDan McDonald * used to hold the virtio_net_header. Small packets also get copied there, as 252*8a324c92SDan McDonald * it's faster then mapping them. Bigger packets get mapped using the "external" 253*8a324c92SDan McDonald * mapping array. An array is used, because a packet may consist of muptiple 254*8a324c92SDan McDonald * fragments, so each fragment gets bound to an entry. According to my 255*8a324c92SDan McDonald * observations, the number of fragments does not exceed 2, but just in case, 256*8a324c92SDan McDonald * a bigger, up to VIOIF_INDIRECT_MAX - 1 array is allocated. To save resources, 257*8a324c92SDan McDonald * the dma handles are allocated lazily in the tx path. 258*8a324c92SDan McDonald */ 259*8a324c92SDan McDonald struct vioif_tx_buf { 260*8a324c92SDan McDonald mblk_t *tb_mp; 261*8a324c92SDan McDonald 262*8a324c92SDan McDonald /* inline buffer */ 263*8a324c92SDan McDonald struct vioif_buf_mapping tb_inline_mapping; 264*8a324c92SDan McDonald 265*8a324c92SDan McDonald /* External buffers */ 266*8a324c92SDan McDonald struct vioif_buf_mapping *tb_external_mapping; 267*8a324c92SDan McDonald unsigned int tb_external_num; 268*8a324c92SDan McDonald }; 269*8a324c92SDan McDonald 270*8a324c92SDan McDonald struct vioif_softc { 271*8a324c92SDan McDonald dev_info_t *sc_dev; /* mirrors virtio_softc->sc_dev */ 272*8a324c92SDan McDonald struct virtio_softc sc_virtio; 273*8a324c92SDan McDonald 274*8a324c92SDan McDonald mac_handle_t sc_mac_handle; 275*8a324c92SDan McDonald mac_register_t *sc_macp; 276*8a324c92SDan McDonald 277*8a324c92SDan McDonald struct virtqueue *sc_rx_vq; 278*8a324c92SDan McDonald struct virtqueue *sc_tx_vq; 279*8a324c92SDan McDonald struct virtqueue *sc_ctrl_vq; 280*8a324c92SDan McDonald 281*8a324c92SDan McDonald unsigned int sc_tx_stopped:1; 282*8a324c92SDan McDonald 283*8a324c92SDan McDonald /* Feature bits. */ 284*8a324c92SDan McDonald unsigned int sc_rx_csum:1; 285*8a324c92SDan McDonald unsigned int sc_tx_csum:1; 286*8a324c92SDan McDonald unsigned int sc_tx_tso4:1; 287*8a324c92SDan McDonald 288*8a324c92SDan McDonald int sc_mtu; 289*8a324c92SDan McDonald uint8_t sc_mac[ETHERADDRL]; 290*8a324c92SDan McDonald /* 291*8a324c92SDan McDonald * For rx buffers, we keep a pointer array, because the buffers 292*8a324c92SDan McDonald * can be loaned upstream, and we have to repopulate the array with 293*8a324c92SDan McDonald * new members. 294*8a324c92SDan McDonald */ 295*8a324c92SDan McDonald struct vioif_rx_buf **sc_rxbufs; 296*8a324c92SDan McDonald 297*8a324c92SDan McDonald /* 298*8a324c92SDan McDonald * For tx, we just allocate an array of buffers. The packet can 299*8a324c92SDan McDonald * either be copied into the inline buffer, or the external mapping 300*8a324c92SDan McDonald * could be used to map the packet 301*8a324c92SDan McDonald */ 302*8a324c92SDan McDonald struct vioif_tx_buf *sc_txbufs; 303*8a324c92SDan McDonald 304*8a324c92SDan McDonald kstat_t *sc_intrstat; 305*8a324c92SDan McDonald /* 306*8a324c92SDan McDonald * We "loan" rx buffers upstream and reuse them after they are 307*8a324c92SDan McDonald * freed. This lets us avoid allocations in the hot path. 308*8a324c92SDan McDonald */ 309*8a324c92SDan McDonald kmem_cache_t *sc_rxbuf_cache; 310*8a324c92SDan McDonald ulong_t sc_rxloan; 311*8a324c92SDan McDonald 312*8a324c92SDan McDonald /* Copying small packets turns out to be faster then mapping them. */ 313*8a324c92SDan McDonald unsigned long sc_rxcopy_thresh; 314*8a324c92SDan McDonald unsigned long sc_txcopy_thresh; 315*8a324c92SDan McDonald /* Some statistic coming here */ 316*8a324c92SDan McDonald uint64_t sc_ipackets; 317*8a324c92SDan McDonald uint64_t sc_opackets; 318*8a324c92SDan McDonald uint64_t sc_rbytes; 319*8a324c92SDan McDonald uint64_t sc_obytes; 320*8a324c92SDan McDonald uint64_t sc_brdcstxmt; 321*8a324c92SDan McDonald uint64_t sc_brdcstrcv; 322*8a324c92SDan McDonald uint64_t sc_multixmt; 323*8a324c92SDan McDonald uint64_t sc_multircv; 324*8a324c92SDan McDonald uint64_t sc_norecvbuf; 325*8a324c92SDan McDonald uint64_t sc_notxbuf; 326*8a324c92SDan McDonald uint64_t sc_ierrors; 327*8a324c92SDan McDonald uint64_t sc_oerrors; 328*8a324c92SDan McDonald }; 329*8a324c92SDan McDonald 330*8a324c92SDan McDonald #define ETHER_HEADER_LEN sizeof (struct ether_header) 331*8a324c92SDan McDonald 332*8a324c92SDan McDonald /* MTU + the ethernet header. */ 333*8a324c92SDan McDonald #define MAX_PAYLOAD 65535 334*8a324c92SDan McDonald #define MAX_MTU (MAX_PAYLOAD - ETHER_HEADER_LEN) 335*8a324c92SDan McDonald #define DEFAULT_MTU ETHERMTU 336*8a324c92SDan McDonald 337*8a324c92SDan McDonald /* 338*8a324c92SDan McDonald * Yeah, we spend 8M per device. Turns out, there is no point 339*8a324c92SDan McDonald * being smart and using merged rx buffers (VIRTIO_NET_F_MRG_RXBUF), 340*8a324c92SDan McDonald * because vhost does not support them, and we expect to be used with 341*8a324c92SDan McDonald * vhost in production environment. 342*8a324c92SDan McDonald */ 343*8a324c92SDan McDonald /* The buffer keeps both the packet data and the virtio_net_header. */ 344*8a324c92SDan McDonald #define VIOIF_RX_SIZE (MAX_PAYLOAD + sizeof (struct virtio_net_hdr)) 345*8a324c92SDan McDonald 346*8a324c92SDan McDonald /* 347*8a324c92SDan McDonald * We win a bit on header alignment, but the host wins a lot 348*8a324c92SDan McDonald * more on moving aligned buffers. Might need more thought. 349*8a324c92SDan McDonald */ 350*8a324c92SDan McDonald #define VIOIF_IP_ALIGN 0 351*8a324c92SDan McDonald 352*8a324c92SDan McDonald /* Maximum number of indirect descriptors, somewhat arbitrary. */ 353*8a324c92SDan McDonald #define VIOIF_INDIRECT_MAX 128 354*8a324c92SDan McDonald 355*8a324c92SDan McDonald /* 356*8a324c92SDan McDonald * We pre-allocate a reasonably large buffer to copy small packets 357*8a324c92SDan McDonald * there. Bigger packets are mapped, packets with multiple 358*8a324c92SDan McDonald * cookies are mapped as indirect buffers. 359*8a324c92SDan McDonald */ 360*8a324c92SDan McDonald #define VIOIF_TX_INLINE_SIZE 2048 361*8a324c92SDan McDonald 362*8a324c92SDan McDonald /* Native queue size for all queues */ 363*8a324c92SDan McDonald #define VIOIF_RX_QLEN 0 364*8a324c92SDan McDonald #define VIOIF_TX_QLEN 0 365*8a324c92SDan McDonald #define VIOIF_CTRL_QLEN 0 366*8a324c92SDan McDonald 367*8a324c92SDan McDonald static uchar_t vioif_broadcast[ETHERADDRL] = { 368*8a324c92SDan McDonald 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 369*8a324c92SDan McDonald }; 370*8a324c92SDan McDonald 371*8a324c92SDan McDonald #define VIOIF_TX_THRESH_MAX 640 372*8a324c92SDan McDonald #define VIOIF_RX_THRESH_MAX 640 373*8a324c92SDan McDonald 374*8a324c92SDan McDonald #define CACHE_NAME_SIZE 32 375*8a324c92SDan McDonald 376*8a324c92SDan McDonald static char vioif_txcopy_thresh[] = 377*8a324c92SDan McDonald "vioif_txcopy_thresh"; 378*8a324c92SDan McDonald static char vioif_rxcopy_thresh[] = 379*8a324c92SDan McDonald "vioif_rxcopy_thresh"; 380*8a324c92SDan McDonald 381*8a324c92SDan McDonald static char *vioif_priv_props[] = { 382*8a324c92SDan McDonald vioif_txcopy_thresh, 383*8a324c92SDan McDonald vioif_rxcopy_thresh, 384*8a324c92SDan McDonald NULL 385*8a324c92SDan McDonald }; 386*8a324c92SDan McDonald 387*8a324c92SDan McDonald /* Add up to ddi? */ 388*8a324c92SDan McDonald static ddi_dma_cookie_t * 389*8a324c92SDan McDonald vioif_dma_curr_cookie(ddi_dma_handle_t dmah) 390*8a324c92SDan McDonald { 391*8a324c92SDan McDonald ddi_dma_impl_t *dmah_impl = (void *) dmah; 392*8a324c92SDan McDonald ASSERT(dmah_impl->dmai_cookie); 393*8a324c92SDan McDonald return (dmah_impl->dmai_cookie); 394*8a324c92SDan McDonald } 395*8a324c92SDan McDonald 396*8a324c92SDan McDonald static void 397*8a324c92SDan McDonald vioif_dma_reset_cookie(ddi_dma_handle_t dmah, ddi_dma_cookie_t *dmac) 398*8a324c92SDan McDonald { 399*8a324c92SDan McDonald ddi_dma_impl_t *dmah_impl = (void *) dmah; 400*8a324c92SDan McDonald dmah_impl->dmai_cookie = dmac; 401*8a324c92SDan McDonald } 402*8a324c92SDan McDonald 403*8a324c92SDan McDonald static link_state_t 404*8a324c92SDan McDonald vioif_link_state(struct vioif_softc *sc) 405*8a324c92SDan McDonald { 406*8a324c92SDan McDonald if (sc->sc_virtio.sc_features & VIRTIO_NET_F_STATUS) { 407*8a324c92SDan McDonald if (virtio_read_device_config_2(&sc->sc_virtio, 408*8a324c92SDan McDonald VIRTIO_NET_CONFIG_STATUS) & VIRTIO_NET_S_LINK_UP) { 409*8a324c92SDan McDonald return (LINK_STATE_UP); 410*8a324c92SDan McDonald } else { 411*8a324c92SDan McDonald return (LINK_STATE_DOWN); 412*8a324c92SDan McDonald } 413*8a324c92SDan McDonald } 414*8a324c92SDan McDonald 415*8a324c92SDan McDonald return (LINK_STATE_UP); 416*8a324c92SDan McDonald } 417*8a324c92SDan McDonald 418*8a324c92SDan McDonald static ddi_dma_attr_t vioif_inline_buf_dma_attr = { 419*8a324c92SDan McDonald DMA_ATTR_V0, /* Version number */ 420*8a324c92SDan McDonald 0, /* low address */ 421*8a324c92SDan McDonald 0xFFFFFFFFFFFFFFFF, /* high address */ 422*8a324c92SDan McDonald 0xFFFFFFFF, /* counter register max */ 423*8a324c92SDan McDonald 1, /* page alignment */ 424*8a324c92SDan McDonald 1, /* burst sizes: 1 - 32 */ 425*8a324c92SDan McDonald 1, /* minimum transfer size */ 426*8a324c92SDan McDonald 0xFFFFFFFF, /* max transfer size */ 427*8a324c92SDan McDonald 0xFFFFFFFFFFFFFFF, /* address register max */ 428*8a324c92SDan McDonald 1, /* scatter-gather capacity */ 429*8a324c92SDan McDonald 1, /* device operates on bytes */ 430*8a324c92SDan McDonald 0, /* attr flag: set to 0 */ 431*8a324c92SDan McDonald }; 432*8a324c92SDan McDonald 433*8a324c92SDan McDonald static ddi_dma_attr_t vioif_mapped_buf_dma_attr = { 434*8a324c92SDan McDonald DMA_ATTR_V0, /* Version number */ 435*8a324c92SDan McDonald 0, /* low address */ 436*8a324c92SDan McDonald 0xFFFFFFFFFFFFFFFF, /* high address */ 437*8a324c92SDan McDonald 0xFFFFFFFF, /* counter register max */ 438*8a324c92SDan McDonald 1, /* page alignment */ 439*8a324c92SDan McDonald 1, /* burst sizes: 1 - 32 */ 440*8a324c92SDan McDonald 1, /* minimum transfer size */ 441*8a324c92SDan McDonald 0xFFFFFFFF, /* max transfer size */ 442*8a324c92SDan McDonald 0xFFFFFFFFFFFFFFF, /* address register max */ 443*8a324c92SDan McDonald 444*8a324c92SDan McDonald /* One entry is used for the virtio_net_hdr on the tx path */ 445*8a324c92SDan McDonald VIOIF_INDIRECT_MAX - 1, /* scatter-gather capacity */ 446*8a324c92SDan McDonald 1, /* device operates on bytes */ 447*8a324c92SDan McDonald 0, /* attr flag: set to 0 */ 448*8a324c92SDan McDonald }; 449*8a324c92SDan McDonald 450*8a324c92SDan McDonald static ddi_device_acc_attr_t vioif_bufattr = { 451*8a324c92SDan McDonald DDI_DEVICE_ATTR_V0, 452*8a324c92SDan McDonald DDI_NEVERSWAP_ACC, 453*8a324c92SDan McDonald DDI_STORECACHING_OK_ACC, 454*8a324c92SDan McDonald DDI_DEFAULT_ACC 455*8a324c92SDan McDonald }; 456*8a324c92SDan McDonald 457*8a324c92SDan McDonald static void 458*8a324c92SDan McDonald vioif_rx_free(caddr_t free_arg) 459*8a324c92SDan McDonald { 460*8a324c92SDan McDonald struct vioif_rx_buf *buf = (void *) free_arg; 461*8a324c92SDan McDonald struct vioif_softc *sc = buf->rb_sc; 462*8a324c92SDan McDonald 463*8a324c92SDan McDonald kmem_cache_free(sc->sc_rxbuf_cache, buf); 464*8a324c92SDan McDonald atomic_dec_ulong(&sc->sc_rxloan); 465*8a324c92SDan McDonald } 466*8a324c92SDan McDonald 467*8a324c92SDan McDonald static int 468*8a324c92SDan McDonald vioif_rx_construct(void *buffer, void *user_arg, int kmflags) 469*8a324c92SDan McDonald { 470*8a324c92SDan McDonald _NOTE(ARGUNUSED(kmflags)); 471*8a324c92SDan McDonald struct vioif_softc *sc = user_arg; 472*8a324c92SDan McDonald struct vioif_rx_buf *buf = buffer; 473*8a324c92SDan McDonald size_t len; 474*8a324c92SDan McDonald 475*8a324c92SDan McDonald if (ddi_dma_alloc_handle(sc->sc_dev, &vioif_mapped_buf_dma_attr, 476*8a324c92SDan McDonald DDI_DMA_SLEEP, NULL, &buf->rb_mapping.vbm_dmah)) { 477*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_WARN, 478*8a324c92SDan McDonald "Can't allocate dma handle for rx buffer"); 479*8a324c92SDan McDonald goto exit_handle; 480*8a324c92SDan McDonald } 481*8a324c92SDan McDonald 482*8a324c92SDan McDonald if (ddi_dma_mem_alloc(buf->rb_mapping.vbm_dmah, 483*8a324c92SDan McDonald VIOIF_RX_SIZE + sizeof (struct virtio_net_hdr), 484*8a324c92SDan McDonald &vioif_bufattr, DDI_DMA_STREAMING, DDI_DMA_SLEEP, 485*8a324c92SDan McDonald NULL, &buf->rb_mapping.vbm_buf, &len, &buf->rb_mapping.vbm_acch)) { 486*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_WARN, 487*8a324c92SDan McDonald "Can't allocate rx buffer"); 488*8a324c92SDan McDonald goto exit_alloc; 489*8a324c92SDan McDonald } 490*8a324c92SDan McDonald ASSERT(len >= VIOIF_RX_SIZE); 491*8a324c92SDan McDonald 492*8a324c92SDan McDonald if (ddi_dma_addr_bind_handle(buf->rb_mapping.vbm_dmah, NULL, 493*8a324c92SDan McDonald buf->rb_mapping.vbm_buf, len, DDI_DMA_READ | DDI_DMA_STREAMING, 494*8a324c92SDan McDonald DDI_DMA_SLEEP, NULL, &buf->rb_mapping.vbm_dmac, 495*8a324c92SDan McDonald &buf->rb_mapping.vbm_ncookies)) { 496*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_WARN, "Can't bind tx buffer"); 497*8a324c92SDan McDonald 498*8a324c92SDan McDonald goto exit_bind; 499*8a324c92SDan McDonald } 500*8a324c92SDan McDonald 501*8a324c92SDan McDonald ASSERT(buf->rb_mapping.vbm_ncookies <= VIOIF_INDIRECT_MAX); 502*8a324c92SDan McDonald 503*8a324c92SDan McDonald buf->rb_sc = sc; 504*8a324c92SDan McDonald buf->rb_frtn.free_arg = (void *) buf; 505*8a324c92SDan McDonald buf->rb_frtn.free_func = vioif_rx_free; 506*8a324c92SDan McDonald 507*8a324c92SDan McDonald return (0); 508*8a324c92SDan McDonald exit_bind: 509*8a324c92SDan McDonald ddi_dma_mem_free(&buf->rb_mapping.vbm_acch); 510*8a324c92SDan McDonald exit_alloc: 511*8a324c92SDan McDonald ddi_dma_free_handle(&buf->rb_mapping.vbm_dmah); 512*8a324c92SDan McDonald exit_handle: 513*8a324c92SDan McDonald 514*8a324c92SDan McDonald return (ENOMEM); 515*8a324c92SDan McDonald } 516*8a324c92SDan McDonald 517*8a324c92SDan McDonald static void 518*8a324c92SDan McDonald vioif_rx_destruct(void *buffer, void *user_arg) 519*8a324c92SDan McDonald { 520*8a324c92SDan McDonald _NOTE(ARGUNUSED(user_arg)); 521*8a324c92SDan McDonald struct vioif_rx_buf *buf = buffer; 522*8a324c92SDan McDonald 523*8a324c92SDan McDonald ASSERT(buf->rb_mapping.vbm_acch); 524*8a324c92SDan McDonald ASSERT(buf->rb_mapping.vbm_acch); 525*8a324c92SDan McDonald 526*8a324c92SDan McDonald (void) ddi_dma_unbind_handle(buf->rb_mapping.vbm_dmah); 527*8a324c92SDan McDonald ddi_dma_mem_free(&buf->rb_mapping.vbm_acch); 528*8a324c92SDan McDonald ddi_dma_free_handle(&buf->rb_mapping.vbm_dmah); 529*8a324c92SDan McDonald } 530*8a324c92SDan McDonald 531*8a324c92SDan McDonald static void 532*8a324c92SDan McDonald vioif_free_mems(struct vioif_softc *sc) 533*8a324c92SDan McDonald { 534*8a324c92SDan McDonald int i; 535*8a324c92SDan McDonald 536*8a324c92SDan McDonald for (i = 0; i < sc->sc_tx_vq->vq_num; i++) { 537*8a324c92SDan McDonald struct vioif_tx_buf *buf = &sc->sc_txbufs[i]; 538*8a324c92SDan McDonald int j; 539*8a324c92SDan McDonald 540*8a324c92SDan McDonald /* Tear down the internal mapping. */ 541*8a324c92SDan McDonald 542*8a324c92SDan McDonald ASSERT(buf->tb_inline_mapping.vbm_acch); 543*8a324c92SDan McDonald ASSERT(buf->tb_inline_mapping.vbm_dmah); 544*8a324c92SDan McDonald 545*8a324c92SDan McDonald (void) ddi_dma_unbind_handle(buf->tb_inline_mapping.vbm_dmah); 546*8a324c92SDan McDonald ddi_dma_mem_free(&buf->tb_inline_mapping.vbm_acch); 547*8a324c92SDan McDonald ddi_dma_free_handle(&buf->tb_inline_mapping.vbm_dmah); 548*8a324c92SDan McDonald 549*8a324c92SDan McDonald /* We should not see any in-flight buffers at this point. */ 550*8a324c92SDan McDonald ASSERT(!buf->tb_mp); 551*8a324c92SDan McDonald 552*8a324c92SDan McDonald /* Free all the dma hdnales we allocated lazily. */ 553*8a324c92SDan McDonald for (j = 0; buf->tb_external_mapping[j].vbm_dmah; j++) 554*8a324c92SDan McDonald ddi_dma_free_handle( 555*8a324c92SDan McDonald &buf->tb_external_mapping[j].vbm_dmah); 556*8a324c92SDan McDonald /* Free the external mapping array. */ 557*8a324c92SDan McDonald kmem_free(buf->tb_external_mapping, 558*8a324c92SDan McDonald sizeof (struct vioif_tx_buf) * VIOIF_INDIRECT_MAX - 1); 559*8a324c92SDan McDonald } 560*8a324c92SDan McDonald 561*8a324c92SDan McDonald kmem_free(sc->sc_txbufs, sizeof (struct vioif_tx_buf) * 562*8a324c92SDan McDonald sc->sc_tx_vq->vq_num); 563*8a324c92SDan McDonald 564*8a324c92SDan McDonald for (i = 0; i < sc->sc_rx_vq->vq_num; i++) { 565*8a324c92SDan McDonald struct vioif_rx_buf *buf = sc->sc_rxbufs[i]; 566*8a324c92SDan McDonald 567*8a324c92SDan McDonald if (buf) 568*8a324c92SDan McDonald kmem_cache_free(sc->sc_rxbuf_cache, buf); 569*8a324c92SDan McDonald } 570*8a324c92SDan McDonald kmem_free(sc->sc_rxbufs, sizeof (struct vioif_rx_buf *) * 571*8a324c92SDan McDonald sc->sc_rx_vq->vq_num); 572*8a324c92SDan McDonald } 573*8a324c92SDan McDonald 574*8a324c92SDan McDonald static int 575*8a324c92SDan McDonald vioif_alloc_mems(struct vioif_softc *sc) 576*8a324c92SDan McDonald { 577*8a324c92SDan McDonald int i, txqsize, rxqsize; 578*8a324c92SDan McDonald size_t len; 579*8a324c92SDan McDonald unsigned int nsegments; 580*8a324c92SDan McDonald 581*8a324c92SDan McDonald txqsize = sc->sc_tx_vq->vq_num; 582*8a324c92SDan McDonald rxqsize = sc->sc_rx_vq->vq_num; 583*8a324c92SDan McDonald 584*8a324c92SDan McDonald sc->sc_txbufs = kmem_zalloc(sizeof (struct vioif_tx_buf) * txqsize, 585*8a324c92SDan McDonald KM_SLEEP); 586*8a324c92SDan McDonald if (sc->sc_txbufs == NULL) { 587*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_WARN, 588*8a324c92SDan McDonald "Failed to allocate the tx buffers array"); 589*8a324c92SDan McDonald goto exit_txalloc; 590*8a324c92SDan McDonald } 591*8a324c92SDan McDonald 592*8a324c92SDan McDonald /* 593*8a324c92SDan McDonald * We don't allocate the rx vioif_bufs, just the pointers, as 594*8a324c92SDan McDonald * rx vioif_bufs can be loaned upstream, and we don't know the 595*8a324c92SDan McDonald * total number we need. 596*8a324c92SDan McDonald */ 597*8a324c92SDan McDonald sc->sc_rxbufs = kmem_zalloc(sizeof (struct vioif_rx_buf *) * rxqsize, 598*8a324c92SDan McDonald KM_SLEEP); 599*8a324c92SDan McDonald if (sc->sc_rxbufs == NULL) { 600*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_WARN, 601*8a324c92SDan McDonald "Failed to allocate the rx buffers pointer array"); 602*8a324c92SDan McDonald goto exit_rxalloc; 603*8a324c92SDan McDonald } 604*8a324c92SDan McDonald 605*8a324c92SDan McDonald for (i = 0; i < txqsize; i++) { 606*8a324c92SDan McDonald struct vioif_tx_buf *buf = &sc->sc_txbufs[i]; 607*8a324c92SDan McDonald 608*8a324c92SDan McDonald /* Allocate and bind an inline mapping. */ 609*8a324c92SDan McDonald 610*8a324c92SDan McDonald if (ddi_dma_alloc_handle(sc->sc_dev, 611*8a324c92SDan McDonald &vioif_inline_buf_dma_attr, 612*8a324c92SDan McDonald DDI_DMA_SLEEP, NULL, &buf->tb_inline_mapping.vbm_dmah)) { 613*8a324c92SDan McDonald 614*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_WARN, 615*8a324c92SDan McDonald "Can't allocate dma handle for tx buffer %d", i); 616*8a324c92SDan McDonald goto exit_tx; 617*8a324c92SDan McDonald } 618*8a324c92SDan McDonald 619*8a324c92SDan McDonald if (ddi_dma_mem_alloc(buf->tb_inline_mapping.vbm_dmah, 620*8a324c92SDan McDonald VIOIF_TX_INLINE_SIZE, &vioif_bufattr, DDI_DMA_STREAMING, 621*8a324c92SDan McDonald DDI_DMA_SLEEP, NULL, &buf->tb_inline_mapping.vbm_buf, 622*8a324c92SDan McDonald &len, &buf->tb_inline_mapping.vbm_acch)) { 623*8a324c92SDan McDonald 624*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_WARN, 625*8a324c92SDan McDonald "Can't allocate tx buffer %d", i); 626*8a324c92SDan McDonald goto exit_tx; 627*8a324c92SDan McDonald } 628*8a324c92SDan McDonald ASSERT(len >= VIOIF_TX_INLINE_SIZE); 629*8a324c92SDan McDonald 630*8a324c92SDan McDonald if (ddi_dma_addr_bind_handle(buf->tb_inline_mapping.vbm_dmah, 631*8a324c92SDan McDonald NULL, buf->tb_inline_mapping.vbm_buf, len, 632*8a324c92SDan McDonald DDI_DMA_WRITE | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, 633*8a324c92SDan McDonald &buf->tb_inline_mapping.vbm_dmac, &nsegments)) { 634*8a324c92SDan McDonald 635*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_WARN, 636*8a324c92SDan McDonald "Can't bind tx buffer %d", i); 637*8a324c92SDan McDonald goto exit_tx; 638*8a324c92SDan McDonald } 639*8a324c92SDan McDonald 640*8a324c92SDan McDonald /* We asked for a single segment */ 641*8a324c92SDan McDonald ASSERT(nsegments == 1); 642*8a324c92SDan McDonald 643*8a324c92SDan McDonald /* 644*8a324c92SDan McDonald * We allow up to VIOIF_INDIRECT_MAX - 1 external mappings. 645*8a324c92SDan McDonald * In reality, I don't expect more then 2-3 used, but who 646*8a324c92SDan McDonald * knows. 647*8a324c92SDan McDonald */ 648*8a324c92SDan McDonald buf->tb_external_mapping = kmem_zalloc( 649*8a324c92SDan McDonald sizeof (struct vioif_tx_buf) * VIOIF_INDIRECT_MAX - 1, 650*8a324c92SDan McDonald KM_SLEEP); 651*8a324c92SDan McDonald 652*8a324c92SDan McDonald /* 653*8a324c92SDan McDonald * The external mapping's dma handles are allocate lazily, 654*8a324c92SDan McDonald * as we don't expect most of them to be used.. 655*8a324c92SDan McDonald */ 656*8a324c92SDan McDonald } 657*8a324c92SDan McDonald 658*8a324c92SDan McDonald return (0); 659*8a324c92SDan McDonald 660*8a324c92SDan McDonald exit_tx: 661*8a324c92SDan McDonald for (i = 0; i < txqsize; i++) { 662*8a324c92SDan McDonald struct vioif_tx_buf *buf = &sc->sc_txbufs[i]; 663*8a324c92SDan McDonald 664*8a324c92SDan McDonald if (buf->tb_inline_mapping.vbm_dmah) 665*8a324c92SDan McDonald (void) ddi_dma_unbind_handle( 666*8a324c92SDan McDonald buf->tb_inline_mapping.vbm_dmah); 667*8a324c92SDan McDonald 668*8a324c92SDan McDonald if (buf->tb_inline_mapping.vbm_acch) 669*8a324c92SDan McDonald ddi_dma_mem_free( 670*8a324c92SDan McDonald &buf->tb_inline_mapping.vbm_acch); 671*8a324c92SDan McDonald 672*8a324c92SDan McDonald if (buf->tb_inline_mapping.vbm_dmah) 673*8a324c92SDan McDonald ddi_dma_free_handle( 674*8a324c92SDan McDonald &buf->tb_inline_mapping.vbm_dmah); 675*8a324c92SDan McDonald 676*8a324c92SDan McDonald if (buf->tb_external_mapping) 677*8a324c92SDan McDonald kmem_free(buf->tb_external_mapping, 678*8a324c92SDan McDonald sizeof (struct vioif_tx_buf) * 679*8a324c92SDan McDonald VIOIF_INDIRECT_MAX - 1); 680*8a324c92SDan McDonald } 681*8a324c92SDan McDonald 682*8a324c92SDan McDonald kmem_free(sc->sc_rxbufs, sizeof (struct vioif_rx_buf) * rxqsize); 683*8a324c92SDan McDonald 684*8a324c92SDan McDonald exit_rxalloc: 685*8a324c92SDan McDonald kmem_free(sc->sc_txbufs, sizeof (struct vioif_tx_buf) * txqsize); 686*8a324c92SDan McDonald exit_txalloc: 687*8a324c92SDan McDonald return (ENOMEM); 688*8a324c92SDan McDonald } 689*8a324c92SDan McDonald 690*8a324c92SDan McDonald /* ARGSUSED */ 691*8a324c92SDan McDonald int 692*8a324c92SDan McDonald vioif_multicst(void *arg, boolean_t add, const uint8_t *macaddr) 693*8a324c92SDan McDonald { 694*8a324c92SDan McDonald return (DDI_SUCCESS); 695*8a324c92SDan McDonald } 696*8a324c92SDan McDonald 697*8a324c92SDan McDonald /* ARGSUSED */ 698*8a324c92SDan McDonald int 699*8a324c92SDan McDonald vioif_promisc(void *arg, boolean_t on) 700*8a324c92SDan McDonald { 701*8a324c92SDan McDonald return (DDI_SUCCESS); 702*8a324c92SDan McDonald } 703*8a324c92SDan McDonald 704*8a324c92SDan McDonald /* ARGSUSED */ 705*8a324c92SDan McDonald int 706*8a324c92SDan McDonald vioif_unicst(void *arg, const uint8_t *macaddr) 707*8a324c92SDan McDonald { 708*8a324c92SDan McDonald return (DDI_FAILURE); 709*8a324c92SDan McDonald } 710*8a324c92SDan McDonald 711*8a324c92SDan McDonald 712*8a324c92SDan McDonald static int 713*8a324c92SDan McDonald vioif_add_rx(struct vioif_softc *sc, int kmflag) 714*8a324c92SDan McDonald { 715*8a324c92SDan McDonald struct vq_entry *ve; 716*8a324c92SDan McDonald struct vioif_rx_buf *buf; 717*8a324c92SDan McDonald 718*8a324c92SDan McDonald ve = vq_alloc_entry(sc->sc_rx_vq); 719*8a324c92SDan McDonald if (!ve) { 720*8a324c92SDan McDonald /* 721*8a324c92SDan McDonald * Out of free descriptors - ring already full. 722*8a324c92SDan McDonald * It would be better to update sc_norxdescavail 723*8a324c92SDan McDonald * but MAC does not ask for this info, hence we 724*8a324c92SDan McDonald * update sc_norecvbuf. 725*8a324c92SDan McDonald */ 726*8a324c92SDan McDonald sc->sc_norecvbuf++; 727*8a324c92SDan McDonald goto exit_vq; 728*8a324c92SDan McDonald } 729*8a324c92SDan McDonald buf = sc->sc_rxbufs[ve->qe_index]; 730*8a324c92SDan McDonald 731*8a324c92SDan McDonald if (!buf) { 732*8a324c92SDan McDonald /* First run, allocate the buffer. */ 733*8a324c92SDan McDonald buf = kmem_cache_alloc(sc->sc_rxbuf_cache, kmflag); 734*8a324c92SDan McDonald sc->sc_rxbufs[ve->qe_index] = buf; 735*8a324c92SDan McDonald } 736*8a324c92SDan McDonald 737*8a324c92SDan McDonald /* Still nothing? Bye. */ 738*8a324c92SDan McDonald if (!buf) { 739*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_WARN, "Can't allocate rx buffer"); 740*8a324c92SDan McDonald sc->sc_norecvbuf++; 741*8a324c92SDan McDonald goto exit_buf; 742*8a324c92SDan McDonald } 743*8a324c92SDan McDonald 744*8a324c92SDan McDonald ASSERT(buf->rb_mapping.vbm_ncookies >= 1); 745*8a324c92SDan McDonald 746*8a324c92SDan McDonald /* 747*8a324c92SDan McDonald * For an unknown reason, the virtio_net_hdr must be placed 748*8a324c92SDan McDonald * as a separate virtio queue entry. 749*8a324c92SDan McDonald */ 750*8a324c92SDan McDonald virtio_ve_add_indirect_buf(ve, buf->rb_mapping.vbm_dmac.dmac_laddress, 751*8a324c92SDan McDonald sizeof (struct virtio_net_hdr), B_FALSE); 752*8a324c92SDan McDonald 753*8a324c92SDan McDonald /* Add the rest of the first cookie. */ 754*8a324c92SDan McDonald virtio_ve_add_indirect_buf(ve, 755*8a324c92SDan McDonald buf->rb_mapping.vbm_dmac.dmac_laddress + 756*8a324c92SDan McDonald sizeof (struct virtio_net_hdr), 757*8a324c92SDan McDonald buf->rb_mapping.vbm_dmac.dmac_size - 758*8a324c92SDan McDonald sizeof (struct virtio_net_hdr), B_FALSE); 759*8a324c92SDan McDonald 760*8a324c92SDan McDonald /* 761*8a324c92SDan McDonald * If the buffer consists of a single cookie (unlikely for a 762*8a324c92SDan McDonald * 64-k buffer), we are done. Otherwise, add the rest of the cookies 763*8a324c92SDan McDonald * using indirect entries. 764*8a324c92SDan McDonald */ 765*8a324c92SDan McDonald if (buf->rb_mapping.vbm_ncookies > 1) { 766*8a324c92SDan McDonald ddi_dma_cookie_t *first_extra_dmac; 767*8a324c92SDan McDonald ddi_dma_cookie_t dmac; 768*8a324c92SDan McDonald first_extra_dmac = 769*8a324c92SDan McDonald vioif_dma_curr_cookie(buf->rb_mapping.vbm_dmah); 770*8a324c92SDan McDonald 771*8a324c92SDan McDonald ddi_dma_nextcookie(buf->rb_mapping.vbm_dmah, &dmac); 772*8a324c92SDan McDonald virtio_ve_add_cookie(ve, buf->rb_mapping.vbm_dmah, 773*8a324c92SDan McDonald dmac, buf->rb_mapping.vbm_ncookies - 1, B_FALSE); 774*8a324c92SDan McDonald vioif_dma_reset_cookie(buf->rb_mapping.vbm_dmah, 775*8a324c92SDan McDonald first_extra_dmac); 776*8a324c92SDan McDonald } 777*8a324c92SDan McDonald 778*8a324c92SDan McDonald virtio_push_chain(ve, B_FALSE); 779*8a324c92SDan McDonald 780*8a324c92SDan McDonald return (DDI_SUCCESS); 781*8a324c92SDan McDonald 782*8a324c92SDan McDonald exit_buf: 783*8a324c92SDan McDonald vq_free_entry(sc->sc_rx_vq, ve); 784*8a324c92SDan McDonald exit_vq: 785*8a324c92SDan McDonald return (DDI_FAILURE); 786*8a324c92SDan McDonald } 787*8a324c92SDan McDonald 788*8a324c92SDan McDonald static int 789*8a324c92SDan McDonald vioif_populate_rx(struct vioif_softc *sc, int kmflag) 790*8a324c92SDan McDonald { 791*8a324c92SDan McDonald int i = 0; 792*8a324c92SDan McDonald int ret; 793*8a324c92SDan McDonald 794*8a324c92SDan McDonald for (;;) { 795*8a324c92SDan McDonald ret = vioif_add_rx(sc, kmflag); 796*8a324c92SDan McDonald if (ret) 797*8a324c92SDan McDonald /* 798*8a324c92SDan McDonald * We could not allocate some memory. Try to work with 799*8a324c92SDan McDonald * what we've got. 800*8a324c92SDan McDonald */ 801*8a324c92SDan McDonald break; 802*8a324c92SDan McDonald i++; 803*8a324c92SDan McDonald } 804*8a324c92SDan McDonald 805*8a324c92SDan McDonald if (i) 806*8a324c92SDan McDonald virtio_sync_vq(sc->sc_rx_vq); 807*8a324c92SDan McDonald 808*8a324c92SDan McDonald return (i); 809*8a324c92SDan McDonald } 810*8a324c92SDan McDonald 811*8a324c92SDan McDonald static int 812*8a324c92SDan McDonald vioif_process_rx(struct vioif_softc *sc) 813*8a324c92SDan McDonald { 814*8a324c92SDan McDonald struct vq_entry *ve; 815*8a324c92SDan McDonald struct vioif_rx_buf *buf; 816*8a324c92SDan McDonald mblk_t *mp; 817*8a324c92SDan McDonald uint32_t len; 818*8a324c92SDan McDonald int i = 0; 819*8a324c92SDan McDonald 820*8a324c92SDan McDonald while ((ve = virtio_pull_chain(sc->sc_rx_vq, &len))) { 821*8a324c92SDan McDonald 822*8a324c92SDan McDonald buf = sc->sc_rxbufs[ve->qe_index]; 823*8a324c92SDan McDonald ASSERT(buf); 824*8a324c92SDan McDonald 825*8a324c92SDan McDonald if (len < sizeof (struct virtio_net_hdr)) { 826*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_WARN, "RX: Cnain too small: %u", 827*8a324c92SDan McDonald len - (uint32_t)sizeof (struct virtio_net_hdr)); 828*8a324c92SDan McDonald sc->sc_ierrors++; 829*8a324c92SDan McDonald virtio_free_chain(ve); 830*8a324c92SDan McDonald continue; 831*8a324c92SDan McDonald } 832*8a324c92SDan McDonald 833*8a324c92SDan McDonald len -= sizeof (struct virtio_net_hdr); 834*8a324c92SDan McDonald /* 835*8a324c92SDan McDonald * We copy small packets that happenned to fit into a single 836*8a324c92SDan McDonald * cookie and reuse the buffers. For bigger ones, we loan 837*8a324c92SDan McDonald * the buffers upstream. 838*8a324c92SDan McDonald */ 839*8a324c92SDan McDonald if (len < sc->sc_rxcopy_thresh) { 840*8a324c92SDan McDonald mp = allocb(len, 0); 841*8a324c92SDan McDonald if (!mp) { 842*8a324c92SDan McDonald sc->sc_norecvbuf++; 843*8a324c92SDan McDonald sc->sc_ierrors++; 844*8a324c92SDan McDonald 845*8a324c92SDan McDonald virtio_free_chain(ve); 846*8a324c92SDan McDonald break; 847*8a324c92SDan McDonald } 848*8a324c92SDan McDonald 849*8a324c92SDan McDonald bcopy((char *)buf->rb_mapping.vbm_buf + 850*8a324c92SDan McDonald sizeof (struct virtio_net_hdr), mp->b_rptr, len); 851*8a324c92SDan McDonald mp->b_wptr = mp->b_rptr + len; 852*8a324c92SDan McDonald 853*8a324c92SDan McDonald } else { 854*8a324c92SDan McDonald mp = desballoc((unsigned char *) 855*8a324c92SDan McDonald buf->rb_mapping.vbm_buf + 856*8a324c92SDan McDonald sizeof (struct virtio_net_hdr) + 857*8a324c92SDan McDonald VIOIF_IP_ALIGN, len, 0, &buf->rb_frtn); 858*8a324c92SDan McDonald if (!mp) { 859*8a324c92SDan McDonald sc->sc_norecvbuf++; 860*8a324c92SDan McDonald sc->sc_ierrors++; 861*8a324c92SDan McDonald 862*8a324c92SDan McDonald virtio_free_chain(ve); 863*8a324c92SDan McDonald break; 864*8a324c92SDan McDonald } 865*8a324c92SDan McDonald mp->b_wptr = mp->b_rptr + len; 866*8a324c92SDan McDonald 867*8a324c92SDan McDonald atomic_inc_ulong(&sc->sc_rxloan); 868*8a324c92SDan McDonald /* 869*8a324c92SDan McDonald * Buffer loaned, we will have to allocate a new one 870*8a324c92SDan McDonald * for this slot. 871*8a324c92SDan McDonald */ 872*8a324c92SDan McDonald sc->sc_rxbufs[ve->qe_index] = NULL; 873*8a324c92SDan McDonald } 874*8a324c92SDan McDonald 875*8a324c92SDan McDonald /* 876*8a324c92SDan McDonald * virtio-net does not tell us if this packet is multicast 877*8a324c92SDan McDonald * or broadcast, so we have to check it. 878*8a324c92SDan McDonald */ 879*8a324c92SDan McDonald if (mp->b_rptr[0] & 0x1) { 880*8a324c92SDan McDonald if (bcmp(mp->b_rptr, vioif_broadcast, ETHERADDRL) != 0) 881*8a324c92SDan McDonald sc->sc_multircv++; 882*8a324c92SDan McDonald else 883*8a324c92SDan McDonald sc->sc_brdcstrcv++; 884*8a324c92SDan McDonald } 885*8a324c92SDan McDonald 886*8a324c92SDan McDonald sc->sc_rbytes += len; 887*8a324c92SDan McDonald sc->sc_ipackets++; 888*8a324c92SDan McDonald 889*8a324c92SDan McDonald virtio_free_chain(ve); 890*8a324c92SDan McDonald mac_rx(sc->sc_mac_handle, NULL, mp); 891*8a324c92SDan McDonald i++; 892*8a324c92SDan McDonald } 893*8a324c92SDan McDonald 894*8a324c92SDan McDonald return (i); 895*8a324c92SDan McDonald } 896*8a324c92SDan McDonald 897*8a324c92SDan McDonald static void 898*8a324c92SDan McDonald vioif_reclaim_used_tx(struct vioif_softc *sc) 899*8a324c92SDan McDonald { 900*8a324c92SDan McDonald struct vq_entry *ve; 901*8a324c92SDan McDonald struct vioif_tx_buf *buf; 902*8a324c92SDan McDonald uint32_t len; 903*8a324c92SDan McDonald mblk_t *mp; 904*8a324c92SDan McDonald int i = 0; 905*8a324c92SDan McDonald 906*8a324c92SDan McDonald while ((ve = virtio_pull_chain(sc->sc_tx_vq, &len))) { 907*8a324c92SDan McDonald /* We don't chain descriptors for tx, so don't expect any. */ 908*8a324c92SDan McDonald ASSERT(!ve->qe_next); 909*8a324c92SDan McDonald 910*8a324c92SDan McDonald buf = &sc->sc_txbufs[ve->qe_index]; 911*8a324c92SDan McDonald mp = buf->tb_mp; 912*8a324c92SDan McDonald buf->tb_mp = NULL; 913*8a324c92SDan McDonald 914*8a324c92SDan McDonald if (mp) { 915*8a324c92SDan McDonald for (i = 0; i < buf->tb_external_num; i++) 916*8a324c92SDan McDonald (void) ddi_dma_unbind_handle( 917*8a324c92SDan McDonald buf->tb_external_mapping[i].vbm_dmah); 918*8a324c92SDan McDonald } 919*8a324c92SDan McDonald 920*8a324c92SDan McDonald virtio_free_chain(ve); 921*8a324c92SDan McDonald 922*8a324c92SDan McDonald /* External mapping used, mp was not freed in vioif_send() */ 923*8a324c92SDan McDonald if (mp) 924*8a324c92SDan McDonald freemsg(mp); 925*8a324c92SDan McDonald i++; 926*8a324c92SDan McDonald } 927*8a324c92SDan McDonald 928*8a324c92SDan McDonald if (sc->sc_tx_stopped && i) { 929*8a324c92SDan McDonald sc->sc_tx_stopped = 0; 930*8a324c92SDan McDonald mac_tx_update(sc->sc_mac_handle); 931*8a324c92SDan McDonald } 932*8a324c92SDan McDonald } 933*8a324c92SDan McDonald 934*8a324c92SDan McDonald /* sc will be used to update stat counters. */ 935*8a324c92SDan McDonald /* ARGSUSED */ 936*8a324c92SDan McDonald static inline void 937*8a324c92SDan McDonald vioif_tx_inline(struct vioif_softc *sc, struct vq_entry *ve, mblk_t *mp, 938*8a324c92SDan McDonald size_t msg_size) 939*8a324c92SDan McDonald { 940*8a324c92SDan McDonald struct vioif_tx_buf *buf; 941*8a324c92SDan McDonald buf = &sc->sc_txbufs[ve->qe_index]; 942*8a324c92SDan McDonald 943*8a324c92SDan McDonald ASSERT(buf); 944*8a324c92SDan McDonald 945*8a324c92SDan McDonald /* Frees mp */ 946*8a324c92SDan McDonald mcopymsg(mp, buf->tb_inline_mapping.vbm_buf + 947*8a324c92SDan McDonald sizeof (struct virtio_net_hdr)); 948*8a324c92SDan McDonald 949*8a324c92SDan McDonald virtio_ve_add_indirect_buf(ve, 950*8a324c92SDan McDonald buf->tb_inline_mapping.vbm_dmac.dmac_laddress + 951*8a324c92SDan McDonald sizeof (struct virtio_net_hdr), msg_size, B_TRUE); 952*8a324c92SDan McDonald } 953*8a324c92SDan McDonald 954*8a324c92SDan McDonald static inline int 955*8a324c92SDan McDonald vioif_tx_lazy_handle_alloc(struct vioif_softc *sc, struct vioif_tx_buf *buf, 956*8a324c92SDan McDonald int i) 957*8a324c92SDan McDonald { 958*8a324c92SDan McDonald int ret = DDI_SUCCESS; 959*8a324c92SDan McDonald 960*8a324c92SDan McDonald if (!buf->tb_external_mapping[i].vbm_dmah) { 961*8a324c92SDan McDonald ret = ddi_dma_alloc_handle(sc->sc_dev, 962*8a324c92SDan McDonald &vioif_mapped_buf_dma_attr, DDI_DMA_SLEEP, NULL, 963*8a324c92SDan McDonald &buf->tb_external_mapping[i].vbm_dmah); 964*8a324c92SDan McDonald if (ret != DDI_SUCCESS) { 965*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_WARN, 966*8a324c92SDan McDonald "Can't allocate dma handle for external tx buffer"); 967*8a324c92SDan McDonald } 968*8a324c92SDan McDonald } 969*8a324c92SDan McDonald 970*8a324c92SDan McDonald return (ret); 971*8a324c92SDan McDonald } 972*8a324c92SDan McDonald 973*8a324c92SDan McDonald static inline int 974*8a324c92SDan McDonald vioif_tx_external(struct vioif_softc *sc, struct vq_entry *ve, mblk_t *mp, 975*8a324c92SDan McDonald size_t msg_size) 976*8a324c92SDan McDonald { 977*8a324c92SDan McDonald _NOTE(ARGUNUSED(msg_size)); 978*8a324c92SDan McDonald 979*8a324c92SDan McDonald struct vioif_tx_buf *buf; 980*8a324c92SDan McDonald mblk_t *nmp; 981*8a324c92SDan McDonald int i, j; 982*8a324c92SDan McDonald int ret = DDI_SUCCESS; 983*8a324c92SDan McDonald 984*8a324c92SDan McDonald buf = &sc->sc_txbufs[ve->qe_index]; 985*8a324c92SDan McDonald 986*8a324c92SDan McDonald ASSERT(buf); 987*8a324c92SDan McDonald 988*8a324c92SDan McDonald buf->tb_external_num = 0; 989*8a324c92SDan McDonald i = 0; 990*8a324c92SDan McDonald nmp = mp; 991*8a324c92SDan McDonald 992*8a324c92SDan McDonald while (nmp) { 993*8a324c92SDan McDonald size_t len; 994*8a324c92SDan McDonald ddi_dma_cookie_t dmac; 995*8a324c92SDan McDonald unsigned int ncookies; 996*8a324c92SDan McDonald 997*8a324c92SDan McDonald len = MBLKL(nmp); 998*8a324c92SDan McDonald /* 999*8a324c92SDan McDonald * For some reason, the network stack can 1000*8a324c92SDan McDonald * actually send us zero-length fragments. 1001*8a324c92SDan McDonald */ 1002*8a324c92SDan McDonald if (len == 0) { 1003*8a324c92SDan McDonald nmp = nmp->b_cont; 1004*8a324c92SDan McDonald continue; 1005*8a324c92SDan McDonald } 1006*8a324c92SDan McDonald 1007*8a324c92SDan McDonald ret = vioif_tx_lazy_handle_alloc(sc, buf, i); 1008*8a324c92SDan McDonald if (ret != DDI_SUCCESS) { 1009*8a324c92SDan McDonald sc->sc_notxbuf++; 1010*8a324c92SDan McDonald sc->sc_oerrors++; 1011*8a324c92SDan McDonald goto exit_lazy_alloc; 1012*8a324c92SDan McDonald } 1013*8a324c92SDan McDonald ret = ddi_dma_addr_bind_handle( 1014*8a324c92SDan McDonald buf->tb_external_mapping[i].vbm_dmah, NULL, 1015*8a324c92SDan McDonald (caddr_t)nmp->b_rptr, len, 1016*8a324c92SDan McDonald DDI_DMA_WRITE | DDI_DMA_STREAMING, 1017*8a324c92SDan McDonald DDI_DMA_SLEEP, NULL, &dmac, &ncookies); 1018*8a324c92SDan McDonald 1019*8a324c92SDan McDonald if (ret != DDI_SUCCESS) { 1020*8a324c92SDan McDonald sc->sc_oerrors++; 1021*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_NOTE, 1022*8a324c92SDan McDonald "TX: Failed to bind external handle"); 1023*8a324c92SDan McDonald goto exit_bind; 1024*8a324c92SDan McDonald } 1025*8a324c92SDan McDonald 1026*8a324c92SDan McDonald /* Check if we still fit into the indirect table. */ 1027*8a324c92SDan McDonald if (virtio_ve_indirect_available(ve) < ncookies) { 1028*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_NOTE, 1029*8a324c92SDan McDonald "TX: Indirect descriptor table limit reached." 1030*8a324c92SDan McDonald " It took %d fragments.", i); 1031*8a324c92SDan McDonald sc->sc_notxbuf++; 1032*8a324c92SDan McDonald sc->sc_oerrors++; 1033*8a324c92SDan McDonald 1034*8a324c92SDan McDonald ret = DDI_FAILURE; 1035*8a324c92SDan McDonald goto exit_limit; 1036*8a324c92SDan McDonald } 1037*8a324c92SDan McDonald 1038*8a324c92SDan McDonald virtio_ve_add_cookie(ve, buf->tb_external_mapping[i].vbm_dmah, 1039*8a324c92SDan McDonald dmac, ncookies, B_TRUE); 1040*8a324c92SDan McDonald 1041*8a324c92SDan McDonald nmp = nmp->b_cont; 1042*8a324c92SDan McDonald i++; 1043*8a324c92SDan McDonald } 1044*8a324c92SDan McDonald 1045*8a324c92SDan McDonald buf->tb_external_num = i; 1046*8a324c92SDan McDonald /* Save the mp to free it when the packet is sent. */ 1047*8a324c92SDan McDonald buf->tb_mp = mp; 1048*8a324c92SDan McDonald 1049*8a324c92SDan McDonald return (DDI_SUCCESS); 1050*8a324c92SDan McDonald 1051*8a324c92SDan McDonald exit_limit: 1052*8a324c92SDan McDonald exit_bind: 1053*8a324c92SDan McDonald exit_lazy_alloc: 1054*8a324c92SDan McDonald 1055*8a324c92SDan McDonald for (j = 0; j < i; j++) { 1056*8a324c92SDan McDonald (void) ddi_dma_unbind_handle( 1057*8a324c92SDan McDonald buf->tb_external_mapping[j].vbm_dmah); 1058*8a324c92SDan McDonald } 1059*8a324c92SDan McDonald 1060*8a324c92SDan McDonald return (ret); 1061*8a324c92SDan McDonald } 1062*8a324c92SDan McDonald 1063*8a324c92SDan McDonald static boolean_t 1064*8a324c92SDan McDonald vioif_send(struct vioif_softc *sc, mblk_t *mp) 1065*8a324c92SDan McDonald { 1066*8a324c92SDan McDonald struct vq_entry *ve; 1067*8a324c92SDan McDonald struct vioif_tx_buf *buf; 1068*8a324c92SDan McDonald struct virtio_net_hdr *net_header = NULL; 1069*8a324c92SDan McDonald size_t msg_size = 0; 1070*8a324c92SDan McDonald uint32_t csum_start; 1071*8a324c92SDan McDonald uint32_t csum_stuff; 1072*8a324c92SDan McDonald uint32_t csum_flags; 1073*8a324c92SDan McDonald uint32_t lso_flags; 1074*8a324c92SDan McDonald uint32_t lso_mss; 1075*8a324c92SDan McDonald mblk_t *nmp; 1076*8a324c92SDan McDonald int ret; 1077*8a324c92SDan McDonald boolean_t lso_required = B_FALSE; 1078*8a324c92SDan McDonald 1079*8a324c92SDan McDonald for (nmp = mp; nmp; nmp = nmp->b_cont) 1080*8a324c92SDan McDonald msg_size += MBLKL(nmp); 1081*8a324c92SDan McDonald 1082*8a324c92SDan McDonald if (sc->sc_tx_tso4) { 1083*8a324c92SDan McDonald mac_lso_get(mp, &lso_mss, &lso_flags); 1084*8a324c92SDan McDonald lso_required = (lso_flags & HW_LSO); 1085*8a324c92SDan McDonald } 1086*8a324c92SDan McDonald 1087*8a324c92SDan McDonald ve = vq_alloc_entry(sc->sc_tx_vq); 1088*8a324c92SDan McDonald 1089*8a324c92SDan McDonald if (!ve) { 1090*8a324c92SDan McDonald sc->sc_notxbuf++; 1091*8a324c92SDan McDonald /* Out of free descriptors - try later. */ 1092*8a324c92SDan McDonald return (B_FALSE); 1093*8a324c92SDan McDonald } 1094*8a324c92SDan McDonald buf = &sc->sc_txbufs[ve->qe_index]; 1095*8a324c92SDan McDonald 1096*8a324c92SDan McDonald /* Use the inline buffer of the first entry for the virtio_net_hdr. */ 1097*8a324c92SDan McDonald (void) memset(buf->tb_inline_mapping.vbm_buf, 0, 1098*8a324c92SDan McDonald sizeof (struct virtio_net_hdr)); 1099*8a324c92SDan McDonald 1100*8a324c92SDan McDonald net_header = (struct virtio_net_hdr *)buf->tb_inline_mapping.vbm_buf; 1101*8a324c92SDan McDonald 1102*8a324c92SDan McDonald mac_hcksum_get(mp, &csum_start, &csum_stuff, NULL, 1103*8a324c92SDan McDonald NULL, &csum_flags); 1104*8a324c92SDan McDonald 1105*8a324c92SDan McDonald /* They want us to do the TCP/UDP csum calculation. */ 1106*8a324c92SDan McDonald if (csum_flags & HCK_PARTIALCKSUM) { 1107*8a324c92SDan McDonald struct ether_header *eth_header; 1108*8a324c92SDan McDonald int eth_hsize; 1109*8a324c92SDan McDonald 1110*8a324c92SDan McDonald /* Did we ask for it? */ 1111*8a324c92SDan McDonald ASSERT(sc->sc_tx_csum); 1112*8a324c92SDan McDonald 1113*8a324c92SDan McDonald /* We only asked for partial csum packets. */ 1114*8a324c92SDan McDonald ASSERT(!(csum_flags & HCK_IPV4_HDRCKSUM)); 1115*8a324c92SDan McDonald ASSERT(!(csum_flags & HCK_FULLCKSUM)); 1116*8a324c92SDan McDonald 1117*8a324c92SDan McDonald eth_header = (void *) mp->b_rptr; 1118*8a324c92SDan McDonald if (eth_header->ether_type == htons(ETHERTYPE_VLAN)) { 1119*8a324c92SDan McDonald eth_hsize = sizeof (struct ether_vlan_header); 1120*8a324c92SDan McDonald } else { 1121*8a324c92SDan McDonald eth_hsize = sizeof (struct ether_header); 1122*8a324c92SDan McDonald } 1123*8a324c92SDan McDonald net_header->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; 1124*8a324c92SDan McDonald net_header->csum_start = eth_hsize + csum_start; 1125*8a324c92SDan McDonald net_header->csum_offset = csum_stuff - csum_start; 1126*8a324c92SDan McDonald } 1127*8a324c92SDan McDonald 1128*8a324c92SDan McDonald /* setup LSO fields if required */ 1129*8a324c92SDan McDonald if (lso_required) { 1130*8a324c92SDan McDonald net_header->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; 1131*8a324c92SDan McDonald net_header->gso_size = (uint16_t)lso_mss; 1132*8a324c92SDan McDonald } 1133*8a324c92SDan McDonald 1134*8a324c92SDan McDonald virtio_ve_add_indirect_buf(ve, 1135*8a324c92SDan McDonald buf->tb_inline_mapping.vbm_dmac.dmac_laddress, 1136*8a324c92SDan McDonald sizeof (struct virtio_net_hdr), B_TRUE); 1137*8a324c92SDan McDonald 1138*8a324c92SDan McDonald /* meanwhile update the statistic */ 1139*8a324c92SDan McDonald if (mp->b_rptr[0] & 0x1) { 1140*8a324c92SDan McDonald if (bcmp(mp->b_rptr, vioif_broadcast, ETHERADDRL) != 0) 1141*8a324c92SDan McDonald sc->sc_multixmt++; 1142*8a324c92SDan McDonald else 1143*8a324c92SDan McDonald sc->sc_brdcstxmt++; 1144*8a324c92SDan McDonald } 1145*8a324c92SDan McDonald 1146*8a324c92SDan McDonald /* 1147*8a324c92SDan McDonald * We copy small packets into the inline buffer. The bigger ones 1148*8a324c92SDan McDonald * get mapped using the mapped buffer. 1149*8a324c92SDan McDonald */ 1150*8a324c92SDan McDonald if (msg_size < sc->sc_txcopy_thresh) { 1151*8a324c92SDan McDonald vioif_tx_inline(sc, ve, mp, msg_size); 1152*8a324c92SDan McDonald } else { 1153*8a324c92SDan McDonald /* statistic gets updated by vioif_tx_external when fail */ 1154*8a324c92SDan McDonald ret = vioif_tx_external(sc, ve, mp, msg_size); 1155*8a324c92SDan McDonald if (ret != DDI_SUCCESS) 1156*8a324c92SDan McDonald goto exit_tx_external; 1157*8a324c92SDan McDonald } 1158*8a324c92SDan McDonald 1159*8a324c92SDan McDonald virtio_push_chain(ve, B_TRUE); 1160*8a324c92SDan McDonald 1161*8a324c92SDan McDonald sc->sc_opackets++; 1162*8a324c92SDan McDonald sc->sc_obytes += msg_size; 1163*8a324c92SDan McDonald 1164*8a324c92SDan McDonald return (B_TRUE); 1165*8a324c92SDan McDonald 1166*8a324c92SDan McDonald exit_tx_external: 1167*8a324c92SDan McDonald 1168*8a324c92SDan McDonald vq_free_entry(sc->sc_tx_vq, ve); 1169*8a324c92SDan McDonald /* 1170*8a324c92SDan McDonald * vioif_tx_external can fail when the buffer does not fit into the 1171*8a324c92SDan McDonald * indirect descriptor table. Free the mp. I don't expect this ever 1172*8a324c92SDan McDonald * to happen. 1173*8a324c92SDan McDonald */ 1174*8a324c92SDan McDonald freemsg(mp); 1175*8a324c92SDan McDonald 1176*8a324c92SDan McDonald return (B_TRUE); 1177*8a324c92SDan McDonald } 1178*8a324c92SDan McDonald 1179*8a324c92SDan McDonald mblk_t * 1180*8a324c92SDan McDonald vioif_tx(void *arg, mblk_t *mp) 1181*8a324c92SDan McDonald { 1182*8a324c92SDan McDonald struct vioif_softc *sc = arg; 1183*8a324c92SDan McDonald mblk_t *nmp; 1184*8a324c92SDan McDonald 1185*8a324c92SDan McDonald while (mp != NULL) { 1186*8a324c92SDan McDonald nmp = mp->b_next; 1187*8a324c92SDan McDonald mp->b_next = NULL; 1188*8a324c92SDan McDonald 1189*8a324c92SDan McDonald if (!vioif_send(sc, mp)) { 1190*8a324c92SDan McDonald sc->sc_tx_stopped = 1; 1191*8a324c92SDan McDonald mp->b_next = nmp; 1192*8a324c92SDan McDonald break; 1193*8a324c92SDan McDonald } 1194*8a324c92SDan McDonald mp = nmp; 1195*8a324c92SDan McDonald } 1196*8a324c92SDan McDonald 1197*8a324c92SDan McDonald return (mp); 1198*8a324c92SDan McDonald } 1199*8a324c92SDan McDonald 1200*8a324c92SDan McDonald int 1201*8a324c92SDan McDonald vioif_start(void *arg) 1202*8a324c92SDan McDonald { 1203*8a324c92SDan McDonald struct vioif_softc *sc = arg; 1204*8a324c92SDan McDonald 1205*8a324c92SDan McDonald mac_link_update(sc->sc_mac_handle, 1206*8a324c92SDan McDonald vioif_link_state(sc)); 1207*8a324c92SDan McDonald 1208*8a324c92SDan McDonald virtio_start_vq_intr(sc->sc_rx_vq); 1209*8a324c92SDan McDonald 1210*8a324c92SDan McDonald return (DDI_SUCCESS); 1211*8a324c92SDan McDonald } 1212*8a324c92SDan McDonald 1213*8a324c92SDan McDonald void 1214*8a324c92SDan McDonald vioif_stop(void *arg) 1215*8a324c92SDan McDonald { 1216*8a324c92SDan McDonald struct vioif_softc *sc = arg; 1217*8a324c92SDan McDonald 1218*8a324c92SDan McDonald virtio_stop_vq_intr(sc->sc_rx_vq); 1219*8a324c92SDan McDonald } 1220*8a324c92SDan McDonald 1221*8a324c92SDan McDonald /* ARGSUSED */ 1222*8a324c92SDan McDonald static int 1223*8a324c92SDan McDonald vioif_stat(void *arg, uint_t stat, uint64_t *val) 1224*8a324c92SDan McDonald { 1225*8a324c92SDan McDonald struct vioif_softc *sc = arg; 1226*8a324c92SDan McDonald 1227*8a324c92SDan McDonald switch (stat) { 1228*8a324c92SDan McDonald case MAC_STAT_IERRORS: 1229*8a324c92SDan McDonald *val = sc->sc_ierrors; 1230*8a324c92SDan McDonald break; 1231*8a324c92SDan McDonald case MAC_STAT_OERRORS: 1232*8a324c92SDan McDonald *val = sc->sc_oerrors; 1233*8a324c92SDan McDonald break; 1234*8a324c92SDan McDonald case MAC_STAT_MULTIRCV: 1235*8a324c92SDan McDonald *val = sc->sc_multircv; 1236*8a324c92SDan McDonald break; 1237*8a324c92SDan McDonald case MAC_STAT_BRDCSTRCV: 1238*8a324c92SDan McDonald *val = sc->sc_brdcstrcv; 1239*8a324c92SDan McDonald break; 1240*8a324c92SDan McDonald case MAC_STAT_MULTIXMT: 1241*8a324c92SDan McDonald *val = sc->sc_multixmt; 1242*8a324c92SDan McDonald break; 1243*8a324c92SDan McDonald case MAC_STAT_BRDCSTXMT: 1244*8a324c92SDan McDonald *val = sc->sc_brdcstxmt; 1245*8a324c92SDan McDonald break; 1246*8a324c92SDan McDonald case MAC_STAT_IPACKETS: 1247*8a324c92SDan McDonald *val = sc->sc_ipackets; 1248*8a324c92SDan McDonald break; 1249*8a324c92SDan McDonald case MAC_STAT_RBYTES: 1250*8a324c92SDan McDonald *val = sc->sc_rbytes; 1251*8a324c92SDan McDonald break; 1252*8a324c92SDan McDonald case MAC_STAT_OPACKETS: 1253*8a324c92SDan McDonald *val = sc->sc_opackets; 1254*8a324c92SDan McDonald break; 1255*8a324c92SDan McDonald case MAC_STAT_OBYTES: 1256*8a324c92SDan McDonald *val = sc->sc_obytes; 1257*8a324c92SDan McDonald break; 1258*8a324c92SDan McDonald case MAC_STAT_NORCVBUF: 1259*8a324c92SDan McDonald *val = sc->sc_norecvbuf; 1260*8a324c92SDan McDonald break; 1261*8a324c92SDan McDonald case MAC_STAT_NOXMTBUF: 1262*8a324c92SDan McDonald *val = sc->sc_notxbuf; 1263*8a324c92SDan McDonald break; 1264*8a324c92SDan McDonald case MAC_STAT_IFSPEED: 1265*8a324c92SDan McDonald /* always 1 Gbit */ 1266*8a324c92SDan McDonald *val = 1000000000ULL; 1267*8a324c92SDan McDonald break; 1268*8a324c92SDan McDonald case ETHER_STAT_LINK_DUPLEX: 1269*8a324c92SDan McDonald /* virtual device, always full-duplex */ 1270*8a324c92SDan McDonald *val = LINK_DUPLEX_FULL; 1271*8a324c92SDan McDonald break; 1272*8a324c92SDan McDonald 1273*8a324c92SDan McDonald default: 1274*8a324c92SDan McDonald return (ENOTSUP); 1275*8a324c92SDan McDonald } 1276*8a324c92SDan McDonald 1277*8a324c92SDan McDonald return (DDI_SUCCESS); 1278*8a324c92SDan McDonald } 1279*8a324c92SDan McDonald 1280*8a324c92SDan McDonald static int 1281*8a324c92SDan McDonald vioif_set_prop_private(struct vioif_softc *sc, const char *pr_name, 1282*8a324c92SDan McDonald uint_t pr_valsize, const void *pr_val) 1283*8a324c92SDan McDonald { 1284*8a324c92SDan McDonald _NOTE(ARGUNUSED(pr_valsize)); 1285*8a324c92SDan McDonald 1286*8a324c92SDan McDonald long result; 1287*8a324c92SDan McDonald 1288*8a324c92SDan McDonald if (strcmp(pr_name, vioif_txcopy_thresh) == 0) { 1289*8a324c92SDan McDonald 1290*8a324c92SDan McDonald if (pr_val == NULL) 1291*8a324c92SDan McDonald return (EINVAL); 1292*8a324c92SDan McDonald 1293*8a324c92SDan McDonald (void) ddi_strtol(pr_val, (char **)NULL, 0, &result); 1294*8a324c92SDan McDonald 1295*8a324c92SDan McDonald if (result < 0 || result > VIOIF_TX_THRESH_MAX) 1296*8a324c92SDan McDonald return (EINVAL); 1297*8a324c92SDan McDonald sc->sc_txcopy_thresh = result; 1298*8a324c92SDan McDonald } 1299*8a324c92SDan McDonald if (strcmp(pr_name, vioif_rxcopy_thresh) == 0) { 1300*8a324c92SDan McDonald 1301*8a324c92SDan McDonald if (pr_val == NULL) 1302*8a324c92SDan McDonald return (EINVAL); 1303*8a324c92SDan McDonald 1304*8a324c92SDan McDonald (void) ddi_strtol(pr_val, (char **)NULL, 0, &result); 1305*8a324c92SDan McDonald 1306*8a324c92SDan McDonald if (result < 0 || result > VIOIF_RX_THRESH_MAX) 1307*8a324c92SDan McDonald return (EINVAL); 1308*8a324c92SDan McDonald sc->sc_rxcopy_thresh = result; 1309*8a324c92SDan McDonald } 1310*8a324c92SDan McDonald return (0); 1311*8a324c92SDan McDonald } 1312*8a324c92SDan McDonald 1313*8a324c92SDan McDonald static int 1314*8a324c92SDan McDonald vioif_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num, 1315*8a324c92SDan McDonald uint_t pr_valsize, const void *pr_val) 1316*8a324c92SDan McDonald { 1317*8a324c92SDan McDonald struct vioif_softc *sc = arg; 1318*8a324c92SDan McDonald const uint32_t *new_mtu; 1319*8a324c92SDan McDonald int err; 1320*8a324c92SDan McDonald 1321*8a324c92SDan McDonald switch (pr_num) { 1322*8a324c92SDan McDonald case MAC_PROP_MTU: 1323*8a324c92SDan McDonald new_mtu = pr_val; 1324*8a324c92SDan McDonald 1325*8a324c92SDan McDonald if (*new_mtu > MAX_MTU) { 1326*8a324c92SDan McDonald return (EINVAL); 1327*8a324c92SDan McDonald } 1328*8a324c92SDan McDonald 1329*8a324c92SDan McDonald err = mac_maxsdu_update(sc->sc_mac_handle, *new_mtu); 1330*8a324c92SDan McDonald if (err) { 1331*8a324c92SDan McDonald return (err); 1332*8a324c92SDan McDonald } 1333*8a324c92SDan McDonald break; 1334*8a324c92SDan McDonald case MAC_PROP_PRIVATE: 1335*8a324c92SDan McDonald err = vioif_set_prop_private(sc, pr_name, 1336*8a324c92SDan McDonald pr_valsize, pr_val); 1337*8a324c92SDan McDonald if (err) 1338*8a324c92SDan McDonald return (err); 1339*8a324c92SDan McDonald break; 1340*8a324c92SDan McDonald default: 1341*8a324c92SDan McDonald return (ENOTSUP); 1342*8a324c92SDan McDonald } 1343*8a324c92SDan McDonald 1344*8a324c92SDan McDonald return (0); 1345*8a324c92SDan McDonald } 1346*8a324c92SDan McDonald 1347*8a324c92SDan McDonald static int 1348*8a324c92SDan McDonald vioif_get_prop_private(struct vioif_softc *sc, const char *pr_name, 1349*8a324c92SDan McDonald uint_t pr_valsize, void *pr_val) 1350*8a324c92SDan McDonald { 1351*8a324c92SDan McDonald int err = ENOTSUP; 1352*8a324c92SDan McDonald int value; 1353*8a324c92SDan McDonald 1354*8a324c92SDan McDonald if (strcmp(pr_name, vioif_txcopy_thresh) == 0) { 1355*8a324c92SDan McDonald 1356*8a324c92SDan McDonald value = sc->sc_txcopy_thresh; 1357*8a324c92SDan McDonald err = 0; 1358*8a324c92SDan McDonald goto done; 1359*8a324c92SDan McDonald } 1360*8a324c92SDan McDonald if (strcmp(pr_name, vioif_rxcopy_thresh) == 0) { 1361*8a324c92SDan McDonald 1362*8a324c92SDan McDonald value = sc->sc_rxcopy_thresh; 1363*8a324c92SDan McDonald err = 0; 1364*8a324c92SDan McDonald goto done; 1365*8a324c92SDan McDonald } 1366*8a324c92SDan McDonald done: 1367*8a324c92SDan McDonald if (err == 0) { 1368*8a324c92SDan McDonald (void) snprintf(pr_val, pr_valsize, "%d", value); 1369*8a324c92SDan McDonald } 1370*8a324c92SDan McDonald return (err); 1371*8a324c92SDan McDonald } 1372*8a324c92SDan McDonald 1373*8a324c92SDan McDonald static int 1374*8a324c92SDan McDonald vioif_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num, 1375*8a324c92SDan McDonald uint_t pr_valsize, void *pr_val) 1376*8a324c92SDan McDonald { 1377*8a324c92SDan McDonald struct vioif_softc *sc = arg; 1378*8a324c92SDan McDonald int err = ENOTSUP; 1379*8a324c92SDan McDonald 1380*8a324c92SDan McDonald switch (pr_num) { 1381*8a324c92SDan McDonald case MAC_PROP_PRIVATE: 1382*8a324c92SDan McDonald err = vioif_get_prop_private(sc, pr_name, 1383*8a324c92SDan McDonald pr_valsize, pr_val); 1384*8a324c92SDan McDonald break; 1385*8a324c92SDan McDonald default: 1386*8a324c92SDan McDonald break; 1387*8a324c92SDan McDonald } 1388*8a324c92SDan McDonald return (err); 1389*8a324c92SDan McDonald } 1390*8a324c92SDan McDonald 1391*8a324c92SDan McDonald static void 1392*8a324c92SDan McDonald vioif_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num, 1393*8a324c92SDan McDonald mac_prop_info_handle_t prh) 1394*8a324c92SDan McDonald { 1395*8a324c92SDan McDonald struct vioif_softc *sc = arg; 1396*8a324c92SDan McDonald char valstr[64]; 1397*8a324c92SDan McDonald int value; 1398*8a324c92SDan McDonald 1399*8a324c92SDan McDonald switch (pr_num) { 1400*8a324c92SDan McDonald case MAC_PROP_MTU: 1401*8a324c92SDan McDonald mac_prop_info_set_range_uint32(prh, ETHERMIN, MAX_MTU); 1402*8a324c92SDan McDonald break; 1403*8a324c92SDan McDonald 1404*8a324c92SDan McDonald case MAC_PROP_PRIVATE: 1405*8a324c92SDan McDonald bzero(valstr, sizeof (valstr)); 1406*8a324c92SDan McDonald if (strcmp(pr_name, vioif_txcopy_thresh) == 0) { 1407*8a324c92SDan McDonald 1408*8a324c92SDan McDonald value = sc->sc_txcopy_thresh; 1409*8a324c92SDan McDonald } else if (strcmp(pr_name, 1410*8a324c92SDan McDonald vioif_rxcopy_thresh) == 0) { 1411*8a324c92SDan McDonald value = sc->sc_rxcopy_thresh; 1412*8a324c92SDan McDonald } else { 1413*8a324c92SDan McDonald return; 1414*8a324c92SDan McDonald } 1415*8a324c92SDan McDonald (void) snprintf(valstr, sizeof (valstr), "%d", value); 1416*8a324c92SDan McDonald break; 1417*8a324c92SDan McDonald 1418*8a324c92SDan McDonald default: 1419*8a324c92SDan McDonald break; 1420*8a324c92SDan McDonald } 1421*8a324c92SDan McDonald } 1422*8a324c92SDan McDonald 1423*8a324c92SDan McDonald static boolean_t 1424*8a324c92SDan McDonald vioif_getcapab(void *arg, mac_capab_t cap, void *cap_data) 1425*8a324c92SDan McDonald { 1426*8a324c92SDan McDonald struct vioif_softc *sc = arg; 1427*8a324c92SDan McDonald 1428*8a324c92SDan McDonald switch (cap) { 1429*8a324c92SDan McDonald case MAC_CAPAB_HCKSUM: 1430*8a324c92SDan McDonald if (sc->sc_tx_csum) { 1431*8a324c92SDan McDonald uint32_t *txflags = cap_data; 1432*8a324c92SDan McDonald 1433*8a324c92SDan McDonald *txflags = HCKSUM_INET_PARTIAL; 1434*8a324c92SDan McDonald return (B_TRUE); 1435*8a324c92SDan McDonald } 1436*8a324c92SDan McDonald return (B_FALSE); 1437*8a324c92SDan McDonald case MAC_CAPAB_LSO: 1438*8a324c92SDan McDonald if (sc->sc_tx_tso4) { 1439*8a324c92SDan McDonald mac_capab_lso_t *cap_lso = cap_data; 1440*8a324c92SDan McDonald 1441*8a324c92SDan McDonald cap_lso->lso_flags = LSO_TX_BASIC_TCP_IPV4; 1442*8a324c92SDan McDonald cap_lso->lso_basic_tcp_ipv4.lso_max = MAX_MTU; 1443*8a324c92SDan McDonald return (B_TRUE); 1444*8a324c92SDan McDonald } 1445*8a324c92SDan McDonald return (B_FALSE); 1446*8a324c92SDan McDonald default: 1447*8a324c92SDan McDonald break; 1448*8a324c92SDan McDonald } 1449*8a324c92SDan McDonald return (B_FALSE); 1450*8a324c92SDan McDonald } 1451*8a324c92SDan McDonald 1452*8a324c92SDan McDonald static mac_callbacks_t vioif_m_callbacks = { 1453*8a324c92SDan McDonald .mc_callbacks = (MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO), 1454*8a324c92SDan McDonald .mc_getstat = vioif_stat, 1455*8a324c92SDan McDonald .mc_start = vioif_start, 1456*8a324c92SDan McDonald .mc_stop = vioif_stop, 1457*8a324c92SDan McDonald .mc_setpromisc = vioif_promisc, 1458*8a324c92SDan McDonald .mc_multicst = vioif_multicst, 1459*8a324c92SDan McDonald .mc_unicst = vioif_unicst, 1460*8a324c92SDan McDonald .mc_tx = vioif_tx, 1461*8a324c92SDan McDonald /* Optional callbacks */ 1462*8a324c92SDan McDonald .mc_reserved = NULL, /* reserved */ 1463*8a324c92SDan McDonald .mc_ioctl = NULL, /* mc_ioctl */ 1464*8a324c92SDan McDonald .mc_getcapab = vioif_getcapab, /* mc_getcapab */ 1465*8a324c92SDan McDonald .mc_open = NULL, /* mc_open */ 1466*8a324c92SDan McDonald .mc_close = NULL, /* mc_close */ 1467*8a324c92SDan McDonald .mc_setprop = vioif_setprop, 1468*8a324c92SDan McDonald .mc_getprop = vioif_getprop, 1469*8a324c92SDan McDonald .mc_propinfo = vioif_propinfo, 1470*8a324c92SDan McDonald }; 1471*8a324c92SDan McDonald 1472*8a324c92SDan McDonald static void 1473*8a324c92SDan McDonald vioif_show_features(struct vioif_softc *sc, const char *prefix, 1474*8a324c92SDan McDonald uint32_t features) 1475*8a324c92SDan McDonald { 1476*8a324c92SDan McDonald char buf[512]; 1477*8a324c92SDan McDonald char *bufp = buf; 1478*8a324c92SDan McDonald char *bufend = buf + sizeof (buf); 1479*8a324c92SDan McDonald 1480*8a324c92SDan McDonald /* LINTED E_PTRDIFF_OVERFLOW */ 1481*8a324c92SDan McDonald bufp += snprintf(bufp, bufend - bufp, prefix); 1482*8a324c92SDan McDonald /* LINTED E_PTRDIFF_OVERFLOW */ 1483*8a324c92SDan McDonald bufp += virtio_show_features(features, bufp, bufend - bufp); 1484*8a324c92SDan McDonald *bufp = '\0'; 1485*8a324c92SDan McDonald 1486*8a324c92SDan McDonald 1487*8a324c92SDan McDonald /* Using '!' to only CE_NOTE this to the system log. */ 1488*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_NOTE, "!%s Vioif (%b)", buf, features, 1489*8a324c92SDan McDonald VIRTIO_NET_FEATURE_BITS); 1490*8a324c92SDan McDonald } 1491*8a324c92SDan McDonald 1492*8a324c92SDan McDonald /* 1493*8a324c92SDan McDonald * Find out which features are supported by the device and 1494*8a324c92SDan McDonald * choose which ones we wish to use. 1495*8a324c92SDan McDonald */ 1496*8a324c92SDan McDonald static int 1497*8a324c92SDan McDonald vioif_dev_features(struct vioif_softc *sc) 1498*8a324c92SDan McDonald { 1499*8a324c92SDan McDonald uint32_t host_features; 1500*8a324c92SDan McDonald 1501*8a324c92SDan McDonald host_features = virtio_negotiate_features(&sc->sc_virtio, 1502*8a324c92SDan McDonald VIRTIO_NET_F_CSUM | 1503*8a324c92SDan McDonald VIRTIO_NET_F_HOST_TSO4 | 1504*8a324c92SDan McDonald VIRTIO_NET_F_HOST_ECN | 1505*8a324c92SDan McDonald VIRTIO_NET_F_MAC | 1506*8a324c92SDan McDonald VIRTIO_NET_F_STATUS | 1507*8a324c92SDan McDonald VIRTIO_F_RING_INDIRECT_DESC | 1508*8a324c92SDan McDonald VIRTIO_F_NOTIFY_ON_EMPTY); 1509*8a324c92SDan McDonald 1510*8a324c92SDan McDonald vioif_show_features(sc, "Host features: ", host_features); 1511*8a324c92SDan McDonald vioif_show_features(sc, "Negotiated features: ", 1512*8a324c92SDan McDonald sc->sc_virtio.sc_features); 1513*8a324c92SDan McDonald 1514*8a324c92SDan McDonald if (!(sc->sc_virtio.sc_features & VIRTIO_F_RING_INDIRECT_DESC)) { 1515*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_NOTE, 1516*8a324c92SDan McDonald "Host does not support RING_INDIRECT_DESC, bye."); 1517*8a324c92SDan McDonald return (DDI_FAILURE); 1518*8a324c92SDan McDonald } 1519*8a324c92SDan McDonald 1520*8a324c92SDan McDonald return (DDI_SUCCESS); 1521*8a324c92SDan McDonald } 1522*8a324c92SDan McDonald 1523*8a324c92SDan McDonald static int 1524*8a324c92SDan McDonald vioif_has_feature(struct vioif_softc *sc, uint32_t feature) 1525*8a324c92SDan McDonald { 1526*8a324c92SDan McDonald return (virtio_has_feature(&sc->sc_virtio, feature)); 1527*8a324c92SDan McDonald } 1528*8a324c92SDan McDonald 1529*8a324c92SDan McDonald static void 1530*8a324c92SDan McDonald vioif_set_mac(struct vioif_softc *sc) 1531*8a324c92SDan McDonald { 1532*8a324c92SDan McDonald int i; 1533*8a324c92SDan McDonald 1534*8a324c92SDan McDonald for (i = 0; i < ETHERADDRL; i++) { 1535*8a324c92SDan McDonald virtio_write_device_config_1(&sc->sc_virtio, 1536*8a324c92SDan McDonald VIRTIO_NET_CONFIG_MAC + i, sc->sc_mac[i]); 1537*8a324c92SDan McDonald } 1538*8a324c92SDan McDonald } 1539*8a324c92SDan McDonald 1540*8a324c92SDan McDonald /* Get the mac address out of the hardware, or make up one. */ 1541*8a324c92SDan McDonald static void 1542*8a324c92SDan McDonald vioif_get_mac(struct vioif_softc *sc) 1543*8a324c92SDan McDonald { 1544*8a324c92SDan McDonald int i; 1545*8a324c92SDan McDonald if (sc->sc_virtio.sc_features & VIRTIO_NET_F_MAC) { 1546*8a324c92SDan McDonald for (i = 0; i < ETHERADDRL; i++) { 1547*8a324c92SDan McDonald sc->sc_mac[i] = virtio_read_device_config_1( 1548*8a324c92SDan McDonald &sc->sc_virtio, 1549*8a324c92SDan McDonald VIRTIO_NET_CONFIG_MAC + i); 1550*8a324c92SDan McDonald } 1551*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_NOTE, "Got MAC address from host: %s", 1552*8a324c92SDan McDonald ether_sprintf((struct ether_addr *)sc->sc_mac)); 1553*8a324c92SDan McDonald } else { 1554*8a324c92SDan McDonald /* Get a few random bytes */ 1555*8a324c92SDan McDonald (void) random_get_pseudo_bytes(sc->sc_mac, ETHERADDRL); 1556*8a324c92SDan McDonald /* Make sure it's a unicast MAC */ 1557*8a324c92SDan McDonald sc->sc_mac[0] &= ~1; 1558*8a324c92SDan McDonald /* Set the "locally administered" bit */ 1559*8a324c92SDan McDonald sc->sc_mac[1] |= 2; 1560*8a324c92SDan McDonald 1561*8a324c92SDan McDonald vioif_set_mac(sc); 1562*8a324c92SDan McDonald 1563*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_NOTE, 1564*8a324c92SDan McDonald "Generated a random MAC address: %s", 1565*8a324c92SDan McDonald ether_sprintf((struct ether_addr *)sc->sc_mac)); 1566*8a324c92SDan McDonald } 1567*8a324c92SDan McDonald } 1568*8a324c92SDan McDonald 1569*8a324c92SDan McDonald /* 1570*8a324c92SDan McDonald * Virtqueue interrupt handlers 1571*8a324c92SDan McDonald */ 1572*8a324c92SDan McDonald /* ARGSUSED */ 1573*8a324c92SDan McDonald uint_t 1574*8a324c92SDan McDonald vioif_rx_handler(caddr_t arg1, caddr_t arg2) 1575*8a324c92SDan McDonald { 1576*8a324c92SDan McDonald struct virtio_softc *vsc = (void *) arg1; 1577*8a324c92SDan McDonald struct vioif_softc *sc = container_of(vsc, 1578*8a324c92SDan McDonald struct vioif_softc, sc_virtio); 1579*8a324c92SDan McDonald 1580*8a324c92SDan McDonald (void) vioif_process_rx(sc); 1581*8a324c92SDan McDonald 1582*8a324c92SDan McDonald (void) vioif_populate_rx(sc, KM_NOSLEEP); 1583*8a324c92SDan McDonald 1584*8a324c92SDan McDonald return (DDI_INTR_CLAIMED); 1585*8a324c92SDan McDonald } 1586*8a324c92SDan McDonald 1587*8a324c92SDan McDonald /* ARGSUSED */ 1588*8a324c92SDan McDonald uint_t 1589*8a324c92SDan McDonald vioif_tx_handler(caddr_t arg1, caddr_t arg2) 1590*8a324c92SDan McDonald { 1591*8a324c92SDan McDonald struct virtio_softc *vsc = (void *)arg1; 1592*8a324c92SDan McDonald struct vioif_softc *sc = container_of(vsc, 1593*8a324c92SDan McDonald struct vioif_softc, sc_virtio); 1594*8a324c92SDan McDonald 1595*8a324c92SDan McDonald vioif_reclaim_used_tx(sc); 1596*8a324c92SDan McDonald return (DDI_INTR_CLAIMED); 1597*8a324c92SDan McDonald } 1598*8a324c92SDan McDonald 1599*8a324c92SDan McDonald static int 1600*8a324c92SDan McDonald vioif_register_ints(struct vioif_softc *sc) 1601*8a324c92SDan McDonald { 1602*8a324c92SDan McDonald int ret; 1603*8a324c92SDan McDonald 1604*8a324c92SDan McDonald struct virtio_int_handler vioif_vq_h[] = { 1605*8a324c92SDan McDonald { vioif_rx_handler }, 1606*8a324c92SDan McDonald { vioif_tx_handler }, 1607*8a324c92SDan McDonald { NULL } 1608*8a324c92SDan McDonald }; 1609*8a324c92SDan McDonald 1610*8a324c92SDan McDonald ret = virtio_register_ints(&sc->sc_virtio, NULL, vioif_vq_h); 1611*8a324c92SDan McDonald 1612*8a324c92SDan McDonald return (ret); 1613*8a324c92SDan McDonald } 1614*8a324c92SDan McDonald 1615*8a324c92SDan McDonald 1616*8a324c92SDan McDonald static void 1617*8a324c92SDan McDonald vioif_check_features(struct vioif_softc *sc) 1618*8a324c92SDan McDonald { 1619*8a324c92SDan McDonald if (vioif_has_feature(sc, VIRTIO_NET_F_CSUM)) { 1620*8a324c92SDan McDonald /* The GSO/GRO featured depend on CSUM, check them here. */ 1621*8a324c92SDan McDonald sc->sc_tx_csum = 1; 1622*8a324c92SDan McDonald sc->sc_rx_csum = 1; 1623*8a324c92SDan McDonald 1624*8a324c92SDan McDonald if (!vioif_has_feature(sc, VIRTIO_NET_F_GUEST_CSUM)) { 1625*8a324c92SDan McDonald sc->sc_rx_csum = 0; 1626*8a324c92SDan McDonald } 1627*8a324c92SDan McDonald cmn_err(CE_NOTE, "Csum enabled."); 1628*8a324c92SDan McDonald 1629*8a324c92SDan McDonald if (vioif_has_feature(sc, VIRTIO_NET_F_HOST_TSO4)) { 1630*8a324c92SDan McDonald 1631*8a324c92SDan McDonald sc->sc_tx_tso4 = 1; 1632*8a324c92SDan McDonald /* 1633*8a324c92SDan McDonald * We don't seem to have a way to ask the system 1634*8a324c92SDan McDonald * not to send us LSO packets with Explicit 1635*8a324c92SDan McDonald * Congestion Notification bit set, so we require 1636*8a324c92SDan McDonald * the device to support it in order to do 1637*8a324c92SDan McDonald * LSO. 1638*8a324c92SDan McDonald */ 1639*8a324c92SDan McDonald if (!vioif_has_feature(sc, VIRTIO_NET_F_HOST_ECN)) { 1640*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_NOTE, 1641*8a324c92SDan McDonald "TSO4 supported, but not ECN. " 1642*8a324c92SDan McDonald "Not using LSO."); 1643*8a324c92SDan McDonald sc->sc_tx_tso4 = 0; 1644*8a324c92SDan McDonald } else { 1645*8a324c92SDan McDonald cmn_err(CE_NOTE, "LSO enabled"); 1646*8a324c92SDan McDonald } 1647*8a324c92SDan McDonald } 1648*8a324c92SDan McDonald } 1649*8a324c92SDan McDonald } 1650*8a324c92SDan McDonald 1651*8a324c92SDan McDonald static int 1652*8a324c92SDan McDonald vioif_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd) 1653*8a324c92SDan McDonald { 1654*8a324c92SDan McDonald int ret, instance; 1655*8a324c92SDan McDonald struct vioif_softc *sc; 1656*8a324c92SDan McDonald struct virtio_softc *vsc; 1657*8a324c92SDan McDonald mac_register_t *macp; 1658*8a324c92SDan McDonald char cache_name[CACHE_NAME_SIZE]; 1659*8a324c92SDan McDonald 1660*8a324c92SDan McDonald instance = ddi_get_instance(devinfo); 1661*8a324c92SDan McDonald 1662*8a324c92SDan McDonald switch (cmd) { 1663*8a324c92SDan McDonald case DDI_ATTACH: 1664*8a324c92SDan McDonald break; 1665*8a324c92SDan McDonald 1666*8a324c92SDan McDonald case DDI_RESUME: 1667*8a324c92SDan McDonald case DDI_PM_RESUME: 1668*8a324c92SDan McDonald /* We do not support suspend/resume for vioif. */ 1669*8a324c92SDan McDonald goto exit; 1670*8a324c92SDan McDonald 1671*8a324c92SDan McDonald default: 1672*8a324c92SDan McDonald goto exit; 1673*8a324c92SDan McDonald } 1674*8a324c92SDan McDonald 1675*8a324c92SDan McDonald sc = kmem_zalloc(sizeof (struct vioif_softc), KM_SLEEP); 1676*8a324c92SDan McDonald ddi_set_driver_private(devinfo, sc); 1677*8a324c92SDan McDonald 1678*8a324c92SDan McDonald vsc = &sc->sc_virtio; 1679*8a324c92SDan McDonald 1680*8a324c92SDan McDonald /* Duplicate for less typing */ 1681*8a324c92SDan McDonald sc->sc_dev = devinfo; 1682*8a324c92SDan McDonald vsc->sc_dev = devinfo; 1683*8a324c92SDan McDonald 1684*8a324c92SDan McDonald /* 1685*8a324c92SDan McDonald * Initialize interrupt kstat. 1686*8a324c92SDan McDonald */ 1687*8a324c92SDan McDonald sc->sc_intrstat = kstat_create("vioif", instance, "intr", "controller", 1688*8a324c92SDan McDonald KSTAT_TYPE_INTR, 1, 0); 1689*8a324c92SDan McDonald if (sc->sc_intrstat == NULL) { 1690*8a324c92SDan McDonald dev_err(devinfo, CE_WARN, "kstat_create failed"); 1691*8a324c92SDan McDonald goto exit_intrstat; 1692*8a324c92SDan McDonald } 1693*8a324c92SDan McDonald kstat_install(sc->sc_intrstat); 1694*8a324c92SDan McDonald 1695*8a324c92SDan McDonald /* map BAR 0 */ 1696*8a324c92SDan McDonald ret = ddi_regs_map_setup(devinfo, 1, 1697*8a324c92SDan McDonald (caddr_t *)&sc->sc_virtio.sc_io_addr, 1698*8a324c92SDan McDonald 0, 0, &vioif_attr, &sc->sc_virtio.sc_ioh); 1699*8a324c92SDan McDonald if (ret != DDI_SUCCESS) { 1700*8a324c92SDan McDonald dev_err(devinfo, CE_WARN, "unable to map bar 0: %d", ret); 1701*8a324c92SDan McDonald goto exit_map; 1702*8a324c92SDan McDonald } 1703*8a324c92SDan McDonald 1704*8a324c92SDan McDonald virtio_device_reset(&sc->sc_virtio); 1705*8a324c92SDan McDonald virtio_set_status(&sc->sc_virtio, VIRTIO_CONFIG_DEVICE_STATUS_ACK); 1706*8a324c92SDan McDonald virtio_set_status(&sc->sc_virtio, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER); 1707*8a324c92SDan McDonald 1708*8a324c92SDan McDonald ret = vioif_dev_features(sc); 1709*8a324c92SDan McDonald if (ret) 1710*8a324c92SDan McDonald goto exit_features; 1711*8a324c92SDan McDonald 1712*8a324c92SDan McDonald vsc->sc_nvqs = vioif_has_feature(sc, VIRTIO_NET_F_CTRL_VQ) ? 3 : 2; 1713*8a324c92SDan McDonald 1714*8a324c92SDan McDonald (void) snprintf(cache_name, CACHE_NAME_SIZE, "vioif%d_rx", instance); 1715*8a324c92SDan McDonald sc->sc_rxbuf_cache = kmem_cache_create(cache_name, 1716*8a324c92SDan McDonald sizeof (struct vioif_rx_buf), 0, vioif_rx_construct, 1717*8a324c92SDan McDonald vioif_rx_destruct, NULL, sc, NULL, KM_SLEEP); 1718*8a324c92SDan McDonald if (sc->sc_rxbuf_cache == NULL) { 1719*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_WARN, "Can't allocate the buffer cache"); 1720*8a324c92SDan McDonald goto exit_cache; 1721*8a324c92SDan McDonald } 1722*8a324c92SDan McDonald 1723*8a324c92SDan McDonald ret = vioif_register_ints(sc); 1724*8a324c92SDan McDonald if (ret) { 1725*8a324c92SDan McDonald dev_err(sc->sc_dev, CE_WARN, 1726*8a324c92SDan McDonald "Failed to allocate interrupt(s)!"); 1727*8a324c92SDan McDonald goto exit_ints; 1728*8a324c92SDan McDonald } 1729*8a324c92SDan McDonald 1730*8a324c92SDan McDonald /* 1731*8a324c92SDan McDonald * Register layout determined, can now access the 1732*8a324c92SDan McDonald * device-specific bits 1733*8a324c92SDan McDonald */ 1734*8a324c92SDan McDonald vioif_get_mac(sc); 1735*8a324c92SDan McDonald 1736*8a324c92SDan McDonald sc->sc_rx_vq = virtio_alloc_vq(&sc->sc_virtio, 0, 1737*8a324c92SDan McDonald VIOIF_RX_QLEN, VIOIF_INDIRECT_MAX, "rx"); 1738*8a324c92SDan McDonald if (!sc->sc_rx_vq) 1739*8a324c92SDan McDonald goto exit_alloc1; 1740*8a324c92SDan McDonald virtio_stop_vq_intr(sc->sc_rx_vq); 1741*8a324c92SDan McDonald 1742*8a324c92SDan McDonald sc->sc_tx_vq = virtio_alloc_vq(&sc->sc_virtio, 1, 1743*8a324c92SDan McDonald VIOIF_TX_QLEN, VIOIF_INDIRECT_MAX, "tx"); 1744*8a324c92SDan McDonald if (!sc->sc_rx_vq) 1745*8a324c92SDan McDonald goto exit_alloc2; 1746*8a324c92SDan McDonald virtio_stop_vq_intr(sc->sc_tx_vq); 1747*8a324c92SDan McDonald 1748*8a324c92SDan McDonald if (vioif_has_feature(sc, VIRTIO_NET_F_CTRL_VQ)) { 1749*8a324c92SDan McDonald sc->sc_ctrl_vq = virtio_alloc_vq(&sc->sc_virtio, 2, 1750*8a324c92SDan McDonald VIOIF_CTRL_QLEN, 0, "ctrl"); 1751*8a324c92SDan McDonald if (!sc->sc_ctrl_vq) { 1752*8a324c92SDan McDonald goto exit_alloc3; 1753*8a324c92SDan McDonald } 1754*8a324c92SDan McDonald virtio_stop_vq_intr(sc->sc_ctrl_vq); 1755*8a324c92SDan McDonald } 1756*8a324c92SDan McDonald 1757*8a324c92SDan McDonald virtio_set_status(&sc->sc_virtio, 1758*8a324c92SDan McDonald VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK); 1759*8a324c92SDan McDonald 1760*8a324c92SDan McDonald sc->sc_rxloan = 0; 1761*8a324c92SDan McDonald 1762*8a324c92SDan McDonald /* set some reasonable-small default values */ 1763*8a324c92SDan McDonald sc->sc_rxcopy_thresh = 300; 1764*8a324c92SDan McDonald sc->sc_txcopy_thresh = 300; 1765*8a324c92SDan McDonald sc->sc_mtu = ETHERMTU; 1766*8a324c92SDan McDonald 1767*8a324c92SDan McDonald vioif_check_features(sc); 1768*8a324c92SDan McDonald 1769*8a324c92SDan McDonald if (vioif_alloc_mems(sc)) 1770*8a324c92SDan McDonald goto exit_alloc_mems; 1771*8a324c92SDan McDonald 1772*8a324c92SDan McDonald if ((macp = mac_alloc(MAC_VERSION)) == NULL) { 1773*8a324c92SDan McDonald dev_err(devinfo, CE_WARN, "Failed to allocate a mac_register"); 1774*8a324c92SDan McDonald goto exit_macalloc; 1775*8a324c92SDan McDonald } 1776*8a324c92SDan McDonald 1777*8a324c92SDan McDonald macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 1778*8a324c92SDan McDonald macp->m_driver = sc; 1779*8a324c92SDan McDonald macp->m_dip = devinfo; 1780*8a324c92SDan McDonald macp->m_src_addr = sc->sc_mac; 1781*8a324c92SDan McDonald macp->m_callbacks = &vioif_m_callbacks; 1782*8a324c92SDan McDonald macp->m_min_sdu = 0; 1783*8a324c92SDan McDonald macp->m_max_sdu = sc->sc_mtu; 1784*8a324c92SDan McDonald macp->m_margin = VLAN_TAGSZ; 1785*8a324c92SDan McDonald macp->m_priv_props = vioif_priv_props; 1786*8a324c92SDan McDonald 1787*8a324c92SDan McDonald sc->sc_macp = macp; 1788*8a324c92SDan McDonald 1789*8a324c92SDan McDonald /* Pre-fill the rx ring. */ 1790*8a324c92SDan McDonald (void) vioif_populate_rx(sc, KM_SLEEP); 1791*8a324c92SDan McDonald 1792*8a324c92SDan McDonald ret = mac_register(macp, &sc->sc_mac_handle); 1793*8a324c92SDan McDonald if (ret != 0) { 1794*8a324c92SDan McDonald dev_err(devinfo, CE_WARN, "vioif_attach: " 1795*8a324c92SDan McDonald "mac_register() failed, ret=%d", ret); 1796*8a324c92SDan McDonald goto exit_register; 1797*8a324c92SDan McDonald } 1798*8a324c92SDan McDonald 1799*8a324c92SDan McDonald ret = virtio_enable_ints(&sc->sc_virtio); 1800*8a324c92SDan McDonald if (ret) { 1801*8a324c92SDan McDonald dev_err(devinfo, CE_WARN, "Failed to enable interrupts"); 1802*8a324c92SDan McDonald goto exit_enable_ints; 1803*8a324c92SDan McDonald } 1804*8a324c92SDan McDonald 1805*8a324c92SDan McDonald mac_link_update(sc->sc_mac_handle, LINK_STATE_UP); 1806*8a324c92SDan McDonald return (DDI_SUCCESS); 1807*8a324c92SDan McDonald 1808*8a324c92SDan McDonald exit_enable_ints: 1809*8a324c92SDan McDonald (void) mac_unregister(sc->sc_mac_handle); 1810*8a324c92SDan McDonald exit_register: 1811*8a324c92SDan McDonald mac_free(macp); 1812*8a324c92SDan McDonald exit_macalloc: 1813*8a324c92SDan McDonald vioif_free_mems(sc); 1814*8a324c92SDan McDonald exit_alloc_mems: 1815*8a324c92SDan McDonald virtio_release_ints(&sc->sc_virtio); 1816*8a324c92SDan McDonald if (sc->sc_ctrl_vq) 1817*8a324c92SDan McDonald virtio_free_vq(sc->sc_ctrl_vq); 1818*8a324c92SDan McDonald exit_alloc3: 1819*8a324c92SDan McDonald virtio_free_vq(sc->sc_tx_vq); 1820*8a324c92SDan McDonald exit_alloc2: 1821*8a324c92SDan McDonald virtio_free_vq(sc->sc_rx_vq); 1822*8a324c92SDan McDonald exit_alloc1: 1823*8a324c92SDan McDonald exit_ints: 1824*8a324c92SDan McDonald kmem_cache_destroy(sc->sc_rxbuf_cache); 1825*8a324c92SDan McDonald exit_cache: 1826*8a324c92SDan McDonald exit_features: 1827*8a324c92SDan McDonald virtio_set_status(&sc->sc_virtio, VIRTIO_CONFIG_DEVICE_STATUS_FAILED); 1828*8a324c92SDan McDonald ddi_regs_map_free(&sc->sc_virtio.sc_ioh); 1829*8a324c92SDan McDonald exit_intrstat: 1830*8a324c92SDan McDonald exit_map: 1831*8a324c92SDan McDonald kstat_delete(sc->sc_intrstat); 1832*8a324c92SDan McDonald kmem_free(sc, sizeof (struct vioif_softc)); 1833*8a324c92SDan McDonald exit: 1834*8a324c92SDan McDonald return (DDI_FAILURE); 1835*8a324c92SDan McDonald } 1836*8a324c92SDan McDonald 1837*8a324c92SDan McDonald static int 1838*8a324c92SDan McDonald vioif_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd) 1839*8a324c92SDan McDonald { 1840*8a324c92SDan McDonald struct vioif_softc *sc; 1841*8a324c92SDan McDonald 1842*8a324c92SDan McDonald if ((sc = ddi_get_driver_private(devinfo)) == NULL) 1843*8a324c92SDan McDonald return (DDI_FAILURE); 1844*8a324c92SDan McDonald 1845*8a324c92SDan McDonald switch (cmd) { 1846*8a324c92SDan McDonald case DDI_DETACH: 1847*8a324c92SDan McDonald break; 1848*8a324c92SDan McDonald 1849*8a324c92SDan McDonald case DDI_PM_SUSPEND: 1850*8a324c92SDan McDonald /* We do not support suspend/resume for vioif. */ 1851*8a324c92SDan McDonald return (DDI_FAILURE); 1852*8a324c92SDan McDonald 1853*8a324c92SDan McDonald default: 1854*8a324c92SDan McDonald return (DDI_FAILURE); 1855*8a324c92SDan McDonald } 1856*8a324c92SDan McDonald 1857*8a324c92SDan McDonald if (sc->sc_rxloan) { 1858*8a324c92SDan McDonald dev_err(devinfo, CE_WARN, "!Some rx buffers are still upstream," 1859*8a324c92SDan McDonald " not detaching."); 1860*8a324c92SDan McDonald return (DDI_FAILURE); 1861*8a324c92SDan McDonald } 1862*8a324c92SDan McDonald 1863*8a324c92SDan McDonald virtio_stop_vq_intr(sc->sc_rx_vq); 1864*8a324c92SDan McDonald virtio_stop_vq_intr(sc->sc_tx_vq); 1865*8a324c92SDan McDonald 1866*8a324c92SDan McDonald virtio_release_ints(&sc->sc_virtio); 1867*8a324c92SDan McDonald 1868*8a324c92SDan McDonald if (mac_unregister(sc->sc_mac_handle)) { 1869*8a324c92SDan McDonald return (DDI_FAILURE); 1870*8a324c92SDan McDonald } 1871*8a324c92SDan McDonald 1872*8a324c92SDan McDonald mac_free(sc->sc_macp); 1873*8a324c92SDan McDonald 1874*8a324c92SDan McDonald vioif_free_mems(sc); 1875*8a324c92SDan McDonald virtio_free_vq(sc->sc_rx_vq); 1876*8a324c92SDan McDonald virtio_free_vq(sc->sc_tx_vq); 1877*8a324c92SDan McDonald 1878*8a324c92SDan McDonald virtio_device_reset(&sc->sc_virtio); 1879*8a324c92SDan McDonald 1880*8a324c92SDan McDonald ddi_regs_map_free(&sc->sc_virtio.sc_ioh); 1881*8a324c92SDan McDonald 1882*8a324c92SDan McDonald kmem_cache_destroy(sc->sc_rxbuf_cache); 1883*8a324c92SDan McDonald kstat_delete(sc->sc_intrstat); 1884*8a324c92SDan McDonald kmem_free(sc, sizeof (struct vioif_softc)); 1885*8a324c92SDan McDonald 1886*8a324c92SDan McDonald return (DDI_SUCCESS); 1887*8a324c92SDan McDonald } 1888*8a324c92SDan McDonald 1889*8a324c92SDan McDonald static int 1890*8a324c92SDan McDonald vioif_quiesce(dev_info_t *devinfo) 1891*8a324c92SDan McDonald { 1892*8a324c92SDan McDonald struct vioif_softc *sc; 1893*8a324c92SDan McDonald 1894*8a324c92SDan McDonald if ((sc = ddi_get_driver_private(devinfo)) == NULL) 1895*8a324c92SDan McDonald return (DDI_FAILURE); 1896*8a324c92SDan McDonald 1897*8a324c92SDan McDonald virtio_stop_vq_intr(sc->sc_rx_vq); 1898*8a324c92SDan McDonald virtio_stop_vq_intr(sc->sc_tx_vq); 1899*8a324c92SDan McDonald virtio_device_reset(&sc->sc_virtio); 1900*8a324c92SDan McDonald 1901*8a324c92SDan McDonald return (DDI_SUCCESS); 1902*8a324c92SDan McDonald } 1903*8a324c92SDan McDonald 1904*8a324c92SDan McDonald int 1905*8a324c92SDan McDonald _init(void) 1906*8a324c92SDan McDonald { 1907*8a324c92SDan McDonald int ret = 0; 1908*8a324c92SDan McDonald 1909*8a324c92SDan McDonald mac_init_ops(&vioif_ops, "vioif"); 1910*8a324c92SDan McDonald 1911*8a324c92SDan McDonald ret = mod_install(&modlinkage); 1912*8a324c92SDan McDonald if (ret != DDI_SUCCESS) { 1913*8a324c92SDan McDonald mac_fini_ops(&vioif_ops); 1914*8a324c92SDan McDonald return (ret); 1915*8a324c92SDan McDonald } 1916*8a324c92SDan McDonald 1917*8a324c92SDan McDonald return (0); 1918*8a324c92SDan McDonald } 1919*8a324c92SDan McDonald 1920*8a324c92SDan McDonald int 1921*8a324c92SDan McDonald _fini(void) 1922*8a324c92SDan McDonald { 1923*8a324c92SDan McDonald int ret; 1924*8a324c92SDan McDonald 1925*8a324c92SDan McDonald ret = mod_remove(&modlinkage); 1926*8a324c92SDan McDonald if (ret == DDI_SUCCESS) { 1927*8a324c92SDan McDonald mac_fini_ops(&vioif_ops); 1928*8a324c92SDan McDonald } 1929*8a324c92SDan McDonald 1930*8a324c92SDan McDonald return (ret); 1931*8a324c92SDan McDonald } 1932*8a324c92SDan McDonald 1933*8a324c92SDan McDonald int 1934*8a324c92SDan McDonald _info(struct modinfo *pModinfo) 1935*8a324c92SDan McDonald { 1936*8a324c92SDan McDonald return (mod_info(&modlinkage, pModinfo)); 1937*8a324c92SDan McDonald } 1938