/*- * Copyright 2003 Harti Brandt * Copyright 2003 Vincent Jardin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * ATM Virtal Adapter Support * -------------------------- * * API between HARP and Netgraph * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Local definitions */ /* * Local methods */ static int vatmpif_nunits = 0; /* * ATM Interface services * * this virtual device does not use a soft SAR of the AAL5 PDU, neither * of the AAL3/4 PDU. */ static struct stack_defn vatmpif_svaal5 = { sd_next: NULL, sd_sap: SAP_CPCS_AAL5, sd_flag: SDF_TERM, /* no soft SAR */ sd_inst: atm_dev_inst, sd_lower: atm_dev_lower, sd_upper: NULL, sd_toku: 0, }; static struct stack_defn vatmpif_svaal4 = { sd_next: &vatmpif_svaal5, sd_sap: SAP_CPCS_AAL3_4, sd_flag: SDF_TERM, /* no soft SAR */ sd_inst: atm_dev_inst, sd_lower: atm_dev_lower, sd_upper: NULL, sd_toku: 0, }; static struct stack_defn vatmpif_svaal0 = { sd_next: &vatmpif_svaal4, sd_sap: SAP_ATM, sd_flag: SDF_TERM, /* no soft SAR */ sd_inst: atm_dev_inst, sd_lower: atm_dev_lower, sd_upper: NULL, sd_toku: 0, }; static struct stack_defn *vatmpif_services = &vatmpif_svaal0; /****************************************************************** HARP API METHODS ******************************************************************/ /* * Local methods */ static int vatmpif_harp_ioctl(int code, caddr_t data, caddr_t arg); static int vatmpif_harp_instvcc(Cmn_unit *cup, Cmn_vcc *cvp); static int vatmpif_harp_openvcc(Cmn_unit *cup, Cmn_vcc *cvp); static int vatmpif_harp_closevcc(Cmn_unit *cup, Cmn_vcc *cvp); static void vatmpif_harp_output(Cmn_unit *cup, Cmn_vcc *cvp, KBuffer *m); static atm_intr_t vatmpif_harp_recv_stack; /* * Attach an virtual ATM physical inteface with the HARP stack * * Each virtual ATM device interface must register itself here * upon completing the netgraph node constructor. * * Arguments: * node pointer on the netgraph node * * Returns: * 0 successful * errno failed - reason indicated */ int vatmpif_harp_attach(node_p node) { Vatmpif_unit *vup; static int unit = 0; int err; /* * Sanity check */ if (node == NULL) return (EINVAL); /* * Get the virtual unit structure */ vup = (Vatmpif_unit *)NG_NODE_PRIVATE(node); if (vup == NULL) return (EINVAL); /* * Start initializing the HARP binding */ vup->vu_unit = unit; /* 9188 bytes: Default ATM network interface MTU + LLC/SNAP header */ vup->vu_mtu = ATM_NIF_MTU + 8; vup->vu_vcc_zone = vatmpif_vcc_zone; vup->vu_nif_zone = vatmpif_nif_zone; vup->vu_ioctl = vatmpif_harp_ioctl; vup->vu_instvcc = vatmpif_harp_instvcc; vup->vu_openvcc = vatmpif_harp_openvcc; vup->vu_closevcc = vatmpif_harp_closevcc; vup->vu_output = vatmpif_harp_output; vup->vu_softc = vup; /* * Consider this virtual unit assigned */ unit++; /* * Get our device type and setup the adapter config info * - at least as much as we can */ vup->vu_config.ac_vendor = VENDOR_NETGRAPH; vup->vu_config.ac_vendapi = VENDAPI_NETGRAPH_1; vup->vu_config.ac_device = DEV_VATMPIF; vup->vu_config.ac_media = MEDIA_VIRTUAL; vup->vu_config.ac_serial = (u_long)node; vup->vu_config.ac_bustype = BUS_VIRTUAL; vup->vu_config.ac_busslot = NGM_ATMPIF_COOKIE; vup->vu_config.ac_ram = (u_long)node; vup->vu_config.ac_ramsize = sizeof(*node); vup->vu_config.ac_macaddr = vup->conf.macaddr; snprintf(vup->vu_config.ac_hard_vers, sizeof(vup->vu_config.ac_hard_vers), "%s", "Virt. ATM 1.0"); snprintf(vup->vu_config.ac_firm_vers, sizeof(vup->vu_config.ac_firm_vers), "%d", __FreeBSD__); /* * Set the interface capabilities */ vup->vu_pif.pif_maxvpi = VATMPIF_MAX_VPI; vup->vu_pif.pif_maxvci = VATMPIF_MAX_VCI; vup->vu_pif.pif_pcr = vup->conf.pcr; /* * Register this interface with ATM core services */ if ((err = atm_physif_register((Cmn_unit *)vup, VATMPIF_DEV_NAME, vatmpif_services)) != 0 ) { /* * Registration failed - back everything out * * The netgraph node must not be created. */ return (err); } vatmpif_nunits++; /* * Mark device initialization completed */ vup->vu_flags |= CUF_INITED; /* Done */ return (0); } /* * Halt driver processing * * This will be called just prior the destruction of the Netgraph's node. * * Arguments: * node pointer on the netgraph node * * Returns: * 0 detach was successful * errno detach failed - reason indicated */ int vatmpif_harp_detach(node_p node) { Vatmpif_unit *vup = (Vatmpif_unit *)NG_NODE_PRIVATE(node); int err; /* * Deregister device from kernel services */ if ((err = atm_physif_deregister((Cmn_unit *)vup))) return (err); vatmpif_nunits--; /* * Clear device initialized */ vup->vu_flags &= ~CUF_INITED; /* Done */ return (0); } /* * Handle netatm core service interface ioctl requests * * Arguments: * code ioctl function (sub)code * data data to/from ioctl * arg optional code-specific argument * * Returns: * 0 request processed successfully * errno request failed - reason code */ static int vatmpif_harp_ioctl(int code, caddr_t data, caddr_t arg) { struct atminfreq *aip = (struct atminfreq *)data; struct atm_pif *pip; Vatmpif_unit *vup; caddr_t buf = aip->air_buf_addr; struct air_vinfo_rsp *avr; size_t count, len, buf_len = aip->air_buf_len; int err = 0; char ifname[2 * IFNAMSIZ]; ATM_DEBUG3("%s: code=%d, opcode=%d\n", __func__, code, aip->air_opcode); switch (aip->air_opcode) { case AIOCS_INF_VST: /* * Get vendor statistics */ pip = (struct atm_pif *)arg; vup = (Vatmpif_unit *)pip; if (pip == NULL) return (ENXIO); snprintf(ifname, sizeof(ifname), "%s%d", pip->pif_name, pip->pif_unit); /* * Cast response structure onto user's buffer */ avr = (struct air_vinfo_rsp *)(void *)buf; /* * How lare is the response structure ? */ len = sizeof(struct air_vinfo_rsp); /* * Sanity check - enough room for response structure */ if (buf_len < len) return ENOSPC; /* * Copy interface name into response structure */ if ((err = copyout(ifname, avr->avsp_intf, IFNAMSIZ)) != 0) break; /* * Advance the buffer address and decrement the size */ buf += len; buf_len -= len; /* * Get the vendor stats */ /* vup->vu_stats */ /* * Stick as much of it as we have room for * into the response */ count = MIN(sizeof(Vatmpif_stats), buf_len); /* * Copy stats into user's buffer. Return value is * amount of data copied. */ if ((err = copyout((caddr_t)&vup->vu_stats, buf, buf_len)) != 0) break; buf += count; buf_len -= count; if (count < sizeof(Vatmpif_stats)) err = ENOSPC; /* * Record amount we are returning as vendor info... */ if ((err = copyout(&count, &avr->avsp_len, sizeof(count))) != 0) break; /* * Update the reply pointers and lengths */ aip->air_buf_addr = buf; aip->air_buf_len = buf_len; break; default: err = ENOSYS; break; } return (err); } /* * Get CBR/VBR/ABR/UBR from bearer attribute * * Arguments: * bearer T_ATM_BEARER_CAP option value structure * * Returns: * Driver traffic class */ static Vatmpif_traffic_type vatmpif_bearerclass(struct attr_bearer *bearer) { switch (bearer->v.bearer_class) { case T_ATM_CLASS_A: return (VATMPIF_TRAF_CBR); case T_ATM_CLASS_C: return (VATMPIF_TRAF_VBR); case T_ATM_CLASS_X: switch (bearer->v.traffic_type) { case T_ATM_CBR: return (VATMPIF_TRAF_CBR); case T_ATM_VBR: return (VATMPIF_TRAF_VBR); case T_ATM_ABR: return (VATMPIF_TRAF_ABR); case T_ATM_NULL: case T_ATM_UBR: return (VATMPIF_TRAF_UBR); } break; } /* never reached */ log(LOG_ERR, "%s: could not determine the traffic type.\n", __func__); return (VATMPIF_TRAF_UBR); } /* * VCC Stack Instantiation * * This function is called via the common driver code during a device VCC * stack instantiation. The common code has already validated some of * the request so we just need to check a few more VATMPIF-specific details. * * Arguments: * cup pointer to device common unit * cvp pointer to common VCC entry * * Returns: * 0 instantiation successful * errno instantiation failed - reason indicated */ static int vatmpif_harp_instvcc(Cmn_unit *cup, Cmn_vcc *cvp) { Vatmpif_unit *vup = (Vatmpif_unit *)cup; Vatmpif_vcc *vvp = (Vatmpif_vcc *)cvp; Atm_attributes *ap = &vvp->vv_connvc->cvc_attr; int32_t pcr = 0; int32_t scr = 0; Vatmpif_traffic_type traffic = VATMPIF_TRAF_UBR; ATM_DEBUG3("%s: vup=%p, vvp=%p\n", __func__, vup, vvp); if (ap->traffic.tag == T_ATM_PRESENT) { pcr = ap->traffic.v.forward.PCR_all_traffic; scr = ap->traffic.v.forward.SCR_all_traffic; } if (pcr < 0) pcr = 0; if (scr < 0) scr = 0; KASSERT(ap->bearer.tag == T_ATM_PRESENT, ("Bearer tag is required")); traffic = vatmpif_bearerclass(&ap->bearer); /* Guarantee PCR of the PVC with CBR */ if (traffic == VATMPIF_TRAF_CBR && vup->vu_cur_pcr + pcr > vup->vu_pif.pif_pcr) { return (EINVAL); } /* Guarantee SCR of the PVC with VBR */ if (traffic == VATMPIF_TRAF_VBR && vup->vu_cur_pcr + scr > vup->vu_pif.pif_pcr) { return (EINVAL); } /* * Validate requested AAL */ KASSERT(ap->aal.tag == T_ATM_PRESENT, ("AAL tag is required")); switch (ap->aal.type) { case ATM_AAL0: break; case ATM_AAL1: break; case ATM_AAL2: return (EINVAL); case ATM_AAL3_4: if (ap->aal.v.aal4.forward_max_SDU_size > vup->vu_mtu || ap->aal.v.aal4.backward_max_SDU_size > vup->vu_mtu) return (EINVAL); break; case ATM_AAL5: if (ap->aal.v.aal5.forward_max_SDU_size > vup->vu_mtu || ap->aal.v.aal5.backward_max_SDU_size > vup->vu_mtu) return (EINVAL); break; default: return (EINVAL); } /* Done */ return (0); } /* * Open a VCC * * This function is called via the common driver code after receiving a * stack *_INIT command. The common has already validated most of * the request so we just need to check a few more VATMPIF-specific details. * Then we just forward to the Netgraph node. * * Called at splimp. * * Arguments: * cup pointer to device common unit * cvp pointer to common VCC entry * * Returns: * 0 open successful * errno open failed - reason indicated */ static int vatmpif_harp_openvcc(Cmn_unit *cup, Cmn_vcc *cvp) { Vatmpif_unit *vup = (Vatmpif_unit *)cup; Vatmpif_vcc *vvp = (Vatmpif_vcc *)cvp; struct vccb *vcp = vvp->vv_connvc->cvc_vcc; Atm_attributes *ap = &vvp->vv_connvc->cvc_attr; ATM_DEBUG5("%s: vup=%p, vvp=%p, vcc=(%d,%d)\n", __func__, vup, vvp, vcp->vc_vpi, vcp->vc_vci); /* * We only need to open incoming VC's so outbound VC's * just get set to CVS_ACTIVE state. */ if ((vcp->vc_type & VCC_IN) == 0) { /* * Set the state and return - nothing else needed */ vvp->vv_state = CVS_ACTIVE; return (0); } /* * Set the AAL and traffic */ switch (ap->aal.type) { case ATM_AAL0: vvp->vv_aal = VATMPIF_AAL_0; break; case ATM_AAL2: return (EINVAL); case ATM_AAL3_4: vvp->vv_aal = VATMPIF_AAL_4; break; case ATM_AAL5: vvp->vv_aal = VATMPIF_AAL_5; break; default: return (EINVAL); } vvp->vv_traffic_type = vatmpif_bearerclass(&ap->bearer); vvp->vv_traffic = ap->traffic.v; switch (vvp->vv_traffic_type) { case VATMPIF_TRAF_ABR: /* TODO */ case VATMPIF_TRAF_UBR: break; case VATMPIF_TRAF_VBR: vup->vu_cur_pcr += vvp->vv_traffic.forward.SCR_all_traffic; break; case VATMPIF_TRAF_CBR: vup->vu_cur_pcr += vvp->vv_traffic.forward.PCR_all_traffic; break; } /* * Indicate VC active */ vvp->vv_state = CVS_ACTIVE; /* Done */ return (0); } /* * Close a VCC * * This function is called via the common driver code after receiving a * stack *_TERM command. The common code has already validated most of * the request so we just need to check a few more VATMPIF-specific detail. * Then we just remove the entry from the list. * * Arguments: * cup pointer to device common unit * cvp pointer to common VCC entry * * Returns: * 0 close successful * errno close failed - reason indicated */ static int vatmpif_harp_closevcc(Cmn_unit *cup, Cmn_vcc *cvp) { Vatmpif_unit *vup = (Vatmpif_unit *)cup; Vatmpif_vcc *vvp = (Vatmpif_vcc *)cvp; struct vccb *vcp = vvp->vv_connvc->cvc_vcc; /* * If this is an outbound only VCI, then we can close * immediately. */ if ((vcp->vc_type & VCC_IN) == 0) { /* * The state will be set to TERM when we return * to the *_TERM caller. */ return (0); } switch (vvp->vv_traffic_type) { case VATMPIF_TRAF_ABR: /* TODO */ case VATMPIF_TRAF_UBR: break; case VATMPIF_TRAF_VBR: vup->vu_cur_pcr -= vvp->vv_traffic.forward.SCR_all_traffic; break; case VATMPIF_TRAF_CBR: vup->vu_cur_pcr -= vvp->vv_traffic.forward.PCR_all_traffic; break; } return (0); } /* * Output a PDU * * This function is called via the common driver code after receiving a * stack *_DATA* command. The command code has already validated most of * the request so we just need to check a few more VATMPIF-specific detail. * Then we just forward the transmit mbuf to the Netgraph node. * * Arguments: * cup pointer to device common * cvp pointer to common VCC entry * m pointer to output PDU buffer chain head * * Returns: * none */ static void vatmpif_harp_output(Cmn_unit *cup, Cmn_vcc *cvp, KBuffer *m) { Vatmpif_unit *vup = (Vatmpif_unit *)cup; Vatmpif_vcc *vvp = (Vatmpif_vcc *)cvp; struct vccb *vcp = vvp->vv_connvc->cvc_vcc; Atm_attributes *ap = &vvp->vv_connvc->cvc_attr; int err = 0; u_long pdulen = 0; if (IS_VATMPIF_DEBUG_PACKET(vup)) atm_dev_pdu_print(cup, cvp, m, __func__); /* * Get packet PDU length */ KB_PLENGET (m, pdulen); err = ng_atmpif_transmit(vup, m, vcp->vc_vpi, vcp->vc_vci, 0, 0, ap->aal.type); /* * Now collect some statistics */ if (err) { vup->vu_pif.pif_oerrors++; vcp->vc_oerrors++; if (vcp->vc_nif) ANIF2IFP(vcp->vc_nif)->if_oerrors++; } else { /* * Good transmission */ switch (ap->aal.type) { case VATMPIF_AAL_0: vup->vu_stats.hva_st_ng.ng_tx_rawcell++; break; case VATMPIF_AAL_4: /* TODO */ break; case VATMPIF_AAL_5: vup->vu_stats.hva_st_aal5.aal5_xmit += (pdulen + 47) / 48; vup->vu_stats.hva_st_aal5.aal5_pdu_xmit++; break; default: log(LOG_ERR, "%s%d: unknown AAL while %s", vup->vu_pif.pif_name, vup->vu_pif.pif_unit, __func__); } vup->vu_pif.pif_opdus++; vup->vu_pif.pif_obytes += pdulen; if (vvp) { vcp = vvp->vv_connvc->cvc_vcc; vcp->vc_opdus++; vcp->vc_obytes += pdulen; if (vcp->vc_nif) { vcp->vc_nif->nif_obytes += pdulen; ANIF2IFP(vcp->vc_nif)->if_opackets++; ANIF2IFP(vcp->vc_nif)->if_obytes += pdulen; } } } } /* * Pass Incoming PDU up to the HARP stack * * This function is called via the core ATM interrupt queue callback * set in vatmpif_harp_recv_drain(). It will pass the supplied incoming * PDU up the incoming VCC's stack. * * Arguments: * tok token to identify stack instantiation * m pointer to incoming PDU buffer chain * * Returns: * none */ static void vatmpif_harp_recv_stack(void *tok, KBuffer *m) { Vatmpif_vcc *vvp = (Vatmpif_vcc *)tok; int err; /* * Send the data up the stack */ STACK_CALL(CPCS_UNITDATA_SIG, vvp->vv_upper, vvp->vv_toku, vvp->vv_connvc, (intptr_t)m, 0, err); if (err) KB_FREEALL(m); } /* * Drain Receive Queue * * The function will process all completed entries at the head of the * receive queue. The received segments will be linked into a received * PDU buffer cahin and it will then be passed up the PDU's VCC stack * function processing by the next higher protocol layer. * * May be called in interrupt state. * Must be called with interrupts locked out. * * Arguments: * vup pointer to the virtual device structure * m pointer to incoming PDU buffer chain * vpi Virtual Path Identifier * vci Virtual Channel Identifier (host order) * pt Payload Type Identifier (3 bit) * ATM_PT_USER_SDU0 * ATM_PT_USER_SDU1 * ATM_PT_USER_CONG_SDU0 * ATM_PT_USER_CONG_SDU1 * ATM_PT_NONUSER * ATM_PT_OAMF5_SEG * ATM_PT_OAMF5_E2E * clp Cell Loss Priority (1 bit) * * Returns: * 0 close successful * errno close failed - reason indicated */ int vatmpif_harp_recv_drain(Vatmpif_unit *vup, KBuffer *m, uint8_t vpi, uint16_t vci, uint8_t pt, uint8_t clp, Vatmpif_aal aal) { Vatmpif_vcc *vvp; struct vccb *vcp; u_long pdulen = 0; caddr_t cp; int err = 0; /* * Locate incoming VCC for this PDU */ vvp = (Vatmpif_vcc *)atm_dev_vcc_find((Cmn_unit *)vup, vpi, vci, VCC_IN); if (vvp == NULL) { vup->vu_stats.hva_st_ng.ng_rx_novcc++; vup->vu_pif.pif_ierrors++; KB_FREEALL(m); err = EIO; goto failed; } switch (aal) { case VATMPIF_AAL_0: vup->vu_stats.hva_st_ng.ng_rx_rawcell++; break; case VATMPIF_AAL_4: /* TODO */ break; case VATMPIF_AAL_5: vup->vu_stats.hva_st_aal5.aal5_rcvd += (pdulen + 47) / 48; vup->vu_stats.hva_st_aal5.aal5_pdu_rcvd++; break; default: vup->vu_stats.hva_st_ng.ng_badpdu++; vup->vu_pif.pif_ierrors++; KB_FREEALL(m); err = EINVAL; goto failed; } /* * TODO: * For now, only user data PDUs are supported */ if (pt & ATM_HDR_SET_PT(ATM_PT_NONUSER)) { vup->vu_stats.hva_st_ng.ng_badpdu++; vup->vu_pif.pif_ierrors++; if (aal == VATMPIF_AAL_5) { vup->vu_stats.hva_st_aal5.aal5_drops += (pdulen + 47) / 48; vup->vu_stats.hva_st_aal5.aal5_pdu_drops++; } err = EINVAL; goto failed; } if (IS_VATMPIF_DEBUG_PACKET(vup)) atm_dev_pdu_print((Cmn_unit *)vup, (Cmn_vcc *)vvp, m, __func__); /* * Get packet PDU length */ KB_PLENGET(m, pdulen); /* * Only try queueing this if there is data * to be handed up to the next layer. */ if (pdulen == 0) { /* * Free zero-length buffer */ vup->vu_stats.hva_st_ng.ng_badpdu++; vup->vu_pif.pif_ierrors++; if (aal == VATMPIF_AAL_5) vup->vu_stats.hva_st_aal5.aal5_pdu_errs++; err = EIO; KB_FREEALL(m); goto failed; } /* TODO: process the AAL4 CRC, AAL5 CRC, * then update aal5_crc_len, aal5_drops, aal5_pdu_crc, * aal5_pdu_errs, aal5_pdu_drops ... */ /* * Quick count the PDU */ vup->vu_pif.pif_ipdus++; vup->vu_pif.pif_ibytes += pdulen; vup->vu_stats.hva_st_ng.ng_rx_pdu++; vup->vu_stats.hva_st_atm.atm_rcvd += (pdulen + 47) / 48; /* * Update the VCC statistics: * XXX: This code should not be into the driver. */ vcp = vvp->vv_connvc->cvc_vcc; if (vcp) { vcp->vc_ipdus++; vcp->vc_ibytes += pdulen; /* * Update the NIF statistics if any * XXX: beurk ! */ if (vcp->vc_nif) { vcp->vc_nif->nif_ibytes += pdulen; ANIF2IFP(vcp->vc_nif)->if_ipackets++; ANIF2IFP(vcp->vc_nif)->if_ibytes += pdulen; } } /* * The STACK_CALL needs to happen at splnet() in order * for the stack sequence processing to work. Schedule an * interrupt queue callback at splnet(). */ /* * Prepend callback function pointer and token value to buffer. * We have already guaranteed that the space is available in the * first buffer because the vatmpif_header structure is greater * than our callback pointer. * XXX */ KB_HEADADJ(m, sizeof(atm_intr_func_t) + sizeof(void *)); KB_DATASTART(m, cp, caddr_t); *((atm_intr_func_t *) cp) = vatmpif_harp_recv_stack; cp += sizeof (atm_intr_func_t); *((void **)cp) = (void *)vvp; /* * Schedule callback */ if ((err = netisr_queue(NETISR_ATM, m))) { /* (0) on success. */ /* * queue is full. Unable to pass up to the HARP stack * Update the stats. */ vup->vu_stats.hva_st_ng.ng_rx_iqfull++; vup->vu_pif.pif_ierrors++; goto failed; } /* Done */ return (0); failed: return (err); }