10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51502Sericheng * Common Development and Distribution License (the "License"). 61502Sericheng * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 221353Sericheng * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * Data-Link Driver 300Sstevel@tonic-gate */ 310Sstevel@tonic-gate 320Sstevel@tonic-gate #include <sys/types.h> 330Sstevel@tonic-gate #include <sys/debug.h> 340Sstevel@tonic-gate #include <sys/sysmacros.h> 350Sstevel@tonic-gate #include <sys/stream.h> 360Sstevel@tonic-gate #include <sys/ddi.h> 370Sstevel@tonic-gate #include <sys/sunddi.h> 380Sstevel@tonic-gate #include <sys/strsun.h> 391184Skrgopi #include <sys/cpuvar.h> 400Sstevel@tonic-gate #include <sys/dlpi.h> 410Sstevel@tonic-gate #include <netinet/in.h> 420Sstevel@tonic-gate #include <sys/sdt.h> 430Sstevel@tonic-gate #include <sys/strsubr.h> 440Sstevel@tonic-gate #include <sys/vlan.h> 450Sstevel@tonic-gate #include <sys/mac.h> 460Sstevel@tonic-gate #include <sys/dls.h> 470Sstevel@tonic-gate #include <sys/dld.h> 480Sstevel@tonic-gate #include <sys/dld_impl.h> 491184Skrgopi #include <sys/dls_soft_ring.h> 500Sstevel@tonic-gate 510Sstevel@tonic-gate typedef boolean_t proto_reqfunc_t(dld_str_t *, union DL_primitives *, mblk_t *); 520Sstevel@tonic-gate 530Sstevel@tonic-gate static proto_reqfunc_t proto_info_req, proto_attach_req, proto_detach_req, 540Sstevel@tonic-gate proto_bind_req, proto_unbind_req, proto_promiscon_req, proto_promiscoff_req, 550Sstevel@tonic-gate proto_enabmulti_req, proto_disabmulti_req, proto_physaddr_req, 560Sstevel@tonic-gate proto_setphysaddr_req, proto_udqos_req, proto_req, proto_capability_req, 570Sstevel@tonic-gate proto_notify_req, proto_unitdata_req, proto_passive_req; 580Sstevel@tonic-gate 59269Sericheng static void proto_poll_disable(dld_str_t *); 601184Skrgopi static boolean_t proto_poll_enable(dld_str_t *, dl_capab_dls_t *); 61269Sericheng static boolean_t proto_capability_advertise(dld_str_t *, mblk_t *); 620Sstevel@tonic-gate 631353Sericheng static task_func_t proto_process_unbind_req, proto_process_detach_req; 641353Sericheng 651184Skrgopi static void proto_soft_ring_disable(dld_str_t *); 661184Skrgopi static boolean_t proto_soft_ring_enable(dld_str_t *, dl_capab_dls_t *); 671184Skrgopi static boolean_t proto_capability_advertise(dld_str_t *, mblk_t *); 681184Skrgopi static void proto_change_soft_ring_fanout(dld_str_t *, int); 691184Skrgopi 700Sstevel@tonic-gate #define DL_ACK_PENDING(state) \ 710Sstevel@tonic-gate ((state) == DL_ATTACH_PENDING || \ 720Sstevel@tonic-gate (state) == DL_DETACH_PENDING || \ 730Sstevel@tonic-gate (state) == DL_BIND_PENDING || \ 740Sstevel@tonic-gate (state) == DL_UNBIND_PENDING) 750Sstevel@tonic-gate 760Sstevel@tonic-gate /* 77269Sericheng * Process a DLPI protocol message. 78269Sericheng * The primitives DL_BIND_REQ, DL_ENABMULTI_REQ, DL_PROMISCON_REQ, 79269Sericheng * DL_SET_PHYS_ADDR_REQ put the data link below our dld_str_t into an 80269Sericheng * 'active' state. The primitive DL_PASSIVE_REQ marks our dld_str_t 81269Sericheng * as 'passive' and forbids it from being subsequently made 'active' 82269Sericheng * by the above primitives. 830Sstevel@tonic-gate */ 840Sstevel@tonic-gate void 850Sstevel@tonic-gate dld_proto(dld_str_t *dsp, mblk_t *mp) 860Sstevel@tonic-gate { 870Sstevel@tonic-gate union DL_primitives *udlp; 880Sstevel@tonic-gate t_uscalar_t prim; 890Sstevel@tonic-gate 900Sstevel@tonic-gate if (MBLKL(mp) < sizeof (t_uscalar_t)) { 910Sstevel@tonic-gate freemsg(mp); 920Sstevel@tonic-gate return; 930Sstevel@tonic-gate } 940Sstevel@tonic-gate 950Sstevel@tonic-gate udlp = (union DL_primitives *)mp->b_rptr; 960Sstevel@tonic-gate prim = udlp->dl_primitive; 970Sstevel@tonic-gate 98269Sericheng switch (prim) { 99269Sericheng case DL_INFO_REQ: 100269Sericheng (void) proto_info_req(dsp, udlp, mp); 101269Sericheng break; 102269Sericheng case DL_BIND_REQ: 103269Sericheng (void) proto_bind_req(dsp, udlp, mp); 104269Sericheng break; 105269Sericheng case DL_UNBIND_REQ: 106269Sericheng (void) proto_unbind_req(dsp, udlp, mp); 107269Sericheng break; 108269Sericheng case DL_UNITDATA_REQ: 109269Sericheng (void) proto_unitdata_req(dsp, udlp, mp); 110269Sericheng break; 111269Sericheng case DL_UDQOS_REQ: 112269Sericheng (void) proto_udqos_req(dsp, udlp, mp); 113269Sericheng break; 114269Sericheng case DL_ATTACH_REQ: 115269Sericheng (void) proto_attach_req(dsp, udlp, mp); 116269Sericheng break; 117269Sericheng case DL_DETACH_REQ: 118269Sericheng (void) proto_detach_req(dsp, udlp, mp); 119269Sericheng break; 120269Sericheng case DL_ENABMULTI_REQ: 121269Sericheng (void) proto_enabmulti_req(dsp, udlp, mp); 122269Sericheng break; 123269Sericheng case DL_DISABMULTI_REQ: 124269Sericheng (void) proto_disabmulti_req(dsp, udlp, mp); 125269Sericheng break; 126269Sericheng case DL_PROMISCON_REQ: 127269Sericheng (void) proto_promiscon_req(dsp, udlp, mp); 128269Sericheng break; 129269Sericheng case DL_PROMISCOFF_REQ: 130269Sericheng (void) proto_promiscoff_req(dsp, udlp, mp); 131269Sericheng break; 132269Sericheng case DL_PHYS_ADDR_REQ: 133269Sericheng (void) proto_physaddr_req(dsp, udlp, mp); 134269Sericheng break; 135269Sericheng case DL_SET_PHYS_ADDR_REQ: 136269Sericheng (void) proto_setphysaddr_req(dsp, udlp, mp); 137269Sericheng break; 138269Sericheng case DL_NOTIFY_REQ: 139269Sericheng (void) proto_notify_req(dsp, udlp, mp); 140269Sericheng break; 141269Sericheng case DL_CAPABILITY_REQ: 142269Sericheng (void) proto_capability_req(dsp, udlp, mp); 143269Sericheng break; 144269Sericheng case DL_PASSIVE_REQ: 145269Sericheng (void) proto_passive_req(dsp, udlp, mp); 146269Sericheng break; 147269Sericheng default: 148269Sericheng (void) proto_req(dsp, udlp, mp); 149269Sericheng break; 1500Sstevel@tonic-gate } 1510Sstevel@tonic-gate } 1520Sstevel@tonic-gate 1530Sstevel@tonic-gate /* 1541353Sericheng * Finish any pending operations. 1551353Sericheng * Requests that need to be processed asynchronously will be handled 1561353Sericheng * by a separate thread. After this function returns, other threads 1571353Sericheng * will be allowed to enter dld; they will not be able to do anything 1581353Sericheng * until ds_dlstate transitions to a non-pending state. 1590Sstevel@tonic-gate */ 160269Sericheng void 161269Sericheng dld_finish_pending_ops(dld_str_t *dsp) 1620Sstevel@tonic-gate { 1631353Sericheng task_func_t *op = NULL; 1641353Sericheng 165269Sericheng ASSERT(MUTEX_HELD(&dsp->ds_thr_lock)); 166269Sericheng ASSERT(dsp->ds_thr == 0); 1670Sstevel@tonic-gate 1681353Sericheng op = dsp->ds_pending_op; 1691353Sericheng dsp->ds_pending_op = NULL; 1701353Sericheng mutex_exit(&dsp->ds_thr_lock); 1711353Sericheng if (op != NULL) 1721353Sericheng (void) taskq_dispatch(system_taskq, op, dsp, TQ_SLEEP); 1730Sstevel@tonic-gate } 1740Sstevel@tonic-gate 175269Sericheng #define NEG(x) -(x) 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate typedef struct dl_info_ack_wrapper { 1780Sstevel@tonic-gate dl_info_ack_t dl_info; 179*2311Sseb uint8_t dl_addr[MAXMACADDRLEN + sizeof (uint16_t)]; 180*2311Sseb uint8_t dl_brdcst_addr[MAXMACADDRLEN]; 1810Sstevel@tonic-gate dl_qos_cl_range1_t dl_qos_range1; 1820Sstevel@tonic-gate dl_qos_cl_sel1_t dl_qos_sel1; 1830Sstevel@tonic-gate } dl_info_ack_wrapper_t; 1840Sstevel@tonic-gate 1850Sstevel@tonic-gate /* 186269Sericheng * DL_INFO_REQ 1870Sstevel@tonic-gate */ 188269Sericheng /*ARGSUSED*/ 189269Sericheng static boolean_t 190269Sericheng proto_info_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 1910Sstevel@tonic-gate { 1920Sstevel@tonic-gate dl_info_ack_wrapper_t *dlwp; 1930Sstevel@tonic-gate dl_info_ack_t *dlp; 1940Sstevel@tonic-gate dl_qos_cl_sel1_t *selp; 1950Sstevel@tonic-gate dl_qos_cl_range1_t *rangep; 1960Sstevel@tonic-gate uint8_t *addr; 1970Sstevel@tonic-gate uint8_t *brdcst_addr; 1980Sstevel@tonic-gate uint_t addr_length; 1990Sstevel@tonic-gate uint_t sap_length; 200269Sericheng mac_info_t minfo; 201269Sericheng mac_info_t *minfop; 202269Sericheng queue_t *q = dsp->ds_wq; 2030Sstevel@tonic-gate 2040Sstevel@tonic-gate /* 2050Sstevel@tonic-gate * Swap the request message for one large enough to contain the 2060Sstevel@tonic-gate * wrapper structure defined above. 2070Sstevel@tonic-gate */ 208269Sericheng if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t), 2090Sstevel@tonic-gate M_PCPROTO, 0)) == NULL) 210269Sericheng return (B_FALSE); 211269Sericheng 212269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 2130Sstevel@tonic-gate 2140Sstevel@tonic-gate bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t)); 2150Sstevel@tonic-gate dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr; 2160Sstevel@tonic-gate 2170Sstevel@tonic-gate dlp = &(dlwp->dl_info); 2180Sstevel@tonic-gate ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr); 2190Sstevel@tonic-gate 2200Sstevel@tonic-gate dlp->dl_primitive = DL_INFO_ACK; 2210Sstevel@tonic-gate 2220Sstevel@tonic-gate /* 2230Sstevel@tonic-gate * Set up the sub-structure pointers. 2240Sstevel@tonic-gate */ 2250Sstevel@tonic-gate addr = dlwp->dl_addr; 2260Sstevel@tonic-gate brdcst_addr = dlwp->dl_brdcst_addr; 2270Sstevel@tonic-gate rangep = &(dlwp->dl_qos_range1); 2280Sstevel@tonic-gate selp = &(dlwp->dl_qos_sel1); 2290Sstevel@tonic-gate 2300Sstevel@tonic-gate /* 2310Sstevel@tonic-gate * This driver supports only version 2 connectionless DLPI provider 2320Sstevel@tonic-gate * nodes. 2330Sstevel@tonic-gate */ 2340Sstevel@tonic-gate dlp->dl_service_mode = DL_CLDLS; 2350Sstevel@tonic-gate dlp->dl_version = DL_VERSION_2; 2360Sstevel@tonic-gate 2370Sstevel@tonic-gate /* 238269Sericheng * Set the style of the provider 2390Sstevel@tonic-gate */ 240269Sericheng dlp->dl_provider_style = dsp->ds_style; 2410Sstevel@tonic-gate ASSERT(dlp->dl_provider_style == DL_STYLE1 || 2420Sstevel@tonic-gate dlp->dl_provider_style == DL_STYLE2); 2430Sstevel@tonic-gate 2440Sstevel@tonic-gate /* 2450Sstevel@tonic-gate * Set the current DLPI state. 2460Sstevel@tonic-gate */ 2470Sstevel@tonic-gate dlp->dl_current_state = dsp->ds_dlstate; 2480Sstevel@tonic-gate 2490Sstevel@tonic-gate /* 250269Sericheng * Gratuitously set the media type. This is to deal with modules 251269Sericheng * that assume the media type is known prior to DL_ATTACH_REQ 2520Sstevel@tonic-gate * being completed. 2530Sstevel@tonic-gate */ 2540Sstevel@tonic-gate dlp->dl_mac_type = DL_ETHER; 2550Sstevel@tonic-gate 2560Sstevel@tonic-gate /* 257269Sericheng * If the stream is not at least attached we try to retrieve the 258269Sericheng * mac_info using mac_info_get() 2590Sstevel@tonic-gate */ 2600Sstevel@tonic-gate if (dsp->ds_dlstate == DL_UNATTACHED || 2610Sstevel@tonic-gate dsp->ds_dlstate == DL_ATTACH_PENDING || 262269Sericheng dsp->ds_dlstate == DL_DETACH_PENDING) { 263269Sericheng if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) { 264269Sericheng /* 265269Sericheng * Cannot find mac_info. giving up. 266269Sericheng */ 267269Sericheng goto done; 268269Sericheng } 269269Sericheng minfop = &minfo; 270269Sericheng } else { 271269Sericheng minfop = (mac_info_t *)dsp->ds_mip; 272269Sericheng } 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate /* 2750Sstevel@tonic-gate * Set the media type (properly this time). 2760Sstevel@tonic-gate */ 277269Sericheng dlp->dl_mac_type = minfop->mi_media; 2780Sstevel@tonic-gate 2790Sstevel@tonic-gate /* 2800Sstevel@tonic-gate * Set the DLSAP length. We only support 16 bit values and they 2810Sstevel@tonic-gate * appear after the MAC address portion of DLSAP addresses. 2820Sstevel@tonic-gate */ 2830Sstevel@tonic-gate sap_length = sizeof (uint16_t); 2840Sstevel@tonic-gate dlp->dl_sap_length = NEG(sap_length); 2850Sstevel@tonic-gate 2860Sstevel@tonic-gate /* 2870Sstevel@tonic-gate * Set the minimum and maximum payload sizes. 2880Sstevel@tonic-gate */ 289269Sericheng dlp->dl_min_sdu = minfop->mi_sdu_min; 290269Sericheng dlp->dl_max_sdu = minfop->mi_sdu_max; 2910Sstevel@tonic-gate 292269Sericheng addr_length = minfop->mi_addr_length; 2930Sstevel@tonic-gate 2940Sstevel@tonic-gate /* 2950Sstevel@tonic-gate * Copy in the media broadcast address. 2960Sstevel@tonic-gate */ 297*2311Sseb if (minfop->mi_brdcst_addr != NULL) { 298*2311Sseb dlp->dl_brdcst_addr_offset = 299*2311Sseb (uintptr_t)brdcst_addr - (uintptr_t)dlp; 300*2311Sseb bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length); 301*2311Sseb dlp->dl_brdcst_addr_length = addr_length; 302*2311Sseb } 3030Sstevel@tonic-gate 3040Sstevel@tonic-gate /* 3050Sstevel@tonic-gate * We only support QoS information for VLAN interfaces. 3060Sstevel@tonic-gate */ 3070Sstevel@tonic-gate if (dsp->ds_vid != VLAN_ID_NONE) { 3080Sstevel@tonic-gate dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp; 3090Sstevel@tonic-gate dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t); 3100Sstevel@tonic-gate 3110Sstevel@tonic-gate rangep->dl_qos_type = DL_QOS_CL_RANGE1; 3120Sstevel@tonic-gate rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN; 3130Sstevel@tonic-gate rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN; 3140Sstevel@tonic-gate rangep->dl_protection.dl_min = DL_UNKNOWN; 3150Sstevel@tonic-gate rangep->dl_protection.dl_max = DL_UNKNOWN; 3160Sstevel@tonic-gate rangep->dl_residual_error = DL_UNKNOWN; 3170Sstevel@tonic-gate 3180Sstevel@tonic-gate /* 3190Sstevel@tonic-gate * Specify the supported range of priorities. 3200Sstevel@tonic-gate */ 3210Sstevel@tonic-gate rangep->dl_priority.dl_min = 0; 3220Sstevel@tonic-gate rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1; 3230Sstevel@tonic-gate 3240Sstevel@tonic-gate dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp; 3250Sstevel@tonic-gate dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t); 3260Sstevel@tonic-gate 3270Sstevel@tonic-gate selp->dl_qos_type = DL_QOS_CL_SEL1; 3280Sstevel@tonic-gate selp->dl_trans_delay = DL_UNKNOWN; 3290Sstevel@tonic-gate selp->dl_protection = DL_UNKNOWN; 3300Sstevel@tonic-gate selp->dl_residual_error = DL_UNKNOWN; 3310Sstevel@tonic-gate 3320Sstevel@tonic-gate /* 3330Sstevel@tonic-gate * Specify the current priority (which can be changed by 3340Sstevel@tonic-gate * the DL_UDQOS_REQ primitive). 3350Sstevel@tonic-gate */ 3360Sstevel@tonic-gate selp->dl_priority = dsp->ds_pri; 3370Sstevel@tonic-gate } else { 3380Sstevel@tonic-gate /* 3390Sstevel@tonic-gate * Shorten the buffer to lose the unused QoS information 340269Sericheng * structures. 3410Sstevel@tonic-gate */ 3420Sstevel@tonic-gate mp->b_wptr = (uint8_t *)rangep; 3430Sstevel@tonic-gate } 3440Sstevel@tonic-gate 3450Sstevel@tonic-gate dlp->dl_addr_length = addr_length + sizeof (uint16_t); 3460Sstevel@tonic-gate if (dsp->ds_dlstate == DL_IDLE) { 3470Sstevel@tonic-gate /* 3480Sstevel@tonic-gate * The stream is bound. Therefore we can formulate a valid 3490Sstevel@tonic-gate * DLSAP address. 3500Sstevel@tonic-gate */ 3510Sstevel@tonic-gate dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp; 352*2311Sseb if (addr_length > 0) 353*2311Sseb bcopy(dsp->ds_curr_addr, addr, addr_length); 3540Sstevel@tonic-gate *(uint16_t *)(addr + addr_length) = dsp->ds_sap; 3550Sstevel@tonic-gate } 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate done: 3580Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0)); 3590Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_qos_range_offset != 0, 3600Sstevel@tonic-gate dlp->dl_qos_range_length != 0)); 3610Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0)); 3620Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_brdcst_addr_offset != 0, 3630Sstevel@tonic-gate dlp->dl_brdcst_addr_length != 0)); 3640Sstevel@tonic-gate 365269Sericheng rw_exit(&dsp->ds_lock); 366269Sericheng 367269Sericheng qreply(q, mp); 368269Sericheng return (B_TRUE); 369269Sericheng } 370269Sericheng 371269Sericheng /* 372269Sericheng * DL_ATTACH_REQ 373269Sericheng */ 374269Sericheng static boolean_t 375269Sericheng proto_attach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 376269Sericheng { 377269Sericheng dl_attach_req_t *dlp = (dl_attach_req_t *)udlp; 378269Sericheng int err = 0; 379269Sericheng t_uscalar_t dl_err; 380269Sericheng queue_t *q = dsp->ds_wq; 381269Sericheng 382269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 383269Sericheng 384269Sericheng if (MBLKL(mp) < sizeof (dl_attach_req_t) || 385269Sericheng dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) { 386269Sericheng dl_err = DL_BADPRIM; 387269Sericheng goto failed; 388269Sericheng } 389269Sericheng 390269Sericheng if (dsp->ds_dlstate != DL_UNATTACHED) { 391269Sericheng dl_err = DL_OUTSTATE; 392269Sericheng goto failed; 393269Sericheng } 394269Sericheng 395269Sericheng dsp->ds_dlstate = DL_ATTACH_PENDING; 396269Sericheng 397269Sericheng err = dld_str_attach(dsp, dlp->dl_ppa); 398269Sericheng if (err != 0) { 399269Sericheng switch (err) { 400269Sericheng case ENOENT: 401269Sericheng dl_err = DL_BADPPA; 402269Sericheng err = 0; 403269Sericheng break; 404269Sericheng default: 405269Sericheng dl_err = DL_SYSERR; 406269Sericheng break; 407269Sericheng } 408269Sericheng dsp->ds_dlstate = DL_UNATTACHED; 409269Sericheng goto failed; 410269Sericheng } 411269Sericheng ASSERT(dsp->ds_dlstate == DL_UNBOUND); 412269Sericheng rw_exit(&dsp->ds_lock); 413269Sericheng 414269Sericheng dlokack(q, mp, DL_ATTACH_REQ); 415269Sericheng return (B_TRUE); 416269Sericheng failed: 417269Sericheng rw_exit(&dsp->ds_lock); 418269Sericheng dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err); 419269Sericheng return (B_FALSE); 4200Sstevel@tonic-gate } 4210Sstevel@tonic-gate 4220Sstevel@tonic-gate /* 423269Sericheng * DL_DETACH_REQ 4240Sstevel@tonic-gate */ 4251353Sericheng static void 4261353Sericheng proto_process_detach_req(void *arg) 4271353Sericheng { 4281353Sericheng dld_str_t *dsp = arg; 4291353Sericheng mblk_t *mp; 4301353Sericheng 4311353Sericheng /* 4321353Sericheng * We don't need to hold locks because no other thread 4331353Sericheng * would manipulate dsp while it is in a PENDING state. 4341353Sericheng */ 4351353Sericheng ASSERT(dsp->ds_pending_req != NULL); 4361353Sericheng ASSERT(dsp->ds_dlstate == DL_DETACH_PENDING); 4371353Sericheng 4381353Sericheng mp = dsp->ds_pending_req; 4391353Sericheng dsp->ds_pending_req = NULL; 4401353Sericheng dld_str_detach(dsp); 4411353Sericheng dlokack(dsp->ds_wq, mp, DL_DETACH_REQ); 4421353Sericheng 4431353Sericheng DLD_WAKEUP(dsp); 4441353Sericheng } 4451353Sericheng 446269Sericheng /*ARGSUSED*/ 447269Sericheng static boolean_t 448269Sericheng proto_detach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 4490Sstevel@tonic-gate { 450269Sericheng queue_t *q = dsp->ds_wq; 451269Sericheng t_uscalar_t dl_err; 4520Sstevel@tonic-gate 453269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 4540Sstevel@tonic-gate 455269Sericheng if (MBLKL(mp) < sizeof (dl_detach_req_t)) { 456269Sericheng dl_err = DL_BADPRIM; 457269Sericheng goto failed; 458269Sericheng } 4590Sstevel@tonic-gate 460269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) { 461269Sericheng dl_err = DL_OUTSTATE; 462269Sericheng goto failed; 4630Sstevel@tonic-gate } 4640Sstevel@tonic-gate 465269Sericheng if (dsp->ds_style == DL_STYLE1) { 466269Sericheng dl_err = DL_BADPRIM; 467269Sericheng goto failed; 468269Sericheng } 469269Sericheng 470269Sericheng dsp->ds_dlstate = DL_DETACH_PENDING; 471269Sericheng 472269Sericheng /* 473269Sericheng * Complete the detach when the driver is single-threaded. 474269Sericheng */ 475269Sericheng mutex_enter(&dsp->ds_thr_lock); 4761353Sericheng ASSERT(dsp->ds_pending_req == NULL); 4771353Sericheng dsp->ds_pending_req = mp; 4781353Sericheng dsp->ds_pending_op = proto_process_detach_req; 4791353Sericheng dsp->ds_pending_cnt++; 480269Sericheng mutex_exit(&dsp->ds_thr_lock); 481269Sericheng rw_exit(&dsp->ds_lock); 482269Sericheng 483269Sericheng return (B_TRUE); 484269Sericheng failed: 485269Sericheng rw_exit(&dsp->ds_lock); 486269Sericheng dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0); 487269Sericheng return (B_FALSE); 4880Sstevel@tonic-gate } 4890Sstevel@tonic-gate 4900Sstevel@tonic-gate /* 491269Sericheng * DL_BIND_REQ 4920Sstevel@tonic-gate */ 493269Sericheng static boolean_t 494269Sericheng proto_bind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 4950Sstevel@tonic-gate { 496269Sericheng dl_bind_req_t *dlp = (dl_bind_req_t *)udlp; 497269Sericheng int err = 0; 498*2311Sseb uint8_t addr[MAXMACADDRLEN]; 499269Sericheng uint_t addr_length; 500269Sericheng t_uscalar_t dl_err; 501269Sericheng t_scalar_t sap; 502269Sericheng queue_t *q = dsp->ds_wq; 503269Sericheng 5041521Syz147064 rw_enter(&dsp->ds_lock, RW_WRITER); 5051521Syz147064 506269Sericheng if (MBLKL(mp) < sizeof (dl_bind_req_t)) { 507269Sericheng dl_err = DL_BADPRIM; 508269Sericheng goto failed; 509269Sericheng } 510269Sericheng 511269Sericheng if (dlp->dl_xidtest_flg != 0) { 512269Sericheng dl_err = DL_NOAUTO; 513269Sericheng goto failed; 514269Sericheng } 515269Sericheng 516269Sericheng if (dlp->dl_service_mode != DL_CLDLS) { 517269Sericheng dl_err = DL_UNSUPPORTED; 518269Sericheng goto failed; 519269Sericheng } 520269Sericheng 521269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) { 522269Sericheng dl_err = DL_OUTSTATE; 523269Sericheng goto failed; 524269Sericheng } 5250Sstevel@tonic-gate 526269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 527269Sericheng !dls_active_set(dsp->ds_dc)) { 528269Sericheng dl_err = DL_SYSERR; 529269Sericheng err = EBUSY; 530269Sericheng goto failed; 531269Sericheng } 532269Sericheng 533269Sericheng dsp->ds_dlstate = DL_BIND_PENDING; 534269Sericheng /* 535269Sericheng * Set the receive callback. 536269Sericheng */ 537269Sericheng dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_RAW) ? 538269Sericheng dld_str_rx_raw : dld_str_rx_unitdata, dsp); 5390Sstevel@tonic-gate 540269Sericheng /* 541269Sericheng * Bind the channel such that it can receive packets. 542269Sericheng */ 543269Sericheng sap = dsp->ds_sap = dlp->dl_sap; 544269Sericheng err = dls_bind(dsp->ds_dc, dlp->dl_sap); 545269Sericheng if (err != 0) { 546269Sericheng switch (err) { 547269Sericheng case EINVAL: 548269Sericheng dl_err = DL_BADADDR; 549269Sericheng err = 0; 550269Sericheng break; 551269Sericheng default: 552269Sericheng dl_err = DL_SYSERR; 553269Sericheng break; 554269Sericheng } 555269Sericheng dsp->ds_dlstate = DL_UNBOUND; 556269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 557269Sericheng dls_active_clear(dsp->ds_dc); 558269Sericheng 5590Sstevel@tonic-gate goto failed; 560269Sericheng } 5610Sstevel@tonic-gate 5620Sstevel@tonic-gate /* 5630Sstevel@tonic-gate * Copy in MAC address. 5640Sstevel@tonic-gate */ 5650Sstevel@tonic-gate addr_length = dsp->ds_mip->mi_addr_length; 5660Sstevel@tonic-gate bcopy(dsp->ds_curr_addr, addr, addr_length); 5670Sstevel@tonic-gate 5680Sstevel@tonic-gate /* 5690Sstevel@tonic-gate * Copy in the DLSAP. 5700Sstevel@tonic-gate */ 5710Sstevel@tonic-gate *(uint16_t *)(addr + addr_length) = dsp->ds_sap; 5720Sstevel@tonic-gate addr_length += sizeof (uint16_t); 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate dsp->ds_dlstate = DL_IDLE; 575269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 576269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 5770Sstevel@tonic-gate 578269Sericheng rw_exit(&dsp->ds_lock); 579269Sericheng 580269Sericheng dlbindack(q, mp, sap, (void *)addr, addr_length, 0, 0); 581269Sericheng return (B_TRUE); 5820Sstevel@tonic-gate failed: 583269Sericheng rw_exit(&dsp->ds_lock); 584269Sericheng dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err); 585269Sericheng return (B_FALSE); 5860Sstevel@tonic-gate } 5870Sstevel@tonic-gate 5880Sstevel@tonic-gate /* 589269Sericheng * DL_UNBIND_REQ 5900Sstevel@tonic-gate */ 591269Sericheng /*ARGSUSED*/ 5921353Sericheng static void 5931353Sericheng proto_process_unbind_req(void *arg) 5940Sstevel@tonic-gate { 5951353Sericheng dld_str_t *dsp = arg; 5961353Sericheng mblk_t *mp; 597269Sericheng 5981353Sericheng /* 5991353Sericheng * We don't need to hold locks because no other thread 6001353Sericheng * would manipulate dsp while it is in a PENDING state. 6011353Sericheng */ 6021353Sericheng ASSERT(dsp->ds_pending_req != NULL); 6031353Sericheng ASSERT(dsp->ds_dlstate == DL_UNBIND_PENDING); 604269Sericheng 605269Sericheng /* 606269Sericheng * Flush any remaining packets scheduled for transmission. 607269Sericheng */ 608269Sericheng dld_tx_flush(dsp); 609269Sericheng 610269Sericheng /* 611269Sericheng * Unbind the channel to stop packets being received. 612269Sericheng */ 613269Sericheng dls_unbind(dsp->ds_dc); 614269Sericheng 615269Sericheng /* 616269Sericheng * Disable polling mode, if it is enabled. 617269Sericheng */ 618269Sericheng proto_poll_disable(dsp); 619269Sericheng 620269Sericheng /* 621269Sericheng * Clear the receive callback. 622269Sericheng */ 623269Sericheng dls_rx_set(dsp->ds_dc, NULL, NULL); 624269Sericheng 625269Sericheng /* 626269Sericheng * Set the mode back to the default (unitdata). 627269Sericheng */ 628269Sericheng dsp->ds_mode = DLD_UNITDATA; 629269Sericheng 6301184Skrgopi /* 6311184Skrgopi * If soft rings were enabled, the workers 6321353Sericheng * should be quiesced. We cannot check for 6331184Skrgopi * ds_soft_ring flag because 6341184Skrgopi * proto_soft_ring_disable() called from 6351184Skrgopi * proto_capability_req() would have reset it. 6361184Skrgopi */ 6371353Sericheng if (dls_soft_ring_workers(dsp->ds_dc)) 6381353Sericheng dls_soft_ring_disable(dsp->ds_dc); 6391353Sericheng 6401353Sericheng mp = dsp->ds_pending_req; 6411353Sericheng dsp->ds_pending_req = NULL; 6421353Sericheng dsp->ds_dlstate = DL_UNBOUND; 6431353Sericheng dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ); 6441353Sericheng 6451353Sericheng DLD_WAKEUP(dsp); 6461353Sericheng } 6471353Sericheng 6481353Sericheng /*ARGSUSED*/ 6491353Sericheng static boolean_t 6501353Sericheng proto_unbind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 6511353Sericheng { 6521353Sericheng queue_t *q = dsp->ds_wq; 6531353Sericheng t_uscalar_t dl_err; 6541353Sericheng 6551353Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 6561353Sericheng 6571353Sericheng if (MBLKL(mp) < sizeof (dl_unbind_req_t)) { 6581353Sericheng dl_err = DL_BADPRIM; 6591353Sericheng goto failed; 6601184Skrgopi } 6611184Skrgopi 6621353Sericheng if (dsp->ds_dlstate != DL_IDLE) { 6631353Sericheng dl_err = DL_OUTSTATE; 6641353Sericheng goto failed; 6651353Sericheng } 6661353Sericheng 6671353Sericheng dsp->ds_dlstate = DL_UNBIND_PENDING; 6681353Sericheng 6691353Sericheng mutex_enter(&dsp->ds_thr_lock); 6701353Sericheng ASSERT(dsp->ds_pending_req == NULL); 6711353Sericheng dsp->ds_pending_req = mp; 6721353Sericheng dsp->ds_pending_op = proto_process_unbind_req; 6731353Sericheng dsp->ds_pending_cnt++; 6741353Sericheng mutex_exit(&dsp->ds_thr_lock); 675269Sericheng rw_exit(&dsp->ds_lock); 676269Sericheng 677269Sericheng return (B_TRUE); 678269Sericheng failed: 679269Sericheng rw_exit(&dsp->ds_lock); 680269Sericheng dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0); 681269Sericheng return (B_FALSE); 6820Sstevel@tonic-gate } 6830Sstevel@tonic-gate 6840Sstevel@tonic-gate /* 685269Sericheng * DL_PROMISCON_REQ 6860Sstevel@tonic-gate */ 687269Sericheng static boolean_t 688269Sericheng proto_promiscon_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 6890Sstevel@tonic-gate { 690269Sericheng dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)udlp; 691269Sericheng int err = 0; 692269Sericheng t_uscalar_t dl_err; 693269Sericheng uint32_t promisc_saved; 694269Sericheng queue_t *q = dsp->ds_wq; 695269Sericheng 6961521Syz147064 rw_enter(&dsp->ds_lock, RW_WRITER); 6971521Syz147064 698269Sericheng if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) { 699269Sericheng dl_err = DL_BADPRIM; 700269Sericheng goto failed; 701269Sericheng } 702269Sericheng 703269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 704269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 705269Sericheng dl_err = DL_OUTSTATE; 7060Sstevel@tonic-gate goto failed; 707269Sericheng } 7080Sstevel@tonic-gate 709269Sericheng promisc_saved = dsp->ds_promisc; 710269Sericheng switch (dlp->dl_level) { 711269Sericheng case DL_PROMISC_SAP: 712269Sericheng dsp->ds_promisc |= DLS_PROMISC_SAP; 713269Sericheng break; 714269Sericheng 715269Sericheng case DL_PROMISC_MULTI: 716269Sericheng dsp->ds_promisc |= DLS_PROMISC_MULTI; 717269Sericheng break; 718269Sericheng 719269Sericheng case DL_PROMISC_PHYS: 720269Sericheng dsp->ds_promisc |= DLS_PROMISC_PHYS; 721269Sericheng break; 722269Sericheng 723269Sericheng default: 724269Sericheng dl_err = DL_NOTSUPPORTED; 725269Sericheng goto failed; 726269Sericheng } 7270Sstevel@tonic-gate 728269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 729269Sericheng !dls_active_set(dsp->ds_dc)) { 730269Sericheng dsp->ds_promisc = promisc_saved; 731269Sericheng dl_err = DL_SYSERR; 732269Sericheng err = EBUSY; 733269Sericheng goto failed; 734269Sericheng } 735269Sericheng 736269Sericheng /* 737269Sericheng * Adjust channel promiscuity. 738269Sericheng */ 739269Sericheng err = dls_promisc(dsp->ds_dc, dsp->ds_promisc); 740269Sericheng if (err != 0) { 741269Sericheng dl_err = DL_SYSERR; 742269Sericheng dsp->ds_promisc = promisc_saved; 743269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 744269Sericheng dls_active_clear(dsp->ds_dc); 745269Sericheng 746269Sericheng goto failed; 747269Sericheng } 748269Sericheng 749269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 750269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 751269Sericheng 752269Sericheng rw_exit(&dsp->ds_lock); 753269Sericheng dlokack(q, mp, DL_PROMISCON_REQ); 754269Sericheng return (B_TRUE); 7550Sstevel@tonic-gate failed: 756269Sericheng rw_exit(&dsp->ds_lock); 757269Sericheng dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err); 758269Sericheng return (B_FALSE); 7590Sstevel@tonic-gate } 7600Sstevel@tonic-gate 7610Sstevel@tonic-gate /* 762269Sericheng * DL_PROMISCOFF_REQ 7630Sstevel@tonic-gate */ 764269Sericheng static boolean_t 765269Sericheng proto_promiscoff_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 7660Sstevel@tonic-gate { 767269Sericheng dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)udlp; 768269Sericheng int err = 0; 769269Sericheng t_uscalar_t dl_err; 770269Sericheng uint32_t promisc_saved; 771269Sericheng queue_t *q = dsp->ds_wq; 772269Sericheng 7731521Syz147064 rw_enter(&dsp->ds_lock, RW_WRITER); 774269Sericheng 775269Sericheng if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) { 776269Sericheng dl_err = DL_BADPRIM; 7770Sstevel@tonic-gate goto failed; 778269Sericheng } 7790Sstevel@tonic-gate 780269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 781269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 782269Sericheng dl_err = DL_OUTSTATE; 7830Sstevel@tonic-gate goto failed; 784269Sericheng } 7850Sstevel@tonic-gate 786269Sericheng promisc_saved = dsp->ds_promisc; 787269Sericheng switch (dlp->dl_level) { 788269Sericheng case DL_PROMISC_SAP: 789269Sericheng if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) { 790269Sericheng dl_err = DL_NOTENAB; 791269Sericheng goto failed; 792269Sericheng } 793269Sericheng dsp->ds_promisc &= ~DLS_PROMISC_SAP; 7940Sstevel@tonic-gate break; 7950Sstevel@tonic-gate 796269Sericheng case DL_PROMISC_MULTI: 797269Sericheng if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) { 798269Sericheng dl_err = DL_NOTENAB; 799269Sericheng goto failed; 800269Sericheng } 801269Sericheng dsp->ds_promisc &= ~DLS_PROMISC_MULTI; 802269Sericheng break; 803269Sericheng 804269Sericheng case DL_PROMISC_PHYS: 805269Sericheng if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) { 806269Sericheng dl_err = DL_NOTENAB; 807269Sericheng goto failed; 808269Sericheng } 809269Sericheng dsp->ds_promisc &= ~DLS_PROMISC_PHYS; 8100Sstevel@tonic-gate break; 8110Sstevel@tonic-gate 8120Sstevel@tonic-gate default: 813269Sericheng dl_err = DL_NOTSUPPORTED; 814269Sericheng goto failed; 815269Sericheng } 816269Sericheng 817269Sericheng /* 818269Sericheng * Adjust channel promiscuity. 819269Sericheng */ 820269Sericheng err = dls_promisc(dsp->ds_dc, dsp->ds_promisc); 821269Sericheng if (err != 0) { 822269Sericheng dsp->ds_promisc = promisc_saved; 8230Sstevel@tonic-gate dl_err = DL_SYSERR; 824269Sericheng goto failed; 825269Sericheng } 826269Sericheng 827269Sericheng rw_exit(&dsp->ds_lock); 828269Sericheng dlokack(q, mp, DL_PROMISCOFF_REQ); 829269Sericheng return (B_TRUE); 830269Sericheng failed: 831269Sericheng rw_exit(&dsp->ds_lock); 832269Sericheng dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err); 833269Sericheng return (B_FALSE); 834269Sericheng } 835269Sericheng 836269Sericheng /* 837269Sericheng * DL_ENABMULTI_REQ 838269Sericheng */ 839269Sericheng static boolean_t 840269Sericheng proto_enabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 841269Sericheng { 842269Sericheng dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)udlp; 843269Sericheng int err = 0; 844269Sericheng t_uscalar_t dl_err; 845269Sericheng queue_t *q = dsp->ds_wq; 846269Sericheng 847269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 848269Sericheng 849269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 850269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 851269Sericheng dl_err = DL_OUTSTATE; 852269Sericheng goto failed; 853269Sericheng } 854269Sericheng 855269Sericheng if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) || 856269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 857269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 858269Sericheng dl_err = DL_BADPRIM; 859269Sericheng goto failed; 860269Sericheng } 861269Sericheng 862269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 863269Sericheng !dls_active_set(dsp->ds_dc)) { 864269Sericheng dl_err = DL_SYSERR; 865269Sericheng err = EBUSY; 866269Sericheng goto failed; 8670Sstevel@tonic-gate } 8680Sstevel@tonic-gate 869269Sericheng err = dls_multicst_add(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset); 870269Sericheng if (err != 0) { 871269Sericheng switch (err) { 872269Sericheng case EINVAL: 873269Sericheng dl_err = DL_BADADDR; 874269Sericheng err = 0; 875269Sericheng break; 876269Sericheng case ENOSPC: 877269Sericheng dl_err = DL_TOOMANY; 878269Sericheng err = 0; 879269Sericheng break; 880269Sericheng default: 881269Sericheng dl_err = DL_SYSERR; 882269Sericheng break; 883269Sericheng } 884269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 885269Sericheng dls_active_clear(dsp->ds_dc); 886269Sericheng 887269Sericheng goto failed; 888269Sericheng } 889269Sericheng 890269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 891269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 892269Sericheng 893269Sericheng rw_exit(&dsp->ds_lock); 894269Sericheng dlokack(q, mp, DL_ENABMULTI_REQ); 895269Sericheng return (B_TRUE); 896269Sericheng failed: 897269Sericheng rw_exit(&dsp->ds_lock); 898269Sericheng dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err); 899269Sericheng return (B_FALSE); 900269Sericheng } 901269Sericheng 902269Sericheng /* 903269Sericheng * DL_DISABMULTI_REQ 904269Sericheng */ 905269Sericheng static boolean_t 906269Sericheng proto_disabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 907269Sericheng { 908269Sericheng dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)udlp; 909269Sericheng int err = 0; 910269Sericheng t_uscalar_t dl_err; 911269Sericheng queue_t *q = dsp->ds_wq; 912269Sericheng 913269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 914269Sericheng 915269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 916269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 917269Sericheng dl_err = DL_OUTSTATE; 918269Sericheng goto failed; 919269Sericheng } 920269Sericheng 921269Sericheng if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) || 922269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 923269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 924269Sericheng dl_err = DL_BADPRIM; 925269Sericheng goto failed; 926269Sericheng } 927269Sericheng 928269Sericheng err = dls_multicst_remove(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset); 929269Sericheng if (err != 0) { 930269Sericheng switch (err) { 931269Sericheng case EINVAL: 932269Sericheng dl_err = DL_BADADDR; 933269Sericheng err = 0; 934269Sericheng break; 935269Sericheng 936269Sericheng case ENOENT: 937269Sericheng dl_err = DL_NOTENAB; 938269Sericheng err = 0; 939269Sericheng break; 940269Sericheng 941269Sericheng default: 942269Sericheng dl_err = DL_SYSERR; 943269Sericheng break; 944269Sericheng } 945269Sericheng goto failed; 946269Sericheng } 947269Sericheng 948269Sericheng rw_exit(&dsp->ds_lock); 949269Sericheng dlokack(q, mp, DL_DISABMULTI_REQ); 950269Sericheng return (B_TRUE); 951269Sericheng failed: 952269Sericheng rw_exit(&dsp->ds_lock); 953269Sericheng dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err); 954269Sericheng return (B_FALSE); 9550Sstevel@tonic-gate } 9560Sstevel@tonic-gate 9570Sstevel@tonic-gate /* 958269Sericheng * DL_PHYS_ADDR_REQ 9590Sstevel@tonic-gate */ 960269Sericheng static boolean_t 961269Sericheng proto_physaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 9620Sstevel@tonic-gate { 963269Sericheng dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)udlp; 964269Sericheng queue_t *q = dsp->ds_wq; 965269Sericheng t_uscalar_t dl_err; 966269Sericheng char *addr; 967269Sericheng uint_t addr_length; 968269Sericheng 969269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 970269Sericheng 971269Sericheng if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) { 972269Sericheng dl_err = DL_BADPRIM; 973269Sericheng goto failed; 974269Sericheng } 975269Sericheng 976269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 977269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 978269Sericheng dl_err = DL_OUTSTATE; 979269Sericheng goto failed; 980269Sericheng } 9810Sstevel@tonic-gate 982269Sericheng if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR && 983269Sericheng dlp->dl_addr_type != DL_FACT_PHYS_ADDR) { 984269Sericheng dl_err = DL_UNSUPPORTED; 9850Sstevel@tonic-gate goto failed; 986269Sericheng } 9870Sstevel@tonic-gate 988269Sericheng addr_length = dsp->ds_mip->mi_addr_length; 989269Sericheng addr = kmem_alloc(addr_length, KM_NOSLEEP); 990269Sericheng if (addr == NULL) { 991269Sericheng rw_exit(&dsp->ds_lock); 992269Sericheng merror(q, mp, ENOSR); 993269Sericheng return (B_FALSE); 994269Sericheng } 9950Sstevel@tonic-gate 996269Sericheng /* 997269Sericheng * Copy out the address before we drop the lock; we don't 998269Sericheng * want to call dlphysaddrack() while holding ds_lock. 999269Sericheng */ 1000269Sericheng bcopy((dlp->dl_addr_type == DL_CURR_PHYS_ADDR) ? 1001269Sericheng dsp->ds_curr_addr : dsp->ds_fact_addr, addr, addr_length); 1002269Sericheng 1003269Sericheng rw_exit(&dsp->ds_lock); 1004269Sericheng dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length); 1005269Sericheng kmem_free(addr, addr_length); 1006269Sericheng return (B_TRUE); 10070Sstevel@tonic-gate failed: 1008269Sericheng rw_exit(&dsp->ds_lock); 1009269Sericheng dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0); 1010269Sericheng return (B_FALSE); 1011269Sericheng } 10120Sstevel@tonic-gate 1013269Sericheng /* 1014269Sericheng * DL_SET_PHYS_ADDR_REQ 1015269Sericheng */ 1016269Sericheng static boolean_t 1017269Sericheng proto_setphysaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 1018269Sericheng { 1019269Sericheng dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)udlp; 1020269Sericheng int err = 0; 1021269Sericheng t_uscalar_t dl_err; 1022269Sericheng queue_t *q = dsp->ds_wq; 10230Sstevel@tonic-gate 1024269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1025269Sericheng 1026269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1027269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1028269Sericheng dl_err = DL_OUTSTATE; 1029269Sericheng goto failed; 1030269Sericheng } 1031269Sericheng 1032269Sericheng if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) || 1033269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 1034269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 1035269Sericheng dl_err = DL_BADPRIM; 1036269Sericheng goto failed; 10370Sstevel@tonic-gate } 10380Sstevel@tonic-gate 1039269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 1040269Sericheng !dls_active_set(dsp->ds_dc)) { 1041269Sericheng dl_err = DL_SYSERR; 1042269Sericheng err = EBUSY; 1043269Sericheng goto failed; 1044269Sericheng } 1045269Sericheng 1046269Sericheng err = mac_unicst_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset); 1047269Sericheng if (err != 0) { 1048269Sericheng switch (err) { 1049269Sericheng case EINVAL: 1050269Sericheng dl_err = DL_BADADDR; 1051269Sericheng err = 0; 1052269Sericheng break; 1053269Sericheng 1054269Sericheng default: 1055269Sericheng dl_err = DL_SYSERR; 1056269Sericheng break; 1057269Sericheng } 1058269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 1059269Sericheng dls_active_clear(dsp->ds_dc); 1060269Sericheng 1061269Sericheng goto failed; 1062269Sericheng } 1063269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 1064269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 1065269Sericheng 1066269Sericheng rw_exit(&dsp->ds_lock); 1067269Sericheng dlokack(q, mp, DL_SET_PHYS_ADDR_REQ); 1068269Sericheng return (B_TRUE); 1069269Sericheng failed: 1070269Sericheng rw_exit(&dsp->ds_lock); 1071269Sericheng dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err); 1072269Sericheng return (B_FALSE); 1073269Sericheng } 1074269Sericheng 1075269Sericheng /* 1076269Sericheng * DL_UDQOS_REQ 1077269Sericheng */ 1078269Sericheng static boolean_t 1079269Sericheng proto_udqos_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 1080269Sericheng { 1081269Sericheng dl_udqos_req_t *dlp = (dl_udqos_req_t *)udlp; 1082269Sericheng dl_qos_cl_sel1_t *selp; 1083269Sericheng int off, len; 1084269Sericheng t_uscalar_t dl_err; 1085269Sericheng queue_t *q = dsp->ds_wq; 1086269Sericheng 1087269Sericheng off = dlp->dl_qos_offset; 1088269Sericheng len = dlp->dl_qos_length; 1089269Sericheng 10901521Syz147064 rw_enter(&dsp->ds_lock, RW_WRITER); 10911521Syz147064 1092269Sericheng if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) { 1093269Sericheng dl_err = DL_BADPRIM; 1094269Sericheng goto failed; 1095269Sericheng } 1096269Sericheng 1097269Sericheng selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off); 1098269Sericheng if (selp->dl_qos_type != DL_QOS_CL_SEL1) { 1099269Sericheng dl_err = DL_BADQOSTYPE; 1100269Sericheng goto failed; 1101269Sericheng } 1102269Sericheng 1103269Sericheng if (dsp->ds_vid == VLAN_ID_NONE || 1104269Sericheng selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 || 1105269Sericheng selp->dl_priority < 0) { 1106269Sericheng dl_err = DL_BADQOSPARAM; 1107269Sericheng goto failed; 1108269Sericheng } 1109269Sericheng 1110269Sericheng dsp->ds_pri = selp->dl_priority; 1111269Sericheng 1112269Sericheng rw_exit(&dsp->ds_lock); 1113269Sericheng dlokack(q, mp, DL_UDQOS_REQ); 1114269Sericheng return (B_TRUE); 1115269Sericheng failed: 1116269Sericheng rw_exit(&dsp->ds_lock); 1117269Sericheng dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0); 1118269Sericheng return (B_FALSE); 11190Sstevel@tonic-gate } 11200Sstevel@tonic-gate 11211184Skrgopi static boolean_t 11221184Skrgopi check_ip_above(queue_t *q) 11231184Skrgopi { 11241184Skrgopi queue_t *next_q; 11251184Skrgopi boolean_t ret = B_TRUE; 11261184Skrgopi 11271184Skrgopi claimstr(q); 11281184Skrgopi next_q = q->q_next; 11291184Skrgopi if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0) 11301184Skrgopi ret = B_FALSE; 11311184Skrgopi releasestr(q); 11321184Skrgopi return (ret); 11331184Skrgopi } 11341184Skrgopi 11350Sstevel@tonic-gate /* 1136269Sericheng * DL_CAPABILITY_REQ 11370Sstevel@tonic-gate */ 1138269Sericheng /*ARGSUSED*/ 1139269Sericheng static boolean_t 1140269Sericheng proto_capability_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 11410Sstevel@tonic-gate { 1142269Sericheng dl_capability_req_t *dlp = (dl_capability_req_t *)udlp; 1143269Sericheng dl_capability_sub_t *sp; 1144269Sericheng size_t size, len; 1145269Sericheng offset_t off, end; 1146269Sericheng t_uscalar_t dl_err; 1147269Sericheng queue_t *q = dsp->ds_wq; 1148269Sericheng boolean_t upgraded; 1149269Sericheng 1150269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 1151269Sericheng 1152269Sericheng if (MBLKL(mp) < sizeof (dl_capability_req_t)) { 1153269Sericheng dl_err = DL_BADPRIM; 1154269Sericheng goto failed; 1155269Sericheng } 1156269Sericheng 1157269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1158269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1159269Sericheng dl_err = DL_OUTSTATE; 1160269Sericheng goto failed; 1161269Sericheng } 1162269Sericheng 1163269Sericheng /* 1164269Sericheng * This request is overloaded. If there are no requested capabilities 1165269Sericheng * then we just want to acknowledge with all the capabilities we 1166269Sericheng * support. Otherwise we enable the set of capabilities requested. 1167269Sericheng */ 1168269Sericheng if (dlp->dl_sub_length == 0) { 1169269Sericheng /* callee drops lock */ 1170269Sericheng return (proto_capability_advertise(dsp, mp)); 1171269Sericheng } 1172269Sericheng 1173269Sericheng if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) { 1174269Sericheng dl_err = DL_BADPRIM; 1175269Sericheng goto failed; 1176269Sericheng } 1177269Sericheng 1178269Sericheng dlp->dl_primitive = DL_CAPABILITY_ACK; 1179269Sericheng 1180269Sericheng off = dlp->dl_sub_offset; 1181269Sericheng len = dlp->dl_sub_length; 11820Sstevel@tonic-gate 11830Sstevel@tonic-gate /* 1184269Sericheng * Walk the list of capabilities to be enabled. 11850Sstevel@tonic-gate */ 1186269Sericheng upgraded = B_FALSE; 1187269Sericheng for (end = off + len; off < end; ) { 1188269Sericheng sp = (dl_capability_sub_t *)(mp->b_rptr + off); 1189269Sericheng size = sizeof (dl_capability_sub_t) + sp->dl_length; 1190269Sericheng 1191269Sericheng if (off + size > end || 1192269Sericheng !IS_P2ALIGNED(off, sizeof (uint32_t))) { 1193269Sericheng dl_err = DL_BADPRIM; 1194269Sericheng goto failed; 1195269Sericheng } 1196269Sericheng 1197269Sericheng switch (sp->dl_cap) { 1198269Sericheng /* 1199269Sericheng * TCP/IP checksum offload to hardware. 1200269Sericheng */ 1201269Sericheng case DL_CAPAB_HCKSUM: { 1202269Sericheng dl_capab_hcksum_t *hcksump; 1203269Sericheng dl_capab_hcksum_t hcksum; 1204269Sericheng 1205269Sericheng hcksump = (dl_capab_hcksum_t *)&sp[1]; 1206269Sericheng /* 1207269Sericheng * Copy for alignment. 1208269Sericheng */ 1209269Sericheng bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t)); 1210269Sericheng dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); 1211269Sericheng bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t)); 1212269Sericheng break; 1213269Sericheng } 1214269Sericheng 1215269Sericheng /* 1216269Sericheng * IP polling interface. 1217269Sericheng */ 1218269Sericheng case DL_CAPAB_POLL: { 12191184Skrgopi dl_capab_dls_t *pollp; 12201184Skrgopi dl_capab_dls_t poll; 1221269Sericheng 12221184Skrgopi pollp = (dl_capab_dls_t *)&sp[1]; 1223269Sericheng /* 1224269Sericheng * Copy for alignment. 1225269Sericheng */ 12261184Skrgopi bcopy(pollp, &poll, sizeof (dl_capab_dls_t)); 1227269Sericheng 1228269Sericheng /* 1229269Sericheng * We need to become writer before enabling and/or 1230269Sericheng * disabling the polling interface. If we couldn' 1231269Sericheng * upgrade, check state again after re-acquiring the 1232269Sericheng * lock to make sure we can proceed. 1233269Sericheng */ 1234269Sericheng if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) { 1235269Sericheng rw_exit(&dsp->ds_lock); 1236269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1237269Sericheng 1238269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1239269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1240269Sericheng dl_err = DL_OUTSTATE; 1241269Sericheng goto failed; 1242269Sericheng } 1243269Sericheng } 1244269Sericheng upgraded = B_TRUE; 1245269Sericheng 12461184Skrgopi switch (poll.dls_flags) { 1247269Sericheng default: 1248269Sericheng /*FALLTHRU*/ 1249269Sericheng case POLL_DISABLE: 1250269Sericheng proto_poll_disable(dsp); 1251269Sericheng break; 1252269Sericheng 1253269Sericheng case POLL_ENABLE: 1254269Sericheng ASSERT(!(dld_opt & DLD_OPT_NO_POLL)); 1255269Sericheng 1256269Sericheng /* 1257269Sericheng * Make sure polling is disabled. 1258269Sericheng */ 1259269Sericheng proto_poll_disable(dsp); 1260269Sericheng 1261269Sericheng /* 1262269Sericheng * Now attempt enable it. 1263269Sericheng */ 12641184Skrgopi if (check_ip_above(dsp->ds_rq) && 12651184Skrgopi proto_poll_enable(dsp, &poll)) { 12661184Skrgopi bzero(&poll, sizeof (dl_capab_dls_t)); 12671184Skrgopi poll.dls_flags = POLL_ENABLE; 12681184Skrgopi } 1269269Sericheng break; 1270269Sericheng } 1271269Sericheng 12721184Skrgopi dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq); 12731184Skrgopi bcopy(&poll, pollp, sizeof (dl_capab_dls_t)); 12741184Skrgopi break; 12751184Skrgopi } 12761184Skrgopi case DL_CAPAB_SOFT_RING: { 12771184Skrgopi dl_capab_dls_t *soft_ringp; 12781184Skrgopi dl_capab_dls_t soft_ring; 12791184Skrgopi 12801184Skrgopi soft_ringp = (dl_capab_dls_t *)&sp[1]; 12811184Skrgopi /* 12821184Skrgopi * Copy for alignment. 12831184Skrgopi */ 12841184Skrgopi bcopy(soft_ringp, &soft_ring, 12851184Skrgopi sizeof (dl_capab_dls_t)); 12861184Skrgopi 12871184Skrgopi /* 12881184Skrgopi * We need to become writer before enabling and/or 12891184Skrgopi * disabling the soft_ring interface. If we couldn' 12901184Skrgopi * upgrade, check state again after re-acquiring the 12911184Skrgopi * lock to make sure we can proceed. 12921184Skrgopi */ 12931184Skrgopi if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) { 12941184Skrgopi rw_exit(&dsp->ds_lock); 12951184Skrgopi rw_enter(&dsp->ds_lock, RW_WRITER); 12961184Skrgopi 12971184Skrgopi if (dsp->ds_dlstate == DL_UNATTACHED || 12981184Skrgopi DL_ACK_PENDING(dsp->ds_dlstate)) { 12991184Skrgopi dl_err = DL_OUTSTATE; 13001184Skrgopi goto failed; 13011184Skrgopi } 13021184Skrgopi } 13031184Skrgopi upgraded = B_TRUE; 13041184Skrgopi 13051184Skrgopi switch (soft_ring.dls_flags) { 13061184Skrgopi default: 13071184Skrgopi /*FALLTHRU*/ 13081184Skrgopi case SOFT_RING_DISABLE: 13091184Skrgopi proto_soft_ring_disable(dsp); 13101184Skrgopi break; 13111184Skrgopi 13121184Skrgopi case SOFT_RING_ENABLE: 13131184Skrgopi /* 13141184Skrgopi * Make sure soft_ring is disabled. 13151184Skrgopi */ 13161184Skrgopi proto_soft_ring_disable(dsp); 13171184Skrgopi 13181184Skrgopi /* 13191184Skrgopi * Now attempt enable it. 13201184Skrgopi */ 13211184Skrgopi if (check_ip_above(dsp->ds_rq) && 13221184Skrgopi proto_soft_ring_enable(dsp, &soft_ring)) { 13231184Skrgopi bzero(&soft_ring, 13241184Skrgopi sizeof (dl_capab_dls_t)); 13251184Skrgopi soft_ring.dls_flags = 13261184Skrgopi SOFT_RING_ENABLE; 13271184Skrgopi } else { 13281184Skrgopi bzero(&soft_ring, 13291184Skrgopi sizeof (dl_capab_dls_t)); 13301184Skrgopi soft_ring.dls_flags = 13311184Skrgopi SOFT_RING_DISABLE; 13321184Skrgopi } 13331184Skrgopi break; 13341184Skrgopi } 13351184Skrgopi 13361184Skrgopi dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq); 13371184Skrgopi bcopy(&soft_ring, soft_ringp, 13381184Skrgopi sizeof (dl_capab_dls_t)); 1339269Sericheng break; 1340269Sericheng } 1341269Sericheng default: 1342269Sericheng break; 1343269Sericheng } 1344269Sericheng 1345269Sericheng off += size; 1346269Sericheng } 1347269Sericheng rw_exit(&dsp->ds_lock); 1348269Sericheng qreply(q, mp); 1349269Sericheng return (B_TRUE); 1350269Sericheng failed: 1351269Sericheng rw_exit(&dsp->ds_lock); 1352269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0); 1353269Sericheng return (B_FALSE); 13540Sstevel@tonic-gate } 13550Sstevel@tonic-gate 13560Sstevel@tonic-gate /* 1357269Sericheng * DL_NOTIFY_REQ 13580Sstevel@tonic-gate */ 1359269Sericheng static boolean_t 1360269Sericheng proto_notify_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 13610Sstevel@tonic-gate { 1362269Sericheng dl_notify_req_t *dlp = (dl_notify_req_t *)udlp; 1363269Sericheng t_uscalar_t dl_err; 1364269Sericheng queue_t *q = dsp->ds_wq; 1365269Sericheng uint_t note = 1366269Sericheng DL_NOTE_PROMISC_ON_PHYS | 1367269Sericheng DL_NOTE_PROMISC_OFF_PHYS | 1368269Sericheng DL_NOTE_PHYS_ADDR | 1369269Sericheng DL_NOTE_LINK_UP | 1370269Sericheng DL_NOTE_LINK_DOWN | 1371*2311Sseb DL_NOTE_CAPAB_RENEG | 1372*2311Sseb DL_NOTE_SPEED; 13730Sstevel@tonic-gate 13741521Syz147064 rw_enter(&dsp->ds_lock, RW_WRITER); 13751521Syz147064 1376269Sericheng if (MBLKL(mp) < sizeof (dl_notify_req_t)) { 1377269Sericheng dl_err = DL_BADPRIM; 1378269Sericheng goto failed; 1379269Sericheng } 13800Sstevel@tonic-gate 1381269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1382269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1383269Sericheng dl_err = DL_OUTSTATE; 1384269Sericheng goto failed; 13850Sstevel@tonic-gate } 13860Sstevel@tonic-gate 1387269Sericheng /* 1388269Sericheng * Cache the notifications that are being enabled. 1389269Sericheng */ 1390269Sericheng dsp->ds_notifications = dlp->dl_notifications & note; 1391269Sericheng rw_exit(&dsp->ds_lock); 1392269Sericheng /* 1393269Sericheng * The ACK carries all notifications regardless of which set is 1394269Sericheng * being enabled. 1395269Sericheng */ 1396269Sericheng dlnotifyack(q, mp, note); 1397269Sericheng 1398269Sericheng /* 1399269Sericheng * Solicit DL_NOTIFY_IND messages for each enabled notification. 1400269Sericheng */ 1401269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 1402269Sericheng if (dsp->ds_notifications != 0) { 1403269Sericheng rw_exit(&dsp->ds_lock); 1404269Sericheng dld_str_notify_ind(dsp); 1405269Sericheng } else { 1406269Sericheng rw_exit(&dsp->ds_lock); 1407269Sericheng } 1408269Sericheng return (B_TRUE); 1409269Sericheng failed: 1410269Sericheng rw_exit(&dsp->ds_lock); 1411269Sericheng dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0); 1412269Sericheng return (B_FALSE); 14130Sstevel@tonic-gate } 14140Sstevel@tonic-gate 14150Sstevel@tonic-gate /* 1416269Sericheng * DL_UINTDATA_REQ 14170Sstevel@tonic-gate */ 1418269Sericheng static boolean_t 1419269Sericheng proto_unitdata_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 14200Sstevel@tonic-gate { 1421269Sericheng queue_t *q = dsp->ds_wq; 1422269Sericheng dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)udlp; 1423269Sericheng off_t off; 1424269Sericheng size_t len, size; 1425269Sericheng const uint8_t *addr; 1426269Sericheng uint16_t sap; 1427269Sericheng uint_t addr_length; 1428*2311Sseb mblk_t *bp, *payload; 1429269Sericheng uint32_t start, stuff, end, value, flags; 1430269Sericheng t_uscalar_t dl_err; 1431269Sericheng 1432269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 1433269Sericheng 1434269Sericheng if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) { 1435269Sericheng dl_err = DL_BADPRIM; 1436269Sericheng goto failed; 1437269Sericheng } 1438269Sericheng 1439269Sericheng if (dsp->ds_dlstate != DL_IDLE) { 1440269Sericheng dl_err = DL_OUTSTATE; 1441269Sericheng goto failed; 1442269Sericheng } 1443269Sericheng addr_length = dsp->ds_mip->mi_addr_length; 1444269Sericheng 1445269Sericheng off = dlp->dl_dest_addr_offset; 1446269Sericheng len = dlp->dl_dest_addr_length; 1447269Sericheng 1448269Sericheng if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) { 1449269Sericheng dl_err = DL_BADPRIM; 1450269Sericheng goto failed; 1451269Sericheng } 1452269Sericheng 1453269Sericheng if (len != addr_length + sizeof (uint16_t)) { 1454269Sericheng dl_err = DL_BADADDR; 1455269Sericheng goto failed; 1456269Sericheng } 1457269Sericheng 1458269Sericheng addr = mp->b_rptr + off; 1459269Sericheng sap = *(uint16_t *)(mp->b_rptr + off + addr_length); 1460269Sericheng 1461269Sericheng /* 1462269Sericheng * Check the length of the packet and the block types. 1463269Sericheng */ 1464269Sericheng size = 0; 1465*2311Sseb payload = mp->b_cont; 1466*2311Sseb for (bp = payload; bp != NULL; bp = bp->b_cont) { 1467269Sericheng if (DB_TYPE(bp) != M_DATA) 1468269Sericheng goto baddata; 1469269Sericheng 1470269Sericheng size += MBLKL(bp); 1471269Sericheng } 1472269Sericheng 1473269Sericheng if (size > dsp->ds_mip->mi_sdu_max) 1474269Sericheng goto baddata; 1475269Sericheng 1476269Sericheng /* 1477269Sericheng * Build a packet header. 1478269Sericheng */ 1479*2311Sseb bp = dls_header(dsp->ds_dc, addr, sap, dsp->ds_pri, payload); 1480*2311Sseb if (bp == NULL) { 1481269Sericheng dl_err = DL_BADADDR; 1482269Sericheng goto failed; 1483269Sericheng } 1484269Sericheng 1485269Sericheng /* 1486269Sericheng * We no longer need the M_PROTO header, so free it. 1487269Sericheng */ 1488269Sericheng freeb(mp); 1489269Sericheng 1490269Sericheng /* 1491269Sericheng * Transfer the checksum offload information if it is present. 1492269Sericheng */ 1493*2311Sseb hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value, 1494269Sericheng &flags); 1495*2311Sseb (void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0); 1496269Sericheng 1497269Sericheng /* 1498269Sericheng * Link the payload onto the new header. 1499269Sericheng */ 1500269Sericheng ASSERT(bp->b_cont == NULL); 1501*2311Sseb bp->b_cont = payload; 1502269Sericheng 1503269Sericheng str_mdata_fastpath_put(dsp, bp); 1504269Sericheng rw_exit(&dsp->ds_lock); 1505269Sericheng return (B_TRUE); 1506269Sericheng failed: 1507269Sericheng rw_exit(&dsp->ds_lock); 1508269Sericheng dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0); 1509269Sericheng return (B_FALSE); 1510269Sericheng 1511269Sericheng baddata: 1512269Sericheng rw_exit(&dsp->ds_lock); 1513269Sericheng dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0); 1514269Sericheng return (B_FALSE); 1515269Sericheng } 1516269Sericheng 1517269Sericheng /* 1518269Sericheng * DL_PASSIVE_REQ 1519269Sericheng */ 1520269Sericheng /* ARGSUSED */ 1521269Sericheng static boolean_t 1522269Sericheng proto_passive_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 1523269Sericheng { 1524269Sericheng t_uscalar_t dl_err; 1525269Sericheng 1526269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1527269Sericheng /* 1528269Sericheng * If we've already become active by issuing an active primitive, 1529269Sericheng * then it's too late to try to become passive. 1530269Sericheng */ 1531269Sericheng if (dsp->ds_passivestate == DLD_ACTIVE) { 1532269Sericheng dl_err = DL_OUTSTATE; 1533269Sericheng goto failed; 1534269Sericheng } 1535269Sericheng 1536269Sericheng if (MBLKL(mp) < sizeof (dl_passive_req_t)) { 1537269Sericheng dl_err = DL_BADPRIM; 1538269Sericheng goto failed; 1539269Sericheng } 1540269Sericheng 1541269Sericheng dsp->ds_passivestate = DLD_PASSIVE; 1542269Sericheng rw_exit(&dsp->ds_lock); 1543269Sericheng dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ); 1544269Sericheng return (B_TRUE); 1545269Sericheng failed: 1546269Sericheng rw_exit(&dsp->ds_lock); 1547269Sericheng dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0); 1548269Sericheng return (B_FALSE); 1549269Sericheng } 1550269Sericheng 1551269Sericheng 1552269Sericheng /* 1553269Sericheng * Catch-all handler. 1554269Sericheng */ 1555269Sericheng static boolean_t 1556269Sericheng proto_req(dld_str_t *dsp, union DL_primitives *dlp, mblk_t *mp) 1557269Sericheng { 1558269Sericheng dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0); 1559269Sericheng return (B_FALSE); 15600Sstevel@tonic-gate } 15610Sstevel@tonic-gate 15620Sstevel@tonic-gate static void 15630Sstevel@tonic-gate proto_poll_disable(dld_str_t *dsp) 15640Sstevel@tonic-gate { 15650Sstevel@tonic-gate mac_handle_t mh; 15660Sstevel@tonic-gate 15671353Sericheng ASSERT(dsp->ds_pending_req != NULL || RW_WRITE_HELD(&dsp->ds_lock)); 1568269Sericheng 15690Sstevel@tonic-gate if (!dsp->ds_polling) 15700Sstevel@tonic-gate return; 15710Sstevel@tonic-gate 15720Sstevel@tonic-gate /* 15730Sstevel@tonic-gate * It should be impossible to enable raw mode if polling is turned on. 15740Sstevel@tonic-gate */ 15750Sstevel@tonic-gate ASSERT(dsp->ds_mode != DLD_RAW); 15760Sstevel@tonic-gate 15770Sstevel@tonic-gate /* 15780Sstevel@tonic-gate * Reset the resource_add callback. 15790Sstevel@tonic-gate */ 15800Sstevel@tonic-gate mh = dls_mac(dsp->ds_dc); 15810Sstevel@tonic-gate mac_resource_set(mh, NULL, NULL); 15821184Skrgopi mac_resources(mh); 15830Sstevel@tonic-gate 15840Sstevel@tonic-gate /* 15850Sstevel@tonic-gate * Set receive function back to default. 15860Sstevel@tonic-gate */ 15870Sstevel@tonic-gate dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_FASTPATH) ? 15880Sstevel@tonic-gate dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp); 15890Sstevel@tonic-gate 15900Sstevel@tonic-gate /* 15910Sstevel@tonic-gate * Note that polling is disabled. 15920Sstevel@tonic-gate */ 15930Sstevel@tonic-gate dsp->ds_polling = B_FALSE; 15940Sstevel@tonic-gate } 15950Sstevel@tonic-gate 15960Sstevel@tonic-gate static boolean_t 15971184Skrgopi proto_poll_enable(dld_str_t *dsp, dl_capab_dls_t *pollp) 15980Sstevel@tonic-gate { 15990Sstevel@tonic-gate mac_handle_t mh; 16000Sstevel@tonic-gate 1601269Sericheng ASSERT(RW_WRITE_HELD(&dsp->ds_lock)); 16020Sstevel@tonic-gate ASSERT(!dsp->ds_polling); 16030Sstevel@tonic-gate 16040Sstevel@tonic-gate /* 16050Sstevel@tonic-gate * We cannot enable polling if raw mode 16060Sstevel@tonic-gate * has been enabled. 16070Sstevel@tonic-gate */ 16080Sstevel@tonic-gate if (dsp->ds_mode == DLD_RAW) 16090Sstevel@tonic-gate return (B_FALSE); 16100Sstevel@tonic-gate 16110Sstevel@tonic-gate mh = dls_mac(dsp->ds_dc); 16120Sstevel@tonic-gate 16130Sstevel@tonic-gate /* 16140Sstevel@tonic-gate * Register resources. 16150Sstevel@tonic-gate */ 16161184Skrgopi mac_resource_set(mh, (mac_resource_add_t)pollp->dls_ring_add, 16171184Skrgopi (void *)pollp->dls_rx_handle); 16180Sstevel@tonic-gate mac_resources(mh); 16190Sstevel@tonic-gate 16200Sstevel@tonic-gate /* 16210Sstevel@tonic-gate * Set the receive function. 16220Sstevel@tonic-gate */ 16231184Skrgopi dls_rx_set(dsp->ds_dc, (dls_rx_t)pollp->dls_rx, 16241184Skrgopi (void *)pollp->dls_rx_handle); 16250Sstevel@tonic-gate 16260Sstevel@tonic-gate /* 16270Sstevel@tonic-gate * Note that polling is enabled. This prevents further DLIOCHDRINFO 16280Sstevel@tonic-gate * ioctls from overwriting the receive function pointer. 16290Sstevel@tonic-gate */ 16300Sstevel@tonic-gate dsp->ds_polling = B_TRUE; 16310Sstevel@tonic-gate return (B_TRUE); 16320Sstevel@tonic-gate } 16330Sstevel@tonic-gate 16341184Skrgopi static void 16351184Skrgopi proto_soft_ring_disable(dld_str_t *dsp) 16361184Skrgopi { 16371184Skrgopi ASSERT(RW_WRITE_HELD(&dsp->ds_lock)); 16381184Skrgopi 16391184Skrgopi if (!dsp->ds_soft_ring) 16401184Skrgopi return; 16411184Skrgopi 16421184Skrgopi /* 16431184Skrgopi * It should be impossible to enable raw mode if soft_ring is turned on. 16441184Skrgopi */ 16451184Skrgopi ASSERT(dsp->ds_mode != DLD_RAW); 16461184Skrgopi proto_change_soft_ring_fanout(dsp, SOFT_RING_NONE); 16471184Skrgopi /* 16481184Skrgopi * Note that fanout is disabled. 16491184Skrgopi */ 16501184Skrgopi dsp->ds_soft_ring = B_FALSE; 16511184Skrgopi } 16521184Skrgopi 16531184Skrgopi static boolean_t 16541184Skrgopi proto_soft_ring_enable(dld_str_t *dsp, dl_capab_dls_t *soft_ringp) 16551184Skrgopi { 16561184Skrgopi ASSERT(RW_WRITE_HELD(&dsp->ds_lock)); 16571184Skrgopi ASSERT(!dsp->ds_soft_ring); 16581184Skrgopi 16591184Skrgopi /* 16601184Skrgopi * We cannot enable soft_ring if raw mode 16611184Skrgopi * has been enabled. 16621184Skrgopi */ 16631184Skrgopi if (dsp->ds_mode == DLD_RAW) 16641184Skrgopi return (B_FALSE); 16651184Skrgopi 16661184Skrgopi if (dls_soft_ring_enable(dsp->ds_dc, soft_ringp) == B_FALSE) 16671184Skrgopi return (B_FALSE); 16681184Skrgopi 16691184Skrgopi dsp->ds_soft_ring = B_TRUE; 16701184Skrgopi return (B_TRUE); 16711184Skrgopi } 16721184Skrgopi 16731184Skrgopi static void 16741184Skrgopi proto_change_soft_ring_fanout(dld_str_t *dsp, int type) 16751184Skrgopi { 16761184Skrgopi dls_rx_t rx; 16771184Skrgopi 16781184Skrgopi if (type == SOFT_RING_NONE) { 16791184Skrgopi rx = (dsp->ds_mode == DLD_FASTPATH) ? 16801184Skrgopi dld_str_rx_fastpath : dld_str_rx_unitdata; 16811184Skrgopi } else { 16821184Skrgopi rx = (dls_rx_t)dls_ether_soft_ring_fanout; 16831184Skrgopi } 16841184Skrgopi dls_soft_ring_rx_set(dsp->ds_dc, rx, dsp, type); 16851184Skrgopi } 16861184Skrgopi 16870Sstevel@tonic-gate /* 16880Sstevel@tonic-gate * DL_CAPABILITY_ACK/DL_ERROR_ACK 16890Sstevel@tonic-gate */ 1690269Sericheng static boolean_t 1691269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp) 16920Sstevel@tonic-gate { 16930Sstevel@tonic-gate dl_capability_ack_t *dlap; 16940Sstevel@tonic-gate dl_capability_sub_t *dlsp; 16950Sstevel@tonic-gate size_t subsize; 16961184Skrgopi dl_capab_dls_t poll; 16971184Skrgopi dl_capab_dls_t soft_ring; 16980Sstevel@tonic-gate dl_capab_hcksum_t hcksum; 16990Sstevel@tonic-gate dl_capab_zerocopy_t zcopy; 17000Sstevel@tonic-gate uint8_t *ptr; 1701*2311Sseb boolean_t cksum_cap; 17020Sstevel@tonic-gate boolean_t poll_cap; 1703269Sericheng queue_t *q = dsp->ds_wq; 1704269Sericheng mblk_t *mp1; 1705269Sericheng 1706269Sericheng ASSERT(RW_READ_HELD(&dsp->ds_lock)); 17070Sstevel@tonic-gate 17080Sstevel@tonic-gate /* 17090Sstevel@tonic-gate * Initially assume no capabilities. 17100Sstevel@tonic-gate */ 17110Sstevel@tonic-gate subsize = 0; 17120Sstevel@tonic-gate 17131555Skrgopi /* 17141555Skrgopi * Advertize soft ring capability if 17151555Skrgopi * VLAN_ID_NONE for GLDv3 drivers 17161555Skrgopi */ 17171555Skrgopi if (dsp->ds_vid == VLAN_ID_NONE) 17181555Skrgopi subsize += sizeof (dl_capability_sub_t) + 17191555Skrgopi sizeof (dl_capab_dls_t); 17201184Skrgopi 17210Sstevel@tonic-gate /* 17220Sstevel@tonic-gate * Check if polling can be enabled on this interface. 17230Sstevel@tonic-gate * If advertising DL_CAPAB_POLL has not been explicitly disabled 17240Sstevel@tonic-gate * then reserve space for that capability. 17250Sstevel@tonic-gate */ 1726*2311Sseb poll_cap = (mac_capab_get(dsp->ds_mh, MAC_CAPAB_POLL, NULL) && 17270Sstevel@tonic-gate !(dld_opt & DLD_OPT_NO_POLL) && (dsp->ds_vid == VLAN_ID_NONE)); 17280Sstevel@tonic-gate if (poll_cap) { 17290Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 17301184Skrgopi sizeof (dl_capab_dls_t); 17310Sstevel@tonic-gate } 17320Sstevel@tonic-gate 17330Sstevel@tonic-gate /* 17340Sstevel@tonic-gate * If the MAC interface supports checksum offload then reserve 17350Sstevel@tonic-gate * space for the DL_CAPAB_HCKSUM capability. 17360Sstevel@tonic-gate */ 1737*2311Sseb if (cksum_cap = mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM, 1738*2311Sseb &hcksum.hcksum_txflags)) { 17390Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 17400Sstevel@tonic-gate sizeof (dl_capab_hcksum_t); 17410Sstevel@tonic-gate } 17420Sstevel@tonic-gate 17430Sstevel@tonic-gate /* 17440Sstevel@tonic-gate * If DL_CAPAB_ZEROCOPY has not be explicitly disabled then 17450Sstevel@tonic-gate * reserve space for it. 17460Sstevel@tonic-gate */ 17470Sstevel@tonic-gate if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) { 17480Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 17490Sstevel@tonic-gate sizeof (dl_capab_zerocopy_t); 17500Sstevel@tonic-gate } 17510Sstevel@tonic-gate 17520Sstevel@tonic-gate /* 1753269Sericheng * If there are no capabilities to advertise or if we 1754269Sericheng * can't allocate a response, send a DL_ERROR_ACK. 17550Sstevel@tonic-gate */ 17561184Skrgopi if ((mp1 = reallocb(mp, 1757269Sericheng sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) { 1758269Sericheng rw_exit(&dsp->ds_lock); 1759269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0); 1760269Sericheng return (B_FALSE); 17610Sstevel@tonic-gate } 17620Sstevel@tonic-gate 1763269Sericheng mp = mp1; 1764269Sericheng DB_TYPE(mp) = M_PROTO; 1765269Sericheng mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize; 1766269Sericheng bzero(mp->b_rptr, MBLKL(mp)); 17670Sstevel@tonic-gate dlap = (dl_capability_ack_t *)mp->b_rptr; 17680Sstevel@tonic-gate dlap->dl_primitive = DL_CAPABILITY_ACK; 17690Sstevel@tonic-gate dlap->dl_sub_offset = sizeof (dl_capability_ack_t); 17700Sstevel@tonic-gate dlap->dl_sub_length = subsize; 17710Sstevel@tonic-gate ptr = (uint8_t *)&dlap[1]; 17720Sstevel@tonic-gate 17730Sstevel@tonic-gate /* 17740Sstevel@tonic-gate * IP polling interface. 17750Sstevel@tonic-gate */ 17760Sstevel@tonic-gate if (poll_cap) { 17770Sstevel@tonic-gate /* 1778269Sericheng * Attempt to disable just in case this is a re-negotiation; 1779269Sericheng * we need to become writer before doing so. 17800Sstevel@tonic-gate */ 1781269Sericheng if (!rw_tryupgrade(&dsp->ds_lock)) { 1782269Sericheng rw_exit(&dsp->ds_lock); 1783269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1784269Sericheng } 17850Sstevel@tonic-gate 1786269Sericheng /* 1787269Sericheng * Check if polling state has changed after we re-acquired 1788269Sericheng * the lock above, so that we don't mis-advertise it. 1789269Sericheng */ 1790*2311Sseb poll_cap = !(dld_opt & DLD_OPT_NO_POLL) && 1791*2311Sseb (dsp->ds_vid == VLAN_ID_NONE); 17920Sstevel@tonic-gate 1793269Sericheng if (!poll_cap) { 1794269Sericheng int poll_capab_size; 1795269Sericheng 1796269Sericheng rw_downgrade(&dsp->ds_lock); 1797269Sericheng 1798269Sericheng poll_capab_size = sizeof (dl_capability_sub_t) + 17991184Skrgopi sizeof (dl_capab_dls_t); 18000Sstevel@tonic-gate 1801269Sericheng mp->b_wptr -= poll_capab_size; 1802269Sericheng subsize -= poll_capab_size; 1803269Sericheng dlap->dl_sub_length = subsize; 1804269Sericheng } else { 1805269Sericheng proto_poll_disable(dsp); 1806269Sericheng 1807269Sericheng rw_downgrade(&dsp->ds_lock); 1808269Sericheng 1809269Sericheng dlsp = (dl_capability_sub_t *)ptr; 1810269Sericheng 1811269Sericheng dlsp->dl_cap = DL_CAPAB_POLL; 18121184Skrgopi dlsp->dl_length = sizeof (dl_capab_dls_t); 1813269Sericheng ptr += sizeof (dl_capability_sub_t); 18140Sstevel@tonic-gate 18151184Skrgopi bzero(&poll, sizeof (dl_capab_dls_t)); 18161184Skrgopi poll.dls_version = POLL_VERSION_1; 18171184Skrgopi poll.dls_flags = POLL_CAPABLE; 18181184Skrgopi poll.dls_tx_handle = (uintptr_t)dsp; 18191184Skrgopi poll.dls_tx = (uintptr_t)str_mdata_fastpath_put; 1820269Sericheng 18211184Skrgopi dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq); 18221184Skrgopi bcopy(&poll, ptr, sizeof (dl_capab_dls_t)); 18231184Skrgopi ptr += sizeof (dl_capab_dls_t); 1824269Sericheng } 18250Sstevel@tonic-gate } 18260Sstevel@tonic-gate 1827269Sericheng ASSERT(RW_READ_HELD(&dsp->ds_lock)); 1828269Sericheng 18291555Skrgopi if (dsp->ds_vid == VLAN_ID_NONE) { 18301555Skrgopi dlsp = (dl_capability_sub_t *)ptr; 18311184Skrgopi 18321555Skrgopi dlsp->dl_cap = DL_CAPAB_SOFT_RING; 18331555Skrgopi dlsp->dl_length = sizeof (dl_capab_dls_t); 18341555Skrgopi ptr += sizeof (dl_capability_sub_t); 18351184Skrgopi 18361555Skrgopi bzero(&soft_ring, sizeof (dl_capab_dls_t)); 18371555Skrgopi soft_ring.dls_version = SOFT_RING_VERSION_1; 18381555Skrgopi soft_ring.dls_flags = SOFT_RING_CAPABLE; 18391555Skrgopi soft_ring.dls_tx_handle = (uintptr_t)dsp; 18401555Skrgopi soft_ring.dls_tx = (uintptr_t)str_mdata_fastpath_put; 18411555Skrgopi soft_ring.dls_ring_change_status = 18421555Skrgopi (uintptr_t)proto_change_soft_ring_fanout; 18431555Skrgopi soft_ring.dls_ring_bind = (uintptr_t)soft_ring_bind; 18441555Skrgopi soft_ring.dls_ring_unbind = (uintptr_t)soft_ring_unbind; 18451184Skrgopi 18461555Skrgopi dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq); 18471555Skrgopi bcopy(&soft_ring, ptr, sizeof (dl_capab_dls_t)); 18481555Skrgopi ptr += sizeof (dl_capab_dls_t); 18491555Skrgopi } 18501184Skrgopi 18510Sstevel@tonic-gate /* 18520Sstevel@tonic-gate * TCP/IP checksum offload. 18530Sstevel@tonic-gate */ 1854*2311Sseb if (cksum_cap) { 18550Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr; 18560Sstevel@tonic-gate 18570Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_HCKSUM; 18580Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_hcksum_t); 18590Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t); 18600Sstevel@tonic-gate 18610Sstevel@tonic-gate hcksum.hcksum_version = HCKSUM_VERSION_1; 18620Sstevel@tonic-gate dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); 18630Sstevel@tonic-gate bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t)); 18640Sstevel@tonic-gate ptr += sizeof (dl_capab_hcksum_t); 18650Sstevel@tonic-gate } 18660Sstevel@tonic-gate 18670Sstevel@tonic-gate /* 18680Sstevel@tonic-gate * Zero copy 18690Sstevel@tonic-gate */ 18700Sstevel@tonic-gate if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) { 18710Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr; 18720Sstevel@tonic-gate 18730Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_ZEROCOPY; 18740Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_zerocopy_t); 18750Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t); 18760Sstevel@tonic-gate 18770Sstevel@tonic-gate bzero(&zcopy, sizeof (dl_capab_zerocopy_t)); 18780Sstevel@tonic-gate zcopy.zerocopy_version = ZEROCOPY_VERSION_1; 18790Sstevel@tonic-gate zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM; 18800Sstevel@tonic-gate 18810Sstevel@tonic-gate dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq); 18820Sstevel@tonic-gate bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t)); 18830Sstevel@tonic-gate ptr += sizeof (dl_capab_zerocopy_t); 18840Sstevel@tonic-gate } 18850Sstevel@tonic-gate 18860Sstevel@tonic-gate ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize); 1887269Sericheng 1888269Sericheng rw_exit(&dsp->ds_lock); 1889269Sericheng qreply(q, mp); 1890269Sericheng return (B_TRUE); 18910Sstevel@tonic-gate } 1892